1 | <?php |
||
2 | |||
3 | declare(strict_types=1); |
||
4 | |||
5 | namespace Gacela\Router\Entities; |
||
6 | |||
7 | use Gacela\Container\Container; |
||
8 | use Gacela\Router\Configure\Bindings; |
||
9 | use Gacela\Router\Exceptions\UnsupportedResponseTypeException; |
||
10 | use Stringable; |
||
11 | |||
12 | use function is_object; |
||
13 | use function is_string; |
||
14 | |||
15 | final class Route |
||
16 | { |
||
17 | /** |
||
18 | * @param object|class-string $controller |
||
0 ignored issues
–
show
Documentation
Bug
introduced
by
![]() |
|||
19 | */ |
||
20 | 102 | public function __construct( |
|
21 | private string $method, |
||
22 | private string $path, |
||
23 | private object|string $controller, |
||
24 | private string $action = '__invoke', |
||
25 | private ?string $pathPattern = null, |
||
26 | ) { |
||
27 | 102 | } |
|
28 | |||
29 | /** |
||
30 | * @psalm-suppress MixedMethodCall |
||
31 | */ |
||
32 | 97 | public function run(Bindings $bindings): string|Stringable |
|
33 | { |
||
34 | 97 | $params = (new RouteParams($this))->getAll(); |
|
35 | |||
36 | 95 | if (!is_object($this->controller)) { |
|
37 | 70 | $creator = new Container($bindings->getAllBindings()); |
|
38 | /** @var object $controller */ |
||
39 | 70 | $controller = $creator->get($this->controller); |
|
40 | 70 | $response = $controller->{$this->action}(...$params); |
|
41 | } else { |
||
42 | /** @var string|Stringable $response */ |
||
43 | 25 | $response = $this->controller->{$this->action}(...$params); |
|
44 | } |
||
45 | |||
46 | 90 | if (!is_string($response) && !($response instanceof Stringable)) { |
|
47 | 4 | throw UnsupportedResponseTypeException::fromType($response); |
|
48 | } |
||
49 | |||
50 | 86 | return $response; |
|
51 | } |
||
52 | |||
53 | 97 | public function path(): string |
|
54 | { |
||
55 | 97 | return $this->path; |
|
56 | } |
||
57 | |||
58 | /** |
||
59 | * @return object|class-string |
||
0 ignored issues
–
show
|
|||
60 | */ |
||
61 | 97 | public function controller(): object|string |
|
62 | { |
||
63 | 97 | return $this->controller; |
|
64 | } |
||
65 | |||
66 | 97 | public function action(): string |
|
67 | { |
||
68 | 97 | return $this->action; |
|
69 | } |
||
70 | |||
71 | 97 | public function getPathPattern(): string |
|
72 | { |
||
73 | 97 | if ($this->pathPattern === null) { |
|
74 | 97 | $this->pathPattern = $this->calculateDefaultPathPattern(); |
|
75 | } |
||
76 | |||
77 | 97 | return $this->pathPattern; |
|
0 ignored issues
–
show
|
|||
78 | } |
||
79 | |||
80 | 102 | public function requestMatches(): bool |
|
81 | { |
||
82 | 102 | return $this->methodMatches() && $this->pathMatches(); |
|
83 | } |
||
84 | |||
85 | 102 | private function methodMatches(): bool |
|
86 | { |
||
87 | 102 | return Request::fromGlobals()->isMethod($this->method); |
|
88 | } |
||
89 | |||
90 | 97 | private function pathMatches(): bool |
|
91 | { |
||
92 | 97 | $path = Request::fromGlobals()->path(); |
|
93 | |||
94 | 97 | return (bool)preg_match($this->getPathPattern(), $path); |
|
95 | } |
||
96 | |||
97 | /** |
||
98 | * @todo: improve this method and maybe move it to a separate class |
||
99 | * |
||
100 | * @return string |
||
101 | */ |
||
102 | 97 | private function calculateDefaultPathPattern(): string |
|
103 | { |
||
104 | 97 | if ($this->path === '') { |
|
105 | 1 | return '#^/?$#'; |
|
106 | } |
||
107 | |||
108 | 97 | $parts = explode('/', $this->path); |
|
109 | 97 | $pattern = ''; |
|
110 | 97 | foreach ($parts as $part) { |
|
111 | 97 | if (preg_match(RouteParams::MANDATORY_PARAM_PATTERN, $part)) { |
|
112 | 43 | $pattern .= '/([^\/]+)'; |
|
113 | 93 | } elseif (preg_match(RouteParams::OPTIONAL_PARAM_PATTERN, $part)) { |
|
114 | 8 | $pattern .= '/?([^\/]+)?'; |
|
115 | } else { |
||
116 | 93 | $pattern .= '/' . $part; |
|
117 | } |
||
118 | } |
||
119 | |||
120 | 97 | return '#^/' . ltrim($pattern, '/') . '$#'; |
|
121 | } |
||
122 | } |
||
123 |