mirror of
https://github.com/skipperbent/simple-php-router.git
synced 2026-06-17 16:57:53 +00:00
Compare commits
175 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2952f6a3b6 | |||
| 16e326ad9f | |||
| d9b2328e82 | |||
| 4a03005c68 | |||
| 2d57b45c7b | |||
| 52034411cf | |||
| 98cc8504d4 | |||
| 4c8ed5bb3d | |||
| 035a5b1629 | |||
| 9fed6ffb3f | |||
| 832aff0358 | |||
| 15da599e82 | |||
| 43e05ad821 | |||
| 9274acb591 | |||
| 2fd32868c2 | |||
| 5c7759ab72 | |||
| e51b72f0e0 | |||
| c7b8593185 | |||
| 3b5e2aee9d | |||
| 27ba532b2d | |||
| fb478f475c | |||
| a8620cbc70 | |||
| 4e054dccf5 | |||
| e7dfbb159c | |||
| 8c5a5327d1 | |||
| 1142d9d4ce | |||
| 3c9a675f25 | |||
| 5d5c96e802 | |||
| dfccd99f2f | |||
| bfdaf8ac52 | |||
| 980a4e9b6a | |||
| 71dc6e172f | |||
| fab0d0641c | |||
| 6ee172927f | |||
| 4169716f87 | |||
| bb5e629199 | |||
| 53e5b5362f | |||
| 0cc0a59fd5 | |||
| 6780b24e59 | |||
| b540c01650 | |||
| 498fd6b07d | |||
| eb8832beec | |||
| 96ab22a4f8 | |||
| eeafcb7862 | |||
| 7f528c133b | |||
| 1a9351c690 | |||
| 5a50190293 | |||
| f98e5ac59d | |||
| a2dbf4149b | |||
| 355ef01d63 | |||
| ba736b47a3 | |||
| d3162b5a2b | |||
| 6ab1200fd5 | |||
| 67a00c6800 | |||
| 810b80487d | |||
| 1420203149 | |||
| 18a9df56ca | |||
| f7af53a9af | |||
| 6b8351f1b8 | |||
| 6e14ded03f | |||
| eb3ddf2bf7 | |||
| 2afe784f47 | |||
| 899081f8d8 | |||
| 94e98ad5f0 | |||
| e7b9206bc9 | |||
| 8f24256434 | |||
| ec355c90b5 | |||
| cd6e800984 | |||
| a8633b5c51 | |||
| 7fdeef74d6 | |||
| 17adfb8aa4 | |||
| 7a429cec1d | |||
| 3da7c4b446 | |||
| 11bd5a7d11 | |||
| f3ac9dc47c | |||
| be32796b01 | |||
| 22563671c5 | |||
| 8b3d71a328 | |||
| 491e920cfc | |||
| 1fae638aaf | |||
| 6cef099110 | |||
| 5f95290e4b | |||
| 7234415e24 | |||
| 257875c6f9 | |||
| 9b743e6e57 | |||
| 457dbc5710 | |||
| fc4fd0edf1 | |||
| 2f2c3ca3ca | |||
| b34738a51a | |||
| 37c8bc9f32 | |||
| 975c27659c | |||
| 75029b330a | |||
| 27371dfa11 | |||
| 6e102711a8 | |||
| 4d58fbf7b5 | |||
| fd5d893040 | |||
| 35bb58818e | |||
| c1512740af | |||
| 212ae133de | |||
| 438d3c258b | |||
| ae58231fa1 | |||
| a58ec1544d | |||
| 1bafbab56b | |||
| 6395801a06 | |||
| 060479f1fe | |||
| b29f2ce37d | |||
| 358b25d4f1 | |||
| 5483ffe458 | |||
| 6d8e95fcaa | |||
| 3d45851d9b | |||
| db78135d19 | |||
| c4fcf750d4 | |||
| ee5c2207f8 | |||
| a92b6008fa | |||
| b1ca3fc9ef | |||
| 253c0c70d4 | |||
| 115c8e510a | |||
| 53ba2d7ac5 | |||
| 35ee79d02c | |||
| 315fe05769 | |||
| 6306b604e8 | |||
| 4dd0739df9 | |||
| a57113309a | |||
| 0bd234d996 | |||
| 27d24758b1 | |||
| 4d08f08c68 | |||
| aeacda1812 | |||
| 765204f552 | |||
| 4267cb8751 | |||
| 714edf7902 | |||
| c3b12ba053 | |||
| b096742d6b | |||
| 3298970798 | |||
| bb6f56ef8c | |||
| 6c675124fa | |||
| 14a030294e | |||
| 8bc8124366 | |||
| 1332ef7139 | |||
| 12bbc98a82 | |||
| 69b0f4320e | |||
| 4c60055c7e | |||
| 89aeeeb593 | |||
| 849749969d | |||
| c37a7159d2 | |||
| df2545dd37 | |||
| bd0a6af41f | |||
| 3510c4c5a4 | |||
| 7ff88bc658 | |||
| ce27196083 | |||
| 9304b9f866 | |||
| ed886cb7b3 | |||
| cbaa0bcaac | |||
| 98b1759967 | |||
| b9be7695a7 | |||
| 97e2edd207 | |||
| 87fbbcbba2 | |||
| 5a501db767 | |||
| 669bfa0a86 | |||
| 4f07f38cf5 | |||
| c67ab20ddd | |||
| ad1ce21c66 | |||
| 5a19d6339c | |||
| 23ee53060e | |||
| 77ba9f165e | |||
| 4dd6417f71 | |||
| 3012435caa | |||
| 0bec524606 | |||
| d4e9ef7744 | |||
| cede827a45 | |||
| 9da90d1435 | |||
| 4d70efcc4d | |||
| e1c549bfdb | |||
| b9f426989b | |||
| f1f5faa81e | |||
| 6c56947792 |
+3
-1
@@ -1,2 +1,4 @@
|
||||
.idea
|
||||
composer.lock
|
||||
composer.lock
|
||||
vendor/
|
||||
demo-project/vendor
|
||||
@@ -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 Laravel 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, with both simplicity and expandability in mind.
|
||||
|
||||
## Installation
|
||||
Add the latest version pf 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,66 +184,28 @@ 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;
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
This is a basic example of a helper function for generating urls.
|
||||
#### Helper functions examples
|
||||
**This is a basic example of a helper function for generating urls.**
|
||||
|
||||
```php
|
||||
use Pecee\SimpleRouter\RouterBase;
|
||||
@@ -212,7 +214,7 @@ function url($controller, $parameters = null, $getParams = null) {
|
||||
}
|
||||
```
|
||||
|
||||
This is a basic example for getting the current csrf token
|
||||
**This is a basic example for getting the current csrf token**
|
||||
|
||||
```php
|
||||
/**
|
||||
@@ -269,6 +271,154 @@ 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
|
||||
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.
|
||||
|
||||
```php
|
||||
$route = Request::getInstance()->getLoadedRoute();
|
||||
|
||||
$route->setCallback('Example\MyCustomClass@hello');
|
||||
|
||||
// -- or --
|
||||
|
||||
$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:
|
||||
|
||||
@@ -277,9 +427,12 @@ http://laravel.com/docs/5.1/routing
|
||||
## Easily extendable
|
||||
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.
|
||||
|
||||
## 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
@@ -13,7 +13,7 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
|
||||
"php": ">=5.4.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "4.7.7"
|
||||
|
||||
@@ -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/*'];
|
||||
|
||||
}
|
||||
@@ -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');
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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');
|
||||
});
|
||||
|
||||
});
|
||||
@@ -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/"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
RewriteEngine on
|
||||
RewriteCond %{SCRIPT_FILENAME} !-f
|
||||
RewriteCond %{SCRIPT_FILENAME} !-d
|
||||
RewriteCond %{SCRIPT_FILENAME} !-l
|
||||
RewriteRule ^(.*)$ index.php/$1
|
||||
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
// load composer dependencies
|
||||
require '../vendor/autoload.php';
|
||||
|
||||
// Start the routing
|
||||
\Demo\Router::start();
|
||||
+11
-9
@@ -7,12 +7,6 @@ class CsrfToken {
|
||||
|
||||
protected $token;
|
||||
|
||||
public function __construct() {
|
||||
if($this->getToken() === null) {
|
||||
$this->setToken($this->generateToken());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate random identifier for CSRF token
|
||||
* @return string
|
||||
@@ -43,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, '/');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -51,10 +45,18 @@ class CsrfToken {
|
||||
* @return string|null
|
||||
*/
|
||||
public function getToken(){
|
||||
if(isset($_COOKIE[self::CSRF_KEY])) {
|
||||
return $_COOKIE[self::CSRF_KEY];
|
||||
if($this->hasToken()) {
|
||||
return $_COOKIE[static::CSRF_KEY];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the csrf token has been defined
|
||||
* @return bool
|
||||
*/
|
||||
public function hasToken() {
|
||||
return isset($_COOKIE[static::CSRF_KEY]);
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
<?php
|
||||
namespace Pecee\SimpleRouter;
|
||||
namespace Pecee\Exception;
|
||||
class RouterException extends \Exception { }
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
+68
-15
@@ -1,15 +1,13 @@
|
||||
<?php
|
||||
namespace Pecee\Http;
|
||||
|
||||
use Pecee\Http\Input\Input;
|
||||
|
||||
class Request {
|
||||
|
||||
protected static $instance;
|
||||
|
||||
protected $data;
|
||||
protected $uri;
|
||||
protected $host;
|
||||
protected $method;
|
||||
protected $headers;
|
||||
|
||||
/**
|
||||
* Return new instance
|
||||
@@ -24,13 +22,27 @@ 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->headers = array_change_key_case(getallheaders(), CASE_LOWER);
|
||||
$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() {
|
||||
$headers = array();
|
||||
foreach ($_SERVER as $name => $value) {
|
||||
if (substr($name, 0, 5) === 'HTTP_') {
|
||||
$headers[strtolower(str_replace('_', '-', substr($name, 5)))] = $value;
|
||||
}
|
||||
}
|
||||
return $headers;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -84,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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -113,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) {
|
||||
@@ -130,4 +154,33 @@ class Request {
|
||||
return isset($this->data[$name]) ? $this->data[$name] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the currently loaded route.
|
||||
* @return \Pecee\SimpleRouter\RouterEntry
|
||||
*/
|
||||
public function getLoadedRoute() {
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -19,14 +19,19 @@ class Response {
|
||||
* Redirect the response
|
||||
*
|
||||
* @param string $url
|
||||
* @param int $httpCode
|
||||
*/
|
||||
public function redirect($url) {
|
||||
$this->header('Location: ' . $url);
|
||||
public function redirect($url, $httpCode = null) {
|
||||
if($httpCode !== null) {
|
||||
$this->httpCode($httpCode);
|
||||
}
|
||||
|
||||
$this->header('location: ' . $url);
|
||||
die();
|
||||
}
|
||||
|
||||
public function refresh() {
|
||||
$this->redirect(url());
|
||||
$this->redirect(Request::getInstance()->getUri());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -64,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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Pecee\SimpleRouter;
|
||||
|
||||
interface IRouteEntry {
|
||||
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
<?php
|
||||
namespace Pecee\SimpleRouter;
|
||||
|
||||
use Pecee\ArrayUtil;
|
||||
use Pecee\Exception\RouterException;
|
||||
use Pecee\Handler\IExceptionHandler;
|
||||
use Pecee\Http\Middleware\BaseCsrfVerifier;
|
||||
use Pecee\Http\Request;
|
||||
use Pecee\Url;
|
||||
|
||||
class RouterBase {
|
||||
|
||||
@@ -15,24 +15,26 @@ class RouterBase {
|
||||
protected $routes;
|
||||
protected $processedRoutes;
|
||||
protected $controllerUrlMap;
|
||||
protected $backstack;
|
||||
protected $loadedRoute;
|
||||
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->routes = array();
|
||||
$this->backstack = array();
|
||||
$this->controllerUrlMap = array();
|
||||
$this->request = Request::getInstance();
|
||||
$this->routes = array();
|
||||
$this->backStack = array();
|
||||
$this->controllerUrlMap = array();
|
||||
$this->bootManagers = array();
|
||||
$this->exceptionHandlers = array();
|
||||
}
|
||||
|
||||
public function addRoute(RouterEntry $route) {
|
||||
if($this->currentRoute !== null) {
|
||||
$this->backstack[] = $route;
|
||||
$this->backStack[] = $route;
|
||||
} else {
|
||||
$this->routes[] = $route;
|
||||
}
|
||||
@@ -41,23 +43,24 @@ class RouterBase {
|
||||
protected function processRoutes(array $routes, array $settings = array(), array $prefixes = array(), $backStack = false, $group = null) {
|
||||
// Loop through each route-request
|
||||
|
||||
$activeGroup = null;
|
||||
|
||||
$routesCount = count($routes);
|
||||
$mergedSettings = array();
|
||||
|
||||
/* @var $route RouterEntry */
|
||||
for($i = 0; $i < $routesCount; $i++) {
|
||||
|
||||
$route = $routes[$i];
|
||||
|
||||
$route->setGroup($group);
|
||||
$route->addSettings($settings);
|
||||
|
||||
if($backStack) {
|
||||
$route->setGroup($group);
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -65,15 +68,16 @@ 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(), '/'));
|
||||
}
|
||||
|
||||
$route->addSettings($settings);
|
||||
/* @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() );
|
||||
}
|
||||
|
||||
$this->controllerUrlMap[] = $route;
|
||||
@@ -82,72 +86,135 @@ class RouterBase {
|
||||
$this->currentRoute = $route;
|
||||
|
||||
if($route instanceof RouterGroup && is_callable($route->getCallback())) {
|
||||
|
||||
$route->renderRoute($this->request);
|
||||
$activeGroup = $route;
|
||||
$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;
|
||||
|
||||
if(count($this->backstack)) {
|
||||
$backStack = $this->backstack;
|
||||
$this->backstack = array();
|
||||
if(count($this->backStack)) {
|
||||
$backStack = $this->backStack;
|
||||
$this->backStack = array();
|
||||
|
||||
// Route any routes added to the backstack
|
||||
$this->processRoutes($backStack, $mergedSettings, $newPrefixes, true, $activeGroup);
|
||||
$this->processRoutes($backStack, $mergedSettings, $newPrefixes, true, $group);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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];
|
||||
|
||||
$routeMatch = $route->matchRoute($this->request);
|
||||
|
||||
if($routeMatch && !($routeMatch instanceof RouterGroup)) {
|
||||
|
||||
if(count($route->getRequestMethods()) && !in_array($this->request->getMethod(), $route->getRequestMethods())) {
|
||||
$routeNotAllowed = true;
|
||||
continue;
|
||||
if(!($this->request instanceof Request)) {
|
||||
throw new RouterException('Custom router bootmanager "'. get_class($manager) .'" must return instance of Request.');
|
||||
}
|
||||
}
|
||||
|
||||
$routeNotAllowed = false;
|
||||
|
||||
$this->loadedRoute = $routeMatch;
|
||||
$routeMatch->loadMiddleware($this->request);
|
||||
$routeMatch->renderRoute($this->request);
|
||||
break;
|
||||
}
|
||||
|
||||
// Loop through each route-request
|
||||
$this->processRoutes($this->routes);
|
||||
|
||||
if($original === true) {
|
||||
// Verify csrf token for request
|
||||
if ($this->baseCsrfVerifier !== null) {
|
||||
$this->baseCsrfVerifier->handle($this->request);
|
||||
}
|
||||
}
|
||||
|
||||
$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->loadedRoute) {
|
||||
throw new RouterException(sprintf('Route not found: %s', $this->request->getUri()), 404);
|
||||
if(!$this->request->loadedRoute) {
|
||||
$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
|
||||
*/
|
||||
@@ -157,17 +224,37 @@ 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return RouterEntry
|
||||
*/
|
||||
public function getLoadedRoute() {
|
||||
if(!($this->loadedRoute instanceof RouterGroup)) {
|
||||
return $this->loadedRoute;
|
||||
if(!($this->request->loadedRoute instanceof RouterGroup)) {
|
||||
return $this->request->loadedRoute;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -176,7 +263,7 @@ class RouterBase {
|
||||
* @return array
|
||||
*/
|
||||
public function getBackstack() {
|
||||
return $this->backstack;
|
||||
return $this->backStack;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -221,9 +308,37 @@ class RouterBase {
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function processUrl($route, $method = null, $parameters = null, $getParams = null) {
|
||||
public function arrayToParams(array $getParams = null, $includeEmpty = true) {
|
||||
|
||||
$url = '/' . trim($route->getUrl(), '/');
|
||||
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 RouterController || $route instanceof RouterResource) && $method !== null) {
|
||||
$url .= $method;
|
||||
@@ -235,26 +350,32 @@ class RouterBase {
|
||||
}
|
||||
} else {
|
||||
/* @var $route RouterEntry */
|
||||
$params = $route->getParameters();
|
||||
if(count($params)) {
|
||||
$i = 0;
|
||||
foreach($params as $param => $value) {
|
||||
$value = (isset($parameters[$param])) ? $parameters[$param] : $value;
|
||||
$url = str_ireplace(array('{' . $param. '}', '{' . $param. '?}'), $value, $url);
|
||||
$i++;
|
||||
}
|
||||
if(is_array($parameters)) {
|
||||
$params = array_merge($route->getParameters(), $parameters);
|
||||
} else {
|
||||
// If no parameters are specified in the route, assume that the provided parameters should be used.
|
||||
if(count($parameters)) {
|
||||
$url = rtrim($url, '/') . '/' . join('/', $parameters);
|
||||
}
|
||||
$params = $route->getParameters();
|
||||
}
|
||||
|
||||
$otherParams = [];
|
||||
|
||||
$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 && count($getParams)) {
|
||||
$url .= '?'.Url::arrayToParams($getParams);
|
||||
if($getParams !== null) {
|
||||
$url .= $this->arrayToParams($getParams);
|
||||
}
|
||||
|
||||
return $url;
|
||||
@@ -270,8 +391,21 @@ class RouterBase {
|
||||
throw new \InvalidArgumentException('Invalid type for getParams. Must be array or null');
|
||||
}
|
||||
|
||||
if($controller === null && $parameters === null && $this->loadedRoute !== null) {
|
||||
return $this->processUrl($this->loadedRoute, null, $getParams);
|
||||
// Return current route if no options has been specified
|
||||
if($controller === null && $parameters === null) {
|
||||
$getParams = (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->request->loadedRoute !== null) {
|
||||
return $this->processUrl($this->request->loadedRoute, $this->request->loadedRoute->getMethod(), $parameters, $getParams);
|
||||
}
|
||||
|
||||
$c = '';
|
||||
@@ -285,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);
|
||||
}
|
||||
|
||||
@@ -328,23 +462,25 @@ class RouterBase {
|
||||
$url = array($controller);
|
||||
|
||||
if(is_array($parameters)) {
|
||||
ArrayUtil::append($url, $parameters);
|
||||
foreach($parameters as $key => $value) {
|
||||
array_push($url,$value);
|
||||
}
|
||||
}
|
||||
|
||||
$url = '/' . trim(join('/', $url), '/') . '/';
|
||||
|
||||
if(is_array($getParams)) {
|
||||
$url .= '?' . Url::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 {
|
||||
@@ -38,10 +39,12 @@ class RouterController extends RouterEntry {
|
||||
|
||||
return $class;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function matchRoute(Request $request) {
|
||||
$url = parse_url($request->getUri());
|
||||
$url = parse_url(urldecode($request->getUri()));
|
||||
$url = rtrim($url['path'], '/') . '/';
|
||||
|
||||
if(strtolower($url) == strtolower($this->url) || stripos($url, $this->url) === 0) {
|
||||
@@ -61,7 +64,7 @@ class RouterController extends RouterEntry {
|
||||
// Set callback
|
||||
$this->setCallback($this->controller . '@' . $this->method);
|
||||
|
||||
return $this;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@@ -76,10 +79,12 @@ class RouterController extends RouterEntry {
|
||||
|
||||
/**
|
||||
* @param string $url
|
||||
* @return static
|
||||
*/
|
||||
public function setUrl($url) {
|
||||
$url = rtrim($url, '/') . '/';
|
||||
$this->url = $url;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -91,9 +96,11 @@ class RouterController extends RouterEntry {
|
||||
|
||||
/**
|
||||
* @param string $controller
|
||||
* @return static
|
||||
*/
|
||||
public function setController($controller) {
|
||||
$this->controller = $controller;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -105,9 +112,11 @@ class RouterController extends RouterEntry {
|
||||
|
||||
/**
|
||||
* @param string $method
|
||||
* @return static
|
||||
*/
|
||||
public function setMethod($method) {
|
||||
$this->method = $method;
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Pecee\SimpleRouter;
|
||||
|
||||
use Pecee\Exception\RouterException;
|
||||
use Pecee\Http\Middleware\IMiddleware;
|
||||
use Pecee\Http\Request;
|
||||
|
||||
@@ -27,7 +28,7 @@ abstract class RouterEntry {
|
||||
public function __construct() {
|
||||
$this->settings = array();
|
||||
$this->settings['requestMethods'] = array();
|
||||
$this->settings['parametersRegex'] = array();
|
||||
$this->settings['where'] = array();
|
||||
$this->settings['parameters'] = array();
|
||||
}
|
||||
|
||||
@@ -77,12 +78,22 @@ abstract class RouterEntry {
|
||||
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 $prefix
|
||||
* @return self
|
||||
*/
|
||||
public function setPrefix($prefix) {
|
||||
$this->prefix = '/' . trim($prefix, '/') . '/';
|
||||
$this->prefix = '/' . ltrim($prefix, '/');
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -112,7 +123,7 @@ abstract class RouterEntry {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @return string|array
|
||||
*/
|
||||
public function getMiddleware() {
|
||||
return $this->middleware;
|
||||
@@ -155,7 +166,7 @@ abstract class RouterEntry {
|
||||
* @return self
|
||||
*/
|
||||
public function where(array $options) {
|
||||
$this->parametersRegex = array_merge($this->parametersRegex, $options);
|
||||
$this->where = array_merge($this->where, $options);
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -178,10 +189,6 @@ abstract class RouterEntry {
|
||||
public function getMergeableSettings() {
|
||||
$settings = $this->settings;
|
||||
|
||||
/*if(isset($settings['middleware'])) {
|
||||
unset($settings['middleware']);
|
||||
}*/
|
||||
|
||||
if(isset($settings['prefix'])) {
|
||||
unset($settings['prefix']);
|
||||
}
|
||||
@@ -225,7 +232,7 @@ abstract class RouterEntry {
|
||||
}
|
||||
|
||||
/**
|
||||
* Dynamicially set settings value
|
||||
* Dynamically set settings value
|
||||
*
|
||||
* @param string $name
|
||||
* @param mixed|null $value
|
||||
@@ -242,29 +249,18 @@ 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 = '';
|
||||
$isParameter = false;
|
||||
$parameter = '';
|
||||
|
||||
// Use custom parameter regex if it exists
|
||||
if(is_array($this->parametersRegex) && isset($this->parametersRegex[$parameter])) {
|
||||
$parameterRegex = $this->parametersRegex[$parameter];
|
||||
}
|
||||
|
||||
$routeLength = strlen($route);
|
||||
for($i = 0; $i < $routeLength; $i++) {
|
||||
|
||||
$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) === '/') {
|
||||
@@ -275,12 +271,18 @@ abstract class RouterEntry {
|
||||
} elseif($isParameter && $character === '}') {
|
||||
$required = true;
|
||||
// Check for optional parameter
|
||||
|
||||
// Use custom parameter regex if it exists
|
||||
if(is_array($this->where) && isset($this->where[$parameter])) {
|
||||
$parameterRegex = $this->where[$parameter];
|
||||
}
|
||||
|
||||
if($lastCharacter === '?') {
|
||||
$parameter = substr($parameter, 0, strlen($parameter)-1);
|
||||
$regex .= '(?:(?:\/{0,1}(?P<'.$parameter.'>'.$parameterRegex.')){0,1}\\/{0,1})';
|
||||
$regex .= '(?:\/?(?P<' . $parameter . '>'. $parameterRegex .')[^\/]?)?';
|
||||
$required = false;
|
||||
} else {
|
||||
$regex .= '(?:\\/{0,1}(?P<' . $parameter . '>'. $parameterRegex .')\\/{0,1})';
|
||||
$regex .= '\/?(?P<' . $parameter . '>'. $parameterRegex .')[^\/]?';
|
||||
}
|
||||
$parameterNames[] = array('name' => $parameter, 'required' => $required);
|
||||
$parameter = '';
|
||||
@@ -299,20 +301,25 @@ abstract class RouterEntry {
|
||||
|
||||
$parameterValues = array();
|
||||
|
||||
if(preg_match('/^'.$regex.'$/is', $url, $parameterValues)) {
|
||||
if(preg_match('/^'.$regex.'\/?$/is', $url, $parameterValues)) {
|
||||
|
||||
$parameters = array();
|
||||
|
||||
$max = count($parameterNames);
|
||||
|
||||
if(count($max)) {
|
||||
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);
|
||||
}
|
||||
|
||||
if(!$name['required'] && $parameterValue === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$parameters[$name['name']] = $parameterValue;
|
||||
}
|
||||
}
|
||||
@@ -332,7 +339,7 @@ abstract class RouterEntry {
|
||||
throw new RouterException($middleware . ' must be instance of Middleware');
|
||||
}
|
||||
|
||||
/* @var $class Middleware */
|
||||
/* @var $class IMiddleware */
|
||||
$middleware->handle($request);
|
||||
}
|
||||
} else {
|
||||
@@ -341,16 +348,14 @@ abstract class RouterEntry {
|
||||
throw new RouterException($this->getMiddleware() . ' must be instance of Middleware');
|
||||
}
|
||||
|
||||
/* @var $class Middleware */
|
||||
/* @var $class IMiddleware */
|
||||
$middleware->handle($request);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
@@ -365,7 +370,11 @@ abstract class RouterEntry {
|
||||
throw new RouterException(sprintf('Method %s does not exist in class %s', $method, $className), 404);
|
||||
}
|
||||
|
||||
call_user_func_array(array($class, $method), $this->getParameters());
|
||||
$parameters = array_filter($this->getParameters(), function($var){
|
||||
return !is_null($var);
|
||||
});
|
||||
|
||||
call_user_func_array(array($class, $method), $parameters);
|
||||
|
||||
return $class;
|
||||
}
|
||||
@@ -385,7 +394,7 @@ abstract class RouterEntry {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get allowed requeset methods
|
||||
* Get allowed request methods
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
|
||||
@@ -2,23 +2,43 @@
|
||||
|
||||
namespace Pecee\SimpleRouter;
|
||||
|
||||
use Pecee\Exception\RouterException;
|
||||
use Pecee\Http\Request;
|
||||
|
||||
class RouterGroup extends RouterEntry {
|
||||
|
||||
public function __construct() {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function matchDomain() {
|
||||
public function matchDomain(Request $request) {
|
||||
if($this->domain !== null) {
|
||||
|
||||
$parameters = $this->parseParameters($this->domain, request()->getHost(), '[^.]*');
|
||||
if(is_array($this->domain)) {
|
||||
|
||||
if($parameters !== null) {
|
||||
$this->parameters = $parameters;
|
||||
$max = count($this->domain);
|
||||
|
||||
for($i = 0; $i < $max; $i++) {
|
||||
$domain = $this->domain[$i];
|
||||
|
||||
$parameters = $this->parseParameters($domain, $request->getHost(), '[^.]*');
|
||||
|
||||
if($parameters !== null) {
|
||||
$this->parameters = $parameters;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$parameters = $this->parseParameters($this->domain, $request->getHost(), '[^.]*');
|
||||
|
||||
if ($parameters !== null) {
|
||||
$this->parameters = $parameters;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function renderRoute(Request $request) {
|
||||
@@ -37,13 +57,18 @@ class RouterGroup extends RouterEntry {
|
||||
throw new RouterException('Method not allowed');
|
||||
}
|
||||
|
||||
$this->matchDomain();
|
||||
$this->matchDomain($request);
|
||||
|
||||
return parent::renderRoute($request);
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -55,4 +80,40 @@ class RouterGroup extends RouterEntry {
|
||||
return $this->exceptionHandler;
|
||||
}
|
||||
|
||||
public function getDomain() {
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,15 +1,13 @@
|
||||
<?php
|
||||
namespace Pecee\SimpleRouter;
|
||||
|
||||
use Pecee\Exception\RouterException;
|
||||
use Pecee\Http\Request;
|
||||
|
||||
class RouterResource extends RouterEntry {
|
||||
|
||||
const DEFAULT_METHOD = 'index';
|
||||
|
||||
protected $url;
|
||||
protected $controller;
|
||||
protected $method;
|
||||
protected $postMethod;
|
||||
|
||||
public function __construct($url, $controller) {
|
||||
@@ -45,62 +43,58 @@ class RouterResource extends RouterEntry {
|
||||
protected function call($method, $parameters) {
|
||||
$this->setCallback($this->controller . '@' . $method);
|
||||
$this->parameters = $parameters;
|
||||
return $this;
|
||||
return true;
|
||||
}
|
||||
|
||||
public function matchRoute(Request $request) {
|
||||
$url = parse_url($request->getUri());
|
||||
$url = parse_url(urldecode($request->getUri()));
|
||||
$url = rtrim($url['path'], '/') . '/';
|
||||
|
||||
if(strtolower($url) == strtolower($this->url) || stripos($url, $this->url) === 0) {
|
||||
$url = rtrim($url, '/');
|
||||
$route = rtrim($this->url, '/') . '/{id?}/{action?}';
|
||||
|
||||
$strippedUrl = trim(substr($url, strlen($this->url)), '/');
|
||||
$path = explode('/', $strippedUrl);
|
||||
$parameters = $this->parseParameters($route, $url);
|
||||
|
||||
$args = $path;
|
||||
$action = '';
|
||||
if($parameters !== null) {
|
||||
|
||||
if(count($args)) {
|
||||
$action = $args[0];
|
||||
array_shift($args);
|
||||
if(is_array($parameters)) {
|
||||
$parameters = array_merge($this->parameters, $parameters);
|
||||
}
|
||||
|
||||
if (count($path)) {
|
||||
$action = isset($parameters['action']) ? $parameters['action'] : null;
|
||||
unset($parameters['action']);
|
||||
|
||||
// Delete
|
||||
if($request->getMethod() === self::REQUEST_TYPE_DELETE && $this->postMethod === self::REQUEST_TYPE_POST) {
|
||||
return $this->call('destroy', $args);
|
||||
}
|
||||
|
||||
// Update
|
||||
if(in_array($request->getMethod(), array('put', 'patch')) && $this->postMethod === self::REQUEST_TYPE_POST) {
|
||||
return $this->call('update', array_merge(array($action), $args));
|
||||
}
|
||||
|
||||
// Edit
|
||||
if(isset($args[0]) && strtolower($args[0]) === 'edit' && $this->postMethod === self::REQUEST_TYPE_GET) {
|
||||
return $this->call('edit', array_merge(array($action), array_slice($args, 1)));
|
||||
}
|
||||
|
||||
// Create
|
||||
if(strtolower($action) === 'create' && $request->getMethod() === self::REQUEST_TYPE_GET) {
|
||||
return $this->call('create', $args);
|
||||
}
|
||||
|
||||
// Save
|
||||
if($this->postMethod === self::REQUEST_TYPE_POST) {
|
||||
return $this->call('store', $args);
|
||||
}
|
||||
|
||||
// Show
|
||||
if($action && $this->postMethod === self::REQUEST_TYPE_GET) {
|
||||
return $this->call('show', array_merge(array($action), $args));
|
||||
}
|
||||
|
||||
// Index
|
||||
return $this->call('index', $args);
|
||||
// Delete
|
||||
if($request->getMethod() === self::REQUEST_TYPE_DELETE && $this->postMethod === self::REQUEST_TYPE_POST) {
|
||||
return $this->call('destroy', $parameters);
|
||||
}
|
||||
|
||||
// Update
|
||||
if(in_array($request->getMethod(), array(self::REQUEST_TYPE_PATCH, self::REQUEST_TYPE_PUT)) && $this->postMethod === self::REQUEST_TYPE_POST) {
|
||||
return $this->call('update', $parameters);
|
||||
}
|
||||
|
||||
// Edit
|
||||
if(isset($action) && strtolower($action) === 'edit' && $this->postMethod === self::REQUEST_TYPE_GET) {
|
||||
return $this->call('edit', $parameters);
|
||||
}
|
||||
|
||||
// Create
|
||||
if(strtolower($action) === 'create' && $request->getMethod() === self::REQUEST_TYPE_GET) {
|
||||
return $this->call('create', $parameters);
|
||||
}
|
||||
|
||||
// Save
|
||||
if($this->postMethod === self::REQUEST_TYPE_POST) {
|
||||
return $this->call('store', $parameters);
|
||||
}
|
||||
|
||||
// Show
|
||||
if(isset($parameters['id']) && $this->postMethod === self::REQUEST_TYPE_GET) {
|
||||
return $this->call('show', $parameters);
|
||||
}
|
||||
|
||||
// Index
|
||||
return $this->call('index', $parameters);
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -115,10 +109,12 @@ class RouterResource extends RouterEntry {
|
||||
|
||||
/**
|
||||
* @param string $url
|
||||
* @return static
|
||||
*/
|
||||
public function setUrl($url) {
|
||||
$url = rtrim($url, '/') . '/';
|
||||
$this->url = $url;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -130,23 +126,11 @@ class RouterResource extends RouterEntry {
|
||||
|
||||
/**
|
||||
* @param string $controller
|
||||
* @return static
|
||||
*/
|
||||
public function setController($controller) {
|
||||
$this->controller = $controller;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getMethod() {
|
||||
return $this->method;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $method
|
||||
*/
|
||||
public function setMethod($method) {
|
||||
$this->method = $method;
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,21 +14,19 @@ class RouterRoute extends RouterEntry {
|
||||
parent::__construct();
|
||||
$this->setUrl($url);
|
||||
$this->setCallback($callback);
|
||||
|
||||
$this->settings['aliases'] = array();
|
||||
}
|
||||
|
||||
public function matchRoute(Request $request) {
|
||||
|
||||
$url = parse_url($request->getUri());
|
||||
$url = parse_url(urldecode($request->getUri()));
|
||||
$url = rtrim($url['path'], '/') . '/';
|
||||
|
||||
// Match on custom defined regular expression
|
||||
if($this->regexMatch) {
|
||||
$parameters = array();
|
||||
if(preg_match('/('.$this->regexMatch.')/is', request()->getHost() . $url, $parameters)) {
|
||||
if(preg_match('/('.$this->regexMatch.')/is', $request->getHost() . $url, $parameters)) {
|
||||
$this->parameters = (!is_array($parameters[0]) ? array($parameters[0]) : $parameters[0]);
|
||||
return $this;
|
||||
return true;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -46,7 +44,7 @@ class RouterRoute extends RouterEntry {
|
||||
$this->parameters = $parameters;
|
||||
}
|
||||
|
||||
return $this;
|
||||
return true;
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -74,7 +72,7 @@ class RouterRoute extends RouterEntry {
|
||||
if(count($parameters)) {
|
||||
$tmp = array();
|
||||
foreach($parameters as $param) {
|
||||
$tmp[$param] = '';
|
||||
$tmp[$param] = null;
|
||||
}
|
||||
$this->parameters = $tmp;
|
||||
}
|
||||
@@ -85,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){
|
||||
|
||||
@@ -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,46 +58,68 @@ class SimpleRouter {
|
||||
$group->setSettings($settings);
|
||||
}
|
||||
|
||||
$router = RouterBase::getInstance();
|
||||
$router->addRoute($group);
|
||||
RouterBase::getInstance()->addRoute($group);
|
||||
|
||||
return $group;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds get + post route
|
||||
*
|
||||
* @param string $url
|
||||
* @param callable $callback
|
||||
* @param array|null $settings
|
||||
* @return RouterRoute
|
||||
*/
|
||||
public static function basic($url, $callback, array $settings = null) {
|
||||
return self::match(['get', 'post'], $url, $callback, $settings);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 {}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user