8000 [HttpFoundation] Add InputBag by azjezz · Pull Request #34363 · symfony/symfony · GitHub
[go: up one dir, main page]

Skip to content

[HttpFoundation] Add InputBag #34363

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 13, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public function handleRequest(FormInterface $form, $request = null)
return;
}

$data = $request->query->get($name);
$data = $request->query->all()[$name];
}
} else {
// Mark the form with an error if the uploaded size was too large
Expand All @@ -87,7 +87,7 @@ public function handleRequest(FormInterface $form, $request = null)
$files = $request->files->all();
} elseif ($request->request->has($name) || $request->files->has($name)) {
$default = $form->getConfig()->getCompound() ? [] : null;
$params = $request->request->get($name, $default);
$params = $request->request->all()[$name] ?? $default;
$files = $request->files->get($name, $default);
} else {
// Don't submit the form if it is not present in the request
Expand Down
2 changes: 2 additions & 0 deletions src/Symfony/Component/HttpFoundation/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ CHANGELOG
* added `MarshallingSessionHandler`, `IdentityMarshaller`
* made `Session` accept a callback to report when the session is being used
* Add support for all core cache control directives
* Added `Symfony\Component\HttpFoundation\InputBag`
* Deprecated retrieving non-string values using `InputBag::get()`, use `InputBag::all()` if you need access to the collection of values

5.0.0
-----
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\HttpFoundation\Exception;

/**
* Raised when a user sends a malformed request.
*/
class BadRequestException extends \UnexpectedValueException implements RequestExceptionInterface
{
}
119 changes: 119 additions & 0 deletions src/Symfony/Component/HttpFoundation/InputBag.php
10000
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\HttpFoundation;

use Symfony\Component\HttpFoundation\Exception\BadRequestException;

/**
* InputBag is a container for user input values such as $_GET, $_POST, $_REQUEST, and $_COOKIE.
*
* @author Saif Eddin Gmati <saif.gmati@symfony.com>
*/
final class InputBag extends ParameterBag
{
/**
* Returns a string input value by name.
*
* @param string|null $default The default value if the input key does not exist
*
* @return string|null
*/
public function get(string $key, $default = null)
{
if (null !== $default && !is_scalar($default) && !method_exists($default, '__toString')) {
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__);
}

$value = parent::get($key, $this);

if (null !== $value && $this !== $value && !is_scalar($value) && !method_exists($value, '__toString')) {
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__);
}

return $this === $value ? $default : $value;
}

/**
* Returns the inputs.
*
* @param string|null $key The name of the input to return or null to get them all
*/
public function all(string $key = null): array
{
if (null === $key) {
return $this->parameters;
}

$value = $this->parameters[$key] ?? [];
if (!\is_array($value)) {
throw new BadRequestException(sprintf('Unexpected value for "%s" input, expecting "array", got "%s".', $key, get_debug_type($value)));
}

return $value;
}

/**
* Replaces the current input values by a new set.
*/
public function replace(array $inputs = [])
{
$this->parameters = [];
$this->add($inputs);
}

/**
* Adds input values.
*/
public function add(array $inputs = [])
{
foreach ($inputs as $input => $value) {
$this->set($input, $value);
}
}

/**
* Sets an input by name.
*
* @param string|array $value
*/
public function set(string $key, $value)
{
if (!is_scalar($value) && !method_exists($value, '__toString') && !\is_array($value)) {
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__);
}

$this->parameters[$key] = $value;
}

/**
* {@inheritdoc}
*/
public function filter(string $key, $default = null, int $filter = FILTER_DEFAULT, $options = [])
{
$value = $this->has($key) ? $this->all()[$key] : $default;

// Always turn $options into an array - this allows filter_var option shortcuts.
if (!\is_array($options) && $options) {
$options = ['flags' => $options];
}

if (\is_array($value) && !(($options['flags'] ?? 0) & (FILTER_REQUIRE_ARRAY | FILTER_FORCE_ARRAY))) {
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__);

if (!isset($options['flags'])) {
$options['flags'] = FILTER_REQUIRE_ARRAY;
}
}

return filter_var($value, $filter, $options);
}
}
32 changes: 16 additions & 16 deletions src/Symfony/Component/HttpFoundation/Request.php
Original file line number Diff line number Diff line change
Expand Up @@ -84,14 +84,14 @@ class Request
/**
* Request body parameters ($_POST).
*
* @var ParameterBag
* @var InputBag
*/
public $request;

/**
* Query string parameters ($_GET).
*
* @var ParameterBag
* @var InputBag
*/
public $query;

Expand All @@ -112,7 +112,7 @@ class Request
/**
* Cookies ($_COOKIE).
*
* @var ParameterBag
* @var InputBag
*/
public $cookies;

Expand Down Expand Up @@ -267,10 +267,10 @@ public function __construct(array $query = [], array $request = [], array $attri
*/
public function initialize(array $query = [], array $request = [], array $attributes = [], array $cookies = [], array $files = [], array $server = [], $content = null)
{
$this->request = new ParameterBag($request);
$this->query = new ParameterBag($query);
$this->request = new InputBag($request);
$this->query = new InputBag($query);
$this->attributes = new ParameterBag($attributes);
$this->cookies = new ParameterBag($cookies);
$this->cookies = new InputBag($cookies);
$this->files = new FileBag($files);
$this->server = new ServerBag($server);
$this->headers = new HeaderBag($this->server->getHeaders());
Expand Down Expand Up @@ -301,7 +301,7 @@ public static function createFromGlobals()
&& \in_array(strtoupper($request->server->get('REQUEST_METHOD', 'GET')), ['PUT', 'DELETE', 'PATCH'])
) {
parse_str($request->getContent(), $data);
$request->request = new ParameterBag($data);
$request->request = new InputBag($data);
}

return $request;
Expand Down Expand Up @@ -443,16 +443,16 @@ public function duplicate(array $query = null, array $request = null, array $att
{
$dup = clone $this;
if (null !== $query) {
$dup->query = new ParameterBag($query);
$dup->query = new InputBag($query);
}
if (null !== $request) {
$dup->request = new ParameterBag($request);
$dup->request = new InputBag($request);
}
if (null !== $attributes) {
$dup->attributes = new ParameterBag($attributes);
}
if (null !== $cookies) {
$dup->cookies = new ParameterBag($cookies);
$dup->cookies = new InputBag($cookies);
}
if (null !== $files) {
$dup->files = new FileBag($files);
Expand Down Expand Up @@ -708,12 +708,12 @@ public function get(string $key, $default = null)
return $result;
}

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

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

return $default;
Expand Down Expand Up @@ -1564,8 +1564,8 @@ public function isNoCache()

/**
* Gets the preferred format for the response by inspecting, in the following order:
* * the request format set using setRequestFormat
* * the values of the Accept HTTP header
* * the request format set using setRequestFormat;
* * the values of the Accept HTTP header.
*
* Note that if you use this method, you should send the "Vary: Accept" header
* in the response to prevent any issues with intermediary HTTP caches.
Expand Down
103 changes: 103 additions & 0 deletions src/Symfony/Component/HttpFoundation/Tests/InputBagTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\HttpFoundation\Tests;

use PHPUnit\Framework\TestCase;
use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait;
use Symfony\Component\HttpFoundation\Exception\BadRequestException;
use Symfony\Component\HttpFoundation\InputBag;

class InputBagTest extends TestCase
{
use ExpectDeprecationTrait;

public function testGet()
{
$bag = new InputBag(['foo' => 'bar', 'null' => null]);

$this->assertEquals('bar', $bag->get('foo'), '->get() gets the value of a parameter');
$this->assertEquals('default', $bag->get('unknown', 'default'), '->get() returns second argument as default if a parameter is not defined');
$this->assertNull($bag->get('null', 'default'), '->get() returns null if null is set');
}

public function testGetDoesNotUseDeepByDefault()
{
$bag = new InputBag(['foo' => ['bar' => 'moo']]);

$this->assertNull($bag->get('foo[bar]'));
}

public function testAllWithInputKey()
{
$bag = new InputBag(['foo' => ['bar', 'baz'], 'null' => null]);

$this->assertEquals(['bar', 'baz'], $bag->all('foo'), '->all() gets the value of a parameter');
$this->assertEquals([], $bag->all('unknown'), '->all() returns an empty array if a parameter is not defined');
}

public function testAllThrowsForNonArrayValues()
{
$this->expectException(BadRequestException::class);
$bag = new InputBag(['foo' => 'bar', 'null' => null]);
$bag->all('foo');
}

public function testFilterArray()
{
$bag = new InputBag([
'foo' => ['12', '8'],
]);

$result = $bag->filter('foo', null, \FILTER_VALIDATE_INT, \FILTER_FORCE_ARRAY);
$this->assertSame([12, 8], $result);
}

/**
* @group legacy
*/
public function testSetWithNonStringishOrArrayIsDeprecated()
{
$bag = new InputBag();
$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.');
$bag->set('foo', new InputBag());
}

/**
* @group legacy
*/
public function testGettingANonStringValueIsDeprecated()
{
$bag = new InputBag(['foo' => ['a', 'b']]);
$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.');
$bag->get('foo');
}

/**
* @group legacy
*/
public function testGetWithNonStringDefaultValueIsDeprecated()
{
$bag = new InputBag(['foo' => 'bar']);
$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.');
$bag->get('foo', ['a', 'b']);
}

/**
* @group legacy
*/
public function testFilterArrayWithoutArrayFlagIsDeprecated()
{
$bag = new InputBag(['foo' => ['bar', 'baz']]);
$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');
$bag->filter('foo', \FILTER_VALIDATE_INT);
}
}
4 changes: 2 additions & 2 deletions src/Symfony/Component/Security/Http/ParameterBagUtils.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,12 @@ final class ParameterBagUtils
public static function getParameterBagValue(ParameterBag $parameters, string $path)
{
if (false === $pos = strpos($path, '[')) {
return $parameters->get($path);
return $parameters->all()[$path] ?? null;
}

$root = substr($path, 0, $pos);

if (null === $value = $parameters->get($root)) {
if (null === $value = $parameters->all()[$root] ?? null) {
return null;
}

Expand Down
0