[HttpKernel] Fixed the nullable support for php 7.1 and below
This commit is contained in:
parent
120a05d0e1
commit
4a1ab6dcd5
@ -79,7 +79,7 @@ final class ArgumentResolver implements ArgumentResolverInterface
|
|||||||
$representative = get_class($representative);
|
$representative = get_class($representative);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new \RuntimeException(sprintf('Controller "%s" requires that you provide a value for the "$%s" argument (because there is no default value or because there is a non optional argument after this one).', $representative, $metadata->getName()));
|
throw new \RuntimeException(sprintf('Controller "%s" requires that you provide a value for the "$%s" argument. Either the argument is nullable and no null value has been provided, no default value has been provided or because there is a non optional argument after this one.', $representative, $metadata->getName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return $arguments;
|
return $arguments;
|
||||||
|
@ -27,7 +27,7 @@ final class DefaultValueResolver implements ArgumentValueResolverInterface
|
|||||||
*/
|
*/
|
||||||
public function supports(Request $request, ArgumentMetadata $argument)
|
public function supports(Request $request, ArgumentMetadata $argument)
|
||||||
{
|
{
|
||||||
return $argument->hasDefaultValue();
|
return $argument->hasDefaultValue() || $argument->isNullable();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -35,6 +35,6 @@ final class DefaultValueResolver implements ArgumentValueResolverInterface
|
|||||||
*/
|
*/
|
||||||
public function resolve(Request $request, ArgumentMetadata $argument)
|
public function resolve(Request $request, ArgumentMetadata $argument)
|
||||||
{
|
{
|
||||||
yield $argument->getDefaultValue();
|
yield $argument->hasDefaultValue() ? $argument->getDefaultValue() : null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ class ArgumentMetadata
|
|||||||
private $isVariadic;
|
private $isVariadic;
|
||||||
private $hasDefaultValue;
|
private $hasDefaultValue;
|
||||||
private $defaultValue;
|
private $defaultValue;
|
||||||
|
private $isNullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $name
|
* @param string $name
|
||||||
@ -30,14 +31,16 @@ class ArgumentMetadata
|
|||||||
* @param bool $isVariadic
|
* @param bool $isVariadic
|
||||||
* @param bool $hasDefaultValue
|
* @param bool $hasDefaultValue
|
||||||
* @param mixed $defaultValue
|
* @param mixed $defaultValue
|
||||||
|
* @param bool $isNullable
|
||||||
*/
|
*/
|
||||||
public function __construct($name, $type, $isVariadic, $hasDefaultValue, $defaultValue)
|
public function __construct($name, $type, $isVariadic, $hasDefaultValue, $defaultValue, $isNullable = false)
|
||||||
{
|
{
|
||||||
$this->name = $name;
|
$this->name = $name;
|
||||||
$this->type = $type;
|
$this->type = $type;
|
||||||
$this->isVariadic = $isVariadic;
|
$this->isVariadic = $isVariadic;
|
||||||
$this->hasDefaultValue = $hasDefaultValue;
|
$this->hasDefaultValue = $hasDefaultValue;
|
||||||
$this->defaultValue = $defaultValue;
|
$this->defaultValue = $defaultValue;
|
||||||
|
$this->isNullable = (bool) $isNullable;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -84,6 +87,16 @@ class ArgumentMetadata
|
|||||||
return $this->hasDefaultValue;
|
return $this->hasDefaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the argument is nullable in PHP 7.1 or higher.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isNullable()
|
||||||
|
{
|
||||||
|
return $this->isNullable;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the default value of the argument.
|
* Returns the default value of the argument.
|
||||||
*
|
*
|
||||||
|
@ -18,6 +18,30 @@ namespace Symfony\Component\HttpKernel\ControllerMetadata;
|
|||||||
*/
|
*/
|
||||||
final class ArgumentMetadataFactory implements ArgumentMetadataFactoryInterface
|
final class ArgumentMetadataFactory implements ArgumentMetadataFactoryInterface
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* If the ...$arg functionality is available.
|
||||||
|
*
|
||||||
|
* Requires at least PHP 5.6.0 or HHVM 3.9.1
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $supportsVariadic;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the reflection supports the getType() method to resolve types.
|
||||||
|
*
|
||||||
|
* Requires at least PHP 7.0.0 or HHVM 3.11.0
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $supportsParameterType;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->supportsVariadic = method_exists('ReflectionParameter', 'isVariadic');
|
||||||
|
$this->supportsParameterType = method_exists('ReflectionParameter', 'getType');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
@ -34,7 +58,7 @@ final class ArgumentMetadataFactory implements ArgumentMetadataFactoryInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
foreach ($reflection->getParameters() as $param) {
|
foreach ($reflection->getParameters() as $param) {
|
||||||
$arguments[] = new ArgumentMetadata($param->getName(), $this->getType($param), $this->isVariadic($param), $this->hasDefaultValue($param), $this->getDefaultValue($param));
|
$arguments[] = new ArgumentMetadata($param->getName(), $this->getType($param), $this->isVariadic($param), $this->hasDefaultValue($param), $this->getDefaultValue($param), $this->isNullable($param));
|
||||||
}
|
}
|
||||||
|
|
||||||
return $arguments;
|
return $arguments;
|
||||||
@ -49,7 +73,7 @@ final class ArgumentMetadataFactory implements ArgumentMetadataFactoryInterface
|
|||||||
*/
|
*/
|
||||||
private function isVariadic(\ReflectionParameter $parameter)
|
private function isVariadic(\ReflectionParameter $parameter)
|
||||||
{
|
{
|
||||||
return PHP_VERSION_ID >= 50600 && $parameter->isVariadic();
|
return $this->supportsVariadic && $parameter->isVariadic();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -64,6 +88,23 @@ final class ArgumentMetadataFactory implements ArgumentMetadataFactoryInterface
|
|||||||
return $parameter->isDefaultValueAvailable();
|
return $parameter->isDefaultValueAvailable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns if the argument is allowed to be null but is still mandatory.
|
||||||
|
*
|
||||||
|
* @param \ReflectionParameter $parameter
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
private function isNullable(\ReflectionParameter $parameter)
|
||||||
|
{
|
||||||
|
if ($this->supportsParameterType) {
|
||||||
|
return null !== ($type = $parameter->getType()) && $type->allowsNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
// fallback for supported php 5.x versions
|
||||||
|
return $this->hasDefaultValue($parameter) && null === $this->getDefaultValue($parameter);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a default value if available.
|
* Returns a default value if available.
|
||||||
*
|
*
|
||||||
@ -85,7 +126,7 @@ final class ArgumentMetadataFactory implements ArgumentMetadataFactoryInterface
|
|||||||
*/
|
*/
|
||||||
private function getType(\ReflectionParameter $parameter)
|
private function getType(\ReflectionParameter $parameter)
|
||||||
{
|
{
|
||||||
if (PHP_VERSION_ID >= 70000) {
|
if ($this->supportsParameterType) {
|
||||||
return $parameter->hasType() ? (string) $parameter->getType() : null;
|
return $parameter->hasType() ? (string) $parameter->getType() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@ use Symfony\Component\HttpKernel\Controller\ArgumentResolver\VariadicValueResolv
|
|||||||
use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface;
|
use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface;
|
||||||
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadataFactory;
|
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadataFactory;
|
||||||
use Symfony\Component\HttpKernel\Tests\Fixtures\Controller\ExtendingRequest;
|
use Symfony\Component\HttpKernel\Tests\Fixtures\Controller\ExtendingRequest;
|
||||||
|
use Symfony\Component\HttpKernel\Tests\Fixtures\Controller\NullableController;
|
||||||
use Symfony\Component\HttpKernel\Tests\Fixtures\Controller\VariadicController;
|
use Symfony\Component\HttpKernel\Tests\Fixtures\Controller\VariadicController;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
|
||||||
@ -202,6 +203,32 @@ class ArgumentResolverTest extends \PHPUnit_Framework_TestCase
|
|||||||
$resolver->getArguments($request, $controller);
|
$resolver->getArguments($request, $controller);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @requires PHP 7.1
|
||||||
|
*/
|
||||||
|
public function testGetNullableArguments()
|
||||||
|
{
|
||||||
|
$request = Request::create('/');
|
||||||
|
$request->attributes->set('foo', 'foo');
|
||||||
|
$request->attributes->set('bar', new \stdClass());
|
||||||
|
$request->attributes->set('mandatory', 'mandatory');
|
||||||
|
$controller = array(new NullableController(), 'action');
|
||||||
|
|
||||||
|
$this->assertEquals(array('foo', new \stdClass(), 'value', 'mandatory'), self::$resolver->getArguments($request, $controller));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @requires PHP 7.1
|
||||||
|
*/
|
||||||
|
public function testGetNullableArgumentsWithDefaults()
|
||||||
|
{
|
||||||
|
$request = Request::create('/');
|
||||||
|
$request->attributes->set('mandatory', 'mandatory');
|
||||||
|
$controller = array(new NullableController(), 'action');
|
||||||
|
|
||||||
|
$this->assertEquals(array(null, null, 'value', 'mandatory'), self::$resolver->getArguments($request, $controller));
|
||||||
|
}
|
||||||
|
|
||||||
public function __invoke($foo, $bar = null)
|
public function __invoke($foo, $bar = null)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -15,10 +15,14 @@ use Fake\ImportedAndFake;
|
|||||||
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata;
|
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata;
|
||||||
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadataFactory;
|
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadataFactory;
|
||||||
use Symfony\Component\HttpKernel\Tests\Fixtures\Controller\BasicTypesController;
|
use Symfony\Component\HttpKernel\Tests\Fixtures\Controller\BasicTypesController;
|
||||||
|
use Symfony\Component\HttpKernel\Tests\Fixtures\Controller\NullableController;
|
||||||
use Symfony\Component\HttpKernel\Tests\Fixtures\Controller\VariadicController;
|
use Symfony\Component\HttpKernel\Tests\Fixtures\Controller\VariadicController;
|
||||||
|
|
||||||
class ArgumentMetadataFactoryTest extends \PHPUnit_Framework_TestCase
|
class ArgumentMetadataFactoryTest extends \PHPUnit_Framework_TestCase
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @var ArgumentMetadataFactory
|
||||||
|
*/
|
||||||
private $factory;
|
private $factory;
|
||||||
|
|
||||||
protected function setUp()
|
protected function setUp()
|
||||||
@ -42,9 +46,9 @@ class ArgumentMetadataFactoryTest extends \PHPUnit_Framework_TestCase
|
|||||||
$arguments = $this->factory->createArgumentMetadata(array($this, 'signature2'));
|
$arguments = $this->factory->createArgumentMetadata(array($this, 'signature2'));
|
||||||
|
|
||||||
$this->assertEquals(array(
|
$this->assertEquals(array(
|
||||||
new ArgumentMetadata('foo', self::class, false, true, null),
|
new ArgumentMetadata('foo', self::class, false, true, null, true),
|
||||||
new ArgumentMetadata('bar', __NAMESPACE__.'\FakeClassThatDoesNotExist', false, true, null),
|
new ArgumentMetadata('bar', __NAMESPACE__.'\FakeClassThatDoesNotExist', false, true, null, true),
|
||||||
new ArgumentMetadata('baz', 'Fake\ImportedAndFake', false, true, null),
|
new ArgumentMetadata('baz', 'Fake\ImportedAndFake', false, true, null, true),
|
||||||
), $arguments);
|
), $arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,7 +78,7 @@ class ArgumentMetadataFactoryTest extends \PHPUnit_Framework_TestCase
|
|||||||
$arguments = $this->factory->createArgumentMetadata(array($this, 'signature5'));
|
$arguments = $this->factory->createArgumentMetadata(array($this, 'signature5'));
|
||||||
|
|
||||||
$this->assertEquals(array(
|
$this->assertEquals(array(
|
||||||
new ArgumentMetadata('foo', 'array', false, true, null),
|
new ArgumentMetadata('foo', 'array', false, true, null, true),
|
||||||
new ArgumentMetadata('bar', null, false, false, null),
|
new ArgumentMetadata('bar', null, false, false, null),
|
||||||
), $arguments);
|
), $arguments);
|
||||||
}
|
}
|
||||||
@ -106,6 +110,21 @@ class ArgumentMetadataFactoryTest extends \PHPUnit_Framework_TestCase
|
|||||||
), $arguments);
|
), $arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @requires PHP 7.1
|
||||||
|
*/
|
||||||
|
public function testNullableTypesSignature()
|
||||||
|
{
|
||||||
|
$arguments = $this->factory->createArgumentMetadata(array(new NullableController(), 'action'));
|
||||||
|
|
||||||
|
$this->assertEquals(array(
|
||||||
|
new ArgumentMetadata('foo', 'string', false, false, null, true),
|
||||||
|
new ArgumentMetadata('bar', \stdClass::class, false, false, null, true),
|
||||||
|
new ArgumentMetadata('baz', 'string', false, true, 'value', true),
|
||||||
|
new ArgumentMetadata('mandatory', null, false, false, null),
|
||||||
|
), $arguments);
|
||||||
|
}
|
||||||
|
|
||||||
private function signature1(ArgumentMetadataFactoryTest $foo, array $bar, callable $baz)
|
private function signature1(ArgumentMetadataFactoryTest $foo, array $bar, callable $baz)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -15,10 +15,18 @@ use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata;
|
|||||||
|
|
||||||
class ArgumentMetadataTest extends \PHPUnit_Framework_TestCase
|
class ArgumentMetadataTest extends \PHPUnit_Framework_TestCase
|
||||||
{
|
{
|
||||||
public function testDefaultValueAvailable()
|
public function testWithBcLayerWithDefault()
|
||||||
{
|
{
|
||||||
$argument = new ArgumentMetadata('foo', 'string', false, true, 'default value');
|
$argument = new ArgumentMetadata('foo', 'string', false, true, 'default value');
|
||||||
|
|
||||||
|
$this->assertFalse($argument->isNullable());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDefaultValueAvailable()
|
||||||
|
{
|
||||||
|
$argument = new ArgumentMetadata('foo', 'string', false, true, 'default value', true);
|
||||||
|
|
||||||
|
$this->assertTrue($argument->isNullable());
|
||||||
$this->assertTrue($argument->hasDefaultValue());
|
$this->assertTrue($argument->hasDefaultValue());
|
||||||
$this->assertSame('default value', $argument->getDefaultValue());
|
$this->assertSame('default value', $argument->getDefaultValue());
|
||||||
}
|
}
|
||||||
@ -28,8 +36,9 @@ class ArgumentMetadataTest extends \PHPUnit_Framework_TestCase
|
|||||||
*/
|
*/
|
||||||
public function testDefaultValueUnavailable()
|
public function testDefaultValueUnavailable()
|
||||||
{
|
{
|
||||||
$argument = new ArgumentMetadata('foo', 'string', false, false, null);
|
$argument = new ArgumentMetadata('foo', 'string', false, false, null, false);
|
||||||
|
|
||||||
|
$this->assertFalse($argument->isNullable());
|
||||||
$this->assertFalse($argument->hasDefaultValue());
|
$this->assertFalse($argument->hasDefaultValue());
|
||||||
$argument->getDefaultValue();
|
$argument->getDefaultValue();
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Symfony\Component\HttpKernel\Tests\Fixtures\Controller;
|
||||||
|
|
||||||
|
class NullableController
|
||||||
|
{
|
||||||
|
public function action(?string $foo, ?\stdClass $bar, ?string $baz = 'value', $mandatory)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user