Passed
Push — main ( deaffd...dea3f5 )
by Chema
55s queued 13s
created

Route::controller()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
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
The doc comment object|class-string at position 2 could not be parsed: Unknown type name 'class-string' at position 2 in object|class-string.
Loading history...
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
Documentation Bug introduced by
The doc comment object|class-string at position 2 could not be parsed: Unknown type name 'class-string' at position 2 in object|class-string.
Loading history...
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
Bug Best Practice introduced by
The expression return $this->pathPattern could return the type null which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
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