Code Coverage
 
Lines
Branches
Paths
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
52 / 52
93.33% covered (success)
93.33%
56 / 60
47.14% covered (danger)
47.14%
33 / 70
90.91% covered (success)
90.91%
20 / 22
CRAP
0.00% covered (danger)
0.00%
0 / 1
Route
100.00% covered (success)
100.00%
52 / 52
93.33% covered (success)
93.33%
56 / 60
47.14% covered (danger)
47.14%
33 / 70
100.00% covered (success)
100.00%
22 / 22
215.90
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 any
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 map
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 get
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 post
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 put
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 patch
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 delete
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 head
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 options
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 withMethods
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 methods
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 prefix
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
5 / 5
75.00% covered (warning)
75.00%
3 / 4
100.00% covered (success)
100.00%
1 / 1
3.14
 controller
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 name
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 url
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 view
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 pattern
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 match
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 routePattern
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 pathWithoutPrefix
100.00% covered (success)
100.00%
12 / 12
85.00% covered (warning)
85.00%
17 / 20
12.50% covered (danger)
12.50%
5 / 40
100.00% covered (success)
100.00%
1 / 1
39.83
 normalizePrefix
100.00% covered (success)
100.00%
1 / 1
85.71% covered (warning)
85.71%
6 / 7
66.67% covered (warning)
66.67%
2 / 3
100.00% covered (success)
100.00%
1 / 1
2.15
1<?php
2
3declare(strict_types=1);
4
5namespace Celemas\Router;
6
7use Celemas\Router\Exception\ValueError;
8use Closure;
9
10/**
11 * @psalm-api
12 *
13 * @psalm-type View = callable|list{string, string}|non-empty-string
14 */
15class Route
16{
17    use AddsBeforeAfter;
18    use AddsMiddleware;
19
20    /** @var null|list<string> */
21    protected ?array $methods = null;
22
23    /** @var Closure|list{string, string}|string */
24    protected Closure|array|string $view;
25
26    protected ?RoutePattern $routePattern = null;
27
28    /**
29     * @param string $pattern The URL pattern of the route
30     *
31     * @param callable|list{string, string}|non-empty-string $view The callable view. Can be a closure, an invokable object or any other callable
32     *
33     * @param string $name The name of the route. If not given the pattern will be hashed and used as name.
34     */
35    public function __construct(
36        protected string $pattern,
37        callable|array|string $view,
38        protected string $name = '',
39    ) {
40        if (is_callable($view)) {
41            $this->view = Closure::fromCallable($view);
42        } else {
43            $this->view = $view;
44        }
45    }
46
47    /** @param callable|list{string, string}|non-empty-string $view */
48    public static function any(string $pattern, callable|array|string $view, string $name = ''): self
49    {
50        return new self($pattern, $view, $name);
51    }
52
53    /**
54     * @param array<array-key, string> $methods
55     * @param callable|list{string, string}|non-empty-string $view
56     */
57    public static function map(
58        array $methods,
59        string $pattern,
60        callable|array|string $view,
61        string $name = '',
62    ): self {
63        if ($methods === []) {
64            throw new ValueError('Route method list cannot be empty.');
65        }
66
67        return new self($pattern, $view, $name)->withMethods(...array_values($methods));
68    }
69
70    /** @param callable|list{string, string}|non-empty-string $view */
71    public static function get(string $pattern, callable|array|string $view, string $name = ''): self
72    {
73        return new self($pattern, $view, $name)->withMethods('GET');
74    }
75
76    /** @param callable|list{string, string}|non-empty-string $view */
77    public static function post(string $pattern, callable|array|string $view, string $name = ''): self
78    {
79        return new self($pattern, $view, $name)->withMethods('POST');
80    }
81
82    /** @param callable|list{string, string}|non-empty-string $view */
83    public static function put(string $pattern, callable|array|string $view, string $name = ''): self
84    {
85        return new self($pattern, $view, $name)->withMethods('PUT');
86    }
87
88    /** @param callable|list{string, string}|non-empty-string $view */
89    public static function patch(string $pattern, callable|array|string $view, string $name = ''): self
90    {
91        return new self($pattern, $view, $name)->withMethods('PATCH');
92    }
93
94    /** @param callable|list{string, string}|non-empty-string $view */
95    public static function delete(
96        string $pattern,
97        callable|array|string $view,
98        string $name = '',
99    ): self {
100        return new self($pattern, $view, $name)->withMethods('DELETE');
101    }
102
103    /** @param callable|list{string, string}|non-empty-string $view */
104    public static function head(string $pattern, callable|array|string $view, string $name = ''): self
105    {
106        return new self($pattern, $view, $name)->withMethods('HEAD');
107    }
108
109    /** @param callable|list{string, string}|non-empty-string $view */
110    public static function options(
111        string $pattern,
112        callable|array|string $view,
113        string $name = '',
114    ): self {
115        return new self($pattern, $view, $name)->withMethods('OPTIONS');
116    }
117
118    private function withMethods(string ...$args): self
119    {
120        /** @var list<string> $methods */
121        $methods = array_map(static fn(string $method): string => strtoupper($method), $args);
122        $this->methods = [...($this->methods ?? []), ...$methods];
123
124        return $this;
125    }
126
127    /** @return list<string> */
128    public function methods(): array
129    {
130        return $this->methods ?? [];
131    }
132
133    /** @internal */
134    public function prefix(string $pattern = '', string $name = ''): static
135    {
136        if ($pattern !== '') {
137            $this->pattern = $pattern . $this->pattern;
138            $this->routePattern = null;
139        }
140
141        if ($name !== '') {
142            $this->name = $name . $this->name;
143        }
144
145        return $this;
146    }
147
148    /** @internal */
149    public function controller(string $controller): static
150    {
151        if (is_string($this->view)) {
152            $this->view = [$controller, $this->view];
153
154            return $this;
155        }
156
157        throw new ValueError(
158            'Cannot add controller to route action. Controller groups require bare method names.',
159        );
160    }
161
162    public function name(): string
163    {
164        return $this->name;
165    }
166
167    /** @param array<array-key, mixed> $params */
168    public function url(array $params = []): string
169    {
170        return $this->routePattern()->generate($params);
171    }
172
173    /** @return Closure|list{string, string}|string */
174    public function view(): Closure|array|string
175    {
176        return $this->view;
177    }
178
179    public function pattern(): string
180    {
181        return $this->pattern;
182    }
183
184    /** @return null|array<string, string> */
185    public function match(string $url, string $prefix = ''): ?array
186    {
187        $path = $this->pathWithoutPrefix($url, $prefix);
188
189        if ($path === null) {
190            return null;
191        }
192
193        return $this->routePattern()->match($path);
194    }
195
196    private function routePattern(): RoutePattern
197    {
198        return $this->routePattern ??= new RoutePattern($this->pattern);
199    }
200
201    private function pathWithoutPrefix(string $url, string $prefix): ?string
202    {
203        $path = $url === '' ? '/' : $url;
204        $prefix = self::normalizePrefix($prefix);
205
206        if ($prefix === '') {
207            return $path;
208        }
209
210        if ($path === $prefix) {
211            return '/';
212        }
213
214        if (!str_starts_with($path, $prefix . '/')) {
215            return null;
216        }
217
218        $path = substr($path, strlen($prefix));
219
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
221            return null;
222        }
223
224        return $path;
225    }
226
227    private static function normalizePrefix(string $prefix): string
228    {
229        return $prefix === '' ? '' : '/' . trim($prefix, '/');
230    }
231}

Paths

Below are the source code lines that represent each code path as identified by Xdebug. Please note a path is not necessarily coterminous with a line, a line may contain multiple paths and therefore show up more than once. Please also be aware that some paths may include implicit rather than explicit branches, e.g. an if statement always has an else as part of its logical flow even if you didn't write one.

Route->__construct
36        protected string $pattern,
37        callable|array|string $view,
38        protected string $name = '',
39    ) {
40        if (is_callable($view)) {
 
40        if (is_callable($view)) {
41            $this->view = Closure::fromCallable($view);
 
45    }
36        protected string $pattern,
37        callable|array|string $view,
38        protected string $name = '',
39    ) {
40        if (is_callable($view)) {
 
43            $this->view = $view;
44        }
45    }
 
45    }
Route->any
48    public static function any(string $pattern, callable|array|string $view, string $name = ''): self
49    {
50        return new self($pattern, $view, $name);
51    }
Route->controller
149    public function controller(string $controller): static
150    {
151        if (is_string($this->view)) {
 
152            $this->view = [$controller, $this->view];
153
154            return $this;
149    public function controller(string $controller): static
150    {
151        if (is_string($this->view)) {
 
157        throw new ValueError(
158            'Cannot add controller to route action. Controller groups require bare method names.',
159        );
160    }
Route->delete
96        string $pattern,
97        callable|array|string $view,
98        string $name = '',
99    ): self {
100        return new self($pattern, $view, $name)->withMethods('DELETE');
101    }
Route->get
71    public static function get(string $pattern, callable|array|string $view, string $name = ''): self
72    {
73        return new self($pattern, $view, $name)->withMethods('GET');
74    }
Route->head
104    public static function head(string $pattern, callable|array|string $view, string $name = ''): self
105    {
106        return new self($pattern, $view, $name)->withMethods('HEAD');
107    }
Route->map
58        array $methods,
59        string $pattern,
60        callable|array|string $view,
61        string $name = '',
62    ): self {
63        if ($methods === []) {
 
64            throw new ValueError('Route method list cannot be empty.');
58        array $methods,
59        string $pattern,
60        callable|array|string $view,
61        string $name = '',
62    ): self {
63        if ($methods === []) {
 
67        return new self($pattern, $view, $name)->withMethods(...array_values($methods));
68    }
Route->match
185    public function match(string $url, string $prefix = ''): ?array
186    {
187        $path = $this->pathWithoutPrefix($url, $prefix);
188
189        if ($path === null) {
 
190            return null;
185    public function match(string $url, string $prefix = ''): ?array
186    {
187        $path = $this->pathWithoutPrefix($url, $prefix);
188
189        if ($path === null) {
 
193        return $this->routePattern()->match($path);
194    }
Route->methods
130        return $this->methods ?? [];
131    }
Route->name
164        return $this->name;
165    }
Route->normalizePrefix
227    private static function normalizePrefix(string $prefix): string
228    {
229        return $prefix === '' ? '' : '/' . trim($prefix, '/');
 
229        return $prefix === '' ? '' : '/' . trim($prefix, '/');
 
229        return $prefix === '' ? '' : '/' . trim($prefix, '/');
230    }
227    private static function normalizePrefix(string $prefix): string
228    {
229        return $prefix === '' ? '' : '/' . trim($prefix, '/');
 
229        return $prefix === '' ? '' : '/' . trim($prefix, '/');
 
229        return $prefix === '' ? '' : '/' . trim($prefix, '/');
 
229        return $prefix === '' ? '' : '/' . trim($prefix, '/');
 
229        return $prefix === '' ? '' : '/' . trim($prefix, '/');
230    }
227    private static function normalizePrefix(string $prefix): string
228    {
229        return $prefix === '' ? '' : '/' . trim($prefix, '/');
 
229        return $prefix === '' ? '' : '/' . trim($prefix, '/');
 
229        return $prefix === '' ? '' : '/' . trim($prefix, '/');
 
229        return $prefix === '' ? '' : '/' . trim($prefix, '/');
 
229        return $prefix === '' ? '' : '/' . trim($prefix, '/');
230    }
Route->options
111        string $pattern,
112        callable|array|string $view,
113        string $name = '',
114    ): self {
115        return new self($pattern, $view, $name)->withMethods('OPTIONS');
116    }
Route->patch
89    public static function patch(string $pattern, callable|array|string $view, string $name = ''): self
90    {
91        return new self($pattern, $view, $name)->withMethods('PATCH');
92    }
Route->pathWithoutPrefix
201    private function pathWithoutPrefix(string $url, string $prefix): ?string
202    {
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
204        $prefix = self::normalizePrefix($prefix);
205
206        if ($prefix === '') {
 
207            return $path;
201    private function pathWithoutPrefix(string $url, string $prefix): ?string
202    {
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
204        $prefix = self::normalizePrefix($prefix);
205
206        if ($prefix === '') {
 
210        if ($path === $prefix) {
 
211            return '/';
201    private function pathWithoutPrefix(string $url, string $prefix): ?string
202    {
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
204        $prefix = self::normalizePrefix($prefix);
205
206        if ($prefix === '') {
 
210        if ($path === $prefix) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
215            return null;
201    private function pathWithoutPrefix(string $url, string $prefix): ?string
202    {
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
204        $prefix = self::normalizePrefix($prefix);
205
206        if ($prefix === '') {
 
210        if ($path === $prefix) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
219
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
221            return null;
201    private function pathWithoutPrefix(string $url, string $prefix): ?string
202    {
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
204        $prefix = self::normalizePrefix($prefix);
205
206        if ($prefix === '') {
 
210        if ($path === $prefix) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
219
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
224        return $path;
225    }
201    private function pathWithoutPrefix(string $url, string $prefix): ?string
202    {
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
204        $prefix = self::normalizePrefix($prefix);
205
206        if ($prefix === '') {
 
210        if ($path === $prefix) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
219
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
221            return null;
201    private function pathWithoutPrefix(string $url, string $prefix): ?string
202    {
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
204        $prefix = self::normalizePrefix($prefix);
205
206        if ($prefix === '') {
 
210        if ($path === $prefix) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
219
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
224        return $path;
225    }
201    private function pathWithoutPrefix(string $url, string $prefix): ?string
202    {
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
204        $prefix = self::normalizePrefix($prefix);
205
206        if ($prefix === '') {
 
210        if ($path === $prefix) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
219
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
221            return null;
201    private function pathWithoutPrefix(string $url, string $prefix): ?string
202    {
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
204        $prefix = self::normalizePrefix($prefix);
205
206        if ($prefix === '') {
 
210        if ($path === $prefix) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
219
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
224        return $path;
225    }
201    private function pathWithoutPrefix(string $url, string $prefix): ?string
202    {
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
204        $prefix = self::normalizePrefix($prefix);
205
206        if ($prefix === '') {
 
210        if ($path === $prefix) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
219
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
221            return null;
201    private function pathWithoutPrefix(string $url, string $prefix): ?string
202    {
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
204        $prefix = self::normalizePrefix($prefix);
205
206        if ($prefix === '') {
 
210        if ($path === $prefix) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
219
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
224        return $path;
225    }
201    private function pathWithoutPrefix(string $url, string $prefix): ?string
202    {
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
204        $prefix = self::normalizePrefix($prefix);
205
206        if ($prefix === '') {
 
210        if ($path === $prefix) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
215            return null;
201    private function pathWithoutPrefix(string $url, string $prefix): ?string
202    {
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
204        $prefix = self::normalizePrefix($prefix);
205
206        if ($prefix === '') {
 
210        if ($path === $prefix) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
219
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
221            return null;
201    private function pathWithoutPrefix(string $url, string $prefix): ?string
202    {
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
204        $prefix = self::normalizePrefix($prefix);
205
206        if ($prefix === '') {
 
210        if ($path === $prefix) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
219
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
224        return $path;
225    }
201    private function pathWithoutPrefix(string $url, string $prefix): ?string
202    {
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
204        $prefix = self::normalizePrefix($prefix);
205
206        if ($prefix === '') {
 
210        if ($path === $prefix) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
219
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
221            return null;
201    private function pathWithoutPrefix(string $url, string $prefix): ?string
202    {
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
204        $prefix = self::normalizePrefix($prefix);
205
206        if ($prefix === '') {
 
210        if ($path === $prefix) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
219
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
224        return $path;
225    }
201    private function pathWithoutPrefix(string $url, string $prefix): ?string
202    {
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
204        $prefix = self::normalizePrefix($prefix);
205
206        if ($prefix === '') {
 
210        if ($path === $prefix) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
219
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
221            return null;
201    private function pathWithoutPrefix(string $url, string $prefix): ?string
202    {
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
204        $prefix = self::normalizePrefix($prefix);
205
206        if ($prefix === '') {
 
210        if ($path === $prefix) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
219
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
224        return $path;
225    }
201    private function pathWithoutPrefix(string $url, string $prefix): ?string
202    {
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
204        $prefix = self::normalizePrefix($prefix);
205
206        if ($prefix === '') {
 
210        if ($path === $prefix) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
219
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
221            return null;
201    private function pathWithoutPrefix(string $url, string $prefix): ?string
202    {
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
204        $prefix = self::normalizePrefix($prefix);
205
206        if ($prefix === '') {
 
210        if ($path === $prefix) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
219
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
224        return $path;
225    }
201    private function pathWithoutPrefix(string $url, string $prefix): ?string
202    {
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
204        $prefix = self::normalizePrefix($prefix);
205
206        if ($prefix === '') {
 
207            return $path;
201    private function pathWithoutPrefix(string $url, string $prefix): ?string
202    {
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
204        $prefix = self::normalizePrefix($prefix);
205
206        if ($prefix === '') {
 
210        if ($path === $prefix) {
 
211            return '/';
201    private function pathWithoutPrefix(string $url, string $prefix): ?string
202    {
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
204        $prefix = self::normalizePrefix($prefix);
205
206        if ($prefix === '') {
 
210        if ($path === $prefix) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
215            return null;
201    private function pathWithoutPrefix(string $url, string $prefix): ?string
202    {
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
204        $prefix = self::normalizePrefix($prefix);
205
206        if ($prefix === '') {
 
210        if ($path === $prefix) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
219
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
221            return null;
201    private function pathWithoutPrefix(string $url, string $prefix): ?string
202    {
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
204        $prefix = self::normalizePrefix($prefix);
205
206        if ($prefix === '') {
 
210        if ($path === $prefix) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
219
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
224        return $path;
225    }
201    private function pathWithoutPrefix(string $url, string $prefix): ?string
202    {
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
204        $prefix = self::normalizePrefix($prefix);
205
206        if ($prefix === '') {
 
210        if ($path === $prefix) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
219
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
221            return null;
201    private function pathWithoutPrefix(string $url, string $prefix): ?string
202    {
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
204        $prefix = self::normalizePrefix($prefix);
205
206        if ($prefix === '') {
 
210        if ($path === $prefix) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
219
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
224        return $path;
225    }
201    private function pathWithoutPrefix(string $url, string $prefix): ?string
202    {
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
204        $prefix = self::normalizePrefix($prefix);
205
206        if ($prefix === '') {
 
210        if ($path === $prefix) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
219
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
221            return null;
201    private function pathWithoutPrefix(string $url, string $prefix): ?string
202    {
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
204        $prefix = self::normalizePrefix($prefix);
205
206        if ($prefix === '') {
 
210        if ($path === $prefix) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
219
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
224        return $path;
225    }
201    private function pathWithoutPrefix(string $url, string $prefix): ?string
202    {
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
204        $prefix = self::normalizePrefix($prefix);
205
206        if ($prefix === '') {
 
210        if ($path === $prefix) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
219
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
221            return null;
201    private function pathWithoutPrefix(string $url, string $prefix): ?string
202    {
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
204        $prefix = self::normalizePrefix($prefix);
205
206        if ($prefix === '') {
 
210        if ($path === $prefix) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
219
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
224        return $path;
225    }
201    private function pathWithoutPrefix(string $url, string $prefix): ?string
202    {
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
204        $prefix = self::normalizePrefix($prefix);
205
206        if ($prefix === '') {
 
210        if ($path === $prefix) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
215            return null;
201    private function pathWithoutPrefix(string $url, string $prefix): ?string
202    {
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
204        $prefix = self::normalizePrefix($prefix);
205
206        if ($prefix === '') {
 
210        if ($path === $prefix) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
219
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
221            return null;
201    private function pathWithoutPrefix(string $url, string $prefix): ?string
202    {
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
204        $prefix = self::normalizePrefix($prefix);
205
206        if ($prefix === '') {
 
210        if ($path === $prefix) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
219
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
224        return $path;
225    }
201    private function pathWithoutPrefix(string $url, string $prefix): ?string
202    {
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
204        $prefix = self::normalizePrefix($prefix);
205
206        if ($prefix === '') {
 
210        if ($path === $prefix) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
219
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
221            return null;
201    private function pathWithoutPrefix(string $url, string $prefix): ?string
202    {
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
204        $prefix = self::normalizePrefix($prefix);
205
206        if ($prefix === '') {
 
210        if ($path === $prefix) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
219
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
224        return $path;
225    }
201    private function pathWithoutPrefix(string $url, string $prefix): ?string
202    {
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
204        $prefix = self::normalizePrefix($prefix);
205
206        if ($prefix === '') {
 
210        if ($path === $prefix) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
219
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
221            return null;
201    private function pathWithoutPrefix(string $url, string $prefix): ?string
202    {
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
204        $prefix = self::normalizePrefix($prefix);
205
206        if ($prefix === '') {
 
210        if ($path === $prefix) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
219
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
224        return $path;
225    }
201    private function pathWithoutPrefix(string $url, string $prefix): ?string
202    {
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
204        $prefix = self::normalizePrefix($prefix);
205
206        if ($prefix === '') {
 
210        if ($path === $prefix) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
219
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
221            return null;
201    private function pathWithoutPrefix(string $url, string $prefix): ?string
202    {
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
 
203        $path = $url === '' ? '/' : $url;
204        $prefix = self::normalizePrefix($prefix);
205
206        if ($prefix === '') {
 
210        if ($path === $prefix) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
214        if (!str_starts_with($path, $prefix . '/')) {
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
 
218        $path = substr($path, strlen($prefix));
219
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
220        if ($path === '/' && $this->routePattern()->pattern() === '/') {
 
224        return $path;
225    }
Route->pattern
181        return $this->pattern;
182    }
Route->post
77    public static function post(string $pattern, callable|array|string $view, string $name = ''): self
78    {
79        return new self($pattern, $view, $name)->withMethods('POST');
80    }
Route->prefix
134    public function prefix(string $pattern = '', string $name = ''): static
135    {
136        if ($pattern !== '') {
 
137            $this->pattern = $pattern . $this->pattern;
138            $this->routePattern = null;
139        }
140
141        if ($name !== '') {
 
141        if ($name !== '') {
 
142            $this->name = $name . $this->name;
143        }
144
145        return $this;
 
145        return $this;
146    }
134    public function prefix(string $pattern = '', string $name = ''): static
135    {
136        if ($pattern !== '') {
 
137            $this->pattern = $pattern . $this->pattern;
138            $this->routePattern = null;
139        }
140
141        if ($name !== '') {
 
141        if ($name !== '') {
 
145        return $this;
146    }
134    public function prefix(string $pattern = '', string $name = ''): static
135    {
136        if ($pattern !== '') {
 
141        if ($name !== '') {
 
142            $this->name = $name . $this->name;
143        }
144
145        return $this;
 
145        return $this;
146    }
134    public function prefix(string $pattern = '', string $name = ''): static
135    {
136        if ($pattern !== '') {
 
141        if ($name !== '') {
 
145        return $this;
146    }
Route->put
83    public static function put(string $pattern, callable|array|string $view, string $name = ''): self
84    {
85        return new self($pattern, $view, $name)->withMethods('PUT');
86    }
Route->routePattern
198        return $this->routePattern ??= new RoutePattern($this->pattern);
199    }
Route->url
168    public function url(array $params = []): string
169    {
170        return $this->routePattern()->generate($params);
171    }
Route->view
176        return $this->view;
177    }
Route->withMethods
118    private function withMethods(string ...$args): self
119    {
120        /** @var list<string> $methods */
121        $methods = array_map(static fn(string $method): string => strtoupper($method), $args);
122        $this->methods = [...($this->methods ?? []), ...$methods];
123
124        return $this;
125    }
{closure:/workspace/celemas/router/src/Route.php:121-121}
121        $methods = array_map(static fn(string $method): string => strtoupper($method), $args);