summaryrefslogtreecommitdiff
path: root/vendor/guzzle/guzzle/src/Guzzle/Http/QueryString.php
blob: 38a26407382a3cdf31c443c729f1c99a461c2647 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
<?php

namespace Guzzle\Http;

use Guzzle\Common\Collection;
use Guzzle\Common\Exception\RuntimeException;
use Guzzle\Http\QueryAggregator\DuplicateAggregator;
use Guzzle\Http\QueryAggregator\QueryAggregatorInterface;
use Guzzle\Http\QueryAggregator\PhpAggregator;

/**
 * Query string object to handle managing query string parameters and aggregating those parameters together as a string.
 */
class QueryString extends Collection
{
    /** @var string Used to URL encode with rawurlencode */
    const RFC_3986 = 'RFC 3986';

    /** @var string Used to encode with urlencode */
    const FORM_URLENCODED = 'application/x-www-form-urlencoded';

    /** @var string Constant used to create blank query string values (e.g. ?foo) */
    const BLANK = "_guzzle_blank_";

    /** @var string The query string field separator (e.g. '&') */
    protected $fieldSeparator = '&';

    /** @var string The query string value separator (e.g. '=') */
    protected $valueSeparator = '=';

    /** @var bool URL encode fields and values */
    protected $urlEncode = 'RFC 3986';

    /** @var QueryAggregatorInterface */
    protected $aggregator;

    /** @var array Cached PHP aggregator */
    private static $defaultAggregator = null;

    /**
     * Parse a query string into a QueryString object
     *
     * @param string $query Query string to parse
     *
     * @return self
     */
    public static function fromString($query)
    {
        $q = new static();
        if ($query === '') {
            return $q;
        }

        $foundDuplicates = $foundPhpStyle = false;

        foreach (explode('&', $query) as $kvp) {
            $parts = explode('=', $kvp, 2);
            $key = rawurldecode($parts[0]);
            if ($paramIsPhpStyleArray = substr($key, -2) == '[]') {
                $foundPhpStyle = true;
                $key = substr($key, 0, -2);
            }
            if (isset($parts[1])) {
                $value = rawurldecode(str_replace('+', '%20', $parts[1]));
                if (isset($q[$key])) {
                    $q->add($key, $value);
                    $foundDuplicates = true;
                } elseif ($paramIsPhpStyleArray) {
                    $q[$key] = array($value);
                } else {
                    $q[$key] = $value;
                }
            } else {
                // Uses false by default to represent keys with no trailing "=" sign.
                $q->add($key, false);
            }
        }

        // Use the duplicate aggregator if duplicates were found and not using PHP style arrays
        if ($foundDuplicates && !$foundPhpStyle) {
            $q->setAggregator(new DuplicateAggregator());
        }

        return $q;
    }

    /**
     * Convert the query string parameters to a query string string
     *
     * @return string
     * @throws RuntimeException
     */
    public function __toString()
    {
        if (!$this->data) {
            return '';
        }

        $queryList = array();
        foreach ($this->prepareData($this->data) as $name => $value) {
            $queryList[] = $this->convertKvp($name, $value);
        }

        return implode($this->fieldSeparator, $queryList);
    }

    /**
     * Get the query string field separator
     *
     * @return string
     */
    public function getFieldSeparator()
    {
        return $this->fieldSeparator;
    }

    /**
     * Get the query string value separator
     *
     * @return string
     */
    public function getValueSeparator()
    {
        return $this->valueSeparator;
    }

    /**
     * Returns the type of URL encoding used by the query string
     *
     * One of: false, "RFC 3986", or "application/x-www-form-urlencoded"
     *
     * @return bool|string
     */
    public function getUrlEncoding()
    {
        return $this->urlEncode;
    }

    /**
     * Returns true or false if using URL encoding
     *
     * @return bool
     */
    public function isUrlEncoding()
    {
        return $this->urlEncode !== false;
    }

    /**
     * Provide a function for combining multi-valued query string parameters into a single or multiple fields
     *
     * @param null|QueryAggregatorInterface $aggregator Pass in a QueryAggregatorInterface object to handle converting
     *                                                  deeply nested query string variables into a flattened array.
     *                                                  Pass null to use the default PHP style aggregator. For legacy
     *                                                  reasons, this function accepts a callable that must accepts a
     *                                                  $key, $value, and query object.
     * @return self
     * @see \Guzzle\Http\QueryString::aggregateUsingComma()
     */
    public function setAggregator(QueryAggregatorInterface $aggregator = null)
    {
        // Use the default aggregator if none was set
        if (!$aggregator) {
            if (!self::$defaultAggregator) {
                self::$defaultAggregator = new PhpAggregator();
            }
            $aggregator = self::$defaultAggregator;
        }

        $this->aggregator = $aggregator;

        return $this;
    }

    /**
     * Set whether or not field names and values should be rawurlencoded
     *
     * @param bool|string $encode Set to TRUE to use RFC 3986 encoding (rawurlencode), false to disable encoding, or
     *                            form_urlencoding to use application/x-www-form-urlencoded encoding (urlencode)
     * @return self
     */
    public function useUrlEncoding($encode)
    {
        $this->urlEncode = ($encode === true) ? self::RFC_3986 : $encode;

        return $this;
    }

    /**
     * Set the query string separator
     *
     * @param string $separator The query string separator that will separate fields
     *
     * @return self
     */
    public function setFieldSeparator($separator)
    {
        $this->fieldSeparator = $separator;

        return $this;
    }

    /**
     * Set the query string value separator
     *
     * @param string $separator The query string separator that will separate values from fields
     *
     * @return self
     */
    public function setValueSeparator($separator)
    {
        $this->valueSeparator = $separator;

        return $this;
    }

    /**
     * Returns an array of url encoded field names and values
     *
     * @return array
     */
    public function urlEncode()
    {
        return $this->prepareData($this->data);
    }

    /**
     * URL encodes a value based on the url encoding type of the query string object
     *
     * @param string $value Value to encode
     *
     * @return string
     */
    public function encodeValue($value)
    {
        if ($this->urlEncode == self::RFC_3986) {
            return rawurlencode($value);
        } elseif ($this->urlEncode == self::FORM_URLENCODED) {
            return urlencode($value);
        } else {
            return (string) $value;
        }
    }

    /**
     * Url encode parameter data and convert nested query strings into a flattened hash.
     *
     * @param array $data The data to encode
     *
     * @return array Returns an array of encoded values and keys
     */
    protected function prepareData(array $data)
    {
        // If no aggregator is present then set the default
        if (!$this->aggregator) {
            $this->setAggregator(null);
        }

        $temp = array();
        foreach ($data as $key => $value) {
            if ($value === false || $value === null) {
                // False and null will not include the "=". Use an empty string to include the "=".
                $temp[$this->encodeValue($key)] = $value;
            } elseif (is_array($value)) {
                $temp = array_merge($temp, $this->aggregator->aggregate($key, $value, $this));
            } else {
                $temp[$this->encodeValue($key)] = $this->encodeValue($value);
            }
        }

        return $temp;
    }

    /**
     * Converts a key value pair that can contain strings, nulls, false, or arrays
     * into a single string.
     *
     * @param string $name  Name of the field
     * @param mixed  $value Value of the field
     * @return string
     */
    private function convertKvp($name, $value)
    {
        if ($value === self::BLANK || $value === null || $value === false) {
            return $name;
        } elseif (!is_array($value)) {
            return $name . $this->valueSeparator . $value;
        }

        $result = '';
        foreach ($value as $v) {
            $result .= $this->convertKvp($name, $v) . $this->fieldSeparator;
        }

        return rtrim($result, $this->fieldSeparator);
    }
}