1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
declare(strict_types=1); |
4
|
|
|
|
5
|
|
|
namespace Spiral\Core; |
6
|
|
|
|
7
|
|
|
use Psr\Container\ContainerExceptionInterface; |
8
|
|
|
use Psr\Container\ContainerInterface; |
9
|
|
|
use Spiral\Core\Attribute\Proxy; |
10
|
|
|
use Spiral\Core\Exception\ControllerException; |
11
|
|
|
use Spiral\Core\Exception\Resolver\ArgumentResolvingException; |
12
|
|
|
use Spiral\Core\Exception\Resolver\InvalidArgumentException; |
13
|
|
|
use Spiral\Interceptors\Context\CallContext; |
14
|
|
|
use Spiral\Interceptors\HandlerInterface; |
15
|
|
|
use Spiral\Interceptors\Internal\ActionResolver; |
16
|
|
|
|
17
|
|
|
/** |
18
|
|
|
* Provides ability to call controllers in IoC scope. |
19
|
|
|
* |
20
|
|
|
* Make sure to bind ScopeInterface in your container. |
21
|
|
|
* |
22
|
|
|
* @deprecated will be removed in Spiral v4.0 |
23
|
|
|
*/ |
24
|
|
|
abstract class AbstractCore implements CoreInterface, HandlerInterface |
|
|
|
|
25
|
|
|
{ |
26
|
|
|
/** @internal */ |
27
|
|
|
protected ResolverInterface $resolver; |
28
|
|
|
|
29
|
417 |
|
public function __construct( |
30
|
|
|
/** @internal */ |
31
|
|
|
#[Proxy] protected ContainerInterface $container |
32
|
|
|
) { |
33
|
|
|
// TODO: can we simplify this? |
34
|
|
|
// resolver is usually the container itself |
35
|
|
|
/** @psalm-suppress MixedAssignment */ |
36
|
417 |
|
$this->resolver = $container instanceof ResolverInterface |
37
|
26 |
|
? $container |
38
|
391 |
|
: $container |
39
|
391 |
|
->get(InvokerInterface::class) |
40
|
|
|
->invoke(static fn (#[Proxy] ResolverInterface $resolver) => $resolver); |
41
|
|
|
} |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* @psalm-assert class-string $controller |
45
|
|
|
* @psalm-assert non-empty-string $action |
46
|
|
|
*/ |
47
|
56 |
|
public function callAction(string $controller, string $action, array $parameters = []): mixed |
48
|
|
|
{ |
49
|
56 |
|
$method = ActionResolver::pathToReflection($controller, $action); |
50
|
|
|
|
51
|
|
|
// Validate method |
52
|
46 |
|
ActionResolver::validateControllerMethod($method); |
53
|
|
|
|
54
|
44 |
|
return $this->invoke(null, $controller, $method, $parameters); |
55
|
|
|
} |
56
|
|
|
|
57
|
30 |
|
public function handle(CallContext $context): mixed |
58
|
|
|
{ |
59
|
30 |
|
$target = $context->getTarget(); |
60
|
30 |
|
$reflection = $target->getReflection(); |
61
|
30 |
|
return $reflection instanceof \ReflectionMethod |
62
|
27 |
|
? $this->invoke($target->getObject(), $target->getPath()[0], $reflection, $context->getArguments()) |
63
|
28 |
|
: $this->callAction($target->getPath()[0], $target->getPath()[1], $context->getArguments()); |
64
|
|
|
} |
65
|
|
|
|
66
|
71 |
|
protected function resolveArguments(\ReflectionMethod $method, array $parameters): array |
67
|
|
|
{ |
68
|
71 |
|
foreach ($method->getParameters() as $parameter) { |
69
|
43 |
|
$name = $parameter->getName(); |
70
|
|
|
if ( |
71
|
43 |
|
\array_key_exists($name, $parameters) && |
72
|
43 |
|
$parameters[$name] === null && |
73
|
43 |
|
$parameter->isDefaultValueAvailable() |
74
|
|
|
) { |
75
|
|
|
/** @psalm-suppress MixedAssignment */ |
76
|
2 |
|
$parameters[$name] = $parameter->getDefaultValue(); |
77
|
|
|
} |
78
|
|
|
} |
79
|
|
|
|
80
|
|
|
// getting the set of arguments should be sent to requested method |
81
|
71 |
|
return $this->resolver->resolveArguments($method, $parameters); |
82
|
|
|
} |
83
|
|
|
|
84
|
|
|
/** |
85
|
|
|
* @throws \Throwable |
86
|
|
|
*/ |
87
|
71 |
|
private function invoke(?object $object, string $class, \ReflectionMethod $method, array $arguments): mixed |
88
|
|
|
{ |
89
|
|
|
try { |
90
|
71 |
|
$args = $this->resolveArguments($method, $arguments); |
91
|
6 |
|
} catch (ArgumentResolvingException | InvalidArgumentException $e) { |
92
|
4 |
|
throw new ControllerException( |
|
|
|
|
93
|
4 |
|
\sprintf( |
94
|
4 |
|
'Missing/invalid parameter %s of `%s`->`%s`', |
95
|
4 |
|
$e->getParameter(), |
96
|
4 |
|
$class, |
97
|
4 |
|
$method->getName(), |
98
|
4 |
|
), |
99
|
4 |
|
ControllerException::BAD_ARGUMENT, |
100
|
4 |
|
$e, |
101
|
4 |
|
); |
102
|
2 |
|
} catch (ContainerExceptionInterface $e) { |
103
|
1 |
|
throw new ControllerException( |
|
|
|
|
104
|
1 |
|
$e->getMessage(), |
105
|
1 |
|
ControllerException::ERROR, |
106
|
1 |
|
$e, |
107
|
1 |
|
); |
108
|
|
|
} |
109
|
|
|
|
110
|
65 |
|
return $method->invokeArgs($object ?? $this->container->get($class), $args); |
111
|
|
|
} |
112
|
|
|
} |
113
|
|
|
|
This interface has been deprecated. The supplier of the interface has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the interface will be removed and what other interface to use instead.