Passed
Push — master ( e7d330...e6cdc9 )
by Aleksei
11:08 queued 15s
created

AbstractCore::callAction()   B

Complexity

Conditions 6
Paths 5

Size

Total Lines 39
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 30
CRAP Score 6

Importance

Changes 0
Metric Value
eloc 27
dl 0
loc 39
ccs 30
cts 30
cp 1
rs 8.8657
c 0
b 0
f 0
cc 6
nc 5
nop 3
crap 6
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\Exception\ControllerException;
10
use Spiral\Core\Exception\Resolver\ArgumentResolvingException;
11
use Spiral\Core\Exception\Resolver\InvalidArgumentException;
12
use TypeError;
13
14
/**
15
 * Provides ability to call controllers in IoC scope.
16
 *
17
 * Make sure to bind ScopeInterface in your container.
18
 */
19
abstract class AbstractCore implements CoreInterface
20
{
21
    /** @internal */
22
    protected ResolverInterface $resolver;
23
24 380
    public function __construct(
25
        /** @internal */
26
        protected ContainerInterface $container
27
    ) {
28
        // resolver is usually the container itself
29 380
        $this->resolver = $container->get(ResolverInterface::class);
30
    }
31
32 79
    public function callAction(string $controller, string $action, array $parameters = []): mixed
33
    {
34
        try {
35 79
            $method = new \ReflectionMethod($controller, $action);
36 10
        } catch (\ReflectionException $e) {
37 10
            throw new ControllerException(
38 10
                \sprintf('Invalid action `%s`->`%s`', $controller, $action),
39 10
                ControllerException::BAD_ACTION,
40 10
                $e
41 10
            );
42
        }
43
44 69
        if ($method->isStatic() || !$method->isPublic()) {
45 2
            throw new ControllerException(
46 2
                \sprintf('Invalid action `%s`->`%s`', $controller, $action),
47 2
                ControllerException::BAD_ACTION
48 2
            );
49
        }
50
51
        try {
52 67
            $args = $this->resolveArguments($method, $parameters);
53 6
        } catch (ArgumentResolvingException|InvalidArgumentException $e) {
54 4
            throw new ControllerException(
55 4
                \sprintf('Missing/invalid parameter %s of `%s`->`%s`', $e->getParameter(), $controller, $action),
56 4
                ControllerException::BAD_ARGUMENT,
57 4
                $e
58 4
            );
59 2
        } catch (ContainerExceptionInterface $e) {
60 1
            throw new ControllerException(
61 1
                $e->getMessage(),
62 1
                ControllerException::ERROR,
63 1
                $e
64 1
            );
65
        }
66
67 61
        $container = $this->container;
68 61
        return ContainerScope::runScope(
69 61
            $container,
70 61
            static fn () => $method->invokeArgs($container->get($controller), $args)
71 61
        );
72
    }
73
74 67
    protected function resolveArguments(\ReflectionMethod $method, array $parameters): array
75
    {
76 67
        foreach ($method->getParameters() as $parameter) {
77 39
            $name = $parameter->getName();
78
            if (
79 39
                \array_key_exists($name, $parameters) &&
80 39
                $parameters[$name] === null &&
81 39
                $parameter->isDefaultValueAvailable()
82
            ) {
83 2
                $parameters[$name] = $parameter->getDefaultValue();
84
            }
85
        }
86
87
        // getting the set of arguments should be sent to requested method
88 67
        return $this->resolver->resolveArguments($method, $parameters, validate: true);
89
    }
90
}
91