1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/* |
4
|
|
|
* This file is part of the Ariadne Component Library. |
5
|
|
|
* |
6
|
|
|
* (c) Muze <[email protected]> |
7
|
|
|
* |
8
|
|
|
* For the full copyright and license information, please view the LICENSE |
9
|
|
|
* file that was distributed with this source code. |
10
|
|
|
*/ |
11
|
|
|
|
12
|
|
|
namespace arc\url; |
13
|
|
|
|
14
|
|
|
/** |
15
|
|
|
* Query parses any valid url query part and generates array accessible values for it |
16
|
|
|
* - you can use the same name multiple times -- ?name=value1&name=value2 |
17
|
|
|
* this will result in name becoming an array, similarly arrays will not be encoded with trailing [] behind the name |
18
|
|
|
* - you can use names without values -- ?name&name2&name3 |
19
|
|
|
* - names may include any valid character -- ?valid+name |
20
|
|
|
* |
21
|
|
|
*/ |
22
|
|
|
class Query extends \ArrayObject implements QueryInterface |
23
|
|
|
{ |
24
|
14 |
|
public function __construct($query = '') |
25
|
|
|
{ |
26
|
14 |
|
parent::__construct( $this->parse( $query ), \ArrayObject::ARRAY_AS_PROPS ); |
27
|
14 |
|
} |
28
|
|
|
|
29
|
12 |
|
public function __toString() |
30
|
|
|
{ |
31
|
12 |
|
return str_replace( ['%7E', '%20'], ['~', '+'], // ~ and + are often unnecesarily encoded |
32
|
12 |
|
$this->compile( (array) $this ) |
33
|
|
|
); |
34
|
|
|
} |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* Import a query string or an array of key => value pairs into the UrlQuery. |
38
|
|
|
* |
39
|
|
|
* Usage: |
40
|
|
|
* $query->import( 'foo=bar&bar=1' ); |
41
|
|
|
* $query->import( array( 'foo' => 'bar', 'bar' => 1 ) ); |
42
|
|
|
* |
43
|
|
|
* @param string|array $values query string or array of values to import into this query |
44
|
|
|
*/ |
45
|
14 |
|
public function import($values) |
46
|
|
|
{ |
47
|
14 |
|
$this->exchangeArray( $this->parse( $values ) + $this->getArrayCopy() ); |
48
|
|
|
|
49
|
14 |
|
return $this; |
50
|
|
|
} |
51
|
|
|
|
52
|
|
|
/** |
53
|
|
|
* Removes all current query parameters. |
54
|
|
|
* @return $this |
55
|
|
|
*/ |
56
|
|
|
public function reset() |
57
|
|
|
{ |
58
|
|
|
$this->exchangeArray( [] ); |
59
|
|
|
|
60
|
|
|
return $this; |
61
|
|
|
} |
62
|
|
|
|
63
|
|
|
// === \arc\KeyValueStoreInterface === |
64
|
|
|
|
65
|
|
|
/** |
66
|
|
|
* @param string $name name of the query parameter |
67
|
|
|
* @return mixed The named query parameter |
68
|
|
|
*/ |
69
|
|
|
public function getvar($name) |
70
|
|
|
{ |
71
|
|
|
return $this->offsetGet($name); |
72
|
|
|
} |
73
|
|
|
|
74
|
|
|
/** |
75
|
|
|
* @param string $name name for the query parameter |
76
|
|
|
* @param mixed $value value of the query parameter |
77
|
|
|
*/ |
78
|
|
|
public function putvar($name, $value) |
79
|
|
|
{ |
80
|
|
|
$this->offsetSet($name, $value); |
81
|
|
|
} |
82
|
|
|
|
83
|
2 |
|
protected function compile($values) |
84
|
|
|
{ |
85
|
2 |
|
$result = array(); |
86
|
2 |
|
foreach ($values as $name => $value) { |
87
|
2 |
|
$result[] = $this->encodeValue( $name, $value ); |
88
|
|
|
} |
89
|
|
|
|
90
|
2 |
|
return implode( '&', $result ); |
91
|
|
|
} |
92
|
|
|
|
93
|
14 |
|
protected function parse($values) |
94
|
|
|
{ |
95
|
14 |
|
$result = array(); |
96
|
14 |
|
if (is_array( $values ) || ( $values instanceof \Traversable )) { |
97
|
|
|
foreach ($values as $name => $value) { |
98
|
|
|
$result[$name] = $value; |
99
|
|
|
} |
100
|
14 |
|
} elseif (is_string( $values )) { |
101
|
14 |
|
$result = $this->parseQueryString( $values ); |
102
|
|
|
} |
103
|
|
|
|
104
|
14 |
|
return $result; |
105
|
|
|
} |
106
|
|
|
|
107
|
3 |
|
protected function parseQueryString($queryString) |
108
|
|
|
{ |
109
|
3 |
|
$result = array(); |
110
|
3 |
|
if ($queryString) { |
111
|
3 |
|
$values = preg_split( '/[\&\;]/', $queryString ); |
112
|
3 |
|
foreach ($values as $queryStringEntry) { |
113
|
3 |
|
list( $name, $value ) = $this->parseQueryStringEntry( $queryStringEntry ); |
114
|
3 |
|
if (!isset($value)) { |
115
|
|
|
// no '=' in query entry => ?name&... |
116
|
2 |
|
$result[] = $name; |
117
|
2 |
|
} elseif (!isset( $result[$name] )) { |
118
|
|
|
// new entry => ?name=1&... |
|
|
|
|
119
|
2 |
|
$result[ $name ] = $value; |
120
|
1 |
|
} elseif (!is_array( $result[$name] )) { |
121
|
|
|
// entry with same name exists already but is not an array yet => ?name=1&name=2&... |
122
|
1 |
|
$result[ $name ] = array( $result[$name], $value ); |
123
|
|
|
} else { |
124
|
|
|
// entry with same name exists and is an array => ?name=1&name=2&name=3&... |
125
|
3 |
|
$result[ $name ][] = $value; |
126
|
|
|
} |
127
|
|
|
} |
128
|
|
|
} |
129
|
|
|
|
130
|
3 |
|
return $result; |
131
|
|
|
} |
132
|
|
|
|
133
|
3 |
|
private function parseQueryStringEntry($queryStringEntry) |
134
|
|
|
{ |
135
|
3 |
|
$result = explode( '=', $queryStringEntry, 2 ) + array( 1 => null ); // value may be null if no '=' is found in the query string |
136
|
3 |
|
foreach ($result as $key => $value) { |
137
|
3 |
|
if (isset($value)) { |
138
|
3 |
|
$value = urldecode( $value ); |
139
|
|
|
} |
140
|
3 |
|
$result[$key] = $value; |
141
|
|
|
} |
142
|
|
|
|
143
|
3 |
|
return $result; |
144
|
|
|
} |
145
|
|
|
|
146
|
2 |
|
private function encodeValue($name, $value = null) |
147
|
|
|
{ |
148
|
2 |
|
if (is_array( $value )) { |
149
|
1 |
|
$result = array(); |
150
|
1 |
|
foreach ($value as $val) { |
151
|
1 |
|
$result[] = $this->encodeValue( $name, $val ); |
152
|
|
|
} |
153
|
|
|
|
154
|
1 |
|
return implode( '&', $result ); |
155
|
|
|
} else { |
156
|
2 |
|
return ( is_numeric( $name ) |
157
|
2 |
|
? RawUrlEncode( $value ) |
158
|
2 |
|
: RawUrlEncode( $name ) . ( isset( $value ) ? '=' . RawUrlEncode( (string) $value ) : '' ) |
159
|
|
|
); |
160
|
|
|
} |
161
|
|
|
} |
162
|
|
|
} |
163
|
|
|
|
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.
The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.
This check looks for comments that seem to be mostly valid code and reports them.