scratch – Blame information for rev

Subversion Repositories:
Rev:
Rev Author Line No. Line
87 office 1 <?php
2  
3 namespace GuzzleHttp;
4  
5 /**
6 * Manages query string variables and can aggregate them into a string
7 */
8 class Query extends Collection
9 {
10 const RFC3986 = 'RFC3986';
11 const RFC1738 = 'RFC1738';
12  
13 /** @var bool URL encode fields and values */
14 private $encoding = self::RFC3986;
15  
16 /** @var callable */
17 private $aggregator;
18  
19 /**
20 * Parse a query string into a Query object
21 *
22 * $urlEncoding is used to control how the query string is parsed and how
23 * it is ultimately serialized. The value can be set to one of the
24 * following:
25 *
26 * - true: (default) Parse query strings using RFC 3986 while still
27 * converting "+" to " ".
28 * - false: Disables URL decoding of the input string and URL encoding when
29 * the query string is serialized.
30 * - 'RFC3986': Use RFC 3986 URL encoding/decoding
31 * - 'RFC1738': Use RFC 1738 URL encoding/decoding
32 *
33 * @param string $query Query string to parse
34 * @param bool|string $urlEncoding Controls how the input string is decoded
35 * and encoded.
36 * @return self
37 */
38 public static function fromString($query, $urlEncoding = true)
39 {
40 static $qp;
41 if (!$qp) {
42 $qp = new QueryParser();
43 }
44  
45 $q = new static();
46  
47 if ($urlEncoding !== true) {
48 $q->setEncodingType($urlEncoding);
49 }
50  
51 $qp->parseInto($q, $query, $urlEncoding);
52  
53 return $q;
54 }
55  
56 /**
57 * Convert the query string parameters to a query string string
58 *
59 * @return string
60 */
61 public function __toString()
62 {
63 if (!$this->data) {
64 return '';
65 }
66  
67 // The default aggregator is statically cached
68 static $defaultAggregator;
69  
70 if (!$this->aggregator) {
71 if (!$defaultAggregator) {
72 $defaultAggregator = self::phpAggregator();
73 }
74 $this->aggregator = $defaultAggregator;
75 }
76  
77 $result = '';
78 $aggregator = $this->aggregator;
79  
80 foreach ($aggregator($this->data) as $key => $values) {
81 foreach ($values as $value) {
82 if ($result) {
83 $result .= '&';
84 }
85 if ($this->encoding == self::RFC1738) {
86 $result .= urlencode($key);
87 if ($value !== null) {
88 $result .= '=' . urlencode($value);
89 }
90 } elseif ($this->encoding == self::RFC3986) {
91 $result .= rawurlencode($key);
92 if ($value !== null) {
93 $result .= '=' . rawurlencode($value);
94 }
95 } else {
96 $result .= $key;
97 if ($value !== null) {
98 $result .= '=' . $value;
99 }
100 }
101 }
102 }
103  
104 return $result;
105 }
106  
107 /**
108 * Controls how multi-valued query string parameters are aggregated into a
109 * string.
110 *
111 * $query->setAggregator($query::duplicateAggregator());
112 *
113 * @param callable $aggregator Callable used to convert a deeply nested
114 * array of query string variables into a flattened array of key value
115 * pairs. The callable accepts an array of query data and returns a
116 * flattened array of key value pairs where each value is an array of
117 * strings.
118 *
119 * @return self
120 */
121 public function setAggregator(callable $aggregator)
122 {
123 $this->aggregator = $aggregator;
124  
125 return $this;
126 }
127  
128 /**
129 * Specify how values are URL encoded
130 *
131 * @param string|bool $type One of 'RFC1738', 'RFC3986', or false to disable encoding
132 *
133 * @return self
134 * @throws \InvalidArgumentException
135 */
136 public function setEncodingType($type)
137 {
138 if ($type === false || $type === self::RFC1738 || $type === self::RFC3986) {
139 $this->encoding = $type;
140 } else {
141 throw new \InvalidArgumentException('Invalid URL encoding type');
142 }
143  
144 return $this;
145 }
146  
147 /**
148 * Query string aggregator that does not aggregate nested query string
149 * values and allows duplicates in the resulting array.
150 *
151 * Example: http://test.com?q=1&q=2
152 *
153 * @return callable
154 */
155 public static function duplicateAggregator()
156 {
157 return function (array $data) {
158 return self::walkQuery($data, '', function ($key, $prefix) {
159 return is_int($key) ? $prefix : "{$prefix}[{$key}]";
160 });
161 };
162 }
163  
164 /**
165 * Aggregates nested query string variables using the same technique as
166 * ``http_build_query()``.
167 *
168 * @param bool $numericIndices Pass false to not include numeric indices
169 * when multi-values query string parameters are present.
170 *
171 * @return callable
172 */
173 public static function phpAggregator($numericIndices = true)
174 {
175 return function (array $data) use ($numericIndices) {
176 return self::walkQuery(
177 $data,
178 '',
179 function ($key, $prefix) use ($numericIndices) {
180 return !$numericIndices && is_int($key)
181 ? "{$prefix}[]"
182 : "{$prefix}[{$key}]";
183 }
184 );
185 };
186 }
187  
188 /**
189 * Easily create query aggregation functions by providing a key prefix
190 * function to this query string array walker.
191 *
192 * @param array $query Query string to walk
193 * @param string $keyPrefix Key prefix (start with '')
194 * @param callable $prefixer Function used to create a key prefix
195 *
196 * @return array
197 */
198 public static function walkQuery(array $query, $keyPrefix, callable $prefixer)
199 {
200 $result = [];
201 foreach ($query as $key => $value) {
202 if ($keyPrefix) {
203 $key = $prefixer($key, $keyPrefix);
204 }
205 if (is_array($value)) {
206 $result += self::walkQuery($value, $key, $prefixer);
207 } elseif (isset($result[$key])) {
208 $result[$key][] = $value;
209 } else {
210 $result[$key] = array($value);
211 }
212 }
213  
214 return $result;
215 }
216 }