Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feature #34363 [HttpFoundation] Add InputBag (azjezz)
This PR was merged into the 5.1-dev branch. Discussion ---------- [HttpFoundation] Add InputBag | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | Deprecations? | yes | License | MIT When ppl read a request attribute, they never check if an array is returned This means many apps just fail with a 500 when adding `[]` in the query string. This PR turns them to 400 basically (with a deprecation for now) Commits ------- 0a2ef70 [HttpFoundation] add InputBag
- Loading branch information
Showing
7 changed files
with
263 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
19 changes: 19 additions & 0 deletions
19
src/Symfony/Component/HttpFoundation/Exception/BadRequestException.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
{ | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
103 changes: 103 additions & 0 deletions
103
src/Symfony/Component/HttpFoundation/Tests/InputBagTest.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters