Page MenuHomec4science

PhutilQueryStringParser.php
No OneTemporary

File Metadata

Created
Thu, May 23, 10:20

PhutilQueryStringParser.php

<?php
/**
* Utilities for parsing HTTP query strings.
*
* The builtin functions in PHP (notably, `parse_str()` and automatic parsing
* prior to request handling) are not suitable in the general case because they
* silently convert some characters in parameter names into underscores.
*
* For example, if you call `parse_str()` with input like this:
*
* x.y=z
*
* ...the output is this:
*
* array(
* 'x_y' => 'z',
* );
*
* ...with the `.` replaced with an underscore, `_`. Other characters converted
* in this way include space and unmatched opening brackets.
*
* Broadly, this is part of the terrible legacy of `register_globals`. Since
* we'd like to be able to parse all valid query strings without destroying any
* data, this class implements a less-encumbered parser.
*/
final class PhutilQueryStringParser {
/**
* Parses a query string into a dictionary, applying PHP rules for handling
* array nomenclature (like `a[]=1`) in parameter names.
*
* For a more basic parse, see @{method:parseQueryStringToPairList}.
*
* @param string Query string.
* @return map<string, wild> Parsed dictionary.
*/
public function parseQueryString($query_string) {
$result = array();
$list = $this->parseQueryStringToPairList($query_string);
foreach ($list as $parts) {
list($key, $value) = $parts;
if (!strlen($key)) {
continue;
}
$this->parseQueryKeyToArr($key, $value, $result);
}
return $result;
}
/**
* Parses a query string into a basic list of pairs, without handling any
* array information in the keys. For example:
*
* a[]=1&a[]=2
*
* ...will parse into:
*
* array(
* array('a[]', '1'),
* array('a[]', '2'),
* );
*
* Use @{method:parseQueryString} to produce a more sophisticated parse which
* applies array rules and returns a dictionary.
*
* @param string Query string.
* @return list<pair<string, string>> List of parsed parameters.
*/
public function parseQueryStringToPairList($query_string) {
$list = array();
if (!strlen($query_string)) {
return $list;
}
$pairs = explode('&', $query_string);
foreach ($pairs as $pair) {
if (!strlen($pair)) {
continue;
}
$parts = explode('=', $pair, 2);
if (count($parts) < 2) {
$parts[] = '';
}
$list[] = array(
urldecode($parts[0]),
urldecode($parts[1]),
);
}
return $list;
}
/**
* Treats the key as a flat query that potentially has square brackets. If
* there are square brackets we parse them into an array.
*
* Example input:
* $key = "email[0]";
* $val = "my@example.com";
*
* Example output:
* array("email" => array(0 => "my@example.com"));
*
* @param string $key
* @param string $val
* @param array $input_arr
*/
private function parseQueryKeyToArr($key, $val, array &$input_arr) {
if (preg_match('/^[^\[\]]+(?:\[[^\[\]]*\])+$/', $key)) {
$key_pieces = preg_split('/\]?\[/', rtrim($key, ']'));
if ($key_pieces) {
$cursor = &$input_arr;
foreach ($key_pieces as $piece) {
if (strlen($piece)) {
if (empty($cursor[$piece]) || !is_array($cursor[$piece])) {
$cursor[$piece] = array();
}
} else {
$cursor[] = array();
$piece = last_key($cursor);
}
$cursor = &$cursor[$piece];
}
$cursor = $val;
unset($cursor);
}
} else {
$input_arr[$key] = $val;
}
}
}

Event Timeline