Skip to content

[2.8] Move argument resolving from ControllerResolver #14971

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 9 commits into from
Closed
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
Add priority
  • Loading branch information
wouterj committed Jun 14, 2015
commit 4924e8683599cdc3a76851dac58ac833f7dfe672
Original file line number Diff line number Diff line change
Expand Up @@ -23,26 +23,30 @@
class ArgumentResolverManager
{
/**
* @var ArgumentResolverInterface[]
* @var array
*/
private $resolvers;
private $resolvers = array();

/**
* @param ArgumentResolverInterface[] $resolvers
* @var null|ArgumentResolverInterface[]
*/
public function __construct(array $resolvers = array())
{
$this->resolvers = $resolvers;
}
private $sortedResolvers;

/**
* Adds an argument resolver.
*
* @param ArgumentResolverInterface $resolver
*/
public function add(ArgumentResolverInterface $resolver)
public function add(ArgumentResolverInterface $resolver, $priority = 0)
Copy link
Member

Choose a reason for hiding this comment

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

do we really need the priority here ? IMO, we could just rely on having ->add() being called in the right order, as we do in many other places.
The sorting by priority would then be done in the compiler pass at compile time (the priority being used to give control over the order when building the container).

IIRC, the only place where we have the priority handled at runtime is for the event dispatcher, and this is because listeners are not all known at compile time.

{
$this->resolvers[] = $resolver;
if (!isset($this->resolvers[$priority])) {
$this->resolvers[$priority] = array();
}

$this->resolvers[$priority][] = $resolver;

// force a new sort of the resolvers
$this->sortedResolvers = null;
}

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.

This method will not work for controllers defined like Foo::bar, though it's a valid callable format. Is it intended?

Copy link
Member Author

Choose a reason for hiding this comment

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

yes. This class is designed to work with the ControllerResolver purely. The ControllerResolver already converts Foo::bar to array($obj, $method), so there is no need to check for the Foo::bar syntax here again.

Copy link
Member

Choose a reason for hiding this comment

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

missing phpdoc

Expand All @@ -64,7 +68,7 @@ public function getArguments(Request $request, $controller)
$arguments = array();

foreach ($parameters as $parameter) {
foreach ($this->resolvers as $argumentResolver) {
foreach ($this->getSortedResolvers() as $argumentResolver) {
if ($argumentResolver->supports($request, $parameter)) {
$arguments[] = $argumentResolver->resolve($request, $parameter);
continue 2;
Expand All @@ -88,4 +92,15 @@ public function getArguments(Request $request, $controller)

return $arguments;
}

private function getSortedResolvers()
{
if (null === $this->sortedResolvers) {
$this->sortedResolvers = $this->resolvers;
ksort($this->sortedResolvers);
$this->sortedResolvers = call_user_func_array('array_merge', $this->sortedResolvers);
}

return $this->sortedResolvers;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -172,10 +172,9 @@ protected function instantiateController($class)
private function getArgumentResolverManager()
{
if (null === $this->argumentResolverManager) {
$this->argumentResolverManager = new ArgumentResolverManager(array(
new RequestArgumentResolver(),
new RequestAttributesArgumentResolver(),
));
$this->argumentResolverManager = new ArgumentResolverManager();
Copy link
Contributor

Choose a reason for hiding this comment

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

What about adding constructor with optional argument $resolvers and pass instances of resolvers to them instead of calling add by hand twice here?

$this->argumentResolverManager->add(new RequestArgumentResolver());
$this->argumentResolverManager->add(new RequestAttributesArgumentResolver());
}

return $this->argumentResolverManager;
Expand Down
7 changes: 3 additions & 4 deletions src/Symfony/Component/HttpKernel/HttpKernel.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,9 @@ public function __construct(EventDispatcherInterface $dispatcher, ControllerReso
$this->requestStack = $requestStack ?: new RequestStack();

if (null === $argumentResolverManager) {
$argumentResolverManager = new ArgumentResolverManager(array(
new RequestArgumentResolver(),
new RequestAttributesArgumentResolver(),
));
$argumentResolverManager = new ArgumentResolverManager();
$argumentResolverManager->add(new RequestArgumentResolver());
$argumentResolverManager->add(new RequestAttributesArgumentResolver());
}

if (method_exists($resolver, 'setArgumentResolverManager')) {
Copy link
Contributor

Choose a reason for hiding this comment

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

If i create a ControllerResolver and use the method setArgumentResolverManager on it to set the manager directly on the resolver, and i then do not give HttpKernel an instance of that manager, the HttpKernel class will set it and overwrite the correct bootstrapping that was done.

Is there a purpose for having this code here?

Copy link
Member Author

Choose a reason for hiding this comment

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

The ControllerResolver#setArgumentResolverManager() method is tagged as @internal, which means you shouldn't use it in your own code. It only exists, so we can call it here in the HttpKernel.

This is to be BC. In 3.0, ControllerResolver#setArgumentResolverManager() will be removed and the complete argument resolving part will be called by HTTP kernel directly (ControllerResolver will only resolve the controller then). So if you want to add custom argument resolvers, do it by customizing HttpKernel instead of ControllerResolver.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ public function setUp()
$this->resolver1 = $this->getMock('Symfony\Component\HttpKernel\Controller\ArgumentResolver\ArgumentResolverInterface');
$this->resolver2 = $this->getMock('Symfony\Component\HttpKernel\Controller\ArgumentResolver\ArgumentResolverInterface');

$this->manager = new ArgumentResolverManager(array($this->resolver1, $this->resolver2));
$this->manager = new ArgumentResolverManager();
$this->manager->add($this->resolver1);
$this->manager->add($this->resolver2);

$this->request = $this->getMock('Symfony\Component\HttpFoundation\Request');
}
Expand Down Expand Up @@ -107,6 +109,20 @@ public function testControllerWithOneParameterWithNullDefault()
$this->assertArguments(array(null), function ($a = null) { });
}

public function testPriority()
{
$this->promiseResolverToNotMatch($this->resolver1);
$this->resolver1->expects($this->never())->method('resolve');

$this->promiseResolverToMatch($this->resolver2, 'resolved by 2');

$this->manager = new ArgumentResolverManager();
$this->manager->add($this->resolver1);
$this->manager->add($this->resolver2, 100);

$this->assertArguments(array('resolved by 2'), function ($a) { });
}

private function assertArguments(array $expected, $controller)
{
$this->assertEquals($expected, $this->manager->getArguments($this->request, $controller));
Expand Down
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy