Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
56 / 56
100.00% covered (success)
100.00%
6 / 6
CRAP
100.00% covered (success)
100.00%
1 / 1
Reflector
100.00% covered (success)
100.00%
56 / 56
100.00% covered (success)
100.00%
6 / 6
23
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getMethodRoutes
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
 getObjectOrigins
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
4
 getRoutes
100.00% covered (success)
100.00%
18 / 18
100.00% covered (success)
100.00%
1 / 1
6
 getMethodRoutesNotFound
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
 getRoutesNotFound
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
6
1<?php declare(strict_types=1);
2/*
3 * This file is part of Aplus Framework Routing Library.
4 *
5 * (c) Natan Felles <natanfelles@gmail.com>
6 *
7 * For the full copyright and license information, please view the LICENSE
8 * file that was distributed with this source code.
9 */
10namespace Framework\Routing;
11
12use Framework\Routing\Attributes\Origin;
13use Framework\Routing\Attributes\Route;
14use Framework\Routing\Attributes\RouteNotFound;
15use ReflectionClass;
16use ReflectionException;
17
18/**
19 * Class Reflector.
20 *
21 * @package routing
22 */
23class Reflector
24{
25    /**
26     * @var ReflectionClass<object>
27     */
28    protected ReflectionClass $reflection;
29
30    /**
31     * @template T of object
32     *
33     * @param class-string<T>|object $routeActions
34     *
35     * @throws ReflectionException
36     */
37    public function __construct(object | string $routeActions)
38    {
39        $this->reflection = new ReflectionClass($routeActions); // @phpstan-ignore-line
40    }
41
42    /**
43     * @param string $method
44     *
45     * @throws ReflectionException
46     *
47     * @return array<Route>
48     */
49    protected function getMethodRoutes(string $method) : array
50    {
51        $reflectionMethod = $this->reflection->getMethod($method);
52        $routes = [];
53        foreach ($reflectionMethod->getAttributes() as $attribute) {
54            if ($attribute->getName() === Route::class) {
55                $routes[] = $attribute->newInstance();
56            }
57        }
58        return $routes; // @phpstan-ignore-line
59    }
60
61    /**
62     * @template T of object
63     *
64     * @param ReflectionClass<T> $reflection
65     *
66     * @return array<int,string>
67     */
68    protected function getObjectOrigins(ReflectionClass $reflection) : array
69    {
70        $origins = [];
71        foreach ($reflection->getAttributes() as $attribute) {
72            if ($attribute->getName() === Origin::class) {
73                /**
74                 * @var Origin $origin
75                 */
76                $origin = $attribute->newInstance();
77                $origins[] = $origin->getOrigin();
78            }
79        }
80        $parent = $reflection->getParentClass();
81        if ($parent) {
82            $origins = [...$origins, ...$this->getObjectOrigins($parent)];
83        }
84        $origins = \array_unique($origins);
85        \sort($origins);
86        return $origins;
87    }
88
89    /**
90     * @throws ReflectionException
91     *
92     * @return array<mixed>
93     */
94    public function getRoutes() : array
95    {
96        $origins = $this->getObjectOrigins($this->reflection);
97        $result = [];
98        foreach ($this->reflection->getMethods() as $method) {
99            if ( ! $method->isPublic()) {
100                continue;
101            }
102            $routes = $this->getMethodRoutes($method->getName());
103            if (empty($routes)) {
104                continue;
105            }
106            foreach ($routes as $route) {
107                $result[] = [
108                    'origins' => $route->getOrigins() ?: $origins,
109                    'methods' => $route->getMethods(),
110                    'path' => $route->getPath(),
111                    'arguments' => $route->getArguments(),
112                    'name' => $route->getName(),
113                    'action' => $this->reflection->name . '::' . $method->name,
114                ];
115            }
116        }
117        return $result;
118    }
119
120    /**
121     * @param string $method
122     *
123     * @throws ReflectionException
124     *
125     * @return array<RouteNotFound>
126     */
127    protected function getMethodRoutesNotFound(string $method) : array
128    {
129        $reflectionMethod = $this->reflection->getMethod($method);
130        $routes = [];
131        foreach ($reflectionMethod->getAttributes() as $attribute) {
132            if ($attribute->getName() === RouteNotFound::class) {
133                $routes[] = $attribute->newInstance();
134            }
135        }
136        return $routes; // @phpstan-ignore-line
137    }
138
139    /**
140     * @throws ReflectionException
141     *
142     * @return array<mixed>
143     */
144    public function getRoutesNotFound() : array
145    {
146        $origins = $this->getObjectOrigins($this->reflection);
147        $result = [];
148        foreach ($this->reflection->getMethods() as $method) {
149            if ( ! $method->isPublic()) {
150                continue;
151            }
152            $routes = $this->getMethodRoutesNotFound($method->getName());
153            if (empty($routes)) {
154                continue;
155            }
156            foreach ($routes as $route) {
157                $result[] = [
158                    'origins' => $route->getOrigins() ?: $origins,
159                    'action' => $this->reflection->name . '::' . $method->name,
160                ];
161            }
162        }
163        return $result;
164    }
165}