Added an ArgumentResolver with clean extension point

This commit is contained in:
Iltar van der Berg 2016-03-25 10:46:04 +01:00
parent 360fc5fc4b
commit cfcf764d24
30 changed files with 1085 additions and 106 deletions

View File

@ -65,10 +65,12 @@ HttpKernel
* Passing objects as URI attributes to the ESI and SSI renderers has been
deprecated and will be removed in Symfony 4.0. The inline fragment
renderer should be used with object attributes.
* The `ControllerResolver::getArguments()` method is deprecated and will be
removed in 4.0. If you have your own `ControllerResolverInterface`
implementation, you should replace this method by implementing the
`ArgumentResolverInterface` and injecting it in the HttpKernel.
`ArgumentResolverInterface` and injecting it in the `HttpKernel`, or using
the `ArgumentResolver` and injecting this in the `HttpKernel`.
Serializer
----------

View File

@ -0,0 +1,65 @@
<?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\Bundle\FrameworkBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
/**
* Gathers and configures the argument value resolvers.
*
* @author Iltar van der Berg <kjarli@gmail.com>
*/
class ControllerArgumentValueResolverPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition('argument_resolver')) {
return;
}
$definition = $container->getDefinition('argument_resolver');
$argumentResolvers = $this->findAndSortTaggedServices('controller_argument.value_resolver', $container);
$definition->replaceArgument(1, $argumentResolvers);
}
/**
* Finds all services with the given tag name and order them by their priority.
*
* @param string $tagName
* @param ContainerBuilder $container
*
* @return array
*/
private function findAndSortTaggedServices($tagName, ContainerBuilder $container)
{
$services = $container->findTaggedServiceIds($tagName);
$sortedServices = array();
foreach ($services as $serviceId => $tags) {
foreach ($tags as $attributes) {
$priority = isset($attributes['priority']) ? $attributes['priority'] : 0;
$sortedServices[$priority][] = new Reference($serviceId);
}
}
if (empty($sortedServices)) {
return array();
}
krsort($sortedServices);
// Flatten the array
return call_user_func_array('array_merge', $sortedServices);
}
}

View File

@ -171,6 +171,8 @@ class FrameworkExtension extends Extension
'Symfony\\Component\\HttpKernel\\EventListener\\RouterListener',
'Symfony\\Component\\HttpKernel\\Controller\\ControllerResolver',
'Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolver',
'Symfony\\Component\\HttpKernel\\ControllerMetadata\\ArgumentMetadata',
'Symfony\\Component\\HttpKernel\\ControllerMetadata\\ArgumentMetadataFactory',
'Symfony\\Component\\HttpKernel\\Event\\KernelEvent',
'Symfony\\Component\\HttpKernel\\Event\\FilterControllerEvent',
'Symfony\\Component\\HttpKernel\\Event\\FilterResponseEvent',

View File

@ -14,6 +14,7 @@ namespace Symfony\Bundle\FrameworkBundle;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddConstraintValidatorsPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddValidatorInitializersPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddConsoleCommandPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ControllerArgumentValueResolverPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\FormPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\PropertyInfoPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TemplatingPass;
@ -87,6 +88,7 @@ class FrameworkBundle extends Bundle
$container->addCompilerPass(new FragmentRendererPass(), PassConfig::TYPE_AFTER_REMOVING);
$container->addCompilerPass(new SerializerPass());
$container->addCompilerPass(new PropertyInfoPass());
$container->addCompilerPass(new ControllerArgumentValueResolverPass());
if ($container->getParameter('kernel.debug')) {
$container->addCompilerPass(new UnusedTagsPass(), PassConfig::TYPE_AFTER_REMOVING);

View File

@ -17,7 +17,28 @@
<argument type="service" id="logger" on-invalid="ignore" />
</service>
<service id="argument_resolver" class="Symfony\Component\HttpKernel\Controller\ArgumentResolver" public="false" />
<service id="argument_resolver" class="Symfony\Component\HttpKernel\Controller\ArgumentResolver" public="false">
<argument type="service" id="argument_metadata_factory" />
<argument type="collection" />
</service>
<service id="argument_metadata_factory" class="Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadataFactory" public="false" />
<service id="argument_value_resolver.argument_from_attribute" class="Symfony\Component\HttpKernel\Controller\ArgumentValueResolver\ArgumentFromAttributeResolver" public="false">
<tag name="controller_argument.value_resolver" priority="100" />
</service>
<service id="argument_value_resolver.argument_is_request" class="Symfony\Component\HttpKernel\Controller\ArgumentValueResolver\RequestResolver" public="false">
<tag name="controller_argument.value_resolver" priority="50" />
</service>
<service id="argument_value_resolver.default_argument_value" class="Symfony\Component\HttpKernel\Controller\ArgumentValueResolver\DefaultArgumentValueResolver" public="false">
<tag name="controller_argument.value_resolver" priority="-100" />
</service>
<service id="argument_value_resolver.variadic_argument_from_attribute" class="Symfony\Component\HttpKernel\Controller\ArgumentValueResolver\VariadicArgumentValueResolver" public="false">
<tag name="controller_argument.value_resolver" priority="-150" />
</service>
<service id="response_listener" class="Symfony\Component\HttpKernel\EventListener\ResponseListener">
<tag name="kernel.event_subscriber" />

View File

@ -4,9 +4,10 @@ CHANGELOG
3.1.0
-----
* deprecated passing objects as URI attributes to the ESI and SSI renderers
* Added an `ArgumentResolver` with `getArguments()` and the respective interface `ArgumentResolverInterface`
* Deprecated `ControllerResolver::getArguments()`, which uses the `ArgumentResolver` as BC layer by extending it
* The `HttpKernel` now accepts an additional argument for an `ArgumentResolver`
* Added a `LegacyArgumentResolver` with `getArguments()` and the corresponding interface `ArgumentResolverInterface`
* Deprecated `ControllerResolver::getArguments()`, which uses the `LegacyArgumentResolver` as BC layer by extending it
* The `HttpKernel` now accepts an additional argument for an `ArgumentResolverInterface`
* Added the `ArgumentResolver` which features an extension point to resolve arguments in a more dynamic way
3.0.0
-----

View File

@ -12,57 +12,64 @@
namespace Symfony\Component\HttpKernel\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadataFactoryInterface;
/**
* Responsible for the creation of the action arguments.
* Responsible for the resolving of arguments passed to an action.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Iltar van der Berg <kjarli@gmail.com>
*/
class ArgumentResolver implements ArgumentResolverInterface
final class ArgumentResolver implements ArgumentResolverInterface
{
private $argumentMetadataFactory;
/**
* @var ArgumentValueResolverInterface[]
*/
private $argumentValueResolvers;
public function __construct(ArgumentMetadataFactoryInterface $argumentMetadataFactory = null, array $argumentValueResolvers = array())
{
$this->argumentMetadataFactory = $argumentMetadataFactory;
$this->argumentValueResolvers = $argumentValueResolvers;
}
/**
* {@inheritdoc}
*/
public function getArguments(Request $request, $controller)
{
if (is_array($controller)) {
$r = new \ReflectionMethod($controller[0], $controller[1]);
} elseif (is_object($controller) && !$controller instanceof \Closure) {
$r = new \ReflectionObject($controller);
$r = $r->getMethod('__invoke');
} else {
$r = new \ReflectionFunction($controller);
}
return $this->doGetArguments($request, $controller, $r->getParameters());
}
protected function doGetArguments(Request $request, $controller, array $parameters)
{
$attributes = $request->attributes->all();
$arguments = array();
foreach ($parameters as $param) {
if (array_key_exists($param->name, $attributes)) {
if (PHP_VERSION_ID >= 50600 && $param->isVariadic() && is_array($attributes[$param->name])) {
$arguments = array_merge($arguments, array_values($attributes[$param->name]));
} else {
$arguments[] = $attributes[$param->name];
}
} elseif ($param->getClass() && $param->getClass()->isInstance($request)) {
$arguments[] = $request;
} elseif ($param->isDefaultValueAvailable()) {
$arguments[] = $param->getDefaultValue();
} else {
if (is_array($controller)) {
$repr = sprintf('%s::%s()', get_class($controller[0]), $controller[1]);
} elseif (is_object($controller)) {
$repr = get_class($controller);
} else {
$repr = $controller;
foreach ($this->argumentMetadataFactory->createArgumentMetadata($controller) as $metadata) {
foreach ($this->argumentValueResolvers as $resolver) {
if (!$resolver->supports($request, $metadata)) {
continue;
}
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).', $repr, $param->name));
$resolved = $resolver->resolve($request, $metadata);
if (!$resolved instanceof \Generator) {
throw new \InvalidArgumentException(sprintf('%s::resolve() must yield at least one value.', get_class($resolver)));
}
foreach ($resolved as $append) {
$arguments[] = $append;
}
// continue to the next controller argument
continue 2;
}
$representative = $controller;
if (is_array($representative)) {
$representative = sprintf('%s::%s()', get_class($representative[0]), $representative[1]);
} elseif (is_object($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()));
}
return $arguments;

View File

@ -24,8 +24,8 @@ interface ArgumentResolverInterface
/**
* Returns the arguments to pass to the controller.
*
* @param Request $request A Request instance
* @param callable $controller A PHP callable
* @param Request $request
* @param callable $controller
*
* @return array An array of arguments to pass to the controller
*

View File

@ -0,0 +1,40 @@
<?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\HttpKernel\Controller\ArgumentValueResolver;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface;
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata;
/**
* Grabs a non-variadic value from the request and returns it.
*
* @author Iltar van der Berg <kjarli@gmail.com>
*/
final class ArgumentFromAttributeResolver implements ArgumentValueResolverInterface
{
/**
* {@inheritdoc}
*/
public function supports(Request $request, ArgumentMetadata $argument)
{
return !$argument->isVariadic() && $request->attributes->has($argument->getName());
}
/**
* {@inheritdoc}
*/
public function resolve(Request $request, ArgumentMetadata $argument)
{
yield $request->attributes->get($argument->getName());
}
}

View File

@ -0,0 +1,40 @@
<?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\HttpKernel\Controller\ArgumentValueResolver;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface;
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata;
/**
* Returns the default value defined in the action signature if present and no value has been given.
*
* @author Iltar van der Berg <kjarli@gmail.com>
*/
final class DefaultArgumentValueResolver implements ArgumentValueResolverInterface
{
/**
* {@inheritdoc}
*/
public function supports(Request $request, ArgumentMetadata $argument)
{
return $argument->hasDefaultValue() && !$request->attributes->has($argument->getName());
}
/**
* {@inheritdoc}
*/
public function resolve(Request $request, ArgumentMetadata $argument)
{
yield $argument->getDefaultValue();
}
}

View File

@ -0,0 +1,40 @@
<?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\HttpKernel\Controller\ArgumentValueResolver;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface;
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata;
/**
* Supports the same instance as the request object passed along.
*
* @author Iltar van der Berg <kjarli@gmail.com>
*/
final class RequestResolver implements ArgumentValueResolverInterface
{
/**
* {@inheritdoc}
*/
public function supports(Request $request, ArgumentMetadata $argument)
{
return $argument->getType() === Request::class || is_subclass_of($request, $argument->getType());
}
/**
* {@inheritdoc}
*/
public function resolve(Request $request, ArgumentMetadata $argument)
{
yield $request;
}
}

View File

@ -0,0 +1,48 @@
<?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\HttpKernel\Controller\ArgumentValueResolver;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface;
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata;
/**
* Grabs the variadic value from the request and returns it.
*
* @author Iltar van der Berg <kjarli@gmail.com>
*/
final class VariadicArgumentValueResolver implements ArgumentValueResolverInterface
{
/**
* {@inheritdoc}
*/
public function supports(Request $request, ArgumentMetadata $argument)
{
return $argument->isVariadic() && $request->attributes->has($argument->getName());
}
/**
* {@inheritdoc}
*/
public function resolve(Request $request, ArgumentMetadata $argument)
{
$values = $request->attributes->get($argument->getName());
if (!is_array($values)) {
throw new \InvalidArgumentException(sprintf('The action argument "...$%1$s" is required to be an array, the request attribute "%1$s" contains a type of "%2$s" instead.', $argument->getName(), gettype($values)));
}
foreach ($values as $value) {
yield $value;
}
}
}

View File

@ -0,0 +1,45 @@
<?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\HttpKernel\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata;
/**
* Responsible for resolving the value of an argument based on its metadata.
*
* @author Iltar van der Berg <kjarli@gmail.com>
*/
interface ArgumentValueResolverInterface
{
/**
* Whether this resolver can resolve can resolve the value for the given ArgumentMetadata.
*
* @param Request $request
* @param ArgumentMetadata $argument
*
* @return bool
*/
public function supports(Request $request, ArgumentMetadata $argument);
/**
* Yield the possible value(s).
*
* An implementation must yield at least one value.
*
* @param Request $request
* @param ArgumentMetadata $argument
*
* @return \Generator
*/
public function resolve(Request $request, ArgumentMetadata $argument);
}

View File

@ -23,7 +23,7 @@ use Symfony\Component\HttpFoundation\Request;
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class ControllerResolver extends ArgumentResolver implements ControllerResolverInterface
class ControllerResolver extends LegacyArgumentResolver implements ControllerResolverInterface
{
private $logger;
@ -85,21 +85,21 @@ class ControllerResolver extends ArgumentResolver implements ControllerResolverI
/**
* {@inheritdoc}
*
* @deprecated this method is deprecated as of 3.1 and will be removed in 4.0. Implement the ArgumentResolverInterface or extend the ArgumentResolver instead.
* @deprecated this method is deprecated as of 3.1 and will be removed in 4.0. Implement the ArgumentResolverInterface or extend the LegacyArgumentResolver instead.
*/
public function getArguments(Request $request, $controller)
{
@trigger_error(sprintf('%s is deprecated as of 3.1 and will be removed in 4.0. Implement the %s or extend the %s and inject it in the HttpKernel instead.', __METHOD__, ArgumentResolverInterface::class, ArgumentResolver::class), E_USER_DEPRECATED);
@trigger_error(sprintf('%s is deprecated as of 3.1 and will be removed in 4.0. Implement the %s or extend the %s and inject it in the HttpKernel instead.', __METHOD__, ArgumentResolverInterface::class, LegacyArgumentResolver::class), E_USER_DEPRECATED);
return parent::getArguments($request, $controller);
}
/**
* @deprecated this method is deprecated as of 3.1 and will be removed in 4.0. Implement the ArgumentResolverInterface or extend the ArgumentResolver instead.
* @deprecated this method is deprecated as of 3.1 and will be removed in 4.0. Implement the ArgumentResolverInterface or extend the LegacyArgumentResolver instead.
*/
protected function doGetArguments(Request $request, $controller, array $parameters)
{
@trigger_error(sprintf('%s is deprecated as of 3.1 and will be removed in 4.0. Implement the %s or extend the %s and inject it in the HttpKernel instead.', __METHOD__, ArgumentResolverInterface::class, ArgumentResolver::class), E_USER_DEPRECATED);
@trigger_error(sprintf('%s is deprecated as of 3.1 and will be removed in 4.0. Implement the %s or extend the %s and inject it in the HttpKernel instead.', __METHOD__, ArgumentResolverInterface::class, LegacyArgumentResolver::class), E_USER_DEPRECATED);
return parent::doGetArguments($request, $controller, $parameters);
}

View File

@ -0,0 +1,75 @@
<?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\HttpKernel\Controller;
use Symfony\Component\HttpFoundation\Request;
/**
* Responsible for the creation of the action arguments.
*
* @deprecated This class is deprecated since 3.1 and will be removed in 4.0. Please use the ArgumentResolver::class instead.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class LegacyArgumentResolver implements ArgumentResolverInterface
{
/**
* {@inheritdoc}
*/
public function getArguments(Request $request, $controller)
{
// only trigger the deprecation notice if actually used, the ControllerResolver still extends this for BC reasons
@trigger_error(sprintf('The %s class is deprecated since 3.1 and will be removed in 4.0. Please use the %s instead.', __CLASS__, ArgumentResolver::class), E_USER_DEPRECATED);
if (is_array($controller)) {
$r = new \ReflectionMethod($controller[0], $controller[1]);
} elseif (is_object($controller) && !$controller instanceof \Closure) {
$r = new \ReflectionObject($controller);
$r = $r->getMethod('__invoke');
} else {
$r = new \ReflectionFunction($controller);
}
return $this->doGetArguments($request, $controller, $r->getParameters());
}
protected function doGetArguments(Request $request, $controller, array $parameters)
{
$attributes = $request->attributes->all();
$arguments = array();
foreach ($parameters as $param) {
if (array_key_exists($param->name, $attributes)) {
if (PHP_VERSION_ID >= 50600 && $param->isVariadic() && is_array($attributes[$param->name])) {
$arguments = array_merge($arguments, array_values($attributes[$param->name]));
} else {
$arguments[] = $attributes[$param->name];
}
} elseif ($param->getClass() && $param->getClass()->isInstance($request)) {
$arguments[] = $request;
} elseif ($param->isDefaultValueAvailable()) {
$arguments[] = $param->getDefaultValue();
} else {
if (is_array($controller)) {
$repr = sprintf('%s::%s()', get_class($controller[0]), $controller[1]);
} elseif (is_object($controller)) {
$repr = get_class($controller);
} else {
$repr = $controller;
}
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).', $repr, $param->name));
}
}
return $arguments;
}
}

View File

@ -38,6 +38,7 @@ class TraceableControllerResolver implements ControllerResolverInterface, Argume
$this->stopwatch = $stopwatch;
$this->argumentResolver = $argumentResolver;
// required for BC reasons
if (null === $this->argumentResolver) {
$this->argumentResolver = $resolver;
}

View File

@ -0,0 +1,98 @@
<?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\HttpKernel\ControllerMetadata;
/**
* Responsible for storing metadata of an argument.
*
* @author Iltar van der Berg <kjarli@gmail.com>
*/
class ArgumentMetadata
{
private $name;
private $type;
private $isVariadic;
private $hasDefaultValue;
private $defaultValue;
/**
* @param string $name
* @param string $type
* @param bool $isVariadic
* @param bool $hasDefaultValue
* @param mixed $defaultValue
*/
public function __construct($name, $type, $isVariadic, $hasDefaultValue, $defaultValue)
{
$this->name = $name;
$this->type = $type;
$this->isVariadic = $isVariadic;
$this->hasDefaultValue = $hasDefaultValue;
$this->defaultValue = $defaultValue;
}
/**
* Returns the name as given in PHP, $foo would yield "foo".
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Returns the type of the argument.
*
* The type is the PHP class in 5.5+ and additionally the basic type in PHP 7.0+.
*
* @return string
*/
public function getType()
{
return $this->type;
}
/**
* Returns whether the argument is defined as "...$variadic".
*
* @return bool
*/
public function isVariadic()
{
return $this->isVariadic;
}
/**
* Returns whether the argument has a default value.
*
* Implies whether an argument is optional.
*
* @return bool
*/
public function hasDefaultValue()
{
return $this->hasDefaultValue;
}
/**
* Returns the default value of the argument.
*
* Make sure to call {@see self::hasDefaultValue()} first to see if a default value is possible.
*
* @return mixed
*/
public function getDefaultValue()
{
return $this->defaultValue;
}
}

View File

@ -0,0 +1,109 @@
<?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\HttpKernel\ControllerMetadata;
/**
* Builds {@see ArgumentMetadata} objects based on the given Controller.
*
* @author Iltar van der Berg <kjarli@gmail.com>
*/
final class ArgumentMetadataFactory implements ArgumentMetadataFactoryInterface
{
/**
* {@inheritdoc}
*/
public function createArgumentMetadata($controller)
{
$arguments = array();
if (is_array($controller)) {
$reflection = new \ReflectionMethod($controller[0], $controller[1]);
} elseif (is_object($controller) && !$controller instanceof \Closure) {
$reflection = (new \ReflectionObject($controller))->getMethod('__invoke');
} else {
$reflection = new \ReflectionFunction($controller);
}
foreach ($reflection->getParameters() as $param) {
$arguments[] = new ArgumentMetadata($param->getName(), $this->getType($param), $this->isVariadic($param), $this->hasDefaulValue($param), $this->getDefaulValue($param));
}
return $arguments;
}
/**
* Returns whether an argument is variadic.
*
* @param \ReflectionParameter $parameter
*
* @return bool
*/
private function isVariadic(\ReflectionParameter $parameter)
{
return PHP_VERSION_ID >= 50600 && $parameter->isVariadic();
}
/**
* Determines whether an argument has a default value.
*
* @param \ReflectionParameter $parameter
*
* @return bool
*/
private function hasDefaulValue(\ReflectionParameter $parameter)
{
return $parameter->isDefaultValueAvailable();
}
/**
* Returns a default value if available.
*
* @param \ReflectionParameter $parameter
*
* @return mixed|null
*/
private function getDefaulValue(\ReflectionParameter $parameter)
{
return $this->hasDefaulValue($parameter) ? $parameter->getDefaultValue() : null;
}
/**
* Returns an associated type to the given parameter if available.
*
* @param \ReflectionParameter $parameter
*
* @return null|string
*/
private function getType(\ReflectionParameter $parameter)
{
if (PHP_VERSION_ID >= 70000) {
return $parameter->hasType() ? (string) $parameter->getType() : null;
}
if ($parameter->isArray()) {
return 'array';
}
if ($parameter->isCallable()) {
return 'callable';
}
try {
$refClass = $parameter->getClass();
} catch (\ReflectionException $e) {
// mandatory; extract it from the exception message
return str_replace(['Class ', ' does not exist'], '', $e->getMessage());
}
return $refClass ? $refClass->getName() : null;
}
}

View File

@ -0,0 +1,27 @@
<?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\HttpKernel\ControllerMetadata;
/**
* Builds method argument data.
*
* @author Iltar van der Berg <kjarli@gmail.com>
*/
interface ArgumentMetadataFactoryInterface
{
/**
* @param mixed $controller The controller to resolve the arguments for.
*
* @return ArgumentMetadata[]
*/
public function createArgumentMetadata($controller);
}

View File

@ -11,7 +11,6 @@
namespace Symfony\Component\HttpKernel;
use Symfony\Component\HttpKernel\Controller\ArgumentResolver;
use Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface;
use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
@ -40,14 +39,6 @@ class HttpKernel implements HttpKernelInterface, TerminableInterface
protected $requestStack;
private $argumentResolver;
/**
* Constructor.
*
* @param EventDispatcherInterface $dispatcher An EventDispatcherInterface instance
* @param ControllerResolverInterface $resolver A ControllerResolverInterface instance
* @param RequestStack $requestStack A stack for master/sub requests
* @param ArgumentResolverInterface $argumentResolver An ArgumentResolverInterface instance
*/
public function __construct(EventDispatcherInterface $dispatcher, ControllerResolverInterface $resolver, RequestStack $requestStack = null, ArgumentResolverInterface $argumentResolver = null)
{
$this->dispatcher = $dispatcher;

View File

@ -12,18 +12,28 @@
namespace Symfony\Component\HttpKernel\Tests\Controller;
use Symfony\Component\HttpKernel\Controller\ArgumentResolver;
use Symfony\Component\HttpKernel\Controller\ControllerResolver;
use Symfony\Component\HttpKernel\Controller\ArgumentValueResolver\ArgumentFromAttributeResolver;
use Symfony\Component\HttpKernel\Controller\ArgumentValueResolver\DefaultArgumentValueResolver;
use Symfony\Component\HttpKernel\Controller\ArgumentValueResolver\RequestResolver;
use Symfony\Component\HttpKernel\Controller\ArgumentValueResolver\VariadicArgumentValueResolver;
use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface;
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadataFactory;
use Symfony\Component\HttpKernel\Tests\Fixtures\Controller\VariadicController;
use Symfony\Component\HttpFoundation\Request;
class ArgumentResolverTest extends \PHPUnit_Framework_TestCase
{
/**
* @group legacy
*/
public function testGetArguments()
{
$resolver = new ArgumentResolver();
$factory = new ArgumentMetadataFactory();
$argumentValueResolvers = array(
new ArgumentFromAttributeResolver(),
new VariadicArgumentValueResolver(),
new RequestResolver(),
new DefaultArgumentValueResolver(),
);
$resolver = new ArgumentResolver($factory, $argumentValueResolvers);
$request = Request::create('/');
$controller = array(new self(), 'testGetArguments');
@ -62,7 +72,7 @@ class ArgumentResolverTest extends \PHPUnit_Framework_TestCase
$request = Request::create('/');
$request->attributes->set('foo', 'foo');
$request->attributes->set('foobar', 'foobar');
$controller = 'Symfony\Component\HttpKernel\Tests\Controller\another_controller_function';
$controller = 'Symfony\Component\HttpKernel\Tests\Controller\argument_resolver_controller_function';
$this->assertEquals(array('foo', 'foobar'), $resolver->getArguments($request, $controller));
$request = Request::create('/');
@ -84,11 +94,18 @@ class ArgumentResolverTest extends \PHPUnit_Framework_TestCase
/**
* @requires PHP 5.6
* @group legacy
*/
public function testGetVariadicArguments()
{
$resolver = new ControllerResolver();
$factory = new ArgumentMetadataFactory();
$argumentValueResolvers = array(
new ArgumentFromAttributeResolver(),
new VariadicArgumentValueResolver(),
new RequestResolver(),
new DefaultArgumentValueResolver(),
);
$resolver = new ArgumentResolver($factory, $argumentValueResolvers);
$request = Request::create('/');
$request->attributes->set('foo', 'foo');
@ -97,14 +114,47 @@ class ArgumentResolverTest extends \PHPUnit_Framework_TestCase
$this->assertEquals(array('foo', 'foo', 'bar'), $resolver->getArguments($request, $controller));
}
public function testCreateControllerCanReturnAnyCallable()
/**
* @requires PHP 5.6
* @expectedException \InvalidArgumentException
*/
public function testGetVariadicArgumentsWithoutArrayInRequest()
{
$mock = $this->getMock('Symfony\Component\HttpKernel\Controller\ControllerResolver', array('createController'));
$mock->expects($this->once())->method('createController')->will($this->returnValue('Symfony\Component\HttpKernel\Tests\Controller\another_controller_function'));
$factory = new ArgumentMetadataFactory();
$argumentValueResolvers = array(
new ArgumentFromAttributeResolver(),
new VariadicArgumentValueResolver(),
new RequestResolver(),
new DefaultArgumentValueResolver(),
);
$resolver = new ArgumentResolver($factory, $argumentValueResolvers);
$request = Request::create('/');
$request->attributes->set('_controller', 'foobar');
$mock->getController($request);
$request->attributes->set('foo', 'foo');
$request->attributes->set('bar', 'foo');
$controller = array(new VariadicController(), 'action');
$resolver->getArguments($request, $controller);
}
/**
* @requires PHP 5.6
* @expectedException \InvalidArgumentException
*/
public function testGetArgumentWithoutArray()
{
$factory = new ArgumentMetadataFactory();
$valueResolver = $this->getMock(ArgumentValueResolverInterface::class);
$resolver = new ArgumentResolver($factory, array($valueResolver));
$valueResolver->expects($this->any())->method('supports')->willReturn(true);
$valueResolver->expects($this->any())->method('resolve')->willReturn('foo');
$request = Request::create('/');
$request->attributes->set('foo', 'foo');
$request->attributes->set('bar', 'foo');
$controller = array($this, 'controllerMethod2');
$resolver->getArguments($request, $controller);
}
public function __invoke($foo, $bar = null)
@ -132,6 +182,6 @@ class ArgumentResolverTest extends \PHPUnit_Framework_TestCase
}
}
function another_controller_function($foo, $foobar)
function argument_resolver_controller_function($foo, $foobar)
{
}

View File

@ -0,0 +1,123 @@
<?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\HttpKernel\Tests\Controller;
use Symfony\Component\HttpKernel\Controller\LegacyArgumentResolver;
use Symfony\Component\HttpKernel\Controller\ControllerResolver;
use Symfony\Component\HttpKernel\Tests\Fixtures\Controller\VariadicController;
use Symfony\Component\HttpFoundation\Request;
class LegacyArgumentResolverTest extends \PHPUnit_Framework_TestCase
{
public function testGetArguments()
{
$resolver = new LegacyArgumentResolver();
$request = Request::create('/');
$controller = array(new self(), 'testGetArguments');
$this->assertEquals(array(), $resolver->getArguments($request, $controller), '->getArguments() returns an empty array if the method takes no arguments');
$request = Request::create('/');
$request->attributes->set('foo', 'foo');
$controller = array(new self(), 'controllerMethod1');
$this->assertEquals(array('foo'), $resolver->getArguments($request, $controller), '->getArguments() returns an array of arguments for the controller method');
$request = Request::create('/');
$request->attributes->set('foo', 'foo');
$controller = array(new self(), 'controllerMethod2');
$this->assertEquals(array('foo', null), $resolver->getArguments($request, $controller), '->getArguments() uses default values if present');
$request->attributes->set('bar', 'bar');
$this->assertEquals(array('foo', 'bar'), $resolver->getArguments($request, $controller), '->getArguments() overrides default values if provided in the request attributes');
$request = Request::create('/');
$request->attributes->set('foo', 'foo');
$controller = function ($foo) {};
$this->assertEquals(array('foo'), $resolver->getArguments($request, $controller));
$request = Request::create('/');
$request->attributes->set('foo', 'foo');
$controller = function ($foo, $bar = 'bar') {};
$this->assertEquals(array('foo', 'bar'), $resolver->getArguments($request, $controller));
$request = Request::create('/');
$request->attributes->set('foo', 'foo');
$controller = new self();
$this->assertEquals(array('foo', null), $resolver->getArguments($request, $controller));
$request->attributes->set('bar', 'bar');
$this->assertEquals(array('foo', 'bar'), $resolver->getArguments($request, $controller));
$request = Request::create('/');
$request->attributes->set('foo', 'foo');
$request->attributes->set('foobar', 'foobar');
$controller = 'Symfony\Component\HttpKernel\Tests\Controller\another_controller_function';
$this->assertEquals(array('foo', 'foobar'), $resolver->getArguments($request, $controller));
$request = Request::create('/');
$request->attributes->set('foo', 'foo');
$request->attributes->set('foobar', 'foobar');
$controller = array(new self(), 'controllerMethod3');
try {
$resolver->getArguments($request, $controller);
$this->fail('->getArguments() throws a \RuntimeException exception if it cannot determine the argument value');
} catch (\Exception $e) {
$this->assertInstanceOf('\RuntimeException', $e, '->getArguments() throws a \RuntimeException exception if it cannot determine the argument value');
}
$request = Request::create('/');
$controller = array(new self(), 'controllerMethod5');
$this->assertEquals(array($request), $resolver->getArguments($request, $controller), '->getArguments() injects the request');
}
/**
* @requires PHP 5.6
*/
public function testGetVariadicArguments()
{
$resolver = new ControllerResolver();
$request = Request::create('/');
$request->attributes->set('foo', 'foo');
$request->attributes->set('bar', array('foo', 'bar'));
$controller = array(new VariadicController(), 'action');
$this->assertEquals(array('foo', 'foo', 'bar'), $resolver->getArguments($request, $controller));
}
public function __invoke($foo, $bar = null)
{
}
public function controllerMethod1($foo)
{
}
protected function controllerMethod2($foo, $bar = null)
{
}
protected function controllerMethod3($foo, $bar, $foobar)
{
}
protected static function controllerMethod4()
{
}
protected function controllerMethod5(Request $request)
{
}
}
function another_controller_function($foo, $foobar)
{
}

View File

@ -0,0 +1,133 @@
<?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\HttpKernel\Tests\ControllerMetadata;
use Fake\ImportedAndFake;
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata;
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadataFactory;
use Symfony\Component\HttpKernel\Tests\Fixtures\Controller\BasicTypesController;
use Symfony\Component\HttpKernel\Tests\Fixtures\Controller\VariadicController;
class ArgumentMetadataFactoryTest extends \PHPUnit_Framework_TestCase
{
private $factory;
protected function setUp()
{
$this->factory = new ArgumentMetadataFactory();
}
public function testSignature1()
{
$arguments = $this->factory->createArgumentMetadata([$this, 'signature1']);
$this->assertEquals(array(
new ArgumentMetadata('foo', self::class, false, false, null),
new ArgumentMetadata('bar', 'array', false, false, null),
new ArgumentMetadata('baz', 'callable', false, false, null),
), $arguments);
}
public function testSignature2()
{
$arguments = $this->factory->createArgumentMetadata([$this, 'signature2']);
$this->assertEquals(array(
new ArgumentMetadata('foo', self::class, false, true, null),
new ArgumentMetadata('bar', __NAMESPACE__.'\FakeClassThatDoesNotExist', false, true, null),
new ArgumentMetadata('baz', 'Fake\ImportedAndFake', false, true, null),
), $arguments);
}
public function testSignature3()
{
$arguments = $this->factory->createArgumentMetadata([$this, 'signature3']);
$this->assertEquals(array(
new ArgumentMetadata('bar', __NAMESPACE__.'\FakeClassThatDoesNotExist', false, false, null),
new ArgumentMetadata('baz', 'Fake\ImportedAndFake', false, false, null),
), $arguments);
}
public function testSignature4()
{
$arguments = $this->factory->createArgumentMetadata([$this, 'signature4']);
$this->assertEquals(array(
new ArgumentMetadata('foo', null, false, true, 'default'),
new ArgumentMetadata('bar', null, false, true, 500),
new ArgumentMetadata('baz', null, false, true, []),
), $arguments);
}
public function testSignature5()
{
$arguments = $this->factory->createArgumentMetadata([$this, 'signature5']);
$this->assertEquals(array(
new ArgumentMetadata('foo', 'array', false, true, null),
new ArgumentMetadata('bar', null, false, false, null),
), $arguments);
}
/**
* @requires PHP 5.6
*/
public function testVariadicSignature()
{
$arguments = $this->factory->createArgumentMetadata([new VariadicController(), 'action']);
$this->assertEquals(array(
new ArgumentMetadata('foo', null, false, false, null),
new ArgumentMetadata('bar', null, true, false, null),
), $arguments);
}
/**
* @requires PHP 7.0
*/
public function testBasicTypesSignature()
{
$arguments = $this->factory->createArgumentMetadata([new BasicTypesController(), 'action']);
$this->assertEquals(array(
new ArgumentMetadata('foo', 'string', false, false, null),
new ArgumentMetadata('bar', 'int', false, false, null),
new ArgumentMetadata('baz', 'float', false, false, null),
), $arguments);
}
private function signature1(ArgumentMetadataFactoryTest $foo, array $bar, callable $baz)
{
}
private function signature2(ArgumentMetadataFactoryTest $foo = null, FakeClassThatDoesNotExist $bar = null, ImportedAndFake $baz = null)
{
}
private function signature3(FakeClassThatDoesNotExist $bar, ImportedAndFake $baz)
{
}
private function signature4($foo = 'default', $bar = 500, $baz = [])
{
}
private function signature5(array $foo = null, $bar)
{
}
}

View File

@ -11,6 +11,7 @@
namespace Symfony\Component\HttpKernel\Tests\DataCollector;
use Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface;
use Symfony\Component\HttpKernel\HttpKernel;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\DataCollector\RequestDataCollector;
@ -191,7 +192,7 @@ class RequestDataCollectorTest extends \PHPUnit_Framework_TestCase
protected function injectController($collector, $controller, $request)
{
$resolver = $this->getMock('Symfony\\Component\\HttpKernel\\Controller\\ControllerResolverInterface');
$httpKernel = new HttpKernel(new EventDispatcher(), $resolver);
$httpKernel = new HttpKernel(new EventDispatcher(), $resolver, null, $this->getMock(ArgumentResolverInterface::class));
$event = new FilterControllerEvent($httpKernel, $controller, $request, HttpKernelInterface::MASTER_REQUEST);
$collector->onKernelController($event);
}

View File

@ -0,0 +1,10 @@
<?php
namespace Symfony\Component\HttpKernel\Tests\Fixtures\Controller;
class BasicTypesController
{
public function action(string $foo, int $bar, float $baz)
{
}
}

View File

@ -13,7 +13,13 @@ namespace Symfony\Component\HttpKernel\Tests\Fragment;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpKernel\Controller\ArgumentResolver;
use Symfony\Component\HttpKernel\Controller\ArgumentValueResolver\ArgumentFromAttributeResolver;
use Symfony\Component\HttpKernel\Controller\ArgumentValueResolver\DefaultArgumentValueResolver;
use Symfony\Component\HttpKernel\Controller\ArgumentValueResolver\RequestResolver;
use Symfony\Component\HttpKernel\Controller\ArgumentValueResolver\VariadicArgumentValueResolver;
use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface;
use Symfony\Component\HttpKernel\Controller\ControllerReference;
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadataFactory;
use Symfony\Component\HttpKernel\HttpKernel;
use Symfony\Component\HttpKernel\Fragment\InlineFragmentRenderer;
use Symfony\Component\HttpKernel\KernelEvents;
@ -51,7 +57,10 @@ class InlineFragmentRendererTest extends \PHPUnit_Framework_TestCase
$strategy->render(new ControllerReference('main_controller', array('object' => $object), array()), Request::create('/'));
}
public function testRenderWithObjectsAsAttributesPassedAsObjectsInTheController()
/**
* @group legacy
*/
public function testRenderWithObjectsAsAttributesPassedAsObjectsInTheControllerLegacy()
{
$resolver = $this->getMock('Symfony\\Component\\HttpKernel\\Controller\\ControllerResolver', array('getController'));
$resolver
@ -62,7 +71,34 @@ class InlineFragmentRendererTest extends \PHPUnit_Framework_TestCase
}))
;
$kernel = new HttpKernel(new EventDispatcher(), $resolver, new RequestStack(), new ArgumentResolver());
$kernel = new HttpKernel(new EventDispatcher(), $resolver, new RequestStack());
$renderer = new InlineFragmentRenderer($kernel);
$response = $renderer->render(new ControllerReference('main_controller', array('object' => new \stdClass(), 'object1' => new Bar()), array()), Request::create('/'));
$this->assertEquals('bar', $response->getContent());
}
/**
* @group legacy
*/
public function testRenderWithObjectsAsAttributesPassedAsObjectsInTheController()
{
$resolver = $this->getMock(ControllerResolverInterface::class);
$resolver
->expects($this->once())
->method('getController')
->will($this->returnValue(function (\stdClass $object, Bar $object1) {
return new Response($object1->getBar());
}))
;
$argumentValueResolvers = array(
new ArgumentFromAttributeResolver(),
new VariadicArgumentValueResolver(),
new RequestResolver(),
new DefaultArgumentValueResolver(),
);
$kernel = new HttpKernel(new EventDispatcher(), $resolver, new RequestStack(), new ArgumentResolver(new ArgumentMetadataFactory(), $argumentValueResolvers));
$renderer = new InlineFragmentRenderer($kernel);
$response = $renderer->render(new ControllerReference('main_controller', array('object' => new \stdClass(), 'object1' => new Bar()), array()), Request::create('/'));

View File

@ -11,6 +11,7 @@
namespace Symfony\Component\HttpKernel\Tests\HttpCache;
use Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface;
use Symfony\Component\HttpKernel\HttpKernel;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpFoundation\Request;
@ -18,7 +19,7 @@ use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface;
use Symfony\Component\EventDispatcher\EventDispatcher;
class TestHttpKernel extends HttpKernel implements ControllerResolverInterface
class TestHttpKernel extends HttpKernel implements ControllerResolverInterface, ArgumentResolverInterface
{
protected $body;
protected $status;
@ -35,7 +36,7 @@ class TestHttpKernel extends HttpKernel implements ControllerResolverInterface
$this->headers = $headers;
$this->customizer = $customizer;
parent::__construct(new EventDispatcher(), $this);
parent::__construct(new EventDispatcher(), $this, null, $this);
}
public function getBackendRequest()

View File

@ -11,6 +11,7 @@
namespace Symfony\Component\HttpKernel\Tests\HttpCache;
use Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface;
use Symfony\Component\HttpKernel\HttpKernel;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpFoundation\Request;
@ -18,7 +19,7 @@ use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface;
use Symfony\Component\EventDispatcher\EventDispatcher;
class TestMultipleHttpKernel extends HttpKernel implements ControllerResolverInterface
class TestMultipleHttpKernel extends HttpKernel implements ControllerResolverInterface, ArgumentResolverInterface
{
protected $bodies = array();
protected $statuses = array();
@ -34,7 +35,7 @@ class TestMultipleHttpKernel extends HttpKernel implements ControllerResolverInt
$this->headers[] = $response['headers'];
}
parent::__construct(new EventDispatcher(), $this);
parent::__construct(new EventDispatcher(), $this, null, $this);
}
public function getBackendRequest()

View File

@ -11,6 +11,10 @@
namespace Symfony\Component\HttpKernel\Tests;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface;
use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface;
use Symfony\Component\HttpKernel\HttpKernel;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\KernelEvents;
@ -28,7 +32,7 @@ class HttpKernelTest extends \PHPUnit_Framework_TestCase
*/
public function testHandleWhenControllerThrowsAnExceptionAndCatchIsTrue()
{
$kernel = new HttpKernel(new EventDispatcher(), $this->getResolver(function () { throw new \RuntimeException(); }));
$kernel = $this->getHttpKernel(new EventDispatcher(), function () { throw new \RuntimeException(); });
$kernel->handle(new Request(), HttpKernelInterface::MASTER_REQUEST, true);
}
@ -38,7 +42,7 @@ class HttpKernelTest extends \PHPUnit_Framework_TestCase
*/
public function testHandleWhenControllerThrowsAnExceptionAndCatchIsFalseAndNoListenerIsRegistered()
{
$kernel = new HttpKernel(new EventDispatcher(), $this->getResolver(function () { throw new \RuntimeException(); }));
$kernel = $this->getHttpKernel(new EventDispatcher(), function () { throw new \RuntimeException(); });
$kernel->handle(new Request(), HttpKernelInterface::MASTER_REQUEST, false);
}
@ -50,7 +54,7 @@ class HttpKernelTest extends \PHPUnit_Framework_TestCase
$event->setResponse(new Response($event->getException()->getMessage()));
});
$kernel = new HttpKernel($dispatcher, $this->getResolver(function () { throw new \RuntimeException('foo'); }));
$kernel = $this->getHttpKernel($dispatcher, function () { throw new \RuntimeException('foo'); });
$response = $kernel->handle(new Request(), HttpKernelInterface::MASTER_REQUEST, true);
$this->assertEquals('500', $response->getStatusCode());
@ -66,7 +70,7 @@ class HttpKernelTest extends \PHPUnit_Framework_TestCase
// should set a response, but does not
});
$kernel = new HttpKernel($dispatcher, $this->getResolver(function () use ($exception) { throw $exception; }));
$kernel = $this->getHttpKernel($dispatcher, function () use ($exception) { throw $exception; });
try {
$kernel->handle(new Request(), HttpKernelInterface::MASTER_REQUEST, true);
@ -83,7 +87,7 @@ class HttpKernelTest extends \PHPUnit_Framework_TestCase
$event->setResponse(new RedirectResponse('/login', 301));
});
$kernel = new HttpKernel($dispatcher, $this->getResolver(function () { throw new AccessDeniedHttpException(); }));
$kernel = $this->getHttpKernel($dispatcher, function () { throw new AccessDeniedHttpException(); });
$response = $kernel->handle(new Request());
$this->assertEquals('301', $response->getStatusCode());
@ -97,7 +101,7 @@ class HttpKernelTest extends \PHPUnit_Framework_TestCase
$event->setResponse(new Response($event->getException()->getMessage()));
});
$kernel = new HttpKernel($dispatcher, $this->getResolver(function () { throw new MethodNotAllowedHttpException(array('POST')); }));
$kernel = $this->getHttpKernel($dispatcher, function () { throw new MethodNotAllowedHttpException(array('POST')); });
$response = $kernel->handle(new Request());
$this->assertEquals('405', $response->getStatusCode());
@ -114,7 +118,7 @@ class HttpKernelTest extends \PHPUnit_Framework_TestCase
$event->setResponse(new Response('', $responseStatusCode, array('X-Status-Code' => $expectedStatusCode)));
});
$kernel = new HttpKernel($dispatcher, $this->getResolver(function () { throw new \RuntimeException(); }));
$kernel = $this->getHttpKernel($dispatcher, function () { throw new \RuntimeException(); });
$response = $kernel->handle(new Request());
$this->assertEquals($expectedStatusCode, $response->getStatusCode());
@ -138,7 +142,7 @@ class HttpKernelTest extends \PHPUnit_Framework_TestCase
$event->setResponse(new Response('hello'));
});
$kernel = new HttpKernel($dispatcher, $this->getResolver());
$kernel = $this->getHttpKernel($dispatcher);
$this->assertEquals('hello', $kernel->handle(new Request())->getContent());
}
@ -149,7 +153,7 @@ class HttpKernelTest extends \PHPUnit_Framework_TestCase
public function testHandleWhenNoControllerIsFound()
{
$dispatcher = new EventDispatcher();
$kernel = new HttpKernel($dispatcher, $this->getResolver(false));
$kernel = $this->getHttpKernel($dispatcher, false);
$kernel->handle(new Request());
}
@ -158,7 +162,7 @@ class HttpKernelTest extends \PHPUnit_Framework_TestCase
{
$response = new Response('foo');
$dispatcher = new EventDispatcher();
$kernel = new HttpKernel($dispatcher, $this->getResolver(function () use ($response) { return $response; }));
$kernel = $this->getHttpKernel($dispatcher, function () use ($response) { return $response; });
$this->assertSame($response, $kernel->handle(new Request()));
}
@ -166,7 +170,7 @@ class HttpKernelTest extends \PHPUnit_Framework_TestCase
public function testHandleWhenTheControllerIsAnObjectWithInvoke()
{
$dispatcher = new EventDispatcher();
$kernel = new HttpKernel($dispatcher, $this->getResolver(new Controller()));
$kernel = $this->getHttpKernel($dispatcher, new Controller());
$this->assertResponseEquals(new Response('foo'), $kernel->handle(new Request()));
}
@ -174,7 +178,7 @@ class HttpKernelTest extends \PHPUnit_Framework_TestCase
public function testHandleWhenTheControllerIsAFunction()
{
$dispatcher = new EventDispatcher();
$kernel = new HttpKernel($dispatcher, $this->getResolver('Symfony\Component\HttpKernel\Tests\controller_func'));
$kernel = $this->getHttpKernel($dispatcher, 'Symfony\Component\HttpKernel\Tests\controller_func');
$this->assertResponseEquals(new Response('foo'), $kernel->handle(new Request()));
}
@ -182,7 +186,7 @@ class HttpKernelTest extends \PHPUnit_Framework_TestCase
public function testHandleWhenTheControllerIsAnArray()
{
$dispatcher = new EventDispatcher();
$kernel = new HttpKernel($dispatcher, $this->getResolver(array(new Controller(), 'controller')));
$kernel = $this->getHttpKernel($dispatcher, array(new Controller(), 'controller'));
$this->assertResponseEquals(new Response('foo'), $kernel->handle(new Request()));
}
@ -190,7 +194,7 @@ class HttpKernelTest extends \PHPUnit_Framework_TestCase
public function testHandleWhenTheControllerIsAStaticArray()
{
$dispatcher = new EventDispatcher();
$kernel = new HttpKernel($dispatcher, $this->getResolver(array('Symfony\Component\HttpKernel\Tests\Controller', 'staticcontroller')));
$kernel = $this->getHttpKernel($dispatcher, array('Symfony\Component\HttpKernel\Tests\Controller', 'staticcontroller'));
$this->assertResponseEquals(new Response('foo'), $kernel->handle(new Request()));
}
@ -201,7 +205,7 @@ class HttpKernelTest extends \PHPUnit_Framework_TestCase
public function testHandleWhenTheControllerDoesNotReturnAResponse()
{
$dispatcher = new EventDispatcher();
$kernel = new HttpKernel($dispatcher, $this->getResolver(function () { return 'foo'; }));
$kernel = $this->getHttpKernel($dispatcher, function () { return 'foo'; });
$kernel->handle(new Request());
}
@ -212,7 +216,8 @@ class HttpKernelTest extends \PHPUnit_Framework_TestCase
$dispatcher->addListener(KernelEvents::VIEW, function ($event) {
$event->setResponse(new Response($event->getControllerResult()));
});
$kernel = new HttpKernel($dispatcher, $this->getResolver(function () { return 'foo'; }));
$kernel = $this->getHttpKernel($dispatcher, function () { return 'foo'; });
$this->assertEquals('foo', $kernel->handle(new Request())->getContent());
}
@ -223,7 +228,7 @@ class HttpKernelTest extends \PHPUnit_Framework_TestCase
$dispatcher->addListener(KernelEvents::RESPONSE, function ($event) {
$event->setResponse(new Response('foo'));
});
$kernel = new HttpKernel($dispatcher, $this->getResolver());
$kernel = $this->getHttpKernel($dispatcher);
$this->assertEquals('foo', $kernel->handle(new Request())->getContent());
}
@ -231,7 +236,7 @@ class HttpKernelTest extends \PHPUnit_Framework_TestCase
public function testTerminate()
{
$dispatcher = new EventDispatcher();
$kernel = new HttpKernel($dispatcher, $this->getResolver());
$kernel = $this->getHttpKernel($dispatcher);
$dispatcher->addListener(KernelEvents::TERMINATE, function ($event) use (&$called, &$capturedKernel, &$capturedRequest, &$capturedResponse) {
$called = true;
$capturedKernel = $event->getKernel();
@ -255,29 +260,33 @@ class HttpKernelTest extends \PHPUnit_Framework_TestCase
$stack->expects($this->at(1))->method('pop');
$dispatcher = new EventDispatcher();
$kernel = new HttpKernel($dispatcher, $this->getResolver(), $stack);
$kernel = $this->getHttpKernel($dispatcher, null, $stack);
$kernel->handle($request, HttpKernelInterface::MASTER_REQUEST);
}
protected function getResolver($controller = null)
private function getHttpKernel(EventDispatcherInterface $eventDispatcher, $controller = null, RequestStack $requestStack = null)
{
if (null === $controller) {
$controller = function () { return new Response('Hello'); };
}
$resolver = $this->getMock('Symfony\\Component\\HttpKernel\\Controller\\ControllerResolverInterface');
$resolver->expects($this->any())
$controllerResolver = $this->getMock(ControllerResolverInterface::class);
$controllerResolver
->expects($this->any())
->method('getController')
->will($this->returnValue($controller));
$resolver->expects($this->any())
$argumentResolver = $this->getMock(ArgumentResolverInterface::class);
$argumentResolver
->expects($this->any())
->method('getArguments')
->will($this->returnValue(array()));
return $resolver;
return new HttpKernel($eventDispatcher, $controllerResolver, $requestStack, $argumentResolver);
}
protected function assertResponseEquals(Response $expected, Response $actual)
private function assertResponseEquals(Response $expected, Response $actual)
{
$expected->setDate($actual->getDate());
$this->assertEquals($expected, $actual);

View File

@ -11,17 +11,18 @@
namespace Symfony\Component\HttpKernel\Tests;
use Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface;
use Symfony\Component\HttpKernel\HttpKernel;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface;
use Symfony\Component\EventDispatcher\EventDispatcher;
class TestHttpKernel extends HttpKernel implements ControllerResolverInterface
class TestHttpKernel extends HttpKernel implements ControllerResolverInterface, ArgumentResolverInterface
{
public function __construct()
{
parent::__construct(new EventDispatcher(), $this);
parent::__construct(new EventDispatcher(), $this, null, $this);
}
public function getController(Request $request)