Skip to content

Commit dbc6730

Browse files
committed
Url: internally stores query as an array, added getQueryParameters()
1 parent 332d6b1 commit dbc6730

File tree

8 files changed

+100
-33
lines changed

8 files changed

+100
-33
lines changed

src/Http/Helpers.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,4 +79,17 @@ public static function removeDuplicateCookies()
7979
}
8080
}
8181

82+
83+
/**
84+
* @internal
85+
*/
86+
public static function stripSlashes($arr)
87+
{
88+
$res = array();
89+
foreach ($arr as $k => $v) {
90+
$res[stripslashes($k)] = is_array($v) ? self::stripSlashes($v) : stripslashes($v);
91+
}
92+
return $res;
93+
}
94+
8295
}

src/Http/Request.php

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,7 @@ public function __construct(UrlScript $url, $query = NULL, $post = NULL, $files
6666
$headers = NULL, $method = NULL, $remoteAddress = NULL, $remoteHost = NULL, $rawBodyCallback = NULL)
6767
{
6868
$this->url = $url;
69-
if ($query === NULL) {
70-
parse_str($url->getQuery(), $this->query);
71-
} else {
72-
$this->query = (array) $query;
73-
}
69+
$this->query = $query === NULL ? $url->getQueryParameters() : (array) $query;
7470
$this->post = (array) $post;
7571
$this->files = (array) $files;
7672
$this->cookies = (array) $cookies;

src/Http/RequestFactory.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ public function createHttpRequest()
100100
// GET, POST, COOKIE
101101
$useFilter = (!in_array(ini_get('filter.default'), array('', 'unsafe_raw')) || ini_get('filter.default_flags'));
102102

103-
parse_str($url->getQuery(), $query);
103+
$query = $url->getQueryParameters();
104104
$post = $useFilter ? filter_input_array(INPUT_POST, FILTER_UNSAFE_RAW) : (empty($_POST) ? array() : $_POST);
105105
$cookies = $useFilter ? filter_input_array(INPUT_COOKIE, FILTER_UNSAFE_RAW) : (empty($_COOKIE) ? array() : $_COOKIE);
106106

src/Http/Url.php

Lines changed: 39 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
* @property-read string $basePath
4646
* @property-read string $baseUrl
4747
* @property-read string $relativeUrl
48+
* @property-read array $queryParameters
4849
*/
4950
class Url extends Nette\Object
5051
{
@@ -75,8 +76,8 @@ class Url extends Nette\Object
7576
/** @var string */
7677
private $path = '';
7778

78-
/** @var string */
79-
private $query = '';
79+
/** @var array */
80+
private $query = array();
8081

8182
/** @var string */
8283
private $fragment = '';
@@ -101,7 +102,7 @@ public function __construct($url = NULL)
101102
$this->user = isset($p['user']) ? rawurldecode($p['user']) : '';
102103
$this->pass = isset($p['pass']) ? rawurldecode($p['pass']) : '';
103104
$this->setPath(isset($p['path']) ? $p['path'] : '');
104-
$this->query = isset($p['query']) ? self::unescape($p['query'], '%&;=+ ') : '';
105+
$this->setQuery(isset($p['query']) ? $p['query'] : array());
105106
$this->fragment = isset($p['fragment']) ? rawurldecode($p['fragment']) : '';
106107

107108
} elseif ($url instanceof self) {
@@ -254,7 +255,7 @@ public function getPath()
254255
*/
255256
public function setQuery($value)
256257
{
257-
$this->query = (string) (is_array($value) ? http_build_query($value, '', '&') : $value);
258+
$this->query = is_array($value) ? $value : self::parseQuery($value);
258259
return $this;
259260
}
260261

@@ -266,8 +267,9 @@ public function setQuery($value)
266267
*/
267268
public function appendQuery($value)
268269
{
269-
$value = (string) (is_array($value) ? http_build_query($value, '', '&') : $value);
270-
$this->query .= ($this->query === '' || $value === '') ? $value : '&' . $value;
270+
$this->query = is_array($value)
271+
? $value + $this->query
272+
: self::parseQuery($this->getQuery() . '&' . $value);
271273
return $this;
272274
}
273275

@@ -277,6 +279,18 @@ public function appendQuery($value)
277279
* @return string
278280
*/
279281
public function getQuery()
282+
{
283+
if (PHP_VERSION < 50400) {
284+
return str_replace('+', '%20', http_build_query($this->query, '', '&'));
285+
}
286+
return http_build_query($this->query, '', '&', PHP_QUERY_RFC3986);
287+
}
288+
289+
290+
/**
291+
* @return array
292+
*/
293+
public function getQueryParameters()
280294
{
281295
return $this->query;
282296
}
@@ -289,8 +303,7 @@ public function getQuery()
289303
*/
290304
public function getQueryParameter($name, $default = NULL)
291305
{
292-
parse_str($this->query, $params);
293-
return isset($params[$name]) ? $params[$name] : $default;
306+
return isset($this->query[$name]) ? $this->query[$name] : $default;
294307
}
295308

296309

@@ -301,13 +314,7 @@ public function getQueryParameter($name, $default = NULL)
301314
*/
302315
public function setQueryParameter($name, $value)
303316
{
304-
parse_str($this->query, $params);
305-
if ($value === NULL) {
306-
unset($params[$name]);
307-
} else {
308-
$params[$name] = $value;
309-
}
310-
$this->setQuery($params);
317+
$this->query[$name] = $value;
311318
return $this;
312319
}
313320

@@ -341,7 +348,7 @@ public function getFragment()
341348
public function getAbsoluteUrl()
342349
{
343350
return $this->getHostUrl() . $this->path
344-
. ($this->query === '' ? '' : '?' . $this->query)
351+
. ($this->query ? '?' . $this->getQuery() : '')
345352
. ($this->fragment === '' ? '' : '#' . $this->fragment);
346353
}
347354

@@ -414,9 +421,9 @@ public function getRelativeUrl()
414421
public function isEqual($url)
415422
{
416423
$url = new self($url);
417-
parse_str($url->query, $query);
424+
$query = $url->query;
418425
sort($query);
419-
parse_str($this->query, $query2);
426+
$query2 = $this->query;
420427
sort($query2);
421428
$http = in_array($this->scheme, array('http', 'https'), TRUE);
422429
return $url->scheme === $this->scheme
@@ -442,7 +449,6 @@ function($m) { return rawurlencode($m[0]); },
442449
self::unescape($this->path, '%/')
443450
);
444451
$this->host = strtolower($this->host);
445-
$this->query = self::unescape(strtr($this->query, '+', ' '), '%&;=+');
446452
return $this;
447453
}
448454

@@ -477,4 +483,18 @@ function($m) { return '%25' . strtoupper($m[1]); },
477483
return rawurldecode($s);
478484
}
479485

486+
487+
/**
488+
* Parses query string.
489+
* @return array
490+
*/
491+
public static function parseQuery($s)
492+
{
493+
parse_str($s, $res);
494+
if (get_magic_quotes_gpc()) { // for PHP 5.3
495+
$res = Helpers::stripSlashes($res);
496+
}
497+
return $res;
498+
}
499+
480500
}

tests/Http/Request.request.phpt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,16 +39,16 @@ test(function() {
3939
Assert::same( 'nette.org', $request->getUrl()->host );
4040
Assert::same( 8080, $request->getUrl()->port );
4141
Assert::same( '/file.php', $request->getUrl()->path );
42-
Assert::same( 'x param=val.&pa%%72am=val2&param3=v%20a%26l%3Du%2Be', $request->getUrl()->query );
42+
Assert::same( 'x_param=val.&pa%25ram=val2&param3=v%20a%26l%3Du%2Be', $request->getUrl()->query );
4343
Assert::same( '', $request->getUrl()->fragment );
4444
Assert::same( 'val.', $request->getQuery('x_param') );
4545
Assert::same( 'val2', $request->getQuery('pa%ram') );
4646
Assert::same( 'nette.org:8080', $request->getUrl()->authority );
4747
Assert::same( 'https://nette.org:8080', $request->getUrl()->hostUrl );
4848
Assert::same( 'https://nette.org:8080/', $request->getUrl()->baseUrl );
4949
Assert::same( '/', $request->getUrl()->basePath );
50-
Assert::same( 'file.php?x param=val.&pa%%72am=val2&param3=v%20a%26l%3Du%2Be', $request->getUrl()->relativeUrl );
51-
Assert::same( 'https://nette.org:8080/file.php?x param=val.&pa%%72am=val2&param3=v%20a%26l%3Du%2Be', $request->getUrl()->absoluteUrl );
50+
Assert::same( 'file.php?x_param=val.&pa%25ram=val2&param3=v%20a%26l%3Du%2Be', $request->getUrl()->relativeUrl );
51+
Assert::same( 'https://nette.org:8080/file.php?x_param=val.&pa%25ram=val2&param3=v%20a%26l%3Du%2Be', $request->getUrl()->absoluteUrl );
5252
Assert::same( '', $request->getUrl()->pathInfo );
5353
});
5454

@@ -65,7 +65,7 @@ test(function() {
6565
Assert::same( 'nette.org', $request->getUrl()->host );
6666
Assert::same( 8080, $request->getUrl()->port );
6767
Assert::same( '/file.php', $request->getUrl()->path );
68-
Assert::same( 'x param=val.&pa%%72am=val2&param3=v%20a%26l%3Du%2Be)', $request->getUrl()->query );
68+
Assert::same( 'x_param=val.&pa%25ram=val2&param3=v%20a%26l%3Du%2Be%29', $request->getUrl()->query );
6969
Assert::same( '', $request->getUrl()->fragment );
7070
Assert::same( 'val.', $request->getQuery('x_param') );
7171
Assert::same( 'val2', $request->getQuery('pa%ram') );

tests/Http/Url.canonicalize.phpt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@ require __DIR__ . '/../bootstrap.php';
1313

1414
$url = new Url('http://hostname/path?arg=value&arg2=v%20a%26l%3Du%2Be');
1515
$url->canonicalize();
16-
Assert::same( 'http://hostname/path?arg=value&arg2=v a%26l%3Du%2Be', (string) $url );
16+
Assert::same( 'http://hostname/path?arg=value&arg2=v%20a%26l%3Du%2Be', (string) $url );
1717

1818

1919
$url = new Url('http://username%3A:password%3A@hostN%61me:60/p%61th%2f%25()?arg=value&arg2=v%20a%26l%3Du%2Be#%61nchor');
2020
$url->canonicalize();
21-
Assert::same( 'http://hostname:60/path%2F%25()?arg=value&arg2=v a%26l%3Du%2Be#anchor', (string) $url );
21+
Assert::same( 'http://hostname:60/path%2F%25()?arg=value&arg2=v%20a%26l%3Du%2Be#anchor', (string) $url );
2222

2323

2424
$url = new Url('http://host/%1f%20 %21!%22"%23%24$%25%26&%27\'%28(%29)%2a*%2b+%2c,%2d-%2e.%2f/%300%311%322%333%344%355%366%377%388%399%3a:%3b;%3c<%3d=%3e>%3f%40@'

tests/Http/Url.parseQuery.phpt

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
/**
4+
* Test: Nette\Http\Url::parseQuery()
5+
*/
6+
7+
use Nette\Http\Url,
8+
Tester\Assert;
9+
10+
11+
require __DIR__ . '/../bootstrap.php';
12+
13+
14+
Assert::same( array(), Url::parseQuery('') );
15+
Assert::same( array('key' => ''), Url::parseQuery('key') );
16+
Assert::same( array('key' => ''), Url::parseQuery('key=') );
17+
Assert::same( array('key' => 'val'), Url::parseQuery('key=val') );
18+
Assert::same( array('key' => ''), Url::parseQuery('&key=&') );
19+
Assert::same( array('a' => array('val', 'val')), Url::parseQuery('a[]=val&a[]=val') );
20+
Assert::same( array('a' => array('x' => 'val', 'y' => 'val')), Url::parseQuery('%61[x]=val&%61[y]=val') );
21+
Assert::same( array('a_b' => 'val', 'c' => array('d e' => 'val')), Url::parseQuery('a b=val&c[d e]=val') );
22+
Assert::same( array('a_b' => 'val', 'c' => array('d.e' => 'val')), Url::parseQuery('a.b=val&c[d.e]=val') );
23+
Assert::same( array('key"\'' => '"\''), Url::parseQuery('key"\'="\'') );

tests/Http/Url.query.phpt

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,25 +13,37 @@ require __DIR__ . '/../bootstrap.php';
1313

1414
$url = new Url('http://hostname/path?arg=value');
1515
Assert::same( 'arg=value', $url->query );
16+
Assert::same( array('arg' => 'value'), $url->getQueryParameters() );
1617

1718
$url->appendQuery(NULL);
1819
Assert::same( 'arg=value', $url->query );
20+
Assert::same( array('arg' => 'value'), $url->getQueryParameters() );
1921

2022
$url->appendQuery(array(NULL));
2123
Assert::same( 'arg=value', $url->query );
24+
Assert::same( array(NULL, 'arg' => 'value'), $url->getQueryParameters() );
2225

2326
$url->appendQuery('arg2=value2');
2427
Assert::same( 'arg=value&arg2=value2', $url->query );
28+
Assert::same( array('arg' => 'value', 'arg2' => 'value2'), $url->getQueryParameters() );
2529

2630
$url->appendQuery(array('arg3' => 'value3'));
27-
Assert::same( 'arg=value&arg2=value2&arg3=value3', $url->query );
31+
Assert::same( 'arg3=value3&arg=value&arg2=value2', $url->query );
2832

2933
$url->appendQuery('arg4[]=1');
3034
$url->appendQuery('arg4[]=2');
31-
Assert::same( 'arg=value&arg2=value2&arg3=value3&arg4[]=1&arg4[]=2', $url->query );
35+
Assert::same( 'arg3=value3&arg=value&arg2=value2&arg4%5B0%5D=1&arg4%5B1%5D=2', $url->query );
36+
37+
$url->appendQuery('arg4[0]=3');
38+
Assert::same( 'arg3=value3&arg=value&arg2=value2&arg4%5B0%5D=3&arg4%5B1%5D=2', $url->query );
39+
40+
$url->appendQuery(array('arg4' => 4));
41+
Assert::same( 'arg4=4&arg3=value3&arg=value&arg2=value2', $url->query );
42+
3243

3344
$url->setQuery(array('arg3' => 'value3'));
3445
Assert::same( 'arg3=value3', $url->query );
46+
Assert::same( array('arg3' => 'value3'), $url->getQueryParameters() );
3547

3648
$url->setQuery(array('arg' => 'value'));
3749
Assert::same( 'value', $url->getQueryParameter('arg') );
@@ -40,7 +52,10 @@ Assert::same( 123, $url->getQueryParameter('invalid', 123) );
4052

4153
$url->setQueryParameter('arg2', 'abc');
4254
Assert::same( 'abc', $url->getQueryParameter('arg2') );
55+
Assert::same( array('arg' => 'value', 'arg2' => 'abc'), $url->getQueryParameters() );
4356
$url->setQueryParameter('arg2', 'def');
4457
Assert::same( 'def', $url->getQueryParameter('arg2') );
58+
Assert::same( array('arg' => 'value', 'arg2' => 'def'), $url->getQueryParameters() );
4559
$url->setQueryParameter('arg2', NULL);
4660
Assert::same( NULL, $url->getQueryParameter('arg2') );
61+
Assert::same( array('arg' => 'value', 'arg2' => NULL), $url->getQueryParameters() );

0 commit comments

Comments
 (0)