Skip to content

Commit 1644a2f

Browse files
authored
Merge pull request #71 from Rarst/advanced-callable-resolver
Upgraded CallableResolver to Advanced interface
2 parents ad74ba0 + 61b6466 commit 1644a2f

6 files changed

+212
-3
lines changed

src/CallableResolver.php

+53-2
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,15 @@
22

33
namespace DI\Bridge\Slim;
44

5-
use Slim\Interfaces\CallableResolverInterface;
5+
use Invoker\Exception\NotCallableException;
6+
use Psr\Http\Server\MiddlewareInterface;
7+
use Psr\Http\Server\RequestHandlerInterface;
8+
use Slim\Interfaces\AdvancedCallableResolverInterface;
69

710
/**
811
* Resolve middleware and route callables using PHP-DI.
912
*/
10-
class CallableResolver implements CallableResolverInterface
13+
class CallableResolver implements AdvancedCallableResolverInterface
1114
{
1215
/**
1316
* @var \Invoker\CallableResolver
@@ -18,11 +21,59 @@ public function __construct(\Invoker\CallableResolver $callableResolver)
1821
{
1922
$this->callableResolver = $callableResolver;
2023
}
24+
2125
/**
2226
* {@inheritdoc}
2327
*/
2428
public function resolve($toResolve): callable
2529
{
30+
return $this->callableResolver->resolve($this->translateNotation($toResolve));
31+
}
32+
33+
/**
34+
* {@inheritdoc}
35+
*/
36+
public function resolveRoute($toResolve): callable
37+
{
38+
return $this->resolvePossibleSignature($toResolve, 'handle', RequestHandlerInterface::class);
39+
}
40+
41+
/**
42+
* {@inheritdoc}
43+
*/
44+
public function resolveMiddleware($toResolve): callable
45+
{
46+
return $this->resolvePossibleSignature($toResolve, 'process', MiddlewareInterface::class);
47+
}
48+
49+
/**
50+
* Translate Slim string callable notation ('nameOrKey:method') to PHP-DI notation ('nameOrKey::method').
51+
*/
52+
private function translateNotation($toResolve)
53+
{
54+
if (is_string($toResolve) && preg_match(\Slim\CallableResolver::$callablePattern, $toResolve)) {
55+
$toResolve = str_replace(':', '::', $toResolve);
56+
}
57+
58+
return $toResolve;
59+
}
60+
61+
private function resolvePossibleSignature($toResolve, string $method, string $typeName): callable
62+
{
63+
if (is_string($toResolve)) {
64+
$toResolve = $this->translateNotation($toResolve);
65+
66+
try {
67+
$callable = $this->callableResolver->resolve([$toResolve, $method]);
68+
69+
if (is_array($callable) && $callable[0] instanceof $typeName) {
70+
return $callable;
71+
}
72+
} catch (NotCallableException $e) {
73+
// Fall back to looking for a generic callable.
74+
}
75+
}
76+
2677
return $this->callableResolver->resolve($toResolve);
2778
}
2879
}

tests/CallableTest.php

+89
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace DI\Bridge\Slim\Test;
6+
7+
use DI\Bridge\Slim\Bridge;
8+
use DI\Bridge\Slim\Test\Fixture\UserController;
9+
use DI\Bridge\Slim\Test\Fixture\UserControllerInvokable;
10+
use DI\Bridge\Slim\Test\Fixture\UserControllerPsr;
11+
use DI\Bridge\Slim\Test\Fixture\UserMiddlewarePsr;
12+
use DI\Container;
13+
use PHPUnit\Framework\TestCase;
14+
15+
use Slim\Interfaces\AdvancedCallableResolverInterface;
16+
17+
use function DI\get;
18+
19+
20+
class CallableTest extends TestCase
21+
{
22+
/**
23+
* @test
24+
*/
25+
public function resolves_callable()
26+
{
27+
$app = Bridge::create();
28+
$resolver = $app->getCallableResolver();
29+
30+
/** @var Container $container */
31+
$container = $app->getContainer();
32+
$container->set('controller.user', get(UserController::class));
33+
34+
[$object, $method] = $resolver->resolve(UserController::class . ':dashboard');
35+
$this->assertInstanceOf(UserController::class, $object);
36+
$this->assertEquals('dashboard', $method);
37+
38+
[$object, $method] = $resolver->resolve('controller.user:dashboard');
39+
$this->assertInstanceOf(UserController::class, $object);
40+
$this->assertEquals('dashboard', $method);
41+
42+
[$object, $method] = $resolver->resolve(UserController::class . '::dashboard');
43+
$this->assertInstanceOf(UserController::class, $object);
44+
$this->assertEquals('dashboard', $method);
45+
46+
[$object, $method] = $resolver->resolve([UserController::class, 'dashboard']);
47+
$this->assertInstanceOf(UserController::class, $object);
48+
$this->assertEquals('dashboard', $method);
49+
50+
$resolved = $resolver->resolve(UserControllerInvokable::class);
51+
$this->assertInstanceOf(UserControllerInvokable::class, $resolved);
52+
}
53+
54+
/**
55+
* @test
56+
*/
57+
public function resolves_route()
58+
{
59+
$app = Bridge::create();
60+
/** @var AdvancedCallableResolverInterface $resolver */
61+
$resolver = $app->getCallableResolver();
62+
63+
/** @var Container $container */
64+
$container = $app->getContainer();
65+
$container->set('controller.userpsr', get(UserControllerPsr::class));
66+
67+
[$object, $method] = $resolver->resolveRoute(UserControllerPsr::class);
68+
$this->assertInstanceOf(UserControllerPsr::class, $object);
69+
$this->assertEquals('handle', $method);
70+
71+
[$object, $method] = $resolver->resolveRoute('controller.userpsr');
72+
$this->assertInstanceOf(UserControllerPsr::class, $object);
73+
$this->assertEquals('handle', $method);
74+
}
75+
76+
/**
77+
* @test
78+
*/
79+
public function resolves_middleware()
80+
{
81+
$app = Bridge::create();
82+
/** @var AdvancedCallableResolverInterface $resolver */
83+
$resolver = $app->getCallableResolver();
84+
85+
[$object, $method] = $resolver->resolveMiddleware(UserMiddlewarePsr::class);
86+
$this->assertInstanceOf(UserMiddlewarePsr::class, $object);
87+
$this->assertEquals('process', $method);
88+
}
89+
}
+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace DI\Bridge\Slim\Test\Fixture;
4+
5+
use Psr\Http\Message\ResponseInterface;
6+
7+
class UserControllerInvokable
8+
{
9+
public function __invoke(ResponseInterface $response)
10+
{
11+
$response->getBody()->write('Hello world!');
12+
13+
return $response;
14+
}
15+
}

tests/Fixture/UserControllerPsr.php

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace DI\Bridge\Slim\Test\Fixture;
5+
6+
use Laminas\Diactoros\Response\TextResponse;
7+
use Psr\Http\Message\ResponseInterface;
8+
use Psr\Http\Message\ServerRequestInterface;
9+
use Psr\Http\Server\RequestHandlerInterface;
10+
11+
class UserControllerPsr implements RequestHandlerInterface
12+
{
13+
public function handle(ServerRequestInterface $request): ResponseInterface
14+
{
15+
return new TextResponse('Hello world!');
16+
}
17+
}

tests/Fixture/UserMiddlewarePsr.php

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace DI\Bridge\Slim\Test\Fixture;
6+
7+
use Laminas\Diactoros\Response\TextResponse;
8+
use Psr\Http\Message\ResponseInterface;
9+
use Psr\Http\Message\ServerRequestInterface;
10+
use Psr\Http\Server\MiddlewareInterface;
11+
use Psr\Http\Server\RequestHandlerInterface;
12+
13+
class UserMiddlewarePsr implements MiddlewareInterface
14+
{
15+
/**
16+
* @inheritDoc
17+
*/
18+
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
19+
{
20+
return new TextResponse('Hello ' . $request->getQueryParams()['foo']);
21+
}
22+
}

tests/MiddlewareTest.php

+16-1
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@
33
namespace DI\Bridge\Slim\Test;
44

55
use DI\Bridge\Slim\Bridge;
6+
use DI\Bridge\Slim\Test\Fixture\UserMiddlewarePsr;
67
use DI\Bridge\Slim\Test\Mock\RequestFactory;
8+
use Laminas\Diactoros\Response\TextResponse;
79
use PHPUnit\Framework\TestCase;
810
use Psr\Http\Message\ServerRequestInterface;
911
use Psr\Http\Server\RequestHandlerInterface;
10-
use Zend\Diactoros\Response\TextResponse;
1112

1213
class MiddlewareTest extends TestCase
1314
{
@@ -26,4 +27,18 @@ public function invokes_closure_middleware()
2627

2728
$this->assertEquals('Hello matt', $response->getBody()->__toString());
2829
}
30+
31+
/**
32+
* @test
33+
*/
34+
public function invokes_class_name_middleware()
35+
{
36+
$app = Bridge::create();
37+
$app->add(UserMiddlewarePsr::class);
38+
$app->get('/', function () {});
39+
40+
$response = $app->handle(RequestFactory::create('/', 'foo=matt'));
41+
42+
$this->assertEquals('Hello matt', $response->getBody()->__toString());
43+
}
2944
}

0 commit comments

Comments
 (0)