Compare commits

...

113 Commits

Author SHA1 Message Date
Simon Sessingø 2952f6a3b6 Added \JsonSerializable interface to Response->json (issue: #284). 2017-08-31 11:59:58 +01:00
Simon Sessingø 16e326ad9f Development
- all() in Input class now returns correct array.
- all() now supports json data.
- Minor bugfixes.
2016-11-08 18:16:17 +02:00
Simon Sessingø d9b2328e82 Fixed RouterGroup not pushing multiple middlewares properly 2016-11-07 04:56:47 +01: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ø 2d57b45c7b Fixed urls not being visible 2016-11-06 09:04:05 +01:00
Simon Sessingø 52034411cf Merge pull request #121 from skipperbent/development
Development
2016-11-06 08:14:36 +01:00
Simon Sessingø 98cc8504d4 Development
- Group only loads if prefix matches (if any).
2016-11-06 08:13:47 +01:00
Simon Sessingø 4c8ed5bb3d Merge pull request #120 from skipperbent/development
Development
2016-11-05 23:09:19 +01:00
Simon Sessingø 035a5b1629 Development
- Added support for cloudflare when using getIp method in Request.
- Fixed undefined variable notice in RouterBase class.
2016-11-05 23:07:14 +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ø 832aff0358 Optimised for cli-usage 2016-10-28 07:40:51 +02:00
Simon Sessingø 15da599e82 Merge pull request #118 from skipperbent/development
Minor optimisations
2016-10-27 19:15:59 +02:00
Simon Sessingø 43e05ad821 Minor optimisations 2016-10-27 19:15:38 +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ø 2fd32868c2 Optimisations and bugfixes 2016-10-27 17:06:05 +02:00
Simon Sessingø 5c7759ab72 Merge pull request #116 from skipperbent/development
Development
2016-10-27 16:45:29 +02:00
Simon Sessingø e51b72f0e0 Development
- Changed from http_build_query to custom solution as it doesn't support querystrings with "%" on some php versions.
2016-10-27 16:44:35 +02:00
Simon Sessingø c7b8593185 Merge pull request #114 from skipperbent/development
Development
2016-10-20 08:38:23 +02:00
Simon Sessingø 3b5e2aee9d Added credits 2016-10-20 08:37:39 +02:00
Simon Sessingø 27ba532b2d Updated documentation 2016-10-20 08:36:50 +02:00
Simon Sessingø fb478f475c Merge pull request #113 from skipperbent/development
Development
2016-10-20 08:35:20 +02:00
Simon Sessingø a8620cbc70 Updates
- Simplified exception-handling (see demo project for examples).
- Optimised sample-project.
- Optimised and added further unit-tests.
- Optimised and bugfixes.
2016-10-20 08:31:21 +02:00
Simon Sessingø 4e054dccf5 Updated documentation 2016-10-04 02:51:37 +02:00
Simon Sessingø e7dfbb159c Updated documentation 2016-10-04 02:50:36 +02:00
Simon Sessingø 8c5a5327d1 Update documentation 2016-10-04 02:47:28 +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ø 3c9a675f25 Optimised middleware load order 2016-09-28 12:29:30 +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ø dfccd99f2f Automatically push middlewares if multiple in nested group. 2016-09-28 12:21:23 +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ø 980a4e9b6a Updated documentation and added demo-project. 2016-09-26 14:37:54 +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ø fab0d0641c Allow for default parameters (including A-Z, a-z, 0-9 and "-") when parsing parameters in RouterResource. 2016-06-14 22:20:05 +02:00
Simon Sessingø 6ee172927f Merge pull request #107 from skipperbent/development
Bugfix
2016-06-04 18:44:59 +02:00
Simon Sessingø 4169716f87 Bugfix
- Fixed some routes with ending slash not matching on when using Ressource.
2016-06-04 18:44:33 +02:00
Simon Sessingø bb5e629199 Merge pull request #106 from skipperbent/development
Development
2016-06-04 18:21:16 +02:00
Simon Sessingø 53e5b5362f Development
- Enhanced regular expression used for matching parameters.
- Added basic unit-tests for parameters.
- Fixed typos in PHP-docs and other minor optimizations.
2016-06-04 18:20:46 +02:00
Simon Sessingø 0cc0a59fd5 Merge pull request #105 from skipperbent/development
Development
2016-06-04 15:13:47 +02:00
Simon Sessingø 6780b24e59 Development
- Optimized the way parameters are parsed as a result, simple-router now supports routes like `/{param1}-{param2}.json`.
- Replaced reg-ex for parameter-matching with `\w` which means that default parameter matching on routes now include `_` (underscore) per default.
- Simplified `MiddlewareTest` class.
2016-06-04 15:12:04 +02:00
Simon Sessingø b540c01650 Update README.md 2016-06-03 00:02:45 +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ø eb8832beec Added setValue method to InputItem class. 2016-05-04 13:35:23 +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ø eeafcb7862 Added exist method to Input class. 2016-05-03 07:20:52 +02:00
Simon Sessingø 7f528c133b Merge pull request #101 from skipperbent/development
Development
2016-05-01 02:01:47 +02:00
Simon Sessingø 1a9351c690 - Fixed notifications if included in projects running in command-line where certain variables aren't available. 2016-05-01 00:02:42 +02:00
Simon Sessingø 5a50190293 Merge pull request #100 from skipperbent/development
Development
2016-04-25 00:41:50 +02:00
Simon Sessingø f98e5ac59d - Optimized handleException method in RouterBase. 2016-04-23 10:52:51 +02:00
Simon Sessingø a2dbf4149b - Added route to ExceptionHandler so Middlewares can be loaded. 2016-04-23 10:50:56 +02:00
Simon Sessingø 355ef01d63 Merge pull request #99 from skipperbent/development
Development
2016-04-22 15:38:02 +02:00
Simon Sessingø ba736b47a3 - Removed support from middlewares on each load - this should be implemented in custom Router instead.
- Updated documentation to reflect how to implement Middlewares loaded on each route.
2016-04-22 15:37:27 +02:00
Simon Sessingø d3162b5a2b Merge pull request #98 from skipperbent/development
Development
2016-04-22 14:30:40 +02:00
Simon Sessingø 6ab1200fd5 - Return $item if it's an array in get method in Input class. 2016-04-22 14:29:21 +02:00
Simon Sessingø 67a00c6800 - Optimized Input class.
- Input class now returns array instead of InputItem instance when object is of type array.
- Optimized key behavior in Input class.
- Optimized arrayToParams method in RouterBase class.
- Added getMergableSettings method to RouterGroup to avoid middleware merge; as they will already be loaded.
2016-04-22 12:55:24 +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ø 1420203149 - Added custom ExceptionHandler example to documentation.
- Fixed reference to request() helper in Input class.
- Changed RouterBase handleException method to support 404-exceptions.
2016-04-21 07:16:29 +02:00
Simon Sessingø 18a9df56ca Merge pull request #96 from skipperbent/development
Development
2016-04-20 08:10:18 +02:00
Simon Sessingø f7af53a9af - Optimized Input class to ensure that InputFile items are always returned as object as they contain no value. 2016-04-19 14:48:26 +02:00
Simon Sessingø 6b8351f1b8 - Input class no longer tries to search for parameter in FILE or POST if it's not a postback. 2016-04-19 02:08:14 +02:00
Simon Sessingø 6e14ded03f Merge pull request #95 from skipperbent/development
Development
2016-04-16 23:23:56 +02:00
Simon Sessingø eb3ddf2bf7 [OPTIMISATION] Optimized get method in Input class to trim value and return default value if empty. 2016-04-16 21:34:22 +02:00
Simon Sessingø 2afe784f47 [BUGFIX] Fixed middlewaresToLoad logic used before any routes loaded in RouterBase. 2016-04-16 13:08:32 +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ø 94e98ad5f0 Update README.md 2016-04-16 00:01:12 +02:00
Simon Sessingø e7b9206bc9 Merge pull request #93 from skipperbent/development
Development
2016-04-15 23:21:00 +02:00
Simon Sessingø 8f24256434 Merge pull request #92 from skipperbent/feature-input
Removed old input method from Request class.
2016-04-15 23:20:50 +02:00
Simon Sessingø ec355c90b5 Removed old input method from Request class. 2016-04-15 23:20:16 +02:00
Simon Sessingø cd6e800984 Merge pull request #91 from skipperbent/development
Development
2016-04-15 23:14:55 +02:00
Simon Sessingø a8633b5c51 Merge pull request #90 from skipperbent/feature-input
Feature input
2016-04-15 23:12:40 +02:00
Simon Sessingø 7fdeef74d6 InputFile now inherits from InputItem 2016-04-15 23:10:49 +02:00
Simon Sessingø 17adfb8aa4 [FEATURE] Added Input classes from pecee-framework.
- Updated documentation to reflect new changes.
2016-04-15 23:07:51 +02:00
Simon Sessingø 7a429cec1d [BUGFIX] Fixed get-parameters as array (param[id]=value) causing array-to-string notice. 2016-04-14 23:52:08 +02:00
Simon Sessingø 3da7c4b446 - Fixed getIp in HttpRequest class not picking up local-ip.
- Made setdefaultNamespace method chainable.
- Simplified SimpleRouter class.
2016-04-11 22:37:15 +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ø f3ac9dc47c [BUGFIX] Fixed notice 2016-04-09 15:38:45 +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ø 22563671c5 [TASK] Moved group-middleware rendering to routeRequest to ensure all route-urls has been initialised.
- Optimisations.
2016-04-09 15:26:18 +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ø 491e920cfc [BUGFIX] Fixed only match group route if prefix is set 2016-04-09 10:01:02 +02:00
Simon Sessingø 1fae638aaf Merge pull request #86 from skipperbent/development
Development
2016-04-09 09:50:56 +02:00
Simon Sessingø 6cef099110 [TASK] Updated documentation with list of sites that uses project in production 2016-04-09 09:49:59 +02:00
Simon Sessingø 5f95290e4b [OPTIMISATION] Optimised exception-message thrown when not using IExceptionHandler interface. 2016-04-09 09:45:39 +02:00
Simon Sessingø 7234415e24 [TASK] Made abstract ExceptionHandler class an interface.
- Updated documentation to reflect new changes.
2016-04-09 09:42:57 +02:00
Simon Sessingø 257875c6f9 [TASK] Load group middleware if prefix matches
- Middlewares from groups are now loaded if prefix matches.
- Optimisations.
2016-04-09 09:25:41 +02:00
Simon Sessingø 9b743e6e57 [FEATURE] Added requirements (issue: #80) 2016-04-09 08:05:18 +02:00
Simon Sessingø 457dbc5710 [BUGFIX] Fixed issue #83
- Added more tests.
2016-04-09 07:51:03 +02:00
Simon Sessingø fc4fd0edf1 [FEATURE] Added ExceptionHandling functionality + tests.
- Somehow the ExceptionHandling functionality never really get implemented and/or got lost in translation, it's back whup!
- Added some basic tests for Middleware + routing.
2016-04-09 07:05:49 +02:00
Simon Sessingø 2f2c3ca3ca [OPTIMISATIONS] Optimised code and removed unused references 2016-04-09 05:58:18 +02:00
Simon Sessingø b34738a51a [OPTIMISATION] Optimised CSRF-token management. 2016-04-09 05:51:27 +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ø 975c27659c [BUGFIX] Bugfixes and optimisations
- Fixed issue causing nested groups not merging namespace correctly.
- Fixed issue causing longer urls (for example: /route) to have higher priority than (/route/match) in some cases.
2016-04-07 23:16:50 +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ø 27371dfa11 [FEATURE] Added support for multiple aliases 2016-04-07 19:33:04 +02:00
Simon Sessingø 6e102711a8 [BUGFIX] Fixed nested groups not merging settings to routes 2016-04-07 01:38:03 +02:00
Simon Sessingø 4d58fbf7b5 [TASK] Added rewrite_uri parameter to Request class 2016-03-19 19:14:07 +01: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ø 35bb58818e [BUGFIX] Readded rendering of groups 2016-03-19 19:00:23 +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ø 438d3c258b [TASK] Readded merging 2016-03-19 17:11:54 +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ø a58ec1544d Merge branch 'development' of https://github.com/skipperbent/simple-php-router into development 2016-03-19 16:27:54 +01:00
Simon Sessingø 1bafbab56b [TASK] Moved RouterException. Readded lost stuff from Request. 2016-03-19 16:27:25 +01:00
Simon Sessingø 6395801a06 [OPTIMISATIONS] Documentation 2016-03-19 16:19:04 +01:00
Simon Sessingø 060479f1fe Update README.md 2016-03-19 16:18:33 +01:00
Simon Sessingø b29f2ce37d [FEATURE] Added custom boot managers 2016-03-19 16:14:36 +01:00
Simon Sessingø 358b25d4f1 Merge pull request #73 from skipperbent/development
Development
2016-03-18 17:55:31 +01:00
Simon Sessingø 5483ffe458 [TASK] Readded group match inheritence .
- Settings on routes now have higher priority when merging settings from
  group.
2016-03-18 17:43:57 +01:00
Simon Sessingø 6d8e95fcaa [TASK] Match on group are no longer merged to child routes. 2016-03-18 15:21:45 +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ø db78135d19 [BUGFIX] Only render group if prefix matches. 2016-03-16 19:57:54 +01:00
Simon Sessingø c4fcf750d4 Added support for x-forwarded-proto http header 2016-03-14 01:09:35 +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
40 changed files with 1513 additions and 257 deletions
+3 -1
View File
@@ -1,2 +1,4 @@
.idea
composer.lock
composer.lock
vendor/
demo-project/vendor
+195 -64
View File
@@ -1,19 +1,16 @@
# Simple PHP router
Simple, fast and yet powerful PHP router that is easy to get integrated and in any project. Heavily inspired by the way Laravel handles routing.
Simple, fast and yet powerful PHP router that is easy to get integrated and in any project. Heavily inspired by the way Laravel handles routing, with both simplicity and expandability in mind.
## Installation
Add the latest version of Simple PHP Router to your ```composer.json```
Add the latest version of Simple PHP Router running this command.
```json
{
"require": {
"pecee/simple-php-router": "1.*"
},
"require-dev": {
"pecee/simple-php-router": "1.*"
}
}
```
composer require pecee/simple-router
```
## Requirements
- PHP 5.4 or greater
## Notes
@@ -32,10 +29,16 @@ The goal of this project is to create a router that is 100% compatible with the
- CSRF protection.
- Optional parameters
- Sub-domain routing
- Custom boot managers to redirect urls to other routes
- Input manager; to manage `GET`, `POST` params.
### Features currently "in-the-works"
## Installation and demo
- Global Constraints
We've included a simple demo project for the router which can be found in the `demo-project` folder.
Please refer to the demo-project documentation for further reading on how to setup and install simple-php-router:
[Link to demo documentation](demo-project/README.md)
## Initialising the router
@@ -44,7 +47,7 @@ In your ```index.php``` require your ```routes.php``` and call the ```routeReque
This is an example of a basic ```index.php``` file:
```php
use \Pecee\SimpleRouter;
use \Pecee\SimpleRouter\SimpleRouter;
require_once 'routes.php'; // change this to whatever makes sense in your project
@@ -52,7 +55,7 @@ require_once 'routes.php'; // change this to whatever makes sense in your projec
$defaultControllerNamespace = 'MyWebsite\\Controller';
// Do the routing
SimpleRouter::init($defaultControllerNamespace);
SimpleRouter::start($defaultControllerNamespace);
```
## Adding routes
@@ -61,6 +64,9 @@ This router is heavily inspired by the Laravel 5.* router, so anything you find
### Basic example
- ExceptionsHandlers must implement the `IExceptionHandler` interface.
- Middlewares must implement the `IMiddleware` interface.
```php
use Pecee\SimpleRouter\SimpleRouter;
@@ -72,12 +78,15 @@ use Pecee\SimpleRouter\SimpleRouter;
* the request, for instance if a user is not authenticated.
*/
// Add CSRF support (if needed)
SimpleRouter::csrfVerifier(new \Pecee\Http\Middleware\BaseCsrfVerifier());
SimpleRouter::group(['prefix' => 'v1', 'middleware' => '\MyWebsite\Middleware\SomeMiddlewareClass'], function() {
SimpleRouter::group(['prefix' => '/services', 'exceptionHandler' => '\MyProject\Handler\CustomExceptionHandler'], function() {
SimpleRouter::get('/answers/{id}', 'ControllerAnswers@show')->where(['id' => '[0-9]+');
// Optional parameter
SimpleRouter::get('/answers/{id?}', 'ControllerAnswers@show');
@@ -102,6 +111,37 @@ SimpleRouter::group(['prefix' => 'v1', 'middleware' => '\MyWebsite\Middleware\So
});
```
#### ExceptionHandler example
This is a basic example of an ExceptionHandler implementation:
```php
namespace Demo\Handlers;
use Pecee\Http\Request;
use Pecee\SimpleRouter\RouterEntry;
class CustomExceptionHandler implements IExceptionHandler {
public function handleError( Request $request, RouterEntry $router = null, \Exception $error) {
// If the error-code is 404; show another route which contains the page-not-found
if($error->getCode() === 404) {
// Load your custom 404-page view
}
// Output error as json if on api path.
if(stripos($request->getUri(), '/api') !== false) {
response()->json(['error' => $error->getMessage()]);
}
// Otherwise default exception will be thrown by the router.
}
}
```
### Sub-domain routing
Route groups may also be used to route wildcard sub-domains. Sub-domains may be assigned route parameters just like route URIs, allowing you to capture a portion of the sub-domain for usage in your route or controller. The sub-domain may be specified using the ```domain``` key on the group attribute array:
@@ -144,60 +184,21 @@ This is a simple example of an integration into a framework.
The framework has it's own ```Router``` class which inherits from the ```SimpleRouter``` class. This allows the framework to add custom functionality.
```php
namespace MyProject;
namespace Demo;
use Pecee\Handler\ExceptionHandler;
use Pecee\SimpleRouter\RouterBase;
use Pecee\SimpleRouter\SimpleRouter;
class Router extends SimpleRouter {
protected static $defaultExceptionHandler;
public static function start($defaultNamespace = null) {
// Load routes.php
$file = $_ENV['base_path'] . DIRECTORY_SEPARATOR . 'lib' . DIRECTORY_SEPARATOR . 'routes.php';
if(file_exists($file)) {
require_once $file;
}
// change this to whatever makes sense in your project
require_once 'routes.php';
// Set default namespace
$defaultNamespace = '\\'.$_ENV['app_name'] . '\\Controller';
// Do initial stuff
// Handle exceptions
try {
parent::start($defaultNamespace);
} catch(\Exception $e) {
parent::start('\\Demo\\Controllers');
$route = RouterBase::getInstance()->getLoadedRoute();
$exceptionHandler = null;
// Load and use exception-handler defined on group
if($route && $route->getGroup()) {
$exceptionHandler = $route->getGroup()->getExceptionHandler();
if($exceptionHandler !== null) {
$class = new $exceptionHandler();
$class->handleError(RouterBase::getInstance()->getRequest(), $route, $e);
}
}
// Otherwise use the fallback default exceptions handler
if(self::$defaultExceptionHandler !== null) {
$class = new self::$defaultExceptionHandler();
$class->handleError(RouterBase::getInstance()->getRequest(), $route, $e);
}
throw $e;
}
}
public static function setDefaultExceptionHandler($handler) {
self::$defaultExceptionHandler = $handler;
}
}
@@ -270,9 +271,62 @@ Register the new class in your ```routes.php```, custom ```Router``` class or wh
SimpleRouter::csrfVerifier(new \Demo\Middleware\CsrfVerifier());
```
## Using router bootmanager to make custom rewrite rules
Sometimes it can be necessary to keep urls stored in the database, file or similar. In this example, we want the url ```/my-cat-is-beatiful``` to load the route ```/article/view/1``` which the router knows, because it's defined in the ```routes.php``` file.
To interfere with the router, we create a class that inherits from ```RouterBootManager```. This class will be loaded before any other rules in ```routes.php``` and allow us to "change" the current route, if any of our criteria are fulfilled (like coming from the url ```/my-cat-is-beatiful```).
```php
use Pecee\Http\Request;
use Pecee\SimpleRouter\RouterBootManager;
class CustomRouterRules extends RouterBootManager{
public function boot(Request $request) {
$rewriteRules = [
'/my-cat-is-beatiful' => '/article/view/1',
'/horses-are-great' => '/article/view/2'
];
foreach($rewriteRules as $url => $rule) {
// If the current uri matches the url, we use our custom route
if($request->getUri() === $url) {
$request->setUri($rule);
}
}
return $request;
}
}
```
The above should be pretty self-explanatory and can easily be changed to loop through urls store in the database, file or cache.
What happens is that if the current route matches the route defined in the index of our ```$rewriteRules``` array, we set the route to the array value instead.
By doing this the route will now load the url ```/article/view/1``` instead of ```/my-cat-is-beatiful```.
The last thing we need to do, is to add our custom boot-manager to the ```routes.php``` file. You can create as many bootmanagers as you like and easily add them in your ```routes.php``` file.
**routes.php example:**
```php
// Add new bootmanager
SimpleRouter::addBootManager(new CustomRouterRules());
// This rule is what our custom bootmanager will use.
SimpleRouter::get('/article/view/{id}', 'ControllerArticle@view');
```
## Easily overwrite route about to be loaded
Sometimes it can be useful to manipulate the route that's about to be loaded, for instance if a user is not authenticated or if an error occurred within your Middleware that requires
some other route to be initialised. Simple PHP Router allows you to easily change the route about to be executed. All information about the current route is stored in
Sometimes it can be useful to manipulate the route that's about to be loaded, for instance if a user is not authenticated or if an error occurred within your Middleware that requires
some other route to be initialised. Simple PHP Router allows you to easily change the route about to be executed. All information about the current route is stored in
the ```\Pecee\SimpleRouter\Http\Request``` object.
**Note:** Please note that it's only possible to change the route BEFORE any route has initially been loaded, so doing this in your custom ExceptionHandler or Middleware is highly recommended.
@@ -288,6 +342,83 @@ $route->setClass('Example\MyCustomClass');
$route->setMethod('hello');
```
## Using the Input class to manage parameters
We've added the `Input` class to easy access parameters from your Controller-classes.
**Return single parameter value (matches both GET, POST, FILE):**
```php
$value = Request::getInstance()->getInput()->get('name');
```
**Return parameter object (matches both GET, POST, FILE):**
```php
$object = Request::getInstance()->getInput()->getObject('name');
```
**Return specific GET parameter (where name is the name of your parameter):**
```php
$object = Request::getInstance()->getInput()->get->name;
$object = Request::getInstance()->getInput()->post->name;
$object = Request::getInstance()->getInput()->file->name;
```
**Return all parameters:**
```php
// Get all
$objects = Request::getInstance()->getInput()->all();
// Only match certain keys
$objects = Request::getInstance()->getInput()->all([
'company_name',
'user_id'
]);
```
All object inherits from `InputItem` class and will always contain these methods:
- `getValue()` - returns the value of the input.
- `getIndex()` - returns the index/key of the input.
- `getName()` - returns a human friendly name for the input (company_name will be Company Name etc).
`InputFile` has the same methods as above along with some other file-specific methods like:
- `getTmpName()` - get file temporary name.
- `getSize()` - get file size.
- `move($destination)` - move file to destination.
- `getContents()` - get file content.
- `getType()` - get mime-type for file.
- `getError()` - get file upload error.
### Easy access your input
Create a helper function to easily get access to the input elements.
Example:
```php
/**
* Get input class
* @return \Pecee\Http\Input\Input
*/
function input() {
return \Pecee\Http\Request::getInstance()->getInput();
}
```
Then you can easily do something like this in your controller:
```php
// Get parameter site_id or default-value 2
$value = input()->get('site_id', '2');
```
## Sites
This is some sites that uses the simple-router project in production.
- [holla.dk](http://www.holla.dk)
- [ninjaimg.com](http://ninjaimg.com)
- [bookandbegin.com](https://bookandbegin.com)
- [dscuz.com](https://www.dscuz.com)
## Documentation
While I work on a better documentation, please refer to the Laravel 5 routing documentation here:
@@ -297,11 +428,11 @@ http://laravel.com/docs/5.1/routing
The router can be easily extended to customize your needs.
## Ideas and issues
If you want a great new feature or experience any issues what-so-ever, please feel free to leave an issue and i'll look into it whenever possible.
If you want a great new feature or experience any issues what-so-ever, please feel free to leave an issue and i'll look into it whenever possible.
## The MIT License (MIT)
Copyright (c) 2015 Simon Sessingø / simple-php-router
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
+1 -1
View File
@@ -13,7 +13,7 @@
}
],
"require": {
"php": ">=5.4.0"
},
"require-dev": {
"phpunit/phpunit": "4.7.7"
+100
View File
@@ -0,0 +1,100 @@
# 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.
@@ -0,0 +1,22 @@
<?php
namespace Demo\Controllers;
use Pecee\Http\Request;
class ApiController {
public function index() {
// The variable authenticated is set to true in the ApiVerification middleware class.
$request = Request::getInstance();
header('content-type: application/json');
echo json_encode([
'authenticated' => $request->authenticated
]);
}
}
@@ -0,0 +1,29 @@
<?php
namespace Demo\Controllers;
class DefaultController {
public function index() {
// implement
echo 'DefaultController -> index';
}
public function contact() {
echo 'DefaultController -> contact';
}
public function companies($id = null) {
echo 'DefaultController -> companies -> id: ' . $id;
}
public function notFound() {
echo 'Page not found';
}
}
@@ -0,0 +1,34 @@
<?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 $router = 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;
}
}
@@ -0,0 +1,16 @@
<?php
namespace Demo\Middlewares;
use Pecee\Http\Middleware\IMiddleware;
use Pecee\Http\Request;
class ApiVerification implements IMiddleware {
public function handle(Request $request) {
// Do authentication
$request->authenticated = true;
}
}
@@ -0,0 +1,13 @@
<?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/*'];
}
+25
View File
@@ -0,0 +1,25 @@
<?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) {
// change this to whatever makes sense in your project
require_once 'routes.php';
// Do initial stuff
parent::start('\\Demo\\Controllers');
}
}
+23
View File
@@ -0,0 +1,23 @@
<?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
@@ -0,0 +1,26 @@
{
"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
@@ -0,0 +1,5 @@
RewriteEngine on
RewriteCond %{SCRIPT_FILENAME} !-f
RewriteCond %{SCRIPT_FILENAME} !-d
RewriteCond %{SCRIPT_FILENAME} !-l
RewriteRule ^(.*)$ index.php/$1
+7
View File
@@ -0,0 +1,7 @@
<?php
// load composer dependencies
require '../vendor/autoload.php';
// Start the routing
\Demo\Router::start();
+3 -3
View File
@@ -37,7 +37,7 @@ class CsrfToken {
* @param $token
*/
public function setToken($token) {
setcookie(self::CSRF_KEY, $token, time() + 60 * 120, '/');
setcookie(static::CSRF_KEY, $token, time() + 60 * 120, '/');
}
/**
@@ -46,7 +46,7 @@ class CsrfToken {
*/
public function getToken(){
if($this->hasToken()) {
return $_COOKIE[self::CSRF_KEY];
return $_COOKIE[static::CSRF_KEY];
}
return null;
}
@@ -56,7 +56,7 @@ class CsrfToken {
* @return bool
*/
public function hasToken() {
return isset($_COOKIE[self::CSRF_KEY]);
return isset($_COOKIE[static::CSRF_KEY]);
}
}
@@ -1,3 +1,3 @@
<?php
namespace Pecee\SimpleRouter;
namespace Pecee\Exception;
class RouterException extends \Exception { }
+11
View File
@@ -0,0 +1,11 @@
<?php
namespace Pecee\Handler;
use Pecee\Http\Request;
use Pecee\SimpleRouter\RouterEntry;
interface IExceptionHandler {
public function handleError(Request $request, RouterEntry $router = null, \Exception $error);
}
+213
View File
@@ -0,0 +1,213 @@
<?php
namespace Pecee\Http\Input;
use Pecee\Http\Request;
class Input {
/**
* @var \Pecee\Http\Input\InputCollection
*/
public $get;
/**
* @var \Pecee\Http\Input\InputCollection
*/
public $post;
/**
* @var \Pecee\Http\Input\InputCollection
*/
public $file;
/**
* @var Request
*/
protected $request;
public function __construct(Request &$request) {
$this->request = $request;
$this->setGet();
$this->setPost();
$this->setFile();
}
/**
* Get all get/post items
* @param array|null $filter Only take items in filter
* @return array
*/
public function all(array $filter = null) {
$output = $_POST;
if($this->request->getMethod() === 'post') {
$contents = file_get_contents('php://input');
if (stripos(trim($contents), '{') === 0) {
$output = json_decode($contents, true);
if($output === false) {
$output = array();
}
}
}
$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;
}
}
}
}
+67
View File
@@ -0,0 +1,67 @@
<?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
* @return mixed
*/
public function findFirst($index) {
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 null;
}
/**
* @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);
}
}
+68
View File
@@ -0,0 +1,68 @@
<?php
namespace Pecee\Http\Input;
class InputFile extends InputItem {
protected $name;
protected $size;
protected $type;
protected $error;
protected $tmpName;
/**
* @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);
}
public function move($destination) {
return move_uploaded_file($this->tmpName, $destination);
}
public function getContents() {
return file_get_contents($this->tmpName);
}
public function setTmpName($name) {
$this->tmpName = $name;
}
public function setSize($size) {
$this->size = $size;
}
public function setType($type) {
$this->type = $type;
}
public function setError($error) {
$this->error = $error;
}
}
+63
View File
@@ -0,0 +1,63 @@
<?php
namespace Pecee\Http\Input;
class InputItem {
protected $index;
protected $name;
protected $value;
public function __construct($index, $value = null) {
$this->index = $index;
$this->value = $value;
// Make the name human friendly, by replace _ with space
$this->name = ucfirst(str_replace('_', ' ', $this->index));
}
/**
* @return array
*/
public function getName() {
return $this->name;
}
/**
* @return array
*/
public function getValue() {
return $this->value;
}
/**
* @return string
*/
public function getIndex() {
return $this->index;
}
/**
* Set input name
* @param string $name
* @return static $this
*/
public function setName($name) {
$this->name = $name;
return $this;
}
/**
* Set input value
* @param string $value
* @return static $this
*/
public function setValue($value) {
$this->value = $value;
return $this;
}
public function __toString() {
return (string)$this->getValue();
}
}
+25 -4
View File
@@ -12,10 +12,13 @@ class BaseCsrfVerifier implements IMiddleware {
protected $except;
protected $csrfToken;
protected $token;
public function __construct() {
$this->csrfToken = new CsrfToken();
// Generate or get the CSRF-Token from Cookie.
$this->token = (!$this->hasToken()) ? $this->generateToken() : $this->csrfToken->getToken();
}
/**
@@ -50,14 +53,14 @@ class BaseCsrfVerifier implements IMiddleware {
if($request->getMethod() != 'get' && !$this->skip($request)) {
$token = (isset($_POST[self::POST_KEY])) ? $_POST[self::POST_KEY] : null;
$token = (isset($_POST[static::POST_KEY])) ? $_POST[static::POST_KEY] : null;
// If the token is not posted, check headers for valid x-csrf-token
if($token === null) {
$token = $request->getHeader(self::HEADER_KEY);
$token = $request->getHeader(static::HEADER_KEY);
}
if( !$this->csrfToken->validate( $token ) ) {
if( !$this->csrfToken->validate($token) ) {
throw new TokenMismatchException('Invalid csrf-token.');
}
@@ -65,4 +68,22 @@ class BaseCsrfVerifier implements IMiddleware {
}
public function generateToken() {
$token = $this->csrfToken->generateToken();
$this->csrfToken->setToken($token);
return $token;
}
public function hasToken() {
if($this->token != null) {
return true;
}
return $this->csrfToken->hasToken();
}
public function getToken() {
return $this->token;
}
}
+48 -16
View File
@@ -1,18 +1,13 @@
<?php
namespace Pecee\Http;
use Pecee\SimpleRouter\RouterBase;
use Pecee\Http\Input\Input;
class Request {
protected static $instance;
protected $data;
protected $uri;
protected $host;
protected $method;
protected $headers;
protected $loadedRoute;
/**
* Return new instance
@@ -27,10 +22,11 @@ class Request {
public function __construct() {
$this->data = array();
$this->host = $_SERVER['HTTP_HOST'];
$this->uri = $_SERVER['REQUEST_URI'];
$this->method = (isset($_POST['_method'])) ? strtolower($_POST['_method']) : strtolower($_SERVER['REQUEST_METHOD']);
$this->host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : array();
$this->uri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : array();
$this->method = (isset($_POST['_method'])) ? strtolower($_POST['_method']) : (isset($_SERVER['REQUEST_METHOD']) ? strtolower($_SERVER['REQUEST_METHOD']) : array());
$this->headers = $this->getAllHeaders();
$this->input = new Input($this);
}
protected function getAllHeaders() {
@@ -44,6 +40,9 @@ class Request {
}
public function getIsSecure() {
if(isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) === 'https') {
return true;
}
return isset($_SERVER['HTTPS']) ? true : (isset($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] === 443);
}
@@ -97,7 +96,10 @@ class Request {
* @return string
*/
public function getIp() {
return isset($_SERVER['HTTP_X_FORWARDED_FOR']) ? $_SERVER['HTTP_X_FORWARDED_FOR'] : $_SERVER['REMOTE_ADDR'];
if(isset($_SERVER['HTTP_CF_CONNECTING_IP'])) {
return $_SERVER['HTTP_CF_CONNECTING_IP'];
}
return ((isset($_SERVER['HTTP_X_FORWARDED_FOR']) && strlen($_SERVER['HTTP_X_FORWARDED_FOR'])) ? $_SERVER['HTTP_X_FORWARDED_FOR'] : isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : null);
}
/**
@@ -126,13 +128,22 @@ class Request {
}
/**
* Get request input or default value
* @param string $name
* @param string $defaultValue
* @return mixed
* Get input class
* @return Input
*/
public function getInput($name, $defaultValue) {
return (isset($_REQUEST[$name]) ? $_REQUEST[$name] : $defaultValue);
public function getInput() {
return $this->input;
}
public function isFormatAccepted($format) {
return (isset($_SERVER['HTTP_ACCEPT']) && stripos($_SERVER['HTTP_ACCEPT'], $format) > -1);
}
public function getAcceptFormats() {
if(isset($_SERVER['HTTP_ACCEPT'])) {
return explode(',', $_SERVER['HTTP_ACCEPT']);
}
return array();
}
public function __set($name, $value = null) {
@@ -151,4 +162,25 @@ class Request {
return $this->loadedRoute;
}
/**
* @param mixed $uri
*/
public function setUri($uri) {
$this->uri = $uri;
}
/**
* @param mixed $host
*/
public function setHost($host) {
$this->host = $host;
}
/**
* @param mixed $method
*/
public function setMethod($method) {
$this->method = $method;
}
}
+12 -6
View File
@@ -25,8 +25,8 @@ class Response {
if($httpCode !== null) {
$this->httpCode($httpCode);
}
$this->header('Location: ' . $url);
$this->header('location: ' . $url);
die();
}
@@ -69,13 +69,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);
}
/**
-7
View File
@@ -1,7 +0,0 @@
<?php
namespace Pecee\SimpleRouter;
interface IRouteEntry {
}
+149 -73
View File
@@ -1,7 +1,8 @@
<?php
namespace Pecee\SimpleRouter;
use Pecee\CsrfToken;
use Pecee\Exception\RouterException;
use Pecee\Handler\IExceptionHandler;
use Pecee\Http\Middleware\BaseCsrfVerifier;
use Pecee\Http\Request;
@@ -16,21 +17,19 @@ class RouterBase {
protected $controllerUrlMap;
protected $backStack;
protected $defaultNamespace;
protected $bootManagers;
protected $baseCsrfVerifier;
protected $exceptionHandlers;
// TODO: make interface for controller routers, so they can be easily detected
// TODO: clean up - cut some of the methods down to smaller pieces
public function __construct() {
$this->request = Request::getInstance();
$this->routes = array();
$this->backStack = array();
$this->controllerUrlMap = array();
$this->baseCsrfVerifier = new BaseCsrfVerifier();
$this->request = Request::getInstance();
$csrf = new CsrfToken();
$token = ($csrf->hasToken()) ? $csrf->getToken() : $csrf->generateToken();
$csrf->setToken($token);
$this->bootManagers = array();
$this->exceptionHandlers = array();
}
public function addRoute(RouterEntry $route) {
@@ -59,11 +58,9 @@ class RouterBase {
}
if($this->defaultNamespace && !$route->getNamespace()) {
$namespace = null;
$namespace = $this->defaultNamespace;
if ($route->getNamespace()) {
$namespace = $this->defaultNamespace . '\\' . $route->getNamespace();
} else {
$namespace = $this->defaultNamespace;
$namespace .= '\\' . $route->getNamespace();
}
$route->setNamespace($namespace);
@@ -71,25 +68,40 @@ class RouterBase {
$newPrefixes = $prefixes;
if($route->getPrefix()) {
array_push($newPrefixes, rtrim($route->getPrefix(), '/'));
if($route->getPrefix() && trim($route->getPrefix(), '/') !== '') {
array_push($newPrefixes, trim($route->getPrefix(), '/'));
}
/* @var $group RouterGroup */
$group = null;
if(!($route instanceof RouterGroup)) {
if(is_array($newPrefixes) && count($newPrefixes) && $backStack) {
$route->setUrl( join('/', $newPrefixes) . $route->getUrl() );
$route->setUrl( '/' . join('/', $newPrefixes) . $route->getUrl() );
}
$group = null;
$this->controllerUrlMap[] = $route;
}
$this->currentRoute = $route;
if($route instanceof RouterGroup && is_callable($route->getCallback())) {
$group = $route;
$route->renderRoute($this->request);
$mergedSettings = array_merge($route->getMergeableSettings(), $settings);
if($route->matchRoute($this->request)) {
$group = $route;
$mergedSettings = array_merge($settings, $group->getMergeableSettings());
// Add ExceptionHandler
if ($group->getExceptionHandler() !== null) {
$this->exceptionHandlers[] = $route;
}
}
}
$this->currentRoute = null;
@@ -104,62 +116,105 @@ class RouterBase {
}
}
public function routeRequest() {
public function routeRequest($original = true) {
// Verify csrf token for request
if($this->baseCsrfVerifier !== null) {
/* @var $csrfVerifier BaseCsrfVerifier */
$csrfVerifier = $this->baseCsrfVerifier;
$csrfVerifier = new $csrfVerifier();
$csrfVerifier->handle($this->request);
}
// Loop through each route-request
$this->processRoutes($this->routes);
$originalUri = $this->request->getUri();
$routeNotAllowed = false;
$max = count($this->controllerUrlMap);
try {
/* @var $route RouterEntry */
for($i = 0; $i < $max; $i++) {
// Initialize boot-managers
if(count($this->bootManagers)) {
/* @var $manager RouterBootManager */
foreach($this->bootManagers as $manager) {
$this->request = $manager->boot($this->request);
$route = $this->controllerUrlMap[$i];
if($route->getGroup() !== null) {
if($route->getGroup()->matchDomain($this->request) === false) {
continue;
if(!($this->request instanceof Request)) {
throw new RouterException('Custom router bootmanager "'. get_class($manager) .'" must return instance of Request.');
}
}
}
$routeMatch = $route->matchRoute($this->request);
// Loop through each route-request
$this->processRoutes($this->routes);
if($routeMatch) {
if(count($route->getRequestMethods()) && !in_array($this->request->getMethod(), $route->getRequestMethods())) {
$routeNotAllowed = true;
continue;
if($original === true) {
// Verify csrf token for request
if ($this->baseCsrfVerifier !== null) {
$this->baseCsrfVerifier->handle($this->request);
}
$routeNotAllowed = false;
$this->request->loadedRoute = $route;
$route->loadMiddleware($this->request);
$this->request->loadedRoute->renderRoute($this->request);
break;
}
$max = count($this->controllerUrlMap);
/* @var $route RouterEntry */
for ($i = 0; $i < $max; $i++) {
$route = $this->controllerUrlMap[$i];
$routeMatch = $route->matchRoute($this->request);
if ($routeMatch) {
if (count($route->getRequestMethods()) && !in_array($this->request->getMethod(), $route->getRequestMethods())) {
$routeNotAllowed = true;
continue;
}
$routeNotAllowed = false;
$this->request->rewrite_uri = $this->request->uri;
$this->request->setUri($originalUri);
$this->request->loadedRoute = $route;
$route->loadMiddleware($this->request);
$this->request->loadedRoute->renderRoute($this->request);
break;
}
}
} catch(\Exception $e) {
$this->handleException($e);
}
if($routeNotAllowed) {
throw new RouterException('Route or method not allowed', 403);
$this->handleException(new RouterException('Route or method not allowed', 403));
}
if(!$this->request->loadedRoute) {
throw new RouterException(sprintf('Route not found: %s', $this->request->getUri()), 404);
$this->handleException(new RouterException(sprintf('Route not found: %s', $this->request->getUri()), 404));
}
}
protected function handleException(\Exception $e) {
$request = null;
/* @var $route RouterGroup */
foreach ($this->exceptionHandlers as $route) {
$route->loadMiddleware($this->request);
$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->request->loadedRoute, $e);
}
if($request !== null) {
$this->request = $request;
$this->routeRequest(false);
return;
}
throw $e;
}
/**
* @return string
*/
@@ -169,9 +224,29 @@ class RouterBase {
/**
* @param string $defaultNamespace
* @return static
*/
public function setDefaultNamespace($defaultNamespace) {
$this->defaultNamespace = $defaultNamespace;
return $this;
}
/**
* @return array
*/
public function getBootManagers() {
return $this->bootManagers;
}
/**
* @param array $bootManagers
*/
public function setBootManagers(array $bootManagers) {
$this->bootManagers = $bootManagers;
}
public function addBootManager(RouterBootManager $bootManager) {
$this->bootManagers[] = $bootManager;
}
/**
@@ -234,18 +309,21 @@ class RouterBase {
}
public function arrayToParams(array $getParams = null, $includeEmpty = true) {
if (is_array($getParams) && count($getParams) > 0) {
foreach ($getParams as $key => $val) {
if (!empty($val) || empty($val) && $includeEmpty) {
$getParams[$key] = $key . '=' . $val;
}
if(is_array($getParams) && count($getParams)) {
if ($includeEmpty === false) {
$getParams = array_filter($getParams, function ($item) {
return (!empty($item));
});
}
return join('&', $getParams);
return '?' . http_build_query($getParams);
}
return '';
}
protected function processUrl($route, $method = null, $parameters = null, $getParams = null) {
protected function processUrl(RouterRoute $route, $method = null, $parameters = null, $getParams = null) {
$domain = '';
@@ -296,8 +374,8 @@ class RouterBase {
$url = rtrim($url, '/') . '/';
if($getParams !== null && count($getParams)) {
$url .= '?' . $this->arrayToParams($getParams);
if($getParams !== null) {
$url .= $this->arrayToParams($getParams);
}
return $url;
@@ -317,11 +395,10 @@ class RouterBase {
if($controller === null && $parameters === null) {
$getParams = (is_array($getParams)) ? array_merge($_GET, $getParams) : $_GET;
$url = parse_url(Request::getInstance()->getUri());
$url = $url['path'];
$url = parse_url($this->request->getUri(), PHP_URL_PATH);
if(count($getParams)) {
$url .= '?' . $this->arrayToParams($getParams);
if($getParams !== null) {
$url .= $this->arrayToParams($getParams);
}
return $url;
@@ -342,7 +419,7 @@ class RouterBase {
$route = $this->controllerUrlMap[$i];
// Check an alias exist, if the matches - use it
if($route instanceof RouterRoute && strtolower($route->getAlias()) === strtolower($controller)) {
if($route instanceof RouterRoute && $route->hasAlias($controller)) {
return $this->processUrl($route, $route->getMethod(), $parameters, $getParams);
}
@@ -392,19 +469,18 @@ class RouterBase {
$url = '/' . trim(join('/', $url), '/') . '/';
if($getParams !== null && count($getParams)) {
$url .= '?' . $this->arrayToParams($getParams);
if($getParams !== null) {
$url .= $this->arrayToParams($getParams);
}
return $url;
}
public static function getInstance() {
if(self::$instance === null) {
self::$instance = new static();
if(static::$instance === null) {
static::$instance = new static();
}
return self::$instance;
return static::$instance;
}
}
@@ -0,0 +1,10 @@
<?php
namespace Pecee\SimpleRouter;
use Pecee\Http\Request;
abstract class RouterBootManager {
abstract public function boot(Request $request);
}
@@ -1,6 +1,7 @@
<?php
namespace Pecee\SimpleRouter;
use Pecee\Exception\RouterException;
use Pecee\Http\Request;
class RouterController extends RouterEntry {
@@ -78,10 +79,12 @@ class RouterController extends RouterEntry {
/**
* @param string $url
* @return static
*/
public function setUrl($url) {
$url = rtrim($url, '/') . '/';
$this->url = $url;
return $this;
}
/**
@@ -93,9 +96,11 @@ class RouterController extends RouterEntry {
/**
* @param string $controller
* @return static
*/
public function setController($controller) {
$this->controller = $controller;
return $this;
}
/**
@@ -107,9 +112,11 @@ class RouterController extends RouterEntry {
/**
* @param string $method
* @return static
*/
public function setMethod($method) {
$this->method = $method;
return $this;
}
}
+11 -17
View File
@@ -2,6 +2,7 @@
namespace Pecee\SimpleRouter;
use Pecee\Exception\RouterException;
use Pecee\Http\Middleware\IMiddleware;
use Pecee\Http\Request;
@@ -92,7 +93,7 @@ abstract class RouterEntry {
* @return self
*/
public function setPrefix($prefix) {
$this->prefix = '/' . trim($prefix, '/') . '/';
$this->prefix = '/' . ltrim($prefix, '/');
return $this;
}
@@ -122,7 +123,7 @@ abstract class RouterEntry {
}
/**
* @return string
* @return string|array
*/
public function getMiddleware() {
return $this->middleware;
@@ -231,7 +232,7 @@ abstract class RouterEntry {
}
/**
* Dynamicially set settings value
* Dynamically set settings value
*
* @param string $name
* @param mixed|null $value
@@ -248,7 +249,7 @@ abstract class RouterEntry {
return new $name();
}
protected function parseParameters($route, $url, $parameterRegex = '[a-z0-9]+') {
protected function parseParameters($route, $url, $parameterRegex = '[\w]+') {
$parameterNames = array();
$regex = '';
$lastCharacter = '';
@@ -260,12 +261,6 @@ abstract class RouterEntry {
$character = $route[$i];
// Skip "/" if we are at the end of a parameter
if($lastCharacter === '}' && $character === '/') {
$lastCharacter = $character;
continue;
}
if($character === '{') {
// Remove "/" and "\" from regex
if(substr($regex, strlen($regex)-1) === '/') {
@@ -284,10 +279,10 @@ abstract class RouterEntry {
if($lastCharacter === '?') {
$parameter = substr($parameter, 0, strlen($parameter)-1);
$regex .= '(?:\\/?(?P<'.$parameter.'>[^\/]+)?\\/?)';
$regex .= '(?:\/?(?P<' . $parameter . '>'. $parameterRegex .')[^\/]?)?';
$required = false;
} else {
$regex .= '\\/(?P<' . $parameter . '>'. $parameterRegex .')\\/';
$regex .= '\/?(?P<' . $parameter . '>'. $parameterRegex .')[^\/]?';
}
$parameterNames[] = array('name' => $parameter, 'required' => $required);
$parameter = '';
@@ -306,7 +301,8 @@ abstract class RouterEntry {
$parameterValues = array();
if(preg_match('/^'.$regex.'$/is', $url, $parameterValues)) {
if(preg_match('/^'.$regex.'\/?$/is', $url, $parameterValues)) {
$parameters = array();
$max = count($parameterNames);
@@ -314,7 +310,7 @@ abstract class RouterEntry {
if($max) {
for($i = 0; $i < $max; $i++) {
$name = $parameterNames[$i];
$parameterValue = (isset($parameterValues[$name['name']]) && !empty($parameterValues[$name['name']])) ? $parameterValues[$name['name']] : null;
$parameterValue = isset($parameterValues[$name['name']]) ? $parameterValues[$name['name']] : null;
if($name['required'] && $parameterValue === null) {
throw new RouterException('Missing required parameter ' . $name['name'], 404);
@@ -359,9 +355,7 @@ abstract class RouterEntry {
}
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 {
@@ -400,7 +394,7 @@ abstract class RouterEntry {
}
/**
* Get allowed requeset methods
* Get allowed request methods
*
* @return array
*/
+39 -5
View File
@@ -2,14 +2,11 @@
namespace Pecee\SimpleRouter;
use Pecee\Exception\RouterException;
use Pecee\Http\Request;
class RouterGroup extends RouterEntry {
public function __construct() {
parent::__construct();
}
public function matchDomain(Request $request) {
if($this->domain !== null) {
@@ -66,7 +63,12 @@ class RouterGroup extends RouterEntry {
}
public function matchRoute(Request $request) {
return null;
// 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) {
@@ -82,4 +84,36 @@ class RouterGroup extends RouterEntry {
return $this->domain;
}
/**
* @param array $settings
* @return self
*/
public function addSettings(array $settings = null) {
if($this->getNamespace() !== null && isset($settings['namespace'])) {
unset($settings['namespace']);
}
// Push middleware if multiple
if($this->getMiddleware() !== null && isset($settings['middleware'])) {
if(!is_array($this->getMiddleware())) {
$middlewares = [
$this->getMiddleware(),
$settings['middleware']
];
} else {
$middlewares = array_push($settings['middleware'], $this->getMiddleware());
}
$settings['middleware'] = array_unique(array_reverse($middlewares));
}
if(is_array($settings)) {
$this->settings = array_merge($this->settings, $settings);
}
return $this;
}
}
+6 -1
View File
@@ -1,6 +1,7 @@
<?php
namespace Pecee\SimpleRouter;
use Pecee\Exception\RouterException;
use Pecee\Http\Request;
class RouterResource extends RouterEntry {
@@ -51,7 +52,7 @@ class RouterResource extends RouterEntry {
$route = rtrim($this->url, '/') . '/{id?}/{action?}';
$parameters = $this->parseParameters($route, $url, '[0-9]+?');
$parameters = $this->parseParameters($route, $url);
if($parameters !== null) {
@@ -108,10 +109,12 @@ class RouterResource extends RouterEntry {
/**
* @param string $url
* @return static
*/
public function setUrl($url) {
$url = rtrim($url, '/') . '/';
$this->url = $url;
return $this;
}
/**
@@ -123,9 +126,11 @@ class RouterResource extends RouterEntry {
/**
* @param string $controller
* @return static
*/
public function setController($controller) {
$this->controller = $controller;
return $this;
}
}
+22 -7
View File
@@ -2,7 +2,6 @@
namespace Pecee\SimpleRouter;
use Pecee\ArrayUtil;
use Pecee\Http\Request;
class RouterRoute extends RouterEntry {
@@ -15,8 +14,6 @@ class RouterRoute extends RouterEntry {
parent::__construct();
$this->setUrl($url);
$this->setCallback($callback);
$this->settings['aliases'] = array();
}
public function matchRoute(Request $request) {
@@ -50,8 +47,6 @@ class RouterRoute extends RouterEntry {
return true;
}
return null;
}
@@ -88,15 +83,35 @@ class RouterRoute extends RouterEntry {
/**
* Get alias for the url which can be used when getting the url route.
* @return string
* @return string|array
*/
public function getAlias(){
return $this->alias;
}
/**
* Check if route has given alias.
*
* @param $name
* @return bool
*/
public function hasAlias($name) {
if(is_array($this->alias)) {
foreach($this->alias as $alias) {
if(strtolower($alias) === strtolower($name)) {
return true;
}
}
} else {
return strtolower($this->getAlias()) === strtolower($name);
}
return false;
}
/**
* Set the url alias for easier getting the url route.
* @param string $alias
* @param string|array $alias
* @return self
*/
public function setAlias($alias){
+35 -51
View File
@@ -16,12 +16,10 @@ class SimpleRouter {
/**
* Start/route request
* @param null $defaultNamespace
* @throws RouterException
* @throws \Pecee\Exception\RouterException
*/
public static function start($defaultNamespace = null) {
$router = RouterBase::getInstance();
$router->setDefaultNamespace($defaultNamespace);
$router->routeRequest();
RouterBase::getInstance()->setDefaultNamespace($defaultNamespace)->routeRequest();
}
/**
@@ -32,48 +30,24 @@ class SimpleRouter {
RouterBase::getInstance()->setBaseCsrfVerifier($baseCsrfVerifier);
}
public static function addBootManager(RouterBootManager $bootManager) {
RouterBase::getInstance()->addBootManager($bootManager);
}
public static function get($url, $callback, array $settings = null) {
$route = new RouterRoute($url, $callback);
$route->addSettings($settings);
$route->setRequestMethods(array(RouterRoute::REQUEST_TYPE_GET));
$router = RouterBase::getInstance();
$router->addRoute($route);
return $route;
return self::match(['get'], $url, $callback, $settings);
}
public static function post($url, $callback, array $settings = null) {
$route = new RouterRoute($url, $callback);
$route->addSettings($settings);
$route->setRequestMethods(array(RouterRoute::REQUEST_TYPE_POST));
$router = RouterBase::getInstance();
$router->addRoute($route);
return $route;
return self::match(['post'], $url, $callback, $settings);
}
public static function put($url, $callback, array $settings = null) {
$route = new RouterRoute($url, $callback);
$route->addSettings($settings);
$route->setRequestMethods(array(RouterRoute::REQUEST_TYPE_PUT, RouterRoute::REQUEST_TYPE_PATCH));
$router = RouterBase::getInstance();
$router->addRoute($route);
return $route;
return self::match(['put'], $url, $callback, $settings);
}
public static function delete($url, $callback, array $settings = null) {
$route = new RouterRoute($url, $callback);
$route->addSettings($settings);
$route->setRequestMethods(array(RouterRoute::REQUEST_TYPE_DELETE));
$router = RouterBase::getInstance();
$router->addRoute($route);
return $route;
return self::match(['delete'], $url, $callback, $settings);
}
public static function group($settings = array(), $callback) {
@@ -84,8 +58,7 @@ class SimpleRouter {
$group->setSettings($settings);
}
$router = RouterBase::getInstance();
$router->addRoute($group);
RouterBase::getInstance()->addRoute($group);
return $group;
}
@@ -94,7 +67,7 @@ class SimpleRouter {
* Adds get + post route
*
* @param string $url
* @param function $callback
* @param callable $callback
* @param array|null $settings
* @return RouterRoute
*/
@@ -105,37 +78,48 @@ class SimpleRouter {
public static function match(array $requestMethods, $url, $callback, array $settings = null) {
$route = new RouterRoute($url, $callback);
$route->setRequestMethods($requestMethods);
$route->addSettings($settings);
$router = RouterBase::getInstance();
$router->addRoute($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);
$route->addSettings($settings);
$router = RouterBase::getInstance();
$router->addRoute($route);
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);
$route->addSettings($settings);
$router = RouterBase::getInstance();
$router->addRoute($route);
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);
$route->addSettings($settings);
$router = RouterBase::getInstance();
$router->addRoute($route);
if($settings !== null) {
$route->addSettings($settings);
}
RouterBase::getInstance()->addRoute($route);
return $route;
}
+18
View File
@@ -0,0 +1,18 @@
<?php
class DummyController {
public function start() {
echo static::class . '@' .'start() OK';
}
public function param($params = null) {
$params = func_get_args();
echo 'Params: ' . join(', ', $params);
}
public function notFound() {
echo 'not found';
}
}
+14
View File
@@ -0,0 +1,14 @@
<?php
require_once 'Exceptions/MiddlewareLoadedException.php';
use Pecee\Http\Middleware\IMiddleware;
use Pecee\Http\Request;
class DummyMiddleware implements IMiddleware {
public function handle(Request $request) {
throw new MiddlewareLoadedException('Middleware loaded!');
}
}
@@ -0,0 +1,2 @@
<?php
class MiddlewareLoadedException extends \Exception {}
+8
View File
@@ -0,0 +1,8 @@
<?php
class ExceptionHandler implements \Pecee\Handler\IExceptionHandler {
public function handleError(\Pecee\Http\Request $request, \Pecee\SimpleRouter\RouterEntry $router = null, \Exception $error){
throw $error;
}
}
+43
View File
@@ -0,0 +1,43 @@
<?php
require_once 'Dummy/DummyMiddleware.php';
require_once 'Dummy/DummyController.php';
class GroupTest extends PHPUnit_Framework_TestCase {
protected $result;
protected function group() {
$this->result = true;
}
public function testGroup() {
$this->result = false;
\Pecee\SimpleRouter\SimpleRouter::group(['prefix' => '/group'], $this->group());
try {
\Pecee\SimpleRouter\SimpleRouter::start();
} catch(Exception $e) {
}
$this->assertTrue($this->result);
}
public function testNestedGroup() {
\Pecee\SimpleRouter\RouterBase::getInstance()->getRequest()->setUri('/api/v1/test');
\Pecee\SimpleRouter\RouterBase::getInstance()->getRequest()->setMethod('get');
\Pecee\SimpleRouter\SimpleRouter::group(['prefix' => '/api'], function() {
\Pecee\SimpleRouter\SimpleRouter::group(['prefix' => '/v1'], function() {
\Pecee\SimpleRouter\SimpleRouter::get('/test', 'DummyController@start');
});
});
\Pecee\SimpleRouter\SimpleRouter::start();
}
}
+30
View File
@@ -0,0 +1,30 @@
<?php
require_once 'Dummy/DummyMiddleware.php';
require_once 'Dummy/DummyController.php';
require_once 'Dummy/Handler/ExceptionHandler.php';
class MiddlewareTest extends PHPUnit_Framework_TestCase {
public function testMiddlewareFound() {
\Pecee\Http\Request::getInstance()->setMethod('get');
\Pecee\Http\Request::getInstance()->setUri('/my/test/url');
\Pecee\SimpleRouter\SimpleRouter::group(['exceptionHandler' => 'ExceptionHandler'], function() {
\Pecee\SimpleRouter\SimpleRouter::get('/my/test/url', 'DummyController@start', ['middleware' => 'DummyMiddleware']);
});
$found = false;
try {
\Pecee\SimpleRouter\SimpleRouter::start();
}catch(\Exception $e) {
$found = ($e instanceof MiddlewareLoadedException);
}
$this->assertTrue($found);
}
}
+109
View File
@@ -0,0 +1,109 @@
<?php
require_once 'Dummy/DummyMiddleware.php';
require_once 'Dummy/DummyController.php';
require_once 'Dummy/Handler/ExceptionHandler.php';
class RouterRouteTest extends PHPUnit_Framework_TestCase {
public function testNotFound() {
\Pecee\Http\Request::getInstance()->setMethod('get');
\Pecee\Http\Request::getInstance()->setUri('/test-param1-param2');
\Pecee\SimpleRouter\SimpleRouter::group(['exceptionHandler' => 'ExceptionHandler'], function() {
\Pecee\SimpleRouter\SimpleRouter::get('/non-existing-path', 'DummyController@start');
});
$found = false;
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()->getRequest()->setUri('/my/test/url');
\Pecee\SimpleRouter\RouterBase::getInstance()->getRequest()->setMethod('get');
\Pecee\SimpleRouter\SimpleRouter::get('/my/test/url', 'DummyController@start');
\Pecee\SimpleRouter\SimpleRouter::start();
}
public function testPost() {
\Pecee\SimpleRouter\RouterBase::getInstance()->getRequest()->setUri('/my/test/url');
\Pecee\Http\Request::getInstance()->setMethod('post');
\Pecee\SimpleRouter\SimpleRouter::post('/my/test/url', 'DummyController@start');
\Pecee\SimpleRouter\SimpleRouter::start();
}
public function testPut() {
\Pecee\SimpleRouter\RouterBase::getInstance()->getRequest()->setUri('/my/test/url');
\Pecee\Http\Request::getInstance()->setMethod('put');
\Pecee\SimpleRouter\SimpleRouter::put('/my/test/url', 'DummyController@start');
\Pecee\SimpleRouter\SimpleRouter::start();
}
public function testDelete() {
\Pecee\SimpleRouter\RouterBase::getInstance()->getRequest()->setUri('/my/test/url');
\Pecee\Http\Request::getInstance()->setMethod('delete');
\Pecee\SimpleRouter\SimpleRouter::delete('/my/test/url', 'DummyController@start');
\Pecee\SimpleRouter\SimpleRouter::start();
}
public function testMethodNotAllowed() {
\Pecee\SimpleRouter\RouterBase::getInstance()->getRequest()->setUri('/my/test/url');
\Pecee\Http\Request::getInstance()->setMethod('post');
\Pecee\SimpleRouter\SimpleRouter::get('/my/test/url', 'DummyController@start');
try {
\Pecee\SimpleRouter\SimpleRouter::start();
} catch(\Exception $e) {
$this->assertEquals(403, $e->getCode());
}
}
public function testSimpleParam() {
\Pecee\Http\Request::getInstance()->setMethod('get');
\Pecee\Http\Request::getInstance()->setUri('/test-param1');
\Pecee\SimpleRouter\SimpleRouter::get('/test-{param1}', 'DummyController@param');
\Pecee\SimpleRouter\SimpleRouter::start();
}
public function testMultiParam() {
\Pecee\Http\Request::getInstance()->setMethod('get');
\Pecee\Http\Request::getInstance()->setUri('/test-param1-param2');
\Pecee\SimpleRouter\SimpleRouter::get('/test-{param1}-{param2}', 'DummyController@param');
\Pecee\SimpleRouter\SimpleRouter::start();
}
public function testPathParam() {
\Pecee\Http\Request::getInstance()->setMethod('get');
\Pecee\Http\Request::getInstance()->setUri('/test/path/param1');
\Pecee\SimpleRouter\SimpleRouter::get('/test/path/{param}', 'DummyController@param');
\Pecee\SimpleRouter\SimpleRouter::start();
}
}