feature #18308 Added an ArgumentResolver with clean extension point (iltar, HeahDude)
This PR was merged into the 3.1-dev branch. Discussion ---------- Added an ArgumentResolver with clean extension point | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | yes | Tests pass? | yes | Fixed tickets | #17933 (pre-work), #1547, #10710 | License | MIT | Doc PR | symfony/symfony-docs#6422 **This PR is a follow up for and blocked by: #18187**, relates to #11457 by @WouterJ. When reviewing, please take the last commit: [Added an ArgumentResolver with clean extension point](4c092b31f9
) This PR provides: - The ability to tag your own `ArgumentValueResolverInterface`. This means that you can effectively expand on the argument resolving in the `HttpKernel` without having to implement your own `ArgumentResolver`. - The possibility to cache away argument metadata via a new `ArgumentMetadataFactory` which simply fetches the data from the cache, effectively omitting 1 reflection call per request. *Not implemented in this PR, but possible once this is merged.* - The possibility to add a PSR-7 adapter to resolve the correct request, avoids the paramconverters - The possibility to add a value resolver to fetch stuff from $request->query - Drupal could simplify [their argument resolving](https://github.com/drupal/drupal/blob/8.1.x/core/lib/Drupal/Core/Controller/ControllerResolver.php) by a lot - etc. The aim for this PR is to provide a 100% BC variant to add argument resolving in a clean way, this is shown by the 2 tests: `LegacyArgumentResolverTest` and `ArgumentResolverTest`. /cc @dawehner @larowlan if you have time, can you check the impact for Drupal? I think this should be a very simple change which should make it more maintainable. Commits -------1bf80c9
Improved DX for the ArgumentResolverf29bf4c
Refactor ArgumentResolverTestcee5106
cs fixescfcf764
Added an ArgumentResolver with clean extension point360fc5f
Extracting arg resolving from ControllerResolver
This commit is contained in:
commit
cef7e5b1b0
@ -66,6 +66,11 @@ HttpKernel
|
||||
deprecated and will be removed in Symfony 4.0. The inline fragment
|
||||
renderer should be used with object attributes.
|
||||
|
||||
* The `ControllerResolver::getArguments()` method has been deprecated and will
|
||||
be removed in 4.0. If you have your own `ControllerResolverInterface`
|
||||
implementation, you should inject either an `ArgumentResolverInterface`
|
||||
instance or the new `ArgumentResolver` in the `HttpKernel`.
|
||||
|
||||
Serializer
|
||||
----------
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -150,9 +150,6 @@ class FrameworkExtension extends Extension
|
||||
|
||||
$loader->load('debug.xml');
|
||||
|
||||
$definition = $container->findDefinition('http_kernel');
|
||||
$definition->replaceArgument(1, new Reference('debug.controller_resolver'));
|
||||
|
||||
// replace the regular event_dispatcher service with the debug one
|
||||
$definition = $container->findDefinition('event_dispatcher');
|
||||
$definition->setPublic(false);
|
||||
@ -173,6 +170,9 @@ class FrameworkExtension extends Extension
|
||||
'Symfony\\Component\\HttpKernel\\EventListener\\ResponseListener',
|
||||
'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',
|
||||
|
@ -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);
|
||||
|
@ -17,8 +17,14 @@
|
||||
<argument type="service" id="logger" on-invalid="null" />
|
||||
</service>
|
||||
|
||||
<service id="debug.controller_resolver" class="Symfony\Component\HttpKernel\Controller\TraceableControllerResolver">
|
||||
<argument type="service" id="controller_resolver" />
|
||||
<service id="debug.controller_resolver" decorates="controller_resolver" class="Symfony\Component\HttpKernel\Controller\TraceableControllerResolver">
|
||||
<argument type="service" id="debug.controller_resolver.inner" />
|
||||
<argument type="service" id="debug.stopwatch" />
|
||||
<argument type="service" id="argument_resolver" />
|
||||
</service>
|
||||
|
||||
<service id="debug.argument_resolver" decorates="argument_resolver" class="Symfony\Component\HttpKernel\Controller\TraceableArgumentResolver">
|
||||
<argument type="service" id="debug.argument_resolver.inner" />
|
||||
<argument type="service" id="debug.stopwatch" />
|
||||
</service>
|
||||
</services>
|
||||
|
@ -13,6 +13,7 @@
|
||||
<argument type="service" id="event_dispatcher" />
|
||||
<argument type="service" id="controller_resolver" />
|
||||
<argument type="service" id="request_stack" />
|
||||
<argument type="service" id="argument_resolver" />
|
||||
</service>
|
||||
|
||||
<service id="request_stack" class="Symfony\Component\HttpFoundation\RequestStack" />
|
||||
|
@ -17,6 +17,29 @@
|
||||
<argument type="service" id="logger" on-invalid="ignore" />
|
||||
</service>
|
||||
|
||||
<service id="argument_metadata_factory" class="Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadataFactory" 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_resolver.request_attribute" class="Symfony\Component\HttpKernel\Controller\ArgumentResolver\RequestAttributeValueResolver" public="false">
|
||||
<tag name="controller_argument.value_resolver" priority="100" />
|
||||
</service>
|
||||
|
||||
<service id="argument_resolver.request" class="Symfony\Component\HttpKernel\Controller\ArgumentResolver\RequestValueResolver" public="false">
|
||||
<tag name="controller_argument.value_resolver" priority="50" />
|
||||
</service>
|
||||
|
||||
<service id="argument_resolver.default" class="Symfony\Component\HttpKernel\Controller\ArgumentResolver\DefaultValueResolver" public="false">
|
||||
<tag name="controller_argument.value_resolver" priority="-100" />
|
||||
</service>
|
||||
|
||||
<service id="argument_resolver.variadic" class="Symfony\Component\HttpKernel\Controller\ArgumentResolver\VariadicValueResolver" 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" />
|
||||
<argument>%kernel.charset%</argument>
|
||||
|
@ -23,7 +23,7 @@
|
||||
"symfony/config": "~2.8|~3.0",
|
||||
"symfony/event-dispatcher": "~2.8|~3.0",
|
||||
"symfony/http-foundation": "~3.1",
|
||||
"symfony/http-kernel": "~2.8|~3.0",
|
||||
"symfony/http-kernel": "~3.1",
|
||||
"symfony/polyfill-mbstring": "~1.0",
|
||||
"symfony/filesystem": "~2.8|~3.0",
|
||||
"symfony/finder": "~2.8|~3.0",
|
||||
|
@ -4,6 +4,12 @@ CHANGELOG
|
||||
3.1.0
|
||||
-----
|
||||
* deprecated passing objects as URI attributes to the ESI and SSI renderers
|
||||
* added `Symfony\Component\HttpKernel\Controller\LegacyArgumentResolver`
|
||||
* deprecated `ControllerResolver::getArguments()`
|
||||
* made `ControllerResolver` extend the `LegacyArgumentResolver` for BC
|
||||
* added `Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface`
|
||||
* added `Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface` as argument to `HttpKernel`
|
||||
* added `Symfony\Component\HttpKernel\Controller\ArgumentResolver`
|
||||
|
||||
3.0.0
|
||||
-----
|
||||
|
@ -0,0 +1,87 @@
|
||||
<?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\Controller\ArgumentResolver\DefaultValueResolver;
|
||||
use Symfony\Component\HttpKernel\Controller\ArgumentResolver\RequestAttributeValueResolver;
|
||||
use Symfony\Component\HttpKernel\Controller\ArgumentResolver\RequestValueResolver;
|
||||
use Symfony\Component\HttpKernel\Controller\ArgumentResolver\VariadicValueResolver;
|
||||
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadataFactory;
|
||||
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadataFactoryInterface;
|
||||
|
||||
/**
|
||||
* Responsible for resolving the arguments passed to an action.
|
||||
*
|
||||
* @author Iltar van der Berg <kjarli@gmail.com>
|
||||
*/
|
||||
final class ArgumentResolver implements ArgumentResolverInterface
|
||||
{
|
||||
private $argumentMetadataFactory;
|
||||
|
||||
/**
|
||||
* @var ArgumentValueResolverInterface[]
|
||||
*/
|
||||
private $argumentValueResolvers;
|
||||
|
||||
public function __construct(ArgumentMetadataFactoryInterface $argumentMetadataFactory = null, array $argumentValueResolvers = array())
|
||||
{
|
||||
$this->argumentMetadataFactory = $argumentMetadataFactory ?: new ArgumentMetadataFactory();
|
||||
$this->argumentValueResolvers = $argumentValueResolvers ?: array(
|
||||
new RequestAttributeValueResolver(),
|
||||
new RequestValueResolver(),
|
||||
new DefaultValueResolver(),
|
||||
new VariadicValueResolver(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getArguments(Request $request, $controller)
|
||||
{
|
||||
$arguments = array();
|
||||
|
||||
foreach ($this->argumentMetadataFactory->createArgumentMetadata($controller) as $metadata) {
|
||||
foreach ($this->argumentValueResolvers as $resolver) {
|
||||
if (!$resolver->supports($request, $metadata)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$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;
|
||||
}
|
||||
}
|
@ -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\ArgumentResolver;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface;
|
||||
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata;
|
||||
|
||||
/**
|
||||
* Yields the default value defined in the action signature when no value has been given.
|
||||
*
|
||||
* @author Iltar van der Berg <kjarli@gmail.com>
|
||||
*/
|
||||
final class DefaultValueResolver 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();
|
||||
}
|
||||
}
|
@ -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\ArgumentResolver;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface;
|
||||
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata;
|
||||
|
||||
/**
|
||||
* Yields a non-variadic argument's value from the request attributes.
|
||||
*
|
||||
* @author Iltar van der Berg <kjarli@gmail.com>
|
||||
*/
|
||||
final class RequestAttributeValueResolver 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());
|
||||
}
|
||||
}
|
@ -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\ArgumentResolver;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface;
|
||||
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata;
|
||||
|
||||
/**
|
||||
* Yields the same instance as the request object passed along.
|
||||
*
|
||||
* @author Iltar van der Berg <kjarli@gmail.com>
|
||||
*/
|
||||
final class RequestValueResolver implements ArgumentValueResolverInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supports(Request $request, ArgumentMetadata $argument)
|
||||
{
|
||||
return $argument->getType() === Request::class || is_subclass_of($argument->getType(), Request::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function resolve(Request $request, ArgumentMetadata $argument)
|
||||
{
|
||||
yield $request;
|
||||
}
|
||||
}
|
@ -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\ArgumentResolver;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface;
|
||||
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata;
|
||||
|
||||
/**
|
||||
* Yields a variadic argument's values from the request attributes.
|
||||
*
|
||||
* @author Iltar van der Berg <kjarli@gmail.com>
|
||||
*/
|
||||
final class VariadicValueResolver 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;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* An ArgumentResolverInterface instance knows how to determine the
|
||||
* arguments for a specific action.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
interface ArgumentResolverInterface
|
||||
{
|
||||
/**
|
||||
* Returns the arguments to pass to the controller.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param callable $controller
|
||||
*
|
||||
* @return array An array of arguments to pass to the controller
|
||||
*
|
||||
* @throws \RuntimeException When no value could be provided for a required argument
|
||||
*/
|
||||
public function getArguments(Request $request, $controller);
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
<?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 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).
|
||||
*
|
||||
* @param Request $request
|
||||
* @param ArgumentMetadata $argument
|
||||
*
|
||||
* @return \Generator
|
||||
*/
|
||||
public function resolve(Request $request, ArgumentMetadata $argument);
|
||||
}
|
@ -23,7 +23,7 @@ use Symfony\Component\HttpFoundation\Request;
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class ControllerResolver implements ControllerResolverInterface
|
||||
class ControllerResolver extends LegacyArgumentResolver implements ControllerResolverInterface
|
||||
{
|
||||
private $logger;
|
||||
|
||||
@ -84,50 +84,24 @@ class ControllerResolver implements ControllerResolverInterface
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @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)
|
||||
{
|
||||
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());
|
||||
@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 LegacyArgumentResolver instead.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
@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);
|
||||
|
||||
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;
|
||||
return parent::doGetArguments($request, $controller, $parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -52,6 +52,8 @@ interface ControllerResolverInterface
|
||||
* @return array An array of arguments to pass to the controller
|
||||
*
|
||||
* @throws \RuntimeException When value for argument given is not provided
|
||||
*
|
||||
* @deprecated This method is deprecated as of 3.1 and will be removed in 4.0. Please use the {@see ArgumentResolverInterface} instead.
|
||||
*/
|
||||
public function getArguments(Request $request, $controller);
|
||||
}
|
||||
|
@ -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 it for BC
|
||||
@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;
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
<?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\Stopwatch\Stopwatch;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class TraceableArgumentResolver implements ArgumentResolverInterface
|
||||
{
|
||||
private $resolver;
|
||||
private $stopwatch;
|
||||
|
||||
public function __construct(ArgumentResolverInterface $resolver, Stopwatch $stopwatch)
|
||||
{
|
||||
$this->resolver = $resolver;
|
||||
$this->stopwatch = $stopwatch;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getArguments(Request $request, $controller)
|
||||
{
|
||||
$e = $this->stopwatch->start('controller.get_arguments');
|
||||
|
||||
$ret = $this->resolver->getArguments($request, $controller);
|
||||
|
||||
$e->stop();
|
||||
|
||||
return $ret;
|
||||
}
|
||||
}
|
@ -19,21 +19,29 @@ use Symfony\Component\HttpFoundation\Request;
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class TraceableControllerResolver implements ControllerResolverInterface
|
||||
class TraceableControllerResolver implements ControllerResolverInterface, ArgumentResolverInterface
|
||||
{
|
||||
private $resolver;
|
||||
private $stopwatch;
|
||||
private $argumentResolver;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param ControllerResolverInterface $resolver A ControllerResolverInterface instance
|
||||
* @param Stopwatch $stopwatch A Stopwatch instance
|
||||
* @param ArgumentResolverInterface $argumentResolver Only required for BC
|
||||
*/
|
||||
public function __construct(ControllerResolverInterface $resolver, Stopwatch $stopwatch)
|
||||
public function __construct(ControllerResolverInterface $resolver, Stopwatch $stopwatch, ArgumentResolverInterface $argumentResolver = null)
|
||||
{
|
||||
$this->resolver = $resolver;
|
||||
$this->stopwatch = $stopwatch;
|
||||
$this->argumentResolver = $argumentResolver;
|
||||
|
||||
// BC
|
||||
if (null === $this->argumentResolver) {
|
||||
$this->argumentResolver = $resolver;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -52,12 +60,20 @@ class TraceableControllerResolver implements ControllerResolverInterface
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @deprecated This method is deprecated as of 3.1 and will be removed in 4.0.
|
||||
*/
|
||||
public function getArguments(Request $request, $controller)
|
||||
{
|
||||
@trigger_error(sprintf('This %s method is deprecated as of 3.1 and will be removed in 4.0. Please use the %s instead.', __METHOD__, TraceableArgumentResolver::class), E_USER_DEPRECATED);
|
||||
|
||||
if ($this->argumentResolver instanceof TraceableArgumentResolver) {
|
||||
return $this->argumentResolver->getArguments($request, $controller);
|
||||
}
|
||||
|
||||
$e = $this->stopwatch->start('controller.get_arguments');
|
||||
|
||||
$ret = $this->resolver->getArguments($request, $controller);
|
||||
$ret = $this->argumentResolver->getArguments($request, $controller);
|
||||
|
||||
$e->stop();
|
||||
|
||||
|
@ -0,0 +1,102 @@
|
||||
<?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.
|
||||
*
|
||||
* @throws \LogicException if no default value is present; {@see self::hasDefaultValue()}
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getDefaultValue()
|
||||
{
|
||||
if (!$this->hasDefaultValue) {
|
||||
throw new \LogicException(sprintf('Argument $%s does not have a default value. Use %s::hasDefaultValue() to avoid this exception.', $this->name, __CLASS__));
|
||||
}
|
||||
|
||||
return $this->defaultValue;
|
||||
}
|
||||
}
|
@ -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->hasDefaultValue($param), $this->getDefaultValue($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 hasDefaultValue(\ReflectionParameter $parameter)
|
||||
{
|
||||
return $parameter->isDefaultValueAvailable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a default value if available.
|
||||
*
|
||||
* @param \ReflectionParameter $parameter
|
||||
*
|
||||
* @return mixed|null
|
||||
*/
|
||||
private function getDefaultValue(\ReflectionParameter $parameter)
|
||||
{
|
||||
return $this->hasDefaultValue($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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
@ -11,6 +11,8 @@
|
||||
|
||||
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;
|
||||
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
|
||||
@ -36,19 +38,20 @@ class HttpKernel implements HttpKernelInterface, TerminableInterface
|
||||
protected $dispatcher;
|
||||
protected $resolver;
|
||||
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
|
||||
*/
|
||||
public function __construct(EventDispatcherInterface $dispatcher, ControllerResolverInterface $resolver, RequestStack $requestStack = null)
|
||||
public function __construct(EventDispatcherInterface $dispatcher, ControllerResolverInterface $resolver, RequestStack $requestStack = null, ArgumentResolverInterface $argumentResolver = null)
|
||||
{
|
||||
$this->dispatcher = $dispatcher;
|
||||
$this->resolver = $resolver;
|
||||
$this->requestStack = $requestStack ?: new RequestStack();
|
||||
$this->argumentResolver = $argumentResolver;
|
||||
|
||||
if (null === $this->argumentResolver) {
|
||||
@trigger_error(sprintf('As of 3.1 an %s is used to resolve arguments. In 4.0 the $argumentResolver becomes the %s if no other is provided instead of using the $resolver argument.', ArgumentResolverInterface::class, ArgumentResolver::class), E_USER_DEPRECATED);
|
||||
// fallback in case of deprecations
|
||||
$this->argumentResolver = $resolver;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -133,7 +136,7 @@ class HttpKernel implements HttpKernelInterface, TerminableInterface
|
||||
$controller = $event->getController();
|
||||
|
||||
// controller arguments
|
||||
$arguments = $this->resolver->getArguments($request, $controller);
|
||||
$arguments = $this->argumentResolver->getArguments($request, $controller);
|
||||
|
||||
// call controller
|
||||
$response = call_user_func_array($controller, $arguments);
|
||||
|
@ -0,0 +1,236 @@
|
||||
<?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\ArgumentResolver;
|
||||
use Symfony\Component\HttpKernel\Controller\ArgumentResolver\DefaultValueResolver;
|
||||
use Symfony\Component\HttpKernel\Controller\ArgumentResolver\RequestAttributeValueResolver;
|
||||
use Symfony\Component\HttpKernel\Controller\ArgumentResolver\RequestValueResolver;
|
||||
use Symfony\Component\HttpKernel\Controller\ArgumentResolver\VariadicValueResolver;
|
||||
use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface;
|
||||
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadataFactory;
|
||||
use Symfony\Component\HttpKernel\Tests\Fixtures\Controller\ExtendingRequest;
|
||||
use Symfony\Component\HttpKernel\Tests\Fixtures\Controller\VariadicController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
class ArgumentResolverTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
/** @var ArgumentResolver */
|
||||
private static $resolver;
|
||||
|
||||
public static function setUpBeforeClass()
|
||||
{
|
||||
$factory = new ArgumentMetadataFactory();
|
||||
$argumentValueResolvers = array(
|
||||
new RequestAttributeValueResolver(),
|
||||
new RequestValueResolver(),
|
||||
new DefaultValueResolver(),
|
||||
new VariadicValueResolver(),
|
||||
);
|
||||
|
||||
self::$resolver = new ArgumentResolver($factory, $argumentValueResolvers);
|
||||
}
|
||||
|
||||
public function testDefaultState()
|
||||
{
|
||||
$this->assertEquals(self::$resolver, new ArgumentResolver());
|
||||
$this->assertNotEquals(self::$resolver, new ArgumentResolver(null, array(new RequestAttributeValueResolver())));
|
||||
}
|
||||
|
||||
public function testGetArguments()
|
||||
{
|
||||
$request = Request::create('/');
|
||||
$request->attributes->set('foo', 'foo');
|
||||
$controller = array(new self(), 'controllerWithFoo');
|
||||
|
||||
$this->assertEquals(array('foo'), self::$resolver->getArguments($request, $controller), '->getArguments() returns an array of arguments for the controller method');
|
||||
}
|
||||
|
||||
public function testGetArgumentsReturnsEmptyArrayWhenNoArguments()
|
||||
{
|
||||
$request = Request::create('/');
|
||||
$controller = array(new self(), 'controllerWithoutArguments');
|
||||
|
||||
$this->assertEquals(array(), self::$resolver->getArguments($request, $controller), '->getArguments() returns an empty array if the method takes no arguments');
|
||||
}
|
||||
|
||||
public function testGetArgumentsUsesDefaultValue()
|
||||
{
|
||||
$request = Request::create('/');
|
||||
$request->attributes->set('foo', 'foo');
|
||||
$controller = array(new self(), 'controllerWithFooAndDefaultBar');
|
||||
|
||||
$this->assertEquals(array('foo', null), self::$resolver->getArguments($request, $controller), '->getArguments() uses default values if present');
|
||||
}
|
||||
|
||||
public function testGetArgumentsOverrideDefaultValueByRequestAttribute()
|
||||
{
|
||||
$request = Request::create('/');
|
||||
$request->attributes->set('foo', 'foo');
|
||||
$request->attributes->set('bar', 'bar');
|
||||
$controller = array(new self(), 'controllerWithFooAndDefaultBar');
|
||||
|
||||
$this->assertEquals(array('foo', 'bar'), self::$resolver->getArguments($request, $controller), '->getArguments() overrides default values if provided in the request attributes');
|
||||
}
|
||||
|
||||
public function testGetArgumentsFromClosure()
|
||||
{
|
||||
$request = Request::create('/');
|
||||
$request->attributes->set('foo', 'foo');
|
||||
$controller = function ($foo) {};
|
||||
|
||||
$this->assertEquals(array('foo'), self::$resolver->getArguments($request, $controller));
|
||||
}
|
||||
|
||||
public function testGetArgumentsUsesDefaultValueFromClosure()
|
||||
{
|
||||
$request = Request::create('/');
|
||||
$request->attributes->set('foo', 'foo');
|
||||
$controller = function ($foo, $bar = 'bar') {};
|
||||
|
||||
$this->assertEquals(array('foo', 'bar'), self::$resolver->getArguments($request, $controller));
|
||||
}
|
||||
|
||||
public function testGetArgumentsFromInvokableObject()
|
||||
{
|
||||
$request = Request::create('/');
|
||||
$request->attributes->set('foo', 'foo');
|
||||
$controller = new self();
|
||||
|
||||
$this->assertEquals(array('foo', null), self::$resolver->getArguments($request, $controller));
|
||||
|
||||
// Test default bar overridden by request attribute
|
||||
$request->attributes->set('bar', 'bar');
|
||||
|
||||
$this->assertEquals(array('foo', 'bar'), self::$resolver->getArguments($request, $controller));
|
||||
}
|
||||
|
||||
public function testGetArgumentsFromFunctionName()
|
||||
{
|
||||
$request = Request::create('/');
|
||||
$request->attributes->set('foo', 'foo');
|
||||
$request->attributes->set('foobar', 'foobar');
|
||||
$controller = __NAMESPACE__.'\controller_function';
|
||||
|
||||
$this->assertEquals(array('foo', 'foobar'), self::$resolver->getArguments($request, $controller));
|
||||
}
|
||||
|
||||
public function testGetArgumentsFailsOnUnresolvedValue()
|
||||
{
|
||||
$request = Request::create('/');
|
||||
$request->attributes->set('foo', 'foo');
|
||||
$request->attributes->set('foobar', 'foobar');
|
||||
$controller = array(new self(), 'controllerWithFooBarFoobar');
|
||||
|
||||
try {
|
||||
self::$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');
|
||||
}
|
||||
}
|
||||
|
||||
public function testGetArgumentsInjectsRequest()
|
||||
{
|
||||
$request = Request::create('/');
|
||||
$controller = array(new self(), 'controllerWithRequest');
|
||||
|
||||
$this->assertEquals(array($request), self::$resolver->getArguments($request, $controller), '->getArguments() injects the request');
|
||||
}
|
||||
|
||||
public function testGetArgumentsInjectsExtendingRequest()
|
||||
{
|
||||
$request = ExtendingRequest::create('/');
|
||||
$controller = array(new self(), 'controllerWithExtendingRequest');
|
||||
|
||||
$this->assertEquals(array($request), self::$resolver->getArguments($request, $controller), '->getArguments() injects the request when extended');
|
||||
}
|
||||
|
||||
/**
|
||||
* @requires PHP 5.6
|
||||
*/
|
||||
public function testGetVariadicArguments()
|
||||
{
|
||||
$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'), self::$resolver->getArguments($request, $controller));
|
||||
}
|
||||
|
||||
/**
|
||||
* @requires PHP 5.6
|
||||
* @expectedException \InvalidArgumentException
|
||||
*/
|
||||
public function testGetVariadicArgumentsWithoutArrayInRequest()
|
||||
{
|
||||
$request = Request::create('/');
|
||||
$request->attributes->set('foo', 'foo');
|
||||
$request->attributes->set('bar', 'foo');
|
||||
$controller = array(new VariadicController(), 'action');
|
||||
|
||||
self::$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, 'controllerWithFooAndDefaultBar');
|
||||
$resolver->getArguments($request, $controller);
|
||||
}
|
||||
|
||||
public function __invoke($foo, $bar = null)
|
||||
{
|
||||
}
|
||||
|
||||
public function controllerWithFoo($foo)
|
||||
{
|
||||
}
|
||||
|
||||
public function controllerWithoutArguments()
|
||||
{
|
||||
}
|
||||
|
||||
protected function controllerWithFooAndDefaultBar($foo, $bar = null)
|
||||
{
|
||||
}
|
||||
|
||||
protected function controllerWithFooBarFoobar($foo, $bar, $foobar)
|
||||
{
|
||||
}
|
||||
|
||||
protected function controllerWithRequest(Request $request)
|
||||
{
|
||||
}
|
||||
|
||||
protected function controllerWithExtendingRequest(ExtendingRequest $request)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
function controller_function($foo, $foobar)
|
||||
{
|
||||
}
|
@ -137,6 +137,9 @@ class ControllerResolverTest extends \PHPUnit_Framework_TestCase
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group legacy
|
||||
*/
|
||||
public function testGetArguments()
|
||||
{
|
||||
$resolver = $this->createControllerResolver();
|
||||
@ -200,6 +203,7 @@ class ControllerResolverTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
/**
|
||||
* @requires PHP 5.6
|
||||
* @group legacy
|
||||
*/
|
||||
public function testGetVariadicArguments()
|
||||
{
|
||||
|
@ -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)
|
||||
{
|
||||
}
|
@ -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)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
<?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 Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata;
|
||||
|
||||
class ArgumentMetadataTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testDefaultValueAvailable()
|
||||
{
|
||||
$argument = new ArgumentMetadata('foo', 'string', false, true, 'default value');
|
||||
|
||||
$this->assertTrue($argument->hasDefaultValue());
|
||||
$this->assertSame('default value', $argument->getDefaultValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \LogicException
|
||||
*/
|
||||
public function testDefaultValueUnavailable()
|
||||
{
|
||||
$argument = new ArgumentMetadata('foo', 'string', false, false, null);
|
||||
|
||||
$this->assertFalse($argument->hasDefaultValue());
|
||||
$argument->getDefaultValue();
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
@ -12,6 +12,7 @@
|
||||
namespace Symfony\Component\HttpKernel\Tests\Debug;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Symfony\Component\HttpKernel\Debug\TraceableEventDispatcher;
|
||||
use Symfony\Component\HttpKernel\HttpKernel;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
@ -108,10 +109,11 @@ class TraceableEventDispatcherTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
protected function getHttpKernel($dispatcher, $controller)
|
||||
{
|
||||
$resolver = $this->getMock('Symfony\Component\HttpKernel\Controller\ControllerResolverInterface');
|
||||
$resolver->expects($this->once())->method('getController')->will($this->returnValue($controller));
|
||||
$resolver->expects($this->once())->method('getArguments')->will($this->returnValue(array()));
|
||||
$controllerResolver = $this->getMock('Symfony\Component\HttpKernel\Controller\ControllerResolverInterface');
|
||||
$controllerResolver->expects($this->once())->method('getController')->will($this->returnValue($controller));
|
||||
$argumentResolver = $this->getMock('Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface');
|
||||
$argumentResolver->expects($this->once())->method('getArguments')->will($this->returnValue(array()));
|
||||
|
||||
return new HttpKernel($dispatcher, $resolver);
|
||||
return new HttpKernel($dispatcher, $controllerResolver, new RequestStack(), $argumentResolver);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\HttpKernel\Tests\Fixtures\Controller;
|
||||
|
||||
class BasicTypesController
|
||||
{
|
||||
public function action(string $foo, int $bar, float $baz)
|
||||
{
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\HttpKernel\Tests\Fixtures\Controller;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
class ExtendingRequest extends Request
|
||||
{
|
||||
}
|
@ -11,6 +11,9 @@
|
||||
|
||||
namespace Symfony\Component\HttpKernel\Tests\Fragment;
|
||||
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Symfony\Component\HttpKernel\Controller\ArgumentResolver;
|
||||
use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface;
|
||||
use Symfony\Component\HttpKernel\Controller\ControllerReference;
|
||||
use Symfony\Component\HttpKernel\HttpKernel;
|
||||
use Symfony\Component\HttpKernel\Fragment\InlineFragmentRenderer;
|
||||
@ -49,7 +52,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
|
||||
@ -60,7 +66,28 @@ class InlineFragmentRendererTest extends \PHPUnit_Framework_TestCase
|
||||
}))
|
||||
;
|
||||
|
||||
$kernel = new HttpKernel(new EventDispatcher(), $resolver);
|
||||
$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());
|
||||
}))
|
||||
;
|
||||
|
||||
$kernel = new HttpKernel(new EventDispatcher(), $resolver, new RequestStack(), new ArgumentResolver());
|
||||
$renderer = new InlineFragmentRenderer($kernel);
|
||||
|
||||
$response = $renderer->render(new ControllerReference('main_controller', array('object' => new \stdClass(), 'object1' => new Bar()), array()), Request::create('/'));
|
||||
@ -142,8 +169,8 @@ class InlineFragmentRendererTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
public function testExceptionInSubRequestsDoesNotMangleOutputBuffers()
|
||||
{
|
||||
$resolver = $this->getMock('Symfony\\Component\\HttpKernel\\Controller\\ControllerResolverInterface');
|
||||
$resolver
|
||||
$controllerResolver = $this->getMock('Symfony\\Component\\HttpKernel\\Controller\\ControllerResolverInterface');
|
||||
$controllerResolver
|
||||
->expects($this->once())
|
||||
->method('getController')
|
||||
->will($this->returnValue(function () {
|
||||
@ -152,13 +179,15 @@ class InlineFragmentRendererTest extends \PHPUnit_Framework_TestCase
|
||||
throw new \RuntimeException();
|
||||
}))
|
||||
;
|
||||
$resolver
|
||||
|
||||
$argumentResolver = $this->getMock('Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolverInterface');
|
||||
$argumentResolver
|
||||
->expects($this->once())
|
||||
->method('getArguments')
|
||||
->will($this->returnValue(array()))
|
||||
;
|
||||
|
||||
$kernel = new HttpKernel(new EventDispatcher(), $resolver);
|
||||
$kernel = new HttpKernel(new EventDispatcher(), $controllerResolver, new RequestStack(), $argumentResolver);
|
||||
$renderer = new InlineFragmentRenderer($kernel);
|
||||
|
||||
// simulate a main request with output buffering
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
Reference in New Issue
Block a user