Passed
Pull Request — master (#1104)
by Aleksei
26:20
created

AbstractCore::invoke()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 24
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 20
CRAP Score 3

Importance

Changes 0
Metric Value
eloc 17
dl 0
loc 24
ccs 20
cts 20
cp 1
rs 9.7
c 0
b 0
f 0
cc 3
nc 3
nop 4
crap 3
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
0 ignored issues
show
Deprecated Code introduced by
The interface Spiral\Core\CoreInterface has been deprecated: Use {@see HandlerInterface} instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

24
abstract class AbstractCore implements /** @scrutinizer ignore-deprecated */ CoreInterface, HandlerInterface

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.

Loading history...
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(
0 ignored issues
show
Deprecated Code introduced by
The class Spiral\Core\Exception\ControllerException has been deprecated: will be removed in Spiral v4.0 ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

92
            throw /** @scrutinizer ignore-deprecated */ new ControllerException(
Loading history...
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(
0 ignored issues
show
Deprecated Code introduced by
The class Spiral\Core\Exception\ControllerException has been deprecated: will be removed in Spiral v4.0 ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

103
            throw /** @scrutinizer ignore-deprecated */ new ControllerException(
Loading history...
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