39 | */
40 | public function getOrigins() : array
41 | {
42 | return $this->origins;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/Debug/RoutingCollection.php:
--------------------------------------------------------------------------------
1 |
6 | *
7 | * For the full copyright and license information, please view the LICENSE
8 | * file that was distributed with this source code.
9 | */
10 | namespace Framework\Routing\Debug;
11 |
12 | use Framework\Debug\Collection;
13 |
14 | /**
15 | * Class RoutingCollection.
16 | *
17 | * @package routing
18 | */
19 | class RoutingCollection extends Collection
20 | {
21 | protected string $iconPath = __DIR__ . '/icons/routing.svg';
22 | }
23 |
--------------------------------------------------------------------------------
/src/Debug/RoutingCollector.php:
--------------------------------------------------------------------------------
1 |
6 | *
7 | * For the full copyright and license information, please view the LICENSE
8 | * file that was distributed with this source code.
9 | */
10 | namespace Framework\Routing\Debug;
11 |
12 | use Closure;
13 | use Framework\Debug\Collector;
14 | use Framework\Debug\Debugger;
15 | use Framework\Routing\RouteCollection;
16 | use Framework\Routing\Router;
17 |
18 | /**
19 | * Class RoutingCollector.
20 | *
21 | * @package routing
22 | */
23 | class RoutingCollector extends Collector
24 | {
25 | protected Router $router;
26 |
27 | public function setRouter(Router $router) : static
28 | {
29 | $this->router = $router;
30 | return $this;
31 | }
32 |
33 | public function getActivities() : array
34 | {
35 | $activities = [];
36 | $serveCount = 0;
37 | foreach ($this->getData() as $data) {
38 | if ($data['type'] === 'serve') {
39 | $serveCount++;
40 | $activities[] = [
41 | 'collector' => $this->getName(),
42 | 'class' => static::class,
43 | 'description' => 'Serve route collection ' . $serveCount,
44 | 'start' => $data['start'],
45 | 'end' => $data['end'],
46 | ];
47 | } elseif ($data['type'] === 'match') {
48 | $activities[] = [
49 | 'collector' => $this->getName(),
50 | 'class' => static::class,
51 | 'description' => 'Match route',
52 | 'start' => $data['start'],
53 | 'end' => $data['end'],
54 | ];
55 | } elseif ($data['type'] === 'run') {
56 | $activities[] = [
57 | 'collector' => $this->getName(),
58 | 'class' => static::class,
59 | 'description' => 'Run matched route',
60 | 'start' => $data['start'],
61 | 'end' => $data['end'],
62 | ];
63 | }
64 | }
65 | return $activities;
66 | }
67 |
68 | public function getContents() : string
69 | {
70 | if (!isset($this->router)) {
71 | return 'A Router instance has not been set on this collector.
';
72 | }
73 | \ob_start(); ?>
74 | Matched Route
75 | = $this->renderMatchedRoute() ?>
76 | Route Collections
77 | = $this->renderRouteCollections() ?>
78 | Router Infos
79 | Auto Methods: = $this->router->isAutoMethods() ? 'On' : 'Off' ?>
80 | Auto Options: = $this->router->isAutoOptions() ? 'On' : 'Off' ?>
81 |
82 | Default Route Action Method: = \htmlentities($this->router->getDefaultRouteActionMethod()) ?>
83 |
84 | router->defaultRouteNotFound; // @phpstan-ignore-line
86 | if ($notFound): ?>
87 | Default Route Not Found: =
88 | $notFound instanceof Closure ? 'Closure' : \htmlentities($notFound)
89 | ?>
90 |
92 | Placeholders
93 | router->getPlaceholders() as $placeholder => $pattern) {
96 | $placeholders[\trim($placeholder, '{}')] = $pattern;
97 | }
98 | \ksort($placeholders); ?>
99 | Total of = \count($placeholders) ?> placeholders.
100 |
101 |
102 |
103 | Placeholder |
104 | Pattern |
105 |
106 |
107 |
108 | $pattern): ?>
109 |
110 | {= \htmlentities($placeholder) ?>} |
111 |
112 | = \htmlentities($pattern) ?>
113 | |
114 |
115 |
116 |
117 |
118 | router->getMatchedRoute();
125 | if ($route === null) {
126 | return 'No matching route on this Router instance.
';
127 | }
128 | \ob_start(); ?>
129 |
130 |
131 |
132 | RC |
133 | Method |
134 | Origin |
135 | Path |
136 | Action |
137 | Name |
138 | Has Options |
139 | Time to Match |
140 | Runtime |
141 |
142 |
143 |
144 |
145 | router->getCollections() as $index => $collection) {
147 | if ($collection === $this->router->getMatchedCollection()) {
148 | echo $index + 1;
149 | }
150 | } ?> |
151 | = $this->router->getResponse()->getRequest()->getMethod() ?> |
152 |
153 | = \htmlentities($this->router->getMatchedOrigin()) ?> |
154 | = \htmlentities($this->router->getMatchedPath()) ?> |
155 | = $route->getAction() instanceof Closure
156 | ? 'Closure'
157 | : \htmlentities($route->getAction()) ?> |
158 | = \htmlentities((string) $route->getName()) ?> |
159 | = $route->getOptions() ? 'Yes' : 'No' ?> |
160 | getData() as $data) {
162 | if ($data['type'] === 'match') {
163 | echo Debugger::roundSecondsToMilliseconds($data['end'] - $data['start']);
164 | }
165 | } ?> |
166 | getData() as $data) {
168 | if ($data['type'] === 'run') {
169 | echo Debugger::roundSecondsToMilliseconds($data['end'] - $data['start']);
170 | }
171 | } ?> |
172 |
173 |
174 |
175 | router->getCollections());
182 | if ($countCollections === 0) {
183 | return 'No route collection has been set.
';
184 | }
185 | $plural = $countCollections > 1;
186 | \ob_start(); ?>
187 | There = $plural ? 'are' : 'is' ?> = $countCollections ?> route collection=
188 | $plural ? 's' : '' ?> set.
189 |
190 | router->getCollections() as $index => $collection): ?>
192 | Route Collection = $index + 1 ?>
193 | Origin: = $this->toCodeBrackets($collection->origin) ?>
194 | name !== null): ?>
196 | Name: = $collection->name ?>
197 | notFoundAction ?? null;
200 | if ($notFound !== null):
201 | ?>
202 | Route Not Found: = $notFound instanceof Closure
203 | ? 'Closure'
204 | : \htmlentities($notFound) ?>
205 | renderRouteCollectionsTable($collection);
208 | endforeach;
209 | return \ob_get_clean(); // @phpstan-ignore-line
210 | }
211 |
212 | protected function renderRouteCollectionTime(RouteCollection $collection) : string
213 | {
214 | $contents = '';
215 | foreach ($this->getData() as $data) {
216 | if ($data['type'] === 'serve' && $data['collectionId'] === \spl_object_id($collection)) {
217 | $contents = 'Time to Serve: '
218 | . Debugger::roundSecondsToMilliseconds($data['end'] - $data['start'])
219 | . ' ms
';
220 | break;
221 | }
222 | }
223 | return $contents;
224 | }
225 |
226 | protected function renderRouteCollectionsTable(RouteCollection $collection) : string
227 | {
228 | $routesCount = \count($collection);
229 | \ob_start();
230 | echo 'Routes Count: ' . $routesCount . '
';
231 | echo $this->renderRouteCollectionTime($collection);
232 | if ($routesCount === 0) {
233 | echo 'No route has been set in this collection.
';
234 | return \ob_get_clean(); // @phpstan-ignore-line
235 | }
236 | // @phpstan-ignore-next-line
237 | if ($routesCount === 1 && $collection->router->getMatchedOrigin() && $collection->getRouteNotFound()) {
238 | echo 'Only Route Not Found has been set in this collection.
';
239 | return \ob_get_clean(); // @phpstan-ignore-line
240 | } ?>
241 |
242 |
243 |
244 | # |
245 | Method |
246 | Path |
247 | Action |
248 | Name |
249 | Has Options |
250 |
251 |
252 |
253 | getRoutes($collection) as $index => $route): ?>
254 | >
255 | = ++$index ?> |
256 | = \htmlentities($route['method']) ?> |
257 | = $this->toCodeBrackets(\htmlentities($route['path'])) ?> |
258 | = \htmlentities($route['action']) ?> |
259 | = \htmlentities((string) $route['name']) ?> |
260 | = \htmlentities($route['hasOptions']) ?> |
261 |
262 |
263 |
264 |
265 | >
273 | */
274 | protected function getRoutes(RouteCollection $collection) : array
275 | {
276 | $result = [];
277 | $collectionRoutes = $collection->routes;
278 | \ksort($collectionRoutes);
279 | foreach ($collectionRoutes as $method => $routes) {
280 | foreach ($routes as $route) {
281 | $result[] = [
282 | 'method' => $method,
283 | 'path' => $route->getPath(),
284 | 'action' => \is_string($route->getAction()) ? $route->getAction() : 'Closure',
285 | 'name' => $route->getName(),
286 | 'hasOptions' => $route->getOptions() ? 'Yes' : 'No',
287 | 'matched' => $route === $this->router->getMatchedRoute(),
288 | ];
289 | }
290 | }
291 | return $result;
292 | }
293 |
294 | protected function toCodeBrackets(string $str) : string
295 | {
296 | return \strtr($str, [
297 | '{' => '{',
298 | '}' => '}
',
299 | ]);
300 | }
301 | }
302 |
--------------------------------------------------------------------------------
/src/Debug/icons/routing.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/Languages/en/routing.php:
--------------------------------------------------------------------------------
1 |
6 | *
7 | * For the full copyright and license information, please view the LICENSE
8 | * file that was distributed with this source code.
9 | */
10 | return [
11 | 'error404' => 'Error 404',
12 | 'pageNotFound' => 'Page not found',
13 | ];
14 |
--------------------------------------------------------------------------------
/src/Languages/es/routing.php:
--------------------------------------------------------------------------------
1 |
6 | *
7 | * For the full copyright and license information, please view the LICENSE
8 | * file that was distributed with this source code.
9 | */
10 | return [
11 | 'error404' => 'Error 404',
12 | 'pageNotFound' => 'Página no encontrada',
13 | ];
14 |
--------------------------------------------------------------------------------
/src/Languages/pt-br/routing.php:
--------------------------------------------------------------------------------
1 |
6 | *
7 | * For the full copyright and license information, please view the LICENSE
8 | * file that was distributed with this source code.
9 | */
10 | return [
11 | 'error404' => 'Erro 404',
12 | 'pageNotFound' => 'Página não encontrada',
13 | ];
14 |
--------------------------------------------------------------------------------
/src/PresenterInterface.php:
--------------------------------------------------------------------------------
1 |
6 | *
7 | * For the full copyright and license information, please view the LICENSE
8 | * file that was distributed with this source code.
9 | */
10 | namespace Framework\Routing;
11 |
12 | use Framework\HTTP\Method;
13 | use Framework\HTTP\Response;
14 | use Framework\HTTP\Status;
15 |
16 | /**
17 | * Interface PresenterInterface.
18 | *
19 | * The interface for data management via a Web Browser UI
20 | * using the HTTP GET and POST methods.
21 | *
22 | * Note: If a presenter needs more than one parameter to get URL path information
23 | * provided by placeholders, in addition to $id, do not implement this interface.
24 | * But this interface can be a reference because its method names are used in
25 | * {@see RouteCollection::presenter()}.
26 | *
27 | * @see https://developer.mozilla.org/en-US/docs/Glossary/UI
28 | *
29 | * @package routing
30 | */
31 | interface PresenterInterface
32 | {
33 | /**
34 | * Handles a GET request for /.
35 | *
36 | * Common usage: Show a list of paginated items.
37 | *
38 | * @see Method::GET
39 | *
40 | * @return mixed
41 | */
42 | public function index() : mixed;
43 |
44 | /**
45 | * Handles a GET request for /new.
46 | *
47 | * Common usage: Show a form with inputs to create a new item.
48 | * The POST action must go to the 'create' method URL.
49 | *
50 | * @see PresenterInterface::create()
51 | * @see Method::GET
52 | *
53 | * @return mixed
54 | */
55 | public function new() : mixed;
56 |
57 | /**
58 | * Handles a POST request for /.
59 | *
60 | * Common usage: Try to create a new item. On success, redirect to the 'show' or
61 | * 'edit' method URL. On fail, back to the 'new' method URL.
62 | *
63 | * @see PresenterInterface::edit()
64 | * @see PresenterInterface::new()
65 | * @see PresenterInterface::show()
66 | * @see Method::POST
67 | * @see Response::redirect()
68 | *
69 | * @return mixed
70 | */
71 | public function create() : mixed;
72 |
73 | /**
74 | * Handles a GET request for /$id.
75 | *
76 | * Common usage: Show a specific item based on the $id.
77 | *
78 | * @param string $id
79 | *
80 | * @see Method::GET
81 | * @see Status::NOT_FOUND
82 | *
83 | * @return mixed
84 | */
85 | public function show(string $id) : mixed;
86 |
87 | /**
88 | * Handles a GET request for /$id/edit.
89 | *
90 | * Common usage: Show a form to edit a specific item based on the $id.
91 | * The POST action must go to the 'update' method URL.
92 | *
93 | * @param string $id
94 | *
95 | * @see PresenterInterface::update()
96 | * @see Method::GET
97 | *
98 | * @return mixed
99 | */
100 | public function edit(string $id) : mixed;
101 |
102 | /**
103 | * Handles a POST request for /$id/update.
104 | *
105 | * Common usage: Try to update an item based on the $id. After the process, back
106 | * to the 'edit' method URL and show a message.
107 | *
108 | * @param string $id
109 | *
110 | * @see PresenterInterface::edit()
111 | * @see Method::POST
112 | * @see Response::redirect()
113 | *
114 | * @return mixed
115 | */
116 | public function update(string $id) : mixed;
117 |
118 | /**
119 | * Handles a GET request for /$id/remove.
120 | *
121 | * Common usage: Show an alert message about the item to be deleted based on the
122 | * $id. The confirmation action must call a POST request to the 'delete'
123 | * method URL.
124 | *
125 | * @param string $id
126 | *
127 | * @see PresenterInterface::delete()
128 | * @see Method::GET
129 | *
130 | * @return mixed
131 | */
132 | public function remove(string $id) : mixed;
133 |
134 | /**
135 | * Handles a POST request for /$id/delete.
136 | *
137 | * Common usage: Try to delete an item based on the $id. On success, go to the
138 | * 'index' method URL and show a success message. On fail, back to the 'remove'
139 | * method URL and show the error message.
140 | *
141 | * @param string $id
142 | *
143 | * @see PresenterInterface::index()
144 | * @see PresenterInterface::remove()
145 | * @see Method::POST
146 | * @see Response::redirect()
147 | *
148 | * @return mixed
149 | */
150 | public function delete(string $id) : mixed;
151 | }
152 |
--------------------------------------------------------------------------------
/src/Reflector.php:
--------------------------------------------------------------------------------
1 |
6 | *
7 | * For the full copyright and license information, please view the LICENSE
8 | * file that was distributed with this source code.
9 | */
10 | namespace Framework\Routing;
11 |
12 | use Framework\Routing\Attributes\Origin;
13 | use Framework\Routing\Attributes\Route;
14 | use Framework\Routing\Attributes\RouteNotFound;
15 | use ReflectionClass;
16 | use ReflectionException;
17 |
18 | /**
19 | * Class Reflector.
20 | *
21 | * @package routing
22 | */
23 | class Reflector
24 | {
25 | /**
26 | * @var ReflectionClass