Skip to content

Commit 5fa0a07

Browse files
committed
[HttpFoundation] add InputBag
1 parent 5ab8a06 commit 5fa0a07

File tree

5 files changed

+259
-16
lines changed

5 files changed

+259
-16
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ CHANGELOG
1616
* added `MarshallingSessionHandler`, `IdentityMarshaller`
1717
* made `Session` accept a callback to report when the session is being used
1818
* Add support for all core cache control directives
19+
* Added `Symfony\Component\HttpFoundation\InputBag`
20+
* Deprecated retrieving non-string values using `InputBag::get()`, use `InputBag::all()` if you need access to the collection of values
1921

2022
5.0.0
2123
-----

Exception/BadRequestException.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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 Symfony\Component\HttpFoundation\Exception;
13+
14+
/**
15+
* Raised when a user sends a malformed request.
16+
*/
17+
class BadRequestException extends \UnexpectedValueException implements RequestExceptionInterface
18+
{
19+
}

InputBag.php

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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 Symfony\Component\HttpFoundation;
13+
14+
use Symfony\Component\HttpFoundation\Exception\BadRequestException;
15+
16+
/**
17+
* InputBag is a container for user input values such as $_GET, $_POST, $_REQUEST, and $_COOKIE.
18+
*
19+
* @author Saif Eddin Gmati <saif.gmati@symfony.com>
20+
*/
21+
final class InputBag extends ParameterBag
22+
{
23+
/**
24+
* Returns a string input value by name.
25+
*
26+
* @param string|null $default The default value if the input key does not exist
27+
*
28+
* @return string|null
29+
*/
30+
public function get(string $key, $default = null)
31+
{
32+
if (null !== $default && !is_scalar($default) && !method_exists($default, '__toString')) {
33+
trigger_deprecation('symfony/http-foundation', '5.1', 'Passing a non-string value as 2nd argument to "%s()" is deprecated, pass a string or null instead.', __METHOD__);
34+
}
35+
36+
$value = parent::get($key, $this);
37+
38+
if (null !== $value && $this !== $value && !is_scalar($value) && !method_exists($value, '__toString')) {
39+
trigger_deprecation('symfony/http-foundation', '5.1', 'Retrieving a non-string value from "%s()" is deprecated, and will throw a "%s" exception in Symfony 6.0, use "%s::all()" instead.', __METHOD__, BadRequestException::class, __CLASS__);
40+
}
41+
42+
return $this === $value ? $default : $value;
43+
}
44+
45+
/**
46+
* Returns the inputs.
47+
*
48+
* @param string|null $key The name of the input to return or null to get them all
49+
*/
50+
public function all(string $key = null): array
51+
{
52+
if (null === $key) {
53+
return $this->parameters;
54+
}
55+
56+
$value = $this->parameters[$key] ?? [];
57+
if (!\is_array($value)) {
58+
throw new BadRequestException(sprintf('Unexpected value for "%s" input, expecting "array", got "%s".', $key, get_debug_type($value)));
59+
}
60+
61+
return $value;
62+
}
63+
64+
/**
65+
* Replaces the current input values by a new set.
66+
*/
67+
public function replace(array $inputs = [])
68+
{
69+
$this->parameters = [];
70+
$this->add($inputs);
71+
}
72+
73+
/**
74+
* Adds input values.
75+
*/
76+
public function add(array $inputs = [])
77+
{
78+
foreach ($inputs as $input => $value) {
79+
$this->set($input, $value);
80+
}
81+
}
82+
83+
/**
84+
* Sets an input by name.
85+
*
86+
* @param string|array $value
87+
*/
88+
public function set(string $key, $value)
89+
{
90+
if (!is_scalar($value) && !method_exists($value, '__toString') && !\is_array($value)) {
91+
trigger_deprecation('symfony/http-foundation', '5.1', 'Passing "%s" as a 2nd Argument to "%s()" is deprecated, pass a string or an array instead.', get_debug_type($value), __METHOD__);
92+
}
93+
94+
$this->parameters[$key] = $value;
95+
}
96+
97+
/**
98+
* {@inheritdoc}
99+
*/
100+
public function filter(string $key, $default = null, int $filter = FILTER_DEFAULT, $options = [])
101+
{
102+
$value = $this->has($key) ? $this->all()[$key] : $default;
103+
104+
// Always turn $options into an array - this allows filter_var option shortcuts.
105+
if (!\is_array($options) && $options) {
106+
$options = ['flags' => $options];
107+
}
108+
109+
if (\is_array($value) && !(($options['flags'] ?? 0) & (FILTER_REQUIRE_ARRAY | FILTER_FORCE_ARRAY))) {
110+
trigger_deprecation('symfony/http-foundation', '5.1', 'Filtering an array value with "%s()" without passing the FILTER_REQUIRE_ARRAY or FILTER_FORCE_ARRAY flag is deprecated', __METHOD__);
111+
112+
if (!isset($options['flags'])) {
113+
$options['flags'] = FILTER_REQUIRE_ARRAY;
114+
}
115+
}
116+
117+
return filter_var($value, $filter, $options);
118+
}
119+
}

Request.php

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -84,14 +84,14 @@ class Request
8484
/**
8585
* Request body parameters ($_POST).
8686
*
87-
* @var ParameterBag
87+
* @var InputBag
8888
*/
8989
public $request;
9090

9191
/**
9292
* Query string parameters ($_GET).
9393
*
94-
* @var ParameterBag
94+
* @var InputBag
9595
*/
9696
public $query;
9797

@@ -112,7 +112,7 @@ class Request
112112
/**
113113
* Cookies ($_COOKIE).
114114
*
115-
* @var ParameterBag
115+
* @var InputBag
116116
*/
117117
public $cookies;
118118

@@ -267,10 +267,10 @@ public function __construct(array $query = [], array $request = [], array $attri
267267
*/
268268
public function initialize(array $query = [], array $request = [], array $attributes = [], array $cookies = [], array $files = [], array $server = [], $content = null)
269269
{
270-
$this->request = new ParameterBag($request);
271-
$this->query = new ParameterBag($query);
270+
$this->request = new InputBag($request);
271+
$this->query = new InputBag($query);
272272
$this->attributes = new ParameterBag($attributes);
273-
$this->cookies = new ParameterBag($cookies);
273+
$this->cookies = new InputBag($cookies);
274274
$this->files = new FileBag($files);
275275
$this->server = new ServerBag($server);
276276
$this->headers = new HeaderBag($this->server->getHeaders());
@@ -301,7 +301,7 @@ public static function createFromGlobals()
301301
&& \in_array(strtoupper($request->server->get('REQUEST_METHOD', 'GET')), ['PUT', 'DELETE', 'PATCH'])
302302
) {
303303
parse_str($request->getContent(), $data);
304-
$request->request = new ParameterBag($data);
304+
$request->request = new InputBag($data);
305305
}
306306

307307
return $request;
@@ -443,16 +443,16 @@ public function duplicate(array $query = null, array $request = null, array $att
443443
{
444444
$dup = clone $this;
445445
if (null !== $query) {
446-
$dup->query = new ParameterBag($query);
446+
$dup->query = new InputBag($query);
447447
}
448448
if (null !== $request) {
449-
$dup->request = new ParameterBag($request);
449+
$dup->request = new InputBag($request);
450450
}
451451
if (null !== $attributes) {
452452
$dup->attributes = new ParameterBag($attributes);
453453
}
454454
if (null !== $cookies) {
455-
$dup->cookies = new ParameterBag($cookies);
455+
$dup->cookies = new InputBag($cookies);
456456
}
457457
if (null !== $files) {
458458
$dup->files = new FileBag($files);
@@ -708,12 +708,12 @@ public function get(string $key, $default = null)
708708
return $result;
709709
}
710710

711-
if ($this !== $result = $this->query->get($key, $this)) {
712-
return $result;
711+
if ($this->query->has($key)) {
712+
return $this->query->all()[$key];
713713
}
714714

715-
if ($this !== $result = $this->request->get($key, $this)) {
716-
return $result;
715+
if ($this->request->has($key)) {
716+
return $this->request->all()[$key];
717717
}
718718

719719
return $default;
@@ -1564,8 +1564,8 @@ public function isNoCache()
15641564

15651565
/**
15661566
* Gets the preferred format for the response by inspecting, in the following order:
1567-
* * the request format set using setRequestFormat
1568-
* * the values of the Accept HTTP header
1567+
* * the request format set using setRequestFormat;
1568+
* * the values of the Accept HTTP header.
15691569
*
15701570
* Note that if you use this method, you should send the "Vary: Accept" header
15711571
* in the response to prevent any issues with intermediary HTTP caches.

Tests/InputBagTest.php

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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 Symfony\Component\HttpFoundation\Tests;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait;
16+
use Symfony\Component\HttpFoundation\Exception\BadRequestException;
17+
use Symfony\Component\HttpFoundation\InputBag;
18+
19+
class InputBagTest extends TestCase
20+
{
21+
use ExpectDeprecationTrait;
22+
23+
public function testGet()
24+
{
25+
$bag = new InputBag(['foo' => 'bar', 'null' => null]);
26+
27+
$this->assertEquals('bar', $bag->get('foo'), '->get() gets the value of a parameter');
28+
$this->assertEquals('default', $bag->get('unknown', 'default'), '->get() returns second argument as default if a parameter is not defined');
29+
$this->assertNull($bag->get('null', 'default'), '->get() returns null if null is set');
30+
}
31+
32+
public function testGetDoesNotUseDeepByDefault()
33+
{
34+
$bag = new InputBag(['foo' => ['bar' => 'moo']]);
35+
36+
$this->assertNull($bag->get('foo[bar]'));
37+
}
38+
39+
public function testAllWithInputKey()
40+
{
41+
$bag = new InputBag(['foo' => ['bar', 'baz'], 'null' => null]);
42+
43+
$this->assertEquals(['bar', 'baz'], $bag->all('foo'), '->all() gets the value of a parameter');
44+
$this->assertEquals([], $bag->all('unknown'), '->all() returns an empty array if a parameter is not defined');
45+
}
46+
47+
public function testAllThrowsForNonArrayValues()
48+
{
49+
$this->expectException(BadRequestException::class);
50+
$bag = new InputBag(['foo' => 'bar', 'null' => null]);
51+
$bag->all('foo');
52+
}
53+
54+
public function testFilterArray()
55+
{
56+
$bag = new InputBag([
57+
'foo' => ['12', '8'],
58+
]);
59+
60+
$result = $bag->filter('foo', null, \FILTER_VALIDATE_INT, \FILTER_FORCE_ARRAY);
61+
$this->assertSame([12, 8], $result);
62+
}
63+
64+
/**
65+
* @group legacy
66+
*/
67+
public function testSetWithNonStringishOrArrayIsDeprecated()
68+
{
69+
$bag = new InputBag();
70+
$this->expectDeprecation('Since symfony/http-foundation 5.1: Passing "Symfony\Component\HttpFoundation\InputBag" as a 2nd Argument to "Symfony\Component\HttpFoundation\InputBag::set()" is deprecated, pass a string or an array instead.');
71+
$bag->set('foo', new InputBag());
72+
}
73+
74+
/**
75+
* @group legacy
76+
*/
77+
public function testGettingANonStringValueIsDeprecated()
78+
{
79+
$bag = new InputBag(['foo' => ['a', 'b']]);
80+
$this->expectDeprecation('Since symfony/http-foundation 5.1: Retrieving a non-string value from "Symfony\Component\HttpFoundation\InputBag::get()" is deprecated, and will throw a "Symfony\Component\HttpFoundation\Exception\BadRequestException" exception in Symfony 6.0, use "Symfony\Component\HttpFoundation\InputBag::all()" instead.');
81+
$bag->get('foo');
82+
}
83+
84+
/**
85+
* @group legacy
86+
*/
87+
public function testGetWithNonStringDefaultValueIsDeprecated()
88+
{
89+
$bag = new InputBag(['foo' => 'bar']);
90+
$this->expectDeprecation('Since symfony/http-foundation 5.1: Passing a non-string value as 2nd argument to "Symfony\Component\HttpFoundation\InputBag::get()" is deprecated, pass a string or null instead.');
91+
$bag->get('foo', ['a', 'b']);
92+
}
93+
94+
/**
95+
* @group legacy
96+
*/
97+
public function testFilterArrayWithoutArrayFlagIsDeprecated()
98+
{
99+
$bag = new InputBag(['foo' => ['bar', 'baz']]);
100+
$this->expectDeprecation('Since symfony/http-foundation 5.1: Filtering an array value with "Symfony\Component\HttpFoundation\InputBag::filter()" without passing the FILTER_REQUIRE_ARRAY or FILTER_FORCE_ARRAY flag is deprecated');
101+
$bag->filter('foo', \FILTER_VALIDATE_INT);
102+
}
103+
}

0 commit comments

Comments
 (0)