Passed
Push — main ( fc0b01...4caf15 )
by Moises
01:36
created

Route::setStrictMode()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 2
c 1
b 0
f 0
dl 0
loc 4
rs 10
cc 1
nc 1
nop 1
1
<?php
2
3
namespace DevPontes\Route;
4
5
use DevPontes\Route\Traits\Matcher;
6
use DevPontes\Route\Exception\ErrorRoute;
7
8
/**
9
 * Description of Route
10
 *
11
 * @author Moises Pontes
12
 * @package DevPontes\Route
13
 */
14
class Route
15
{
16
    use Matcher;
17
18
    /** @var ErrorRoute */
19
    private $fail;
20
21
    /** @var string */
22
    private $namespace;
23
24
    /** @var array */
25
    private $url;
26
27
    /** @var array */
28
    private $routes = [];
29
30
    /** @var boolean */
31
    private $strictMode = false;
32
33
    /**
34
     * Route constructor.
35
     *
36
     * @param array $routes
37
     */
38
    public function __construct(array $routes)
39
    {
40
        $url = filter_input(INPUT_GET, 'url', FILTER_SANITIZE_SPECIAL_CHARS);
41
42
        $this->setUrl($url);
43
        $this->setRoutes($routes);
44
    }
45
46
    /**
47
     * @return ErrorRoute|null
48
     */
49
    public function fail(): ?ErrorRoute
50
    {
51
        return $this->fail;
52
    }
53
54
    /**
55
     * Enable or disable strict mode for URL normalization.
56
     *
57
     * - When strict mode is enabled, trailing slashes are preserved.
58
     * - When disabled, trailing slashes are removed (except for root "/").
59
     *
60
     * @param boolean $strictMode
61
     * @return self
62
     */
63
    public function setStrictMode(bool $strictMode): self
64
    {
65
        $this->strictMode = $strictMode;
66
        return $this;
67
    }
68
69
    /**
70
     * Set namespace app
71
     *
72
     * @param string $namespace
73
     * @return Route
74
     */
75
    public function namespace(string $namespace): Route
76
    {
77
        $this->namespace = $namespace;
78
        return $this;
79
    }
80
81
    /**
82
     *  Normalize a URL string:
83
     *
84
     * - Trim spaces and collapse multiple slashes into one
85
     * - Remove trailing slash (except root "/") when strictMode = false
86
     * - Ensure the URL always starts with "/"
87
     *
88
     * @param string $url
89
     * @return string
90
     */
91
    private function normalizeUrl(string $url): string
92
    {
93
        $url = preg_replace('#/+#', '/', trim($url));
94
95
        if ($url === '' || $url === false) {
96
            return '/';
97
        }
98
99
        if (!$this->strictMode && $url !== '/') {
100
            $url = rtrim($url, '/');
101
        }
102
103
        return $url[0] === '/' ? $url : '/' . $url;
104
    }
105
106
    /**
107
     * Separate the URL
108
     *
109
     * @param string|null $url
110
     * @return void
111
     */
112
    private function setUrl(?string $url): void
113
    {
114
        $this->url = explode('/', $this->normalizeUrl($url ?? ''));
115
    }
116
117
    /**
118
     * Config the routes
119
     *
120
     * @param array $routes
121
     * @return void
122
     */
123
    private function setRoutes(array $routes): void
124
    {
125
        foreach ($routes as $route) {
126
            $action = explode('@', $route[1]);
127
            $url = preg_replace('/\{[^}]+\}/', '{}', $route[0]);
128
            $this->routes[] = new Router($url, $action[1], $action[0]);
129
        }
130
    }
131
132
    /**
133
     * Run route
134
     *
135
     * @return void
136
     */
137
    public function run(): void
138
    {
139
        try {
140
            $router = $this->match($this->url);
141
142
            if (empty($router)) {
143
                throw new ErrorRoute("Page not found", 404);
144
            } else {
145
                $router->execute($this->namespace);
146
            }
147
        } catch (ErrorRoute $err) {
148
            $this->fail = $err;
149
        }
150
    }
151
}
152