Passed
Push — master ( a820ea...11dd35 )
by Melech
05:58 queued 02:00
created

AttributeCollector::getRoutes()   B

Complexity

Conditions 8
Paths 36

Size

Total Lines 98
Code Lines 58

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 58
c 1
b 0
f 0
dl 0
loc 98
rs 7.6719
cc 8
nc 36
nop 1

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Valkyrja Framework package.
7
 *
8
 * (c) Melech Mizrachi <[email protected]>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Valkyrja\Http\Routing\Collector;
15
16
use Override;
0 ignored issues
show
Bug introduced by
The type Override 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...
17
use ReflectionException;
18
use Valkyrja\Attribute\Contract\Attributes;
19
use Valkyrja\Http\Middleware\Contract\RouteDispatchedMiddleware;
20
use Valkyrja\Http\Middleware\Contract\RouteMatchedMiddleware;
21
use Valkyrja\Http\Middleware\Contract\SendingResponseMiddleware;
22
use Valkyrja\Http\Middleware\Contract\TerminatedMiddleware;
23
use Valkyrja\Http\Middleware\Contract\ThrowableCaughtMiddleware;
24
use Valkyrja\Http\Routing\Attribute\Parameter;
25
use Valkyrja\Http\Routing\Attribute\Route;
26
use Valkyrja\Http\Routing\Attribute\Route\Middleware;
27
use Valkyrja\Http\Routing\Attribute\Route\RequestMethod;
28
use Valkyrja\Http\Routing\Attribute\Route\RequestStruct;
29
use Valkyrja\Http\Routing\Attribute\Route\ResponseStruct;
30
use Valkyrja\Http\Routing\Collector\Contract\Collector as Contract;
31
use Valkyrja\Http\Routing\Data\Contract\Route as RouteContract;
32
use Valkyrja\Http\Routing\Processor\Contract\Processor;
33
use Valkyrja\Http\Struct\Request\Contract\RequestStruct as RequestStructContract;
34
use Valkyrja\Http\Struct\Response\Contract\ResponseStruct as ResponseStructContract;
35
use Valkyrja\Reflection\Contract\Reflection;
36
37
use function array_column;
38
39
/**
40
 * Class AttributeCollector.
41
 *
42
 * @author Melech Mizrachi
43
 */
44
class AttributeCollector implements Contract
45
{
46
    public function __construct(
47
        protected Attributes $attributes,
48
        protected Reflection $reflection,
49
        protected Processor $processor
50
    ) {
51
    }
52
53
    /**
54
     * @inheritDoc
55
     *
56
     * @throws ReflectionException
57
     */
58
    #[Override]
59
    public function getRoutes(string ...$classes): array
60
    {
61
        $routes     = [];
62
        $attributes = [];
63
64
        foreach ($classes as $class) {
65
            /** @var Route[] $memberAttributes */
66
            $memberAttributes = $this->attributes->forClassMembers($class, Route::class);
67
68
            // Iterate through all the members' attributes
69
            foreach ($memberAttributes as $routeAttribute) {
70
                $routeDispatch = $routeAttribute->getDispatch();
71
72
                $method              = $routeDispatch->getMethod();
73
                $routeParameters     = $this->attributes->forMethod($class, $method, Parameter::class);
74
                $routeMiddleware     = $this->attributes->forMethod($class, $method, Middleware::class);
75
                $routeRequestStruct  = $this->attributes->forMethod($class, $method, RequestStruct::class);
76
                $routeResponseStruct = $this->attributes->forMethod($class, $method, ResponseStruct::class);
77
                $requestMethods      = $this->attributes->forMethod($class, $method, RequestMethod::class);
78
79
                /** @var class-string[] $middlewareClasses */
80
                $middlewareClasses = array_column(
81
                    $routeMiddleware,
82
                    'name'
83
                );
84
85
                foreach ($middlewareClasses as $middlewareClass) {
86
                    $routeAttribute = match (true) {
87
                        is_a($middlewareClass, RouteMatchedMiddleware::class, true)    => $routeAttribute->withAddedRouteMatchedMiddleware(
88
                            $middlewareClass
89
                        ),
90
                        is_a($middlewareClass, RouteDispatchedMiddleware::class, true) => $routeAttribute->withAddedRouteDispatchedMiddleware(
91
                            $middlewareClass
92
                        ),
93
                        is_a($middlewareClass, ThrowableCaughtMiddleware::class, true) => $routeAttribute->withAddedThrowableCaughtMiddleware(
94
                            $middlewareClass
95
                        ),
96
                        is_a($middlewareClass, SendingResponseMiddleware::class, true) => $routeAttribute->withAddedSendingResponseMiddleware(
97
                            $middlewareClass
98
                        ),
99
                        is_a($middlewareClass, TerminatedMiddleware::class, true)      => $routeAttribute->withAddedTerminatedMiddleware(
100
                            $middlewareClass
101
                        ),
102
                        default                                                        => $routeAttribute,
103
                    };
104
                }
105
106
                foreach ($requestMethods as $requestMethod) {
107
                    /** @psalm-suppress MixedArgument Unsure why Psalm doesn't realize that the requestMethods property is an array of RequestMethod enums */
108
                    $routeAttribute = $routeAttribute->withAddedRequestMethods(...$requestMethod->requestMethods);
109
                }
110
111
                /** @var class-string<RequestStructContract>[] $requestStruct */
112
                $requestStruct = array_column($routeRequestStruct, 'name');
113
114
                if ($requestStruct !== []) {
115
                    $routeAttribute = $routeAttribute->withRequestStruct($requestStruct[0]);
116
                }
117
118
                /** @var class-string<ResponseStructContract>[] $responseStruct */
119
                $responseStruct = array_column($routeResponseStruct, 'name');
120
121
                if ($responseStruct !== []) {
122
                    $routeAttribute = $routeAttribute->withResponseStruct($responseStruct[0]);
123
                }
124
125
                $routeAttribute = $routeAttribute->withParameters(
126
                    ...$this->attributes->forMethodParameters($class, $method, Parameter::class),
127
                    ...$routeParameters
128
                );
129
130
                // And set a new route with the controller defined annotation additions
131
                $attributes[] = $routeAttribute;
132
            }
133
        }
134
135
        foreach ($attributes as $attribute) {
136
            $attribute = $this->setRouteProperties($attribute);
137
138
            $routes[] = new \Valkyrja\Http\Routing\Data\Route(
139
                path: $attribute->getPath(),
140
                name: $attribute->getName(),
141
                dispatch: $attribute->getDispatch(),
142
                requestMethods: $attribute->getRequestMethods(),
143
                regex: $attribute->getRegex(),
144
                parameters: $attribute->getParameters(),
145
                routeMatchedMiddleware: $attribute->getRouteMatchedMiddleware(),
146
                routeDispatchedMiddleware: $attribute->getRouteDispatchedMiddleware(),
147
                throwableCaughtMiddleware: $attribute->getThrowableCaughtMiddleware(),
148
                sendingResponseMiddleware: $attribute->getSendingResponseMiddleware(),
149
                terminatedMiddleware: $attribute->getTerminatedMiddleware(),
150
                requestStruct: $attribute->getRequestStruct(),
151
                responseStruct: $attribute->getResponseStruct()
152
            );
153
        }
154
155
        return $routes;
156
    }
157
158
    /**
159
     * Set the route properties from arguments.
160
     *
161
     * @param RouteContract $route
162
     *
163
     * @throws ReflectionException
164
     *
165
     * @return RouteContract
166
     */
167
    protected function setRouteProperties(RouteContract $route): RouteContract
168
    {
169
        $dispatch = $route->getDispatch();
170
171
        $methodReflection = $this->reflection->forClassMethod($dispatch->getClass(), $dispatch->getMethod());
172
        // Set the dependencies
173
        $route = $route->withDispatch(
174
            $dispatch->withDependencies($this->reflection->getDependencies($methodReflection))
175
        );
176
177
        return $this->processor->route($route);
178
    }
179
}
180