PHPのお勉強!

PHP TOP

parse_str

(PHP 4, PHP 5, PHP 7, PHP 8)

parse_str文字列を処理し、変数に代入する

説明

parse_str(string $string, array &$result): void

URL 経由で渡されるクエリ文字列と同様に string を処理し、現在のスコープに変数をセットします。 (result が指定された場合は、配列 result にセットします)

パラメータ

string

入力文字列。

result

2 番目の引数 result が指定された場合、 変数は、代わりに配列の要素としてこの変数に保存されます。

警告

この関数を result パラメータを指定せずに使うことは 絶対に おすすめしません し、 PHP 7.2 以降では 推奨されません。 PHP 8.0.0 以降では、result パラメータの指定が 必須 になっています。

戻り値

値を返しません。

変更履歴

バージョン 説明
8.0.0 result は、オプションではなくなりました。
7.2.0 parse_str()関数を 第二引数を指定せずに使うと E_DEPRECATED レベルの警告が発生するようになりました。

例1 parse_str() の使用法

<?php
$str
= "first=value&arr[]=foo+bar&arr[]=baz";

// 推奨
parse_str($str, $output);
echo
$output['first']; // value
echo $output['arr'][0]; // foo bar
echo $output['arr'][1]; // baz

// お勧めできない
parse_str($str);
echo
$first; // value
echo $arr[0]; // foo bar
echo $arr[1]; // baz
?>

PHP における変数は、名前にドットやスペースを許容しないので、 そういった値はアンダースコアに変換されます。同じことは、 この関数に result パラメータを指定した 場合の、配列のキーの名前にも当てはまります。

例2 parse_str() name mangling

<?php
parse_str
("My Value=Something");
echo
$My_Value; // Something

parse_str("My Value=Something", $output);
echo
$output['My_Value']; // Something
?>

注意

注意:

全ての作成された変数 (第二引数が設定された場合は配列に設定される値) の値は、既に urldecode() されています。

注意:

現在の QUERY_STRING を取得するには、変数 $_SERVER['QUERY_STRING'] を使用する事ができます。また、 外部から来る変数 のセクションも読んでください。

参考

  • parse_url() - URL を解釈し、その構成要素を返す
  • pathinfo() - ファイルパスに関する情報を返す
  • http_build_query() - URL エンコードされたクエリ文字列を生成する
  • urldecode() - URL エンコードされた文字列をデコードする

add a note

User Contributed Notes 31 notes

up
121
Evan K
17 years ago
It bears mentioning that the parse_str builtin does NOT process a query string in the CGI standard way, when it comes to duplicate fields. If multiple fields of the same name exist in a query string, every other web processing language would read them into an array, but PHP silently overwrites them:

<?php
# silently fails to handle multiple values
parse_str('foo=1&foo=2&foo=3');

# the above produces:
$foo = array('foo' => '3');
?>

Instead, PHP uses a non-standards compliant practice of including brackets in fieldnames to achieve the same effect.

<?php
# bizarre php-specific behavior
parse_str('foo[]=1&foo[]=2&foo[]=3');

# the above produces:
$foo = array('foo' => array('1', '2', '3') );
?>

This can be confusing for anyone who's used to the CGI standard, so keep it in mind. As an alternative, I use a "proper" querystring parser function:

<?php
function proper_parse_str($str) {
# result array
$arr = array();

# split on outer delimiter
$pairs = explode('&', $str);

# loop through each pair
foreach ($pairs as $i) {
# split into name and value
list($name,$value) = explode('=', $i, 2);

# if name already exists
if( isset($arr[$name]) ) {
# stick multiple values into an array
if( is_array($arr[$name]) ) {
$arr[$name][] = $value;
}
else {
$arr[$name] = array($arr[$name], $value);
}
}
# otherwise, simply stick it in a scalar
else {
$arr[$name] = $value;
}
}

# return result array
return $arr;
}

$query = proper_parse_str($_SERVER['QUERY_STRING']);
?>
up
6
Roemer Lievaart
2 years ago
If you need the key names preserved and don't want spaces or . or unmatched [ or ] replaced by an underscore, yet you want all the other goodies of parse_str(), like turning matched [] into array elements, you can use this code as a base for that.

<?php
const periodPlaceholder = 'QQleQPunT';
const
spacePlaceholder = 'QQleQSpaTIE';

function
parse_str_clean($querystr): array {
// without the converting of spaces and dots etc to underscores.
$qquerystr = str_ireplace(['.','%2E','+',' ','%20'], [periodPlaceholder,periodPlaceholder,spacePlaceholder,spacePlaceholder,spacePlaceholder], $querystr);
$arr = null ; parse_str($qquerystr, $arr);

sanitizeKeys($arr, $querystr);
return
$arr;
}

function
sanitizeKeys(&$arr, $querystr) {
foreach(
$arr as $key=>$val) {
// restore values to original
$newval = $val ;
if (
is_string($val)) {
$newval = str_replace([periodPlaceholder,spacePlaceholder], ["."," "], $val);
}

$newkey = str_replace([periodPlaceholder,spacePlaceholder], ["."," "], $key);

if (
str_contains($newkey, '_') ) {

// periode of space or [ or ] converted to _. Restore with querystring
$regex = '/&('.str_replace('_', '[ \.\[\]]', preg_quote($newkey, '/')).')=/';
$matches = null ;
if (
preg_match_all($regex, "&".urldecode($querystr), $matches) ) {

if (
count(array_unique($matches[1])) === 1 && $key != $matches[1][0] ) {
$newkey = $matches[1][0] ;
}
}
}
if (
$newkey != $key ) {
unset(
$arr[$key]);
$arr[$newkey] = $newval ;
} elseif(
$val != $newval )
$arr[$key] = $newval;

if (
is_array($val)) {
sanitizeKeys($arr[$newkey], $querystr);
}
}
}

?>

For example:

parse_str_clean("code.1=printr%28hahaha&code 1=448044&test.mijn%5B%5D%5B2%5D=test%20Roemer&test%5Bmijn=test%202e%20Roemer");

Produces:

array(4) {
["code.1"]=>
string(13) "printr(hahaha"
["code 1"]=>
string(6) "448044"
["test.mijn"]=>
array(1) {
[0]=>
array(1) {
[2]=>
string(11) "test Roemer"
}
}
["test[mijn"]=>
string(14) "test 2e Roemer"
}

Whereas if you feed the same querystring to parse_str, you will get

array(2) {
["code_1"]=>
string(6) "448044"
["test_mijn"]=>
string(14) "test 2e Roemer"
}
up
35
shagshag
12 years ago
That's not says in the description but max_input_vars directive affects this function. If there are more input variables on the string than specified by this directive, an E_WARNING is issued, and further input variables are truncated from the request.
up
16
zweibieren at yahoo dot com
9 years ago
If the arr argument is provided, all its existing elements are removed.
up
17
alxcube at gmail dot com
8 years ago
if you need custom arg separator, you can use this function. it returns parsed query as associative array.

<?php

/**
* Parses http query string into an array
*
* @author Alxcube <alxcube@gmail.com>
*
* @param string $queryString String to parse
* @param string $argSeparator Query arguments separator
* @param integer $decType Decoding type
* @return array
*/
function http_parse_query($queryString, $argSeparator = '&', $decType = PHP_QUERY_RFC1738) {
$result = array();
$parts = explode($argSeparator, $queryString);

foreach (
$parts as $part) {
list(
$paramName, $paramValue) = explode('=', $part, 2);

switch (
$decType) {
case
PHP_QUERY_RFC3986:
$paramName = rawurldecode($paramName);
$paramValue = rawurldecode($paramValue);
break;

case
PHP_QUERY_RFC1738:
default:
$paramName = urldecode($paramName);
$paramValue = urldecode($paramValue);
break;
}


if (
preg_match_all('/\[([^\]]*)\]/m', $paramName, $matches)) {
$paramName = substr($paramName, 0, strpos($paramName, '['));
$keys = array_merge(array($paramName), $matches[1]);
} else {
$keys = array($paramName);
}

$target = &$result;

foreach (
$keys as $index) {
if (
$index === '') {
if (isset(
$target)) {
if (
is_array($target)) {
$intKeys = array_filter(array_keys($target), 'is_int');
$index = count($intKeys) ? max($intKeys)+1 : 0;
} else {
$target = array($target);
$index = 1;
}
} else {
$target = array();
$index = 0;
}
} elseif (isset(
$target[$index]) && !is_array($target[$index])) {
$target[$index] = array($target[$index]);
}

$target = &$target[$index];
}

if (
is_array($target)) {
$target[] = $paramValue;
} else {
$target = $paramValue;
}
}

return
$result;
}

?>
up
2
php at voodoolabs dot net
17 years ago
This is probably a better solution than below. The first line makes sure the file doesn't exist then the second line directs all requests to a script. No need to output a 200 header with this method either.

RewriteEngine On

RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
up
5
kent at nospam dot ioflux dot com
19 years ago
You may want to parse the query string into an array.

<?php
/**
* Similar to parse_str. Returns false if the query string or URL is empty. Because we're not parsing to
* variables but to array key entries, this function will handle ?[]=1&[]=2 "correctly."
*
* @return array Similar to the $_GET formatting that PHP does automagically.
* @param string $url A query string or URL
* @param boolean $qmark Find and strip out everything before the question mark in the string
*/
function parse_query_string($url, $qmark=true)
{
if (
$qmark) {
$pos = strpos($url, "?");
if (
$pos !== false) {
$url = substr($url, $pos + 1);
}
}
if (empty(
$url))
return
false;
$tokens = explode("&", $url);
$urlVars = array();
foreach (
$tokens as $token) {
$value = string_pair($token, "=", "");
if (
preg_match('/^([^\[]*)(\[.*\])$/', $token, $matches)) {
parse_query_string_array($urlVars, $matches[1], $matches[2], $value);
} else {
$urlVars[urldecode($token)] = urldecode($value);
}
}
return
$urlVars;
}

/**
* Utility function for parse_query_string. Given a result array, a starting key, and a set of keys formatted like "[a][b][c]"
* and the final value, updates the result array with the correct PHP array keys.
*
* @return void
* @param array $result A result array to populate from the query string
* @param string $k The starting key to populate in $result
* @param string $arrayKeys The key list to parse in the form "[][a][what%20ever]"
* @param string $value The value to place at the destination array key
*/
function parse_query_string_array(&$result, $k, $arrayKeys, $value)
{
if (!
preg_match_all('/\[([^\]]*)\]/', $arrayKeys, $matches))
return
$value;
if (!isset(
$result[$k])) {
$result[urldecode($k)] = array();
}
$temp =& $result[$k];
$last = urldecode(array_pop($matches[1]));
foreach (
$matches[1] as $k) {
$k = urldecode($k);
if (
$k === "") {
$temp[] = array();
$temp =& $temp[count($temp)-1];
} else if (!isset(
$temp[$k])) {
$temp[$k] = array();
$temp =& $temp[$k];
}
}
if (
$last === "") {
$temp[] = $value;
} else {
$temp[urldecode($last)] = $value;
}
}

/**
* Breaks a string into a pair for a common parsing function.
*
* The string passed in is truncated to the left half of the string pair, if any, and the right half, if anything, is returned.
*
* An example of using this would be:
* <code>
* $path = "Account.Balance";
* $field = string_pair($path);
*
* $path is "Account"
* $field is "Balance"
*
* $path = "Account";
* $field = string_pair($path);
*
* $path is "Account"
* $field is false
* </code>
*
* @return string The "right" portion of the string is returned if the delimiter is found.
* @param string $a A string to break into a pair. The "left" portion of the string is returned here if the delimiter is found.
* @param string $delim The characters used to delimit a string pair
* @param mixed $default The value to return if the delimiter is not found in the string
* @desc
*/
function string_pair(&$a, $delim='.', $default=false)
{
$n = strpos($a, $delim);
if (
$n === false)
return
$default;
$result = substr($a, $n+strlen($delim));
$a = substr($a, 0, $n);
return
$result;
}

?>
up
13
Olivier Mengué
18 years ago
Vladimir: the function is OK in how it deals with &amp;.
&amp; must only be used when outputing URLs in HTML/XML data.
You should ask yourself why you have &amp; in your URL when you give it to parse_str.
up
8
Tore Bj?lseth
19 years ago
As of PHP 5, you can do the exact opposite with http_build_query(). Just remember to use the optional array output parameter.

This is a very useful combination if you want to re-use a search string url, but also slightly modify it:

Example:
<?
$url1 = "action=search&interest[]=sports&interest[]=music&sort=id";
$str = parse_str($url1, $output);

// Modifying criteria:
$output['sort'] = "interest";

$url2 = http_build_query($output);

echo "<br>url1: ".$url1;
echo "<br>url2: ".$url2;
?>

Results in:
url1: action=search&interest[]=sports&interest[]=music&sort=id
url2: action=search&interest[0]=sports&interest[1]=music&sort=interest

(Array indexes are automatically created.)
up
4
tobsn at php dot net
16 years ago
just a heads up with the example above:
?var[]=123 - the [] has to be urlencoded.
var names and var values - both have to be urlencoded!
up
3
avi at amarcus dot com
19 years ago
If you are trying to preserve a complex array, the function serialize might be better than http_build_query or other methods of making a query string.
up
4
helpmepro1 at gmail dot com
16 years ago
<?
//by shimon doodkin

$url_form=url_to_form($url);
echo '<form action="'.$url_form['action'].'" method="get">';
echo $url_form['hidden'];
echo '<input name="otherfiled" type="text">';
echo '<input type="submit">';
echo '</form>';

function url_to_form($url)
{
$url=split('\?',$url,2);
$action=$url[0];
$hidden="";
if(isset($url[1]))
{
$pairs=split('&',$url[1]);
foreach($pairs as $pair)
{
$pair=split('=',$pair,2);
$name=$pair[0];
if(isset($pair[1]))
$value=$pair[1];
else
$value='';
$name=$name;
$value=htmlspecialchars($value);
if($name!='')
$hidden.='<hidden name="'.$name.'" value="'.$value.'">';
}
}
return array('action'=>$action,'hidden'=>$hidden);
}

?>
up
2
jgbreezer at gmail dot com
17 years ago
Vladimir Kornea wrote on 8 Sep 2006:
"This function is confused by ampersands (&) being encoded as HTML entities (&amp;)"

Well, it would be - it's not supposed to be passed html entities, that's a different encoding scheme. This function does correctly decode url encoded params for you though (with the rawurlencode rather than urlencode, ie '+' is translated to a space).
up
5
jrgns at jadeit dot co dot za
12 years ago
The array to be populated does not need to be defined before calling the function:

<?php
error_reporting
(E_ALL | E_STRICT);
parse_str('var=value', $array);
?>

This will not produce a notice.
up
3
PEPE_RIVAS at repixel dot net
18 years ago
CONVERT ANY FORMATTED STRING INTO VARIABLES

I developed a online payment solution for credit cards using a merchant, and this merchant returns me an answer of the state of the transaction like this:

estado=1,txnid=5555444-8454445-4455554,monto=100.00

to have all that data into variables could be fine for me! so i use str_replace(), the problem is this function recognizes each group of variables with the & character... and i have comma separated values... so i replace comma with &

<?php
$string
= "estado=1,txnid=5555444-8454445-4455554,monto=100.00";
$string = str_replace(",","&",$string);
parse_str($string);
echo
$monto; // outputs 100.00
?>