├── .gitignore
├── .travis.yml
├── LICENSE
├── composer.json
├── composer.lock
├── phpunit.xml.dist
├── readme.md
├── src
└── Routing
│ ├── Dispatcher
│ ├── ClosureDispatcher.php
│ ├── ControllerDispatcher.php
│ ├── DispatcherInterface.php
│ └── TemplateDispatcher.php
│ ├── Matcher
│ ├── ArrayMatcher.php
│ ├── MatcherInterface.php
│ └── UriMatcher.php
│ ├── Middleware.php
│ ├── MiddlewareInterface.php
│ ├── Response.php
│ ├── ResponseInterface.php
│ ├── Route.php
│ ├── RouteCollection.php
│ └── Router.php
└── tests
├── app
├── Block1
│ ├── Namespace1Controller.php
│ ├── Normal1Controller.php
│ ├── Views
│ │ ├── Smart
│ │ │ └── index1.html
│ │ ├── contact.php
│ │ ├── index.html
│ │ ├── log.php
│ │ └── user.html
│ └── routes.php
├── Block2
│ ├── Controllers
│ │ ├── Namespace2Controller.php
│ │ └── Normal2Controller.php
│ ├── Smart
│ │ └── index2.html
│ ├── index.html
│ ├── log.php
│ ├── routes.php
│ └── user.html
├── Config
│ ├── middleware.inc.php
│ └── routes.php
├── Controllers
│ ├── AppController.php
│ ├── NamespaceController.php
│ └── NormalController.php
└── Views
│ ├── Smart
│ └── index.html
│ ├── index.html
│ └── user.html
├── bootstrap.php
└── src
└── Routing
├── MiddlewareTest.php
├── RouteCollectionTest.php
└── RouterTest.php
/.gitignore:
--------------------------------------------------------------------------------
1 | /vendor
2 | /Block1
3 | Autoload.php
4 | .htaccess
5 | /.git
6 | index.php
7 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 | php:
3 | - "7.1"
4 | - "7.0"
5 | - "5.6"
6 |
7 | before_script:
8 | - composer self-update
9 | - composer install
10 |
11 | script:
12 | - vendor/bin/phpunit -c phpunit.xml.dist
13 |
14 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 jetfirephp
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "jetfirephp/routing",
3 | "type": "library",
4 | "description": "JetFire - Routing",
5 | "keywords": ["Rounting", "Dispatcher", "Micro MVC"],
6 | "license": "MIT",
7 | "authors": [
8 | {
9 | "name": "Sumugan Sinnarasa",
10 | "email": "mugan27@gmail.com",
11 | "role": "Developer"
12 | }
13 | ],
14 | "require": {
15 | "php": ">=5.6.0"
16 | },
17 | "require-dev": {
18 | "phpunit/phpunit": "5.7.*",
19 | "symfony/yaml": "v2.8.2"
20 | },
21 | "suggest": {
22 | "jetfire/autoload": "For loading your class"
23 | },
24 | "autoload": {
25 | "psr-4":{
26 | "JetFire\\Routing\\": "src/Routing/"
27 | }
28 | },
29 | "autoload-dev": {
30 | "psr-4": {
31 | "JetFire\\Routing\\Test\\": "tests/src/Routing/",
32 | "JetFire\\Routing\\App\\": "tests/app/"
33 | }
34 | },
35 | "minimum-stability": "dev"
36 | }
37 |
--------------------------------------------------------------------------------
/composer.lock:
--------------------------------------------------------------------------------
1 | {
2 | "_readme": [
3 | "This file locks the dependencies of your project to a known state",
4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
5 | "This file is @generated automatically"
6 | ],
7 | "hash": "458848d0bc194d11df02bfede2b4c79b",
8 | "content-hash": "154fed92e2d6c624e53ea1027c8d88ba",
9 | "packages": [],
10 | "packages-dev": [
11 | {
12 | "name": "doctrine/instantiator",
13 | "version": "dev-master",
14 | "source": {
15 | "type": "git",
16 | "url": "https://github.com/doctrine/instantiator.git",
17 | "reference": "5acd2bd8c2b600ad5cc4c9180ebf0a930604d6a5"
18 | },
19 | "dist": {
20 | "type": "zip",
21 | "url": "https://api.github.com/repos/doctrine/instantiator/zipball/5acd2bd8c2b600ad5cc4c9180ebf0a930604d6a5",
22 | "reference": "5acd2bd8c2b600ad5cc4c9180ebf0a930604d6a5",
23 | "shasum": ""
24 | },
25 | "require": {
26 | "php": ">=5.3,<8.0-DEV"
27 | },
28 | "require-dev": {
29 | "athletic/athletic": "~0.1.8",
30 | "ext-pdo": "*",
31 | "ext-phar": "*",
32 | "phpunit/phpunit": "~4.0",
33 | "squizlabs/php_codesniffer": "~2.0"
34 | },
35 | "type": "library",
36 | "extra": {
37 | "branch-alias": {
38 | "dev-master": "1.0.x-dev"
39 | }
40 | },
41 | "autoload": {
42 | "psr-4": {
43 | "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/"
44 | }
45 | },
46 | "notification-url": "https://packagist.org/downloads/",
47 | "license": [
48 | "MIT"
49 | ],
50 | "authors": [
51 | {
52 | "name": "Marco Pivetta",
53 | "email": "ocramius@gmail.com",
54 | "homepage": "http://ocramius.github.com/"
55 | }
56 | ],
57 | "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors",
58 | "homepage": "https://github.com/doctrine/instantiator",
59 | "keywords": [
60 | "constructor",
61 | "instantiate"
62 | ],
63 | "time": "2017-02-16 16:15:51"
64 | },
65 | {
66 | "name": "myclabs/deep-copy",
67 | "version": "1.x-dev",
68 | "source": {
69 | "type": "git",
70 | "url": "https://github.com/myclabs/DeepCopy.git",
71 | "reference": "8e6e04167378abf1ddb4d3522d8755c5fd90d102"
72 | },
73 | "dist": {
74 | "type": "zip",
75 | "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/8e6e04167378abf1ddb4d3522d8755c5fd90d102",
76 | "reference": "8e6e04167378abf1ddb4d3522d8755c5fd90d102",
77 | "shasum": ""
78 | },
79 | "require": {
80 | "php": ">=5.4.0"
81 | },
82 | "require-dev": {
83 | "doctrine/collections": "1.*",
84 | "phpunit/phpunit": "~4.1"
85 | },
86 | "type": "library",
87 | "autoload": {
88 | "psr-4": {
89 | "DeepCopy\\": "src/DeepCopy/"
90 | }
91 | },
92 | "notification-url": "https://packagist.org/downloads/",
93 | "license": [
94 | "MIT"
95 | ],
96 | "description": "Create deep copies (clones) of your objects",
97 | "homepage": "https://github.com/myclabs/DeepCopy",
98 | "keywords": [
99 | "clone",
100 | "copy",
101 | "duplicate",
102 | "object",
103 | "object graph"
104 | ],
105 | "time": "2017-04-12 18:52:22"
106 | },
107 | {
108 | "name": "phpdocumentor/reflection-common",
109 | "version": "dev-master",
110 | "source": {
111 | "type": "git",
112 | "url": "https://github.com/phpDocumentor/ReflectionCommon.git",
113 | "reference": "a046af61c36e9162372f205de091a1cab7340f1c"
114 | },
115 | "dist": {
116 | "type": "zip",
117 | "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/a046af61c36e9162372f205de091a1cab7340f1c",
118 | "reference": "a046af61c36e9162372f205de091a1cab7340f1c",
119 | "shasum": ""
120 | },
121 | "require": {
122 | "php": ">=5.5"
123 | },
124 | "require-dev": {
125 | "phpunit/phpunit": "^4.6"
126 | },
127 | "type": "library",
128 | "extra": {
129 | "branch-alias": {
130 | "dev-master": "1.0.x-dev"
131 | }
132 | },
133 | "autoload": {
134 | "psr-4": {
135 | "phpDocumentor\\Reflection\\": [
136 | "src"
137 | ]
138 | }
139 | },
140 | "notification-url": "https://packagist.org/downloads/",
141 | "license": [
142 | "MIT"
143 | ],
144 | "authors": [
145 | {
146 | "name": "Jaap van Otterdijk",
147 | "email": "opensource@ijaap.nl"
148 | }
149 | ],
150 | "description": "Common reflection classes used by phpdocumentor to reflect the code structure",
151 | "homepage": "http://www.phpdoc.org",
152 | "keywords": [
153 | "FQSEN",
154 | "phpDocumentor",
155 | "phpdoc",
156 | "reflection",
157 | "static analysis"
158 | ],
159 | "time": "2017-04-30 11:58:12"
160 | },
161 | {
162 | "name": "phpdocumentor/reflection-docblock",
163 | "version": "3.2.0",
164 | "source": {
165 | "type": "git",
166 | "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
167 | "reference": "46f7e8bb075036c92695b15a1ddb6971c751e585"
168 | },
169 | "dist": {
170 | "type": "zip",
171 | "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/46f7e8bb075036c92695b15a1ddb6971c751e585",
172 | "reference": "46f7e8bb075036c92695b15a1ddb6971c751e585",
173 | "shasum": ""
174 | },
175 | "require": {
176 | "php": ">=5.5",
177 | "phpdocumentor/reflection-common": "^1.0@dev",
178 | "phpdocumentor/type-resolver": "^0.4.0",
179 | "webmozart/assert": "^1.0"
180 | },
181 | "require-dev": {
182 | "mockery/mockery": "^0.9.4",
183 | "phpunit/phpunit": "^4.4"
184 | },
185 | "type": "library",
186 | "autoload": {
187 | "psr-4": {
188 | "phpDocumentor\\Reflection\\": [
189 | "src/"
190 | ]
191 | }
192 | },
193 | "notification-url": "https://packagist.org/downloads/",
194 | "license": [
195 | "MIT"
196 | ],
197 | "authors": [
198 | {
199 | "name": "Mike van Riel",
200 | "email": "me@mikevanriel.com"
201 | }
202 | ],
203 | "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
204 | "time": "2017-07-15 11:38:20"
205 | },
206 | {
207 | "name": "phpdocumentor/type-resolver",
208 | "version": "0.4.0",
209 | "source": {
210 | "type": "git",
211 | "url": "https://github.com/phpDocumentor/TypeResolver.git",
212 | "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7"
213 | },
214 | "dist": {
215 | "type": "zip",
216 | "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7",
217 | "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7",
218 | "shasum": ""
219 | },
220 | "require": {
221 | "php": "^5.5 || ^7.0",
222 | "phpdocumentor/reflection-common": "^1.0"
223 | },
224 | "require-dev": {
225 | "mockery/mockery": "^0.9.4",
226 | "phpunit/phpunit": "^5.2||^4.8.24"
227 | },
228 | "type": "library",
229 | "extra": {
230 | "branch-alias": {
231 | "dev-master": "1.0.x-dev"
232 | }
233 | },
234 | "autoload": {
235 | "psr-4": {
236 | "phpDocumentor\\Reflection\\": [
237 | "src/"
238 | ]
239 | }
240 | },
241 | "notification-url": "https://packagist.org/downloads/",
242 | "license": [
243 | "MIT"
244 | ],
245 | "authors": [
246 | {
247 | "name": "Mike van Riel",
248 | "email": "me@mikevanriel.com"
249 | }
250 | ],
251 | "time": "2017-07-14 14:27:02"
252 | },
253 | {
254 | "name": "phpspec/prophecy",
255 | "version": "dev-master",
256 | "source": {
257 | "type": "git",
258 | "url": "https://github.com/phpspec/prophecy.git",
259 | "reference": "79db5dd907ee977039f77ac8471a739497463429"
260 | },
261 | "dist": {
262 | "type": "zip",
263 | "url": "https://api.github.com/repos/phpspec/prophecy/zipball/79db5dd907ee977039f77ac8471a739497463429",
264 | "reference": "79db5dd907ee977039f77ac8471a739497463429",
265 | "shasum": ""
266 | },
267 | "require": {
268 | "doctrine/instantiator": "^1.0.2",
269 | "php": "^5.3|^7.0",
270 | "phpdocumentor/reflection-docblock": "^2.0|^3.0.2",
271 | "sebastian/comparator": "^1.1|^2.0",
272 | "sebastian/recursion-context": "^1.0|^2.0|^3.0"
273 | },
274 | "require-dev": {
275 | "phpspec/phpspec": "^2.5|^3.2",
276 | "phpunit/phpunit": "^4.8 || ^5.6.5"
277 | },
278 | "type": "library",
279 | "extra": {
280 | "branch-alias": {
281 | "dev-master": "1.7.x-dev"
282 | }
283 | },
284 | "autoload": {
285 | "psr-0": {
286 | "Prophecy\\": "src/"
287 | }
288 | },
289 | "notification-url": "https://packagist.org/downloads/",
290 | "license": [
291 | "MIT"
292 | ],
293 | "authors": [
294 | {
295 | "name": "Konstantin Kudryashov",
296 | "email": "ever.zet@gmail.com",
297 | "homepage": "http://everzet.com"
298 | },
299 | {
300 | "name": "Marcello Duarte",
301 | "email": "marcello.duarte@gmail.com"
302 | }
303 | ],
304 | "description": "Highly opinionated mocking framework for PHP 5.3+",
305 | "homepage": "https://github.com/phpspec/prophecy",
306 | "keywords": [
307 | "Double",
308 | "Dummy",
309 | "fake",
310 | "mock",
311 | "spy",
312 | "stub"
313 | ],
314 | "time": "2017-05-31 16:22:32"
315 | },
316 | {
317 | "name": "phpunit/php-code-coverage",
318 | "version": "4.0.x-dev",
319 | "source": {
320 | "type": "git",
321 | "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
322 | "reference": "ef7b2f56815df854e66ceaee8ebe9393ae36a40d"
323 | },
324 | "dist": {
325 | "type": "zip",
326 | "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/ef7b2f56815df854e66ceaee8ebe9393ae36a40d",
327 | "reference": "ef7b2f56815df854e66ceaee8ebe9393ae36a40d",
328 | "shasum": ""
329 | },
330 | "require": {
331 | "ext-dom": "*",
332 | "ext-xmlwriter": "*",
333 | "php": "^5.6 || ^7.0",
334 | "phpunit/php-file-iterator": "^1.3",
335 | "phpunit/php-text-template": "^1.2",
336 | "phpunit/php-token-stream": "^1.4.2 || ^2.0",
337 | "sebastian/code-unit-reverse-lookup": "^1.0",
338 | "sebastian/environment": "^1.3.2 || ^2.0",
339 | "sebastian/version": "^1.0 || ^2.0"
340 | },
341 | "require-dev": {
342 | "ext-xdebug": "^2.1.4",
343 | "phpunit/phpunit": "^5.7"
344 | },
345 | "suggest": {
346 | "ext-xdebug": "^2.5.1"
347 | },
348 | "type": "library",
349 | "extra": {
350 | "branch-alias": {
351 | "dev-master": "4.0.x-dev"
352 | }
353 | },
354 | "autoload": {
355 | "classmap": [
356 | "src/"
357 | ]
358 | },
359 | "notification-url": "https://packagist.org/downloads/",
360 | "license": [
361 | "BSD-3-Clause"
362 | ],
363 | "authors": [
364 | {
365 | "name": "Sebastian Bergmann",
366 | "email": "sb@sebastian-bergmann.de",
367 | "role": "lead"
368 | }
369 | ],
370 | "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
371 | "homepage": "https://github.com/sebastianbergmann/php-code-coverage",
372 | "keywords": [
373 | "coverage",
374 | "testing",
375 | "xunit"
376 | ],
377 | "time": "2017-04-02 07:44:40"
378 | },
379 | {
380 | "name": "phpunit/php-file-iterator",
381 | "version": "dev-master",
382 | "source": {
383 | "type": "git",
384 | "url": "https://github.com/sebastianbergmann/php-file-iterator.git",
385 | "reference": "3cc8f69b3028d0f96a9078e6295d86e9bf019be5"
386 | },
387 | "dist": {
388 | "type": "zip",
389 | "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/3cc8f69b3028d0f96a9078e6295d86e9bf019be5",
390 | "reference": "3cc8f69b3028d0f96a9078e6295d86e9bf019be5",
391 | "shasum": ""
392 | },
393 | "require": {
394 | "php": ">=5.3.3"
395 | },
396 | "type": "library",
397 | "extra": {
398 | "branch-alias": {
399 | "dev-master": "1.4.x-dev"
400 | }
401 | },
402 | "autoload": {
403 | "classmap": [
404 | "src/"
405 | ]
406 | },
407 | "notification-url": "https://packagist.org/downloads/",
408 | "license": [
409 | "BSD-3-Clause"
410 | ],
411 | "authors": [
412 | {
413 | "name": "Sebastian Bergmann",
414 | "email": "sb@sebastian-bergmann.de",
415 | "role": "lead"
416 | }
417 | ],
418 | "description": "FilterIterator implementation that filters files based on a list of suffixes.",
419 | "homepage": "https://github.com/sebastianbergmann/php-file-iterator/",
420 | "keywords": [
421 | "filesystem",
422 | "iterator"
423 | ],
424 | "time": "2016-10-03 07:40:28"
425 | },
426 | {
427 | "name": "phpunit/php-text-template",
428 | "version": "1.2.1",
429 | "source": {
430 | "type": "git",
431 | "url": "https://github.com/sebastianbergmann/php-text-template.git",
432 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686"
433 | },
434 | "dist": {
435 | "type": "zip",
436 | "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686",
437 | "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686",
438 | "shasum": ""
439 | },
440 | "require": {
441 | "php": ">=5.3.3"
442 | },
443 | "type": "library",
444 | "autoload": {
445 | "classmap": [
446 | "src/"
447 | ]
448 | },
449 | "notification-url": "https://packagist.org/downloads/",
450 | "license": [
451 | "BSD-3-Clause"
452 | ],
453 | "authors": [
454 | {
455 | "name": "Sebastian Bergmann",
456 | "email": "sebastian@phpunit.de",
457 | "role": "lead"
458 | }
459 | ],
460 | "description": "Simple template engine.",
461 | "homepage": "https://github.com/sebastianbergmann/php-text-template/",
462 | "keywords": [
463 | "template"
464 | ],
465 | "time": "2015-06-21 13:50:34"
466 | },
467 | {
468 | "name": "phpunit/php-timer",
469 | "version": "dev-master",
470 | "source": {
471 | "type": "git",
472 | "url": "https://github.com/sebastianbergmann/php-timer.git",
473 | "reference": "d107f347d368dd8a384601398280c7c608390ab7"
474 | },
475 | "dist": {
476 | "type": "zip",
477 | "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/d107f347d368dd8a384601398280c7c608390ab7",
478 | "reference": "d107f347d368dd8a384601398280c7c608390ab7",
479 | "shasum": ""
480 | },
481 | "require": {
482 | "php": "^5.3.3 || ^7.0"
483 | },
484 | "require-dev": {
485 | "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0"
486 | },
487 | "type": "library",
488 | "extra": {
489 | "branch-alias": {
490 | "dev-master": "1.0-dev"
491 | }
492 | },
493 | "autoload": {
494 | "classmap": [
495 | "src/"
496 | ]
497 | },
498 | "notification-url": "https://packagist.org/downloads/",
499 | "license": [
500 | "BSD-3-Clause"
501 | ],
502 | "authors": [
503 | {
504 | "name": "Sebastian Bergmann",
505 | "email": "sb@sebastian-bergmann.de",
506 | "role": "lead"
507 | }
508 | ],
509 | "description": "Utility class for timing",
510 | "homepage": "https://github.com/sebastianbergmann/php-timer/",
511 | "keywords": [
512 | "timer"
513 | ],
514 | "time": "2017-03-07 15:42:04"
515 | },
516 | {
517 | "name": "phpunit/php-token-stream",
518 | "version": "dev-master",
519 | "source": {
520 | "type": "git",
521 | "url": "https://github.com/sebastianbergmann/php-token-stream.git",
522 | "reference": "9ddb181faa4a3841fe131c357fd01de75cbb4da9"
523 | },
524 | "dist": {
525 | "type": "zip",
526 | "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/9ddb181faa4a3841fe131c357fd01de75cbb4da9",
527 | "reference": "9ddb181faa4a3841fe131c357fd01de75cbb4da9",
528 | "shasum": ""
529 | },
530 | "require": {
531 | "ext-tokenizer": "*",
532 | "php": "^5.6 || ^7.0"
533 | },
534 | "require-dev": {
535 | "phpunit/phpunit": "^5.7 || ^6.0"
536 | },
537 | "type": "library",
538 | "extra": {
539 | "branch-alias": {
540 | "dev-master": "2.0-dev"
541 | }
542 | },
543 | "autoload": {
544 | "classmap": [
545 | "src/"
546 | ]
547 | },
548 | "notification-url": "https://packagist.org/downloads/",
549 | "license": [
550 | "BSD-3-Clause"
551 | ],
552 | "authors": [
553 | {
554 | "name": "Sebastian Bergmann",
555 | "email": "sebastian@phpunit.de"
556 | }
557 | ],
558 | "description": "Wrapper around PHP's tokenizer extension.",
559 | "homepage": "https://github.com/sebastianbergmann/php-token-stream/",
560 | "keywords": [
561 | "tokenizer"
562 | ],
563 | "time": "2017-03-07 07:36:57"
564 | },
565 | {
566 | "name": "phpunit/phpunit",
567 | "version": "5.7.x-dev",
568 | "source": {
569 | "type": "git",
570 | "url": "https://github.com/sebastianbergmann/phpunit.git",
571 | "reference": "c0c61c97be37e1c77ab61ad73448e9208c05aade"
572 | },
573 | "dist": {
574 | "type": "zip",
575 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c0c61c97be37e1c77ab61ad73448e9208c05aade",
576 | "reference": "c0c61c97be37e1c77ab61ad73448e9208c05aade",
577 | "shasum": ""
578 | },
579 | "require": {
580 | "ext-dom": "*",
581 | "ext-json": "*",
582 | "ext-libxml": "*",
583 | "ext-mbstring": "*",
584 | "ext-xml": "*",
585 | "myclabs/deep-copy": "~1.3",
586 | "php": "^5.6 || ^7.0",
587 | "phpspec/prophecy": "^1.6.2",
588 | "phpunit/php-code-coverage": "^4.0.4",
589 | "phpunit/php-file-iterator": "~1.4",
590 | "phpunit/php-text-template": "~1.2",
591 | "phpunit/php-timer": "^1.0.6",
592 | "phpunit/phpunit-mock-objects": "^3.2",
593 | "sebastian/comparator": "^1.2.4",
594 | "sebastian/diff": "^1.4.3",
595 | "sebastian/environment": "^1.3.4 || ^2.0",
596 | "sebastian/exporter": "~2.0",
597 | "sebastian/global-state": "^1.1",
598 | "sebastian/object-enumerator": "~2.0",
599 | "sebastian/resource-operations": "~1.0",
600 | "sebastian/version": "~1.0.3|~2.0",
601 | "symfony/yaml": "~2.1|~3.0"
602 | },
603 | "conflict": {
604 | "phpdocumentor/reflection-docblock": "3.0.2"
605 | },
606 | "require-dev": {
607 | "ext-pdo": "*"
608 | },
609 | "suggest": {
610 | "ext-xdebug": "*",
611 | "phpunit/php-invoker": "~1.1"
612 | },
613 | "bin": [
614 | "phpunit"
615 | ],
616 | "type": "library",
617 | "extra": {
618 | "branch-alias": {
619 | "dev-master": "5.7.x-dev"
620 | }
621 | },
622 | "autoload": {
623 | "classmap": [
624 | "src/"
625 | ]
626 | },
627 | "notification-url": "https://packagist.org/downloads/",
628 | "license": [
629 | "BSD-3-Clause"
630 | ],
631 | "authors": [
632 | {
633 | "name": "Sebastian Bergmann",
634 | "email": "sebastian@phpunit.de",
635 | "role": "lead"
636 | }
637 | ],
638 | "description": "The PHP Unit Testing framework.",
639 | "homepage": "https://phpunit.de/",
640 | "keywords": [
641 | "phpunit",
642 | "testing",
643 | "xunit"
644 | ],
645 | "time": "2017-06-23 12:45:27"
646 | },
647 | {
648 | "name": "phpunit/phpunit-mock-objects",
649 | "version": "3.4.x-dev",
650 | "source": {
651 | "type": "git",
652 | "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git",
653 | "reference": "a23b761686d50a560cc56233b9ecf49597cc9118"
654 | },
655 | "dist": {
656 | "type": "zip",
657 | "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/a23b761686d50a560cc56233b9ecf49597cc9118",
658 | "reference": "a23b761686d50a560cc56233b9ecf49597cc9118",
659 | "shasum": ""
660 | },
661 | "require": {
662 | "doctrine/instantiator": "^1.0.2",
663 | "php": "^5.6 || ^7.0",
664 | "phpunit/php-text-template": "^1.2",
665 | "sebastian/exporter": "^1.2 || ^2.0"
666 | },
667 | "conflict": {
668 | "phpunit/phpunit": "<5.4.0"
669 | },
670 | "require-dev": {
671 | "phpunit/phpunit": "^5.4"
672 | },
673 | "suggest": {
674 | "ext-soap": "*"
675 | },
676 | "type": "library",
677 | "extra": {
678 | "branch-alias": {
679 | "dev-master": "3.2.x-dev"
680 | }
681 | },
682 | "autoload": {
683 | "classmap": [
684 | "src/"
685 | ]
686 | },
687 | "notification-url": "https://packagist.org/downloads/",
688 | "license": [
689 | "BSD-3-Clause"
690 | ],
691 | "authors": [
692 | {
693 | "name": "Sebastian Bergmann",
694 | "email": "sb@sebastian-bergmann.de",
695 | "role": "lead"
696 | }
697 | ],
698 | "description": "Mock Object library for PHPUnit",
699 | "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/",
700 | "keywords": [
701 | "mock",
702 | "xunit"
703 | ],
704 | "time": "2017-06-30 09:13:00"
705 | },
706 | {
707 | "name": "sebastian/code-unit-reverse-lookup",
708 | "version": "dev-master",
709 | "source": {
710 | "type": "git",
711 | "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git",
712 | "reference": "3488be0a7b346cd6e5361510ed07e88f9bea2e88"
713 | },
714 | "dist": {
715 | "type": "zip",
716 | "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/3488be0a7b346cd6e5361510ed07e88f9bea2e88",
717 | "reference": "3488be0a7b346cd6e5361510ed07e88f9bea2e88",
718 | "shasum": ""
719 | },
720 | "require": {
721 | "php": "^5.6 || ^7.0"
722 | },
723 | "require-dev": {
724 | "phpunit/phpunit": "^5.7 || ^6.0"
725 | },
726 | "type": "library",
727 | "extra": {
728 | "branch-alias": {
729 | "dev-master": "1.0.x-dev"
730 | }
731 | },
732 | "autoload": {
733 | "classmap": [
734 | "src/"
735 | ]
736 | },
737 | "notification-url": "https://packagist.org/downloads/",
738 | "license": [
739 | "BSD-3-Clause"
740 | ],
741 | "authors": [
742 | {
743 | "name": "Sebastian Bergmann",
744 | "email": "sebastian@phpunit.de"
745 | }
746 | ],
747 | "description": "Looks up which function or method a line of code belongs to",
748 | "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/",
749 | "time": "2017-03-04 10:23:55"
750 | },
751 | {
752 | "name": "sebastian/comparator",
753 | "version": "1.2.x-dev",
754 | "source": {
755 | "type": "git",
756 | "url": "https://github.com/sebastianbergmann/comparator.git",
757 | "reference": "18a5d97c25f408f48acaf6d1b9f4079314c5996a"
758 | },
759 | "dist": {
760 | "type": "zip",
761 | "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/18a5d97c25f408f48acaf6d1b9f4079314c5996a",
762 | "reference": "18a5d97c25f408f48acaf6d1b9f4079314c5996a",
763 | "shasum": ""
764 | },
765 | "require": {
766 | "php": ">=5.3.3",
767 | "sebastian/diff": "~1.2",
768 | "sebastian/exporter": "~1.2 || ~2.0"
769 | },
770 | "require-dev": {
771 | "phpunit/phpunit": "~4.4"
772 | },
773 | "type": "library",
774 | "extra": {
775 | "branch-alias": {
776 | "dev-master": "1.2.x-dev"
777 | }
778 | },
779 | "autoload": {
780 | "classmap": [
781 | "src/"
782 | ]
783 | },
784 | "notification-url": "https://packagist.org/downloads/",
785 | "license": [
786 | "BSD-3-Clause"
787 | ],
788 | "authors": [
789 | {
790 | "name": "Jeff Welch",
791 | "email": "whatthejeff@gmail.com"
792 | },
793 | {
794 | "name": "Volker Dusch",
795 | "email": "github@wallbash.com"
796 | },
797 | {
798 | "name": "Bernhard Schussek",
799 | "email": "bschussek@2bepublished.at"
800 | },
801 | {
802 | "name": "Sebastian Bergmann",
803 | "email": "sebastian@phpunit.de"
804 | }
805 | ],
806 | "description": "Provides the functionality to compare PHP values for equality",
807 | "homepage": "http://www.github.com/sebastianbergmann/comparator",
808 | "keywords": [
809 | "comparator",
810 | "compare",
811 | "equality"
812 | ],
813 | "time": "2017-03-07 10:34:43"
814 | },
815 | {
816 | "name": "sebastian/diff",
817 | "version": "1.4.x-dev",
818 | "source": {
819 | "type": "git",
820 | "url": "https://github.com/sebastianbergmann/diff.git",
821 | "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4"
822 | },
823 | "dist": {
824 | "type": "zip",
825 | "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7f066a26a962dbe58ddea9f72a4e82874a3975a4",
826 | "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4",
827 | "shasum": ""
828 | },
829 | "require": {
830 | "php": "^5.3.3 || ^7.0"
831 | },
832 | "require-dev": {
833 | "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0"
834 | },
835 | "type": "library",
836 | "extra": {
837 | "branch-alias": {
838 | "dev-master": "1.4-dev"
839 | }
840 | },
841 | "autoload": {
842 | "classmap": [
843 | "src/"
844 | ]
845 | },
846 | "notification-url": "https://packagist.org/downloads/",
847 | "license": [
848 | "BSD-3-Clause"
849 | ],
850 | "authors": [
851 | {
852 | "name": "Kore Nordmann",
853 | "email": "mail@kore-nordmann.de"
854 | },
855 | {
856 | "name": "Sebastian Bergmann",
857 | "email": "sebastian@phpunit.de"
858 | }
859 | ],
860 | "description": "Diff implementation",
861 | "homepage": "https://github.com/sebastianbergmann/diff",
862 | "keywords": [
863 | "diff"
864 | ],
865 | "time": "2017-05-22 07:24:03"
866 | },
867 | {
868 | "name": "sebastian/environment",
869 | "version": "2.0.x-dev",
870 | "source": {
871 | "type": "git",
872 | "url": "https://github.com/sebastianbergmann/environment.git",
873 | "reference": "5795ffe5dc5b02460c3e34222fee8cbe245d8fac"
874 | },
875 | "dist": {
876 | "type": "zip",
877 | "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/5795ffe5dc5b02460c3e34222fee8cbe245d8fac",
878 | "reference": "5795ffe5dc5b02460c3e34222fee8cbe245d8fac",
879 | "shasum": ""
880 | },
881 | "require": {
882 | "php": "^5.6 || ^7.0"
883 | },
884 | "require-dev": {
885 | "phpunit/phpunit": "^5.0"
886 | },
887 | "type": "library",
888 | "extra": {
889 | "branch-alias": {
890 | "dev-master": "2.0.x-dev"
891 | }
892 | },
893 | "autoload": {
894 | "classmap": [
895 | "src/"
896 | ]
897 | },
898 | "notification-url": "https://packagist.org/downloads/",
899 | "license": [
900 | "BSD-3-Clause"
901 | ],
902 | "authors": [
903 | {
904 | "name": "Sebastian Bergmann",
905 | "email": "sebastian@phpunit.de"
906 | }
907 | ],
908 | "description": "Provides functionality to handle HHVM/PHP environments",
909 | "homepage": "http://www.github.com/sebastianbergmann/environment",
910 | "keywords": [
911 | "Xdebug",
912 | "environment",
913 | "hhvm"
914 | ],
915 | "time": "2016-11-26 07:53:53"
916 | },
917 | {
918 | "name": "sebastian/exporter",
919 | "version": "2.0.x-dev",
920 | "source": {
921 | "type": "git",
922 | "url": "https://github.com/sebastianbergmann/exporter.git",
923 | "reference": "5e8e30670c3f36481e75211dbbcfd029a41ebf07"
924 | },
925 | "dist": {
926 | "type": "zip",
927 | "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/5e8e30670c3f36481e75211dbbcfd029a41ebf07",
928 | "reference": "5e8e30670c3f36481e75211dbbcfd029a41ebf07",
929 | "shasum": ""
930 | },
931 | "require": {
932 | "php": "^5.3.3 || ^7.0",
933 | "sebastian/recursion-context": "^2.0"
934 | },
935 | "require-dev": {
936 | "ext-mbstring": "*",
937 | "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0"
938 | },
939 | "type": "library",
940 | "extra": {
941 | "branch-alias": {
942 | "dev-master": "2.0.x-dev"
943 | }
944 | },
945 | "autoload": {
946 | "classmap": [
947 | "src/"
948 | ]
949 | },
950 | "notification-url": "https://packagist.org/downloads/",
951 | "license": [
952 | "BSD-3-Clause"
953 | ],
954 | "authors": [
955 | {
956 | "name": "Jeff Welch",
957 | "email": "whatthejeff@gmail.com"
958 | },
959 | {
960 | "name": "Volker Dusch",
961 | "email": "github@wallbash.com"
962 | },
963 | {
964 | "name": "Bernhard Schussek",
965 | "email": "bschussek@2bepublished.at"
966 | },
967 | {
968 | "name": "Sebastian Bergmann",
969 | "email": "sebastian@phpunit.de"
970 | },
971 | {
972 | "name": "Adam Harvey",
973 | "email": "aharvey@php.net"
974 | }
975 | ],
976 | "description": "Provides the functionality to export PHP variables for visualization",
977 | "homepage": "http://www.github.com/sebastianbergmann/exporter",
978 | "keywords": [
979 | "export",
980 | "exporter"
981 | ],
982 | "time": "2017-03-07 10:36:49"
983 | },
984 | {
985 | "name": "sebastian/global-state",
986 | "version": "1.1.x-dev",
987 | "source": {
988 | "type": "git",
989 | "url": "https://github.com/sebastianbergmann/global-state.git",
990 | "reference": "cea85a84b00f2795341ebbbca4fa396347f2494e"
991 | },
992 | "dist": {
993 | "type": "zip",
994 | "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/cea85a84b00f2795341ebbbca4fa396347f2494e",
995 | "reference": "cea85a84b00f2795341ebbbca4fa396347f2494e",
996 | "shasum": ""
997 | },
998 | "require": {
999 | "php": ">=5.3.3"
1000 | },
1001 | "require-dev": {
1002 | "phpunit/phpunit": "~4.2|~5.0"
1003 | },
1004 | "suggest": {
1005 | "ext-uopz": "*"
1006 | },
1007 | "type": "library",
1008 | "extra": {
1009 | "branch-alias": {
1010 | "dev-master": "1.0-dev"
1011 | }
1012 | },
1013 | "autoload": {
1014 | "classmap": [
1015 | "src/"
1016 | ]
1017 | },
1018 | "notification-url": "https://packagist.org/downloads/",
1019 | "license": [
1020 | "BSD-3-Clause"
1021 | ],
1022 | "authors": [
1023 | {
1024 | "name": "Sebastian Bergmann",
1025 | "email": "sebastian@phpunit.de"
1026 | }
1027 | ],
1028 | "description": "Snapshotting of global state",
1029 | "homepage": "http://www.github.com/sebastianbergmann/global-state",
1030 | "keywords": [
1031 | "global state"
1032 | ],
1033 | "time": "2017-02-23 14:11:06"
1034 | },
1035 | {
1036 | "name": "sebastian/object-enumerator",
1037 | "version": "2.0.x-dev",
1038 | "source": {
1039 | "type": "git",
1040 | "url": "https://github.com/sebastianbergmann/object-enumerator.git",
1041 | "reference": "c956fe7a68318639f694fc6bba0c89b7cdf1b08c"
1042 | },
1043 | "dist": {
1044 | "type": "zip",
1045 | "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/c956fe7a68318639f694fc6bba0c89b7cdf1b08c",
1046 | "reference": "c956fe7a68318639f694fc6bba0c89b7cdf1b08c",
1047 | "shasum": ""
1048 | },
1049 | "require": {
1050 | "php": "^5.6 || ^7.0",
1051 | "sebastian/recursion-context": "^2.0"
1052 | },
1053 | "require-dev": {
1054 | "phpunit/phpunit": "^5.7"
1055 | },
1056 | "type": "library",
1057 | "extra": {
1058 | "branch-alias": {
1059 | "dev-master": "2.0.x-dev"
1060 | }
1061 | },
1062 | "autoload": {
1063 | "classmap": [
1064 | "src/"
1065 | ]
1066 | },
1067 | "notification-url": "https://packagist.org/downloads/",
1068 | "license": [
1069 | "BSD-3-Clause"
1070 | ],
1071 | "authors": [
1072 | {
1073 | "name": "Sebastian Bergmann",
1074 | "email": "sebastian@phpunit.de"
1075 | }
1076 | ],
1077 | "description": "Traverses array structures and object graphs to enumerate all referenced objects",
1078 | "homepage": "https://github.com/sebastianbergmann/object-enumerator/",
1079 | "time": "2017-03-07 10:37:45"
1080 | },
1081 | {
1082 | "name": "sebastian/recursion-context",
1083 | "version": "2.0.x-dev",
1084 | "source": {
1085 | "type": "git",
1086 | "url": "https://github.com/sebastianbergmann/recursion-context.git",
1087 | "reference": "7e4d7c56f6e65d215f71ad913a5256e5439aca1c"
1088 | },
1089 | "dist": {
1090 | "type": "zip",
1091 | "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/7e4d7c56f6e65d215f71ad913a5256e5439aca1c",
1092 | "reference": "7e4d7c56f6e65d215f71ad913a5256e5439aca1c",
1093 | "shasum": ""
1094 | },
1095 | "require": {
1096 | "php": ">=5.3.3"
1097 | },
1098 | "require-dev": {
1099 | "phpunit/phpunit": "~4.4"
1100 | },
1101 | "type": "library",
1102 | "extra": {
1103 | "branch-alias": {
1104 | "dev-master": "2.0.x-dev"
1105 | }
1106 | },
1107 | "autoload": {
1108 | "classmap": [
1109 | "src/"
1110 | ]
1111 | },
1112 | "notification-url": "https://packagist.org/downloads/",
1113 | "license": [
1114 | "BSD-3-Clause"
1115 | ],
1116 | "authors": [
1117 | {
1118 | "name": "Jeff Welch",
1119 | "email": "whatthejeff@gmail.com"
1120 | },
1121 | {
1122 | "name": "Sebastian Bergmann",
1123 | "email": "sebastian@phpunit.de"
1124 | },
1125 | {
1126 | "name": "Adam Harvey",
1127 | "email": "aharvey@php.net"
1128 | }
1129 | ],
1130 | "description": "Provides functionality to recursively process PHP variables",
1131 | "homepage": "http://www.github.com/sebastianbergmann/recursion-context",
1132 | "time": "2017-03-08 08:21:15"
1133 | },
1134 | {
1135 | "name": "sebastian/resource-operations",
1136 | "version": "dev-master",
1137 | "source": {
1138 | "type": "git",
1139 | "url": "https://github.com/sebastianbergmann/resource-operations.git",
1140 | "reference": "fadc83f7c41fb2924e542635fea47ae546816ece"
1141 | },
1142 | "dist": {
1143 | "type": "zip",
1144 | "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/fadc83f7c41fb2924e542635fea47ae546816ece",
1145 | "reference": "fadc83f7c41fb2924e542635fea47ae546816ece",
1146 | "shasum": ""
1147 | },
1148 | "require": {
1149 | "php": ">=5.6.0"
1150 | },
1151 | "type": "library",
1152 | "extra": {
1153 | "branch-alias": {
1154 | "dev-master": "1.0.x-dev"
1155 | }
1156 | },
1157 | "autoload": {
1158 | "classmap": [
1159 | "src/"
1160 | ]
1161 | },
1162 | "notification-url": "https://packagist.org/downloads/",
1163 | "license": [
1164 | "BSD-3-Clause"
1165 | ],
1166 | "authors": [
1167 | {
1168 | "name": "Sebastian Bergmann",
1169 | "email": "sebastian@phpunit.de"
1170 | }
1171 | ],
1172 | "description": "Provides a list of PHP built-in functions that operate on resources",
1173 | "homepage": "https://www.github.com/sebastianbergmann/resource-operations",
1174 | "time": "2016-10-03 07:43:09"
1175 | },
1176 | {
1177 | "name": "sebastian/version",
1178 | "version": "dev-master",
1179 | "source": {
1180 | "type": "git",
1181 | "url": "https://github.com/sebastianbergmann/version.git",
1182 | "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019"
1183 | },
1184 | "dist": {
1185 | "type": "zip",
1186 | "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019",
1187 | "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019",
1188 | "shasum": ""
1189 | },
1190 | "require": {
1191 | "php": ">=5.6"
1192 | },
1193 | "type": "library",
1194 | "extra": {
1195 | "branch-alias": {
1196 | "dev-master": "2.0.x-dev"
1197 | }
1198 | },
1199 | "autoload": {
1200 | "classmap": [
1201 | "src/"
1202 | ]
1203 | },
1204 | "notification-url": "https://packagist.org/downloads/",
1205 | "license": [
1206 | "BSD-3-Clause"
1207 | ],
1208 | "authors": [
1209 | {
1210 | "name": "Sebastian Bergmann",
1211 | "email": "sebastian@phpunit.de",
1212 | "role": "lead"
1213 | }
1214 | ],
1215 | "description": "Library that helps with managing the version number of Git-hosted PHP projects",
1216 | "homepage": "https://github.com/sebastianbergmann/version",
1217 | "time": "2016-10-03 07:35:21"
1218 | },
1219 | {
1220 | "name": "symfony/yaml",
1221 | "version": "v2.8.2",
1222 | "source": {
1223 | "type": "git",
1224 | "url": "https://github.com/symfony/yaml.git",
1225 | "reference": "34c8a4b51e751e7ea869b8262f883d008a2b81b8"
1226 | },
1227 | "dist": {
1228 | "type": "zip",
1229 | "url": "https://api.github.com/repos/symfony/yaml/zipball/34c8a4b51e751e7ea869b8262f883d008a2b81b8",
1230 | "reference": "34c8a4b51e751e7ea869b8262f883d008a2b81b8",
1231 | "shasum": ""
1232 | },
1233 | "require": {
1234 | "php": ">=5.3.9"
1235 | },
1236 | "type": "library",
1237 | "extra": {
1238 | "branch-alias": {
1239 | "dev-master": "2.8-dev"
1240 | }
1241 | },
1242 | "autoload": {
1243 | "psr-4": {
1244 | "Symfony\\Component\\Yaml\\": ""
1245 | },
1246 | "exclude-from-classmap": [
1247 | "/Tests/"
1248 | ]
1249 | },
1250 | "notification-url": "https://packagist.org/downloads/",
1251 | "license": [
1252 | "MIT"
1253 | ],
1254 | "authors": [
1255 | {
1256 | "name": "Fabien Potencier",
1257 | "email": "fabien@symfony.com"
1258 | },
1259 | {
1260 | "name": "Symfony Community",
1261 | "homepage": "https://symfony.com/contributors"
1262 | }
1263 | ],
1264 | "description": "Symfony Yaml Component",
1265 | "homepage": "https://symfony.com",
1266 | "time": "2016-01-13 10:28:07"
1267 | },
1268 | {
1269 | "name": "webmozart/assert",
1270 | "version": "dev-master",
1271 | "source": {
1272 | "type": "git",
1273 | "url": "https://github.com/webmozart/assert.git",
1274 | "reference": "4a8bf11547e139e77b651365113fc12850c43d9a"
1275 | },
1276 | "dist": {
1277 | "type": "zip",
1278 | "url": "https://api.github.com/repos/webmozart/assert/zipball/4a8bf11547e139e77b651365113fc12850c43d9a",
1279 | "reference": "4a8bf11547e139e77b651365113fc12850c43d9a",
1280 | "shasum": ""
1281 | },
1282 | "require": {
1283 | "php": "^5.3.3 || ^7.0"
1284 | },
1285 | "require-dev": {
1286 | "phpunit/phpunit": "^4.6",
1287 | "sebastian/version": "^1.0.1"
1288 | },
1289 | "type": "library",
1290 | "extra": {
1291 | "branch-alias": {
1292 | "dev-master": "1.3-dev"
1293 | }
1294 | },
1295 | "autoload": {
1296 | "psr-4": {
1297 | "Webmozart\\Assert\\": "src/"
1298 | }
1299 | },
1300 | "notification-url": "https://packagist.org/downloads/",
1301 | "license": [
1302 | "MIT"
1303 | ],
1304 | "authors": [
1305 | {
1306 | "name": "Bernhard Schussek",
1307 | "email": "bschussek@gmail.com"
1308 | }
1309 | ],
1310 | "description": "Assertions to validate method input/output with nice error messages.",
1311 | "keywords": [
1312 | "assert",
1313 | "check",
1314 | "validate"
1315 | ],
1316 | "time": "2016-11-23 20:04:41"
1317 | }
1318 | ],
1319 | "aliases": [],
1320 | "minimum-stability": "dev",
1321 | "stability-flags": [],
1322 | "prefer-stable": false,
1323 | "prefer-lowest": false,
1324 | "platform": {
1325 | "php": ">=5.6.0"
1326 | },
1327 | "platform-dev": []
1328 | }
1329 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
13 |
14 |
15 |
16 | ./tests/
17 |
18 |
19 |
20 |
21 |
22 | ./
23 |
24 | ./tests
25 | ./vendor
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | ## JetFire PHP Routing
2 | [](https://insight.sensiolabs.com/projects/aeb83524-44bc-4501-90dc-18041cc7a84a) [](https://travis-ci.org/jetfirephp/routing) [](https://scrutinizer-ci.com/g/jetfirephp/routing/?branch=master)
3 |
4 | A simple & powerful router for PHP 5.4+
5 |
6 | ### Features
7 |
8 | V1.3
9 | * [Support subdomain](#subdomain)
10 | * [Support group resolver](#group-resolver)
11 |
12 | V1.2
13 | * [ClosureTemplate resolver](#closureTemplate-resolver)
14 | * [ControllerTemplate resolver](#controllerTemplate-resolver)
15 | * Possibility to choose a resolver
16 |
17 | V1.1
18 | * [Support dependency injection container](#config)
19 | * [Add your custom matcher and dispatcher](#custom-matcher)
20 |
21 | V1.0
22 | * Support static & dynamic route patterns
23 | * [Support REST routing](#rest)
24 | * [Support reversed routing using named routes](#named-routes)
25 | * [Uri matcher](#uri-matcher)
26 | * [Array matcher](#array-matcher)
27 | * [Closure resolver](#closure-resolver)
28 | * [Template resolver](#template-resolver)
29 | * [Controller resolver](#controller-resolver)
30 | * [Route Middleware](#middleware)
31 | * [Integration with other libraries](#libraries)
32 |
33 | ### Getting started
34 |
35 | 1. PHP 5.4+ is required
36 | 2. Install `JetFire\Routing` using Composer
37 | 3. Setup URL rewriting so that all requests are handled by index.php
38 |
39 | ### Installation
40 |
41 | Via [composer](https://getcomposer.org)
42 |
43 | ```bash
44 | $ composer require jetfirephp/routing
45 | ```
46 |
47 | #### .htaccess
48 |
49 | ```php
50 | RewriteEngine on
51 | RewriteCond %{REQUEST_FILENAME} !-f
52 | RewriteCond %{REQUEST_FILENAME} !-d
53 | RewriteRule ^(.[a-zA-Z0-9\-\_\/]*)$ index.php?url=$1 [QSA,L]
54 | ```
55 |
56 | ### Usage
57 |
58 | Create an instance of `JetFire\Routing\RouteCollection` and define your routes. Then create an instance of `JetFire\Routing\Router` and run your routes.
59 |
60 | ```php
61 | // Require composer autoloader
62 | require __DIR__ . '/vendor/autoload.php';
63 |
64 | // Create RouteCollection instance
65 | $collection = new \JetFire\Routing\RouteCollection();
66 |
67 | // Define your routes
68 | // ...
69 |
70 | // Create an instance of Router
71 | $router = new \JetFire\Routing\Router($collection)
72 |
73 | // select your matcher
74 | $matcher1 = new \JetFire\Routing\Matcher\ArrayMatcher($router);
75 | $matcher2 = new \JetFire\Routing\Matcher\UriMatcher($router);
76 |
77 | // set your matcher to the router
78 | $router->setMatcher([$matcher1,$matcher2])
79 |
80 | // Run it!
81 | $router->run();
82 | ```
83 | ### Matcher
84 |
85 | `JetFire\Routing` provide 2 type of matcher for your routes : `JetFire\Routing\Matcher\ArrayMatcher` and `JetFire\Routing\Matcher\UriMatcher`
86 |
87 |
88 | #### Uri Matcher
89 |
90 | With Uri Matcher you don't have to define your routes. Depending on the uri it can check if a target exist for the current url.
91 | But you have to define your views directory path and controllers namespace to the collection :
92 |
93 | ```php
94 | $options = [
95 | 'view_dir' => '_VIEW_DIR_PATH_',
96 | 'ctrl_namespace' => '_CONTROLLERS_NAMESPACE_'
97 | ];
98 | $collection = new \JetFire\Routing\RouteCollection(null,$options);
99 | // or
100 | $collection = new \JetFire\Routing\RouteCollection();
101 | $collection->setOption($options);
102 | // or
103 | $collection = new \JetFire\Routing\RouteCollection();
104 | $collection->addRoutes(null,$options)
105 | ```
106 |
107 | For example if the uri is : `/home/index`
108 |
109 | ##### Resolver
110 |
111 | Here are the list of Uri Matcher resolver :
112 |
113 | ```php
114 | $resolver = [
115 | 'isControllerAndTemplate',
116 | 'isController',
117 | 'isTemplate'
118 | ];
119 | ```
120 |
121 | ##### Template resolver
122 |
123 | Uri Matcher check if an `index.php` file exist in `/_VIEW_DIR_PATH_/Home` directory.
124 |
125 | If you want to check for other extension (html,json,...) You can configure the router like this :
126 |
127 | ```php
128 | $router->setConfig([
129 |
130 | // Define your template extension like this
131 | 'templateExtension' => ['.php','.html','.twig','.json','.xml'],
132 |
133 | ]);
134 | ```
135 |
136 | ##### Controller resolver
137 |
138 | With Controller resolver, Uri Matcher checks if a controller with name `HomeController` located in the namespace `_CONTROLLERS_NAMESPACE_` has the `index` method.
139 | You have to require your controller before matching or you can use your custom autoloader to load your controllers.
140 | Uri Matcher support also dynamic routes. For example if the uri is : `/home/user/peter/parker` then you must have a method `user` with two parameters like this :
141 |
142 | ```php
143 | class HomeController {
144 | public function user($firstName,$lastName){
145 | // $firstName = peter
146 | // $lastName = parker
147 | }
148 | }
149 | ```
150 |
151 |
152 |
153 | #### Array Matcher
154 |
155 | With Array Matcher you have to add your routes like this :
156 |
157 | ```php
158 | $options = [
159 | 'view_dir' => '_VIEW_DIR_PATH_',
160 | 'ctrl_namespace' => '_CONTROLLERS_NAMESPACE_'
161 | ];
162 |
163 | // addRoutes expect an array argument
164 | $collection->addRoutes([
165 | '/home/index' => '_TARGET_'
166 | ],$options);
167 |
168 | // or a file containing an array
169 | $collection->addRoutes('path_to_array_file',$options);
170 | ```
171 |
172 | We recommend that you define your routes in a separate file and pass the path to `addRoutes()` method.
173 |
174 | ```php
175 | // routes.php file
176 | return [
177 | '/home/index' => '_TARGET_'
178 | ];
179 | ```
180 |
181 | ##### Resolver
182 |
183 | Here are the list of Uri Matcher resolver :
184 |
185 | ```php
186 | $resolver = [
187 | 'isControllerAndTemplate',
188 | 'isClosureAndTemplate',
189 | 'isClosure',
190 | 'isController',
191 | 'isTemplate'
192 | ];
193 | ```
194 |
195 | You have 5 actions possible for Array Routing. We assume you are using a separate file for your routes.
196 |
197 |
198 | ##### Template resolver
199 |
200 | ```php
201 | return [
202 |
203 | // static route
204 | '/home/index' => 'Home/index.php',
205 |
206 | // dynamic route with arguments
207 | '/home/user-:id-:slug' => [
208 | 'use' => 'Home/page.html',
209 | 'arguments' => ['id' => '[0-9]+','slug' => '[a-z-]*'],
210 | ],
211 |
212 | ];
213 | ```
214 |
215 | ##### Controller resolver
216 |
217 | ```php
218 | return [
219 |
220 | // static route
221 | '/home/index' => 'HomeController@index',
222 |
223 | // dynamic route with arguments
224 | '/home/user-:id-:slug' => [
225 | 'use' => 'HomeController@page',
226 | 'arguments' => ['id' => '[0-9]+','slug' => '[a-z-]*'],
227 | ],
228 |
229 | ];
230 | ```
231 |
232 | ##### Controller and Template resolver
233 |
234 | ```php
235 | return [
236 |
237 | // controller and template resolver
238 | // call first the controller and render then the template
239 | // if the template is not found, the controller is returned
240 | '/home/log' => [
241 | 'use' => 'HomeController@log',
242 | 'template' => 'Home/log.php', //in your controller you can return an array of data that you can access in your template
243 | ],
244 |
245 | // dynamic route with arguments
246 | '/home/user-:id-:slug' => [
247 | 'use' => 'HomeController@page',
248 | 'template' => 'Home/log.php',
249 | 'arguments' => ['id' => '[0-9]+','slug' => '[a-z-]*'],
250 | ],
251 |
252 | ];
253 | ```
254 |
255 | ##### Controller group resolver
256 |
257 | ```php
258 | return [
259 |
260 | // suppose we have the following methods in the AccountController :
261 | // public function create();
262 | // public function read($id);
263 | // public function update($id);
264 | // public function destroy($id);
265 | // if the uri is /account/create the router will call the associated method in the controller
266 | '/account/*' => [
267 | 'use' => 'AccountController@{method}',
268 | ],
269 |
270 | ];
271 | ```
272 |
273 | ##### Closure resolver
274 |
275 | ```php
276 | return [
277 |
278 | // static route
279 | '/home/index' => function(){
280 | return 'Hello world !';
281 | },
282 |
283 | // dynamic route with arguments
284 | '/home/user-:id-:slug' => [
285 | 'use' => function($id,$slug){
286 | return 'Hello User '.$id.'-'.$slug;
287 | },
288 | 'arguments' => ['id' => '[0-9]+','slug' => '[a-z-]*'],
289 | ],
290 |
291 | ];
292 | ```
293 |
294 | ##### Closure and Template resolver
295 |
296 | ```php
297 | return [
298 |
299 | // closure and template matching
300 | // call first the closure and render then the template
301 | '/home/log' => [
302 | 'use' => function(){
303 | return ['name' => 'Peter'];
304 | }
305 | 'template' => 'Home/log.php', // in log.php you can access the return data like this : $name ='Peter'
306 | ],
307 |
308 | '/home/user-:id-:slug' => [
309 | 'use' => function($id,$slug){
310 | return ['id' => $id,'slug' => $slug];
311 | },
312 | 'template' => 'Home/log.php',
313 | 'arguments' => ['id' => '[0-9]+','slug' => '[a-z-]*'],
314 | ],
315 |
316 | ];
317 | ```
318 |
319 | ### Block Routes
320 |
321 | With `JetFire\Routing` you have the ability to create block routes to better organize your code.
322 | For example , if you have an administration for your website , you can create block only for this section and another block to the public part like this :
323 |
324 | ```php
325 | // Create RouteCollection instance
326 | $collection = new \JetFire\Routing\RouteCollection();
327 |
328 | // Block routes
329 | $collection->addRoutes('admin_routes_path',['view_dir' => 'admin_view_path' , 'ctrl_namespace' => 'admin_controllers_namespace','prefix' => 'admin']);
330 | $collection->addRoutes('public_routes_path',['view_dir' => 'public_view_path' , 'ctrl_namespace' => 'public_controllers_namespace']);
331 |
332 | // Create an instance of Router
333 | $router = new \JetFire\Routing\Router($collection)
334 | // Select your matcher
335 | $router->addRouter(new \JetFire\Routing\Matcher\ArrayMatcher($router));
336 |
337 | // Run it!
338 | $router->run();
339 | ```
340 |
341 | ### Router Configuration
342 |
343 | Here are the list of router configuration that you can edit :
344 |
345 | ```php
346 | $router->setConfig([
347 |
348 | // You can add/remove extension for views
349 | // default extension for views
350 | 'templateExtension' => ['.html', '.php', '.json', '.xml'],
351 |
352 | // If you use template engine library, you can use this to render the view
353 | // See the 'Integration with other libraries' section for more details
354 | 'templateCallback' => [],
355 |
356 | // If you want to add a dependency injection container for your controllers constructor or method
357 | // for example if your controller 'HomeController' method 'log' method require a class like this : public function log(Request $request)
358 | // by default :
359 | 'di' => function($class){
360 | return new $class;
361 | },
362 |
363 | // See the Named Routes section for more details
364 | 'generateRoutesPath' => false,
365 | ]);
366 | ```
367 |
368 | ### Collection Options
369 |
370 | Here are the list of options that you can edit for each collection routes :
371 |
372 | ```php
373 | $options = [
374 | // your view directory
375 | 'view_dir' => 'view_directory',
376 | // your controllers namespace
377 | 'ctrl_namespace' => 'controllers_namespace',
378 | // your routes prefix
379 | 'prefix' => 'your_prefix'
380 | ];
381 | ```
382 |
383 |
384 | ### Named Routes
385 |
386 | You can specify a name for each route like this :
387 |
388 | ```php
389 | return [
390 |
391 | '/home/index' => [
392 | 'use' => 'Home/index.html',
393 | 'name' => 'home.index'
394 | ],
395 |
396 | '/home/user-:id-:slug' => [
397 | 'use' => 'HomeController@user',
398 | 'name' => 'home.user',
399 | 'arguments' => ['id' => '[0-9]+','slug' => '[a-z-]*'],
400 | ],
401 | ];
402 | ```
403 | And then to get the url of this route you can do like this :
404 |
405 | ```php
406 | // You have to enable generateRoutesPath to get routes url
407 | $router->setConfig([
408 | 'generateRoutesPath' => true,
409 | // Other configuration
410 | // ...
411 | ]);
412 |
413 | // Reverse routing
414 | $collection->getRoutePath('home.index'); // return http://your_domain/home/index
415 | $collection->getRoutePath('home.user',[ 'id' => 1, 'slug' => 'toto']); // return http://your_domain/home/user-1-toto
416 | ```
417 |
418 | Supported only in `JetFire\Routing\Matcher\ArrayMatcher`.
419 |
420 | ### REST Routing
421 |
422 | You can specify the request method for each route like this :
423 |
424 | ```php
425 | return [
426 |
427 | '/api/users' => [
428 | 'use' => [
429 | 'GET' => function($response){
430 | $response->setHeaders(['Content-Type' => 'application/json']);
431 | return ['name' => 'Peter'];
432 | },
433 | 'POST' => function($response){
434 | $response->setHeaders(['Content-Type' => 'application/json']);
435 | $response->setStatusCode(201);
436 | return [];
437 | },
438 | ],
439 | 'name' => 'api.users',
440 | 'method' => ['GET', 'POST']
441 | ],
442 |
443 | '/api/users/:id' => [
444 | 'use' => [
445 | 'GET' => function($response){
446 | $response->setHeaders(['Content-Type' => 'application/json']);
447 | return ['name' => 'Peter'];
448 | },
449 | 'PUT' => function($response){
450 | $response->setHeaders(['Content-Type' => 'application/json']);
451 | return [];
452 | },
453 | ],
454 | 'name' => 'api.user',
455 | 'arguments' => ['id' => '[0-9]+'],
456 | 'method' => ['GET','PUT']
457 | ],
458 | ];
459 | ```
460 |
461 | ### Prefix
462 |
463 | You can set a prefix for each routes collection like this :
464 |
465 | ```php
466 | $collection->addRoutes('routes_file_1',['prefix' => 'prefix_1']); // all routes in routes_file_1 begin with prefix_1/
467 | $collection->addRoutes('routes_file_2',['prefix' => 'prefix_2']); // all routes in routes_file_2 begin with prefix_2/
468 | ```
469 | Or :
470 | ```php
471 | $collection->addRoutes('routes_file_1');
472 | $collection->addRoutes('routes_file_2');
473 | $collection->setPrefix(['prefix_1','prefix_2']);
474 | ```
475 |
476 | ### Middleware
477 |
478 | Middlewares are called before and after a route match the current uri.
479 | You have to create a middleware config file like this :
480 |
481 | ```php
482 | // Your middleware file
483 | return [
484 |
485 | // global middleware are called every time
486 | 'global_middleware' => [
487 | // Here you define your middleware class to be called
488 | 'app\Middleware\Global',
489 | ],
490 |
491 | // block middleware are called when the current route block match one of the following block
492 | 'block_middleware' => [
493 | // You define here for each block the middleware class to be called
494 | '/app/Blocks/PublicBlock/' => 'app\Middleware\Public',
495 | '/app/Blocks/AdminBlock/' => 'app\Middleware\Admin',
496 | '/app/Blocks/UserBlock/' => 'app\Middleware\User',
497 | ],
498 |
499 | // class middleware are called when the controller router match one of the following controller
500 | 'class_middleware' => [
501 | // You define here for each controller the middleware class to be called
502 | '/app/Blocks/PublicBlock/Controllers/HomeController' => 'app\Middleware\Home',
503 | ],
504 |
505 | // route middleware are called when the current route match one of the following middleware name
506 | 'route_middleware' => [
507 | // You define here a name to the middleware and assign the class to be called
508 | // You have to specify this name to the route like this : `'middleware' => 'home'`
509 | 'home' => 'app\Middleware\App'
510 | ],
511 |
512 | ];
513 | ```
514 |
515 | Then you have to instantiate the middleware class `Middleware` like this :
516 |
517 | ```php
518 | $middleware = new Middleware($router);
519 | $middleware->setCallbackAction('before', 'your_before_middleware_file');
520 | $middleware->setCallbackAction('between', 'your_between_middleware_file');
521 | $middleware->setCallbackAction('after', 'your_after_middleware_file');
522 |
523 | $router->addMiddleware($middleware);
524 | ```
525 |
526 | Let see how to create your Middleware Class. For example we take the Global middleware :
527 |
528 | ```php
529 | namespace app\Middleware;
530 |
531 | class Global{
532 |
533 | // Middleware class must implements handle method
534 | // object passed in argument will be inject automatically
535 | public function handle(){
536 | // here you put your code
537 | // ...
538 | }
539 | }
540 | ```
541 | See the API section to learn how to handle your $route in middleware class.
542 |
543 |
544 | ### Custom Matcher and Dispatcher
545 |
546 | If the default matcher and dispatcher doesn't match your expectation, you can write your own matcher and dispatcher like this :
547 |
548 | ```php
549 | class MyCustomMatcher implements MatcherInterface{
550 |
551 | public function __construct(Router $router);
552 |
553 | // in this method you can check if the current uri match your expectation
554 | // return true or false
555 | // if it match you have to set your route target with an array of params and the dispatcher class name to be called
556 | // $this->setTarget(['dispatcher' => '\My\Custom\Dispatcher\Class\Name', 'other_params' => 'values']);
557 | public function match();
558 |
559 | // set your route target $this->router->route->setTarget($target);
560 | public function setTarget($target = []);
561 |
562 | // set your resolver
563 | public function setResolver($resolver = []);
564 |
565 | // you can add multiple resolver method in the same matcher
566 | public function addResolver($resolver);
567 |
568 | // to retrieve your resolver
569 | public function getResolver();
570 |
571 | // dispatcher yo be called
572 | public function setDispatcher($dispatcher = []);
573 |
574 | public function addDispatcher($dispatcher);
575 | }
576 |
577 | class MyCustomDispatcher implements DispatcherInterface{
578 |
579 | public function __construct(Router $router);
580 |
581 | // your target to call
582 | // you can get your route target information with $this->route->getTarget()
583 | public function call();
584 | }
585 |
586 | $router->addMatcher('MyCustomMatcher');
587 | ```
588 |
589 | You can also override the default matcher like this :
590 |
591 | ```php
592 | class MyCustomMatcher extends ArrayMatcher implements MatcherInterface{
593 |
594 | public function __construct(Router $router){
595 | parent::__construct($router);
596 | // your custom match method
597 | $this->addResolver('customResolver');
598 | }
599 |
600 | public function customResolver(){
601 | // your code here
602 | // ...
603 | // then you set the route target with the dispatcher
604 | }
605 | }
606 |
607 | class MyCustomDispatcher implements DispatcherInterface{
608 |
609 | public function __construct(Router $router);
610 |
611 | // your target to call
612 | // you can get your route target information with $this->route->getTarget()
613 | public function call();
614 | }
615 | ```
616 |
617 |
618 | ### Integration with other libraries
619 |
620 | If you want to integrate other template engine libraries like twig, smarty ... you have to set the 'templateCallback' in router.
621 |
622 | ```php
623 | // Twig template engine
624 | require_once '/path/to/lib/Twig/Autoloader.php';
625 | Twig_Autoloader::register();
626 |
627 | // Other template engine
628 | $tpl = new \Acme\Template\Template();
629 |
630 | $router->setConfig([
631 | 'templateCallback' => [
632 |
633 | // if the router find a template with twig enxtension then it will call the twig template engine
634 | 'twig' => function($route){
635 | $loader = new Twig_Loader_Filesystem($route->getTarget('block'));
636 | $twig = new Twig_Environment($loader, []);
637 | $template = $twig->loadTemplate($route->getTarget('template'));
638 | echo $template->render($route->getData());
639 | },
640 |
641 | // for other template engine
642 | 'tpl' => function($route) use ($tpl){
643 | $tpl->load($route->getTarget('template'));
644 | $tpl->setData($route->getData());
645 | $tpl->display();
646 | }
647 | ],
648 |
649 | // Other configuration
650 | // ...
651 | ]);
652 | ```
653 |
654 |
655 | ### Subdomain
656 |
657 | ```php
658 | return [
659 | '{subdomain}.{host}/home' => [
660 | 'use' => 'AdminController@index',
661 | 'name' => 'admin.home.index',
662 | 'subdomain' => 'admin' // could be a regex for multiple subdomain
663 | ]
664 | ];
665 | ```
666 |
667 | Or if you want to add a subdomain for a bloc, you have to add this line in your route collection options :
668 |
669 | ```php
670 | $options = [
671 | // ...
672 | 'subdomain' => 'your_subdomain'
673 | ];
674 | ```
675 |
676 | ### API
677 |
678 | Below is a list of the public methods and variables in the common classes you will most likely use.
679 |
680 | ```php
681 | // JetFire\Routing\RouteCollection
682 | $collection->
683 | routesByName // routes url by their name
684 | countRoutes // count routes block
685 | addRoutes($collection,$options = []) // set your routes
686 | getRoutes($key = null) // return all routes
687 | getRoutePath($name,$params = []) // return the url of route
688 | setPrefix($prefix) // $prefix can be a string (applied for every collection)
689 | // or an array (for each collection you can specify a prefix)
690 | setOption($options = []) // set your routes option
691 | generateRoutesPath() // generate routes url by their name
692 |
693 | // JetFire\Routing\Router
694 | $router->
695 | response // JetFire\Routing\ResponseInterface instance
696 | route // JetFire\Routing\Route instance
697 | collection // JetFire\Routing\RouteCollection instance
698 | middlewareCollection // middleware collection
699 | matcher // list of matcher
700 | dispatcher // the dispatcher instance
701 | setConfig($config) // router configuration
702 | getConfig() // get router configuration
703 | run() // run the router with the request url
704 |
705 | // JetFire\Routing\Route
706 | $route->
707 | set($args = []) // set your route array parameters
708 | getUrl() // return the route url
709 | setUrl($url) // set the route url
710 | getName() // return the route name
711 | setName($name) // set the route name
712 | getCallback() // return the route callback (template,controller or anonymous function)
713 | setCallback($callback) // set the route callback
714 | getMethod() // return the route method (GET,POST,PUT,DELETE)
715 | getDetail() // return the route detail
716 | setDetail($detail) // set the route detail
717 | addDetail($key,$value) // add a detail for the route
718 | getTarget($key = null) // return the route target (dispatcher,template or controller|action or function)
719 | setTarget($target = []) // set the route target
720 | hasTarget($key = null) // check if the route has the following target
721 | getData() // return data for the route
722 | __call($name,$arguments) // magic call to get or set route detail
723 |
724 | ```
725 |
726 | ### License
727 |
728 | The JetFire Routing is released under the MIT public license : http://www.opensource.org/licenses/MIT.
729 |
--------------------------------------------------------------------------------
/src/Routing/Dispatcher/ClosureDispatcher.php:
--------------------------------------------------------------------------------
1 | router = $router;
28 | }
29 |
30 |
31 | /**
32 | * @description call anonymous function
33 | */
34 | public function call()
35 | {
36 | $classInstance = [
37 | Response::class => $this->router->response,
38 | Route::class => $this->router->route,
39 | RouteCollection::class => $this->router->collection,
40 | ];
41 |
42 | $params = empty($this->router->route->getParameters()) ? $classInstance : array_merge($this->router->route->getParameters(), $classInstance);
43 | $content = call_user_func_array($this->router->route->getTarget('closure'), $params);
44 | if ($content instanceof ResponseInterface) {
45 | $this->router->response = $content;
46 | } else {
47 | if (is_array($content)) {
48 | $this->router->route->addTarget('data', $content);
49 | $content = json_encode($content);
50 | }
51 | $this->router->callMiddleware('between');
52 | $this->router->response->setContent($content);
53 | }
54 | }
55 |
56 | }
57 |
--------------------------------------------------------------------------------
/src/Routing/Dispatcher/ControllerDispatcher.php:
--------------------------------------------------------------------------------
1 | router = $router;
32 | }
33 |
34 |
35 | /**
36 | * @throws \Exception
37 | */
38 | public function call()
39 | {
40 |
41 | if (!class_exists($this->router->route->getTarget('controller'))) {
42 | throw new \Exception('Class not found : "' . $this->router->route->getTarget('controller') . '"');
43 | }
44 |
45 | $classInstance = [
46 | Route::class => $this->router->route,
47 | Response::class => $this->router->response,
48 | RouteCollection::class => $this->router->collection,
49 | ];
50 |
51 | $reflectionMethod = new ReflectionMethod($this->router->route->getTarget('controller'), $this->router->route->getTarget('action'));
52 | $dependencies = [];
53 | $count = 0;
54 |
55 | foreach ($reflectionMethod->getParameters() as $arg) {
56 | if (!is_null($arg->getClass())) {
57 | if (isset($classInstance[$arg->getClass()->name])) {
58 | $dependencies[] = $classInstance[$arg->getClass()->name];
59 | } else {
60 | $dependencies[] = call_user_func_array($this->router->route->getTarget('di'), [$arg->getClass()->name]);
61 | }
62 | } else {
63 | $count++;
64 | }
65 | }
66 |
67 | if ($count == count($this->router->route->getParameters()) || ($this->router->route->getParameters() == '' && $count == 0)) {
68 | $dependencies = array_merge($dependencies, ($this->router->route->getParameters() == '') ? [] : $this->router->route->getParameters());
69 | $content = $reflectionMethod->invokeArgs($this->getController($classInstance), $dependencies);
70 | if ($content instanceof ResponseInterface) {
71 | $this->router->response = $content;
72 | } else {
73 | if (is_array($content)) {
74 | $this->router->route->addTarget('data', $content);
75 | $content = json_encode($content);
76 | }
77 | $this->router->callMiddleware('between');
78 | $this->router->response->setContent($content);
79 | }
80 | } else {
81 | $this->router->response->setStatusCode(404);
82 | }
83 | }
84 |
85 |
86 | /**
87 | * @param array $classInstance
88 | * @return object
89 | * @throws \Exception
90 | */
91 | private function getController($classInstance = [])
92 | {
93 | $reflector = new ReflectionClass($this->router->route->getTarget('controller'));
94 | if (!$reflector->isInstantiable()) {
95 | throw new \Exception('Target [' . $this->router->route->getTarget('controller') . '] is not instantiable.');
96 | }
97 | $constructor = $reflector->getConstructor();
98 | if (is_null($constructor)) {
99 | $class = $this->router->route->getTarget('controller');
100 | return call_user_func_array($this->router->route->getTarget('di'), [$class]);
101 | }
102 | $dependencies = [];
103 | foreach ($constructor->getParameters() as $dep) {
104 | $class = $dep->getClass()->name;
105 | if (isset($classInstance[$class])) {
106 | $dependencies[] = $classInstance[$class];
107 | } else {
108 | $dependencies[] = call_user_func_array($this->router->route->getTarget('di'), [$class]);
109 | }
110 | }
111 | return $reflector->newInstanceArgs($dependencies);
112 | }
113 |
114 | }
115 |
--------------------------------------------------------------------------------
/src/Routing/Dispatcher/DispatcherInterface.php:
--------------------------------------------------------------------------------
1 | 'application/json',
24 | 'xml' => 'application/xml',
25 | 'txt' => 'text/plain',
26 | 'html' => 'text/html'
27 | ];
28 |
29 | /**
30 | * @param Router $router
31 | */
32 | public function __construct(Router $router)
33 | {
34 | $this->router = $router;
35 | }
36 |
37 | /**
38 | * @description call template file
39 | */
40 | public function call()
41 | {
42 | if (!is_file($this->router->route->getTarget('template'))) {
43 | throw new \Exception('Template file not found : "' . $this->router->route->getTarget('template') . '"');
44 | }
45 | $this->setContentType($this->router->route->getTarget('extension'));
46 | if (isset($this->router->route->getTarget('callback')[$this->router->route->getTarget('extension')])) {
47 | $this->router->response->setContent(call_user_func_array($this->router->route->getTarget('callback')[$this->router->route->getTarget('extension')], [$this->router->route]));
48 | } else {
49 | ob_start();
50 | if (isset($this->router->route->getTarget()['data'])) extract($this->router->route->getTarget('data'));
51 | if (isset($this->router->route->getParams()['data'])) extract($this->router->route->getParams()['data']);
52 | require($this->router->route->getTarget('template'));
53 | $this->router->response->setContent(ob_get_clean());
54 | }
55 | }
56 |
57 | /**
58 | * @param $extension
59 | */
60 | public function setContentType($extension)
61 | {
62 | isset($this->types[$extension])
63 | ? $this->router->response->setHeaders(['Content-Type' => $this->types[$extension]])
64 | : $this->router->response->setHeaders(['Content-Type' => $this->types['html']]);
65 | }
66 |
67 | }
68 |
--------------------------------------------------------------------------------
/src/Routing/Matcher/ArrayMatcher.php:
--------------------------------------------------------------------------------
1 | ClosureDispatcher::class,
37 | 'isController' => ControllerDispatcher::class,
38 | 'isTemplate' => TemplateDispatcher::class,
39 | 'isControllerAndTemplate' => [ControllerDispatcher::class, TemplateDispatcher::class],
40 | 'isClosureAndTemplate' => [ClosureDispatcher::class, TemplateDispatcher::class],
41 | ];
42 |
43 | /**
44 | * @param Router $router
45 | */
46 | public function __construct(Router $router)
47 | {
48 | $this->router = $router;
49 | }
50 |
51 | /**
52 | * @param array $resolver
53 | */
54 | public function setResolver($resolver = [])
55 | {
56 | $this->resolver = $resolver;
57 | }
58 |
59 | /**
60 | * @param string $resolver
61 | */
62 | public function addResolver($resolver)
63 | {
64 | $this->resolver[] = $resolver;
65 | }
66 |
67 | /**
68 | * @return array
69 | */
70 | public function getResolver()
71 | {
72 | return $this->resolver;
73 | }
74 |
75 | /**
76 | * @param array $dispatcher
77 | */
78 | public function setDispatcher($dispatcher = [])
79 | {
80 | $this->dispatcher = $dispatcher;
81 | }
82 |
83 | /**
84 | * @param $method
85 | * @param $class
86 | * @internal param array $dispatcher
87 | */
88 | public function addDispatcher($method, $class)
89 | {
90 | $this->dispatcher[$method] = $class;
91 | }
92 |
93 | /**
94 | * @return bool
95 | */
96 | public function match()
97 | {
98 | $this->request = [];
99 | for ($i = 0; $i < $this->router->collection->countRoutes; ++$i) {
100 | $this->request['prefix'] = ($this->router->collection->getRoutes('prefix_' . $i) != '') ? $this->router->collection->getRoutes('prefix_' . $i) : '';
101 | $this->request['subdomain'] = ($this->router->collection->getRoutes('subdomain_' . $i) != '') ? $this->router->collection->getRoutes('subdomain_' . $i) : '';
102 | foreach ($this->router->collection->getRoutes('routes_' . $i) as $route => $params) {
103 | $this->request['params'] = $params;
104 | $this->request['collection_index'] = $i;
105 | if ($this->checkSubdomain($route)) {
106 | $route = strstr($route, '/');
107 | $this->request['route'] = preg_replace_callback('#:([\w]+)#', [$this, 'paramMatch'], '/' . trim(trim($this->request['prefix'], '/') . '/' . trim($route, '/'), '/'));
108 | if ($this->routeMatch('#^' . $this->request['route'] . '$#')) {
109 | $this->setCallback();
110 | return $this->generateTarget();
111 | }
112 | }
113 | }
114 | }
115 | return false;
116 | }
117 |
118 | /**
119 | * @param $route
120 | * @return bool
121 | */
122 | private function checkSubdomain($route)
123 | {
124 | $url = (isset($_SERVER['REQUEST_SCHEME']) ? $_SERVER['REQUEST_SCHEME'] : 'http') . '://' . ($host = (isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : $_SERVER['HTTP_HOST']));
125 | $host = explode(':', $host)[0];
126 | $domain = $this->router->collection->getDomain($url);
127 | if (!empty($this->request['subdomain']) && $route[0] == '/') $route = trim($this->request['subdomain'], '.') . '.' . $domain . $route;
128 | if ($route[0] == '/') {
129 | return ($host != $domain) ? false : true;
130 | } elseif ($route[0] != '/' && $host != $domain) {
131 | $route = substr($route, 0, strpos($route, "/"));
132 | $route = str_replace('{host}', $domain, $route);
133 | $route = preg_replace_callback('#{subdomain}#', [$this, 'subdomainMatch'], $route);
134 | if (preg_match('#^' . $route . '$#', $host, $this->request['called_subdomain'])) {
135 | $this->request['called_subdomain'] = array_shift($this->request['called_subdomain']);
136 | $this->request['subdomain'] = str_replace('.' . $domain, '', $host);
137 | return true;
138 | }
139 | }
140 | return false;
141 | }
142 |
143 | /**
144 | * @return string
145 | */
146 | private function subdomainMatch()
147 | {
148 | if (is_array($this->request['params']) && isset($this->request['params']['subdomain'])) {
149 | return '(' . $this->request['params']['subdomain'] . ')';
150 | }
151 | return '([^/]+)';
152 | }
153 |
154 | /**
155 | * @param $match
156 | * @return string
157 | */
158 | private function paramMatch($match)
159 | {
160 | if (is_array($this->request['params']) && isset($this->request['params']['arguments'][$match[1]])) {
161 | $this->request['params']['arguments'][$match[1]] = str_replace('(', '(?:', $this->request['params']['arguments'][$match[1]]);
162 | return '(' . $this->request['params']['arguments'][$match[1]] . ')';
163 | }
164 | if(isset($this->router->collection->getRoutes('params_' . $this->request['collection_index'])['arguments'][$match[1]])){
165 | $this->request['params']['arguments'][$match[1]] = str_replace('(', '(?:', $this->router->collection->getRoutes('params_' . $this->request['collection_index'])['arguments'][$match[1]]);
166 | return '(' . $this->request['params']['arguments'][$match[1]] . ')';
167 | }
168 | return '([^/]+)';
169 | }
170 |
171 | /**
172 | * @param $regex
173 | * @return bool
174 | */
175 | private function routeMatch($regex)
176 | {
177 | $regex = (substr($this->request['route'], -1) == '*') ? '#^' . $this->request['route'] . '#' : $regex;
178 | if (preg_match($regex, $this->router->route->getUrl(), $this->request['parameters'])) {
179 | array_shift($this->request['parameters']);
180 | return true;
181 | }
182 | return false;
183 | }
184 |
185 | /**
186 | * @return bool
187 | */
188 | private function generateTarget()
189 | {
190 | if ($this->validMethod()) {
191 | foreach ($this->resolver as $resolver) {
192 | if (is_array($target = call_user_func_array([$this, $resolver], [$this->router->route->getCallback()]))) {
193 | $this->setTarget($target);
194 | return true;
195 | }
196 | }
197 | }
198 | $this->router->response->setStatusCode(405);
199 | return false;
200 | }
201 |
202 | /**
203 | * @param array $target
204 | */
205 | public function setTarget($target = [])
206 | {
207 | $index = isset($this->request['collection_index']) ? $this->request['collection_index'] : 0;
208 | $this->checkRequest('subdomain');
209 | $this->checkRequest('prefix');
210 | $this->router->route->setDetail($this->request);
211 | $this->router->route->setTarget($target);
212 | $this->router->route->addTarget('block', $this->router->collection->getRoutes('block_' . $index));
213 | $this->router->route->addTarget('view_dir', $this->router->collection->getRoutes('view_dir_' . $index));
214 | $this->router->route->addTarget('params', $this->router->collection->getRoutes('params_' . $index));
215 | }
216 |
217 | /**
218 | * @param $key
219 | */
220 | private function checkRequest($key)
221 | {
222 | if (strpos($this->request[$key], ':') !== false && isset($this->request['parameters'][0])) {
223 | $replacements = $this->request['parameters'];
224 | $keys = [];
225 | $this->request['@' . $key] = $this->request[$key];
226 | $this->request[$key] = preg_replace_callback('#:([\w?]+)#', function ($matches) use (&$replacements, &$keys) {
227 | $route_key = preg_replace("/[^A-Za-z0-9\\-_:]/", '', $matches[0]);
228 | $keys[$route_key] = isset($replacements[0]) ? $replacements[0] : null;
229 | return is_null($keys[$route_key]) ? '' : array_shift($replacements);
230 | }, $this->request[$key]);
231 | $this->request['keys'] = $keys;
232 | $this->request['parameters'] = $replacements;
233 | }
234 | }
235 |
236 | /**
237 | *
238 | */
239 | private function setCallback()
240 | {
241 | if (isset($this->request['params'])) {
242 | if (is_callable($this->request['params'])) {
243 | $this->router->route->setCallback($this->request['params']);
244 | } else {
245 | if (is_array($this->request['params']) && isset($this->request['params']['use'])) {
246 | if (is_array($this->request['params']['use']) && isset($this->request['params']['use'][$this->router->route->getMethod()])) {
247 | $this->router->route->setCallback($this->request['params']['use'][$this->router->route->getMethod()]);
248 | } elseif (!is_array($this->request['params']['use'])) {
249 | $this->router->route->setCallback($this->request['params']['use']);
250 | }
251 | } else {
252 | $this->router->route->setCallback($this->request['params']);
253 | }
254 | if (isset($this->request['params']['name'])) {
255 | $this->router->route->setName($this->request['params']['name']);
256 | }
257 | if (isset($this->request['params']['method'])) {
258 | $this->request['params']['method'] = is_array($this->request['params']['method']) ? $this->request['params']['method'] : [$this->request['params']['method']];
259 | }
260 | }
261 | }
262 | }
263 |
264 | /**
265 | * @return bool
266 | */
267 | public function validMethod()
268 | {
269 | if (is_callable($this->request['params'])) {
270 | return true;
271 | }
272 | if (!empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') {
273 | return (isset($this->request['params']['ajax']) && $this->request['params']['ajax'] === true) ? true : false;
274 | }
275 | $method = (isset($this->request['params']['method'])) ? $this->request['params']['method'] : ['GET'];
276 | return (in_array($this->router->route->getMethod(), $method)) ? true : false;
277 | }
278 |
279 | /**
280 | * @param $callback
281 | * @return array|bool
282 | * @throws \Exception
283 | */
284 | public function isClosureAndTemplate($callback)
285 | {
286 | if (is_array($cls = $this->isClosure($callback))) {
287 | if (is_array($this->request['params']) && isset($this->request['params']['template']) && is_array($tpl = $this->isTemplate($this->request['params']['template']))) {
288 | return array_merge(array_merge($cls, $tpl), [
289 | 'dispatcher' => $this->dispatcher['isClosureAndTemplate']
290 | ]);
291 | }
292 | return $cls;
293 | }
294 | return false;
295 | }
296 |
297 | /**
298 | * @param $callback
299 | * @return array|bool
300 | * @throws \Exception
301 | */
302 | public function isControllerAndTemplate($callback)
303 | {
304 | if (is_array($ctrl = $this->isController($callback))) {
305 | if (is_array($this->request['params']) && isset($this->request['params']['template']) && is_array($tpl = $this->isTemplate($this->request['params']['template']))) {
306 | return array_merge(array_merge($ctrl, $tpl), [
307 | 'dispatcher' => $this->dispatcher['isControllerAndTemplate']
308 | ]);
309 | }
310 | return $ctrl;
311 | }
312 | return false;
313 | }
314 |
315 |
316 | /**
317 | * @param $callback
318 | * @return bool|array
319 | */
320 | public function isClosure($callback)
321 | {
322 | if (is_callable($callback)) {
323 | return [
324 | 'dispatcher' => $this->dispatcher['isClosure'],
325 | 'closure' => $callback
326 | ];
327 | }
328 | return false;
329 | }
330 |
331 | /**
332 | * @param $callback
333 | * @throws \Exception
334 | * @return bool|array
335 | */
336 | public function isController($callback)
337 | {
338 | if (is_string($callback) && strpos($callback, '@') !== false) {
339 | $routes = explode('@', $callback);
340 | if (!isset($routes[1])) $routes[1] = 'index';
341 | if ($routes[1] == '{method}') {
342 | $params = explode('/', trim(preg_replace('#' . rtrim(str_replace('*', '', $this->request['route']), '/') . '#', '', $this->router->route->getUrl()), '/'));
343 | $routes[1] = empty($params[0]) ? 'index' : $params[0];
344 | $this->request['@method'] = $routes[1];
345 | array_shift($params);
346 | $this->request['parameters'] = array_merge($this->request['parameters'], $params);
347 | if (preg_match('/[A-Z]/', $routes[1])) return false;
348 | $routes[1] = lcfirst(str_replace(' ', '', ucwords(str_replace('-', ' ', $routes[1]))));
349 | }
350 | $index = isset($this->request['collection_index']) ? $this->request['collection_index'] : 0;
351 | $class = (class_exists($routes[0]))
352 | ? $routes[0]
353 | : $this->router->collection->getRoutes()['ctrl_namespace_' . $index] . $routes[0];
354 | if (method_exists($class, $routes[1])) {
355 | return [
356 | 'dispatcher' => $this->dispatcher['isController'],
357 | 'di' => $this->router->getConfig()['di'],
358 | 'controller' => $class,
359 | 'action' => $routes[1]
360 | ];
361 | }
362 | if (!strpos($callback, '{method}') !== false) {
363 | throw new \Exception('The required method "' . $routes[1] . '" is not found in "' . $class . '"');
364 | }
365 | }
366 | return false;
367 | }
368 |
369 | /**
370 | * @param $callback
371 | * @throws \Exception
372 | * @return bool|array
373 | */
374 | public function isTemplate($callback)
375 | {
376 | if (is_string($callback) && strpos($callback, '@') === false) {
377 | $replace = isset($this->request['@method']) ? str_replace('-', '_', $this->request['@method']) : 'index';
378 | $path = str_replace('{template}', $replace, trim($callback, '/'));
379 | $extension = substr(strrchr($path, "."), 1);
380 | $index = isset($this->request['collection_index']) ? $this->request['collection_index'] : 0;
381 | $viewDir = is_array($viewDir = $this->router->collection->getRoutes('view_dir_' . $index)) ? $viewDir : [$viewDir];
382 | $target = null;
383 | if (in_array('.' . $extension, $this->router->getConfig()['templateExtension'])){
384 | foreach ($viewDir as $dir) {
385 | if (is_file($fullPath = rtrim($dir, '/') . '/' . $path) || is_file($fullPath = $path)) $target = $fullPath;
386 | }
387 | }
388 | if(is_null($target)){
389 | foreach ($viewDir as $dir) {
390 | foreach ($this->router->getConfig()['templateExtension'] as $ext) {
391 | if (is_file($fullPath = rtrim($dir, '/') . '/' . $path . $ext) || is_file($fullPath = $path . $ext)) {
392 | $target = $fullPath;
393 | $extension = substr(strrchr($ext, "."), 1);
394 | break;
395 | }
396 | }
397 | }
398 | }
399 | return [
400 | 'dispatcher' => $this->dispatcher['isTemplate'],
401 | 'template' => $target,
402 | 'extension' => $extension,
403 | 'callback' => $this->router->getConfig()['templateCallback']
404 | ];
405 | }
406 | return false;
407 | }
408 |
409 | }
410 |
--------------------------------------------------------------------------------
/src/Routing/Matcher/MatcherInterface.php:
--------------------------------------------------------------------------------
1 | TemplateDispatcher::class,
36 | 'isController' => ControllerDispatcher::class,
37 | 'isControllerAndTemplate' => [ControllerDispatcher::class, TemplateDispatcher::class],
38 | ];
39 |
40 | /**
41 | * @param Router $router
42 | */
43 | public function __construct(Router $router)
44 | {
45 | $this->router = $router;
46 | }
47 |
48 | /**
49 | * @param array $resolver
50 | */
51 | public function setResolver($resolver = [])
52 | {
53 | $this->resolver = $resolver;
54 | }
55 |
56 | /**
57 | * @param string $resolver
58 | */
59 | public function addResolver($resolver)
60 | {
61 | $this->resolver[] = $resolver;
62 | }
63 |
64 | /**
65 | * @return array
66 | */
67 | public function getResolver()
68 | {
69 | return $this->resolver;
70 | }
71 |
72 | /**
73 | * @param array $dispatcher
74 | */
75 | public function setDispatcher($dispatcher = [])
76 | {
77 | $this->dispatcher = $dispatcher;
78 | }
79 |
80 | /**
81 | * @param $method
82 | * @param $class
83 | */
84 | public function addDispatcher($method, $class)
85 | {
86 | $this->dispatcher[$method] = $class;
87 | }
88 |
89 | /**
90 | * @return bool
91 | */
92 | public function match()
93 | {
94 | foreach ($this->resolver as $resolver) {
95 | if (is_array($target = call_user_func([$this, $resolver]))) {
96 | $this->setTarget($target);
97 | return true;
98 | }
99 | }
100 | return false;
101 | }
102 |
103 | /**
104 | * @param array $target
105 | */
106 | public function setTarget($target = [])
107 | {
108 | $index = isset($this->request['collection_index']) ? $this->request['collection_index'] : 0;
109 | $this->router->route->setDetail($this->request);
110 | $this->router->route->setTarget($target);
111 | $this->router->route->addTarget('block', $this->router->collection->getRoutes('block_' . $index));
112 | $this->router->route->addTarget('view_dir', $this->router->collection->getRoutes('view_dir_' . $index));
113 | $this->router->route->addTarget('params', $this->router->collection->getRoutes('params_' . $index));
114 | }
115 |
116 | /**
117 | * @return array|bool
118 | */
119 | public function isControllerAndTemplate()
120 | {
121 | if (is_array($ctrl = $this->isController())) {
122 | if (is_array($tpl = $this->isTemplate())) {
123 | return array_merge(array_merge($ctrl, $tpl), [
124 | 'dispatcher' => $this->dispatcher['isControllerAndTemplate']
125 | ]);
126 | }
127 | return $ctrl;
128 | }
129 | return $this->isTemplate();
130 | }
131 |
132 | /**
133 | * @return bool|array
134 | */
135 | public function isTemplate()
136 | {
137 | foreach ($this->router->getConfig()['templateExtension'] as $extension) {
138 | for ($i = 0; $i < $this->router->collection->countRoutes; ++$i) {
139 | $url = explode('/', str_replace($this->router->collection->getRoutes('prefix_' . $i), '', $this->router->route->getUrl()));
140 | $end = array_pop($url);
141 | $url = implode('/', array_map('ucwords', $url)) . '/' . $end;
142 | $viewDir = is_array($viewDir = $this->router->collection->getRoutes('view_dir_' . $i)) ? $viewDir : [$viewDir];
143 | foreach ($viewDir as $dir) {
144 | if (is_file(($template = rtrim($dir, '/') . $url . $extension))) {
145 | $this->request['collection_index'] = $i;
146 | return [
147 | 'dispatcher' => $this->dispatcher['isTemplate'],
148 | 'template' => $template,
149 | 'extension' => substr(strrchr($extension, "."), 1),
150 | 'callback' => $this->router->getConfig()['templateCallback']
151 | ];
152 | }
153 | }
154 | }
155 | }
156 | return false;
157 | }
158 |
159 | /**
160 | * @return bool|array
161 | */
162 | public function isController()
163 | {
164 | $routes = array_slice(explode('/', $this->router->route->getUrl()), 1);
165 | $i = 0;
166 | do {
167 | $route = ('/' . $routes[0] == $this->router->collection->getRoutes('prefix_' . $i)) ? array_slice($routes, 1) : $routes;
168 | if (isset($route[0])) {
169 | $class = (class_exists($this->router->collection->getRoutes('ctrl_namespace_' . $i) . ucfirst($route[0]) . 'Controller'))
170 | ? $this->router->collection->getRoutes('ctrl_namespace_' . $i) . ucfirst($route[0]) . 'Controller'
171 | : ucfirst($route[0]) . 'Controller';
172 | $route[1] = isset($route[1]) ? $route[1] : 'index';
173 | if (method_exists($class, $route[1])) {
174 | $this->request['parameters'] = array_slice($route, 2);
175 | $this->request['collection_index'] = $i;
176 | return [
177 | 'dispatcher' => $this->dispatcher['isController'],
178 | 'di' => $this->router->getConfig()['di'],
179 | 'controller' => $class,
180 | 'action' => $route[1]
181 | ];
182 | }
183 | }
184 | ++$i;
185 | } while ($i < $this->router->collection->countRoutes);
186 | return false;
187 | }
188 |
189 | }
190 |
--------------------------------------------------------------------------------
/src/Routing/Middleware.php:
--------------------------------------------------------------------------------
1 | router = $router;
45 | }
46 |
47 | /**
48 | * @return array
49 | */
50 | public function getMiddleware()
51 | {
52 | return $this->middleware;
53 | }
54 |
55 | /**
56 | * @param $action
57 | * @param $middleware
58 | * @return mixed|void
59 | */
60 | public function setCallbackAction($action, $middleware)
61 | {
62 | $this->setMiddleware($action, $middleware);
63 | }
64 |
65 | /**
66 | * @param $action
67 | * @param $middleware
68 | */
69 | private function setMiddleware($action, $middleware)
70 | {
71 | if (is_string($middleware)) {
72 | $middleware = rtrim($middleware, '/');
73 | }
74 | if (is_array($middleware)) {
75 | $this->middleware[$action] = $middleware;
76 | } elseif (is_file($middleware) && is_array($mid = include $middleware)) {
77 | $this->middleware[$action] = $mid;
78 | } else {
79 | throw new \InvalidArgumentException('Accepted argument for setMiddleware are array and array file');
80 | }
81 | }
82 |
83 | /**
84 | * @param $action
85 | * @return Router
86 | */
87 | public function getCallbacks($action)
88 | {
89 | return $action == 'after' ? array_reverse($this->callbacks) : $this->callbacks;
90 | }
91 |
92 | /**
93 | * @description global middleware
94 | * @param $action
95 | */
96 | public function globalMiddleware($action)
97 | {
98 | if (isset($this->middleware[$action]['global_middleware'])) {
99 | $this->callHandlers($this->middleware[$action]['global_middleware']);
100 | }
101 | }
102 |
103 | /**
104 | * @description block middleware
105 | * @param $action
106 | */
107 | public function blockMiddleware($action)
108 | {
109 | if (isset($this->middleware[$action]['block_middleware'])) {
110 | if (isset($this->middleware[$action]['block_middleware'][$this->router->route->getTarget('block')])) {
111 | $blocks = $this->middleware[$action]['block_middleware'][$this->router->route->getTarget('block')];
112 | $this->callHandlers($blocks);
113 | }
114 | }
115 | }
116 |
117 | /**
118 | * @description controller middleware
119 | * @param $action
120 | */
121 | public function classMiddleware($action)
122 | {
123 | if (isset($this->middleware[$action]['class_middleware'])) {
124 | $ctrl = str_replace('\\', '/', $this->router->route->getTarget('controller'));
125 | if (isset($this->middleware[$action]['class_middleware'][$ctrl]) && class_exists($this->router->route->getTarget('controller'))) {
126 | $classes = $this->middleware[$action]['class_middleware'][$ctrl];
127 | $this->callHandlers($classes);
128 | }
129 | }
130 | }
131 |
132 | /**
133 | * @description route middleware
134 | * @param $action
135 | */
136 | public function routeMiddleware($action)
137 | {
138 | if (isset($this->middleware[$action]['route_middleware'])) {
139 | if (isset($this->router->route->getPath()['middleware']) && class_exists($this->middleware[$action]['route_middleware'][$this->router->route->getPath()['middleware']])) {
140 | $classes = $this->middleware[$action]['route_middleware'][$this->router->route->getPath()['middleware']];
141 | $this->callHandlers($classes);
142 | }
143 | }
144 | }
145 |
146 | /**
147 | * @param $handlers
148 | * @param array $params
149 | */
150 | private function callHandlers($handlers, $params = []){
151 | $handlers = is_array($handlers) ? $handlers : [$handlers];
152 | foreach ($handlers as $handler) {
153 | if($this->next && $this->handle($handler, $params) !== true){
154 | break;
155 | }
156 | }
157 | }
158 |
159 | /**
160 | * @param $callback
161 | * @param array $params
162 | * @return mixed
163 | */
164 | private function handle($callback, $params = [])
165 | {
166 | $callback = explode('@', $callback);
167 | $response = true;
168 | $method = isset($callback[1]) ? $callback[1] : 'handle';
169 | if (class_exists($callback[0])) {
170 | $instance = call_user_func($this->router->getConfig()['di'], $callback[0]);
171 | if (method_exists($instance, $method)) {
172 | $reflectionMethod = new ReflectionMethod($instance, $method);
173 | $dependencies = $params;
174 | foreach ($reflectionMethod->getParameters() as $arg) {
175 | if (!is_null($arg->getClass())) {
176 | $dependencies[] = $this->getClass($arg->getClass()->name);
177 | }
178 | }
179 | $dependencies = array_merge($dependencies, [$this->router->route]);
180 | $response = $reflectionMethod->invokeArgs($instance, $dependencies);
181 | if(is_array($response) && isset($response['call'])){
182 | if(isset($response['response']) && $response['response'] instanceof ResponseInterface){
183 | $this->router->response = $response['response'];
184 | }
185 | $params = isset($response['params']) ? $response['params']: [];
186 | $this->callHandlers($response['call'], $params);
187 | $this->next = isset($response['next']) ? (bool)$response['next'] : false;
188 | } else if ($response instanceof ResponseInterface) {
189 | $this->router->response = $response;
190 | }
191 | }
192 | }
193 | return $response;
194 | }
195 |
196 | /**
197 | * @param $class
198 | * @return Route|RouteCollection|Router|mixed
199 | */
200 | private function getClass($class)
201 | {
202 | switch ($class) {
203 | case Route::class:
204 | return $this->router->route;
205 | case Router::class:
206 | return $this->router;
207 | case RouteCollection::class:
208 | return $this->router->collection;
209 | case ResponseInterface::class:
210 | return $this->router->response;
211 | default:
212 | return call_user_func_array($this->router->getConfig()['di'], [$class]);
213 | }
214 | }
215 | }
216 |
--------------------------------------------------------------------------------
/src/Routing/MiddlewareInterface.php:
--------------------------------------------------------------------------------
1 | 'Continue',
55 | 101 => 'Switching Protocols',
56 | 102 => 'Processing', // RFC2518
57 | 200 => 'OK',
58 | 201 => 'Created',
59 | 202 => 'Accepted',
60 | 203 => 'Non-Authoritative Information',
61 | 204 => 'No Content',
62 | 205 => 'Reset Content',
63 | 206 => 'Partial Content',
64 | 207 => 'Multi-Status', // RFC4918
65 | 208 => 'Already Reported', // RFC5842
66 | 226 => 'IM Used', // RFC3229
67 | 300 => 'Multiple Choices',
68 | 301 => 'Moved Permanently',
69 | 302 => 'Found',
70 | 303 => 'See Other',
71 | 304 => 'Not Modified',
72 | 305 => 'Use Proxy',
73 | 307 => 'Temporary Redirect',
74 | 308 => 'Permanent Redirect', // RFC7238
75 | 400 => 'Bad Request',
76 | 401 => 'Unauthorized',
77 | 402 => 'Payment Required',
78 | 403 => 'Forbidden',
79 | 404 => 'Not Found',
80 | 405 => 'Method Not Allowed',
81 | 406 => 'Not Acceptable',
82 | 407 => 'Proxy Authentication Required',
83 | 408 => 'Request Timeout',
84 | 409 => 'Conflict',
85 | 410 => 'Gone',
86 | 411 => 'Length Required',
87 | 412 => 'Precondition Failed',
88 | 413 => 'Payload Too Large',
89 | 414 => 'URI Too Long',
90 | 415 => 'Unsupported Media Type',
91 | 416 => 'Range Not Satisfiable',
92 | 417 => 'Expectation Failed',
93 | 418 => 'I\'m a teapot', // RFC2324
94 | 422 => 'Unprocessable Entity', // RFC4918
95 | 423 => 'Locked', // RFC4918
96 | 424 => 'Failed Dependency', // RFC4918
97 | 425 => 'Reserved for WebDAV advanced collections expired proposal', // RFC2817
98 | 426 => 'Upgrade Required', // RFC2817
99 | 428 => 'Precondition Required', // RFC6585
100 | 429 => 'Too Many Requests', // RFC6585
101 | 431 => 'Request Header Fields Too Large', // RFC6585
102 | 500 => 'Internal Server Error',
103 | 501 => 'Not Implemented',
104 | 502 => 'Bad Gateway',
105 | 503 => 'Service Unavailable',
106 | 504 => 'Gateway Timeout',
107 | 505 => 'HTTP Version Not Supported',
108 | 506 => 'Variant Also Negotiates (Experimental)', // RFC2295
109 | 507 => 'Insufficient Storage', // RFC4918
110 | 508 => 'Loop Detected', // RFC5842
111 | 510 => 'Not Extended', // RFC2774
112 | 511 => 'Network Authentication Required', // RFC6585
113 | );
114 |
115 | /**
116 | * Constructor.
117 | *
118 | * @param mixed $content The response content, see setContent()
119 | * @param int $status The response status code
120 | * @param array $headers An array of response headers
121 | *
122 | * @throws \InvalidArgumentException When the HTTP status code is not valid
123 | */
124 | public function __construct($content = '', $status = 200, $headers = array())
125 | {
126 | $this->headers = $headers;
127 | $this->setContent($content);
128 | $this->setStatusCode($status);
129 | $this->setProtocolVersion('1.0');
130 | }
131 |
132 |
133 | /**
134 | * Returns the Response as an HTTP string.
135 | *
136 | * The string representation of the Response is the same as the
137 | * one that will be sent to the client only if the prepare() method
138 | * has been called before.
139 | *
140 | * @return string The Response as an HTTP string
141 | *
142 | * @see prepare()
143 | */
144 | public function __toString()
145 | {
146 | return
147 | sprintf('HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText)."\r\n".
148 | $this->headers."\r\n".
149 | $this->getContent();
150 | }
151 |
152 | /**
153 | * Sends content for the current web response.
154 | *
155 | * @return Response
156 | */
157 | public function sendContent()
158 | {
159 | echo $this->content;
160 |
161 | return $this;
162 | }
163 |
164 | /**
165 | * @return $this
166 | */
167 | public function sendHeaders()
168 | {
169 | foreach($this->headers as $key => $content)
170 | header($key.' : '.$content);
171 | http_response_code($this->getStatusCode());
172 | return $this;
173 | }
174 |
175 | /**
176 | * Sends HTTP headers and content.
177 | *
178 | * @return Response
179 | */
180 | public function send()
181 | {
182 | $this->sendHeaders();
183 | $this->sendContent();
184 |
185 | if (function_exists('fastcgi_finish_request')) {
186 | fastcgi_finish_request();
187 | }
188 |
189 | return $this;
190 | }
191 |
192 | /**
193 | * @param array $headers
194 | */
195 | public function setHeaders($headers = [])
196 | {
197 | $this->headers = $headers;
198 | }
199 | /**
200 | * Sets the response content.
201 | *
202 | * Valid types are strings, numbers, null, and objects that implement a __toString() method.
203 | *
204 | * @param mixed $content Content that can be cast to string
205 | *
206 | * @return Response
207 | *
208 | * @throws \UnexpectedValueException
209 | */
210 | public function setContent($content)
211 | {
212 | if (null !== $content && !is_string($content) && !is_numeric($content) && !is_callable(array($content, '__toString'))) {
213 | throw new \UnexpectedValueException(sprintf('The Response content must be a string or object implementing __toString(), "%s" given.', gettype($content)));
214 | }
215 |
216 | $this->content = (string) $content;
217 |
218 | return $this;
219 | }
220 |
221 | /**
222 | * Gets the current response content.
223 | *
224 | * @return string Content
225 | */
226 | public function getContent()
227 | {
228 | return $this->content;
229 | }
230 |
231 | /**
232 | * Sets the HTTP protocol version (1.0 or 1.1).
233 | *
234 | * @param string $version The HTTP protocol version
235 | *
236 | * @return Response
237 | */
238 | public function setProtocolVersion($version)
239 | {
240 | $this->version = $version;
241 |
242 | return $this;
243 | }
244 |
245 | /**
246 | * Gets the HTTP protocol version.
247 | *
248 | * @return string The HTTP protocol version
249 | */
250 | public function getProtocolVersion()
251 | {
252 | return $this->version;
253 | }
254 |
255 | /**
256 | * Sets the response status code.
257 | *
258 | * @param int $code HTTP status code
259 | * @param mixed $text HTTP status text
260 | *
261 | * If the status text is null it will be automatically populated for the known
262 | * status codes and left empty otherwise.
263 | *
264 | * @return Response
265 | *
266 | * @throws \InvalidArgumentException When the HTTP status code is not valid
267 | */
268 | public function setStatusCode($code, $text = null)
269 | {
270 | $this->statusCode = $code = (int) $code;
271 | if ($this->isInvalid()) {
272 | throw new \InvalidArgumentException(sprintf('The HTTP status code "%s" is not valid.', $code));
273 | }
274 |
275 | if (null === $text) {
276 | $this->statusText = isset(self::$statusTexts[$code]) ? self::$statusTexts[$code] : 'unknown status';
277 |
278 | return $this;
279 | }
280 |
281 | if (false === $text) {
282 | $this->statusText = '';
283 |
284 | return $this;
285 | }
286 |
287 | $this->statusText = $text;
288 |
289 | return $this;
290 | }
291 |
292 | /**
293 | * Retrieves the status code for the current web response.
294 | *
295 | * @return int Status code
296 | */
297 | public function getStatusCode()
298 | {
299 | return $this->statusCode;
300 | }
301 |
302 | /**
303 | * Sets the response charset.
304 | *
305 | * @param string $charset Character set
306 | *
307 | * @return Response
308 | */
309 | public function setCharset($charset)
310 | {
311 | $this->charset = $charset;
312 |
313 | return $this;
314 | }
315 |
316 | /**
317 | * Retrieves the response charset.
318 | *
319 | * @return string Character set
320 | */
321 | public function getCharset()
322 | {
323 | return $this->charset;
324 | }
325 |
326 |
327 | // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
328 | /**
329 | * Is response invalid?
330 | *
331 | * @return bool
332 | */
333 | public function isInvalid()
334 | {
335 | return $this->statusCode < 100 || $this->statusCode >= 600;
336 | }
337 |
338 | /**
339 | * Is response informative?
340 | *
341 | * @return bool
342 | */
343 | public function isInformational()
344 | {
345 | return $this->statusCode >= 100 && $this->statusCode < 200;
346 | }
347 |
348 | /**
349 | * Is response successful?
350 | *
351 | * @return bool
352 | */
353 | public function isSuccessful()
354 | {
355 | return $this->statusCode >= 200 && $this->statusCode < 300;
356 | }
357 |
358 | /**
359 | * Is the response a redirect?
360 | *
361 | * @return bool
362 | */
363 | public function isRedirection()
364 | {
365 | return $this->statusCode >= 300 && $this->statusCode < 400;
366 | }
367 |
368 | /**
369 | * Is there a client error?
370 | *
371 | * @return bool
372 | */
373 | public function isClientError()
374 | {
375 | return $this->statusCode >= 400 && $this->statusCode < 500;
376 | }
377 |
378 | /**
379 | * Was there a server side error?
380 | *
381 | * @return bool
382 | */
383 | public function isServerError()
384 | {
385 | return $this->statusCode >= 500 && $this->statusCode < 600;
386 | }
387 |
388 | /**
389 | * Is the response OK?
390 | *
391 | * @return bool
392 | */
393 | public function isOk()
394 | {
395 | return 200 === $this->statusCode;
396 | }
397 |
398 | /**
399 | * Is the response forbidden?
400 | *
401 | * @return bool
402 | */
403 | public function isForbidden()
404 | {
405 | return 403 === $this->statusCode;
406 | }
407 |
408 | /**
409 | * Is the response a not found error?
410 | *
411 | * @return bool
412 | */
413 | public function isNotFound()
414 | {
415 | return 404 === $this->statusCode;
416 | }
417 |
418 | /**
419 | * Is the response a redirect of some form?
420 | *
421 | * @param string $location
422 | *
423 | * @return bool
424 | */
425 | public function isRedirect($location = null)
426 | {
427 | return in_array($this->statusCode, array(201, 301, 302, 303, 307, 308)) && (null === $location ?: $location == $this->headers->get('Location'));
428 | }
429 |
430 | /**
431 | * Is the response empty?
432 | *
433 | * @return bool
434 | */
435 | public function isEmpty()
436 | {
437 | return in_array($this->statusCode, array(204, 304));
438 | }
439 |
440 | }
441 |
--------------------------------------------------------------------------------
/src/Routing/ResponseInterface.php:
--------------------------------------------------------------------------------
1 | method = (isset($_SERVER['REQUEST_METHOD'])) ? $_SERVER['REQUEST_METHOD'] : 'GET';
47 | if ($this->method == 'POST' && isset($_SERVER['X-HTTP-METHOD-OVERRIDE'])) {
48 | $this->method = strtoupper($_SERVER['X-HTTP-METHOD-OVERRIDE']);
49 | }
50 | if (isset($_POST['_METHOD']) && in_array($_POST['_METHOD'], array('PUT', 'PATCH', 'HEAD', 'DELETE'))) {
51 | $this->method = $_POST['_METHOD'];
52 | }
53 | }
54 |
55 | /**
56 | * @param array $args
57 | */
58 | public function set($args = [])
59 | {
60 | if (isset($args['name'])) $this->name = $args['name'];
61 | if (isset($args['callback'])) $this->callback = $args['callback'];
62 | if (isset($args['target'])) $this->target = $args['target'];
63 | if (isset($args['detail'])) $this->detail = $args['detail'];
64 | }
65 |
66 | /**
67 | * @return null
68 | */
69 | public function getUrl()
70 | {
71 | return $this->url;
72 | }
73 |
74 | /**
75 | * @param $url
76 | */
77 | public function setUrl($url)
78 | {
79 | $this->url = $url;
80 | }
81 |
82 | /**
83 | * @return null
84 | */
85 | public function getName()
86 | {
87 | return $this->name;
88 | }
89 |
90 | /**
91 | * @param $name
92 | */
93 | public function setName($name)
94 | {
95 | $this->name = $name;
96 | }
97 |
98 | /**
99 | * @return mixed
100 | */
101 | public function getCallback()
102 | {
103 | return $this->callback;
104 | }
105 |
106 | /**
107 | * @param $callback
108 | */
109 | public function setCallback($callback)
110 | {
111 | $this->callback = $callback;
112 | }
113 |
114 | /**
115 | * @return array
116 | */
117 | public function getMethod()
118 | {
119 | return $this->method;
120 | }
121 |
122 | /**
123 | * @return array
124 | */
125 | public function getDetail()
126 | {
127 | return $this->detail;
128 | }
129 |
130 | /**
131 | * @param $detail
132 | */
133 | public function setDetail($detail)
134 | {
135 | $this->detail = array_merge($detail, $this->detail);
136 | }
137 |
138 | /**
139 | * @param $key
140 | * @param $value
141 | */
142 | public function addDetail($key, $value)
143 | {
144 | $this->detail[$key] = $value;
145 | }
146 |
147 | /**
148 | * @param null $key
149 | * @return array|string
150 | */
151 | public function getTarget($key = null)
152 | {
153 | if (!is_null($key))
154 | return isset($this->target[$key]) ? $this->target[$key] : '';
155 | return empty($this->target) ? '' : $this->target;
156 | }
157 |
158 | /**
159 | * @param $target
160 | */
161 | public function setTarget($target = [])
162 | {
163 | $this->target = $target;
164 | }
165 |
166 | /**
167 | * @param $key
168 | * @param $value
169 | */
170 | public function addTarget($key, $value)
171 | {
172 | $this->target[$key] = $value;
173 | }
174 |
175 | /**
176 | * @param null $key
177 | * @return bool
178 | */
179 | public function hasTarget($key = null)
180 | {
181 | if (!is_null($key))
182 | return isset($this->target[$key]) ? true : false;
183 | return empty($this->target) ? false : true;
184 | }
185 |
186 | /**
187 | * @return array
188 | */
189 | public function getData()
190 | {
191 | return (isset($this->getDetail()['data']) && is_array($this->getDetail()['data'])) ? $this->getDetail()['data'] : [];
192 | }
193 |
194 | /**
195 | * @param $name
196 | * @param $arguments
197 | * @return null
198 | */
199 | public function __call($name, $arguments)
200 | {
201 | if (substr($name, 0, 3) === "get") {
202 | $key = strtolower(str_replace('get', '', $name));
203 | return isset($this->detail[$key]) ? $this->detail[$key] : '';
204 | } elseif (substr($name, 0, 3) === "set") {
205 | $key = strtolower(str_replace('set', '', $name));
206 | $this->detail[$key] = $arguments[0];
207 | }
208 | return '';
209 | }
210 | }
211 |
--------------------------------------------------------------------------------
/src/Routing/RouteCollection.php:
--------------------------------------------------------------------------------
1 | addRoutes($routes, $options);
37 | }
38 |
39 | /**
40 | * @param array|string $routes
41 | * @param array $options
42 | */
43 | public function addRoutes($routes = null, $options = [])
44 | {
45 | if (!is_null($routes) && !is_array($routes)) {
46 | if (strpos($routes, '.php') === false) $routes = trim($routes, '/') . '/';
47 | if (is_file($routes . '/routes.php') && is_array($routesFile = include $routes . '/routes.php')) $routes = $routesFile;
48 | elseif (is_file($routes) && is_array($routesFile = include $routes)) $routes = $routesFile;
49 | else throw new \InvalidArgumentException('Argument for "' . get_called_class() . '" constructor is not recognized. Expected argument array or file containing array but "' . $routes . '" given');
50 | }
51 | $options['routes'] = is_array($routes) ? $routes : [];
52 | $this->setRoutes($options, $this->countRoutes);
53 | $this->countRoutes++;
54 | }
55 |
56 | /**
57 | * @param null $key
58 | * @return array
59 | */
60 | public function getRoutes($key = null)
61 | {
62 | return (!is_null($key))
63 | ? isset($this->routes[$key]) ? $this->routes[$key] : ''
64 | : $this->routes;
65 | }
66 |
67 | /**
68 | * @param $args
69 | */
70 | public function setPrefix($args)
71 | {
72 | if (is_array($args)) {
73 | $nbrArgs = count($args);
74 | for ($i = 0; $i < $nbrArgs; ++$i)
75 | $this->routes['prefix_' . $i] = '/' . trim($args[$i], '/');
76 | } elseif (is_string($args))
77 | for ($i = 0; $i < $this->countRoutes; ++$i)
78 | $this->routes['prefix_' . $i] = '/' . trim($args, '/');
79 | if ($this->countRoutes == 0) $this->countRoutes++;
80 | }
81 |
82 | /**
83 | * @param $args
84 | */
85 | public function setOption($args = [])
86 | {
87 | $nbrArgs = count($args);
88 | for ($i = 0; $i < $nbrArgs; ++$i) {
89 | if (is_array($args[$i])) {
90 | $this->setRoutes($args[$i], $i);
91 | if (!isset($this->routes['routes_' . $i])) $this->routes['routes_' . $i] = [];
92 | }
93 | }
94 | if ($this->countRoutes == 0) $this->countRoutes++;
95 | }
96 |
97 | /**
98 | * @param array $args
99 | * @param $i
100 | */
101 | public function setRoutes($args = [], $i)
102 | {
103 | $this->routes['routes_' . $i] = $args['routes'];
104 | $this->routes['block_' . $i] = (isset($args['block']) && !empty($args['block'])) ? rtrim($args['block'], '/') . '/' : '';
105 | $this->routes['view_dir_' . $i] = (isset($args['view_dir']) && !empty($args['view_dir'])) ? $args['view_dir'] : [];
106 | $this->routes['ctrl_namespace_' . $i] = (isset($args['ctrl_namespace']) && !empty($args['ctrl_namespace'])) ? trim($args['ctrl_namespace'], '\\') . '\\' : '';
107 | $this->routes['prefix_' . $i] = (isset($args['prefix']) && !empty($args['prefix'])) ? '/' . trim($args['prefix'], '/') : '';
108 | $this->routes['subdomain_' . $i] = (isset($args['subdomain'])) ? $args['subdomain'] : '';
109 | $this->routes['protocol_' . $i] = (isset($args['protocol'])) ? $args['protocol'] : 'http';
110 | $this->routes['params_' . $i] = (isset($args['params'])) ? $args['params'] : [];
111 | }
112 |
113 | /**
114 | * @param string $root
115 | * @param string $script_file
116 | * @return bool
117 | */
118 | public function generateRoutesPath($root = null, $script_file = 'index.php')
119 | {
120 | $protocol = isset($_SERVER['REQUEST_SCHEME']) ? $_SERVER['REQUEST_SCHEME'] : 'http';
121 | $domain = isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : null;
122 |
123 | if(!is_null($root)){
124 | $protocol = explode('://', $root);
125 | $protocol = $protocol[0];
126 | }else{
127 | $root = $protocol . '://' . $domain . ((!empty($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] != 80) ? ':' . $_SERVER['SERVER_PORT'] : '') . str_replace('/' . $script_file, '', $_SERVER['SCRIPT_NAME']);
128 | }
129 |
130 | $new_domain = $this->getDomain($root);
131 | if (!is_null($domain) && strpos($domain, $new_domain) !== false) {
132 | $root = str_replace($domain, $new_domain, $root);
133 | }
134 |
135 | $count = 0;
136 | for ($i = 0; $i < $this->countRoutes; ++$i) {
137 | $prefix = (isset($this->routes['prefix_' . $i])) ? $this->routes['prefix_' . $i] : '';
138 | $subdomain = (isset($this->routes['subdomain_' . $i])) ? $this->routes['subdomain_' . $i] : '';
139 | $block_protocol = (isset($this->routes['protocol_' . $i])) ? $this->routes['protocol_' . $i] : 'http';
140 | $url = (!empty($subdomain)) ? str_replace($protocol . '://', $block_protocol . '://' . $subdomain . '.', $root) : $root;
141 | if (isset($this->routes['routes_' . $i]))
142 | foreach ($this->routes['routes_' . $i] as $route => $dependencies) {
143 | if (is_array($dependencies) && isset($dependencies['use']) && !is_array($dependencies['use'])) {
144 | $use = (is_callable($dependencies['use'])) ? 'closure-' . $count : trim($dependencies['use'], '/');
145 | } elseif (!is_array($dependencies)) {
146 | $use = (is_callable($dependencies)) ? 'closure-' . $count : trim($dependencies, '/');
147 | } else {
148 | $use = $route;
149 | }
150 | if (isset($route[0]) && $route[0] == '/') {
151 | $full_url = rtrim($url, '/') . '/' . trim($prefix, '/') . (empty($prefix) ? '' : '/') . trim($route, '/');
152 | (!is_callable($dependencies) && isset($dependencies['name']))
153 | ? $this->routesByName[$use . '#' . $dependencies['name']] = $full_url
154 | : $this->routesByName[$use] = $full_url;
155 | } else {
156 | $full_url = $block_protocol . '://' . str_replace('{host}', $new_domain, $route);
157 | (!is_callable($dependencies) && isset($dependencies['name']))
158 | ? $this->routesByName[$use . '#' . $dependencies['name']] = $full_url . $prefix
159 | : $this->routesByName[$use] = $full_url . $prefix;
160 | }
161 | $count++;
162 | }
163 | }
164 | return true;
165 | }
166 |
167 | /**
168 | * @param $url
169 | * @return string
170 | */
171 | public function getDomain($url)
172 | {
173 | $url = parse_url($url);
174 | $domain = $url['host'];
175 | if (preg_match('/(?P[a-z0-9][a-z0-9\-]{1,63}\.[a-z\.]{2,6})$/i', $domain, $regs)) {
176 | return $regs['domain'];
177 | }
178 | return $domain;
179 | }
180 |
181 | /**
182 | * @param null $name
183 | * @param array $params
184 | * @return mixed
185 | */
186 | public function getRoutePath($name, $params = [])
187 | {
188 | foreach ($this->routesByName as $key => $route) {
189 | $param = explode('#', $key);
190 | if(isset($params['{subdomain}'])) $route = str_replace('*', $params['{subdomain}'], $route);
191 | if(isset($params['{method}'])) $route = str_replace('*', $params['{method}'], $route);
192 | foreach ($params as $key2 => $value) $route = str_replace(':' . $key2, $value, $route);
193 | if ($param[0] == trim($name, '/')) return $route;
194 | else if (isset($param[1]) && $param[1] == $name) return $route;
195 | }
196 | return null;
197 | }
198 | }
199 |
--------------------------------------------------------------------------------
/src/Routing/Router.php:
--------------------------------------------------------------------------------
1 | ['.html', '.php', '.json', '.xml'],
41 | 'templateCallback' => [],
42 | 'di' => '',
43 | 'generateRoutesPath' => false,
44 | ];
45 |
46 | /**
47 | * @param RouteCollection $collection
48 | * @param ResponseInterface $response
49 | * @param Route $route
50 | */
51 | public function __construct(RouteCollection $collection, ResponseInterface $response = null, Route $route = null)
52 | {
53 | $this->collection = $collection;
54 | $this->response = is_null($response) ? new Response() : $response;
55 | $this->route = is_null($route) ? new Route() : $route;
56 | $this->config['di'] = function ($class) {
57 | return new $class;
58 | };
59 | }
60 |
61 | /**
62 | * @param array $config
63 | */
64 | public function setConfig($config)
65 | {
66 | $this->config = array_merge($this->config, $config);
67 | }
68 |
69 | /**
70 | * @return array
71 | */
72 | public function getConfig()
73 | {
74 | return $this->config;
75 | }
76 |
77 | /**
78 | * @param object|array $middleware
79 | */
80 | public function setMiddleware($middleware)
81 | {
82 | $this->middlewareCollection = is_array($middleware)
83 | ? $middleware
84 | : [$middleware];
85 | }
86 |
87 | /**
88 | * @param MiddlewareInterface $middleware
89 | */
90 | public function addMiddleware(MiddlewareInterface $middleware)
91 | {
92 | $this->middlewareCollection[] = $middleware;
93 | }
94 |
95 | /**
96 | * @param object|array $matcher
97 | */
98 | public function setMatcher($matcher)
99 | {
100 | $this->matcher = is_array($matcher)
101 | ? $matcher
102 | : [$matcher];
103 | }
104 |
105 | /**
106 | * @param string $matcher
107 | */
108 | public function addMatcher($matcher)
109 | {
110 | $this->matcher[] = $matcher;
111 | }
112 |
113 | /**
114 | * @description main function
115 | */
116 | public function run()
117 | {
118 | $this->setUrl();
119 | if ($this->config['generateRoutesPath']) $this->collection->generateRoutesPath();
120 | if ($this->match() === true) {
121 | $this->callMiddleware('before');
122 | if (!in_array(substr($this->response->getStatusCode(), 0, 1), [3,4,5])) {
123 | $this->callTarget();
124 | }
125 | }else{
126 | $this->response->setStatusCode(404);
127 | }
128 | $this->callMiddleware('after');
129 | return $this->response->send();
130 | }
131 |
132 | /**
133 | * @description call the middleware before and after the target
134 | * @param $action
135 | */
136 | public function callMiddleware($action)
137 | {
138 | foreach ($this->middlewareCollection as $middleware) {
139 | if ($middleware instanceof MiddlewareInterface) {
140 | foreach ($middleware->getCallbacks($action) as $callback) {
141 | if (method_exists($middleware, $callback)) {
142 | call_user_func_array([$middleware, $callback], [$action]);
143 | }
144 | }
145 | }
146 | }
147 | }
148 |
149 | /**
150 | * @param null $url
151 | */
152 | public function setUrl($url = null)
153 | {
154 | if (is_null($url))
155 | $url = (isset($_GET['url'])) ? $_GET['url'] : substr(str_replace(str_replace('/index.php', '', $_SERVER['SCRIPT_NAME']), '', $_SERVER['REQUEST_URI']), 1);
156 | $this->route->setUrl('/' . trim(explode('?', $url)[0], '/'));
157 | }
158 |
159 | /**
160 | * @return bool
161 | */
162 | public function match()
163 | {
164 | foreach ($this->matcher as $key => $matcher) {
165 | if (call_user_func([$this->matcher[$key], 'match'])) return true;
166 | }
167 | return false;
168 | }
169 |
170 | /**
171 | * @description call the target for the request uri
172 | */
173 | public function callTarget()
174 | {
175 | $target = is_array($this->route->getTarget('dispatcher')) ? $this->route->getTarget('dispatcher') : [$this->route->getTarget('dispatcher')];
176 | if (!empty($target)) {
177 | foreach ($target as $call) {
178 | $this->dispatcher = new $call($this);
179 | call_user_func([$this->dispatcher, 'call']);
180 | }
181 | }
182 | }
183 | }
184 |
--------------------------------------------------------------------------------
/tests/app/Block1/Namespace1Controller.php:
--------------------------------------------------------------------------------
1 | 'JetFire'];
15 | }
16 | }
--------------------------------------------------------------------------------
/tests/app/Block1/Views/Smart/index1.html:
--------------------------------------------------------------------------------
1 | Smart1
--------------------------------------------------------------------------------
/tests/app/Block1/Views/contact.php:
--------------------------------------------------------------------------------
1 | Contact1
--------------------------------------------------------------------------------
/tests/app/Block1/Views/index.html:
--------------------------------------------------------------------------------
1 | Hello1
--------------------------------------------------------------------------------
/tests/app/Block1/Views/log.php:
--------------------------------------------------------------------------------
1 | = $name ?>
--------------------------------------------------------------------------------
/tests/app/Block1/Views/user.html:
--------------------------------------------------------------------------------
1 | User1
--------------------------------------------------------------------------------
/tests/app/Block1/routes.php:
--------------------------------------------------------------------------------
1 | 'index',
6 |
7 | '/user1-:id' => [
8 | 'use' => 'user.html',
9 | 'arguments' => ['id' => '[0-9]+']
10 | ],
11 |
12 | '/home1' => [
13 | 'use' => 'JetFire\Routing\App\Block1\Namespace1Controller@index',
14 | ],
15 |
16 | '/home-:id' => [
17 | 'use' => 'Namespace1Controller@index2',
18 | 'arguments' => ['id' => '[0-9]+']
19 | ],
20 |
21 | '/contact1' => [
22 | 'use' => 'Normal1Controller@contact',
23 | 'template' => 'contact.php',
24 | 'name' => 'contact'
25 | ],
26 |
27 | '/log' => [
28 | 'use' => 'Normal1Controller@log',
29 | 'template' => 'log.php',
30 | 'name' => 'log'
31 | ],
32 |
33 | '/search1-:id-:name' => [
34 | 'use' => function ($id, $name) {
35 | echo 'Search' . $id . $name;
36 | },
37 | 'arguments' => ['id' => '[0-9]+', 'name' => '[a-z]*'],
38 | ],
39 | ];
--------------------------------------------------------------------------------
/tests/app/Block2/Controllers/Namespace2Controller.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/app/Block2/routes.php:
--------------------------------------------------------------------------------
1 | 'index.html',
6 |
7 | '/user2-:id' => [
8 | 'use' => 'user.html',
9 | 'arguments' => ['id' => '[0-9]+']
10 | ],
11 |
12 | '/log2' => [
13 | 'use' => function(){
14 | return ['name' => 'JetFire'];
15 | },
16 | 'template' => 'log.php'
17 | ],
18 |
19 | '/home2' => [
20 | 'use' => 'JetFire\Routing\App\Block2\Controllers\Namespace2Controller@index',
21 | ],
22 |
23 | '/home-:id' => [
24 | 'use' => 'Namespace2Controller@index2',
25 | 'arguments' => ['id' => '[0-9]+']
26 | ],
27 |
28 | '/contact2' => [
29 | 'use' => 'Normal2Controller@contact',
30 | 'name' => 'contact'
31 | ],
32 |
33 | '/search2' => [
34 | 'use' => 'Normal2Controller@search',
35 | 'method' => 'POST',
36 | 'name' => 'search'
37 | ],
38 |
39 | '/api/users' => [
40 | 'use' => [
41 | 'GET' => function($response){
42 | $response->setStatusCode(500);
43 | }
44 | ]
45 | ],
46 | '/api/users/1' => [
47 | 'use' => [
48 | 'GET' => function($response){
49 | $response->setHeaders(['Content-Type' => 'application/json']);
50 | return ['name' => 'Peter'];
51 | }
52 | ]
53 | ]
54 | ];
--------------------------------------------------------------------------------
/tests/app/Block2/user.html:
--------------------------------------------------------------------------------
1 | User2
--------------------------------------------------------------------------------
/tests/app/Config/middleware.inc.php:
--------------------------------------------------------------------------------
1 | [
5 |
6 | ],
7 |
8 | 'block_middleware' => [
9 |
10 | ],
11 |
12 | 'class_middleware' => [
13 | 'JetFire/Routing/Test/Controllers/NormalController' => '',
14 | ],
15 |
16 | 'route_middleware' => [
17 | 'home' => ''
18 | ],
19 |
20 | ];
--------------------------------------------------------------------------------
/tests/app/Config/routes.php:
--------------------------------------------------------------------------------
1 | 'index.html',
6 |
7 | '/user-:id' => [
8 | 'use' => 'user.html',
9 | 'arguments' => ['id' => '[0-9]+']
10 | ],
11 |
12 | '/home' => [
13 | 'use' => 'JetFire\Routing\App\Controllers\NamespaceController@index',
14 | ],
15 |
16 | '/home-:id' => [
17 | 'use' => 'NamespaceController@index2',
18 | 'arguments' => ['id' => '[0-9]+']
19 | ],
20 |
21 | '/contact' => [
22 | 'use' => 'NormalController@contact',
23 | 'name' => 'contact'
24 | ],
25 |
26 | '/search' => [
27 | 'use' => 'NormalController@search',
28 | 'method' => 'POST',
29 | 'name' => 'search'
30 | ],
31 |
32 | '/log' => function(){
33 | echo 'yes';
34 | }
35 | ];
--------------------------------------------------------------------------------
/tests/app/Controllers/AppController.php:
--------------------------------------------------------------------------------
1 | addRoutes(ROOT . '/Config/routes.php', [
30 | 'view_dir' => ROOT . '/Views',
31 | 'ctrl_namespace' => 'JetFire\Routing\App\Controllers',
32 | ]);
33 | $collection->addRoutes(ROOT . '/Block1/routes.php', [
34 | 'view_dir' => ROOT . '/Block1/Views',
35 | 'ctrl_namespace' => 'JetFire\Routing\App\Block1',
36 | 'prefix' => 'block1'
37 | ]);
38 | $collection->addRoutes(ROOT . '/Block2/routes.php', [
39 | 'view_dir' => ROOT . '/Block2/',
40 | 'ctrl_namespace' => 'JetFire\Routing\App\Block2\Controllers'
41 | ]);
42 | $_SERVER['REQUEST_METHOD'] = 'GET';
43 | $router = new Router($collection);
44 | $router->addMatcher(new ArrayMatcher($router));
45 | $router->addMatcher(new UriMatcher($router));
46 | $this->middleware = new Middleware($router);
47 | }
48 |
49 |
50 | /**
51 | * @expectedException InvalidArgumentException
52 | */
53 | public function testInvalidMiddleware(){
54 | $this->middleware->setCallbackAction('before', '/Config/middleware.inc.php');
55 | }
56 |
57 | /**
58 | *
59 | */
60 | public function testMiddleware(){
61 | $this->middleware->setCallbackAction('before', ROOT.'/Config/middleware.inc.php');
62 | $this->assertTrue(is_array($this->middleware->getMiddleware()['before']));
63 | }
64 |
65 | }
--------------------------------------------------------------------------------
/tests/src/Routing/RouteCollectionTest.php:
--------------------------------------------------------------------------------
1 | collection = new RouteCollection();
18 | }
19 |
20 | /**
21 | * @expectedException InvalidArgumentException
22 | */
23 | public function testInvalidRoutes(){
24 | $this->collection->addRoutes('routes.php');
25 | }
26 |
27 | public function testCountRoutes(){
28 | $this->collection->addRoutes(ROOT.'/Config/routes.php',['prefix'=>'/public']);
29 | $this->collection->addRoutes([
30 | '/page-1' => 'index.html',
31 | '/page-2' => function(){
32 | return 'Hello !';
33 | }
34 | ]);
35 | $this->assertEquals(2,$this->collection->countRoutes);
36 | return $this->collection;
37 | }
38 |
39 | /**
40 | * @depends testCountRoutes
41 | * @param RouteCollection $collection
42 | */
43 | public function testGetRoutes(RouteCollection $collection){
44 | $this->assertArrayHasKey('view_dir_0',$collection->getRoutes());
45 | $this->assertEquals('/public',$collection->getRoutes('prefix_0'));
46 | $this->assertEquals('',$collection->getRoutes('prefix_1'));
47 | }
48 |
49 | public function testSetPrefix(){
50 | $this->collection->addRoutes(ROOT.'/Config/routes.php');
51 | $this->collection->addRoutes([
52 | '/page-1' => 'index.html',
53 | '/page-2' => function(){
54 | return 'Hello !';
55 | }
56 | ]);
57 | $this->collection->setPrefix('public');
58 | $this->assertEquals('/public',$this->collection->getRoutes('prefix_0'));
59 | $this->assertEquals('/public',$this->collection->getRoutes('prefix_1'));
60 | $this->collection->setPrefix(['public','user']);
61 | $this->assertEquals('/public',$this->collection->getRoutes('prefix_0'));
62 | $this->assertEquals('/user',$this->collection->getRoutes('prefix_1'));
63 | }
64 |
65 | /**
66 | * @depends testCountRoutes
67 | * @param RouteCollection $collection
68 | * @return \JetFire\Routing\RouteCollection
69 | */
70 | public function testGenerateRoutes(RouteCollection $collection){
71 | $_SERVER['SERVER_NAME'] = 'localhost';
72 | $_SERVER['SCRIPT_NAME'] = '';
73 | $this->assertTrue($collection->generateRoutesPath());
74 | return $collection;
75 | }
76 |
77 | /**
78 | * @depends testGenerateRoutes
79 | * @param RouteCollection $collection
80 | */
81 | public function testGetPath(RouteCollection $collection){
82 | $this->assertEquals('http://localhost/public/contact',$collection->getRoutePath('contact'));
83 | $this->assertNotEquals('http://localhost/contact',$collection->getRoutePath('search'));
84 | }
85 | }
--------------------------------------------------------------------------------
/tests/src/Routing/RouterTest.php:
--------------------------------------------------------------------------------
1 | addRoutes(ROOT . '/Config/routes.php', [
29 | 'view_dir' => ROOT . '/Views',
30 | 'ctrl_namespace' => 'JetFire\Routing\App\Controllers',
31 | ]);
32 | $collection->addRoutes(ROOT . '/Block1/routes.php', [
33 | 'view_dir' => ROOT . '/Block1/Views',
34 | 'ctrl_namespace' => 'JetFire\Routing\App\Block1',
35 | 'prefix' => 'block1'
36 | ]);
37 | $collection->addRoutes(ROOT . '/Block2/routes.php', [
38 | 'view_dir' => ROOT . '/Block2/',
39 | 'ctrl_namespace' => 'JetFire\Routing\App\Block2\Controllers'
40 | ]);
41 | $_SERVER['REQUEST_METHOD'] = 'GET';
42 | $this->router = new Router($collection);
43 | $this->router->addMatcher(new ArrayMatcher($this->router));
44 | $this->router->addMatcher(new UriMatcher($this->router));
45 | }
46 |
47 | /**
48 | * @return array
49 | */
50 | public function uriMatchWithoutRoutesProvider()
51 | {
52 | return array(
53 | array(ROOT . '/Views', 'JetFire\Routing\App\Controllers', '/app/index', 'Index', ''),
54 | array(ROOT . '/Views', 'JetFire\Routing\App\Controllers', '/smart/index', 'Smart', ''),
55 | array(ROOT . '/Block1/Views', 'JetFire\Routing\App\Block1', '/smart/index1', 'Smart1', ''),
56 | array(ROOT . '/Block1/Views', 'JetFire\Routing\App\Block1', '/block1/namespace1/index', 'Index1', 'block1'),
57 | array(ROOT . '/Block2', 'JetFire\Routing\App\Block2\Controllers', '/smart/index2', 'Smart2', ''),
58 | array(ROOT . '/Block2', 'JetFire\Routing\App\Block2\Controllers', '/normal2/contact', 'Contact2', ''),
59 | array(ROOT . '/Views', 'JetFire\Routing\App\Controllers', '/app/namespace/index', 'Index', 'app'),
60 | );
61 | }
62 |
63 | /**
64 | * @dataProvider uriMatchWithoutRoutesProvider
65 | * @param $path
66 | * @param $namespace
67 | * @param $url
68 | * @param $output
69 | * @param $prefix
70 | */
71 | public function testUriMatchWithoutRoutes($path, $namespace, $url, $output, $prefix)
72 | {
73 | $collection = new RouteCollection(null, [
74 | 'view_dir' => $path,
75 | 'ctrl_namespace' => $namespace,
76 | 'prefix' => $prefix
77 | ]);
78 | $this->router = new Router($collection);
79 | $this->router->addMatcher(new UriMatcher($this->router));
80 | $this->router->setUrl($url);
81 | $this->assertTrue($this->router->match());
82 | $this->router->callTarget();
83 | $this->router->response->sendContent();
84 | $this->expectOutputString($output);
85 | }
86 |
87 | /**
88 | * @return array
89 | */
90 | public function uriMatchTemplate()
91 | {
92 | return array(
93 | array('/smart/index', 'Smart'),
94 | array('/block1/smart/index1', 'Smart1'),
95 | array('/smart/index2', 'Smart2'),
96 | );
97 | }
98 |
99 | /**
100 | * @dataProvider uriMatchTemplate
101 | * @param $url
102 | * @param $output
103 | */
104 | public function testUriMatchTemplate($url, $output)
105 | {
106 | $this->router->setUrl($url);
107 | $this->assertTrue($this->router->match());
108 | $this->router->callTarget();
109 | $this->router->response->sendContent();
110 | $this->expectOutputString($output);
111 | }
112 |
113 | /**
114 | * @return array
115 | */
116 | public function uriMatchController()
117 | {
118 | return array(
119 | array('/normal/contact', 'Contact'),
120 | array('/block1/normal1/contact', 'Contact1'),
121 | array('/normal2/contact', 'Contact2'),
122 | array('/namespace/index', 'Index'),
123 | array('/namespace1/index', 'Index1'),
124 | array('/namespace2/index', 'Index2'),
125 | );
126 | }
127 |
128 | /**
129 | * @dataProvider uriMatchController
130 | * @param $url
131 | * @param $output
132 | */
133 | public function testUriMatchController($url, $output)
134 | {
135 | $this->router->setUrl($url);
136 | $this->assertTrue($this->router->match());
137 | $this->router->callTarget();
138 | $this->router->response->sendContent();
139 | $this->expectOutputString($output);
140 | }
141 |
142 | /**
143 | * @return array
144 | */
145 | public function matchTemplate()
146 | {
147 | return array(
148 | array('/index', 'Hello'),
149 | array('/block1/index1', 'Hello1'),
150 | array('/index2', 'Hello2'),
151 | array('/user-1', 'User'),
152 | array('/block1/user1-1', 'User1'),
153 | array('/user2-1', 'User2'),
154 | );
155 | }
156 |
157 | /**
158 | * @dataProvider matchTemplate
159 | * @param $url
160 | * @param $output
161 | */
162 | public function testMatchTemplate($url, $output)
163 | {
164 | $this->router->setUrl($url);
165 | $this->assertTrue($this->router->match());
166 | $this->router->callTarget();
167 | $this->router->response->sendContent();
168 | $this->expectOutputString($output);
169 | }
170 |
171 | /**
172 | * @return array
173 | */
174 | public function matchController()
175 | {
176 | return array(
177 | array('/home', 'Index'),
178 | array('/block1/home1', 'Index1'),
179 | array('/home2', 'Index2'),
180 | array('/home-1', 'Index1'),
181 | array('/block1/home-2', 'Index2'),
182 | array('/home-3', 'Index3'),
183 | array('/contact', 'Contact'),
184 | array('/block1/contact1', 'Contact1Contact1'),
185 | array('/block1/log', 'JetFire'),
186 | array('/log2', 'JetFire'),
187 | array('/contact2', 'Contact2'),
188 | );
189 | }
190 |
191 | /**
192 | * @dataProvider matchController
193 | * @param $url
194 | * @param $output
195 | */
196 | public function testMatchController($url, $output)
197 | {
198 | $this->router->setUrl($url);
199 | $this->assertTrue($this->router->match());
200 | $this->router->callTarget();
201 | $this->router->response->sendContent();
202 | $this->expectOutputString($output);
203 | }
204 |
205 | /**
206 | *
207 | */
208 | public function testPostResponseMethod()
209 | {
210 | $collection = new RouteCollection();
211 | $collection->addRoutes(ROOT . '/Config/routes.php', [
212 | 'view_dir' => ROOT . '/Views',
213 | 'ctrl_namespace' => 'JetFire\Routing\App\Controllers',
214 | ]);
215 | $_SERVER['REQUEST_METHOD'] = 'POST';
216 | $router = new Router($collection);
217 | $router->addMatcher(new ArrayMatcher($router));
218 | $router->setUrl('/search');
219 | $this->assertTrue($router->match());
220 | $router->callTarget();
221 | $router->response->sendContent();
222 | $this->assertEquals('POST', $router->route->getMethod());
223 | }
224 |
225 | /**
226 | *
227 | */
228 | public function testGetResponseMethod()
229 | {
230 | $collection = new RouteCollection();
231 | $collection->addRoutes(ROOT . '/Config/routes.php', [
232 | 'view_dir' => ROOT . '/Views',
233 | 'ctrl_namespace' => 'JetFire\Routing\App\Controllers',
234 | ]);
235 | $router = new Router($collection);
236 | $router->addMatcher(new ArrayMatcher($router));
237 | $_SERVER['REQUEST_METHOD'] = 'GET';
238 | $router->setUrl('/search');
239 | $this->assertFalse($router->match());
240 | $this->assertEquals(405, $router->response->getStatusCode());
241 | }
242 |
243 | /**
244 | *
245 | */
246 | public function testClosureWithParameters()
247 | {
248 | $this->router->setUrl('/block1/search1-3-peter');
249 | $this->assertTrue($this->router->match());
250 | $this->router->callTarget();
251 | $this->router->response->sendContent();
252 | $this->expectOutputString('Search3peter');
253 | }
254 |
255 | /**
256 | *
257 | */
258 | public function testClosure()
259 | {
260 | $this->router->setUrl('/log');
261 | $this->assertTrue($this->router->match());
262 | $this->router->callTarget();
263 | $this->router->response->sendContent();
264 | $this->expectOutputString('yes');
265 | }
266 |
267 | /**
268 | *
269 | */
270 | public function testApi()
271 | {
272 | $this->router->setUrl('/api/users');
273 | $this->assertTrue($this->router->match());
274 | $this->router->callTarget();
275 | $this->assertEquals(500, $this->router->response->getStatusCode());
276 |
277 | $this->router->response->setStatusCode(200);
278 | $this->router->setUrl('/api/users/1');
279 | $this->assertTrue($this->router->match());
280 | $this->router->callTarget();
281 | $this->assertEquals('application/json', $this->router->response->headers['Content-Type']);
282 | $this->router->response->sendContent();
283 | $this->expectOutputString('{"name":"Peter"}');
284 | }
285 |
286 | /**
287 | *
288 | */
289 | public function testResponse()
290 | {
291 | $this->router->setUrl('/notfound');
292 | $this->assertFalse($this->router->match());
293 | }
294 |
295 | }
--------------------------------------------------------------------------------