Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Added an ArgumentResolver with clean extension point
  • Loading branch information
Iltar van der Berg committed Apr 1, 2016
commit cfcf764d24582e23c5a4ca1be7af98fd6220dc4c
4 changes: 3 additions & 1 deletion UPGRADE-3.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing empty line

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch

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
----------
Expand Down
Original file line number Diff line number Diff line change
@@ -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');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

first, check if the definition exists and return early otherwise.

$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);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing empty line at the end of this file

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't github show this if the case? Anyhow, line 66 is an empty line here for me, adding one more would result in 2 empty lines in the end

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@HeahDude we should not have an empty line at the end. We should have a final LF char. PhpStorm displays an empty line after this LF, but there is no actual line 66. Github handles this properly (as git does) and displays an extra info at the end of line when it does not end with an EOL character

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@stof Noted. Thanks for that precision :)

Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,8 @@ public function load(array $configs, ContainerBuilder $container)
'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',
Expand Down
2 changes: 2 additions & 0 deletions src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
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;
Expand Down Expand Up @@ -87,6 +88,7 @@ public function build(ContainerBuilder $container)
$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);
Expand Down
23 changes: 22 additions & 1 deletion src/Symfony/Bundle/FrameworkBundle/Resources/config/web.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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" />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought there was a kind of convention to write services used as arguments before being used as arguments. I was wrong ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case I just put it under the controller_resolver because that's where it originates from code wise. I can always move it if desired but in the end it makes no difference

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. It was more a question about "what does the symfony style advise in that case ?"


<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>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about merging the last two as being the "fallback" behavior based on reflection? That could even be "hardcoded" (not sure).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean directly in the ArgumentResolver or in 1 specific resolver?


<service id="response_listener" class="Symfony\Component\HttpKernel\EventListener\ResponseListener">
<tag name="kernel.event_subscriber" />
Expand Down
7 changes: 4 additions & 3 deletions src/Symfony/Component/HttpKernel/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
-----
Expand Down
77 changes: 42 additions & 35 deletions src/Symfony/Component/HttpKernel/Controller/ArgumentResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
* {@inheritdoc}
* @var ArgumentValueResolverInterface[]
*/
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);
}
private $argumentValueResolvers;

return $this->doGetArguments($request, $controller, $r->getParameters());
public function __construct(ArgumentMetadataFactoryInterface $argumentMetadataFactory = null, array $argumentValueResolvers = array())
{
$this->argumentMetadataFactory = $argumentMetadataFactory;
$this->argumentValueResolvers = $argumentValueResolvers;
}

protected function doGetArguments(Request $request, $controller, array $parameters)
/**
* {@inheritdoc}
*/
public function getArguments(Request $request, $controller)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing doc block ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was removed on purpose, I can always add it instead of the @var. As long as the array'd type can be deferred I'm happy.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should use {@inheritdoc} to link the ArgumentResolverInterface with auto generated doc api.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here, needed.

{
$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];

foreach ($this->argumentMetadataFactory->createArgumentMetadata($controller) as $metadata) {
foreach ($this->argumentValueResolvers as $resolver) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Edited

foreach ($this->argumentValueResolvers as $resolver) {
    if (!$resolver->supports($request, $metadata)) {
        continue;
    }

    $resolved = $resolver->getValue($request, $metadata);
    if ($resolved) {
        // variadic is a special case, always being the last and being an array
        if ($metadata->isVariadic() && is_array($resolved)) {
            return array_merge($arguments, $resolved);
        }

        return $resolved;
    }
}

$repr = $controller;

if (is_array($controller)) {
    $repr = sprintf('%s::%s()', get_class($controller[0]), $controller[1]);
} elseif (is_object($controller)) {
    $repr = get_class($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, $metadata->getArgumentName()));

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would cause an issue with (Request $request, $foo) where foo wouldn't exist. Because at that point it would return true while the argument couldn't be found.

I've also thought about the guard clause you've posted but this case will actually cause 2 arguments to be resolved if they overlap. That means if (Request $request) is given and 2 resolvers support this:

  • Request as class
  • 'request' in the attribute bag for example

this would mean you'd call the method with 2 parameters instead of 1.

if (!$resolver->supports($request, $metadata)) {
continue;
}
} 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;

$resolved = $resolver->resolve($request, $metadata);

if (!$resolved instanceof \Generator) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically, we should be more flexible here, by allowing any Traversable (as we only need to loop over it using foreach. There is no reason to force consumers to use a generator when implementing the iterator (even though the core does it this way for simplicity)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes it more complex. If it returns an array, I should iterate over it. If it's not an array, I should make it an array and iterate over it. This last case was discussed to rather throw an exception. If you check the comments above, it was decide to use yield as an elegant alternative. This way someone can yield an array and it won't give a buggy result. If I were to return an array while that single argument would have to be an array, it would start adding each array item as one, effectively breaking fooAction(array $foo)

This is one of the cases where I think it's easier to support only 1 method which is compatible with both arrays, non-arrays and variadic arguments.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@iltar Why ? Document that the return value is a Traversable being a list of argument, and always iterate over it. The code is the same than today, except for the instanceof safeguard here. People may want to implement iterators in other ways than through a generator.
When you only need iterations, the fact that a Generator is used should be an implementation detail. You are overly restrictive here IMO.

The only case where a consumer should care that it deals with a Generator is when using Generator-only APIs (i.e. Generator::send).

I'm not asking you to be able to return a single value from the resolver here (which is what you describe in your comment)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I've discussed this before; @fabpot do you also agree that returning Traversable|array should be possible?

In my opinion it's a bit dangerous as this should only be possible for variadics.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@iltar returning an array in this case would then work the same than when returning an iterator. Iterating over it. I don't see much difference between the case of the array and the Generator here, for the consumer side (and the resolver can be implement in the way the implementer prefers)

What we should have though is a validation in the ArgumentResolver that any non-variadic argument leads to an iterator containing exactly 1 value.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@stof as the current solution is more strict, we can always decide to change it in a later version as it will remain backwards compatible. I don't think the current situation will cause any issues and if they do, we can patch it

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@iltar If we want to make that change, it must be done before the 3.1 release. Otherwise, code relying on Generator instances being returned might break when a Traversable is returned instead.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@xabbuh but we're talking about the return value that is only internally used. No other code should ever rely on what the argument value resolver returns

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@wouterj If we don't expect other code to make use of the return values, we should remove the ArgumentResolverInterface. Having such an interface calls for custom implementations imo which will then probably rely on the return type.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@xabbuh in theory it shouldn't return anything else and a clear exception
is thrown when not the case. The decision for yield was that it's a simple
syntax and uniform for every case. Ideally I'd return the resolve value,
but that means that the variadic version breaks. If it's allowed to also
return iterators and such, it would make it too easy to break things. If
you would return 2 array items, it will become the wrong sequence of
arguments and break your method.

In my opinion having just yield is a good idea because it limits the usage
and reduces the chance of breaking. If you have an idea on how to approve
it, I'm all ears though

On Sun, Apr 10, 2016 at 1:41 PM Christian Flothmann <
notifications@github.com> wrote:

In src/Symfony/Component/HttpKernel/Controller/ArgumentResolver.php
#18308 (comment):

  •    $this->argumentValueResolvers = $argumentValueResolvers;
    
  • }
  • 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) {
    

@wouterj https://github.com/WouterJ If we don't expect other code to
make use of the return values, we should remove the
ArgumentResolverInterface. Having such an interface calls for custom
implementations imo which will then probably rely on the return type.


You are receiving this because you were mentioned.
Reply to this email directly or view it on GitHub
https://github.com/symfony/symfony/pull/18308/files/b1e12875553883b0c3a01a7581109e0bc946a68a#r59132159

throw new \InvalidArgumentException(sprintf('%s::resolve() must yield at least one value.', get_class($resolver)));
}

foreach ($resolved as $append) {
$arguments[] = $append;
}

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));
// 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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
*
Expand Down
Original file line number Diff line number Diff line change
@@ -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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I guess doc blocks were removed here too ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had inherit doc tags before so it was rather useless. If I remember correctly, the "way to go" is to omit the docblock if it's the same as the parent/interface

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@iltar AFAIK, we use inheritdoc tags in Symfony, to make it clear that the phpdoc is inherited and so does not need to be written by someone looking for missing phpdoc (unless I missed a policy change).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@stof that's what I thought at first too, I can re-add them though. @fabpot can you enlighten this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rules are the following:

  • We don't document arguments when it does not add any more information than what the signature already gives
  • We are using @{inheritdoc} to specify that the method is documented in a parent class or interface (to make a difference with methods that are not documented).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They are needed here though.

{
return !$argument->isVariadic() && $request->attributes->has($argument->getName());
}

/**
* {@inheritdoc}
*/
public function resolve(Request $request, ArgumentMetadata $argument)
{
yield $request->attributes->get($argument->getName());
}
}
Original file line number Diff line number Diff line change
@@ -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();
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
}
Loading