Passed
Push — master ( f5a1ef...a14569 )
by butschster
29:16 queued 19:38
created

Route::requestHandler()   B

Complexity

Conditions 8
Paths 13

Size

Total Lines 34
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 8.0551

Importance

Changes 0
Metric Value
eloc 21
c 0
b 0
f 0
dl 0
loc 34
ccs 19
cts 21
cp 0.9048
rs 8.4444
cc 8
nc 13
nop 0
crap 8.0551
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Spiral\Router;
6
7
use Psr\Container\ContainerExceptionInterface;
8
use Psr\Container\ContainerInterface;
9
use Psr\Http\Message\ResponseFactoryInterface;
10
use Psr\Http\Message\ResponseInterface;
11
use Psr\Http\Message\ServerRequestInterface;
12
use Psr\Http\Server\RequestHandlerInterface;
13
use Spiral\Http\CallableHandler;
14
use Spiral\Router\Exception\RouteException;
15
use Spiral\Router\Exception\TargetException;
16
use Spiral\Router\Traits\PipelineTrait;
17
18
/**
19
 * Default route provides ability to route request to a given callable handler.
20
 *
21
 * Examples:
22
 *
23
 * new Route("/login", function(){
24
 *   return "hello";
25
 * });
26
 * new Route("/login", new Handler());
27
 * new Route("/login", \App\Handlers\Handler::class);
28
 *
29
 * new Route("/login", new Action(\App\Controllers|HomeController::class, "login");
30
 * new Route("/<controller>/<action>/<id>", new Namespaced("\App\Controllers");
31
 * new Route("/signup/<action>", new Controller(\App\Controllers\SignupController::class);
32
 * new Route("://<domain>/info", new Action(\App\Controllers|ProfileController::class, "info");
33
 * new Route("/<controller>/<action>/<id>", new Group(["profile" =>
34
 * \App\Controllers|ProfileController::class]);
35
 */
36
final class Route extends AbstractRoute implements ContainerizedInterface
0 ignored issues
show
Bug introduced by
The type Spiral\Router\AbstractRoute was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
37
{
38
    use PipelineTrait;
39
40
    public const ROUTE_ATTRIBUTE = 'route';
41
42
    /** @var string|callable|RequestHandlerInterface|TargetInterface */
43
    private mixed $target;
44
    private ?RequestHandlerInterface $requestHandler = null;
45
46
    /**
47
     * @param string $pattern Uri pattern.
48
     * @param string|callable|RequestHandlerInterface|TargetInterface $target Callable route target.
49
     * @param array $defaults Default value set.
50
     */
51 394
    public function __construct(
52
        string $pattern,
53
        string|callable|RequestHandlerInterface|TargetInterface $target,
54
        array $defaults = []
55
    ) {
56 394
        parent::__construct(
57 394
            $pattern,
58 394
            $target instanceof TargetInterface
59 372
                ? \array_merge($target->getDefaults(), $defaults)
60 394
                : $defaults
61 394
        );
62
63 394
        $this->target = $target;
64
    }
65
66
    /**
67
     * @mutation-free
68
     */
69 385
    public function withUriHandler(UriHandler $uriHandler): static
70
    {
71 385
        if ($this->target instanceof TargetInterface) {
72 367
            $uriHandler = $uriHandler->withConstrains(
73 367
                $this->target->getConstrains(),
74 367
                $this->defaults,
75 367
            );
76
        }
77
78 385
        return parent::withUriHandler($uriHandler);
79
    }
80
81
    /**
82
     * Associated route with given container.
83
     */
84 354
    public function withContainer(ContainerInterface $container): ContainerizedInterface
85
    {
86 354
        $route = clone $this;
87 354
        $route->container = $container;
88
89 354
        if ($route->target instanceof TargetInterface) {
90 342
            $route->target = clone $route->target;
91
        }
92
93 354
        $route->pipeline = $route->makePipeline();
94
95 351
        return $route;
96
    }
97
98 2
    public function withTarget(mixed $target): static
99
    {
100 2
        $route = clone $this;
101 2
        $route->target = $target;
102
103 2
        return $route;
104
    }
105
106 2
    public function getTarget(): mixed
107
    {
108 2
        return $this->target;
109
    }
110
111
    /**
112
     * @throws RouteException
113
     */
114 58
    public function handle(ServerRequestInterface $request): ResponseInterface
115
    {
116 58
        if (empty($this->requestHandler)) {
117 58
            $this->requestHandler = $this->requestHandler();
118
        }
119
120 55
        \assert($this->pipeline !== null);
121 55
        return $this->pipeline->process(
122 55
            $request->withAttribute(self::ROUTE_ATTRIBUTE, $this),
123 55
            $this->requestHandler
0 ignored issues
show
Bug introduced by
It seems like $this->requestHandler can also be of type null; however, parameter $handler of Spiral\Http\Pipeline::process() does only seem to accept Psr\Http\Server\RequestHandlerInterface, maybe add an additional type check? ( Ignorable by Annotation )

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

123
            /** @scrutinizer ignore-type */ $this->requestHandler
Loading history...
124 55
        );
125
    }
126
127
    /**
128
     * @throws RouteException
129
     */
130 58
    protected function requestHandler(): RequestHandlerInterface
131
    {
132 58
        if (!$this->hasContainer()) {
133 2
            throw new RouteException('Unable to configure route pipeline without associated container');
134
        }
135
136 56
        if ($this->target instanceof TargetInterface) {
137
            try {
138 49
                \assert($this->matches !== null);
139 49
                return $this->target->getHandler($this->container, $this->matches);
140
            } catch (TargetException $e) {
141
                throw new RouteException('Invalid target resolution', $e->getCode(), $e);
142
            }
143
        }
144
145 7
        if ($this->target instanceof RequestHandlerInterface) {
146 1
            return $this->target;
147
        }
148
149
        try {
150 6
            $target = \is_string($this->target)
151 4
                ? $this->container->get($this->target)
152 2
                : $this->target;
153
154 5
            if ($target instanceof RequestHandlerInterface) {
155 1
                return $target;
156
            }
157
158 4
            return new CallableHandler(
159 4
                $target,
160 4
                $this->container->get(ResponseFactoryInterface::class)
161 4
            );
162 1
        } catch (ContainerExceptionInterface $e) {
163 1
            throw new RouteException($e->getMessage(), $e->getCode(), $e);
164
        }
165
    }
166
}
167