Skip to content

Commit e0697d0

Browse files
committed
Merge pull request #8 from jdreesen/issue7-silex1
Refactor ControllerResolver
2 parents 4dfe16f + b84888b commit e0697d0

File tree

6 files changed

+158
-21
lines changed

6 files changed

+158
-21
lines changed

composer.json

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"require": {
1717
"php": ">=5.4",
1818
"php-di/php-di": "~5.0",
19+
"php-di/invoker": "~1.2",
1920
"silex/silex" : "~1.3",
2021
"pimple/pimple" : "~1.1"
2122
},

src/Application.php

+13-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
use DI\Container;
88
use DI\ContainerBuilder;
99
use Interop\Container\ContainerInterface;
10+
use Invoker\CallableResolver;
11+
use Invoker\ParameterResolver\AssociativeArrayResolver;
12+
use Invoker\ParameterResolver\Container\TypeHintContainerResolver;
13+
use Invoker\ParameterResolver\ResolverChain;
1014

1115
/**
1216
* Replacement for the Silex Application class to use PHP-DI instead of Pimple.
@@ -36,6 +40,8 @@ public function __construct(ContainerBuilder $containerBuilder = null, array $va
3640
$containerBuilder = $containerBuilder ?: new ContainerBuilder();
3741
$containerBuilder->addDefinitions([
3842
'Interop\Container\ContainerInterface' => $this->containerInteropProxy,
43+
'Silex\Application' => $this,
44+
get_class($this) => $this,
3945
]);
4046
$containerBuilder->wrapContainer($this->containerInteropProxy);
4147
$this->phpdi = $containerBuilder->build();
@@ -44,7 +50,13 @@ public function __construct(ContainerBuilder $containerBuilder = null, array $va
4450

4551
// Override the controller resolver with ours
4652
$this['resolver'] = $this->share(function () {
47-
return new ControllerResolver($this->phpdi);
53+
return new ControllerResolver(
54+
new CallableResolver($this->containerInteropProxy),
55+
new ResolverChain([
56+
new AssociativeArrayResolver,
57+
new TypeHintContainerResolver($this->containerInteropProxy),
58+
])
59+
);
4860
});
4961
}
5062

src/Controller/ControllerResolver.php

+67-18
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22

33
namespace DI\Bridge\Silex\Controller;
44

5-
use DI\InvokerInterface;
5+
use Invoker\CallableResolver;
6+
use Invoker\Exception\NotCallableException;
7+
use Invoker\ParameterResolver\ParameterResolver;
8+
use Invoker\Reflection\CallableReflection;
69
use Symfony\Component\HttpFoundation\Request;
710
use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface;
811

@@ -12,41 +15,87 @@
1215
class ControllerResolver implements ControllerResolverInterface
1316
{
1417
/**
15-
* @var InvokerInterface
18+
* @var CallableResolver
1619
*/
17-
private $invoker;
20+
private $callableResolver;
1821

19-
public function __construct(InvokerInterface $invoker)
22+
/**
23+
* @var ParameterResolver
24+
*/
25+
private $parameterResolver;
26+
27+
/**
28+
* Constructor.
29+
*
30+
* @param CallableResolver $callableResolver
31+
* @param ParameterResolver $parameterResolver
32+
*/
33+
public function __construct(CallableResolver $callableResolver, ParameterResolver $parameterResolver)
2034
{
21-
$this->invoker = $invoker;
35+
$this->callableResolver = $callableResolver;
36+
$this->parameterResolver = $parameterResolver;
2237
}
2338

2439
/**
2540
* {@inheritdoc}
2641
*/
2742
public function getController(Request $request)
2843
{
29-
$controller = $request->attributes->get('_controller');
30-
31-
if (! $controller) {
32-
throw new \LogicException('No controller can be found for this request');
44+
if (! $controller = $request->attributes->get('_controller')) {
45+
throw new \LogicException(sprintf(
46+
'Controller for URI "%s" could not be found because the "_controller" parameter is missing.',
47+
$request->getPathInfo()
48+
));
3349
}
3450

35-
return function () use ($request, $controller) {
36-
$parameters = [
37-
'request' => $request,
38-
];
39-
$parameters += $request->attributes->all();
40-
41-
return $this->invoker->call($controller, $parameters);
42-
};
51+
try {
52+
return $this->callableResolver->resolve($controller);
53+
} catch (NotCallableException $e) {
54+
throw new \InvalidArgumentException(sprintf(
55+
'Controller for URI "%s" is not callable: %s',
56+
$request->getPathInfo(),
57+
$e->getMessage()
58+
));
59+
}
4360
}
4461

4562
/**
4663
* {@inheritdoc}
4764
*/
4865
public function getArguments(Request $request, $controller)
4966
{
50-
return array();
67+
$controllerReflection = CallableReflection::create($controller);
68+
$controllerParameters = $controllerReflection->getParameters();
69+
$resolvedArguments = [];
70+
71+
foreach ($controllerParameters as $index => $parameter) {
72+
if ('request' === $parameter->getName() || ($parameter->getClass() && $parameter->getClass()->isInstance($request))) {
73+
$resolvedArguments[$index] = $request;
74+
75+
break;
76+
}
77+
}
78+
79+
$arguments = $this->parameterResolver->getParameters(
80+
$controllerReflection,
81+
$request->attributes->all(),
82+
$resolvedArguments
83+
);
84+
85+
ksort($arguments);
86+
87+
// Check if all parameters are resolved
88+
$diff = array_diff_key($controllerParameters, $arguments);
89+
if (0 < count($diff)) {
90+
/** @var \ReflectionParameter $parameter */
91+
$parameter = reset($diff);
92+
throw new \RuntimeException(sprintf(
93+
'Controller "%s" requires that you provide a value for the "$%s" argument.',
94+
$controllerReflection->getName(),
95+
$parameter->getName()
96+
));
97+
}
98+
99+
return $arguments;
51100
}
52101
}

tests/Fixture/Application.php

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php
2+
3+
namespace DI\Bridge\Silex\Test\Fixture;
4+
5+
class Application extends \DI\Bridge\Silex\Application
6+
{
7+
}

tests/FunctionalTest.php

+50-2
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,11 @@ public function should_pass_url_placeholders()
6666
/**
6767
* @test
6868
*/
69-
public function should_pass_request_object()
69+
public function should_pass_request_object_by_parameter_name()
7070
{
7171
$app = $this->createApplication();
7272

73-
$app->get('/', function (Request $request) {
73+
$app->get('/', function ($request) {
7474
return 'Hello ' . $request->get('name');
7575
});
7676

@@ -140,4 +140,52 @@ public function should_pass_the_container_based_on_type_hint()
140140
$response = $app->handle(Request::create('/'));
141141
$this->assertEquals('bar', $response->getContent());
142142
}
143+
144+
/**
145+
* @test
146+
*/
147+
public function should_pass_the_silex_application_based_on_type_hint()
148+
{
149+
$app = $this->createApplication();
150+
$app['foo'] = 'bar';
151+
152+
$app->get('/', function (\Silex\Application $a) {
153+
return $a['foo'];
154+
});
155+
156+
$response = $app->handle(Request::create('/'));
157+
$this->assertEquals('bar', $response->getContent());
158+
}
159+
160+
/**
161+
* @test
162+
*/
163+
public function should_pass_the_own_application_based_on_type_hint()
164+
{
165+
$app = new \DI\Bridge\Silex\Test\Fixture\Application(null, [
166+
'foo' => 'bar',
167+
]);
168+
169+
$app->get('/', function (\DI\Bridge\Silex\Test\Fixture\Application $a) {
170+
return $a['foo'];
171+
});
172+
173+
$response = $app->handle(Request::create('/'));
174+
$this->assertEquals('bar', $response->getContent());
175+
}
176+
177+
/**
178+
* @test
179+
*/
180+
public function should_pass_request_object_based_on_type_hint()
181+
{
182+
$app = $this->createApplication();
183+
184+
$app->get('/', function (Request $r) {
185+
return 'Hello ' . $r->get('name');
186+
});
187+
188+
$response = $app->handle(Request::create('/?name=john'));
189+
$this->assertEquals('Hello john', $response->getContent());
190+
}
143191
}

tests/ProvidersTest.php

+20
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
namespace DI\Bridge\Silex\Test;
44

5+
use DI\Bridge\Silex\Test\Fixture\Controller;
56
use DI\ContainerBuilder;
7+
use Silex\Provider\ServiceControllerServiceProvider;
68
use Silex\Provider\SwiftmailerServiceProvider;
79
use Silex\Provider\TwigServiceProvider;
810
use Silex\Provider\UrlGeneratorServiceProvider;
@@ -89,4 +91,22 @@ public function test_mailer()
8991
$response = $app->handle(Request::create('/'));
9092
$this->assertEquals('OK', $response->getContent());
9193
}
94+
95+
/**
96+
* @see https://github.com/PHP-DI/Silex-Bridge/issues/7
97+
* @test
98+
*/
99+
public function test_service_controller_service_provider()
100+
{
101+
$app = $this->createApplication();
102+
103+
$app->register(new ServiceControllerServiceProvider, [
104+
'service.controller' => new Controller,
105+
]);
106+
107+
$app->get('/{name}', 'service.controller:hello');
108+
109+
$response = $app->handle(Request::create('/john'));
110+
$this->assertEquals('Hello john', $response->getContent());
111+
}
92112
}

0 commit comments

Comments
 (0)