Compare commits

...

280 Commits

Author SHA1 Message Date
Simon Sessingø 0090c167bb Merge pull request #287 from skipperbent/v3-development
Response->json now accepts either array or \JsonSerializable (issue: #284)
2017-08-31 13:05:14 +02:00
Simon Sessingø ae68598024 Response->json now accepts either array or \JsonSerializable (issue: 2017-08-31 12:03:17 +01:00
Simon Sessingø 7362b748af Merge pull request #282 from skipperbent/v3-development
V3 development
2017-08-24 16:53:20 +02:00
Simon Sessingø 4fe85d6568 Merge pull request #281 from skipperbent/v3-fix-urldecode
Urldecode is now used on all urls in Uri class (issue: #268)
2017-08-24 16:53:04 +02:00
Simon Sessingø c82d91375c Urldecode is now used on all urls in Uri class (issue: #268) 2017-08-24 15:50:13 +01:00
Simon Sessingø e774122b1e Merge pull request #279 from skipperbent/v3-development
V3 development
2017-08-24 03:12:57 +02:00
Simon Sessingø e8aceb291c Merge pull request #278 from skipperbent/v3-fix-iis-encoding
Ensured that Request class is using unencoded-url header to avoid encoding issues on IIS (issue: #268)
2017-08-24 03:12:48 +02:00
Simon Sessingø 957a382248 Ensured that Request class is using unencoded-url header to avoid encoding issues on IIS (issue: #268) 2017-08-24 02:05:18 +01:00
Simon Sessingø 48e2e3f9bc Merge pull request #276 from skipperbent/v3-development
V3 development
2017-08-23 23:50:18 +02:00
Simon Sessingø f95e12c49c Merge pull request #275 from skipperbent/fix-unicode
Added unicode parameter regex support.
2017-08-23 23:50:04 +02:00
Simon Sessingø 9f509ac818 Added unicode parameter regex support. 2017-08-23 22:49:35 +01:00
Simon Sessingø 3b4d23e9ae Merge pull request #273 from skipperbent/v3-development
Version 3.4
2017-08-23 22:30:41 +02:00
Simon Sessingø b3e99a2283 Merge pull request #267 from skipperbent/fix-url
Fixed: return / if all parameters are empty in getUrl method.
2017-08-23 22:17:15 +02:00
Simon Sessingø c1aa5782a1 Merge pull request #271 from skipperbent/v3-feature-uri
Added Uri class which can be used to parse urls.
2017-08-23 22:17:04 +02:00
Simon Sessingø 387d9710d8 Merge pull request #272 from jatubio/patch-1
Add IIS web.config file example
2017-08-23 22:16:53 +02:00
Simon Sessingø fd098266f7 Merge pull request #266 from zaingithub/patch-1
Update README.md
2017-08-23 22:16:24 +02:00
Simon Sessingø c7473938c5 Changed xxx to web.config 2017-08-23 22:09:55 +02:00
Simon Sessingø 65c811356d Fixed url parsing for unicode characters. 2017-08-23 21:04:11 +01:00
Simon Sessingø ea255baec3 Merge branch 'v3-fix-url-encoding' into v3-feature-uri 2017-08-23 20:44:57 +01:00
Simon Sessingø acf37c4023 Updated Uri class. 2017-08-23 20:41:49 +01:00
Juan Antonio Tubio 6fb92d7cae Add IIS web.config file example 2017-08-23 17:28:14 +02:00
Simon Sessingø e84c8c2f02 Uri is now always urldecoded in Request class to ensure compability with
most webservers (issue: #268).
2017-08-23 16:13:44 +01:00
Simon Sessingø 7c970c442c Added Uri class which can be used to parse urls. 2017-08-23 16:10:15 +01:00
Simon Sessingø 4cd1e8e069 Fixed: return / if all parameters are empty in $router->getUrl method. 2017-08-22 17:18:48 +01:00
zaingithub 8720e732e1 Update README.md
Update section : ## Handling 404, 403 and other errors

$exception->getCode to $exception->getCode()
2017-08-20 12:47:27 +07:00
Simon Sessingø 7e8cb91f68 Updated documentation 2017-08-19 16:21:55 +01:00
Simon Sessingø bc67038e11 Added helpers.php file
- Added updated helpers.php file
- Updated helpers section in documentation.
2017-08-19 13:48:47 +01:00
Simon Sessingø e31d8af2f7 Merge pull request #264 from skipperbent/v3
V3
2017-08-18 20:32:33 +02:00
Simon Sessingø 9f285fd0ce Merge pull request #263 from skipperbent/v3-development
V3 development
2017-08-18 20:32:21 +02:00
Simon Sessingø b7f5d31544 Fixed double / when calling findUrl on routes with the first
parameter optional.
2017-08-17 23:31:27 +01:00
Simon Sessingø 5086347802 Bugfix: fixed using empty value in url still parsing current
parameters.
2017-08-13 18:01:48 +01:00
Simon Sessingø 26f89213a2 Merge pull request #260 from skipperbent/v3
V3
2017-08-13 04:14:17 +02:00
Simon Sessingø bb7991afdc Merge pull request #259 from skipperbent/v3-development
Version 3.3.5
2017-08-13 04:14:04 +02:00
Simon Sessingø 796d01bc25 Merge pull request #258 from skipperbent/v3-fixes
Fixes and development
2017-08-13 04:13:40 +02:00
Simon Sessingø 74187ee326 Fixes and development
- `$parameter` argument in `findUrl` method now supports both string,
array and null value to avoid confusion.

- Using return value in callbacks now displays value (issue: #257)
2017-08-13 03:06:27 +01:00
Simon Sessingø f090d6c038 Merge pull request #256 from skipperbent/v3
V3
2017-08-07 22:23:03 +02:00
Simon Sessingø 6e859e11ab Merge pull request #255 from skipperbent/v3-development
V3 development
2017-08-07 22:22:34 +02:00
Simon Sessingø 6aa38cfa4c Merge pull request #254 from skipperbent/v3-url-match-fix
Fixed wrong url matching on some paths (issue: #253 - thanks @alejoloe007jb)
2017-08-07 22:20:32 +02:00
Simon Sessingø 62f0075cf3 Fixed wrong url matching on some paths (issue: #253 - thanks @alejoloe007jb) 2017-08-07 22:18:36 +02:00
Simon Sessingø 6890b60737 Merge pull request #252 from skipperbent/v3
V3
2017-08-01 21:30:52 +02:00
Simon Sessingø a40f81d5fc Merge pull request #251 from skipperbent/v3-development
V3 development
2017-08-01 21:30:42 +02:00
Simon Sessingø a5527a0e8c Merge pull request #250 from skipperbent/bugfix-urlmatch
Bugfix urlmatch
2017-08-01 21:30:13 +02:00
Simon Sessingø 3c73de866e Added code-comments. 2017-08-01 21:25:44 +02:00
Simon Sessingø 883d8a6b0e Fixes for issue #248 2017-08-01 21:09:18 +02:00
Simon Sessingø 3a7b27796a Url matching fixes 2017-08-01 18:29:47 +02:00
Simon Sessingø 1e07140865 Merge pull request #247 from skipperbent/v3
V3
2017-07-18 11:28:33 +02:00
Simon Sessingø 4cca5186f3 Merge pull request #246 from skipperbent/v3-development
V3 development
2017-07-18 11:28:19 +02:00
Simon Sessingø 5437420175 Merge pull request #245 from skipperbent/php5-5-compatibility
Fixed PHP 5.5 compatibility (issue: #244 - thanks @thexeos)
2017-07-18 11:27:59 +02:00
Simon Sessingø 410fa3c9f5 Fixed PHP 5.5 compatibility (issue: #244 - thanks @thexeos) 2017-07-18 11:26:45 +02:00
Simon Sessingø be20fe4dd1 Updated composer.json tags. 2017-07-08 12:20:49 +02:00
Simon Sessingø bc03490100 Merge pull request #243 from skipperbent/v3
V3
2017-07-08 12:03:42 +02:00
Simon Sessingø 0947f6746e Merge pull request #242 from skipperbent/v3-development
V3 development
2017-07-08 12:03:05 +02:00
Simon Sessingø 31c8710ce7 Merge pull request #241 from skipperbent/v-3-3-1
Version 3.3.1
2017-07-08 12:02:41 +02:00
Simon Sessingø 1ac7761d35 Development
- Optimized `.gitignore`.
- Renamed `XSRF-TOKEN` constant to `CSRF-TOKEN` in `CsrfToken`.
2017-07-08 11:56:57 +02:00
Simon Sessingø 9eb7c5c13c Removed 'name' as it's set as the default value when calling the $getItem function. 2017-07-08 11:49:05 +02:00
Simon Sessingø 5151461a02 Added setMiddleware deprecated method in Route class. 2017-07-08 11:42:27 +02:00
Simon Sessingø b07348a3df Development
- Removed temporary `RouterException` class.
- Added object-types to parameters in `CallbackExceptionHandler` and `SimpleRouter` classes.
- Router now renders groups even if callback is null.
- Renamed `setMiddleware` to `addMiddleware` in `Route` class and `IRoute` interface.
- `addMiddleware` now accept both object and class strings in `Route` class.
- `addExceptionHandler` now accept both object and class strings in `RouteGroup` class.
- Added unit-test for rewrite-exception message change: `testRewriteExceptionMessage` in `RouterRewriteTest`.
- Fixed typo: renamed `testSimularUrls` to `testSimilarUrls` in `RouterUrlTest`.
2017-07-08 11:21:18 +02:00
Simon Sessingø d411b31cc2 Changed message-text thrown in NotFoundHttpException to show redirected routes (issue: #240) 2017-07-06 15:46:09 +02:00
Simon Sessingø ca381d445f Development
- Changed router so it supports both string and object as exception handlers.
- Added `Router::error($callback)` method to `Router` class (issue #238).
- Fixed issues calling `getController` and `getMethod` when callback is an object (issue #239).
- Updated documentation to reflect new changes.
- Added `addExceptionHandler` to `IGroupRoute` interface and `RouteGroup` class.
- Other minor bugfixes and optimisations.
2017-07-06 15:25:04 +02:00
Simon Sessingø 2c61cef7ad Merge pull request #237 from skipperbent/v3
V3
2017-05-09 07:06:33 +02:00
Simon Sessingø 98ee60859f Merge pull request #236 from skipperbent/v3-development
Development
2017-05-09 07:06:18 +02:00
Simon Sessingø 9dd80dd1d9 Development
- Fixed global regex match not working properly.
- Feature: added option to change regular expression used for parameters on routes.
- Added unit-tests for custom parameter regular expression.
- Updated documentation to reflect new features.
2017-05-09 06:43:26 +02:00
Simon Sessingø 3f9ab73f7f Merge pull request #235 from skipperbent/v3
V3
2017-05-09 02:52:06 +02:00
Simon Sessingø fef997422e Merge pull request #234 from skipperbent/v3-development
V3 development
2017-05-09 02:51:58 +02:00
Simon Sessingø 8901e7c125 Development
- Added check in `CsrfToken` class to ensure that IV generation is strong and secure.
- Minor optimisations mostly related to PHPDocs and PHPStorm code-inspection.
2017-05-09 02:49:41 +02:00
Simon Sessingø 50e8926272 Added .htaccess example to documentation (issue #232) 2017-05-09 02:31:01 +02:00
Simon Sessingø e44d6bc30b Merge pull request #231 from skipperbent/v3
V3
2017-03-06 02:56:50 +01:00
Simon Sessingø 27ff761d18 Merge pull request #230 from skipperbent/v3-development
V3 development
2017-03-06 02:56:37 +01:00
Simon Sessingø 0a58d36606 Development
- Fixed: `RouteController` not matching certain urls.
- Made `RouteResource` more strict in url-matching.
- Added PHPUnit `RouterControllerTest` class.
- Fixed merged `testSimularUrls` method in `RouterUrlTest`.
2017-03-06 02:53:44 +01:00
Simon Sessingø db024b9588 Added simular routes PHPUnit test 2017-03-06 02:21:13 +01:00
Simon Sessingø 3f61c90749 Merge pull request #229 from skipperbent/v3
V3
2017-03-05 17:10:35 +01:00
Simon Sessingø 94d0764631 Merge pull request #228 from skipperbent/v3-development
Development
2017-03-05 17:10:27 +01:00
Simon Sessingø ff3f1bdcdd Development
- Fixed issue #227 causing custom resource-routes not to be loaded after latest update.
- Optimized `RouteResource` class.
- Renamed `IRestController` to `IResourceController`.
- Added unit-tests for RouterResource.
- Simplified unit-tests with the `TestRouter` custom router class.
2017-03-05 17:09:46 +01:00
Simon Sessingø e4e632dca9 Merge pull request #226 from skipperbent/v3
V3
2017-03-01 03:39:46 +01:00
Simon Sessingø 3e75d6ec0b Merge pull request #225 from skipperbent/v3-development
Development
2017-03-01 03:39:33 +01:00
Simon Sessingø 8c5d8c2dc9 Development
- Fixed: namespace being prepended on `RouterController` routes with absolute namespaces.
- Fixed: match domain on `RouteController` and `RouteResource`.
- Fixed: strict url-matching on `RouteController`. Fix should provide better url-matching.
- Fixed: `RouterResource` always matching first url when having simular urls (ex: `/funny-cat` and `/funny-dog`).
- Added `loadRoutes` method to `Router` class so routes now can be loaded without routing the request (useful when running in Cli etc).
- Removed `getInstance` from `Router` class. Please use `SimpleRouter::router()` for singleton usage instead.
- Added `getRemoteAddr` alias-method for `getIp` in `Request` class.
- Added `getValue` to `IInputItem` interface.
- Other minor optimizations.
- Updated documentation with note on absolute/relative namespaces.
2017-02-26 09:18:35 +01:00
Simon Sessingø a5c4a1f721 Merge pull request #223 from skipperbent/v3
V3
2017-02-15 03:29:24 +01:00
Simon Sessingø 9d2bffcd02 Merge pull request #222 from skipperbent/v3-development
Development
2017-02-15 03:29:16 +01:00
Simon Sessingø 2a448fccd2 Development
- Fixed: only set default namespace on relative callbacks.
- Fixed: default-namespace not being set when calling `SimpleRouter::resource`.
- Minor optimisations.
2017-02-15 03:28:34 +01:00
Simon Sessingø 0bc7fa7bd5 Merge pull request #220 from skipperbent/v3
V3
2017-02-13 06:47:36 +01:00
Simon Sessingø e682cf0b7c Merge pull request #219 from skipperbent/v3-development
V3 development
2017-02-13 06:47:26 +01:00
Simon Sessingø c87298ee24 Optimisations 2017-02-13 06:45:07 +01:00
Simon Sessingø 79414255e0 Development
- Using `$request->setRewriteRoute($route);` rewrite-callback now renders any added middlewares on the route.
- Rewrite callbacks now sets default-namespace on route, if defined.
2017-02-13 06:29:22 +01:00
Simon Sessingø eb036ded3b Merge pull request #218 from skipperbent/v3
V3
2017-02-12 20:48:12 +01:00
Simon Sessingø e913dd98da Merge pull request #217 from skipperbent/v3-development
Fixed: replace empty parameter-values with null.
2017-02-12 20:48:02 +01:00
Simon Sessingø ccd3cf450e Fixed: replace empty parameter-values with null. 2017-02-12 20:43:49 +01:00
Simon Sessingø debe080181 Merge pull request #215 from skipperbent/v3
Merge pull request #214 from skipperbent/v3-development
2017-02-09 13:34:46 +01:00
Simon Sessingø 2654ca67db Merge pull request #214 from skipperbent/v3-development
Optimisations
2017-02-09 13:34:32 +01:00
Simon Sessingø dd99e1d488 Merge pull request #213 from skipperbent/v3-development
Optimisations
2017-02-09 13:34:03 +01:00
Simon Sessingø 86c3e513c3 Optimisations 2017-02-09 13:33:36 +01:00
Simon Sessingø 1c07311c5d Merge pull request #212 from skipperbent/v3
Version 3
2017-02-09 13:24:44 +01:00
Simon Sessingø ce276bd5b7 Version 3
- Easier rewrite management.
- Optimisations.
- Updated documentation.
2017-02-09 13:18:05 +01:00
Simon Sessingø 801c84eb05 Merge pull request #210 from skipperbent/v2
V2
2017-02-01 07:52:38 +01:00
Simon Sessingø ed98519152 Merge pull request #209 from skipperbent/v2-development
Fixed getExtension method in InputFile retuning empty value.
2017-02-01 07:52:29 +01:00
Simon Sessingø bf069c2d42 Fixed getExtension method in InputFile retuning empty value. 2017-02-01 07:51:35 +01:00
Simon Sessingø 55d031e152 Merge pull request #208 from skipperbent/v2
V2
2017-01-19 18:41:55 +01:00
Simon Sessingø 8509062e00 Merge pull request #207 from skipperbent/v2-development
Development
2017-01-19 18:41:43 +01:00
Simon Sessingø 523d49359b Development
- Fixed where method calling itself instead of setWhere.
- Added unit-tests for regex.
2017-01-19 18:40:47 +01:00
Simon Sessingø 4b4ab906fb Merge pull request #205 from skipperbent/v2
V2
2016-11-28 14:14:38 +01:00
Simon Sessingø 9617cacc31 Merge pull request #204 from skipperbent/v2-development
getRoute optimisations.
2016-11-28 14:14:29 +01:00
Simon Sessingø 7cc2652bcd getRoute optimisations. 2016-11-28 14:13:58 +01:00
Simon Sessingø 54a824ce44 Merge pull request #203 from skipperbent/v2
V2
2016-11-28 14:01:10 +01:00
Simon Sessingø 99088719ed Merge pull request #202 from skipperbent/v2-development
`getUrl` will now use default-parameter value if specifically set to null when using `url()`.
2016-11-28 14:00:59 +01:00
Simon Sessingø e26d55a810 getUrl will now use default-parameter value if specificially set to null when using url(). 2016-11-28 09:37:05 +01:00
Simon Sessingø 2062ff4189 Merge pull request #201 from skipperbent/v2
V2
2016-11-28 06:45:16 +01:00
Simon Sessingø 3e41ee28b6 Merge pull request #200 from skipperbent/v2-development
Optimised route-match behavior
2016-11-28 06:45:05 +01:00
Simon Sessingø 8f3ce68a5e Optimised route-match behavior 2016-11-28 06:43:26 +01:00
Simon Sessingø 13a5f40cd0 Merge pull request #199 from skipperbent/v2
V2
2016-11-28 05:53:36 +01:00
Simon Sessingø 751b4444ae Merge pull request #198 from skipperbent/v2-development
Fixed parameters not merged with default values
2016-11-28 05:53:26 +01:00
Simon Sessingø d25351f4f9 Fixed parameters not merged with default values 2016-11-28 05:53:02 +01:00
Simon Sessingø 351f7a01e8 Merge pull request #197 from skipperbent/v2
V2
2016-11-28 05:26:19 +01:00
Simon Sessingø 8cd42a2c4b Merge pull request #196 from skipperbent/v2-development
2.6.0
2016-11-28 05:26:04 +01:00
Simon Sessingø e8f19fbeae Simplified PARAMETERS_REGEX_MATCH in Route class. 2016-11-28 05:24:12 +01:00
Simon Sessingø 5c89ae2aaf Moved regex/match functionality to LoadableRoute. 2016-11-28 04:45:46 +01:00
Simon Sessingø b694a7c0c9 More cleanup 2016-11-28 04:39:34 +01:00
Simon Sessingø 7847b71bbc Cleanup 2016-11-28 04:38:38 +01:00
Simon Sessingø d9b97ccf42 Bugfixes 2016-11-28 04:20:34 +01:00
Simon Sessingø 74351e0330 Bugfixes 2016-11-27 01:07:11 +01:00
Simon Sessingø f5b03e106c Bugfixes 2016-11-27 00:55:47 +01:00
Simon Sessingø 2c5221051e Development 2016-11-26 10:40:42 +01:00
Simon Sessingø 1496878ae9 Merge pull request #195 from skipperbent/v2
V2
2016-11-26 05:22:17 +01:00
Simon Sessingø e8e1471bab Merge pull request #194 from skipperbent/v2-development
Input optimisations
2016-11-26 05:22:02 +01:00
Simon Sessingø aad11ac581 Input optimisations 2016-11-26 05:21:09 +01:00
Simon Sessingø b3f2e5f812 Merge pull request #193 from skipperbent/v2
V2
2016-11-26 05:03:45 +01:00
Simon Sessingø 4de1498723 Merge pull request #192 from skipperbent/v2-development
V2 development
2016-11-26 05:03:36 +01:00
Simon Sessingø c1835152b6 Removed PHP 7 specific functionality. 2016-11-26 04:53:05 +01:00
Simon Sessingø 6213f2fb75 Development
- Optimised Input-classes.
- `get` and `getObject` methods on `Input` now supports filtering on multiple method-types when using the `$method` parameter.
- Input classes now know how to parse that stupid nested $_FILES array.
- It's now possible to change method-names on ResourceControllers.
- Removed `getValue` and `setValue` from `InputFile` classes.
- Ensured that request-method are only parsed from $_POST or $_SERVER.
- Fixed minor parameter-issues with subdomain routing.
- Added PHPDocs.
- Added even more unit-tests.
- Many small optimisations tweaks.
2016-11-26 04:30:00 +01:00
Simon Sessingø a60fa9f919 Merge pull request #191 from skipperbent/v2
V2
2016-11-25 18:28:37 +01:00
Simon Sessingø ea243f2c89 Merge pull request #190 from skipperbent/v2-development
Development
2016-11-25 18:28:26 +01:00
Simon Sessingø 68fc6b76c0 Development
- It's now possible to adjust the load-order for parameters on routes.
- Replaced PHP 7.1 deprecated `mcrypt_create_iv` function with `random_bytes` if it is available (requires > PHP7)
- Added `setIndex`, `setName` and `setValue` methods to `IInputItem` to allow for extendability.
- Cleanup and bug fixes.
2016-11-25 18:26:50 +01:00
Simon Sessingø ce6f34f4d4 Merge pull request #189 from skipperbent/v2
V2
2016-11-25 12:55:17 +01:00
Simon Sessingø 56653568e4 Merge pull request #188 from skipperbent/v2-development
Development
2016-11-25 12:55:08 +01:00
Simon Sessingø 1c515119b4 Development
- Ensure that request-method is always lowercase.
- Fixed spaces instead of tabs to comply with PSR-2.
2016-11-25 12:51:45 +01:00
Simon Sessingø 2868ffe023 Merge pull request #187 from skipperbent/v2
V2
2016-11-25 03:29:01 +02:00
Simon Sessingø 5d330643e7 Merge pull request #186 from skipperbent/v2-development
Development
2016-11-25 03:28:49 +02:00
Simon Sessingø 2dd2d95af5 Development
- Fixed BootManager not loading.
- Optimised for-loops.
2016-11-25 02:27:46 +01:00
Simon Sessingø 8ab52364da Merge pull request #185 from skipperbent/v2
V2
2016-11-25 03:06:03 +02:00
Simon Sessingø 57aa8eac1e Merge pull request #184 from skipperbent/v2-development
Fixed Exception when using Request
2016-11-25 03:05:17 +02:00
Simon Sessingø 7edee8e6d3 Fixed Exception when using Request 2016-11-25 02:04:42 +01:00
Simon Sessingø 110bc2afcf Merge pull request #183 from skipperbent/v2
V2
2016-11-25 02:58:55 +02:00
Simon Sessingø 4efc72d013 Merge pull request #182 from skipperbent/v2-development
Development
2016-11-25 02:58:43 +02:00
Simon Sessingø e360fb5438 Removed demo-project. 2016-11-25 01:57:39 +01:00
Simon Sessingø c6bce8a420 Development
- Began work on new documentation.
- BaseCsrfVerifier now only matches `POST`, `PUT` and `DELETE`.
- Parameters are now parsed on custom regex-matches.
- Added `$type` option to `get` method in `Input` class.
2016-11-25 01:53:02 +01:00
Simon Sessingø fb6da37963 Simplified parseInput method with new handleGetPost helper method. 2016-11-24 22:58:32 +01:00
Simon Sessingø abe427ff59 Development
- Optimised Input and Input-related features.
- Removed InputCollection class.
- Changed more foreach to for.
- Updated documentation.
2016-11-24 22:44:58 +01:00
Simon Sessingø a92a4cdf0d Merge pull request #181 from skipperbent/v2
V2
2016-11-24 14:51:16 +02:00
Simon Sessingø 49fc991f9a Merge pull request #180 from skipperbent/v2-development
Updated documentation
2016-11-24 14:51:07 +02:00
Simon Sessingø b2f23c6c7d Updated documentation 2016-11-24 13:50:45 +01:00
Simon Sessingø 0a4c78cf64 Merge pull request #179 from skipperbent/v2
V2
2016-11-24 14:45:06 +02:00
Simon Sessingø 20353c6e4d Merge pull request #178 from skipperbent/v2-development
Updated documentation to use Demo instead of MyWebsite
2016-11-24 14:44:56 +02:00
Simon Sessingø 53ece9a7fd Updated documentation to use Demo instead of MyWebsite 2016-11-24 13:44:20 +01:00
Simon Sessingø 6706d86784 Merge pull request #177 from skipperbent/v2
V2
2016-11-24 13:19:00 +02:00
Simon Sessingø 4c62f86a26 Merge pull request #176 from skipperbent/v2-development
Fixed HttpException not thrown as NotFoundHttpException
2016-11-24 13:18:52 +02:00
Simon Sessingø 132cf1a10d Fixed HttpException not thrown as NotFoundHttpException 2016-11-24 12:17:47 +01:00
Simon Sessingø 20c1e0ef69 Merge pull request #175 from skipperbent/v2
V2
2016-11-24 11:06:10 +02:00
Simon Sessingø 6445746324 Merge pull request #174 from skipperbent/v2-development
Optimisations + bugfixes
2016-11-24 11:05:57 +02:00
Simon Sessingø ff1f027bda Make sure parameter-modifier is found before parsing parameters. 2016-11-24 10:04:47 +01:00
Simon Sessingø 9418d54c8e No need to return getValue as it will be returned on render. 2016-11-24 09:56:21 +01:00
Simon Sessingø e4ab14a2cb Ensure that setError is always int. 2016-11-24 09:42:37 +01:00
Simon Sessingø 258e0e0f13 Optimisations + bugfixes 2016-11-24 09:36:10 +01:00
Simon Sessingø 1a2921acb4 Merge pull request #173 from skipperbent/v2-development
Development
2016-11-21 09:30:58 +02:00
Simon Sessingø f1a9a50ee5 Development
- Only add $_GET param is every other parameter is null when calling getUrl.
2016-11-21 08:29:26 +01:00
Simon Sessingø fe560a9ba5 Merge pull request #172 from skipperbent/v2
V2
2016-11-21 09:04:32 +02:00
Simon Sessingø 258d9d05c7 Merge pull request #171 from skipperbent/v2-development
Enhancements
2016-11-21 09:04:22 +02:00
Simon Sessingø 2cc97c120f Enhancements
- Added example on how to get current url in documentation.
- Fixed so urls always ends with /.
2016-11-21 07:56:37 +01:00
Simon Sessingø c522801c28 Merge pull request #170 from skipperbent/v2
V2
2016-11-21 05:28:00 +02:00
Simon Sessingø 7e7319de06 Merge pull request #169 from skipperbent/v2-development
Fixed // on currentRoute urls.
2016-11-21 05:27:53 +02:00
Simon Sessingø 5ad7dcf9fd Fixed // on currentRoute urls. 2016-11-21 04:27:06 +01:00
Simon Sessingø 9d275d6d3b Merge pull request #168 from skipperbent/v2-development
Bugfixes
2016-11-21 05:08:12 +02:00
Simon Sessingø 531b35532b Merge pull request #167 from skipperbent/v2-development
Bugfixes
2016-11-21 05:07:59 +02:00
Simon Sessingø dfd32c0904 Bugfixes
- Moved middleware stuff back to Route class.
- Fixed Tests not working due to invalid method.
- Updated depricated method-calls in demo-project.
2016-11-21 04:06:45 +01:00
Simon Sessingø e6dd9f3f55 Merge pull request #166 from skipperbent/v2
V2
2016-11-21 04:09:35 +02:00
Simon Sessingø c258f937e8 Merge pull request #165 from skipperbent/v2-development
V 2.4.0 development
2016-11-21 04:09:26 +02:00
Simon Sessingø efe5767220 Development
- Removed yet another depricated method.
- Fixed $_GET parameters being merged on default when calling getUrl.
- Fixed double / that could appear on urls sometimes.
2016-11-21 03:05:43 +01:00
Simon Sessingø 5415d73f4d Added phpDocs 2016-11-21 02:15:25 +01:00
Simon Sessingø 60c0bd6355 Removed getRoutes depricated class from Route class. 2016-11-21 01:53:53 +01:00
Simon Sessingø 8370d3d94e Development
- Made easier to extend.
- Added IRoute class.
- Changed namespace for Route classes.
- Moved find-url related stuff to Route classes itself.
- Added more tests for finding urls.
- Added support for custom names on RouteController and RouteResource.
- Removed depricated methods.
- Updated documentation.
- Updated demo-project to reflect changes.
- Other small bugfixes and improvements.
2016-11-21 01:50:28 +01:00
Simon Sessingø df3acb6605 Enchacements
- Added more doctype and descriptions on functionality.
- Renamed methods and properties to make better sense.
- Added IRoute interface for easier extendability.
2016-11-20 18:05:57 +01:00
Simon Sessingø e803b1ca5c Merge pull request #164 from skipperbent/v2
V2
2016-11-19 21:13:12 +02:00
Simon Sessingø 88d58cd7b7 Merge pull request #163 from skipperbent/v2-development
Fixed: wrong argument-type in getUrl method
2016-11-19 21:12:57 +02:00
Simon Sessingø 4a48a3fcf9 Fixed: wrong argument-type in getUrl method 2016-11-19 20:12:06 +01:00
Simon Sessingø 45faf9830e Merge pull request #162 from skipperbent/v2
V2
2016-11-19 20:41:38 +02:00
Simon Sessingø d04c74ccad Merge pull request #161 from skipperbent/v2-development
V2 development
2016-11-19 20:41:26 +02:00
Simon Sessingø 9922dcc552 Updated documentation 2016-11-19 19:37:59 +01:00
Simon Sessingø 87d619ca24 More optimisations 2016-11-19 19:28:10 +01:00
Simon Sessingø 7e63197252 Major overhaul 2016-11-19 19:24:05 +01:00
Simon Sessingø c915c0386a Merge pull request #160 from skipperbent/v2
V2
2016-11-19 10:16:06 +02:00
Simon Sessingø 02809a4daf Merge pull request #159 from skipperbent/v2-development
Bugfixes
2016-11-19 10:15:56 +02:00
Simon Sessingø ba719cf880 Bugfixes 2016-11-19 09:15:34 +01:00
Simon Sessingø 30479b15ca Merge pull request #158 from skipperbent/v2
V2
2016-11-19 09:47:29 +02:00
Simon Sessingø da6b5af19f Merge pull request #157 from skipperbent/v2-development
Bugfixes
2016-11-19 09:47:19 +02:00
Simon Sessingø 808d59d3d3 Bugfixes 2016-11-19 08:46:48 +01:00
Simon Sessingø 4dde51e833 Merge pull request #156 from skipperbent/v2
V2
2016-11-19 07:02:34 +02:00
Simon Sessingø 914ec9d1b7 Merge pull request #155 from skipperbent/v2-development
Small optimisations
2016-11-19 07:02:23 +02:00
Simon Sessingø d1f33d9b01 Small optimisation 2016-11-19 06:01:53 +01:00
Simon Sessingø 961a4d0e94 Merge pull request #154 from skipperbent/v2
V2
2016-11-19 06:25:23 +02:00
Simon Sessingø 5f72755a98 Merge pull request #153 from skipperbent/v2-development
V2 development
2016-11-19 06:25:08 +02:00
Simon Sessingø d4a04920b8 More development 2016-11-19 05:22:51 +01:00
Simon Sessingø 4e12cb8bc3 More changes 2016-11-19 05:06:47 +01:00
Simon Sessingø 8f33cc1a39 Development 2016-11-19 04:41:29 +01:00
Simon Sessingø ed1ac74e7a Development
- Fixed updatae causing middlewares to sometimes load on wrong routes.
- Converted project to PSR/2.
- Updated InputCollection class and added get method for easy access to values.
- Complete refactor of RouterBase.
- Added findRoute method to RouterBase.
- It's now possible to change parameter modifiers and symbol by overwriting properties on RouterBase.
- Added RouterUrlTest unit-test for testing route-urls.
- Added IRestController that can be easily implemented in custom ResourceController-classes.
- It's now possible to use "-" instead of "_" when using getHeader method in Request class.
- Added PHPDocs.
- Fixed "/" route sometimes returning "//" as url.
- Optimisations and bugfixes.
2016-11-19 02:48:19 +01:00
Simon Sessingø 2216090a5f Merge pull request #152 from skipperbent/v2
V2
2016-11-17 19:01:25 +02:00
Simon Sessingø 41d15d3acd Merge pull request #151 from skipperbent/v2-development
Re-added missing methods from version 1.
2016-11-17 19:01:17 +02:00
Simon Sessingø a4447313f6 Re-added missing methods from version 1. 2016-11-17 17:55:34 +01:00
Simon Sessingø d467f60e55 Merge pull request #150 from skipperbent/v2
V2
2016-11-17 17:33:59 +02:00
Simon Sessingø ded9c8ebe0 Merge pull request #149 from skipperbent/v2-development
Updated documentation
2016-11-17 17:33:49 +02:00
Simon Sessingø 99f869b57d Updated documentation 2016-11-17 16:33:27 +01:00
Simon Sessingø c30ae6b098 Merge pull request #148 from skipperbent/v2
V2
2016-11-17 17:25:54 +02:00
Simon Sessingø 394f7beb8b Merge pull request #147 from skipperbent/v2-development
V2 development
2016-11-17 17:25:30 +02:00
Simon Sessingø c94523740b Development
- Fixed ExceptionHandlers loading in reverse order.
- Fixed rewrite when modifying `Request` didn't work properly.
2016-11-17 16:24:24 +01:00
Simon Sessingø 305c0ab7c8 Removed clone from RouterBase 2016-11-17 15:17:27 +01:00
Simon Sessingø c93308c8e1 Merge pull request #146 from skipperbent/v2
V2
2016-11-17 16:04:53 +02:00
Simon Sessingø baab004482 Merge pull request #145 from skipperbent/v2-development
Added default null parameter value to RouterController and RouterResource
2016-11-17 16:04:38 +02:00
Simon Sessingø eb160ff7bb Added default null parameter value to RouterController and RouterResource 2016-11-17 15:03:18 +01:00
Simon Sessingø c3241012af Merge pull request #144 from skipperbent/v2
V2
2016-11-17 15:21:32 +02:00
Simon Sessingø 00c0cad211 Merge pull request #143 from skipperbent/v2-development
Bugfix: set parameters to null when new url is set
2016-11-17 15:21:24 +02:00
Simon Sessingø 2db0601e20 - Fixed: set parameters to null when new url is set 2016-11-17 14:20:29 +01:00
Simon Sessingø 9e2ba2674b Merge pull request #142 from skipperbent/v2
V2
2016-11-17 06:37:11 +02:00
Simon Sessingø c6e85676da Merge pull request #141 from skipperbent/v2-development
Bugfixes
2016-11-17 06:37:03 +02:00
Simon Sessingø c59ab12e1a Bugfixes 2016-11-17 05:36:44 +01:00
Simon Sessingø 4d18f33a81 Merge pull request #140 from skipperbent/v2
V2
2016-11-17 05:03:39 +02:00
Simon Sessingø 5a74c9d27d Merge pull request #139 from skipperbent/v2-development
Development
2016-11-17 05:03:30 +02:00
Simon Sessingø b298665d33 Changed demo-project to use v2 router 2016-11-17 04:02:53 +01:00
Simon Sessingø 9ba531d559 Removed uncommented code 2016-11-17 04:00:59 +01:00
Simon Sessingø 73ee4521bc Bugfixes
- Array arguments are now longer automaticially merged.
- Added domain-route parameter unit-test.
2016-11-17 03:50:33 +01:00
Simon Sessingø b5f8d9410f Bugfixes 2016-11-17 03:17:11 +01:00
Simon Sessingø 9d98b2da4c Merge pull request #138 from skipperbent/v2
V2
2016-11-16 16:16:33 +02:00
Simon Sessingø b5eef6f3ee Merge pull request #137 from skipperbent/v2-development
Bugfixes
2016-11-16 16:16:22 +02:00
Simon Sessingø 75566dc2ba Bugfixes 2016-11-16 15:15:51 +01:00
Simon Sessingø 6930864e7e Merge pull request #136 from skipperbent/v2
V2
2016-11-16 15:32:45 +02:00
Simon Sessingø f3f129ae0b Merge pull request #134 from skipperbent/v2
V2
2016-11-15 09:47:07 +02:00
Simon Sessingø 85b9fac21e Merge pull request #132 from skipperbent/v2
V2
2016-11-15 08:13:22 +02:00
Simon Sessingø 0c93633d13 Merge pull request #130 from skipperbent/v2
V2
2016-11-15 01:12:51 +02:00
Simon Sessingø 973344e46e Merge pull request #128 from skipperbent/v2
V2
2016-11-08 18:23:22 +02:00
Simon Sessingø 4a03005c68 Merge pull request #122 from skipperbent/development
Fixed urls in groups not working
2016-11-06 09:04:56 +01:00
Simon Sessingø 52034411cf Merge pull request #121 from skipperbent/development
Development
2016-11-06 08:14:36 +01:00
Simon Sessingø 4c8ed5bb3d Merge pull request #120 from skipperbent/development
Development
2016-11-05 23:09:19 +01:00
Simon Sessingø 9fed6ffb3f Merge pull request #119 from skipperbent/development
Optimised for cli-usage
2016-10-28 07:41:21 +02:00
Simon Sessingø 15da599e82 Merge pull request #118 from skipperbent/development
Minor optimisations
2016-10-27 19:15:59 +02:00
Simon Sessingø 9274acb591 Merge pull request #117 from skipperbent/development
Optimisations and bugfixes
2016-10-27 17:06:24 +02:00
Simon Sessingø 5c7759ab72 Merge pull request #116 from skipperbent/development
Development
2016-10-27 16:45:29 +02:00
Simon Sessingø c7b8593185 Merge pull request #114 from skipperbent/development
Development
2016-10-20 08:38:23 +02:00
Simon Sessingø fb478f475c Merge pull request #113 from skipperbent/development
Development
2016-10-20 08:35:20 +02:00
Simon Sessingø 1142d9d4ce Merge pull request #112 from skipperbent/development
Optimised middleware load order
2016-09-28 12:30:06 +02:00
Simon Sessingø 5d5c96e802 Merge pull request #111 from skipperbent/development
Automatically push middlewares if multiple in nested group.
2016-09-28 12:24:38 +02:00
Simon Sessingø bfdaf8ac52 Merge pull request #110 from skipperbent/development
Updated documentation and added demo-project.
2016-09-26 14:39:50 +02:00
Simon Sessingø 71dc6e172f Merge pull request #108 from skipperbent/development
Allow for default parameters (including A-Z, a-z, 0-9 and "-") when p…
2016-06-14 22:21:20 +02:00
Simon Sessingø 6ee172927f Merge pull request #107 from skipperbent/development
Bugfix
2016-06-04 18:44:59 +02:00
Simon Sessingø bb5e629199 Merge pull request #106 from skipperbent/development
Development
2016-06-04 18:21:16 +02:00
Simon Sessingø 0cc0a59fd5 Merge pull request #105 from skipperbent/development
Development
2016-06-04 15:13:47 +02:00
Simon Sessingø 498fd6b07d Merge pull request #103 from skipperbent/development
Added setValue method to InputItem class.
2016-05-04 13:41:36 +02:00
Simon Sessingø 96ab22a4f8 Merge pull request #102 from skipperbent/development
Added exist method to Input class.
2016-05-03 07:21:16 +02:00
Simon Sessingø 7f528c133b Merge pull request #101 from skipperbent/development
Development
2016-05-01 02:01:47 +02:00
Simon Sessingø 5a50190293 Merge pull request #100 from skipperbent/development
Development
2016-04-25 00:41:50 +02:00
Simon Sessingø 355ef01d63 Merge pull request #99 from skipperbent/development
Development
2016-04-22 15:38:02 +02:00
Simon Sessingø d3162b5a2b Merge pull request #98 from skipperbent/development
Development
2016-04-22 14:30:40 +02:00
Simon Sessingø 810b80487d Merge pull request #97 from skipperbent/development
- Added custom ExceptionHandler example to documentation.
2016-04-21 08:30:34 +02:00
Simon Sessingø 18a9df56ca Merge pull request #96 from skipperbent/development
Development
2016-04-20 08:10:18 +02:00
Simon Sessingø 6e14ded03f Merge pull request #95 from skipperbent/development
Development
2016-04-16 23:23:56 +02:00
Simon Sessingø 899081f8d8 Merge pull request #94 from skipperbent/development
Update README.md
2016-04-16 00:01:22 +02:00
Simon Sessingø e7b9206bc9 Merge pull request #93 from skipperbent/development
Development
2016-04-15 23:21:00 +02:00
Simon Sessingø cd6e800984 Merge pull request #91 from skipperbent/development
Development
2016-04-15 23:14:55 +02:00
Simon Sessingø 11bd5a7d11 Merge pull request #89 from skipperbent/development
[BUGFIX] Fixed notice
2016-04-09 15:39:12 +02:00
Simon Sessingø be32796b01 Merge pull request #88 from skipperbent/development
[TASK] Moved group-middleware rendering to routeRequest to ensure all…
2016-04-09 15:32:45 +02:00
Simon Sessingø 8b3d71a328 Merge pull request #87 from skipperbent/development
[BUGFIX] Fixed only match group route if prefix is set
2016-04-09 10:02:22 +02:00
Simon Sessingø 1fae638aaf Merge pull request #86 from skipperbent/development
Development
2016-04-09 09:50:56 +02:00
Simon Sessingø 37c8bc9f32 Merge pull request #79 from skipperbent/development
[BUGFIX] Bugfixes and optimisations
2016-04-07 23:21:13 +02:00
Simon Sessingø 75029b330a Merge pull request #78 from skipperbent/development
[BUGFIX] Fixed nested groups not merging settings to routes
2016-04-07 19:34:02 +02:00
Simon Sessingø fd5d893040 Merge pull request #77 from skipperbent/development
[TASK] Added rewrite_uri parameter to Request class
2016-03-19 19:12:42 +01:00
Simon Sessingø c1512740af Merge pull request #76 from skipperbent/development
[BUGFIX] Readded rendering of groups
2016-03-19 18:59:01 +01:00
Simon Sessingø 212ae133de Merge pull request #75 from skipperbent/development
[TASK] Readded merging
2016-03-19 17:40:26 +01:00
Simon Sessingø ae58231fa1 Merge pull request #74 from skipperbent/development
Custom boot-managers
2016-03-19 16:29:03 +01:00
Simon Sessingø 358b25d4f1 Merge pull request #73 from skipperbent/development
Development
2016-03-18 17:55:31 +01:00
Simon Sessingø 3d45851d9b Merge pull request #72 from skipperbent/development
[BUGFIX] Only render group if prefix matches.
2016-03-16 19:58:03 +01:00
Simon Sessingø ee5c2207f8 Merge pull request #71 from skipperbent/development
Added support for x-forwarded-proto http header
2016-03-14 01:08:25 +01:00
Simon Sessingø b1ca3fc9ef Merge pull request #70 from skipperbent/development
[FEATURE] Added http code to redirect method.
2016-03-14 00:59:09 +01:00
Simon Sessingø 253c0c70d4 Merge pull request #69 from skipperbent/development
[BUGFIX] Bugfix
2016-03-01 22:52:38 +01:00
Simon Sessingø 53ba2d7ac5 Merge pull request #68 from skipperbent/development
[TASK] Fixed regex causing optional parameters to sometimes catch req…
2016-01-23 16:12:27 +01:00
Simon Sessingø 315fe05769 Merge pull request #67 from skipperbent/development
Development
2016-01-17 04:57:39 +01:00
Simon Sessingø a57113309a Merge pull request #66 from skipperbent/development
Development
2016-01-15 11:56:04 +01:00
77 changed files with 5901 additions and 2913 deletions
+1 -2
View File
@@ -1,4 +1,3 @@
.idea
composer.lock
vendor/
demo-project/vendor
vendor/
+1099 -378
View File
File diff suppressed because it is too large Load Diff
+32 -23
View File
@@ -1,26 +1,35 @@
{
"name": "pecee/simple-router",
"description": "Simple, fast PHP router that is easy to get integrated and in almost any project. Heavily inspired by the Laravel router.",
"keywords": [ "router", "routing", "laravel", "pecee" ],
"license": "MIT",
"support": {
"source": "https://github.com/skipperbent/simple-php-router/issues"
},
"authors": [
{
"name": "Simon Sessingø",
"email": "simon.sessingoe@gmail.com"
}
],
"require": {
"php": ">=5.4.0"
},
"require-dev": {
"phpunit/phpunit": "4.7.7"
},
"autoload": {
"psr-4": {
"Pecee\\": "src/Pecee/"
}
"name": "pecee/simple-router",
"description": "Simple, fast PHP router that is easy to get integrated and in almost any project. Heavily inspired by the Laravel router.",
"keywords": [
"router",
"router",
"routing",
"route",
"simple-php-router",
"laravel",
"pecee",
"php"
],
"license": "MIT",
"support": {
"source": "https://github.com/skipperbent/simple-php-router/issues"
},
"authors": [
{
"name": "Simon Sessingø",
"email": "simon.sessingoe@gmail.com"
}
],
"require": {
"php": ">=5.4.0"
},
"require-dev": {
"phpunit/phpunit": "4.7.7"
},
"autoload": {
"psr-4": {
"Pecee\\": "src/Pecee/"
}
}
}
-100
View File
@@ -1,100 +0,0 @@
# Simple PHP router demo project
This project is here to give you a basic understanding of how to setup and using simple-php-router.
Please note that this demo-project only covers how to integrate the `simple-php-router` in a project without a framework. If you are using some sort of PHP framework in your project the implementation might vary.
**What we won't cover:**
- How to setup a solution that fits your need. This is a basic demo to help you get started.
- Understanding of MVC; including Controllers, Middlewares or ExceptionHandlers.
- How to integrate into third party frameworks.
**What we cover:**
- How to get up and running fast - from scratch.
- How to get ExceptionHandlers, Middlewares and Controllers working.
- How to setup your webservers.
## Installation
- Navigate to the `demo-project` folder in terminal and run `composer update` to install the latest version.
- Point your webserver to `demo-project/public`.
### Setting up Nginx
If you are using Nginx please make sure that url-rewriting is enabled.
You can easily enable url-rewriting by adding the following configuration for the Nginx configuration-file for the demo-project.
```
location / {
try_files $uri $uri/ /index.php?$query_string;
}
```
### Setting up Apache
Nothing special is required for Apache to work. We've include the `.htaccess` file in the `public` folder. If rewriting is not working for you, please check that the `mod_rewrite` module (htaccess support) is enabled in the Apache configuration.
## Folder structure
| Folder | Description |
| ------------- |-------------|
| app |Contains projects-specific PHP classes|
| public |Public folder which are accessible through the web.|
## Notes
The demo project has it's own `Router` class implementation which extends the `SimpleRouter` class with further functionality.
This class can be useful adding additional functionality that are required before and after routing occurs or any extra functionality belonging to the router itself.
In this project we also use our custom router-class to autoload the `routes.php` file from our custom location (`app/routes.php`).
Please check the `routes.php` file in `demo-project/app` for all the urls/rules available in the project.
### CSRF-verifier
For the purpose of this demo, we've added a custom CSRF-verifier middleware called `CsrfVerifier` and disabled CSRF checks for all calls to `/api/*`. This will ensure that CSRF form-checks are not applied when calling our demo api url.
### Exception handlers
The included `CustomExceptionHandler` class returns a very basic json response for errors received on calls to `/api/*` or otherwise just a simple formatted error response.
### Middlewares
`ApiVerification` class is added to all calls to `/api/*`. This simple class just adds some data to the `Request` object, which is returned in one of the methods in the `ApiController` class. We've added this class to demonstrate that you can use middlewares to ensure that the user has the correct authentication - before router loads the controller itself.
### Urls
Please see `routes.php` for all routes and rules.
| URL |
| ------------- |
| / |
| /api/demo |
| /companies |
| /companies/[id] |
| /contact |
## The MIT License (MIT)
Copyright (c) 2016 Simon Sessingø / simple-php-router
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@@ -1,20 +0,0 @@
<?php
namespace Demo\Controllers;
use Pecee\SimpleRouter\SimpleRouter;
class ApiController {
public function index() {
// The variable authenticated is set to true in the ApiVerification middleware class.
header('content-type: application/json');
echo json_encode([
'authenticated' => request()->authenticated
]);
}
}
@@ -1,29 +0,0 @@
<?php
namespace Demo\Controllers;
class DefaultController {
public function index() {
// implement
echo sprintf('DefaultController -> index (?fun=%s)', input()->get('fun'));
}
public function contact() {
echo 'DefaultController -> contact';
}
public function companies($id = null) {
echo 'DefaultController -> companies -> id: ' . $id;
}
public function notFound() {
echo 'Page not found';
}
}
@@ -1,34 +0,0 @@
<?php
namespace Demo\Handlers;
use Pecee\Handler\IExceptionHandler;
use Pecee\Http\Request;
use Pecee\SimpleRouter\RouterEntry;
class CustomExceptionHandler implements IExceptionHandler {
public function handleError( Request $request, RouterEntry &$route = null, \Exception $error) {
// Return json errors if we encounter an error on /api.
if(stripos($request->getUri(), '/api') !== false) {
header('content-type: application/json');
echo json_encode([
'error' => $error->getMessage(),
'code' => $error->getCode()
]);
die();
}
// else we just throw the error
if($error->getCode() == 404) {
// Return 404 path
$request->setUri('/404');
return $request;
}
throw $error;
}
}
@@ -1,17 +0,0 @@
<?php
namespace Demo\Middlewares;
use Pecee\Http\Middleware\IMiddleware;
use Pecee\Http\Request;
use Pecee\SimpleRouter\RouterEntry;
class ApiVerification implements IMiddleware {
public function handle(Request $request, RouterEntry &$route = null) {
// Do authentication
$request->authenticated = true;
}
}
@@ -1,13 +0,0 @@
<?php
namespace Demo\Middlewares;
use Pecee\Http\Middleware\BaseCsrfVerifier;
class CsrfVerifier extends BaseCsrfVerifier {
/**
* CSRF validation will be ignored on the following urls.
*/
protected $except = ['/api/*'];
}
-29
View File
@@ -1,29 +0,0 @@
<?php
/**
* Custom router which handles default middlewares, default exceptions and things
* that should be happen before and after the router is initialised.
*/
namespace Demo;
use Pecee\SimpleRouter\SimpleRouter;
class Router extends SimpleRouter {
public static function start($defaultNamespace = null) {
// Load our helpers
require_once 'helpers.php';
// Load our custom routes
require_once 'routes.php';
parent::setDefaultNamespace('\Demo\Controllers');
// Do initial stuff
parent::start();
}
}
-39
View File
@@ -1,39 +0,0 @@
<?php
use Pecee\SimpleRouter\SimpleRouter;
function url($controller, $parameters = null, $getParams = null) {
SimpleRouter::getRoute($controller, $parameters, $getParams);
}
/**
* Get current csrf-token
* @return null|string
*/
function csrf_token() {
$token = new \Pecee\CsrfToken();
return $token->getToken();
}
/**
* Get request object
* @return \Pecee\Http\Request
*/
function request() {
return SimpleRouter::request();
}
/**
* Get response object
* @return \Pecee\Http\Response
*/
function response() {
return SimpleRouter::response();
}
/**
* Get input class
* @return \Pecee\Http\Input\Input
*/
function input() {
return SimpleRouter::request()->getInput();
}
-23
View File
@@ -1,23 +0,0 @@
<?php
/**
* This file contains all the routes for the project
*/
use Demo\Router;
Router::csrfVerifier(new \Demo\Middlewares\CsrfVerifier());
Router::group(['exceptionHandler' => 'Demo\Handlers\CustomExceptionHandler'], function() {
Router::get('/', 'DefaultController@index')->setAlias('home');
Router::get('/contact', 'DefaultController@contact')->setAlias('contact');
Router::get('/404', 'DefaultController@notFound')->setAlias('404');
Router::basic('/companies', 'DefaultController@companies')->setAlias('companies');
Router::basic('/companies/{id}', 'DefaultController@companies')->setAlias('companies');
// Api
Router::group(['prefix' => '/api', 'middleware' => 'Demo\Middlewares\ApiVerification'], function() {
Router::resource('/demo', 'ApiController');
});
});
-26
View File
@@ -1,26 +0,0 @@
{
"name": "pecee/simple-router-demo",
"description": "Simple router demo project",
"keywords": [
"simple-router",
"php",
"php-simple-router"
],
"license": "MIT",
"type": "project",
"require": {
"php": ">=5.4.0",
"pecee/simple-router": "1.*"
},
"require-dev": {
},
"config": {
"preferred-install": "dist"
},
"autoload": {
"psr-4": {
"Demo\\": "app/"
}
}
}
-5
View File
@@ -1,5 +0,0 @@
RewriteEngine on
RewriteCond %{SCRIPT_FILENAME} !-f
RewriteCond %{SCRIPT_FILENAME} !-d
RewriteCond %{SCRIPT_FILENAME} !-l
RewriteRule ^(.*)$ index.php/$1
-7
View File
@@ -1,7 +0,0 @@
<?php
// load composer dependencies
require '../vendor/autoload.php';
// Start the routing
\Demo\Router::start();
+66
View File
@@ -0,0 +1,66 @@
<?php
use Pecee\SimpleRouter\SimpleRouter as Router;
/**
* Get url for a route by using either name/alias, class or method name.
*
* The name parameter supports the following values:
* - Route name
* - Controller/resource name (with or without method)
* - Controller class name
*
* When searching for controller/resource by name, you can use this syntax "route.name@method".
* You can also use the same syntax when searching for a specific controller-class "MyController@home".
* If no arguments is specified, it will return the url for the current loaded route.
*
* @param string|null $name
* @param string|array|null $parameters
* @param array|null $getParams
* @return string
*/
function url($name = null, $parameters = null, $getParams = null)
{
return Router::getUrl($name, $parameters, $getParams);
}
/**
* @return \Pecee\Http\Response
*/
function response()
{
return Router::response();
}
/**
* @return \Pecee\Http\Request
*/
function request()
{
return Router::request();
}
/**
* Get input class
* @param string|null $index Parameter index name
* @param string|null $defaultValue Default return value
* @param string|array|null $methods Default method
* @return \Pecee\Http\Input\Input|string
*/
function input($index = null, $defaultValue = null, $methods = null)
{
if ($index !== null) {
return request()->getInput()->get($index, $defaultValue, $methods);
}
return request()->getInput();
}
function redirect($url, $code = null)
{
if ($code !== null) {
response()->httpCode($code);
}
response()->redirect($url);
}
@@ -0,0 +1,47 @@
<?php
namespace Pecee\Controllers;
interface IResourceController
{
/**
* @return void
*/
public function index();
/**
* @param mixed $id
* @return void
*/
public function show($id);
/**
* @return void
*/
public function store();
/**
* @return void
*/
public function create();
/**
* View
* @param mixed $id
* @return void
*/
public function edit($id);
/**
* @param mixed $id
* @return void
*/
public function update($id);
/**
* @param mixed $id
* @return void
*/
public function destroy($id);
}
+30 -13
View File
@@ -1,21 +1,32 @@
<?php
namespace Pecee;
class CsrfToken {
const CSRF_KEY = 'XSRF-TOKEN';
class CsrfToken
{
const CSRF_KEY = 'CSRF-TOKEN';
protected $token;
/**
* Generate random identifier for CSRF token
*
* @throws \RuntimeException
* @return string
*/
public static function generateToken() {
if (function_exists('mcrypt_create_iv')) {
return bin2hex(mcrypt_create_iv(32, MCRYPT_DEV_URANDOM));
public static function generateToken()
{
if (function_exists('random_bytes')) {
return bin2hex(random_bytes(32));
}
return bin2hex(openssl_random_pseudo_bytes(32));
$isSourceStrong = false;
$random = openssl_random_pseudo_bytes(32, $isSourceStrong);
if ($isSourceStrong === false || $random === false) {
throw new \RuntimeException('IV generation failed');
}
return $random;
}
/**
@@ -24,10 +35,12 @@ class CsrfToken {
* @param string $token
* @return bool
*/
public function validate($token) {
if($token !== null && $this->getToken() !== null) {
public function validate($token)
{
if ($token !== null && $this->getToken() !== null) {
return hash_equals($token, $this->getToken());
}
return false;
}
@@ -36,7 +49,8 @@ class CsrfToken {
*
* @param $token
*/
public function setToken($token) {
public function setToken($token)
{
setcookie(static::CSRF_KEY, $token, time() + 60 * 120, '/');
}
@@ -44,10 +58,12 @@ class CsrfToken {
* Get csrf token
* @return string|null
*/
public function getToken(){
if($this->hasToken()) {
public function getToken()
{
if ($this->hasToken() === true) {
return $_COOKIE[static::CSRF_KEY];
}
return null;
}
@@ -55,7 +71,8 @@ class CsrfToken {
* Returns whether the csrf token has been defined
* @return bool
*/
public function hasToken() {
public function hasToken()
{
return isset($_COOKIE[static::CSRF_KEY]);
}
-4
View File
@@ -1,4 +0,0 @@
<?php
namespace Pecee\Exception;
class RouterException extends \Exception { }
@@ -1,4 +0,0 @@
<?php
namespace Pecee\Exception;
class TokenMismatchException extends \Exception {}
-17
View File
@@ -1,17 +0,0 @@
<?php
namespace Pecee\Handler;
use Pecee\Http\Request;
use Pecee\SimpleRouter\RouterEntry;
interface IExceptionHandler {
/**
* @param Request $request
* @param RouterEntry|null $route
* @param \Exception $error
* @return Request|null
*/
public function handleError(Request $request, RouterEntry &$route = null, \Exception $error);
}
@@ -0,0 +1,38 @@
<?php
namespace Pecee\Handlers;
use Pecee\Http\Request;
/**
* Class CallbackExceptionHandler
*
* Class is used to create callbacks which are fired when an exception is reached.
* This allows for easy handling 404-exception etc. without creating an custom ExceptionHandler.
*
* @package Pecee\Handlers
*/
class CallbackExceptionHandler implements IExceptionHandler
{
protected $callback;
public function __construct(\Closure $callback)
{
$this->callback = $callback;
}
/**
* @param Request $request
* @param \Exception $error
* @return Request|null
*/
public function handleError(Request $request, \Exception $error)
{
/* Fire exceptions */
return call_user_func($this->callback,
$request,
$error
);
}
}
+15
View File
@@ -0,0 +1,15 @@
<?php
namespace Pecee\Handlers;
use Pecee\Http\Request;
interface IExceptionHandler
{
/**
* @param Request $request
* @param \Exception $error
* @return Request|null
*/
public function handleError(Request $request, \Exception $error);
}
+19
View File
@@ -0,0 +1,19 @@
<?php
namespace Pecee\Http\Input;
interface IInputItem
{
public function getIndex();
public function setIndex($index);
public function getName();
public function setName($name);
public function getValue();
public function __toString();
}
+251 -175
View File
@@ -3,33 +3,263 @@ namespace Pecee\Http\Input;
use Pecee\Http\Request;
class Input {
class Input
{
/**
* @var array
*/
public $get = [];
/**
* @var \Pecee\Http\Input\InputCollection
* @var array
*/
public $get;
public $post = [];
/**
* @var \Pecee\Http\Input\InputCollection
* @var array
*/
public $post;
/**
* @var \Pecee\Http\Input\InputCollection
*/
public $file;
public $file = [];
/**
* @var Request
*/
protected $request;
public function __construct(Request $request) {
public function __construct(Request $request)
{
$this->request = $request;
$this->setGet();
$this->setPost();
$this->setFile();
$this->parseInputs();
}
public function parseInputs()
{
/* Parse get requests */
if (count($_GET) > 0) {
$this->get = $this->handleGetPost($_GET);
}
/* Parse post requests */
$postVars = $_POST;
if (in_array($this->request->getMethod(), ['put', 'patch', 'delete'], false) === true) {
parse_str(file_get_contents('php://input'), $postVars);
}
if (count($postVars) > 0) {
$this->post = $this->handleGetPost($postVars);
}
/* Parse get requests */
if (count($_FILES) > 0) {
$this->file = $this->parseFiles();
}
}
public function parseFiles()
{
$list = [];
foreach ((array)$_FILES as $key => $value) {
// Handle array input
if (is_array($value['name']) === false) {
$values['index'] = $key;
$list[$key] = InputFile::createFromArray(array_merge($value, $values));
continue;
}
$keys = [];
$files = $this->rearrangeFiles($value['name'], $keys, $value);
if (isset($list[$key])) {
$list[$key][] = $files;
} else {
$list[$key] = $files;
}
}
return $list;
}
protected function rearrangeFiles(array $values, &$index, $original)
{
$output = [];
$getItem = function ($key, $property = 'name') use ($original, $index) {
$path = $original[$property];
$fileValues = array_values($index);
foreach ($fileValues as $i) {
$path = $path[$i];
}
return $path[$key];
};
foreach ($values as $key => $value) {
if (is_array($getItem($key)) === false) {
$file = InputFile::createFromArray([
'index' => $key,
'filename' => $getItem($key),
'error' => $getItem($key, 'error'),
'tmp_name' => $getItem($key, 'tmp_name'),
'type' => $getItem($key, 'type'),
'size' => $getItem($key, 'size'),
]);
if (isset($output[$key])) {
$output[$key][] = $file;
} else {
$output[$key] = $file;
}
continue;
}
$index[] = $key;
$files = $this->rearrangeFiles($value, $index, $original);
if (isset($output[$key])) {
$output[$key][] = $files;
} else {
$output[$key] = $files;
}
}
return $output;
}
protected function handleGetPost(array $array)
{
$list = [];
$max = count($array) - 1;
$keys = array_keys($array);
for ($i = $max; $i >= 0; $i--) {
$key = $keys[$i];
$value = $array[$key];
// Handle array input
if (is_array($value) === false) {
$list[$key] = new InputItem($key, $value);
continue;
}
$output = $this->handleGetPost($value);
$list[$key] = $output;
}
return $list;
}
/**
* Find post-value by index or return default value.
*
* @param string $index
* @param string|null $defaultValue
* @return InputItem|string
*/
public function findPost($index, $defaultValue = null)
{
return isset($this->post[$index]) ? $this->post[$index] : $defaultValue;
}
/**
* Find file by index or return default value.
*
* @param string $index
* @param string|null $defaultValue
* @return InputFile|string
*/
public function findFile($index, $defaultValue = null)
{
return isset($this->file[$index]) ? $this->file[$index] : $defaultValue;
}
/**
* Find parameter/query-string by index or return default value.
*
* @param string $index
* @param string|null $defaultValue
* @return InputItem|string
*/
public function findGet($index, $defaultValue = null)
{
return isset($this->get[$index]) ? $this->get[$index] : $defaultValue;
}
/**
* Get input object
*
* @param string $index
* @param string|null $defaultValue
* @param array|string|null $methods
* @return IInputItem|string
*/
public function getObject($index, $defaultValue = null, $methods = null)
{
if ($methods !== null && is_string($methods) === true) {
$methods = [$methods];
}
$element = null;
if ($methods === null || in_array('get', $methods)) {
$element = $this->findGet($index);
}
if (($element === null && $methods === null) || ($methods !== null && in_array('post', $methods))) {
$element = $this->findPost($index);
}
if (($element === null && $methods === null) || ($methods !== null && in_array('file', $methods))) {
$element = $this->findFile($index);
}
return ($element !== null) ? $element : $defaultValue;
}
/**
* Get input element value matching index
*
* @param string $index
* @param string|null $defaultValue
* @param array|string|null $methods
* @return InputItem|string
*/
public function get($index, $defaultValue = null, $methods = null)
{
$input = $this->getObject($index, $defaultValue, $methods);
if ($input instanceof InputItem) {
return (trim($input->getValue()) === '') ? $defaultValue : $input->getValue();
}
return $input;
}
/**
* Check if a input-item exist
*
* @param string $index
* @return bool
*/
public function exists($index)
{
return ($this->getObject($index) !== null);
}
/**
@@ -37,177 +267,23 @@ class Input {
* @param array|null $filter Only take items in filter
* @return array
*/
public function all(array $filter = null) {
public function all(array $filter = null)
{
$output = $_POST;
if($this->request->getMethod() === 'post') {
if ($this->request->getMethod() === 'post') {
$contents = file_get_contents('php://input');
if (stripos(trim($contents), '{') === 0) {
if (strpos(trim($contents), '{') === 0) {
$output = json_decode($contents, true);
if($output === false) {
$output = array();
if ($output === false) {
$output = [];
}
}
}
$output = array_merge($_GET, $output);
if($filter !== null) {
$output = array_filter($output, function ($key) use ($filter) {
if (in_array($key, $filter)) {
return true;
}
return false;
}, ARRAY_FILTER_USE_KEY);
}
return $output;
}
public function getObject($index, $default = null) {
$key = (strpos($index, '[') > -1) ? substr($index, strpos($index, '[')+1, strpos($index, ']') - strlen($index)) : null;
$index = (strpos($index, '[') > -1) ? substr($index, 0, strpos($index, '[')) : $index;
$element = $this->get->findFirst($index);
if($element !== null) {
return ($key !== null) ? $element[$key] : $element;
}
if($this->request->getMethod() !== 'get') {
$element = $this->post->findFirst($index);
if ($element !== null) {
return ($key !== null) ? $element[$key] : $element;
}
$element = $this->file->findFirst($index);
if ($element !== null) {
return ($key !== null) ? $element[$key] : $element;
}
}
return $default;
}
/**
* Get input element value matching index
* @param string $index
* @param string|null $default
* @return string|null
*/
public function get($index, $default = null) {
$item = $this->getObject($index);
if($item !== null) {
if($item instanceof InputCollection || $item instanceof InputFile) {
return $item;
}
return (trim($item->getValue()) === '') ? $default : $item->getValue();
}
return $default;
}
public function exists($index) {
return ($this->getObject($index) !== null);
}
public function setGet() {
$this->get = new InputCollection();
if(count($_GET)) {
foreach($_GET as $key => $get) {
if(!is_array($get)) {
$this->get->{$key} = new InputItem($key, $get);
continue;
}
$output = new InputCollection();
foreach($get as $k => $g) {
$output->{$k} = new InputItem($k, $g);
}
$this->get->{$key} = $output;
}
}
}
public function setPost() {
$this->post = new InputCollection();
$postVars = $_POST;
if(in_array($this->request->getMethod(), ['put', 'patch', 'delete'])) {
parse_str(file_get_contents('php://input'), $postVars);
}
if(count($postVars)) {
foreach($postVars as $key => $post) {
if(!is_array($post)) {
$this->post->{strtolower($key)} = new InputItem($key, $post);
continue;
}
$output = new InputCollection();
foreach($post as $k => $p) {
$output->{$k} = new InputItem($k, $p);
}
$this->post->{strtolower($key)} = $output;
}
}
}
public function setFile() {
$this->file = new InputCollection();
if(count($_FILES)) {
foreach($_FILES as $key => $value) {
// Multiple files
if(!is_array($value['name'])) {
// Strip empty values
if($value['error'] != '4') {
$file = new InputFile($key);
$file->setName($value['name']);
$file->setSize($value['size']);
$file->setType($value['type']);
$file->setTmpName($value['tmp_name']);
$file->setError($value['error']);
$this->file->{strtolower($key)} = $file;
}
continue;
}
$output = new InputCollection();
foreach($value['name'] as $k=>$val) {
// Strip empty values
if($value['error'][$k] != '4') {
$file = new InputFile($k);
$file->setName($value['name'][$k]);
$file->setSize($value['size'][$k]);
$file->setType($value['type'][$k]);
$file->setTmpName($value['tmp_name'][$k]);
$file->setError($value['error'][$k]);
$output->{$k} = $file;
}
}
$this->file->{strtolower($key)} = $output;
}
}
return ($filter !== null) ? array_intersect_key($output, array_flip($filter)) : array_merge($_GET, $output);
}
}
-85
View File
@@ -1,85 +0,0 @@
<?php
namespace Pecee\Http\Input;
class InputCollection implements \IteratorAggregate {
protected $data = array();
/**
* Search for input element matching index.
* Useful for searching for finding items where $index doesn't contain form name.
*
* @param string $index
* @param string|null $defaultValue
* @return mixed
*/
public function findFirst($index, $defaultValue = null) {
if(count($this->data)) {
if(isset($this->data[$index])) {
return $this->data[$index];
}
foreach($this->data as $key => $value) {
if(strtolower($index) === strtolower($key)) {
return $value;
}
}
}
return $defaultValue;
}
public function getValue($index, $defaultValue = null) {
if(count($this->data)) {
if(isset($this->data[$index])) {
return $this->data[$index]->getValue();
}
foreach($this->data as $key => $value) {
if(strtolower($index) === strtolower($key)) {
return $value->getValue();
}
}
}
return $defaultValue;
}
/**
* @param $index
* @throws \InvalidArgumentException
* @return InputItem
*/
public function __get($index) {
$item = $this->findFirst($index);
// Ensure that item are always available
if($item === null) {
$this->data[$index] = new InputItem($index, null);
return $this->data[$index];
}
return $item;
}
public function __set($index, $value) {
$this->data[$index] = $value;
}
public function getData() {
return $this->data;
}
/**
* Retrieve an external iterator
* @link http://php.net/manual/en/iteratoraggregate.getiterator.php
* @return \Traversable An instance of an object implementing <b>Iterator</b> or
* <b>Traversable</b>
* @since 5.0.0
*/
public function getIterator() {
return new \ArrayIterator($this->data);
}
}
+255 -48
View File
@@ -1,68 +1,275 @@
<?php
namespace Pecee\Http\Input;
class InputFile extends InputItem {
class InputFile implements IInputItem
{
public $index;
public $name;
public $filename;
public $size;
public $type;
public $error;
public $tmpName;
protected $name;
protected $size;
protected $type;
protected $error;
protected $tmpName;
public function __construct($index)
{
$this->index = $index;
/**
* @return string
*/
public function getSize() {
return $this->size;
}
/**
* @return string
*/
public function getType() {
return $this->type;
}
/**
* @return string
*/
public function getError() {
return $this->error;
}
/**
* @return string
*/
public function getTmpName() {
return $this->tmpName;
}
public function getExtension() {
return pathinfo($this->getName(), PATHINFO_EXTENSION);
// Make the name human friendly, by replace _ with space
$this->name = ucfirst(str_replace('_', ' ', $this->index));
}
public function move($destination) {
return move_uploaded_file($this->tmpName, $destination);
}
/**
* Create from array
*
* @param array $values
* @throws \InvalidArgumentException
* @return static
*/
public static function createFromArray(array $values)
{
if (!isset($values['index'])) {
throw new \InvalidArgumentException('Index key is required');
}
public function getContents() {
return file_get_contents($this->tmpName);
}
/* Easy way of ensuring that all indexes-are set and not filling the screen with isset() */
$values = array_merge([
'tmp_name' => null,
'type' => null,
'size' => null,
'name' => null,
'error' => null,
], $values);
return (new static($values['index']))
->setSize($values['size'])
->setError($values['error'])
->setType($values['type'])
->setTmpName($values['tmp_name'])
->setFilename($values['name']);
public function setTmpName($name) {
$this->tmpName = $name;
}
public function setSize($size) {
/**
* @return string
*/
public function getIndex()
{
return $this->index;
}
/**
* Set input index
* @param string $index
* @return static $this
*/
public function setIndex($index)
{
$this->index = $index;
return $this;
}
/**
* @return string
*/
public function getSize()
{
return $this->size;
}
/**
* Set file size
* @param int $size
* @return static $this
*/
public function setSize($size)
{
$this->size = $size;
return $this;
}
public function setType($type) {
/**
* Get mime-type of file
* @return string
*/
public function getMime()
{
return $this->getType();
}
/**
* @return string
*/
public function getType()
{
return $this->type;
}
/**
* Set type
* @param string $type
* @return static $this
*/
public function setType($type)
{
$this->type = $type;
return $this;
}
public function setError($error) {
$this->error = $error;
/**
* Returns extension without "."
*
* @return string
*/
public function getExtension()
{
return pathinfo($this->getFilename(), PATHINFO_EXTENSION);
}
/**
* Get human friendly name
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Set human friendly name.
* Useful for adding validation etc.
*
* @param string $name
* @return static $this
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Set filename
*
* @param string $name
* @return static $this
*/
public function setFilename($name)
{
$this->filename = $name;
return $this;
}
/**
* Get filename
*
* @return string mixed
*/
public function getFilename()
{
return $this->filename;
}
/**
* Move the uploaded temporary file to it's new home
*
* @param string $destination
* @return bool
*/
public function move($destination)
{
return move_uploaded_file($this->tmpName, $destination);
}
/**
* Get file contents
*
* @return string
*/
public function getContents()
{
return file_get_contents($this->tmpName);
}
/**
* Return true if an upload error occurred.
*
* @return bool
*/
public function hasError()
{
return ($this->getError() !== 0);
}
/**
* Get upload-error code.
*
* @return string
*/
public function getError()
{
return $this->error;
}
/**
* Set error
*
* @param int $error
* @return static $this
*/
public function setError($error)
{
$this->error = (int)$error;
return $this;
}
/**
* @return string
*/
public function getTmpName()
{
return $this->tmpName;
}
/**
* Set file temp. name
* @param string $name
* @return static $this
*/
public function setTmpName($name)
{
$this->tmpName = $name;
return $this;
}
public function __toString()
{
return $this->getTmpName();
}
public function getValue()
{
return $this->getFilename();
}
public function toArray()
{
return [
'tmp_name' => $this->tmpName,
'type' => $this->type,
'size' => $this->size,
'name' => $this->filename,
'error' => $this->error,
];
}
}
+36 -20
View File
@@ -1,13 +1,14 @@
<?php
namespace Pecee\Http\Input;
class InputItem {
class InputItem implements IInputItem
{
public $index;
public $name;
public $value;
protected $index;
protected $name;
protected $value;
public function __construct($index, $value = null) {
public function __construct($index, $value = null)
{
$this->index = $index;
$this->value = $value;
@@ -16,24 +17,26 @@ class InputItem {
}
/**
* @return array
* @return string
*/
public function getName() {
return $this->name;
public function getIndex()
{
return $this->index;
}
/**
* @return array
*/
public function getValue() {
return $this->value;
public function setIndex($index)
{
$this->index = $index;
return $this;
}
/**
* @return string
*/
public function getIndex() {
return $this->index;
public function getName()
{
return $this->name;
}
/**
@@ -41,23 +44,36 @@ class InputItem {
* @param string $name
* @return static $this
*/
public function setName($name) {
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* @return string
*/
public function getValue()
{
return $this->value;
}
/**
* Set input value
* @param string $value
* @return static $this
*/
public function setValue($value) {
public function setValue($value)
{
$this->value = $value;
return $this;
}
public function __toString() {
return (string)$this->getValue();
public function __toString()
{
return (string)$this->value;
}
}
+33 -24
View File
@@ -2,12 +2,11 @@
namespace Pecee\Http\Middleware;
use Pecee\CsrfToken;
use Pecee\Exception\TokenMismatchException;
use Pecee\Http\Middleware\Exceptions\TokenMismatchException;
use Pecee\Http\Request;
use Pecee\SimpleRouter\RouterEntry;
class BaseCsrfVerifier implements IMiddleware {
class BaseCsrfVerifier implements IMiddleware
{
const POST_KEY = 'csrf-token';
const HEADER_KEY = 'X-CSRF-TOKEN';
@@ -15,11 +14,12 @@ class BaseCsrfVerifier implements IMiddleware {
protected $csrfToken;
protected $token;
public function __construct() {
public function __construct()
{
$this->csrfToken = new CsrfToken();
// Generate or get the CSRF-Token from Cookie.
$this->token = (!$this->hasToken()) ? $this->generateToken() : $this->csrfToken->getToken();
$this->token = ($this->hasToken() === false) ? $this->generateToken() : $this->csrfToken->getToken();
}
/**
@@ -27,22 +27,26 @@ class BaseCsrfVerifier implements IMiddleware {
* @param Request $request
* @return bool
*/
protected function skip(Request $request) {
if($this->except === null || !is_array($this->except)) {
protected function skip(Request $request)
{
if ($this->except === null || is_array($this->except) === false) {
return false;
}
foreach($this->except as $url) {
$max = count($this->except) - 1;
for ($i = $max; $i >= 0; $i--) {
$url = $this->except[$i];
$url = rtrim($url, '/');
if($url[strlen($url)-1] === '*') {
if ($url[strlen($url) - 1] === '*') {
$url = rtrim($url, '*');
$skip = (stripos($request->getUri(), $url) === 0);
$skip = (stripos($request->getUri()->getPath(), $url) === 0);
} else {
$skip = ($url === rtrim($request->getUri(), '/'));
$skip = ($url === $request->getUri()->getPath());
}
if($skip) {
if ($skip === true) {
return true;
}
}
@@ -50,18 +54,19 @@ class BaseCsrfVerifier implements IMiddleware {
return false;
}
public function handle(Request $request, RouterEntry &$route = null) {
public function handle(Request $request)
{
if($request->getMethod() !== 'get' && !$this->skip($request)) {
if ($this->skip($request) === false && in_array($request->getMethod(), ['post', 'put', 'delete'], false) === true) {
$token = $request->getInput()->post->getValue(static::POST_KEY);
$token = $request->getInput()->get(static::POST_KEY, null, 'post');
// If the token is not posted, check headers for valid x-csrf-token
if($token === null) {
if ($token === null) {
$token = $request->getHeader(static::HEADER_KEY);
}
if( !$this->csrfToken->validate($token) ) {
if ($this->csrfToken->validate($token) === false) {
throw new TokenMismatchException('Invalid csrf-token.');
}
@@ -69,21 +74,25 @@ class BaseCsrfVerifier implements IMiddleware {
}
public function generateToken() {
$token = $this->csrfToken->generateToken();
public function generateToken()
{
$token = CsrfToken::generateToken();
$this->csrfToken->setToken($token);
return $token;
}
public function hasToken() {
if($this->token != null) {
public function hasToken()
{
if ($this->token !== null) {
return true;
}
return $this->csrfToken->hasToken();
}
public function getToken() {
public function getToken()
{
return $this->token;
}
@@ -0,0 +1,7 @@
<?php
namespace Pecee\Http\Middleware\Exceptions;
class TokenMismatchException extends \Exception
{
}
+3 -5
View File
@@ -2,15 +2,13 @@
namespace Pecee\Http\Middleware;
use Pecee\Http\Request;
use Pecee\SimpleRouter\RouterEntry;
interface IMiddleware {
interface IMiddleware
{
/**
* @param Request $request
* @param RouterEntry|null $route
* @return Request|null
*/
public function handle(Request $request, RouterEntry &$route = null);
public function handle(Request $request);
}
+225 -55
View File
@@ -2,64 +2,85 @@
namespace Pecee\Http;
use Pecee\Http\Input\Input;
use Pecee\SimpleRouter\Route\ILoadableRoute;
use Pecee\SimpleRouter\Route\RouteUrl;
use Pecee\SimpleRouter\SimpleRouter;
class Request {
protected $data = array();
class Request
{
private $data = [];
protected $headers;
protected $host;
protected $uri;
protected $method;
protected $input;
/**
* @var ILoadableRoute|null
*/
protected $rewriteRoute;
protected $rewriteUrl;
public function __construct() {
/**
* @var ILoadableRoute|null
*/
protected $loadedRoute;
public function __construct()
{
$this->parseHeaders();
$this->setHost($this->getHeader('http-host'));
// Check if special IIS header exist, otherwise use default.
$this->setUri(new Uri($this->getHeader('unencoded-url', $this->getHeader('request-uri'))));
$this->input = new Input($this);
$this->host = $this->getHeader('http_host');;
$this->uri = $this->getHeader('request_uri');
$this->method = strtolower($this->input->post->findFirst('_method', $this->getHeader('request_method')));
$this->method = strtolower($this->input->get('_method', $this->getHeader('request-method'), 'post'));
}
protected function parseHeaders() {
$this->headers = array();
protected function parseHeaders()
{
$this->headers = [];
foreach ($_SERVER as $name => $value) {
$this->headers[strtolower($name)] = $value;
$max = count($_SERVER) - 1;
$keys = array_keys($_SERVER);
for ($i = $max; $i >= 0; $i--) {
$key = $keys[$i];
$value = $_SERVER[$key];
$this->headers[strtolower($key)] = $value;
$this->headers[strtolower(str_replace('_', '-', $key))] = $value;
}
}
public function isSecure() {
if($this->getHeader('http_x_forwarded_proto') === 'https') {
return true;
}
if($this->getHeader('https') !== null) {
return true;
}
return ($this->getHeader('server_port') === 443);
public function isSecure()
{
return $this->getHeader('http-x-forwarded-proto') === 'https' || $this->getHeader('https') !== null || $this->getHeader('server-port') === 443;
}
/**
* @return string
* @return Uri
*/
public function getUri() {
public function getUri()
{
return $this->uri;
}
/**
* @return string
*/
public function getHost() {
public function getHost()
{
return $this->host;
}
/**
* @return string
*/
public function getMethod() {
public function getMethod()
{
return $this->method;
}
@@ -67,23 +88,26 @@ class Request {
* Get http basic auth user
* @return string|null
*/
public function getUser() {
return $this->getHeader('php_auth_user');
public function getUser()
{
return $this->getHeader('php-auth-user');
}
/**
* Get http basic auth password
* @return string|null
*/
public function getPassword() {
return $this->getHeader('php_auth_pw');
public function getPassword()
{
return $this->getHeader('php-auth-pw');
}
/**
* Get all headers
* @return array
*/
public function getHeaders() {
public function getHeaders()
{
return $this->headers;
}
@@ -91,95 +115,241 @@ class Request {
* Get id address
* @return string
*/
public function getIp() {
if($this->getHeader('http_cf_connecting_ip') !== null) {
return $this->getHeader('http_cf_connecting_ip');
public function getIp()
{
if ($this->getHeader('http-cf-connecting-ip') !== null) {
return $this->getHeader('http-cf-connecting-ip');
}
if($this->getHeader('http_x_forwarded_for') !== null && strlen($this->getHeader('http_x_forwarded_for'))) {
return $this->getHeader('http_x_forwarded_for');
if ($this->getHeader('http-x-forwarded-for') !== null) {
return $this->getHeader('http-x-forwarded_for');
}
return $this->getHeader('remote_addr');
return $this->getHeader('remote-addr');
}
/**
* Get remote address/ip
*
* @alias static::getIp
* @return string
*/
public function getRemoteAddr()
{
return $this->getIp();
}
/**
* Get referer
* @return string
*/
public function getReferer() {
return $this->getHeader('http_referer');
public function getReferer()
{
return $this->getHeader('http-referer');
}
/**
* Get user agent
* @return string
*/
public function getUserAgent() {
return $this->getHeader('http_user_agent');
public function getUserAgent()
{
return $this->getHeader('http-user-agent');
}
/**
* Get header value by name
*
* @param string $name
* @param object|null $defaultValue
* @param string|null $defaultValue
*
* @return string|null
*/
public function getHeader($name, $defaultValue = null) {
return isset($this->headers[strtolower($name)]) ? $this->headers[strtolower($name)] : $defaultValue;
public function getHeader($name, $defaultValue = null)
{
if (array_key_exists(strtolower($name), $this->headers) === true) {
return $this->headers[strtolower($name)];
}
$max = count($_SERVER) - 1;
$keys = array_keys($_SERVER);
for ($i = $max; $i >= 0; $i--) {
$key = $keys[$i];
$name = $_SERVER[$key];
if ($key === $name) {
return $name;
}
}
return $defaultValue;
}
/**
* Get input class
* @return Input
*/
public function getInput() {
public function getInput()
{
return $this->input;
}
/**
* Is format accepted
*
* @param string $format
*
* @return bool
*/
public function isFormatAccepted($format) {
return ($this->getHeader('http_accept') !== null && stripos($this->getHeader('http_accept'), $format) > -1);
public function isFormatAccepted($format)
{
return ($this->getHeader('http-accept') !== null && stripos($this->getHeader('http-accept'), $format) > -1);
}
/**
* Get accept formats
* @return array
*/
public function getAcceptFormats() {
return explode(',', $this->getHeader('http_accept'));
public function getAcceptFormats()
{
return explode(',', $this->getHeader('http-accept'));
}
/**
* @param string $uri
* @param Uri $uri
*/
public function setUri($uri) {
public function setUri(Uri $uri)
{
$this->uri = $uri;
}
/**
* @param string $host
*/
public function setHost($host) {
public function setHost($host)
{
$this->host = $host;
}
/**
* @param string $method
*/
public function setMethod($method) {
public function setMethod($method)
{
$this->method = $method;
}
public function __set($name, $value = null) {
/**
* Set rewrite route
*
* @param ILoadableRoute $route
* @return static
*/
public function setRewriteRoute(ILoadableRoute $route)
{
$this->rewriteRoute = $route;
$callback = $route->getCallback();
/* Only add default namespace on relative callbacks */
if ($callback === null || $callback[0] !== '\\') {
$namespace = SimpleRouter::getDefaultNamespace();
if ($namespace !== null) {
if ($this->rewriteRoute->getNamespace() !== null) {
$namespace .= '\\' . $this->rewriteRoute->getNamespace();
}
$this->rewriteRoute->setDefaultNamespace($namespace);
}
}
return $this;
}
/**
* Get rewrite route
*
* @return ILoadableRoute|null
*/
public function getRewriteRoute()
{
return $this->rewriteRoute;
}
/**
* Get rewrite url
*
* @return string
*/
public function getRewriteUrl()
{
return $this->rewriteUrl;
}
/**
* Set rewrite url
*
* @param string $rewriteUrl
* @return static
*/
public function setRewriteUrl($rewriteUrl)
{
$this->rewriteUrl = $rewriteUrl;
return $this;
}
/**
* Set rewrite callback
* @param string $callback
* @return static
*/
public function setRewriteCallback($callback)
{
return $this->setRewriteRoute(new RouteUrl($this->uri, $callback));
}
/**
* Get loaded route
* @return ILoadableRoute|null
*/
public function getLoadedRoute()
{
return $this->loadedRoute;
}
/**
* Set loaded route
*
* @param ILoadableRoute $route
* @return static
*/
public function setLoadedRoute(ILoadableRoute $route)
{
$this->loadedRoute = $route;
return $this;
}
public function __isset($name)
{
return array_key_exists($name, $this->data);
}
public function __set($name, $value = null)
{
$this->data[$name] = $value;
}
public function __get($name) {
public function __get($name)
{
return isset($this->data[$name]) ? $this->data[$name] : null;
}
+44 -26
View File
@@ -2,11 +2,12 @@
namespace Pecee\Http;
class Response {
class Response
{
protected $request;
public function __construct(Request $request) {
public function __construct(Request $request)
{
$this->request = $request;
}
@@ -16,8 +17,10 @@ class Response {
* @param int $code
* @return static
*/
public function httpCode($code) {
public function httpCode($code)
{
http_response_code($code);
return $this;
}
@@ -27,8 +30,9 @@ class Response {
* @param string $url
* @param int $httpCode
*/
public function redirect($url, $httpCode = null) {
if($httpCode !== null) {
public function redirect($url, $httpCode = null)
{
if ($httpCode !== null) {
$this->httpCode($httpCode);
}
@@ -36,8 +40,9 @@ class Response {
die();
}
public function refresh() {
$this->redirect($this->request->getUri());
public function refresh()
{
$this->redirect($this->request->getUri()->getPath());
}
/**
@@ -45,28 +50,31 @@ class Response {
* @param string $name
* @return static
*/
public function auth($name = '') {
public function auth($name = '')
{
$this->headers([
'WWW-Authenticate: Basic realm="' . $name . '"',
'HTTP/1.0 401 Unauthorized'
'HTTP/1.0 401 Unauthorized',
]);
return $this;
}
public function cache($eTag, $lastModified = 2592000) {
public function cache($eTag, $lastModified = 2592000)
{
$this->headers([
'Cache-Control: public',
'Last-Modified: ' . gmdate("D, d M Y H:i:s", $lastModified) . ' GMT',
'Etag: ' . $eTag
'Last-Modified: ' . gmdate('D, d M Y H:i:s', $lastModified) . ' GMT',
'Etag: ' . $eTag,
]);
if(isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) === $lastModified ||
isset($_SERVER['HTTP_IF_NONE_MATCH']) && $_SERVER['HTTP_IF_NONE_MATCH'] === $eTag) {
$httpModified = $this->request->getHeader('http-if-modified-since');
$httpIfNoneMatch = $this->request->getHeader('http-if-none-match');
$this->headers([
'HTTP/1.1 304 Not Modified'
]);
if (($httpIfNoneMatch !== null && $httpIfNoneMatch === $eTag) || ($httpModified !== null && strtotime($httpModified) === $lastModified)) {
$this->header('HTTP/1.1 304 Not Modified');
exit();
}
@@ -75,13 +83,19 @@ class Response {
}
/**
* Json encode array
* @param array $value
* Json encode
* @param array|\JsonSerializable $value
* @throws \InvalidArgumentException;
*/
public function json(array $value) {
public function json($value)
{
if (($value instanceof \JsonSerializable) === false && is_array($value) === false) {
throw new \InvalidArgumentException('Invalid type for parameter "value". Must be of type array or object implementing the \JsonSerializable interface.');
}
$this->header('Content-type: application/json');
echo json_encode($value);
die();
exit(0);
}
/**
@@ -89,8 +103,10 @@ class Response {
* @param string $value
* @return static
*/
public function header($value) {
public function header($value)
{
header($value);
return $this;
}
@@ -99,10 +115,12 @@ class Response {
* @param array $headers
* @return static
*/
public function headers(array $headers) {
foreach($headers as $header) {
header($header);
public function headers(array $headers)
{
foreach ($headers as $header) {
$this->header($header);
}
return $this;
}
+172
View File
@@ -0,0 +1,172 @@
<?php
namespace Pecee\Http;
class Uri
{
private $originalUrl;
private $data = [
'scheme',
'host',
'port',
'user',
'pass',
'path',
'query',
'fragment',
];
public function __construct($url)
{
$this->originalUrl = $url;
$this->data = array_merge($this->data, $this->parseUrl(urldecode($url)));
if (isset($this->data['path']) === true && $this->data['path'] !== '/') {
$this->data['path'] = rtrim($this->data['path'], '/') . '/';
}
}
/**
* Check if url is using a secure protocol like https
* @return bool
*/
public function isSecure()
{
return (strtolower($this->getScheme()) === 'https');
}
/**
* Checks if url is relative
* @return bool
*/
public function isRelative()
{
return ($this->getHost() === null);
}
/**
* Get url scheme
* @return string|null
*/
public function getScheme()
{
return $this->data['scheme'];
}
/**
* Get url host
* @return string|null
*/
public function getHost()
{
return $this->data['host'];
}
/**
* Get url port
* @return int|null
*/
public function getPort()
{
return ($this->data['port'] !== null) ? (int)$this->data['port'] : null;
}
/**
* Parse username from url
* @return string|null
*/
public function getUserName()
{
return $this->data['user'];
}
/**
* Parse password from url
* @return string|null
*/
public function getPassword()
{
return $this->data['pass'];
}
/**
* Get path from url
* @return string
*/
public function getPath()
{
return $this->data['path'];
}
/**
* Get querystring from url
* @return string|null
*/
public function getQueryString()
{
return $this->data['query'];
}
/**
* Get fragment from url (everything after #)
* @return string|null
*/
public function getFragment()
{
return $this->data['fragment'];
}
/**
* @return string
*/
public function getOriginalUrl()
{
return $this->originalUrl;
}
/**
* UTF-8 aware parse_url() replacement.
* @param string $url
* @param int $component
* @throws \InvalidArgumentException
* @return array
*/
public function parseUrl($url, $component = -1)
{
$encodedUrl = preg_replace_callback(
'%[^:/@?&=#]+%u',
function ($matches) {
return urlencode($matches[0]);
},
$url
);
$parts = parse_url($encodedUrl, $component);
if ($parts === false) {
throw new \InvalidArgumentException('Malformed URL: ' . $url);
}
foreach ((array)$parts as $name => $value) {
$parts[$name] = urldecode($value);
}
return $parts;
}
/**
* Returns data array with information about the url
* @return array
*/
public function getData()
{
return $this->data;
}
public function __toString()
{
return $this->getOriginalUrl();
}
}
@@ -0,0 +1,7 @@
<?php
namespace Pecee\SimpleRouter\Exceptions;
class HttpException extends \Exception
{
}
@@ -0,0 +1,7 @@
<?php
namespace Pecee\SimpleRouter\Exceptions;
class NotFoundHttpException extends HttpException
{
}
@@ -1,11 +0,0 @@
<?php
namespace Pecee\SimpleRouter;
interface IControllerRoute {
public function getController();
public function setController($controller);
public function getMethod();
public function setMethod($method);
}
@@ -1,9 +0,0 @@
<?php
namespace Pecee\SimpleRouter;
interface ILoadableRoute {
public function getUrl();
public function setUrl($url);
}
@@ -0,0 +1,15 @@
<?php
namespace Pecee\SimpleRouter;
use Pecee\Http\Request;
interface IRouterBootManager
{
/**
* Called when router loads it's routes
*
* @param Request $request
* @return Request
*/
public function boot(Request $request);
}
@@ -0,0 +1,36 @@
<?php
namespace Pecee\SimpleRouter\Route;
interface IControllerRoute extends IRoute
{
/**
* Get controller class-name
*
* @return string
*/
public function getController();
/**
* Set controller class-name
*
* @param string $controller
* @return static
*/
public function setController($controller);
/**
* Return active method
*
* @return string
*/
public function getMethod();
/**
* Set active method
*
* @param string $method
* @return static
*/
public function setMethod($method);
}
@@ -0,0 +1,70 @@
<?php
namespace Pecee\SimpleRouter\Route;
use Pecee\Handlers\IExceptionHandler;
use Pecee\Http\Request;
interface IGroupRoute extends IRoute
{
/**
* Method called to check if a domain matches
*
* @param Request $request
* @return bool
*/
public function matchDomain(Request $request);
/**
* Add exception handler
*
* @param IExceptionHandler|string $handler
* @return static $this;
*/
public function addExceptionHandler($handler);
/**
* Set exception-handlers for group
*
* @param array $handlers
* @return static $this
*/
public function setExceptionHandlers(array $handlers);
/**
* Get exception-handlers for group
*
* @return array
*/
public function getExceptionHandlers();
/**
* Get domains for domain.
*
* @return array
*/
public function getDomains();
/**
* Set allowed domains for group.
*
* @param array $domains
* @return $this
*/
public function setDomains(array $domains);
/**
* Set prefix that child-routes will inherit.
*
* @param string $prefix
* @return string
*/
public function setPrefix($prefix);
/**
* Get prefix.
*
* @return string
*/
public function getPrefix();
}
@@ -0,0 +1,68 @@
<?php
namespace Pecee\SimpleRouter\Route;
use Pecee\Http\Request;
interface ILoadableRoute extends IRoute
{
/**
* Find url that matches method, parameters or name.
* Used when calling the url() helper.
*
* @param string|null $method
* @param array|null $parameters
* @param string|null $name
* @return string
*/
public function findUrl($method = null, $parameters = null, $name = null);
/**
* Loads and renders middlewares-classes
*
* @param Request $request
*/
public function loadMiddleware(Request $request);
public function getUrl();
public function setUrl($url);
/**
* Returns the provided name for the router.
*
* @return string
*/
public function getName();
/**
* Check if route has given name.
*
* @param string $name
* @return bool
*/
public function hasName($name);
/**
* Sets the router name, which makes it easier to obtain the url or router at a later point.
*
* @param string $name
* @return static $this
*/
public function setName($name);
/**
* Get regular expression match used for matching route (if defined).
*
* @return string
*/
public function getMatch();
/**
* Add regular expression match for the entire route.
*
* @param string $regex
* @return static
*/
public function setMatch($regex);
}
+187
View File
@@ -0,0 +1,187 @@
<?php
namespace Pecee\SimpleRouter\Route;
use Pecee\Http\Request;
interface IRoute
{
/**
* Method called to check if a domain matches
*
* @param string $route
* @param Request $request
* @return bool
*/
public function matchRoute($route, Request $request);
/**
* Called when route is matched.
* Returns class to be rendered.
*
* @param Request $request
* @throws \Pecee\SimpleRouter\Exceptions\NotFoundHttpException
* @return void
*/
public function renderRoute(Request $request);
/**
* Returns callback name/identifier for the current route based on the callback.
* Useful if you need to get a unique identifier for the loaded route, for instance
* when using translations etc.
*
* @return string
*/
public function getIdentifier();
/**
* Set allowed request methods
*
* @param array $methods
* @return static $this
*/
public function setRequestMethods(array $methods);
/**
* Get allowed request methods
*
* @return array
*/
public function getRequestMethods();
/**
* @return IRoute|null
*/
public function getParent();
/**
* Get the group for the route.
*
* @return IGroupRoute|null
*/
public function getGroup();
/**
* Set group
*
* @param IGroupRoute $group
* @return static $this
*/
public function setGroup(IGroupRoute $group);
/**
* Set parent route
*
* @param IRoute $parent
* @return static $this
*/
public function setParent(IRoute $parent);
/**
* Set callback
*
* @param string $callback
* @return static
*/
public function setCallback($callback);
/**
* @return string
*/
public function getCallback();
public function getMethod();
public function getClass();
public function setMethod($method);
/**
* @param string $namespace
* @return static $this
*/
public function setNamespace($namespace);
/**
* @return string
*/
public function getNamespace();
/**
* @param string $namespace
* @return static $this
*/
public function setDefaultNamespace($namespace);
public function getDefaultNamespace();
/**
* Get parameter names.
*
* @return array
*/
public function getWhere();
/**
* Set parameter names.
*
* @param array $options
* @return static
*/
public function setWhere(array $options);
/**
* Get parameters
*
* @return array
*/
public function getParameters();
/**
* Get parameters
*
* @param array $parameters
* @return static $this
*/
public function setParameters(array $parameters);
/**
* Merge with information from another route.
*
* @param array $settings
* @param bool $merge
* @return static $this
*/
public function setSettings(array $settings, $merge = false);
/**
* Export route settings to array so they can be merged with another route.
*
* @return array
*/
public function toArray();
/**
* Get middlewares array
*
* @return array
*/
public function getMiddlewares();
/**
* Set middleware class-name
*
* @param string $middleware
* @return static
*/
public function addMiddleware($middleware);
/**
* Set middlewares array
*
* @param array $middlewares
* @return $this
*/
public function setMiddlewares(array $middlewares);
}
@@ -0,0 +1,249 @@
<?php
namespace Pecee\SimpleRouter\Route;
use Pecee\Http\Middleware\IMiddleware;
use Pecee\Http\Request;
use Pecee\SimpleRouter\Exceptions\HttpException;
abstract class LoadableRoute extends Route implements ILoadableRoute
{
/**
* @var string
*/
protected $url;
/**
* @var string
*/
protected $name;
protected $regex;
/**
* Loads and renders middlewares-classes
*
* @param Request $request
* @throws HttpException
*/
public function loadMiddleware(Request $request)
{
$max = count($this->getMiddlewares());
if ($max > 0) {
for ($i = 0; $i < $max; $i++) {
$middleware = $this->getMiddlewares()[$i];
if (is_object($middleware) === false) {
$middleware = $this->loadClass($middleware);
}
if (($middleware instanceof IMiddleware) === false) {
throw new HttpException($middleware . ' must be inherit the IMiddleware interface');
}
$middleware->handle($request);
}
}
}
public function matchRegex(Request $request, $url)
{
/* Match on custom defined regular expression */
if ($this->regex === null) {
return null;
}
return (preg_match($this->regex, $request->getHost() . $url) > 0);
}
/**
* Set url
*
* @param string $url
* @return static
*/
public function setUrl($url)
{
$this->url = ($url === '/') ? '/' : '/' . trim($url, '/') . '/';
if (strpos($this->url, $this->paramModifiers[0]) !== false) {
$regex = sprintf(static::PARAMETERS_REGEX_FORMAT, $this->paramModifiers[0], $this->paramOptionalSymbol, $this->paramModifiers[1]);
if (preg_match_all('/' . $regex . '/u', $this->url, $matches)) {
$this->parameters = array_fill_keys($matches[1], null);
}
}
return $this;
}
public function getUrl()
{
return $this->url;
}
/**
* Find url that matches method, parameters or name.
* Used when calling the url() helper.
*
* @param string|null $method
* @param string|array|null $parameters
* @param string|null $name
* @return string
*/
public function findUrl($method = null, $parameters = null, $name = null)
{
$url = $this->getUrl();
$group = $this->getGroup();
if ($group !== null && count($group->getDomains()) > 0) {
$url = '//' . $group->getDomains()[0] . $url;
}
/* Contains parameters that aren't recognized and will be appended at the end of the url */
$unknownParams = [];
/* Create the param string - {parameter} */
$param1 = $this->paramModifiers[0] . '%s' . $this->paramModifiers[1];
/* Create the param string with the optional symbol - {parameter?} */
$param2 = $this->paramModifiers[0] . '%s' . $this->paramOptionalSymbol . $this->paramModifiers[1];
/* Replace any {parameter} in the url with the correct value */
$params = $this->getParameters();
$max = count($params) - 1;
$keys = array_keys($params);
for ($i = $max; $i >= 0; $i--) {
$param = $keys[$i];
if ($parameters === '' || (is_array($parameters) && count($parameters) === 0)) {
$value = '';
} else {
$p = (array)$parameters;
$value = array_key_exists($param, $p) ? $p[$param] : $params[$param];
/* If parameter is specifically set to null - use the original-defined value */
if ($value === null && isset($this->originalParameters[$param])) {
$value = $this->originalParameters[$param];
}
}
if (stripos($url, $param1) !== false || stripos($url, $param) !== false) {
/* Add parameter to the correct position */
$url = str_ireplace([sprintf($param1, $param), sprintf($param2, $param)], $value, $url);
} else {
$unknownParams[$param] = $value;
}
}
$url = '/' . ltrim($url, '/');
$url .= join('/', $unknownParams);
return rtrim($url, '/') . '/';
}
/**
* Returns the provided name for the router.
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Check if route has given name.
*
* @param string $name
* @return bool
*/
public function hasName($name)
{
return (strtolower($this->name) === strtolower($name));
}
/**
* Add regular expression match for the entire route.
*
* @param string $regex
* @return static
*/
public function setMatch($regex)
{
$this->regex = $regex;
return $this;
}
/**
* Get regular expression match used for matching route (if defined).
*
* @return string
*/
public function getMatch()
{
return $this->regex;
}
/**
* Sets the router name, which makes it easier to obtain the url or router at a later point.
* Alias for LoadableRoute::setName().
*
* @see LoadableRoute::setName()
* @param string|array $name
* @return static
*/
public function name($name)
{
return $this->setName($name);
}
/**
* Sets the router name, which makes it easier to obtain the url or router at a later point.
*
* @param string $name
* @return static $this
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Merge with information from another route.
*
* @param array $values
* @param bool $merge
* @return static
*/
public function setSettings(array $values, $merge = false)
{
if (isset($values['as'])) {
if ($this->name !== null && $merge !== false) {
$this->setName($values['as'] . '.' . $this->name);
} else {
$this->setName($values['as']);
}
}
if (isset($values['prefix'])) {
$this->setUrl($values['prefix'] . $this->getUrl());
}
parent::setSettings($values, $merge);
return $this;
}
}
+565
View File
@@ -0,0 +1,565 @@
<?php
namespace Pecee\SimpleRouter\Route;
use Pecee\Http\Middleware\IMiddleware;
use Pecee\Http\Request;
use Pecee\SimpleRouter\Exceptions\NotFoundHttpException;
abstract class Route implements IRoute
{
const PARAMETERS_REGEX_FORMAT = '%s([\w]+)(\%s?)%s';
const PARAMETERS_DEFAULT_REGEX = '[\w]+';
const REQUEST_TYPE_GET = 'get';
const REQUEST_TYPE_POST = 'post';
const REQUEST_TYPE_PUT = 'put';
const REQUEST_TYPE_PATCH = 'patch';
const REQUEST_TYPE_OPTIONS = 'options';
const REQUEST_TYPE_DELETE = 'delete';
public static $requestTypes = [
self::REQUEST_TYPE_GET,
self::REQUEST_TYPE_POST,
self::REQUEST_TYPE_PUT,
self::REQUEST_TYPE_PATCH,
self::REQUEST_TYPE_OPTIONS,
self::REQUEST_TYPE_DELETE,
];
/**
* If enabled parameters containing null-value
* will not be passed along to the callback.
*
* @var bool
*/
protected $filterEmptyParams = false;
/**
* Default regular expression used for parsing parameters.
* @var string|null
*/
protected $defaultParameterRegex;
protected $paramModifiers = '{}';
protected $paramOptionalSymbol = '?';
protected $group;
protected $parent;
protected $callback;
protected $defaultNamespace;
/* Default options */
protected $namespace;
protected $requestMethods = [];
protected $where = [];
protected $parameters = [];
protected $originalParameters = [];
protected $middlewares = [];
protected function loadClass($name)
{
if (class_exists($name) === false) {
throw new NotFoundHttpException(sprintf('Class "%s" does not exist', $name), 404);
}
return new $name();
}
public function renderRoute(Request $request)
{
$callback = $this->getCallback();
if ($callback === null) {
return;
}
/* Render callback function */
if (is_callable($callback) === true) {
/* When the callback is a function */
return call_user_func_array($callback, $this->getParameters());
}
/* When the callback is a class + method */
$controller = explode('@', $callback);
$namespace = $this->getNamespace();
$className = ($namespace !== null && $controller[0][0] !== '\\') ? $namespace . '\\' . $controller[0] : $controller[0];
$class = $this->loadClass($className);
$method = $controller[1];
if (method_exists($class, $method) === false) {
throw new NotFoundHttpException(sprintf('Method "%s" does not exist in class "%s"', $method, $className), 404);
}
$parameters = $this->getParameters();
/* Filter parameters with null-value */
if ($this->filterEmptyParams === true) {
$parameters = array_filter($parameters, function ($var) {
return ($var !== null);
});
}
return call_user_func_array([$class, $method], $parameters);
}
protected function parseParameters($route, $url, $parameterRegex = null)
{
$regex = sprintf(static::PARAMETERS_REGEX_FORMAT, $this->paramModifiers[0], $this->paramOptionalSymbol, $this->paramModifiers[1]);
$parameters = [];
// Ensures that hostnames/domains will work with parameters
$url = '/' . ltrim($url, '/');
if (preg_match_all('/' . $regex . '/u', $route, $parameters)) {
$urlParts = preg_split('/((\-?\/?)\{[^}]+\})/', rtrim($route, '/'));
foreach ($urlParts as $key => $t) {
$regex = '';
if ($key < count($parameters[1])) {
$name = $parameters[1][$key];
/* If custom regex is defined, use that */
if (isset($this->where[$name]) === true) {
$regex = $this->where[$name];
} else {
/* If method specific regex is defined use that, otherwise use the default parameter regex */
if ($parameterRegex !== null) {
$regex = $parameterRegex;
} else {
$regex = ($this->defaultParameterRegex === null) ? static::PARAMETERS_DEFAULT_REGEX : $this->defaultParameterRegex;
}
}
$regex = sprintf('(?:\/|\-)%1$s(?P<%2$s>%3$s)%1$s', $parameters[2][$key], $name, $regex);
}
$urlParts[$key] = preg_quote($t, '/') . $regex;
}
$urlRegex = join('', $urlParts);
} else {
$urlRegex = preg_quote($route, '/');
}
if (preg_match('/^' . $urlRegex . '\/?$/u', $url, $matches) > 0) {
$values = [];
if (isset($parameters[1]) === true) {
/* Only take matched parameters with name */
foreach ($parameters[1] as $name) {
$values[$name] = (isset($matches[$name]) && $matches[$name] !== '') ? $matches[$name] : null;
}
}
return $values;
}
return null;
}
/**
* Returns callback name/identifier for the current route based on the callback.
* Useful if you need to get a unique identifier for the loaded route, for instance
* when using translations etc.
*
* @return string
*/
public function getIdentifier()
{
if (is_string($this->callback) === true && strpos($this->callback, '@') !== false) {
return $this->callback;
}
return 'function_' . md5($this->callback);
}
/**
* Set allowed request methods
*
* @param array $methods
* @return static $this
*/
public function setRequestMethods(array $methods)
{
$this->requestMethods = $methods;
return $this;
}
/**
* Get allowed request methods
*
* @return array
*/
public function getRequestMethods()
{
return $this->requestMethods;
}
/**
* @return IRoute|null
*/
public function getParent()
{
return $this->parent;
}
/**
* Get the group for the route.
*
* @return IGroupRoute|null
*/
public function getGroup()
{
return $this->group;
}
/**
* Set group
*
* @param IGroupRoute $group
* @return static $this
*/
public function setGroup(IGroupRoute $group)
{
$this->group = $group;
return $this;
}
/**
* Set parent route
*
* @param IRoute $parent
* @return static $this
*/
public function setParent(IRoute $parent)
{
$this->parent = $parent;
return $this;
}
/**
* Set callback
*
* @param string $callback
* @return static
*/
public function setCallback($callback)
{
$this->callback = $callback;
return $this;
}
/**
* @return string
*/
public function getCallback()
{
return $this->callback;
}
public function getMethod()
{
if (is_string($this->callback) === true && strpos($this->callback, '@') !== false) {
$tmp = explode('@', $this->callback);
return $tmp[1];
}
return null;
}
public function getClass()
{
if (is_string($this->callback) === true && strpos($this->callback, '@') !== false) {
$tmp = explode('@', $this->callback);
return $tmp[0];
}
return null;
}
public function setMethod($method)
{
$this->callback = sprintf('%s@%s', $this->getClass(), $method);
return $this;
}
public function setClass($class)
{
$this->callback = sprintf('%s@%s', $class, $this->getMethod());
return $this;
}
/**
* @param string $namespace
* @return static $this
*/
public function setNamespace($namespace)
{
$this->namespace = $namespace;
return $this;
}
/**
* @param string $namespace
* @return static $this
*/
public function setDefaultNamespace($namespace)
{
$this->defaultNamespace = $namespace;
return $this;
}
public function getDefaultNamespace()
{
return $this->defaultNamespace;
}
/**
* @return string
*/
public function getNamespace()
{
return ($this->namespace === null) ? $this->defaultNamespace : $this->namespace;
}
/**
* Export route settings to array so they can be merged with another route.
*
* @return array
*/
public function toArray()
{
$values = [];
if ($this->namespace !== null) {
$values['namespace'] = $this->namespace;
}
if (count($this->requestMethods) > 0) {
$values['method'] = $this->requestMethods;
}
if (count($this->where) > 0) {
$values['where'] = $this->where;
}
if (count($this->middlewares) > 0) {
$values['middleware'] = $this->middlewares;
}
if ($this->defaultParameterRegex !== null) {
$values['defaultParameterRegex'] = $this->defaultParameterRegex;
}
return $values;
}
/**
* Merge with information from another route.
*
* @param array $values
* @param bool $merge
* @return static $this
*/
public function setSettings(array $values, $merge = false)
{
if ($this->namespace === null && isset($values['namespace'])) {
$this->setNamespace($values['namespace']);
}
if (isset($values['method'])) {
$this->setRequestMethods(array_merge($this->requestMethods, (array)$values['method']));
}
if (isset($values['where'])) {
$this->setWhere(array_merge($this->where, (array)$values['where']));
}
if (isset($values['parameters'])) {
$this->setParameters(array_merge($this->parameters, (array)$values['parameters']));
}
// Push middleware if multiple
if (isset($values['middleware'])) {
$this->setMiddlewares(array_merge((array)$values['middleware'], $this->middlewares));
}
if (isset($values['defaultParameterRegex'])) {
$this->setDefaultParameterRegex($values['defaultParameterRegex']);
}
return $this;
}
/**
* Get parameter names.
*
* @return array
*/
public function getWhere()
{
return $this->where;
}
/**
* Set parameter names.
*
* @param array $options
* @return static
*/
public function setWhere(array $options)
{
$this->where = $options;
return $this;
}
/**
* Add regular expression parameter match.
* Alias for LoadableRoute::where()
*
* @see LoadableRoute::where()
* @param array $options
* @return static
*/
public function where(array $options)
{
return $this->setWhere($options);
}
/**
* Get parameters
*
* @return array
*/
public function getParameters()
{
/* Sort the parameters after the user-defined param order, if any */
$parameters = [];
if (count($this->originalParameters) > 0) {
$parameters = $this->originalParameters;
}
return array_merge($parameters, $this->parameters);
}
/**
* Get parameters
*
* @param array $parameters
* @return static $this
*/
public function setParameters(array $parameters)
{
/*
* If this is the first time setting parameters we store them so we
* later can organize the array, in case somebody tried to sort the array.
*/
if (count($parameters) > 0 && count($this->originalParameters) === 0) {
$this->originalParameters = $parameters;
}
$this->parameters = array_merge($this->parameters, $parameters);
return $this;
}
/**
* Add middleware class-name
*
* @deprecated This method is deprecated and will be removed in the near future.
* @param IMiddleware|string $middleware
* @return static
*/
public function setMiddleware($middleware)
{
$this->middlewares[] = $middleware;
return $this;
}
/**
* Add middleware class-name
*
* @param IMiddleware|string $middleware
* @return static
*/
public function addMiddleware($middleware)
{
$this->middlewares[] = $middleware;
return $this;
}
/**
* Set middlewares array
*
* @param array $middlewares
* @return $this
*/
public function setMiddlewares(array $middlewares)
{
$this->middlewares = $middlewares;
return $this;
}
/**
* @return string|array
*/
public function getMiddlewares()
{
return $this->middlewares;
}
/**
* Set default regular expression used when matching parameters.
* This is used when no custom parameter regex is found.
*
* @param string $regex
* @return static $this
*/
public function setDefaultParameterRegex($regex)
{
$this->defaultParameterRegex = $regex;
return $this;
}
/**
* Get default regular expression used when matching parameters.
*
* @return string
*/
public function getDefaultParameterRegex()
{
return $this->defaultParameterRegex;
}
}
@@ -0,0 +1,180 @@
<?php
namespace Pecee\SimpleRouter\Route;
use Pecee\Http\Request;
class RouteController extends LoadableRoute implements IControllerRoute
{
protected $defaultMethod = 'index';
protected $controller;
protected $method;
protected $names = [];
public function __construct($url, $controller)
{
$this->setUrl($url);
$this->setName(trim(str_replace('/', '.', $url), '/'));
$this->controller = $controller;
}
/**
* Check if route has given name.
*
* @param string $name
* @return bool
*/
public function hasName($name)
{
if ($this->name === null) {
return false;
}
/* Remove method/type */
if (strpos($name, '.') !== false) {
$method = substr($name, strrpos($name, '.') + 1);
$newName = substr($name, 0, strrpos($name, '.'));
if (in_array($method, $this->names, false) === true && strtolower($this->name) === strtolower($newName)) {
return true;
}
}
return parent::hasName($name);
}
/**
* @param string|null $method
* @param string|array|null $parameters
* @param string|null $name
* @return string
*/
public function findUrl($method = null, $parameters = null, $name = null)
{
if (strpos($name, '.') !== false) {
$found = array_search(substr($name, strrpos($name, '.') + 1), $this->names, false);
if ($found !== false) {
$method = (string)$found;
}
}
$url = '';
$parameters = (array)$parameters;
if ($method !== null) {
/* Remove requestType from method-name, if it exists */
foreach (static::$requestTypes as $requestType) {
if (stripos($method, $requestType) === 0) {
$method = (string)substr($method, strlen($requestType));
break;
}
}
$method .= '/';
}
$group = $this->getGroup();
if ($group !== null && count($group->getDomains()) > 0) {
$url .= '//' . $group->getDomains()[0];
}
$url .= '/' . trim($this->getUrl(), '/') . '/' . strtolower($method) . join('/', $parameters);
return '/' . trim($url, '/') . '/';
}
public function matchRoute($url, Request $request)
{
/* Match global regular-expression for route */
$regexMatch = $this->matchRegex($request, $url);
if ($regexMatch === false || (stripos($url, $this->url) !== 0 && strtolower($url) !== strtolower($this->url))) {
return false;
}
$strippedUrl = trim(str_ireplace($this->url, '/', $url), '/');
$path = explode('/', $strippedUrl);
if (count($path) > 0) {
$method = (isset($path[0]) === false || trim($path[0]) === '') ? $this->defaultMethod : $path[0];
$this->method = $request->getMethod() . ucfirst($method);
$this->parameters = array_slice($path, 1);
// Set callback
$this->setCallback($this->controller . '@' . $this->method);
return true;
}
return false;
}
/**
* Get controller class-name.
*
* @return string
*/
public function getController()
{
return $this->controller;
}
/**
* Get controller class-name.
*
* @param string $controller
* @return static
*/
public function setController($controller)
{
$this->controller = $controller;
return $this;
}
/**
* Return active method
*
* @return string
*/
public function getMethod()
{
return $this->method;
}
/**
* Set active method
*
* @param string $method
* @return static
*/
public function setMethod($method)
{
$this->method = $method;
return $this;
}
/**
* Merge with information from another route.
*
* @param array $values
* @param bool $merge
* @return static
*/
public function setSettings(array $values, $merge = false)
{
if (isset($values['names'])) {
$this->names = $values['names'];
}
parent::setSettings($values, $merge);
return $this;
}
}
+198
View File
@@ -0,0 +1,198 @@
<?php
namespace Pecee\SimpleRouter\Route;
use Pecee\Handlers\IExceptionHandler;
use Pecee\Http\Request;
class RouteGroup extends Route implements IGroupRoute
{
protected $prefix;
protected $name;
protected $domains = [];
protected $exceptionHandlers = [];
/**
* Method called to check if a domain matches
*
* @param Request $request
* @return bool
*/
public function matchDomain(Request $request)
{
if ($this->domains === null || count($this->domains) === 0) {
return true;
}
foreach ($this->domains as $domain) {
$parameters = $this->parseParameters($domain, $request->getHost(), '.*');
if ($parameters !== null && count($parameters) > 0) {
$this->parameters = $parameters;
return true;
}
}
return false;
}
/**
* Method called to check if route matches
*
* @param string $url
* @param Request $request
* @return bool
*/
public function matchRoute($url, Request $request)
{
/* Skip if prefix doesn't match */
if ($this->prefix !== null && stripos($url, $this->prefix) === false) {
return false;
}
return $this->matchDomain($request);
}
/**
* Add exception handler
*
* @param IExceptionHandler|string $handler
* @return static $this
*/
public function addExceptionHandler($handler)
{
$this->exceptionHandlers[] = $handler;
return $this;
}
/**
* Set exception-handlers for group
*
* @param array $handlers
* @return static $this
*/
public function setExceptionHandlers(array $handlers)
{
$this->exceptionHandlers = $handlers;
return $this;
}
/**
* Get exception-handlers for group
*
* @return array
*/
public function getExceptionHandlers()
{
return $this->exceptionHandlers;
}
/**
* Get allowed domains for domain.
*
* @return array
*/
public function getDomains()
{
return $this->domains;
}
/**
* Set allowed domains for group.
*
* @param array $domains
* @return $this
*/
public function setDomains(array $domains)
{
$this->domains = $domains;
return $this;
}
/**
* @param string $prefix
* @return static
*/
public function setPrefix($prefix)
{
$this->prefix = '/' . trim($prefix, '/');
return $this;
}
/**
* Set prefix that child-routes will inherit.
*
* @return string
*/
public function getPrefix()
{
return $this->prefix;
}
/**
* Merge with information from another route.
*
* @param array $values
* @param bool $merge
* @return static
*/
public function setSettings(array $values, $merge = false)
{
if (isset($values['prefix'])) {
$this->setPrefix($values['prefix'] . $this->prefix);
}
if (isset($values['exceptionHandler'])) {
$this->setExceptionHandlers((array)$values['exceptionHandler']);
}
if (isset($values['domain'])) {
$this->setDomains((array)$values['domain']);
}
if (isset($values['as'])) {
if ($this->name !== null && $merge !== false) {
$this->name = $values['as'] . '.' . $this->name;
} else {
$this->name = $values['as'];
}
}
parent::setSettings($values, $merge);
return $this;
}
/**
* Export route settings to array so they can be merged with another route.
*
* @return array
*/
public function toArray()
{
$values = [];
if ($this->prefix !== null) {
$values['prefix'] = $this->getPrefix();
}
if ($this->name !== null) {
$values['as'] = $this->name;
}
if (count($this->parameters) > 0) {
$values['parameters'] = $this->parameters;
}
return array_merge($values, parent::toArray());
}
}
@@ -0,0 +1,222 @@
<?php
namespace Pecee\SimpleRouter\Route;
use Pecee\Http\Request;
class RouteResource extends LoadableRoute implements IControllerRoute
{
protected $urls = [
'index' => '',
'create' => 'create',
'store' => '',
'show' => '',
'edit' => 'edit',
'update' => '',
'destroy' => '',
];
protected $methodNames = [
'index' => 'index',
'create' => 'create',
'store' => 'store',
'show' => 'show',
'edit' => 'edit',
'update' => 'update',
'destroy' => 'destroy',
];
protected $names = [];
protected $controller;
public function __construct($url, $controller)
{
$this->setUrl($url);
$this->controller = $controller;
$this->setName(trim(str_replace('/', '.', $url), '/'));
}
/**
* Check if route has given name.
*
* @param string $name
* @return bool
*/
public function hasName($name)
{
if ($this->name === null) {
return false;
}
if (strtolower($this->name) === strtolower($name)) {
return true;
}
/* Remove method/type */
if (strpos($name, '.') !== false) {
$name = (string)substr($name, 0, strrpos($name, '.'));
}
return (strtolower($this->name) === strtolower($name));
}
public function findUrl($method = null, $parameters = null, $name = null)
{
$url = array_search($name, $this->names, false);
if ($url !== false) {
return rtrim($this->url . $this->urls[$url], '/') . '/';
}
return $this->url;
}
protected function call($method)
{
$this->setCallback($this->controller . '@' . $method);
return true;
}
public function matchRoute($url, Request $request)
{
/* Match global regular-expression for route */
$regexMatch = $this->matchRegex($request, $url);
if ($regexMatch === false || (stripos($url, $this->url) !== 0 && strtolower($url) !== strtolower($this->url))) {
return false;
}
$route = rtrim($this->url, '/') . '/{id?}/{action?}';
/* Parse parameters from current route */
$this->parameters = $this->parseParameters($route, $url);
/* If no custom regular expression or parameters was found on this route, we stop */
if ($regexMatch === null && $this->parameters === null) {
return false;
}
$action = strtolower(trim($this->parameters['action']));
$id = $this->parameters['id'];
// Remove action parameter
unset($this->parameters['action']);
$method = $request->getMethod();
// Delete
if ($method === static::REQUEST_TYPE_DELETE && $id !== null) {
return $this->call($this->methodNames['destroy']);
}
// Update
if ($id !== null && in_array($method, [static::REQUEST_TYPE_PATCH, static::REQUEST_TYPE_PUT], false) === true) {
return $this->call($this->methodNames['update']);
}
// Edit
if ($method === static::REQUEST_TYPE_GET && $id !== null && $action === 'edit') {
return $this->call($this->methodNames['edit']);
}
// Create
if ($method === static::REQUEST_TYPE_GET && $id === 'create') {
return $this->call($this->methodNames['create']);
}
// Save
if ($method === static::REQUEST_TYPE_POST) {
return $this->call($this->methodNames['store']);
}
// Show
if ($method === static::REQUEST_TYPE_GET && $id !== null) {
return $this->call($this->methodNames['show']);
}
// Index
return $this->call($this->methodNames['index']);
}
/**
* @return string
*/
public function getController()
{
return $this->controller;
}
/**
* @param string $controller
* @return static
*/
public function setController($controller)
{
$this->controller = $controller;
return $this;
}
public function setName($name)
{
$this->name = $name;
$this->names = [
'index' => $this->name . '.index',
'create' => $this->name . '.create',
'store' => $this->name . '.store',
'show' => $this->name . '.show',
'edit' => $this->name . '.edit',
'update' => $this->name . '.update',
'destroy' => $this->name . '.destroy',
];
return $this;
}
/**
* Define custom method name for resource controller
*
* @param array $names
* @return static $this
*/
public function setMethodNames(array $names)
{
$this->methodNames = $names;
return $this;
}
/**
* Get method names
*
* @return array $this
*/
public function getMethodNames()
{
return $this->methodNames;
}
/**
* Merge with information from another route.
*
* @param array $values
* @param bool $merge
* @return static
*/
public function setSettings(array $values, $merge = false)
{
if (isset($values['names'])) {
$this->names = $values['names'];
}
if (isset($values['methods'])) {
$this->methodNames = $values['methods'];
}
parent::setSettings($values, $merge);
return $this;
}
}
+38
View File
@@ -0,0 +1,38 @@
<?php
namespace Pecee\SimpleRouter\Route;
use Pecee\Http\Request;
class RouteUrl extends LoadableRoute
{
public function __construct($url, $callback)
{
$this->setUrl($url);
$this->setCallback($callback);
}
public function matchRoute($url, Request $request)
{
/* Match global regular-expression for route */
$regexMatch = $this->matchRegex($request, $url);
if ($regexMatch === false) {
return false;
}
/* Parse parameters from current route */
$parameters = $this->parseParameters($this->url, $url);
/* If no custom regular expression or parameters was found on this route, we stop */
if ($regexMatch === null && $parameters === null) {
return false;
}
/* Set the parameters */
$this->setParameters((array)$parameters);
return true;
}
}
+582
View File
@@ -0,0 +1,582 @@
<?php
namespace Pecee\SimpleRouter;
use Pecee\Handlers\IExceptionHandler;
use Pecee\Http\Middleware\BaseCsrfVerifier;
use Pecee\Http\Request;
use Pecee\SimpleRouter\Exceptions\HttpException;
use Pecee\SimpleRouter\Exceptions\NotFoundHttpException;
use Pecee\SimpleRouter\Route\IControllerRoute;
use Pecee\SimpleRouter\Route\IGroupRoute;
use Pecee\SimpleRouter\Route\ILoadableRoute;
use Pecee\SimpleRouter\Route\IRoute;
class Router
{
/**
* The instance of this class
* @var static
*/
protected static $instance;
/**
* Current request
* @var Request
*/
protected $request;
/**
* Defines if a route is currently being processed.
* @var bool
*/
protected $processingRoute;
/**
* All added routes
* @var array
*/
protected $routes;
/**
* List of processed routes
* @var array
*/
protected $processedRoutes;
/**
* Stack of routes used to keep track of sub-routes added
* when a route is being processed.
* @var array
*/
protected $routeStack;
/**
* List of added bootmanagers
* @var array
*/
protected $bootManagers;
/**
* Csrf verifier class
* @var BaseCsrfVerifier
*/
protected $csrfVerifier;
/**
* Get exception handlers
* @var array
*/
protected $exceptionHandlers;
public function __construct()
{
$this->reset();
}
public function reset()
{
$this->processingRoute = false;
$this->request = new Request();
$this->routes = [];
$this->bootManagers = [];
$this->routeStack = [];
$this->processedRoutes = [];
$this->exceptionHandlers = [];
}
/**
* Add route
* @param IRoute $route
* @return IRoute
*/
public function addRoute(IRoute $route)
{
/*
* If a route is currently being processed, that means that the
* route being added are rendered from the parent routes callback,
* so we add them to the stack instead.
*/
if ($this->processingRoute === true) {
$this->routeStack[] = $route;
} else {
$this->routes[] = $route;
}
return $route;
}
/**
* Process added routes.
*
* @param array $routes
* @param IGroupRoute|null $group
* @param IRoute|null $parent
* @throws NotFoundHttpException
*/
protected function processRoutes(array $routes, IGroupRoute $group = null, IRoute $parent = null)
{
// Loop through each route-request
$max = count($routes) - 1;
$exceptionHandlers = [];
$url = ($this->request->getRewriteUrl() !== null) ? $this->request->getRewriteUrl() : $this->request->getUri()->getPath();
/* @var $route IRoute */
for ($i = $max; $i >= 0; $i--) {
$route = $routes[$i];
/* @var $route IGroupRoute */
if ($route instanceof IGroupRoute) {
$group = $route;
$this->processingRoute = true;
$route->renderRoute($this->request);
$this->processingRoute = false;
if ($route->matchRoute($url, $this->request) === true) {
/* Add exception handlers */
if (count($route->getExceptionHandlers()) > 0) {
/** @noinspection AdditionOperationOnArraysInspection */
$exceptionHandlers += $route->getExceptionHandlers();
}
}
}
if ($group !== null) {
/* Add the parent group */
$route->setGroup($group);
}
if ($parent !== null) {
/* Add the parent route */
$route->setParent($parent);
/* Add/merge parent settings with child */
$route->setSettings($parent->toArray(), true);
}
if ($route instanceof ILoadableRoute) {
/* Add the route to the map, so we can find the active one when all routes has been loaded */
$this->processedRoutes[] = $route;
}
if (count($this->routeStack) > 0) {
/* Pop and grab the routes added when executing group callback earlier */
$stack = $this->routeStack;
$this->routeStack = [];
/* Route any routes added to the stack */
$this->processRoutes($stack, $route, $group);
}
}
$this->exceptionHandlers = array_merge($exceptionHandlers, $this->exceptionHandlers);
}
/**
* Load routes
* @throws NotFoundHttpException
* @return void
*/
public function loadRoutes()
{
/* Initialize boot-managers */
if (count($this->bootManagers) > 0) {
$max = count($this->bootManagers) - 1;
/* @var $manager IRouterBootManager */
for ($i = $max; $i >= 0; $i--) {
$manager = $this->bootManagers[$i];
$manager->boot($this->request);
}
}
/* Loop through each route-request */
$this->processRoutes($this->routes);
}
public function routeRequest($rewrite = false)
{
$routeNotAllowed = false;
try {
if ($rewrite === false) {
$this->loadRoutes();
if ($this->csrfVerifier !== null) {
/* Verify csrf token for request */
$this->csrfVerifier->handle($this->request);
}
}
$url = ($this->request->getRewriteUrl() !== null) ? $this->request->getRewriteUrl() : $this->request->getUri()->getPath();
$max = count($this->processedRoutes) - 1;
/* @var $route ILoadableRoute */
for ($i = $max; $i >= 0; $i--) {
$route = $this->processedRoutes[$i];
/* If the route matches */
if ($route->matchRoute($url, $this->request) === true) {
/* Check if request method matches */
if (count($route->getRequestMethods()) > 0 && in_array($this->request->getMethod(), $route->getRequestMethods(), false) === false) {
$routeNotAllowed = true;
continue;
}
$route->loadMiddleware($this->request);
$rewriteRoute = $this->request->getRewriteRoute();
if ($rewriteRoute !== null) {
$rewriteRoute->loadMiddleware($this->request);
return $rewriteRoute->renderRoute($this->request);
}
/* If the request has changed */
$rewriteUrl = $this->request->getRewriteUrl();
if ($rewriteUrl !== null && $rewriteUrl !== $url) {
unset($this->processedRoutes[$i]);
$this->processedRoutes = array_values($this->processedRoutes);
$this->routeRequest(true);
return;
}
/* Render route */
$routeNotAllowed = false;
$this->request->setLoadedRoute($route);
return $route->renderRoute($this->request);
break;
}
}
} catch (\Exception $e) {
$this->handleException($e);
}
if ($routeNotAllowed === true) {
$this->handleException(new HttpException('Route or method not allowed', 403));
}
if ($this->request->getLoadedRoute() === null) {
$rewriteUrl = $this->request->getRewriteUrl();
if ($rewriteUrl !== null) {
$message = sprintf('Route not found: "%s" (rewrite from: "%s")', $rewriteUrl, $this->request->getUri()->getPath());
} else {
$message = sprintf('Route not found: "%s"', $this->request->getUri()->getPath());
}
$this->handleException(new NotFoundHttpException($message, 404));
}
}
/**
* @param \Exception $e
* @throws HttpException
* @throws \Exception
*/
protected function handleException(\Exception $e)
{
$url = ($this->request->getRewriteUrl() !== null) ? $this->request->getRewriteUrl() : $this->request->getUri()->getPath();
$max = count($this->exceptionHandlers);
/* @var $handler IExceptionHandler */
for ($i = 0; $i < $max; $i++) {
$handler = $this->exceptionHandlers[$i];
if (is_object($handler) === false) {
$handler = new $handler();
}
if (($handler instanceof IExceptionHandler) === false) {
throw new HttpException('Exception handler must implement the IExceptionHandler interface.', 500);
}
if ($handler->handleError($this->request, $e) !== null) {
$rewriteRoute = $this->request->getRewriteRoute();
if ($rewriteRoute !== null) {
$rewriteRoute->loadMiddleware($this->request);
return $rewriteRoute->renderRoute($this->request);
}
$rewriteUrl = $this->request->getRewriteUrl();
/* If the request has changed */
if ($rewriteUrl !== null && $rewriteUrl !== $url) {
unset($this->exceptionHandlers[$i]);
$this->exceptionHandlers = array_values($this->exceptionHandlers);
$this->routeRequest(true);
return;
}
}
}
throw $e;
}
public function arrayToParams(array $getParams = [], $includeEmpty = true)
{
if (count($getParams) > 0) {
if ($includeEmpty === false) {
$getParams = array_filter($getParams, function ($item) {
return (trim($item) !== '');
});
}
return '?' . http_build_query($getParams);
}
return '';
}
/**
* Find route by alias, class, callback or method.
*
* @param string $name
* @return ILoadableRoute|null
*/
public function findRoute($name)
{
$max = count($this->processedRoutes) - 1;
/* @var $route ILoadableRoute */
for ($i = $max; $i >= 0; $i--) {
$route = $this->processedRoutes[$i];
/* Check if the name matches with a name on the route. Should match either router alias or controller alias. */
if ($route->hasName($name)) {
return $route;
}
/* Direct match to controller */
if ($route instanceof IControllerRoute && strtolower($route->getController()) === strtolower($name)) {
return $route;
}
/* Using @ is most definitely a controller@method or alias@method */
if (is_string($name) === true && strpos($name, '@') !== false) {
list($controller, $method) = array_map('strtolower', explode('@', $name));
if ($controller === strtolower($route->getClass()) && $method === strtolower($route->getMethod())) {
return $route;
}
}
/* Check if callback matches (if it's not a function) */
if (is_string($name) === true && is_string($route->getCallback()) && strpos($name, '@') !== false && strpos($route->getCallback(), '@') !== false && is_callable($route->getCallback()) === false) {
/* Check if the entire callback is matching */
if (strpos($route->getCallback(), $name) === 0 || strtolower($route->getCallback()) === strtolower($name)) {
return $route;
}
/* Check if the class part of the callback matches (class@method) */
if (strtolower($name) === strtolower($route->getClass())) {
return $route;
}
}
}
return null;
}
/**
* Get url for a route by using either name/alias, class or method name.
*
* The name parameter supports the following values:
* - Route name
* - Controller/resource name (with or without method)
* - Controller class name
*
* When searching for controller/resource by name, you can use this syntax "route.name@method".
* You can also use the same syntax when searching for a specific controller-class "MyController@home".
* If no arguments is specified, it will return the url for the current loaded route.
*
* @param string|null $name
* @param string|array|null $parameters
* @param array|null $getParams
* @throws \InvalidArgumentException
* @return string
*/
public function getUrl($name = null, $parameters = null, $getParams = null)
{
if ($getParams !== null && is_array($getParams) === false) {
throw new \InvalidArgumentException('Invalid type for getParams. Must be array or null');
}
if ($name === '' && $parameters === '') {
return '/';
}
/* Only merge $_GET when all parameters are null */
if ($name === null && $parameters === null && $getParams === null) {
$getParams = $_GET;
} else {
$getParams = (array)$getParams;
}
/* Return current route if no options has been specified */
if ($name === null && $parameters === null) {
return $this->request->getUri()->getPath() . $this->arrayToParams($getParams);
}
$loadedRoute = $this->request->getLoadedRoute();
/* If nothing is defined and a route is loaded we use that */
if ($name === null && $loadedRoute !== null) {
return $loadedRoute->findUrl($loadedRoute->getMethod(), $parameters, $name) . $this->arrayToParams($getParams);
}
/* We try to find a match on the given name */
$route = $this->findRoute($name);
if ($route !== null) {
return $route->findUrl($route->getMethod(), $parameters, $name) . $this->arrayToParams($getParams);
}
/* Using @ is most definitely a controller@method or alias@method */
if (is_string($name) === true && strpos($name, '@') !== false) {
list($controller, $method) = explode('@', $name);
/* Loop through all the routes to see if we can find a match */
$max = count($this->processedRoutes) - 1;
/* @var $route ILoadableRoute */
for ($i = $max; $i >= 0; $i--) {
$route = $this->processedRoutes[$i];
/* Check if the route contains the name/alias */
if ($route->hasName($controller)) {
return $route->findUrl($method, $parameters, $name) . $this->arrayToParams($getParams);
}
/* Check if the route controller is equal to the name */
if ($route instanceof IControllerRoute && strtolower($route->getController()) === strtolower($controller)) {
return $route->findUrl($method, $parameters, $name) . $this->arrayToParams($getParams);
}
}
}
/* No result so we assume that someone is using a hardcoded url and join everything together. */
$url = trim(join('/', array_merge((array)$name, (array)$parameters)), '/');
return (($url === '') ? '/' : '/' . $url . '/') . $this->arrayToParams($getParams);
}
/**
* Get bootmanagers
* @return array
*/
public function getBootManagers()
{
return $this->bootManagers;
}
/**
* Set bootmanagers
* @param array $bootManagers
*/
public function setBootManagers(array $bootManagers)
{
$this->bootManagers = $bootManagers;
}
/**
* Add bootmanager
* @param IRouterBootManager $bootManager
*/
public function addBootManager(IRouterBootManager $bootManager)
{
$this->bootManagers[] = $bootManager;
}
/**
* @return array
*/
public function getRoutes()
{
return $this->routes;
}
/**
* Set routes
*
* @param array $routes
* @return static $this
*/
public function setRoutes(array $routes)
{
$this->routes = $routes;
return $this;
}
/**
* Get current request
*
* @return Request
*/
public function getRequest()
{
return $this->request;
}
/**
* Get csrf verifier class
* @return BaseCsrfVerifier
*/
public function getCsrfVerifier()
{
return $this->csrfVerifier;
}
/**
* Set csrf verifier class
*
* @param BaseCsrfVerifier $csrfVerifier
* @return static
*/
public function setCsrfVerifier(BaseCsrfVerifier $csrfVerifier)
{
$this->csrfVerifier = $csrfVerifier;
return $this;
}
}
-553
View File
@@ -1,553 +0,0 @@
<?php
namespace Pecee\SimpleRouter;
use Pecee\Exception\RouterException;
use Pecee\Handler\IExceptionHandler;
use Pecee\Http\Middleware\BaseCsrfVerifier;
use Pecee\Http\Request;
use Pecee\Http\Response;
class RouterBase {
protected static $instance;
/**
* Current request
* @var Request
*/
protected $request;
/**
* Response
* @var Response
*/
protected $response;
/**
* Used to keep track of whether to add routes to stack or not.
* @var RouterEntry
*/
protected $currentRoute;
/**
* All added routes
* @var array
*/
protected $routes;
/**
* List of
* @var array
*/
protected $controllerUrlMap;
/**
* Backstack array used to keep track of sub-routes
* @var array
*/
protected $backStack;
/**
* The default namespace that all routes will inherit
* @var string
*/
protected $defaultNamespace;
/**
* List of added bootmanagers
* @var array
*/
protected $bootManagers;
/**
* Csrf verifier class
* @var BaseCsrfVerifier
*/
protected $csrfVerifier;
/**
* Get exception handlers
* @var array
*/
protected $exceptionHandlers;
/**
* The current loaded route
* @var RouterRoute|null
*/
protected $loadedRoute;
/**
* List over route changes (to avoid looping)
* @var array
*/
protected $routeChanges;
public function __construct() {
$this->reset();
}
public function reset() {
$this->request = new Request();
$this->response = new Response($this->request);
$this->routes = array();
$this->backStack = array();
$this->controllerUrlMap = array();
$this->bootManagers = array();
$this->exceptionHandlers = array();
$this->routeChanges = array();
}
/**
* Add route
* @param RouterEntry $route
* @return RouterEntry
*/
public function addRoute(RouterEntry $route) {
if($this->currentRoute !== null) {
$this->backStack[] = $route;
} else {
$this->routes[] = $route;
}
return $route;
}
protected function processRoutes(array $routes, array $settings = array(), array $prefixes = array(), $backStack = false, RouterGroup $group = null) {
// Loop through each route-request
$mergedSettings = array();
/* @var $route RouterEntry */
for($i = 0; $i < count($routes); $i++) {
$route = $routes[$i];
if(count($settings)) {
$route->addSettings($settings);
}
if($backStack && $group !== null) {
$route->setGroup($group);
}
if($route->getNamespace() === null && $this->defaultNamespace !== null) {
$namespace = $this->defaultNamespace;
if ($route->getNamespace()) {
$namespace .= '\\' . $route->getNamespace();
}
$route->setNamespace($namespace);
}
if($group !== null && $group->getPrefix() !== null && trim($group->getPrefix(), '/') !== '') {
$prefixes[] = trim($group->getPrefix(), '/');
}
$group = null;
$this->currentRoute = $route;
if($route instanceof ILoadableRoute) {
if(is_array($prefixes) && count($prefixes) && $backStack) {
$route->setUrl( '/' . join('/', $prefixes) . $route->getUrl() );
}
$this->controllerUrlMap[] = $route;
} else {
if(is_callable($route->getCallback())) {
$route->renderRoute($this->request);
if ($route->matchRoute($this->request)) {
/* @var $group RouterGroup */
$group = $route;
$mergedSettings = array_merge($settings, $group->getMergeableSettings());
// Add ExceptionHandler
if ($group->getExceptionHandler() !== null) {
$this->exceptionHandlers[] = $route;
}
}
}
}
$this->currentRoute = null;
if(count($this->backStack)) {
$backStack = $this->backStack;
$this->backStack = array();
// Route any routes added to the backstack
$this->processRoutes($backStack, $mergedSettings, $prefixes, true, $group);
}
}
}
public function routeRequest(Request $newRequest = null) {
$this->loadedRoute = null;
$routeNotAllowed = false;
// Create a fictive request - so it can be changed in the middleware or exceptionhandler later on...
$request = clone $this->request;
try {
// Initialize boot-managers
if(count($this->bootManagers)) {
/* @var $manager RouterBootManager */
foreach($this->bootManagers as $manager) {
$request = $manager->boot($request);
if(!($this->request instanceof Request)) {
throw new RouterException('Custom router bootmanager "'. get_class($manager) .'" must return instance of Request.');
}
}
}
if($newRequest === null) {
// Loop through each route-request
$this->processRoutes($this->routes);
if($this->csrfVerifier !== null) {
// Verify csrf token for request
$this->csrfVerifier->handle($this->request);
}
}
$request = ($newRequest !== null) ? $newRequest : $request;
/* @var $route RouterEntry */
for ($i = 0; $i < count($this->controllerUrlMap); $i++) {
$route = $this->controllerUrlMap[$i];
if ($route->matchRoute($request)) {
if (count($route->getRequestMethods()) && !in_array($request->getMethod(), $route->getRequestMethods())) {
$routeNotAllowed = true;
continue;
}
$routeNotAllowed = false;
$this->loadedRoute = $route;
$request = $this->loadedRoute->loadMiddleware($request, $this->loadedRoute);
$request = ($request === null) ? $this->request : $request;
if($request !== null && $request->getUri() !== $this->request->getUri() && !in_array($request->getUri(), $this->routeChanges)) {
$this->routeChanges[] = $request->getUri();
$this->routeRequest($request);
return;
}
$this->loadedRoute->renderRoute($request);
break;
}
}
} catch(\Exception $e) {
$this->handleException($e);
}
if($routeNotAllowed) {
$this->handleException(new RouterException('Route or method not allowed', 403));
}
if($this->loadedRoute === null) {
$this->handleException(new RouterException(sprintf('Route not found: %s', $request->getUri()), 404));
}
}
protected function handleException(\Exception $e) {
/* @var $route RouterGroup */
foreach ($this->exceptionHandlers as $route) {
$handler = $route->getExceptionHandler();
$handler = new $handler();
if (!($handler instanceof IExceptionHandler)) {
throw new RouterException('Exception handler must implement the IExceptionHandler interface.');
}
$request = $handler->handleError($this->request, $this->loadedRoute, $e);
if($request !== null && !in_array($request->getUri(), $this->routeChanges)) {
$this->routeChanges[] = $request->getUri();
$this->routeRequest($request);
}
}
throw $e;
}
/**
* Get default namespace
* @return string
*/
public function getDefaultNamespace(){
return $this->defaultNamespace;
}
/**
* Set the main default namespace that all routes will inherit
* @param string $defaultNamespace
* @return static
*/
public function setDefaultNamespace($defaultNamespace) {
$this->defaultNamespace = $defaultNamespace;
return $this;
}
/**
* Get bootmanagers
* @return array
*/
public function getBootManagers() {
return $this->bootManagers;
}
/**
* Set bootmanagers
* @param array $bootManagers
*/
public function setBootManagers(array $bootManagers) {
$this->bootManagers = $bootManagers;
}
/**
* Add bootmanager
* @param RouterBootManager $bootManager
*/
public function addBootManager(RouterBootManager $bootManager) {
$this->bootManagers[] = $bootManager;
}
/**
* @return array
*/
public function getRoutes(){
return $this->routes;
}
/**
* Get current request
*
* @return Request
*/
public function getRequest() {
return $this->request;
}
/**
* Get response
* @return Response
*/
public function getResponse() {
return $this->response;
}
/**
* Get csrf verifier class
* @return BaseCsrfVerifier
*/
public function getCsrfVerifier() {
return $this->csrfVerifier;
}
/**
* Set csrf verifier class
*
* @param BaseCsrfVerifier $csrfVerifier
* @return static
*/
public function setCsrfVerifier(BaseCsrfVerifier $csrfVerifier) {
$this->csrfVerifier = $csrfVerifier;
return $this;
}
public function arrayToParams(array $getParams = null, $includeEmpty = true) {
if(is_array($getParams) && count($getParams)) {
if ($includeEmpty === false) {
$getParams = array_filter($getParams, function ($item) {
return (!empty($item));
});
}
return '?' . http_build_query($getParams);
}
return '';
}
protected function processUrl(RouterRoute $route, $method = null, $parameters = null, $getParams = null) {
$domain = '';
if($route->getGroup() !== null && $route->getGroup()->getDomain() !== null) {
if(is_array($route->getGroup()->getDomain())) {
$domains = $route->getGroup()->getDomain();
$domain = array_shift($domains);
} else {
$domain = $route->getGroup()->getDomain();
}
$domain = '//' . $domain;
}
$url = $domain . '/' . trim($route->getUrl(), '/');
if($route instanceof IControllerRoute && $method !== null) {
$url .= $method;
if(count($parameters)) {
$url .= join('/', $parameters);
}
} else {
if($parameters !== null && is_array($parameters)) {
$params = array_merge($route->getParameters(), $parameters);
} else {
$params = $route->getParameters();
}
$otherParams = array();
$i = 0;
foreach($params as $param => $value) {
$value = (isset($parameters[$param])) ? $parameters[$param] : $value;
if(stripos($url, '{' . $param. '}') !== false || stripos($url, '{' . $param . '?}') !== false) {
$url = str_ireplace(array('{' . $param . '}', '{' . $param . '?}'), $value, $url);
} else {
$otherParams[$param] = $value;
}
$i++;
}
$url = rtrim($url, '/') . '/' . join('/', $otherParams);
}
$url = rtrim($url, '/') . '/';
if($getParams !== null) {
$url .= $this->arrayToParams($getParams);
}
return $url;
}
public function getRoute($controller = null, $parameters = null, $getParams = null) {
if($parameters !== null && !is_array($parameters)) {
throw new \InvalidArgumentException('Invalid type for parameter. Must be array or null');
}
if($getParams !== null && !is_array($getParams)) {
throw new \InvalidArgumentException('Invalid type for getParams. Must be array or null');
}
// Return current route if no options has been specified
if($controller === null && $parameters === null) {
$getParams = ($getParams !== null && is_array($getParams)) ? array_merge($_GET, $getParams) : $_GET;
$url = parse_url($this->request->getUri(), PHP_URL_PATH);
if($getParams !== null) {
$url .= $this->arrayToParams($getParams);
}
return $url;
}
if($controller === null && $this->loadedRoute !== null) {
return $this->processUrl($this->loadedRoute, $this->loadedRoute->getMethod(), $parameters, $getParams);
}
$c = '';
$method = null;
$max = count($this->controllerUrlMap);
/* @var $route RouterRoute */
for($i = 0; $i < $max; $i++) {
$route = $this->controllerUrlMap[$i];
// Check an alias exist, if the matches - use it
if($route instanceof IControllerRoute) {
$c = $route->getController();
} else {
if($route->hasAlias($controller)) {
return $this->processUrl($route, $route->getMethod(), $parameters, $getParams);
}
if(!is_callable($route->getCallback()) && stripos($route->getCallback(), '@') !== false) {
$c = $route->getCallback();
}
}
if($c === $controller || strpos($c, $controller) === 0) {
return $this->processUrl($route, $route->getMethod(), $parameters, $getParams);
}
}
$c = '';
// No match has yet been found, let's try to guess what url that should be returned
for($i = 0; $i < $max; $i++) {
$route = $this->controllerUrlMap[$i];
if($route instanceof IControllerRoute) {
$c = $route->getController();
} else if(!is_callable($route->getCallback()) && stripos($route->getCallback(), '@') !== false) {
$c = $route->getClass();
}
if(stripos($controller, '@') !== false) {
$tmp = explode('@', $controller);
$controller = $tmp[0];
$method = $tmp[1];
}
if($controller === $c) {
return $this->processUrl($route, $method, $parameters, $getParams);
}
}
$controller = ($controller === null) ? '/' : $controller;
$url = array($controller);
if($parameters !== null && is_array($parameters) && count($parameters)) {
$url = array_merge($url, $parameters);
}
$url = '/' . trim(join('/', $url), '/') . '/';
if($getParams !== null) {
$url .= $this->arrayToParams($getParams);
}
return $url;
}
/**
* Get current router instance
* @return static
*/
public static function getInstance() {
if(static::$instance === null) {
static::$instance = new static();
}
return static::$instance;
}
}
@@ -1,10 +0,0 @@
<?php
namespace Pecee\SimpleRouter;
use Pecee\Http\Request;
abstract class RouterBootManager {
abstract public function boot(Request $request);
}
-121
View File
@@ -1,121 +0,0 @@
<?php
namespace Pecee\SimpleRouter;
use Pecee\Exception\RouterException;
use Pecee\Http\Request;
class RouterController extends RouterEntry implements ILoadableRoute, IControllerRoute {
const DEFAULT_METHOD = 'index';
protected $url;
protected $controller;
protected $method;
public function __construct($url, $controller) {
$this->url = $url;
$this->controller = $controller;
}
public function renderRoute(Request $request) {
if(is_object($this->getCallback()) && is_callable($this->getCallback())) {
// When the callback is a function
call_user_func_array($this->getCallback(), $this->getParameters());
} else {
// When the callback is a method
$controller = explode('@', $this->getCallback());
$className = $this->getNamespace() . '\\' . $controller[0];
$class = $this->loadClass($className);
$method = $request->getMethod() . ucfirst($controller[1]);
if (!method_exists($class, $method)) {
throw new RouterException(sprintf('Method %s does not exist in class %s', $method, $className), 404);
}
call_user_func_array(array($class, $method), $this->getParameters());
return $class;
}
return null;
}
public function matchRoute(Request $request) {
$url = parse_url(urldecode($request->getUri()), PHP_URL_PATH);
$url = rtrim($url, '/') . '/';
if(strtolower($url) == strtolower($this->url) || stripos($url, $this->url) === 0) {
$strippedUrl = trim(str_ireplace($this->url, '/', $url), '/');
$path = explode('/', $strippedUrl);
if(count($path)) {
$method = (!isset($path[0]) || trim($path[0]) === '') ? static::DEFAULT_METHOD : $path[0];
$this->method = $method;
array_shift($path);
$this->settings['parameters'] = $path;
// Set callback
$this->setCallback($this->controller . '@' . $this->method);
return true;
}
}
return null;
}
/**
* @return string
*/
public function getUrl() {
return $this->url;
}
/**
* @param string $url
* @return static
*/
public function setUrl($url) {
$url = rtrim($url, '/') . '/';
$this->url = $url;
return $this;
}
/**
* @return string
*/
public function getController() {
return $this->controller;
}
/**
* @param string $controller
* @return static
*/
public function setController($controller) {
$this->controller = $controller;
return $this;
}
/**
* @return string
*/
public function getMethod() {
return $this->method;
}
/**
* @param string $method
* @return static
*/
public function setMethod($method) {
$this->method = $method;
return $this;
}
}
-359
View File
@@ -1,359 +0,0 @@
<?php
namespace Pecee\SimpleRouter;
use Pecee\Exception\RouterException;
use Pecee\Http\Middleware\IMiddleware;
use Pecee\Http\Request;
abstract class RouterEntry {
const REQUEST_TYPE_POST = 'post';
const REQUEST_TYPE_GET = 'get';
const REQUEST_TYPE_PUT = 'put';
const REQUEST_TYPE_PATCH = 'patch';
const REQUEST_TYPE_DELETE = 'delete';
public static $allowedRequestTypes = [
self::REQUEST_TYPE_DELETE,
self::REQUEST_TYPE_GET,
self::REQUEST_TYPE_POST,
self::REQUEST_TYPE_PUT,
self::REQUEST_TYPE_PATCH,
];
protected $settings = [
'requestMethods' => array(),
'where' => array(),
'parameters' => array(),
'middleware' => array(),
];
protected $callback;
/**
* @param string $callback
* @return static
*/
public function setCallback($callback) {
$this->callback = $callback;
return $this;
}
/**
* @return mixed
*/
public function getCallback() {
return $this->callback;
}
public function getMethod() {
if(strpos($this->callback, '@') !== false) {
$tmp = explode('@', $this->callback);
return $tmp[1];
}
return null;
}
public function getClass() {
if(strpos($this->callback, '@') !== false) {
$tmp = explode('@', $this->callback);
return $tmp[0];
}
return null;
}
public function setMethod($method) {
$this->callback = sprintf('%s@%s', $this->getClass(), $method);
return $this;
}
public function setClass($class) {
$this->callback = sprintf('%s@%s', $class, $this->getMethod());
return $this;
}
/**
* @param string $middleware
* @return static
*/
public function setMiddleware($middleware) {
$this->settings['middleware'][] = $middleware;
return $this;
}
/**
* @param string $namespace
* @return static
*/
public function setNamespace($namespace) {
$this->settings['namespace'] = $namespace;
return $this;
}
/**
* @return string|array
*/
public function getMiddleware() {
return $this->settingArray('middleware');
}
/**
* @return string
*/
public function getNamespace() {
return $this->setting('namespace');
}
/**
* @return array
*/
public function getSettings() {
return $this->settings;
}
/**
* @return array
*/
public function getParameters(){
return $this->setting('parameters', array());
}
/**
* @param mixed $parameters
* @return static
*/
public function setParameters($parameters) {
$this->settings['parameters'] = $parameters;
return $this;
}
/**
* Add regular expression parameter match
*
* @param array $options
* @return static
*/
public function where(array $options) {
$this->settings['where'] = array_merge($this->settings['where'], $options);
return $this;
}
/**
* Add regular expression match for url
*
* @param string $regex
* @return static
*/
public function match($regex) {
$this->settings['regexMatch'] = $regex;
return $this;
}
/**
* Get settings that are allowed to be inherited by child routes.
*
* @return array
*/
public function getMergeableSettings() {
return $this->settings;
}
/**
* @param array $settings
* @return static
*/
public function addSettings(array $settings) {
$this->settings = array_merge($this->settings, $settings);
return $this;
}
/**
* @param array $settings
* @return static
*/
public function setSettings($settings) {
$this->settings = $settings;
return $this;
}
protected function loadClass($name) {
if(!class_exists($name)) {
throw new RouterException(sprintf('Class %s does not exist', $name));
}
return new $name();
}
protected function parseParameters($route, $url, $parameterRegex = '[\w]+') {
$parameterNames = array();
$regex = '';
$lastCharacter = '';
$isParameter = false;
$parameter = '';
$routeLength = strlen($route);
for($i = 0; $i < $routeLength; $i++) {
$character = $route[$i];
if($character === '{') {
// Remove "/" and "\" from regex
if(substr($regex, strlen($regex)-1) === '/') {
$regex = substr($regex, 0, strlen($regex) - 2);
}
$isParameter = true;
} elseif($isParameter && $character === '}') {
$required = true;
// Check for optional parameter
// Use custom parameter regex if it exists
if(is_array($this->setting('where')) && isset($this->settings['where'][$parameter])) {
$parameterRegex = $this->settings['where'][$parameter];
}
if($lastCharacter === '?') {
$parameter = substr($parameter, 0, strlen($parameter)-1);
$regex .= '(?:\/?(?P<' . $parameter . '>'. $parameterRegex .')[^\/]?)?';
$required = false;
} else {
$regex .= '\/?(?P<' . $parameter . '>'. $parameterRegex .')[^\/]?';
}
$parameterNames[] = [
'name' => $parameter,
'required' => $required
];
$parameter = '';
$isParameter = false;
} elseif($isParameter) {
$parameter .= $character;
} elseif($character === '/') {
$regex .= '\\' . $character;
} else {
$regex .= str_replace('.', '\\.', $character);
}
$lastCharacter = $character;
}
$parameterValues = array();
if(preg_match('/^'.$regex.'\/?$/is', $url, $parameterValues)) {
$parameters = array();
$max = count($parameterNames);
for($i = 0; $i < $max; $i++) {
$name = $parameterNames[$i];
$parameterValue = isset($parameterValues[$name['name']]) ? $parameterValues[$name['name']] : null;
if($name['required'] && $parameterValue === null) {
throw new RouterException('Missing required parameter ' . $name['name'], 404);
}
if(!$name['required'] && $parameterValue === null) {
continue;
}
$parameters[$name['name']] = $parameterValue;
}
return $parameters;
}
return null;
}
public function loadMiddleware(Request $request, RouterEntry &$route) {
if(count($this->getMiddleware())) {
foreach($this->getMiddleware() as $middleware) {
$middleware = $this->loadClass($middleware);
if (!($middleware instanceof IMiddleware)) {
throw new RouterException($middleware . ' must be instance of Middleware');
}
/* @var $class IMiddleware */
$middleware->handle($request, $route);
}
}
}
public function renderRoute(Request $request) {
if(is_object($this->getCallback()) && is_callable($this->getCallback())) {
// When the callback is a function
call_user_func_array($this->getCallback(), $this->getParameters());
} else {
// When the callback is a method
$controller = explode('@', $this->getCallback());
$className = $this->getNamespace() . '\\' . $controller[0];
$class = $this->loadClass($className);
$method = $controller[1];
if (!method_exists($class, $method)) {
throw new RouterException(sprintf('Method %s does not exist in class %s', $method, $className), 404);
}
$parameters = array_filter($this->getParameters(), function($var){
return ($var !== null);
});
call_user_func_array(array($class, $method), $parameters);
return $class;
}
return null;
}
/**
* Set allowed request methods
*
* @param array $methods
* @return static $this
*/
public function setRequestMethods(array $methods) {
$this->settings['requestMethods'] = $methods;
return $this;
}
/**
* Get allowed request methods
*
* @return array
*/
public function getRequestMethods() {
return $this->settingArray('requestMethods');
}
public function getGroup() {
return $this->setting('group');
}
public function setGroup($group) {
$this->settings['group'] = $group;
return $this;
}
protected function setting($name, $defaultValue = null) {
return isset($this->settings[$name]) ? $this->settings[$name] : $defaultValue;
}
protected function settingArray($name) {
$value = $this->setting($name);
if($value === null) {
return [];
}
return (!is_array($value)) ? array($value) : $value;
}
abstract function matchRoute(Request $request);
}
-138
View File
@@ -1,138 +0,0 @@
<?php
namespace Pecee\SimpleRouter;
use Pecee\Exception\RouterException;
use Pecee\Http\Request;
class RouterGroup extends RouterEntry {
public function matchDomain(Request $request) {
if($this->setting('domain') !== null) {
if(is_array($this->setting('domain'))) {
for($i = 0; $i < count($this->setting('domain')); $i++) {
$domain = $this->settings['domain'][$i];
$parameters = $this->parseParameters($domain, $request->getHost(), '[^.]*');
if($parameters !== null) {
$this->settings['parameters'] = $parameters;
return true;
}
}
return false;
}
$parameters = $this->parseParameters($this->setting('domain'), $request->getHost(), '[^.]*');
if ($parameters !== null) {
$this->settings['parameters'] = $parameters;
return true;
}
return false;
}
return true;
}
public function renderRoute(Request $request) {
// Check if request method is allowed
$hasAccess = true;
if($this->setting('method') !== null) {
if(is_array($this->setting('method'))) {
$hasAccess = (in_array($request->getMethod(), $this->getRequestMethods()));
} else {
$hasAccess = strtolower($this->getRequestMethods()) == strtolower($request->getMethod());
}
}
if(!$hasAccess) {
throw new RouterException('Method not allowed');
}
$this->matchDomain($request);
return parent::renderRoute($request);
}
public function matchRoute(Request $request) {
// Skip if prefix doesn't match
if($this->getPrefix() !== null && stripos($request->getUri(), $this->getPrefix()) === false) {
return false;
}
return $this->matchDomain($request);
}
public function setExceptionHandler($class) {
$this->settings['exceptionHandler'] = $class;
return $this;
}
public function getExceptionHandler() {
return $this->setting('exceptionHandler');
}
public function getDomain() {
return $this->setting('domain');
}
/**
* @param string $prefix
* @return static
*/
public function setPrefix($prefix) {
$this->settings['prefix'] = '/' . ltrim($prefix, '/');
return $this;
}
/**
* @return string
*/
public function getPrefix() {
return $this->setting('prefix');
}
/**
* @param array $settings
* @return static
*/
public function addSettings(array $settings) {
if ($this->getNamespace() !== null && isset($settings['namespace'])) {
unset($settings['namespace']);
}
// Push middleware if multiple
if ($this->getMiddleware() !== null && isset($settings['middleware'])) {
if (!is_array($settings['middleware'])) {
$settings['middleware'] = array_merge($this->getMiddleware(), array($settings['middleware']));
} else {
$settings['middleware'][] = $this->getMiddleware();
}
$settings['middleware'] = array_unique(array_reverse($settings['middleware']));
}
$this->settings = array_merge($this->settings, $settings);
return $this;
}
public function getMergeableSettings() {
$settings = $this->settings;
if(isset($settings['prefix'])) {
unset($settings['prefix']);
}
return $settings;
}
}
-132
View File
@@ -1,132 +0,0 @@
<?php
namespace Pecee\SimpleRouter;
use Pecee\Exception\RouterException;
use Pecee\Http\Request;
class RouterResource extends RouterEntry implements ILoadableRoute, IControllerRoute {
protected $url;
protected $controller;
public function __construct($url, $controller) {
$this->url = $url;
$this->controller = $controller;
}
public function renderRoute(Request $request) {
if(is_object($this->getCallback()) && is_callable($this->getCallback())) {
// When the callback is a function
call_user_func_array($this->getCallback(), $this->getParameters());
} else {
// When the callback is a method
$controller = explode('@', $this->getCallback());
$className = $this->getNamespace() . '\\' . $controller[0];
$class = $this->loadClass($className);
$method = strtolower($controller[1]);
if (!method_exists($class, $method)) {
throw new RouterException(sprintf('Method %s does not exist in class %s', $method, $className), 404);
}
call_user_func_array(array($class, $method), $this->getParameters());
return $class;
}
return null;
}
protected function call($method, $parameters) {
$this->setCallback($this->controller . '@' . $method);
$this->settings['parameters'] = $parameters;
return true;
}
public function matchRoute(Request $request) {
$url = parse_url(urldecode($request->getUri()), PHP_URL_PATH);
$url = rtrim($url, '/') . '/';
$route = rtrim($this->url, '/') . '/{id?}/{action?}';
$parameters = $this->parseParameters($route, $url);
if($parameters !== null) {
if(is_array($parameters)) {
$parameters = array_merge($this->settings['parameters'], $parameters);
}
$action = isset($parameters['action']) ? $parameters['action'] : null;
unset($parameters['action']);
// Delete
if($request->getMethod() === static::REQUEST_TYPE_DELETE && $request->getMethod() === static::REQUEST_TYPE_POST) {
return $this->call('destroy', $parameters);
}
// Update
if(in_array($request->getMethod(), array(static::REQUEST_TYPE_PATCH, static::REQUEST_TYPE_PUT)) && $request->getMethod() === static::REQUEST_TYPE_POST) {
return $this->call('update', $parameters);
}
// Edit
if(isset($action) && strtolower($action) === 'edit' && $request->getMethod() === static::REQUEST_TYPE_GET) {
return $this->call('edit', $parameters);
}
// Create
if(strtolower($action) === 'create' && $request->getMethod() === static::REQUEST_TYPE_GET) {
return $this->call('create', $parameters);
}
// Save
if($request->getMethod() === static::REQUEST_TYPE_POST) {
return $this->call('store', $parameters);
}
// Show
if(isset($parameters['id']) && $request->getMethod() === static::REQUEST_TYPE_GET) {
return $this->call('show', $parameters);
}
// Index
return $this->call('index', $parameters);
}
return null;
}
/**
* @return string
*/
public function getUrl() {
return $this->url;
}
/**
* @param string $url
* @return static
*/
public function setUrl($url) {
$this->url = rtrim($url, '/') . '/';
return $this;
}
/**
* @return string
*/
public function getController() {
return $this->controller;
}
/**
* @param string $controller
* @return static
*/
public function setController($controller) {
$this->controller = $controller;
return $this;
}
}
-127
View File
@@ -1,127 +0,0 @@
<?php
namespace Pecee\SimpleRouter;
use Pecee\Http\Request;
class RouterRoute extends RouterEntry implements ILoadableRoute {
const PARAMETERS_REGEX_MATCH = '{([A-Za-z\-\_]*?)\?{0,1}}';
protected $url;
public function __construct($url, $callback) {
$this->setUrl($url);
$this->setCallback($callback);
}
public function matchRoute(Request $request) {
$url = parse_url(urldecode($request->getUri()), PHP_URL_PATH);
$url = rtrim($url, '/') . '/';
// Match on custom defined regular expression
if($this->setting('regexMatch') !== null) {
$parameters = array();
if(preg_match('/(' . $this->setting('regexMatch') . ')/is', $request->getHost() . $url, $parameters)) {
$this->settings['parameters'] = (!is_array($parameters[0]) ? array($parameters[0]) : $parameters[0]);
return true;
}
return null;
}
// Make regular expression based on route
$route = rtrim($this->url, '/') . '/';
$parameters = $this->parseParameters($route, $url);
if($parameters !== null) {
$this->settings['parameters'] = array_merge($this->settingArray('parameters'), $parameters);
return true;
}
return null;
}
/**
* @return string
*/
public function getUrl() {
return $this->url;
}
/**
* @param string $url
* @return static
*/
public function setUrl($url) {
$parameters = array();
$matches = array();
if(preg_match_all('/' . static::PARAMETERS_REGEX_MATCH . '/is', $url, $matches)) {
$parameters = $matches[1];
}
if(count($parameters)) {
foreach(array_keys($parameters) as $key) {
$parameters[$key] = null;
}
$this->settings['parameters'] = $parameters;
}
$this->url = $url;
return $this;
}
/**
* Get alias for the url which can be used when getting the url route.
* @return string|array
*/
public function getAlias(){
return $this->setting('alias');
}
/**
* Check if route has given alias.
*
* @param string $name
* @return bool
*/
public function hasAlias($name) {
if ($this->getAlias() !== null) {
if (is_array($this->getAlias())) {
foreach ($this->setting('alias') as $alias) {
if (strtolower($alias) === strtolower($name)) {
return true;
}
}
}
return strtolower($this->getAlias()) === strtolower($name);
}
return false;
}
/**
* Set the url alias for easier getting the url route.
* @param string|array $alias
* @return static
*/
public function setAlias($alias){
$this->settings['alias'] = $alias;
return $this;
}
public function addSettings(array $settings) {
// Change as to alias
if(isset($settings['as'])) {
$this->setAlias($settings['as']);
}
return parent::addSettings($settings);
}
}
+352 -73
View File
@@ -1,129 +1,235 @@
<?php
/**
* ---------------------------
* Router helper class
* ---------------------------
* This class is added so calls can be made statically like Router::get() making the code look more pretty.
*
* This class is added so calls can be made statically like Router::get() making the code look pretty.
* It also adds some extra functionality like default-namespace.
*/
namespace Pecee\SimpleRouter;
use Pecee\Handlers\CallbackExceptionHandler;
use Pecee\Http\Middleware\BaseCsrfVerifier;
use Pecee\Http\Response;
use Pecee\SimpleRouter\Exceptions\HttpException;
use Pecee\SimpleRouter\Exceptions\NotFoundHttpException;
use Pecee\SimpleRouter\Route\IRoute;
use Pecee\SimpleRouter\Route\RouteController;
use Pecee\SimpleRouter\Route\RouteGroup;
use Pecee\SimpleRouter\Route\RouteResource;
use Pecee\SimpleRouter\Route\RouteUrl;
class SimpleRouter {
class SimpleRouter
{
/**
* Default namespace added to all routes
* @var string
*/
protected static $defaultNamespace;
/**
* The response object
* @var Response
*/
protected static $response;
/**
* Router instance
* @var Router
*/
protected static $router;
/**
* Start/route request
* @throws \Pecee\Exception\RouterException
*
* @throws HttpException
* @throws NotFoundHttpException
*/
public static function start() {
RouterBase::getInstance()->routeRequest();
public static function start()
{
echo static::router()->routeRequest();
}
/**
* Set default namespace for all routes
* Set default namespace which will be prepended to all routes.
*
* @param string $defaultNamespace
*/
public static function setDefaultNamespace($defaultNamespace) {
RouterBase::getInstance()->setDefaultNamespace($defaultNamespace);
public static function setDefaultNamespace($defaultNamespace)
{
static::$defaultNamespace = $defaultNamespace;
}
/**
* Set base csrf verifier
* Base CSRF verifier
*
* @param BaseCsrfVerifier $baseCsrfVerifier
*/
public static function csrfVerifier(BaseCsrfVerifier $baseCsrfVerifier) {
RouterBase::getInstance()->setCsrfVerifier($baseCsrfVerifier);
public static function csrfVerifier(BaseCsrfVerifier $baseCsrfVerifier)
{
static::router()->setCsrfVerifier($baseCsrfVerifier);
}
public static function addBootManager(RouterBootManager $bootManager) {
RouterBase::getInstance()->addBootManager($bootManager);
/**
* Boot managers allows you to alter the routes before the routing occurs.
* Perfect if you want to load pretty-urls from a file or database.
*
* @param IRouterBootManager $bootManager
*/
public static function addBootManager(IRouterBootManager $bootManager)
{
static::router()->addBootManager($bootManager);
}
public static function get($url, $callback, array $settings = null) {
/**
* Route the given url to your callback on GET request method.
*
* @param string $url
* @param string|\Closure $callback
* @param array|null $settings
* @return RouteUrl
*/
public static function get($url, $callback, array $settings = null)
{
return static::match(['get'], $url, $callback, $settings);
}
public static function post($url, $callback, array $settings = null) {
/**
* Route the given url to your callback on POST request method.
*
* @param string $url
* @param string|\Closure $callback
* @param array|null $settings
* @return RouteUrl
*/
public static function post($url, $callback, array $settings = null)
{
return static::match(['post'], $url, $callback, $settings);
}
public static function put($url, $callback, array $settings = null) {
/**
* Route the given url to your callback on PUT request method.
*
* @param string $url
* @param string|\Closure $callback
* @param array|null $settings
* @return RouteUrl
*/
public static function put($url, $callback, array $settings = null)
{
return static::match(['put'], $url, $callback, $settings);
}
public static function delete($url, $callback, array $settings = null) {
/**
* Route the given url to your callback on PATCH request method.
*
* @param string $url
* @param string|\Closure $callback
* @param array|null $settings
* @return RouteUrl
*/
public static function patch($url, $callback, array $settings = null)
{
return static::match(['patch'], $url, $callback, $settings);
}
/**
* Route the given url to your callback on OPTIONS request method.
*
* @param string $url
* @param string|\Closure $callback
* @param array|null $settings
* @return RouteUrl
*/
public static function options($url, $callback, array $settings = null)
{
return static::match(['options'], $url, $callback, $settings);
}
/**
* Route the given url to your callback on DELETE request method.
*
* @param string $url
* @param string|\Closure $callback
* @param array|null $settings
* @return RouteUrl
*/
public static function delete($url, $callback, array $settings = null)
{
return static::match(['delete'], $url, $callback, $settings);
}
public static function group($settings = array(), $callback) {
$group = new RouterGroup();
/**
* Groups allows for encapsulating routes with special settings.
*
* @param array $settings
* @param \Closure $callback
* @throws \InvalidArgumentException
* @return RouteGroup
*/
public static function group(array $settings = [], \Closure $callback)
{
$group = new RouteGroup();
$group->setCallback($callback);
$group->setSettings($settings);
if($settings !== null && is_array($settings)) {
$group->setSettings($settings);
if (is_callable($callback) === false) {
throw new \InvalidArgumentException('Invalid callback provided. Only functions or methods supported');
}
RouterBase::getInstance()->addRoute($group);
static::router()->addRoute($group);
return $group;
}
/**
* Adds get + post route
* Alias for the form method
*
* @param string $url
* @param callable $callback
* @param array|null $settings
* @return RouterRoute
* @see SimpleRouter::form
* @return RouteUrl
*/
public static function basic($url, $callback, array $settings = null) {
public static function basic($url, $callback, array $settings = null)
{
return static::match(['get', 'post'], $url, $callback, $settings);
}
public static function match(array $requestMethods, $url, $callback, array $settings = null) {
$route = new RouterRoute($url, $callback);
/**
* This type will route the given url to your callback on the provided request methods.
* Route the given url to your callback on POST and GET request method.
*
* @param string $url
* @param string|\Closure $callback
* @param array|null $settings
* @see SimpleRouter::form
* @return RouteUrl
*/
public static function form($url, $callback, array $settings = null)
{
return static::match(['get', 'post'], $url, $callback, $settings);
}
/**
* This type will route the given url to your callback on the provided request methods.
*
* @param array $requestMethods
* @param string $url
* @param string|\Closure $callback
* @param array|null $settings
* @return RouteUrl|IRoute
*/
public static function match(array $requestMethods, $url, $callback, array $settings = null)
{
$route = new RouteUrl($url, $callback);
$route->setRequestMethods($requestMethods);
$route = static::addDefaultNamespace($route);
if($settings !== null) {
$route->addSettings($settings);
}
RouterBase::getInstance()->addRoute($route);
return $route;
}
public static function all($url, $callback, array $settings = null) {
$route = new RouterRoute($url, $callback);
if($settings !== null) {
$route->addSettings($settings);
}
RouterBase::getInstance()->addRoute($route);
return $route;
}
public static function controller($url, $controller, array $settings = null) {
$route = new RouterController($url, $controller);
if($settings !== null) {
$route->addSettings($settings);
}
RouterBase::getInstance()->addRoute($route);
return $route;
}
public static function resource($url, $controller, array $settings = null) {
$route = new RouterResource($url, $controller);
if($settings !== null) {
$route->addSettings($settings);
if ($settings !== null) {
$route->setSettings($settings);
}
static::router()->addRoute($route);
@@ -131,20 +237,193 @@ class SimpleRouter {
return $route;
}
public static function getRoute($controller = null, $parameters = null, $getParams = null) {
return static::router()->getRoute($controller, $parameters, $getParams);
/**
* This type will route the given url to your callback and allow any type of request method
*
* @param string $url
* @param string|\Closure $callback
* @param array|null $settings
* @return RouteUrl|IRoute
*/
public static function all($url, $callback, array $settings = null)
{
$route = new RouteUrl($url, $callback);
$route = static::addDefaultNamespace($route);
if ($settings !== null) {
$route->setSettings($settings);
}
static::router()->addRoute($route);
return $route;
}
public static function request() {
/**
* This route will route request from the given url to the controller.
*
* @param string $url
* @param string $controller
* @param array|null $settings
* @return RouteController|IRoute
*/
public static function controller($url, $controller, array $settings = null)
{
$route = new RouteController($url, $controller);
$route = static::addDefaultNamespace($route);
if ($settings !== null) {
$route->setSettings($settings);
}
static::router()->addRoute($route);
return $route;
}
/**
* This type will route all REST-supported requests to different methods in the provided controller.
*
* @param string $url
* @param string $controller
* @param array|null $settings
* @return RouteResource|IRoute
*/
public static function resource($url, $controller, array $settings = null)
{
$route = new RouteResource($url, $controller);
$route = static::addDefaultNamespace($route);
if ($settings !== null) {
$route->setSettings($settings);
}
static::router()->addRoute($route);
return $route;
}
/**
* Add exception callback handler.
*
* @param \Closure $callback
* @return CallbackExceptionHandler $callbackHandler
*/
public static function error(\Closure $callback)
{
$routes = static::router()->getRoutes();
$callbackHandler = new CallbackExceptionHandler($callback);
$group = new RouteGroup();
$group->addExceptionHandler($callbackHandler);
array_unshift($routes, $group);
static::router()->setRoutes($routes);
return $callbackHandler;
}
/**
* Get url for a route by using either name/alias, class or method name.
*
* The name parameter supports the following values:
* - Route name
* - Controller/resource name (with or without method)
* - Controller class name
*
* When searching for controller/resource by name, you can use this syntax "route.name@method".
* You can also use the same syntax when searching for a specific controller-class "MyController@home".
* If no arguments is specified, it will return the url for the current loaded route.
*
* @param string|null $name
* @param string|array|null $parameters
* @param array|null $getParams
* @throws \Exception
* @return string
*/
public static function getUrl($name = null, $parameters = null, $getParams = null)
{
return static::router()->getUrl($name, $parameters, $getParams);
}
/**
* Get the request
*
* @return \Pecee\Http\Request
*/
public static function request()
{
return static::router()->getRequest();
}
public static function response() {
return static::router()->getResponse();
/**
* Get the response object
*
* @return Response
*/
public static function response()
{
if (static::$response === null) {
static::$response = new Response(static::request());
}
return static::$response;
}
protected static function router() {
return RouterBase::getInstance();
/**
* Returns the router instance
*
* @return Router
*/
public static function router()
{
if (static::$router === null) {
static::$router = new Router();
}
return static::$router;
}
/**
* Prepends the default namespace to all new routes added.
*
* @param IRoute $route
* @return IRoute
*/
protected static function addDefaultNamespace(IRoute $route)
{
if (static::$defaultNamespace !== null) {
$callback = $route->getCallback();
/* Only add default namespace on relative callbacks */
if ($callback === null || $callback[0] !== '\\') {
$namespace = static::$defaultNamespace;
$currentNamespace = $route->getNamespace();
if ($currentNamespace !== null) {
$namespace .= '\\' . $currentNamespace;
}
$route->setDefaultNamespace($namespace);
}
}
return $route;
}
/**
* Get default namespace
* @return string
*/
public static function getDefaultNamespace()
{
return static::$defaultNamespace;
}
}
+25 -8
View File
@@ -1,18 +1,35 @@
<?php
class DummyController {
class DummyController
{
public function method1()
{
}
public function method2()
{
public function start() {
echo static::class . '@' .'start() OK';
}
public function param($params = null) {
$params = func_get_args();
echo 'Params: ' . join(', ', $params);
public function param($params = null)
{
echo join(', ', func_get_args());
}
public function getTest()
{
echo 'getTest';
}
public function notFound() {
echo 'not found';
public function postTest()
{
echo 'postTest';
}
public function putTest()
{
echo 'putTest';
}
}
+6 -7
View File
@@ -1,14 +1,13 @@
<?php
require_once 'Exceptions/MiddlewareLoadedException.php';
use Pecee\Http\Middleware\IMiddleware;
use Pecee\Http\Request;
class DummyMiddleware implements IMiddleware {
public function handle(Request $request, \Pecee\SimpleRouter\RouterEntry &$route = null) {
throw new MiddlewareLoadedException('Middleware loaded!');
}
class DummyMiddleware implements \Pecee\Http\Middleware\IMiddleware
{
public function handle(Request $request)
{
throw new MiddlewareLoadedException('Middleware loaded!');
}
}
@@ -0,0 +1,4 @@
<?php
class ExceptionHandlerException extends \Exception
{
}
@@ -1,2 +1,4 @@
<?php
class MiddlewareLoadedException extends \Exception {}
class MiddlewareLoadedException extends \Exception
{
}
@@ -0,0 +1,18 @@
<?php
class ResponseException extends \Exception
{
protected $response;
public function __construct($response)
{
$this->response = $response;
parent::__construct('', 0);
}
public function getResponse()
{
return $this->response;
}
}
+6 -4
View File
@@ -1,8 +1,10 @@
<?php
class ExceptionHandler implements \Pecee\Handler\IExceptionHandler {
public function handleError(\Pecee\Http\Request $request, \Pecee\SimpleRouter\RouterEntry &$route = null, \Exception $error){
throw $error;
}
class ExceptionHandler implements \Pecee\Handlers\IExceptionHandler
{
public function handleError(\Pecee\Http\Request $request, \Exception $error)
{
echo $error->getMessage();
}
}
@@ -0,0 +1,14 @@
<?php
class ExceptionHandlerFirst implements \Pecee\Handlers\IExceptionHandler
{
public function handleError(\Pecee\Http\Request $request, \Exception $error)
{
global $stack;
$stack[] = static::class;
$request->setUri(new \Pecee\Http\Uri('/'));
return $request;
}
}
@@ -0,0 +1,14 @@
<?php
class ExceptionHandlerSecond implements \Pecee\Handlers\IExceptionHandler
{
public function handleError(\Pecee\Http\Request $request, \Exception $error)
{
global $stack;
$stack[] = static::class;
$request->setUri(new \Pecee\Http\Uri('/'));
return $request;
}
}
@@ -0,0 +1,13 @@
<?php
class ExceptionHandlerThird implements \Pecee\Handlers\IExceptionHandler
{
public function handleError(\Pecee\Http\Request $request, \Exception $error)
{
global $stack;
$stack[] = static::class;
throw new ResponseException('ExceptionHandler loaded');
}
}
+39
View File
@@ -0,0 +1,39 @@
<?php
class ResourceController implements \Pecee\Controllers\IResourceController
{
public function index()
{
echo 'index';
}
public function show($id)
{
echo 'show ' . $id;
}
public function store()
{
echo 'store';
}
public function create()
{
echo 'create';
}
public function edit($id)
{
echo 'edit ' . $id;
}
public function update($id)
{
echo 'update ' . $id;
}
public function destroy($id)
{
echo 'destroy ' . $id;
}
}
+41 -35
View File
@@ -2,75 +2,81 @@
require_once 'Dummy/DummyMiddleware.php';
require_once 'Dummy/DummyController.php';
require_once 'Helpers/TestRouter.php';
class GroupTest extends PHPUnit_Framework_TestCase {
class GroupTest extends PHPUnit_Framework_TestCase
{
protected $result;
public function testGroupLoad() {
public function testGroupLoad()
{
$this->result = false;
\Pecee\SimpleRouter\SimpleRouter::group(['prefix' => '/group'], function() {
TestRouter::group(['prefix' => '/group'], function () {
$this->result = true;
});
try {
\Pecee\SimpleRouter\SimpleRouter::start();
} catch(Exception $e) {
echo $e->getMessage();
}
TestRouter::debug('/', 'get');
} catch(\Exception $e) {
}
$this->assertTrue($this->result);
}
public function testNestedGroup() {
public function testNestedGroup()
{
\Pecee\SimpleRouter\RouterBase::getInstance()->reset();
\Pecee\SimpleRouter\SimpleRouter::request()->setUri('/api/v1/test');
\Pecee\SimpleRouter\SimpleRouter::request()->setMethod('get');
TestRouter::group(['prefix' => '/api'], function () {
\Pecee\SimpleRouter\SimpleRouter::group(['prefix' => '/api'], function() {
\Pecee\SimpleRouter\SimpleRouter::group(['prefix' => '/v1'], function() {
\Pecee\SimpleRouter\SimpleRouter::get('/test', 'DummyController@start');
TestRouter::group(['prefix' => '/v1'], function () {
TestRouter::get('/test', 'DummyController@method1');
});
});
\Pecee\SimpleRouter\SimpleRouter::start();
TestRouter::debug('/api/v1/test', 'get');
}
public function testManyRoutes() {
public function testMultipleRoutes()
{
\Pecee\SimpleRouter\RouterBase::getInstance()->reset();
\Pecee\SimpleRouter\SimpleRouter::request()->setUri('/my/match');
\Pecee\SimpleRouter\SimpleRouter::request()->setMethod('get');
TestRouter::group(['prefix' => '/api'], function () {
\Pecee\SimpleRouter\SimpleRouter::group(['prefix' => '/api'], function() {
\Pecee\SimpleRouter\SimpleRouter::group(['prefix' => '/v1'], function() {
\Pecee\SimpleRouter\SimpleRouter::get('/test', 'DummyController@start');
TestRouter::group(['prefix' => '/v1'], function () {
TestRouter::get('/test', 'DummyController@method1');
});
});
\Pecee\SimpleRouter\SimpleRouter::get('/my/match', 'DummyController@start');
TestRouter::get('/my/match', 'DummyController@method1');
\Pecee\SimpleRouter\SimpleRouter::group(['prefix' => '/service'], function() {
\Pecee\SimpleRouter\SimpleRouter::group(['prefix' => '/v1'], function() {
\Pecee\SimpleRouter\SimpleRouter::get('/no-match', 'DummyController@start');
TestRouter::group(['prefix' => '/service'], function () {
TestRouter::group(['prefix' => '/v1'], function () {
TestRouter::get('/no-match', 'DummyController@method1');
});
});
\Pecee\SimpleRouter\SimpleRouter::start();
TestRouter::debug('/my/match', 'get');
}
public function testUrls() {
public function testUrls()
{
// Test array name
TestRouter::get('/my/fancy/url/1', 'DummyController@method1', ['as' => 'fancy1']);
\Pecee\SimpleRouter\SimpleRouter::get('/my/fancy/url/1', 'DummyController@start', ['as' => 'fancy1']);
\Pecee\SimpleRouter\SimpleRouter::get('/my/fancy/url/2', 'DummyController@start')->setAlias('fancy2');
// Test method name
TestRouter::get('/my/fancy/url/2', 'DummyController@method1')->setName('fancy2');
\Pecee\SimpleRouter\SimpleRouter::start();
TestRouter::debugNoReset('/my/fancy/url/1');
$this->assertTrue((\Pecee\SimpleRouter\SimpleRouter::getRoute('fancy1') === '/my/fancy/url/1/'));
$this->assertTrue((\Pecee\SimpleRouter\SimpleRouter::getRoute('fancy2') === '/my/fancy/url/2/'));
$this->assertEquals('/my/fancy/url/1/', TestRouter::getUrl('fancy1'));
$this->assertEquals('/my/fancy/url/2/', TestRouter::getUrl('fancy2'));
TestRouter::router()->reset();
}
+41
View File
@@ -0,0 +1,41 @@
<?php
class TestRouter extends \Pecee\SimpleRouter\SimpleRouter
{
public static function debugNoReset($testUri, $testMethod = 'get')
{
static::request()->setUri(new \Pecee\Http\Uri($testUri));
static::request()->setMethod($testMethod);
static::start();
}
public static function debug($testUri, $testMethod = 'get')
{
try {
static::debugNoReset($testUri, $testMethod);
} catch(\Exception $e) {
static::router()->reset();
throw $e;
}
static::router()->reset();
}
public static function debugOutput($testUri, $testMethod = 'get')
{
$response = null;
// Route request
ob_start();
static::debug($testUri, $testMethod);
$response = ob_get_contents();
ob_end_clean();
// Return response
return $response;
}
}
+21 -18
View File
@@ -3,29 +3,32 @@
require_once 'Dummy/DummyMiddleware.php';
require_once 'Dummy/DummyController.php';
require_once 'Dummy/Handler/ExceptionHandler.php';
require_once 'Helpers/TestRouter.php';
class MiddlewareTest extends PHPUnit_Framework_TestCase {
class MiddlewareTest extends PHPUnit_Framework_TestCase
{
public function testMiddlewareFound()
{
$this->setExpectedException(MiddlewareLoadedException::class);
public function testMiddlewareFound() {
\Pecee\SimpleRouter\RouterBase::getInstance()->reset();
\Pecee\SimpleRouter\SimpleRouter::request()->setMethod('get');
\Pecee\SimpleRouter\SimpleRouter::request()->setUri('/my/test/url');
\Pecee\SimpleRouter\SimpleRouter::group(['exceptionHandler' => 'ExceptionHandler'], function() {
\Pecee\SimpleRouter\SimpleRouter::get('/my/test/url', 'DummyController@start', ['middleware' => 'DummyMiddleware']);
TestRouter::group(['exceptionHandler' => 'ExceptionHandler'], function () {
TestRouter::get('/my/test/url', 'DummyController@method1', ['middleware' => 'DummyMiddleware']);
});
$found = false;
try {
\Pecee\SimpleRouter\SimpleRouter::start();
}catch(\Exception $e) {
$found = ($e instanceof MiddlewareLoadedException);
}
$this->assertTrue($found);
TestRouter::debug('/my/test/url', 'get');
}
public function testNestedMiddlewareDontLoad()
{
TestRouter::group(['exceptionHandler' => 'ExceptionHandler', 'middleware' => 'DummyMiddleware'], function () {
TestRouter::get('/middleware', 'DummyController@method1');
});
TestRouter::get('/my/test/url', 'DummyController@method1');
TestRouter::debug('/my/test/url', 'get');
}
}
@@ -0,0 +1,27 @@
<?php
require_once 'Dummy/DummyMiddleware.php';
require_once 'Dummy/DummyController.php';
require_once 'Dummy/Exceptions/ExceptionHandlerException.php';
require_once 'Helpers/TestRouter.php';
class RouterCallbackExceptionHandlerTest extends PHPUnit_Framework_TestCase
{
public function testCallbackExceptionHandler()
{
$this->setExpectedException(ExceptionHandlerException::class);
// Match normal route on alias
TestRouter::get('/my-new-url', 'DummyController@method2');
TestRouter::get('/my-url', 'DummyController@method1');
TestRouter::error(function (\Pecee\Http\Request $request, \Exception $exception) {
throw new ExceptionHandlerException();
});
TestRouter::debugNoReset('/404-url', 'get');
TestRouter::router()->reset();
}
}
+42
View File
@@ -0,0 +1,42 @@
<?php
require_once 'Dummy/DummyController.php';
require_once 'Helpers/TestRouter.php';
class RouterControllerTest extends PHPUnit_Framework_TestCase
{
public function testGet()
{
// Match normal route on alias
TestRouter::controller('/url', 'DummyController');
$response = TestRouter::debugOutput('/url/test', 'get');
$this->assertEquals('getTest', $response);
}
public function testPost()
{
// Match normal route on alias
TestRouter::controller('/url', 'DummyController');
$response = TestRouter::debugOutput('/url/test', 'post');
$this->assertEquals('postTest', $response);
}
public function testPut()
{
// Match normal route on alias
TestRouter::controller('/url', 'DummyController');
$response = TestRouter::debugOutput('/url/test', 'put');
$this->assertEquals('putTest', $response);
}
}
+70
View File
@@ -0,0 +1,70 @@
<?php
require_once 'Dummy/ResourceController.php';
require_once 'Helpers/TestRouter.php';
class RouterResourceTest extends PHPUnit_Framework_TestCase
{
public function testResourceStore()
{
TestRouter::resource('/resource', 'ResourceController');
$response = TestRouter::debugOutput('/resource', 'post');
$this->assertEquals('store', $response);
}
public function testResourceCreate()
{
TestRouter::resource('/resource', 'ResourceController');
$response = TestRouter::debugOutput('/resource/create', 'get');
$this->assertEquals('create', $response);
}
public function testResourceIndex()
{
TestRouter::resource('/resource', 'ResourceController');
$response = TestRouter::debugOutput('/resource', 'get');
$this->assertEquals('index', $response);
}
public function testResourceDestroy()
{
TestRouter::resource('/resource', 'ResourceController');
$response = TestRouter::debugOutput('/resource/38', 'delete');
$this->assertEquals('destroy 38', $response);
}
public function testResourceEdit()
{
TestRouter::resource('/resource', 'ResourceController');
$response = TestRouter::debugOutput('/resource/38/edit', 'get');
$this->assertEquals('edit 38', $response);
}
public function testResourceUpdate()
{
TestRouter::resource('/resource', 'ResourceController');
$response = TestRouter::debugOutput('/resource/38', 'put');
$this->assertEquals('update 38', $response);
}
public function testResourceGet()
{
TestRouter::resource('/resource', 'ResourceController');
$response = TestRouter::debugOutput('/resource/38', 'get');
$this->assertEquals('show 38', $response);
}
}
+78
View File
@@ -0,0 +1,78 @@
<?php
require_once 'Dummy/DummyController.php';
require_once 'Dummy/Exceptions/ResponseException.php';
require_once 'Dummy/Handler/ExceptionHandlerFirst.php';
require_once 'Dummy/Handler/ExceptionHandlerSecond.php';
require_once 'Dummy/Handler/ExceptionHandlerThird.php';
require_once 'Helpers/TestRouter.php';
class RouteRewriteTest extends PHPUnit_Framework_TestCase
{
/**
* Redirects to another route through 3 exception handlers.
*
* You will see "ExceptionHandler 1 loaded" 2 times. This happen because
* the exceptionhandler is asking the router to reload.
*
* That means that the exceptionhandler is loaded again, but this time
* the router ignores the same rewrite-route to avoid loop - loads
* the second which have same behavior and is also ignored before
* throwing the final Exception in ExceptionHandler 3.
*
* So this tests:
* 1. If ExceptionHandlers loads
* 2. If ExceptionHandlers load in the correct order
* 3. If ExceptionHandlers can rewrite the page on error
* 4. If the router can avoid redirect-loop due to developer has started loop.
* 5. And finally if we reaches the last exception-handler and that the correct
* exception-type is being thrown.
*/
public function testExceptionHandlerRewrite()
{
global $stack;
$stack = [];
TestRouter::group(['exceptionHandler' => [ExceptionHandlerFirst::class, ExceptionHandlerSecond::class]], function () {
TestRouter::group(['exceptionHandler' => ExceptionHandlerThird::class], function () {
TestRouter::get('/my-path', 'DummyController@method1');
});
});
try {
TestRouter::debug('/my-non-existing-path', 'get');
} catch (\ResponseException $e) {
}
$expectedStack = [
ExceptionHandlerFirst::class,
ExceptionHandlerSecond::class,
ExceptionHandlerThird::class,
];
$this->assertEquals($expectedStack, $stack);
}
public function testRewriteExceptionMessage()
{
$this->setExpectedException(\Pecee\SimpleRouter\Exceptions\NotFoundHttpException::class);
TestRouter::error(function (\Pecee\Http\Request $request, \Exception $error) {
if (strtolower($request->getUri()->getPath()) == '/my/test') {
$request->setRewriteUrl('/another-non-existing');
return $request;
}
});
TestRouter::debug('/my/test', 'get');
}
}
+117 -86
View File
@@ -2,115 +2,146 @@
require_once 'Dummy/DummyMiddleware.php';
require_once 'Dummy/DummyController.php';
require_once 'Dummy/Handler/ExceptionHandler.php';
require_once 'Dummy/Exceptions/ExceptionHandlerException.php';
require_once 'Helpers/TestRouter.php';
class RouterRouteTest extends PHPUnit_Framework_TestCase {
class RouterRouteTest extends PHPUnit_Framework_TestCase
{
protected $result = false;
public function testMultiParam()
{
TestRouter::get('/test-{param1}-{param2}', function ($param1, $param2) {
public function testNotFound() {
\Pecee\SimpleRouter\RouterBase::getInstance()->reset();
\Pecee\SimpleRouter\SimpleRouter::request()->setMethod('get');
\Pecee\SimpleRouter\SimpleRouter::request()->setUri('/test-param1-param2');
if ($param1 === 'param1' && $param2 === 'param2') {
$this->result = true;
}
\Pecee\SimpleRouter\SimpleRouter::group(['exceptionHandler' => 'ExceptionHandler'], function() {
\Pecee\SimpleRouter\SimpleRouter::get('/non-existing-path', 'DummyController@start');
});
$found = false;
TestRouter::debug('/test-param1-param2', 'get');
$this->assertTrue($this->result);
}
public function testNotFound()
{
$this->setExpectedException('\Pecee\SimpleRouter\Exceptions\NotFoundHttpException');
TestRouter::get('/non-existing-path', 'DummyController@method1');
TestRouter::debug('/test-param1-param2', 'post');
}
public function testGet()
{
TestRouter::get('/my/test/url', 'DummyController@method1');
TestRouter::debug('/my/test/url', 'get');
}
public function testPost()
{
TestRouter::post('/my/test/url', 'DummyController@method1');
TestRouter::debug('/my/test/url', 'post');
}
public function testPut()
{
TestRouter::put('/my/test/url', 'DummyController@method1');
TestRouter::debug('/my/test/url', 'put');
}
public function testDelete()
{
TestRouter::delete('/my/test/url', 'DummyController@method1');
TestRouter::debug('/my/test/url', 'delete');
}
public function testMethodNotAllowed()
{
TestRouter::get('/my/test/url', 'DummyController@method1');
try {
\Pecee\SimpleRouter\SimpleRouter::start();
}catch(\Exception $e) {
$found = ($e instanceof \Pecee\Exception\RouterException && $e->getCode() == 404);
}
$this->assertTrue($found);
}
public function testGet() {
\Pecee\SimpleRouter\RouterBase::getInstance()->reset();
\Pecee\SimpleRouter\SimpleRouter::request()->setUri('/my/test/url');
\Pecee\SimpleRouter\SimpleRouter::request()->setMethod('get');
\Pecee\SimpleRouter\SimpleRouter::get('/my/test/url', 'DummyController@start');
\Pecee\SimpleRouter\SimpleRouter::start();
}
public function testPost() {
\Pecee\SimpleRouter\RouterBase::getInstance()->reset();
\Pecee\SimpleRouter\SimpleRouter::request()->setUri('/my/test/url');
\Pecee\SimpleRouter\SimpleRouter::request()->setMethod('post');
\Pecee\SimpleRouter\SimpleRouter::post('/my/test/url', 'DummyController@start');
\Pecee\SimpleRouter\SimpleRouter::start();
}
public function testPut() {
\Pecee\SimpleRouter\RouterBase::getInstance()->reset();
\Pecee\SimpleRouter\SimpleRouter::request()->setUri('/my/test/url');
\Pecee\SimpleRouter\SimpleRouter::request()->setMethod('put');
\Pecee\SimpleRouter\SimpleRouter::put('/my/test/url', 'DummyController@start');
\Pecee\SimpleRouter\SimpleRouter::start();
}
public function testDelete() {
\Pecee\SimpleRouter\RouterBase::getInstance()->reset();
\Pecee\SimpleRouter\SimpleRouter::request()->setUri('/my/test/url');
\Pecee\SimpleRouter\SimpleRouter::request()->setMethod('delete');
\Pecee\SimpleRouter\SimpleRouter::delete('/my/test/url', 'DummyController@start');
\Pecee\SimpleRouter\SimpleRouter::start();
}
public function testMethodNotAllowed() {
\Pecee\SimpleRouter\RouterBase::getInstance()->reset();
\Pecee\SimpleRouter\SimpleRouter::request()->setUri('/my/test/url');
\Pecee\SimpleRouter\SimpleRouter::request()->setMethod('post');
\Pecee\SimpleRouter\SimpleRouter::get('/my/test/url', 'DummyController@start');
try {
\Pecee\SimpleRouter\SimpleRouter::start();
} catch(\Exception $e) {
TestRouter::debug('/my/test/url', 'post');
} catch (\Exception $e) {
$this->assertEquals(403, $e->getCode());
}
}
public function testSimpleParam()
{
TestRouter::get('/test-{param1}', 'DummyController@param');
$response = TestRouter::debugOutput('/test-param1', 'get');
$this->assertEquals('param1', $response);
}
public function testPathParamRegex()
{
TestRouter::get('/{lang}/productscategories/{name}', 'DummyController@param', ['where' => ['lang' => '[a-z]+', 'name' => '[A-Za-z0-9\-]+']]);
$response = TestRouter::debugOutput('/it/productscategories/system', 'get');
$this->assertEquals('it, system', $response);
}
public function testDomainAllowedRoute()
{
$this->result = false;
TestRouter::group(['domain' => '{subdomain}.world.com'], function () {
TestRouter::get('/test', function ($subdomain = null) {
$this->result = ($subdomain === 'hello');
});
});
TestRouter::request()->setHost('hello.world.com');
TestRouter::debug('/test', 'get');
$this->assertTrue($this->result);
}
public function testSimpleParam() {
public function testDomainNotAllowedRoute()
{
$this->result = false;
\Pecee\SimpleRouter\RouterBase::getInstance()->reset();
\Pecee\SimpleRouter\SimpleRouter::request()->setMethod('get');
\Pecee\SimpleRouter\SimpleRouter::request()->setUri('/test-param1');
TestRouter::group(['domain' => '{subdomain}.world.com'], function () {
TestRouter::get('/test', function ($subdomain = null) {
$this->result = ($subdomain === 'hello');
});
});
\Pecee\SimpleRouter\SimpleRouter::get('/test-{param1}', 'DummyController@param');
\Pecee\SimpleRouter\SimpleRouter::start();
TestRouter::request()->setHost('other.world.com');
TestRouter::debug('/test', 'get');
$this->assertFalse($this->result);
}
public function testMultiParam() {
\Pecee\SimpleRouter\RouterBase::getInstance()->reset();
\Pecee\SimpleRouter\SimpleRouter::request()->setMethod('get');
\Pecee\SimpleRouter\SimpleRouter::request()->setUri('/test-param1-param2');
\Pecee\SimpleRouter\SimpleRouter::get('/test-{param1}-{param2}', 'DummyController@param');
\Pecee\SimpleRouter\SimpleRouter::start();
public function testRegEx()
{
TestRouter::get('/my/{path}', 'DummyController@method1')->where(['path' => '[a-zA-Z\-]+']);
TestRouter::debug('/my/custom-path', 'get');
}
public function testPathParamRegex() {
public function testDefaultParameterRegex()
{
TestRouter::get('/my/{path}', 'DummyController@param', ['defaultParameterRegex' => '[\w\-]+']);
$output = TestRouter::debugOutput('/my/custom-regex', 'get');
\Pecee\SimpleRouter\RouterBase::getInstance()->reset();
\Pecee\SimpleRouter\SimpleRouter::request()->setMethod('get');
\Pecee\SimpleRouter\SimpleRouter::request()->setUri('/test/path/123123');
$this->assertEquals('custom-regex', $output);
}
\Pecee\SimpleRouter\SimpleRouter::get('/test/path/{myParam}', 'DummyController@param', ['where' => ['myParam' => '([0-9]+)']]);
\Pecee\SimpleRouter\SimpleRouter::start();
public function testDefaultParameterRegexGroup()
{
TestRouter::group(['defaultParameterRegex' => '[\w\-]+'], function() {
TestRouter::get('/my/{path}', 'DummyController@param');
});
$output = TestRouter::debugOutput('/my/custom-regex', 'get');
$this->assertEquals('custom-regex', $output);
}
}
+173
View File
@@ -0,0 +1,173 @@
<?php
require_once 'Dummy/DummyMiddleware.php';
require_once 'Dummy/DummyController.php';
require_once 'Dummy/Handler/ExceptionHandler.php';
require_once 'Helpers/TestRouter.php';
class RouterUrlTest extends PHPUnit_Framework_TestCase
{
public function testIssue253()
{
TestRouter::get('/', 'DummyController@method1');
TestRouter::get('/page/{id?}', 'DummyController@method1');
TestRouter::get('/test-output', function() {
return 'return value';
});
TestRouter::debugNoReset('/page/22', 'get');
$this->assertEquals('/page/{id?}/', TestRouter::router()->getRequest()->getLoadedRoute()->getUrl());
TestRouter::debugNoReset('/', 'get');
$this->assertEquals('/', TestRouter::router()->getRequest()->getLoadedRoute()->getUrl());
$output = TestRouter::debugOutput('/test-output', 'get');
$this->assertEquals('return value', $output);
TestRouter::router()->reset();
}
public function testUnicodeCharacters()
{
// Test spanish characters
TestRouter::get('/cursos/listado/{listado?}/{category?}', 'DummyController@method1', ['defaultParameterRegex' => '[\w\p{L}\s-]+']);
TestRouter::debugNoReset('/cursos/listado/especialidad/cirugía local', 'get');
$this->assertEquals('/cursos/listado/{listado?}/{category?}/', TestRouter::router()->getRequest()->getLoadedRoute()->getUrl());
// Test danish characters
TestRouter::get('/kategori/økse', 'DummyController@method1', ['defaultParameterRegex' => '[\w\ø]+']);
TestRouter::debugNoReset('/kategori/økse', 'get');
$this->assertEquals('/kategori/økse/', TestRouter::router()->getRequest()->getLoadedRoute()->getUrl());
TestRouter::get('/test/{param}', 'DummyController@method1', ['defaultParameterRegex' => '[\w\p{L}\s-\í]+']);
TestRouter::debugNoReset('/test/Dermatología');
$parameters = TestRouter::request()->getLoadedRoute()->getParameters();
$this->assertEquals('Dermatología', $parameters['param']);
TestRouter::router()->reset();
}
public function testOptionalParameters()
{
TestRouter::get('/aviso/legal', 'DummyController@method1');
TestRouter::get('/aviso/{aviso}', 'DummyController@method1');
TestRouter::get('/pagina/{pagina}', 'DummyController@method1');
TestRouter::get('/{pagina?}', 'DummyController@method1');
TestRouter::debugNoReset('/aviso/optional', 'get');
$this->assertEquals('/aviso/{aviso}/', TestRouter::router()->getRequest()->getLoadedRoute()->getUrl());
TestRouter::debugNoReset('/pagina/optional', 'get');
$this->assertEquals('/pagina/{pagina}/', TestRouter::router()->getRequest()->getLoadedRoute()->getUrl());
TestRouter::debugNoReset('/optional', 'get');
$this->assertEquals('/{pagina?}/', TestRouter::router()->getRequest()->getLoadedRoute()->getUrl());
TestRouter::debugNoReset('/avisolegal', 'get');
$this->assertNotEquals('/aviso/{aviso}/', TestRouter::router()->getRequest()->getLoadedRoute()->getUrl());
TestRouter::debugNoReset('/avisolegal', 'get');
$this->assertEquals('/{pagina?}/', TestRouter::router()->getRequest()->getLoadedRoute()->getUrl());
TestRouter::router()->reset();
}
public function testSimilarUrls()
{
// Match normal route on alias
TestRouter::resource('/url11', 'DummyController@method1');
TestRouter::resource('/url1', 'DummyController@method1', ['as' => 'match']);
TestRouter::debugNoReset('/url1', 'get');
$this->assertEquals(TestRouter::getUrl('match'), TestRouter::getUrl());
TestRouter::router()->reset();
}
public function testUrls()
{
// Match normal route on alias
TestRouter::get('/', 'DummyController@method1', ['as' => 'home']);
TestRouter::get('/about', 'DummyController@about');
TestRouter::group(['prefix' => '/admin', 'as' => 'admin'], function () {
// Match route with prefix on alias
TestRouter::get('/{id?}', 'DummyController@method2', ['as' => 'home']);
// Match controller with prefix and alias
TestRouter::controller('/users', 'DummyController', ['as' => 'users']);
// Match controller with prefix and NO alias
TestRouter::controller('/pages', 'DummyController');
});
TestRouter::group(['prefix' => 'api', 'as' => 'api'], function () {
// Match resource controller
TestRouter::resource('phones', 'DummyController');
});
TestRouter::controller('gadgets', 'DummyController', ['names' => ['getIphoneInfo' => 'iphone']]);
// Match controller with no prefix and no alias
TestRouter::controller('/cats', 'CatsController');
// Pretend to load page
TestRouter::debugNoReset('/', 'get');
$this->assertEquals('/gadgets/iphoneinfo/', TestRouter::getUrl('gadgets.iphone'));
$this->assertEquals('/api/phones/create/', TestRouter::getUrl('api.phones.create'));
// Should match /
$this->assertEquals('/', TestRouter::getUrl('home'));
// Should match /about/
$this->assertEquals('/about/', TestRouter::getUrl('DummyController@about'));
// Should match /admin/
$this->assertEquals('/admin/', TestRouter::getUrl('DummyController@method2'));
// Should match /admin/
$this->assertEquals('/admin/', TestRouter::getUrl('admin.home'));
// Should match /admin/2/
$this->assertEquals('/admin/2/', TestRouter::getUrl('admin.home', ['id' => 2]));
// Should match /admin/users/
$this->assertEquals('/admin/users/', TestRouter::getUrl('admin.users'));
// Should match /admin/users/home/
$this->assertEquals('/admin/users/home/', TestRouter::getUrl('admin.users@home'));
// Should match /cats/
$this->assertEquals('/cats/', TestRouter::getUrl('CatsController'));
// Should match /cats/view/
$this->assertEquals('/cats/view/', TestRouter::getUrl('CatsController', 'view'));
// Should match /cats/view/
//$this->assertEquals('/cats/view/', TestRouter::getUrl('CatsController', ['view']));
// Should match /cats/view/666
$this->assertEquals('/cats/view/666/', TestRouter::getUrl('CatsController@getView', ['666']));
// Should match /funny/man/
$this->assertEquals('/funny/man/', TestRouter::getUrl('/funny/man'));
// Should match /?jackdaniels=true&cola=yeah
$this->assertEquals('/?jackdaniels=true&cola=yeah', TestRouter::getUrl('home', null, ['jackdaniels' => 'true', 'cola' => 'yeah']));
TestRouter::router()->reset();
}
}