-
-
Notifications
You must be signed in to change notification settings - Fork 4
/
CallableResolver.php
154 lines (132 loc) · 4.65 KB
/
CallableResolver.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
<?php
/*
* (c) 2015-2024 Julián Gutiérrez <[email protected]>
*
* @license BSD-3-Clause
* @link https://github.com/juliangut/slim-php-di
*/
declare(strict_types=1);
namespace Jgut\Slim\PHPDI;
use InvalidArgumentException;
use Invoker\CallableResolver as InvokerResolver;
use Invoker\Exception\NotCallableException;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Slim\Interfaces\AdvancedCallableResolverInterface;
class CallableResolver implements AdvancedCallableResolverInterface
{
/** @see https://regex101.com/r/lDdngD/1 */
protected const CALLABLE_PATTERN
= '!^(?P<class>[^\:]+)\:{1,2}(?P<method>[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)$!';
public function __construct(
private InvokerResolver $callableResolver,
) {}
/**
* @param string|callable(): mixed $toResolve
*
* @throws InvalidArgumentException
*
* @return callable(): ResponseInterface
*/
public function resolve($toResolve): callable
{
$resolvable = $toResolve;
if (\is_string($resolvable)) {
$resolvable = $this->callableFromStringNotation($resolvable);
}
return $this->resolveCallable($resolvable, $toResolve);
}
/**
* @param string|callable(): mixed|object $toResolve
*
* @throws InvalidArgumentException
*
* @return callable(): ResponseInterface
*/
public function resolveRoute($toResolve): callable
{
$resolvable = $toResolve;
if (\is_string($resolvable)) {
$resolvable = $this->callableFromStringNotation($resolvable, 'handle');
}
if (\is_object($resolvable)) {
if (!$resolvable instanceof RequestHandlerInterface) {
throw new InvalidArgumentException(
sprintf('Route class should implement "%s".', RequestHandlerInterface::class),
);
}
/** @var callable():mixed $resolvable */
$resolvable = [$resolvable, 'handle'];
}
return $this->resolveCallable($resolvable, $toResolve);
}
/**
* @param string|callable(): mixed|object $toResolve
*
* @throws InvalidArgumentException
*
* @return callable(): ResponseInterface
*/
public function resolveMiddleware($toResolve): callable
{
$resolvable = $toResolve;
if (\is_string($resolvable)) {
$resolvable = $this->callableFromStringNotation($resolvable, 'process');
}
if (\is_object($resolvable)) {
if (!$resolvable instanceof MiddlewareInterface) {
throw new InvalidArgumentException(
sprintf('Middleware class should implement "%s".', MiddlewareInterface::class),
);
}
/** @var callable():mixed $resolvable */
$resolvable = [$resolvable, 'process'];
}
return $this->resolveCallable($resolvable, $toResolve);
}
/**
* Get resolved callable.
*
* @param string|callable(): mixed|list<string> $resolvable
* @param string|callable(): mixed|list<string>|object $toResolve
*
* @throws InvalidArgumentException
*
* @return callable(): ResponseInterface
*/
protected function resolveCallable(
string|callable|array $resolvable,
string|callable|array|object $toResolve,
): callable {
try {
return $this->callableResolver->resolve($resolvable);
} catch (NotCallableException $exception) {
if (\is_callable($toResolve) || \is_array($toResolve)) {
$callable = json_encode($toResolve, \JSON_THROW_ON_ERROR);
} elseif (\is_object($toResolve)) {
$callable = $toResolve::class;
} else {
$callable = $toResolve;
}
throw new InvalidArgumentException(sprintf('"%s" is not resolvable.', $callable), 0, $exception);
}
}
/**
* Get callable from string callable notation.
*
* @return string|callable(): mixed|list<string>
*/
private function callableFromStringNotation(string $toResolve, ?string $defaultMethod = null): string|callable|array
{
$callable = $toResolve;
if (preg_match(self::CALLABLE_PATTERN, $toResolve, $matches) === 1) {
/** @var callable(): mixed $callable */
$callable = [$matches['class'], $matches['method']];
} elseif ($defaultMethod !== null) {
/** @var callable(): mixed $callable */
$callable = [$toResolve, $defaultMethod];
}
return $callable;
}
}