scratch – Blame information for rev 87

Subversion Repositories:
Rev:
Rev Author Line No. Line
87 office 1 <?php
2 namespace GuzzleHttp;
3  
4 /**
5 * Parses query strings into a Query object.
6 *
7 * While parsing, the parser will attempt to determine the most appropriate
8 * query string aggregator to use when serializing the parsed query string
9 * object back into a string. The hope is that parsing then serializing a
10 * query string should be a lossless operation.
11 *
12 * @internal Use Query::fromString()
13 */
14 class QueryParser
15 {
16 private $duplicates;
17 private $numericIndices;
18  
19 /**
20 * Parse a query string into a Query object.
21 *
22 * @param Query $query Query object to populate
23 * @param string $str Query string to parse
24 * @param bool|string $urlEncoding How the query string is encoded
25 */
26 public function parseInto(Query $query, $str, $urlEncoding = true)
27 {
28 if ($str === '') {
29 return;
30 }
31  
32 $result = [];
33 $this->duplicates = false;
34 $this->numericIndices = true;
35 $decoder = self::getDecoder($urlEncoding);
36  
37 foreach (explode('&', $str) as $kvp) {
38  
39 $parts = explode('=', $kvp, 2);
40 $key = $decoder($parts[0]);
41 $value = isset($parts[1]) ? $decoder($parts[1]) : null;
42  
43 // Special handling needs to be taken for PHP nested array syntax
44 if (strpos($key, '[') !== false) {
45 $this->parsePhpValue($key, $value, $result);
46 continue;
47 }
48  
49 if (!isset($result[$key])) {
50 $result[$key] = $value;
51 } else {
52 $this->duplicates = true;
53 if (!is_array($result[$key])) {
54 $result[$key] = [$result[$key]];
55 }
56 $result[$key][] = $value;
57 }
58 }
59  
60 $query->replace($result);
61  
62 if (!$this->numericIndices) {
63 $query->setAggregator(Query::phpAggregator(false));
64 } elseif ($this->duplicates) {
65 $query->setAggregator(Query::duplicateAggregator());
66 }
67 }
68  
69 /**
70 * Returns a callable that is used to URL decode query keys and values.
71 *
72 * @param string|bool $type One of true, false, RFC3986, and RFC1738
73 *
74 * @return callable|string
75 */
76 private static function getDecoder($type)
77 {
78 if ($type === true) {
79 return function ($value) {
80 return rawurldecode(str_replace('+', ' ', $value));
81 };
82 } elseif ($type == Query::RFC3986) {
83 return 'rawurldecode';
84 } elseif ($type == Query::RFC1738) {
85 return 'urldecode';
86 } else {
87 return function ($str) { return $str; };
88 }
89 }
90  
91 /**
92 * Parses a PHP style key value pair.
93 *
94 * @param string $key Key to parse (e.g., "foo[a][b]")
95 * @param string|null $value Value to set
96 * @param array $result Result to modify by reference
97 */
98 private function parsePhpValue($key, $value, array &$result)
99 {
100 $node =& $result;
101 $keyBuffer = '';
102  
103 for ($i = 0, $t = strlen($key); $i < $t; $i++) {
104 switch ($key[$i]) {
105 case '[':
106 if ($keyBuffer) {
107 $this->prepareNode($node, $keyBuffer);
108 $node =& $node[$keyBuffer];
109 $keyBuffer = '';
110 }
111 break;
112 case ']':
113 $k = $this->cleanKey($node, $keyBuffer);
114 $this->prepareNode($node, $k);
115 $node =& $node[$k];
116 $keyBuffer = '';
117 break;
118 default:
119 $keyBuffer .= $key[$i];
120 break;
121 }
122 }
123  
124 if (isset($node)) {
125 $this->duplicates = true;
126 $node[] = $value;
127 } else {
128 $node = $value;
129 }
130 }
131  
132 /**
133 * Prepares a value in the array at the given key.
134 *
135 * If the key already exists, the key value is converted into an array.
136 *
137 * @param array $node Result node to modify
138 * @param string $key Key to add or modify in the node
139 */
140 private function prepareNode(&$node, $key)
141 {
142 if (!isset($node[$key])) {
143 $node[$key] = null;
144 } elseif (!is_array($node[$key])) {
145 $node[$key] = [$node[$key]];
146 }
147 }
148  
149 /**
150 * Returns the appropriate key based on the node and key.
151 */
152 private function cleanKey($node, $key)
153 {
154 if ($key === '') {
155 $key = $node ? (string) count($node) : 0;
156 // Found a [] key, so track this to ensure that we disable numeric
157 // indexing of keys in the resolved query aggregator.
158 $this->numericIndices = false;
159 }
160  
161 return $key;
162 }
163 }