~tcuthbert/wordpress/openstack-objectstorage

« back to all changes in this revision

Viewing changes to vendor/guzzlehttp/guzzle/src/QueryParser.php

  • Committer: Jacek Nykis
  • Date: 2015-02-11 15:35:31 UTC
  • Revision ID: jacek.nykis@canonical.com-20150211153531-hmy6zi0ov2qfkl0b
Initial commit

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
}