Compare commits

..

31 Commits

Author SHA1 Message Date
Simon Sessingø 7e7319de06 Merge pull request #169 from skipperbent/v2-development
Fixed // on currentRoute urls.
2016-11-21 05:27:53 +02:00
Simon Sessingø 5ad7dcf9fd Fixed // on currentRoute urls. 2016-11-21 04:27:06 +01:00
Simon Sessingø 531b35532b Merge pull request #167 from skipperbent/v2-development
Bugfixes
2016-11-21 05:07:59 +02:00
Simon Sessingø dfd32c0904 Bugfixes
- Moved middleware stuff back to Route class.
- Fixed Tests not working due to invalid method.
- Updated depricated method-calls in demo-project.
2016-11-21 04:06:45 +01:00
Simon Sessingø c258f937e8 Merge pull request #165 from skipperbent/v2-development
V 2.4.0 development
2016-11-21 04:09:26 +02:00
Simon Sessingø efe5767220 Development
- Removed yet another depricated method.
- Fixed $_GET parameters being merged on default when calling getUrl.
- Fixed double / that could appear on urls sometimes.
2016-11-21 03:05:43 +01:00
Simon Sessingø 5415d73f4d Added phpDocs 2016-11-21 02:15:25 +01:00
Simon Sessingø 60c0bd6355 Removed getRoutes depricated class from Route class. 2016-11-21 01:53:53 +01:00
Simon Sessingø 8370d3d94e Development
- Made easier to extend.
- Added IRoute class.
- Changed namespace for Route classes.
- Moved find-url related stuff to Route classes itself.
- Added more tests for finding urls.
- Added support for custom names on RouteController and RouteResource.
- Removed depricated methods.
- Updated documentation.
- Updated demo-project to reflect changes.
- Other small bugfixes and improvements.
2016-11-21 01:50:28 +01:00
Simon Sessingø df3acb6605 Enchacements
- Added more doctype and descriptions on functionality.
- Renamed methods and properties to make better sense.
- Added IRoute interface for easier extendability.
2016-11-20 18:05:57 +01:00
Simon Sessingø 88d58cd7b7 Merge pull request #163 from skipperbent/v2-development
Fixed: wrong argument-type in getUrl method
2016-11-19 21:12:57 +02:00
Simon Sessingø 4a48a3fcf9 Fixed: wrong argument-type in getUrl method 2016-11-19 20:12:06 +01:00
Simon Sessingø d04c74ccad Merge pull request #161 from skipperbent/v2-development
V2 development
2016-11-19 20:41:26 +02:00
Simon Sessingø 9922dcc552 Updated documentation 2016-11-19 19:37:59 +01:00
Simon Sessingø 87d619ca24 More optimisations 2016-11-19 19:28:10 +01:00
Simon Sessingø 7e63197252 Major overhaul 2016-11-19 19:24:05 +01:00
Simon Sessingø 02809a4daf Merge pull request #159 from skipperbent/v2-development
Bugfixes
2016-11-19 10:15:56 +02:00
Simon Sessingø ba719cf880 Bugfixes 2016-11-19 09:15:34 +01:00
Simon Sessingø da6b5af19f Merge pull request #157 from skipperbent/v2-development
Bugfixes
2016-11-19 09:47:19 +02:00
Simon Sessingø 808d59d3d3 Bugfixes 2016-11-19 08:46:48 +01:00
Simon Sessingø 914ec9d1b7 Merge pull request #155 from skipperbent/v2-development
Small optimisations
2016-11-19 07:02:23 +02:00
Simon Sessingø d1f33d9b01 Small optimisation 2016-11-19 06:01:53 +01:00
Simon Sessingø 5f72755a98 Merge pull request #153 from skipperbent/v2-development
V2 development
2016-11-19 06:25:08 +02:00
Simon Sessingø d4a04920b8 More development 2016-11-19 05:22:51 +01:00
Simon Sessingø 4e12cb8bc3 More changes 2016-11-19 05:06:47 +01:00
Simon Sessingø 8f33cc1a39 Development 2016-11-19 04:41:29 +01:00
Simon Sessingø ed1ac74e7a Development
- Fixed updatae causing middlewares to sometimes load on wrong routes.
- Converted project to PSR/2.
- Updated InputCollection class and added get method for easy access to values.
- Complete refactor of RouterBase.
- Added findRoute method to RouterBase.
- It's now possible to change parameter modifiers and symbol by overwriting properties on RouterBase.
- Added RouterUrlTest unit-test for testing route-urls.
- Added IRestController that can be easily implemented in custom ResourceController-classes.
- It's now possible to use "-" instead of "_" when using getHeader method in Request class.
- Added PHPDocs.
- Fixed "/" route sometimes returning "//" as url.
- Optimisations and bugfixes.
2016-11-19 02:48:19 +01:00
Simon Sessingø 41d15d3acd Merge pull request #151 from skipperbent/v2-development
Re-added missing methods from version 1.
2016-11-17 19:01:17 +02:00
Simon Sessingø a4447313f6 Re-added missing methods from version 1. 2016-11-17 17:55:34 +01:00
Simon Sessingø ded9c8ebe0 Merge pull request #149 from skipperbent/v2-development
Updated documentation
2016-11-17 17:33:49 +02:00
Simon Sessingø 99f869b57d Updated documentation 2016-11-17 16:33:27 +01:00
57 changed files with 4071 additions and 2616 deletions
+225 -100
View File
@@ -18,7 +18,7 @@ The goal of this project is to create a router that is 100% compatible with the
### Features ### Features
- Basic routing (`GET`, `POST`, `PUT`, `DELETE`) with support for custom multiple verbs. - Basic routing (`GET`, `POST`, `PUT`, `PATCH`, `UPDATE`, `DELETE`) with support for custom multiple verbs.
- Regular Expression Constraints for parameters. - Regular Expression Constraints for parameters.
- Named routes. - Named routes.
- Generating url to routes. - Generating url to routes.
@@ -29,8 +29,8 @@ The goal of this project is to create a router that is 100% compatible with the
- CSRF protection. - CSRF protection.
- Optional parameters - Optional parameters
- Sub-domain routing - Sub-domain routing
- Custom boot managers to redirect urls to other routes - Custom boot managers to rewrite urls to "nicer" ones.
- Input manager; to manage `GET`, `POST` params. - Input manager; easily manage `GET`, `POST` and `FILE` values.
## Installation and demo ## Installation and demo
@@ -54,10 +54,10 @@ require_once 'routes.php';
/* /*
* The default namespace for route-callbacks, so we don't have to specify it each time. * The default namespace for route-callbacks, so we don't have to specify it each time.
* Can be overwritten by using the namespace config option. * Can be overwritten by using the namespace config option on your routes.
*/ */
SimpleRouter::setDefaultNamespace('MyWebsite\Controller'); SimpleRouter::setDefaultNamespace('MyWebsite');
// Start the routing // Start the routing
SimpleRouter::start(); SimpleRouter::start();
@@ -71,51 +71,81 @@ This router is heavily inspired by the Laravel 5.* router, so anything you find
- ExceptionsHandlers must implement the `IExceptionHandler` interface. - ExceptionsHandlers must implement the `IExceptionHandler` interface.
- Middlewares must implement the `IMiddleware` interface. - Middlewares must implement the `IMiddleware` interface.
- Resource controllers can inherit the `IRestController` interface, but is not required.
```php ```php
use Pecee\SimpleRouter\SimpleRouter; use Pecee\SimpleRouter\SimpleRouter;
/* /*
* This route will match the url /v1/services/answers/1/ * This route will match the url /v1/services/answers/1/
* The middleware is just a class that renders before the * The middleware is just a class that renders before the
* Controller or callback is loaded. This is useful for stopping * controller or callback is loaded.
* the request, for instance if a user is not authenticated. *
* This is useful for stopping the request, for
* instance if a user is not authenticated etc.
*/ */
// Add CSRF support (if needed)
SimpleRouter::csrfVerifier(new \Pecee\Http\Middleware\BaseCsrfVerifier()); // Add your csrfVerifier here
SimpleRouter::csrfVerifier(new \Demo\Middlewares\CsrfVerifier());
SimpleRouter::group(['middleware' => 'Middlewares\Site', 'exceptionHandler' => 'Handlers\CustomExceptionHandler'], function() {
SimpleRouter::get('/answers/{id}', 'ControllerAnswers@show', ['where' => ['id' => '[0-9]+']]);
/**
* Using optional parameters
*/
SimpleRouter::get('/answers/{id?}', 'ControllerAnswers@show');
/**
* This example will route url when matching the regular expression to the method.
* For example route: domain.com/ajax/music/world -> ControllerAjax@process (parameter: music/world)
*/
SimpleRouter::all('/ajax', 'ControllerAjax@process')->setMatch('.*?\\/ajax\\/([A-Za-z0-9\\/]+)');
/**
* Restful resource (see IRestController interface for available methods)
*/
SimpleRouter::resource('/rest', 'ControllerRessource');
/**
* Load the entire controller (where url matches method names - getIndex(), postIndex(), putIndex()).
* The url paths will determine which method to render.
*
* For example:
*
* GET /animals => getIndex()
* GET /animals/view => getView()
* POST /animals/save => postSave()
*
* etc.
*/
SimpleRouter::controller('/animals', 'ControllerAnimals');
/**
* Example of providing callback instead of Controller
*/
SimpleRouter::post('/something', function() {
die('Callback example');
});
});
SimpleRouter::get('/page/404', 'ControllerPage@notFound', ['as' => 'page.notfound']); SimpleRouter::get('/page/404', 'ControllerPage@notFound', ['as' => 'page.notfound']);
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');
/**
* This example will route url when matching the regular expression to the method.
* For example route: domain.com/ajax/music/world -> ControllerAjax@process (parameter: music/world)
*/
SimpleRouter::all('/ajax', 'ControllerAjax@process')->match('.*?\\/ajax\\/([A-Za-z0-9\\/]+)');
// Restful resource
SimpleRouter::resource('/rest', 'ControllerRessource');
// Load the entire controller (where url matches method names - getIndex(), postIndex() etc)
SimpleRouter::controller('/controller', 'ControllerDefault');
// Example of providing callback instead of Controller
SimpleRouter::get('/something', function() {
die('Callback example');
});
});
});
``` ```
#### ExceptionHandler example #### ExceptionHandler example
@@ -125,33 +155,45 @@ This is a basic example of an ExceptionHandler implementation:
```php ```php
namespace Demo\Handlers; namespace Demo\Handlers;
use Pecee\Handlers\IExceptionHandler;
use Pecee\Http\Request; use Pecee\Http\Request;
use Pecee\SimpleRouter\RouterEntry; use Pecee\SimpleRouter\Exceptions\NotFoundHttpException;
use Pecee\SimpleRouter\Route\ILoadableRoute;
class CustomExceptionHandler implements IExceptionHandler { class CustomExceptionHandler implements IExceptionHandler
{
public function handleError(Request $request, ILoadableRoute &$route = null, \Exception $error)
{
public function handleError( Request $request, RouterEntry $router = null, \Exception $error) { /* You can use the exception handler to format errors depending on the request and type. */
// If the error-code is 404; show another route which contains the page-not-found if (stripos($request->getUri(), '/api') !== false) {
if($error->getCode() === 404) {
// Throw your custom 404-page view
// - or -
// load another route with our 404 page
// - or -
// you can return the $request object to ignore the error and continue on rendering the route.
return $request->setUri(url('page.notfound'));
}
// Output error as json if on api path. response()->json([
if(stripos($request->getUri(), '/api') !== false) { 'error' => $error->getMessage(),
response()->json(['error' => $error->getMessage()]); 'code' => $error->getCode(),
} ]);
// Otherwise default exception will be thrown by the router. }
} /* The router will throw the NotFoundHttpException on 404 */
if($error instanceof NotFoundHttpException) {
/*
* Render your own custom 404-view, rewrite the request to another route,
* or simply return the $request object to ignore the error and continue on rendering the route.
*
* The code below will make the router render our page.notfound route.
*/
$request->setUri(url('page.notfound'));
return $request;
}
throw $error;
}
} }
``` ```
@@ -162,40 +204,47 @@ Route groups may also be used to route wildcard sub-domains. Sub-domains may be
```php ```php
Route::group(['domain' => '{account}.myapp.com'], function () { Route::group(['domain' => '{account}.myapp.com'], function () {
Route::get('user/{id}', function ($account, $id) { Route::get('user/{id}', function ($account, $id) {
// // Do stuff...
}); });
}); });
``` ```
The prefix group array attribute may be used to prefix each route in the group with a given URI. For example, you may want to prefix all route URIs within the group with admin: The prefix group array attribute may be used to prefix each route in the group with a given URI. For example, you may want to prefix all route URIs within the group with admin:
### Doing it the object oriented (hardcore) way ### Adding routes manually (with no helper)
The ```SimpleRouter``` class referenced in the previous example, is just a simple helper class that knows how to communicate with the ```RouterBase``` class. The ```SimpleRouter``` class referenced in the previous example, is just a simple helper class that knows how to communicate with the ```Router``` class.
If you are up for a challenge, want the full control or simply just want to create your own ```Router``` helper class, this example is for you. If you are up for a challenge, want the full control or simply just want to create your own ```Router``` helper class, this example is for you.
```php ```php
use \Pecee\SimpleRouter\RouterBase; use \Pecee\SimpleRouter\Router;
use \Pecee\SimpleRouter\RouterRoute; use \Pecee\SimpleRouter\Route\RouteUrl;
$router = RouterBase::getInstance(); /* Grap the router instance */
$router = Router::getInstance();
$route = new RouteUrl('/answer/1', function() {
$route = new RouterRoute('/answer/1', function() {
die('this callback will match /answer/1'); die('this callback will match /answer/1');
}); });
$route->setMiddleware('\HSWebserviceV1\Middleware\AuthMiddleware'); $route->setMiddleware('\Demo\Middlewares\AuthMiddleware');
$route->setNamespace('MyWebsite'); $route->setNamespace('MyWebsite');
$route->setPrefix('v1'); $route->setPrefix('v1');
// Add the route to the router /* Add the route to the router */
$router->addRoute($route); $router->addRoute($route);
``` ```
### Extending
This is a simple example of an integration into a framework. 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. The framework has it's own ```Router``` class which inherits from the ```SimpleRouter``` class. This allows the framework to add custom functionality like loading a custom `routes.php` file or add debugging information etc.
```php ```php
namespace Demo; namespace Demo;
@@ -210,7 +259,7 @@ class Router extends SimpleRouter {
require_once 'routes.php'; require_once 'routes.php';
// change default namespace for all routes // change default namespace for all routes
parent::setDefaultNamespace('\Demo\Controllers'); parent::setDefaultNamespace('\Demo');
// Do initial stuff // Do initial stuff
parent::start(); parent::start();
@@ -227,8 +276,25 @@ To simplify to use of simple-router functionality, we recommend you add these he
```php ```php
use Pecee\SimpleRouter\SimpleRouter; use Pecee\SimpleRouter\SimpleRouter;
function url($controller, $parameters = null, $getParams = null) { /**
SimpleRouter::getRoute($controller, $parameters, $getParams); * Get url for a route by using either name/alias, class or method name.
*
* The name parameter supports the following values:
* - Route name
* - Controller/resource name (with or without method)
* - Controller class name
*
* When searching for controller/resource by name, you can use this syntax "route.name@method".
* You can also use the same syntax when searching for a specific controller-class "MyController@home".
* If no arguments is specified, it will return the url for the current loaded route.
*
* @param string|null $name
* @param string|array|null $parameters
* @param array $getParams
* @return string
*/
function url($name = null, $parameters = null, array $getParams = array()) {
SimpleRouter::getUrl($name, $parameters, $getParams);
} }
/** /**
@@ -267,22 +333,73 @@ function input() {
## Getting urls ## Getting urls
**In ```routes.php``` we have added this route:** By default all controller and resource routes will use a simplified version of their url as name.
**Get routes using custom name (single route)**
```php ```php
SimpleRouter::get('/item/{id}', 'myController@show', ['as' => 'item']); SimpleRouter::get('/product-view/{id}', 'ProductsController@show', ['as' => 'product']);
url('product', ['id' => 22], ['category' => 'shoes']);
# output
# /product-view/22/?category=shoes
``` ```
**In the template we then call:** **Getting the url using the name (controller route)**
```php ```php
url('item', ['id' => 22], ['category' => 'shoes']); SimpleRouter::controller('/images', 'ImagesController', ['as' => 'picture']);
url('picture@getView', null, ['category' => 'shoes']);
url('picture', 'getView', ['category' => 'shoes']);
# output
# /images/view/?category=shows
# /images/view/?category=shows
``` ```
**Result url is:** **Getting the url using class**
```php ```php
/item/22/?category=shoes SimpleRouter::get('/product-view/{id}', 'ProductsController@show', ['as' => 'product']);
SimpleRouter::controller('/images', 'ImagesController');
url('ProductsController@show', ['id' => 22], ['category' => 'shoes']);
url('ImagesController@getImage', null, ['id' => 22]);
# output
# /product-view/22/?category=shoes
# /images/image/?category=shows
```
**Using custom names for methods on a controller/resource route**
```php
SimpleRouter::controller('gadgets', 'GadgetsController', ['names' => ['getIphoneInfo' => 'iphone']]);
url('gadgets.iphone');
# output
# /gadgets/iphoneinfo/
```
**Getting REST/resource controller urls**
```php
SimpleRouter::resource('/phones', 'PhonesController');
url('phones');
url('phones.index');
url('phones.create');
url('phones.edit');
// etc..
# output
# /phones/
# /phones/create/
# /phones/edit/
``` ```
## Custom CSRF verifier ## Custom CSRF verifier
@@ -297,10 +414,10 @@ Querystrings are ignored.
use Pecee\Http\Middleware\BaseCsrfVerifier; use Pecee\Http\Middleware\BaseCsrfVerifier;
class CsrfVerifier extends BaseCsrfVerifier { class CsrfVerifier extends BaseCsrfVerifier {
protected $except = [ protected $except = [
'/companies/*', '/companies/*',
'/api' '/api',
]; ];
} }
@@ -316,13 +433,13 @@ SimpleRouter::csrfVerifier(new \Demo\Middleware\CsrfVerifier());
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. 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```). To interfere with the router, we create a class that implements the ```IRouterBootManager``` interface. 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 ```php
use Pecee\Http\Request; use Pecee\Http\Request;
use Pecee\SimpleRouter\RouterBootManager; use Pecee\SimpleRouter\IRouterBootManager;
class CustomRouterRules extends RouterBootManager{ class CustomRouterRules implement IRouterBootManager {
public function boot(Request $request) { public function boot(Request $request) {
@@ -358,7 +475,7 @@ The last thing we need to do, is to add our custom boot-manager to the ```routes
## Easily overwrite route about to be loaded ## Easily overwrite route about to be loaded
Sometimes it can be useful to manipulate the route about to be loaded. Sometimes it can be useful to manipulate the route about to be loaded.
simple-php-router allows you to easily change the route about to be executed. 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\RouterBase``` instance's `loadedRoute` property. All information about the current route is stored in the ```\Pecee\SimpleRouter\Router``` instance's `loadedRoute` property.
For easy access you can use the shortcut method `\Pecee\SimpleRouter\SimpleRouter::router()`. For easy access you can use the shortcut method `\Pecee\SimpleRouter\SimpleRouter::router()`.
@@ -379,30 +496,28 @@ $route->setMethod('hello');
### Examples ### Examples
It's only possible to change the route BEFORE the route has initially been loaded. If you want to redirect to another route, we highly recommend that you It's only possible to change the route BEFORE the route has initially been loaded. If you want to redirect to another route, we highly recommend that you
modify the `RouterEntry` object from a `Middleware` or `ExceptionHandler`, like the examples below. modify the `IRoute` object from a `Middleware` or `ExceptionHandler`, like the examples below.
#### Faking new route #### Rewriting to new route
The example below will cause the router to re-route the request with another url. We are using the `url()` helper function to get the uri to another route added in the `routes.php` file. The example below will cause the router to re-route the request with another url. We are using the `url()` helper function to get the uri to another route added in the `routes.php` file.
This does require the `$request` object to be returned, otherwise the `request` object will be ignored by the router.
Using the example below will NOT inherit the rules from the other route. This means that IF you are faking a route that is enabled in `post`. **NOTE: Use this method if you want to fully load another route using it's settings (request method etc).**
**NOTE: Use this method if you want to fully load a route (middlewares, request-method etc. will be kept).**
```php ```php
namespace demo\Middlewares; namespace Demo\Middlewares;
use Pecee\Http\Middleware\IMiddleware; use Pecee\Http\Middleware\IMiddleware;
use Pecee\Http\Request; use Pecee\Http\Request;
use Pecee\SimpleRouter\RouterEntry; use Pecee\SimpleRouter\Route\ILoadableRoute;
class CustomMiddleware implements Middleware { class CustomMiddleware implements Middleware {
public function handle(Request $request, RouterEntry &$route = null) { public function handle(Request $request, ILoadableRoute &$route) {
return $request->setUri(url('home'));
$request->setUri(url('home'));
} }
} }
@@ -419,16 +534,18 @@ If you wish to change the callback from outside, please have this in mind.
**NOTE: Use this method if you want to load another controller. No additional middlewares or rules will be loaded.** **NOTE: Use this method if you want to load another controller. No additional middlewares or rules will be loaded.**
```php ```php
namespace demo\Middlewares; namespace Demo\Middlewares;
use Pecee\Http\Middleware\IMiddleware; use Pecee\Http\Middleware\IMiddleware;
use Pecee\Http\Request; use Pecee\Http\Request;
use Pecee\SimpleRouter\RouterEntry; use Pecee\SimpleRouter\Route\ILoadableRoute;
class CustomMiddleware implements Middleware { class CustomMiddleware implements Middleware {
public function handle(Request $request, RouterEntry &$route = null) { public function handle(Request $request, ILoadableRoute &$route) {
$route->callback('DefaultController@home'); $route->callback('DefaultController@home');
} }
} }
@@ -439,23 +556,33 @@ class CustomMiddleware implements Middleware {
We've added the `Input` class to easy access parameters from your Controller-classes. We've added the `Input` class to easy access parameters from your Controller-classes.
**Return single parameter value (matches both GET, POST, FILE):** **Return single parameter value (matches both GET, POST, FILE):**
```php ```php
$value = input()->get('name'); $value = input()->get('name');
``` ```
**Return parameter object (matches both GET, POST, FILE):** **Return parameter object (matches both GET, POST, FILE):**
```php ```php
$object = input()->getObject('name'); $object = input()->getObject('name');
``` ```
**Return specific GET parameter (where name is the name of your parameter):** **Return specific GET parameter (where name is the name of your parameter):**
```php ```php
$object = input()->get->name; $object = input()->get->name;
$object = input()->post->name; $object = input()->post->name;
$object = input()->file->name; $object = input()->file->name;
# -- or --
$object = input()->get->get($key, $defaultValue);
$object = input()->post->get($key, $defaultValue);
$object = input()->file->get($key, $defaultValue);
``` ```
**Return all parameters:** **Return all parameters:**
```php ```php
// Get all // Get all
$values = input()->all(); $values = input()->all();
@@ -468,6 +595,7 @@ $values = input()->all([
``` ```
All object inherits from `InputItem` class and will always contain these methods: All object inherits from `InputItem` class and will always contain these methods:
- `getValue()` - returns the value of the input. - `getValue()` - returns the value of the input.
- `getIndex()` - returns the index/key 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). - `getName()` - returns a human friendly name for the input (company_name will be Company Name etc).
@@ -480,9 +608,6 @@ All object inherits from `InputItem` class and will always contain these methods
- `getType()` - get mime-type for file. - `getType()` - get mime-type for file.
- `getError()` - get file upload error. - `getError()` - get file upload error.
### Easy access to methods
Below example requires you to have the helper functions added. Please refer to the helper functions section in the documentation. Below example requires you to have the helper functions added. Please refer to the helper functions section in the documentation.
```php ```php
+10 -14
View File
@@ -1,20 +1,16 @@
<?php <?php
namespace Demo\Controllers; namespace Demo\Controllers;
use Pecee\SimpleRouter\SimpleRouter; class ApiController
{
public function index()
{
// The variable authenticated is set to true in the ApiVerification middleware class.
header('content-type: application/json');
class ApiController { echo json_encode([
'authenticated' => request()->authenticated
public function index() { ]);
}
// The variable authenticated is set to true in the ApiVerification middleware class.
header('content-type: application/json');
echo json_encode([
'authenticated' => request()->authenticated
]);
}
} }
@@ -1,29 +1,27 @@
<?php <?php
namespace Demo\Controllers; namespace Demo\Controllers;
class DefaultController { class DefaultController
{
public function index()
{
// implement
echo sprintf('DefaultController -> index (?fun=%s)', input()->get('fun'));
}
public function index() { public function contact()
{
echo 'DefaultController -> contact';
}
// implement public function companies($id = null)
echo sprintf('DefaultController -> index (?fun=%s)', input()->get('fun')); {
echo 'DefaultController -> companies -> id: ' . $id;
}
} public function notFound()
{
public function contact() { echo 'Page not found';
}
echo 'DefaultController -> contact';
}
public function companies($id = null) {
echo 'DefaultController -> companies -> id: ' . $id;
}
public function notFound() {
echo 'Page not found';
}
} }
@@ -1,34 +1,44 @@
<?php <?php
namespace Demo\Handlers; namespace Demo\Handlers;
use Pecee\Handler\IExceptionHandler; use Pecee\Handlers\IExceptionHandler;
use Pecee\Http\Request; use Pecee\Http\Request;
use Pecee\SimpleRouter\RouterEntry; use Pecee\SimpleRouter\Exceptions\NotFoundHttpException;
use Pecee\SimpleRouter\Route\ILoadableRoute;
class CustomExceptionHandler implements IExceptionHandler { class CustomExceptionHandler implements IExceptionHandler
{
public function handleError(Request $request, ILoadableRoute &$route = null, \Exception $error)
{
public function handleError( Request $request, RouterEntry &$route = null, \Exception $error) { /* You can use the exception handler to format errors depending on the request and type. */
// Return json errors if we encounter an error on /api. if (stripos($request->getUri(), '/api') !== false) {
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 response()->json([
if($error->getCode() == 404) { 'error' => $error->getMessage(),
'code' => $error->getCode(),
]);
// Return 404 path }
$request->setUri('/404');
return $request;
} /* The router will throw the NotFoundHttpException on 404 */
if($error instanceof NotFoundHttpException) {
throw $error; /*
} * Render your own custom 404-view, rewrite the request to another route,
* or simply return the $request object to ignore the error and continue on rendering the route.
*
* The code below will make the router render our page.notfound route.
*/
$request->setUri(url('page.notfound'));
return $request;
}
throw $error;
}
} }
@@ -3,17 +3,16 @@ namespace Demo\Middlewares;
use Pecee\Http\Middleware\IMiddleware; use Pecee\Http\Middleware\IMiddleware;
use Pecee\Http\Request; use Pecee\Http\Request;
use Pecee\SimpleRouter\RouterEntry; use Pecee\SimpleRouter\Route\ILoadableRoute;
class ApiVerification implements IMiddleware { class ApiVerification implements IMiddleware
{
public function handle(Request $request, ILoadableRoute &$route)
{
// Do authentication
$request->authenticated = true;
public function handle(Request $request, RouterEntry &$route) { return $request;
}
// Do authentication
$request->authenticated = true;
return $request;
}
} }
@@ -3,11 +3,11 @@ namespace Demo\Middlewares;
use Pecee\Http\Middleware\BaseCsrfVerifier; use Pecee\Http\Middleware\BaseCsrfVerifier;
class CsrfVerifier extends BaseCsrfVerifier { class CsrfVerifier extends BaseCsrfVerifier
{
/** /**
* CSRF validation will be ignored on the following urls. * CSRF validation will be ignored on the following urls.
*/ */
protected $except = ['/api/*']; protected $except = ['/api/*'];
} }
+12 -15
View File
@@ -1,29 +1,26 @@
<?php <?php
/** /**
* Custom router which handles default middlewares, default exceptions and things * Custom router which handles default middlewares, default exceptions and things
* that should be happen before and after the router is initialised. * that should be happen before and after the router is initialised.
*/ */
namespace Demo; namespace Demo;
use Pecee\SimpleRouter\SimpleRouter; use Pecee\SimpleRouter\SimpleRouter;
class Router extends SimpleRouter { class Router extends SimpleRouter
{
public static function start($defaultNamespace = null)
{
// Load our helpers
require_once 'helpers.php';
public static function start($defaultNamespace = null) { // Load our custom routes
require_once 'routes.php';
// Load our helpers parent::setDefaultNamespace('\Demo');
require_once 'helpers.php';
// Load our custom routes // Do initial stuff
require_once 'routes.php'; parent::start();
}
parent::setDefaultNamespace('\Demo\Controllers');
// Do initial stuff
parent::start();
}
} }
+16 -11
View File
@@ -1,39 +1,44 @@
<?php <?php
use Pecee\SimpleRouter\SimpleRouter; use Pecee\SimpleRouter\SimpleRouter;
function url($controller, $parameters = null, $getParams = null) { function url($controller, $parameters = null, $getParams = null)
SimpleRouter::getRoute($controller, $parameters, $getParams); {
SimpleRouter::getUrl($controller, $parameters, $getParams);
} }
/** /**
* Get current csrf-token * Get current csrf-token
* @return null|string * @return null|string
*/ */
function csrf_token() { function csrf_token()
$token = new \Pecee\CsrfToken(); {
return $token->getToken(); $token = new \Pecee\CsrfToken();
return $token->getToken();
} }
/** /**
* Get request object * Get request object
* @return \Pecee\Http\Request * @return \Pecee\Http\Request
*/ */
function request() { function request()
return SimpleRouter::request(); {
return SimpleRouter::request();
} }
/** /**
* Get response object * Get response object
* @return \Pecee\Http\Response * @return \Pecee\Http\Response
*/ */
function response() { function response()
return SimpleRouter::response(); {
return SimpleRouter::response();
} }
/** /**
* Get input class * Get input class
* @return \Pecee\Http\Input\Input * @return \Pecee\Http\Input\Input
*/ */
function input() { function input()
return SimpleRouter::request()->getInput(); {
return SimpleRouter::request()->getInput();
} }
+12 -10
View File
@@ -7,17 +7,19 @@ use Demo\Router;
Router::csrfVerifier(new \Demo\Middlewares\CsrfVerifier()); Router::csrfVerifier(new \Demo\Middlewares\CsrfVerifier());
Router::group(['exceptionHandler' => 'Demo\Handlers\CustomExceptionHandler'], function() { Router::group(['exceptionHandler' => 'Demo\Handlers\CustomExceptionHandler'], function () {
Router::get('/', 'DefaultController@index')->setAlias('home'); Router::get('/', 'DefaultController@index')->setName('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::get('/contact', 'DefaultController@contact')->setName('contact');
Router::group(['prefix' => '/api', 'middleware' => 'Demo\Middlewares\ApiVerification'], function() {
Router::resource('/demo', 'ApiController'); Router::get('/404', 'DefaultController@notFound')->setName('404');
});
Router::basic('/companies/{id?}', 'DefaultController@companies')->setName('companies');
// Api
Router::group(['prefix' => '/api', 'middleware' => 'Demo\Middlewares\ApiVerification'], function () {
Router::resource('/demo', 'ApiController');
});
}); });
+46
View File
@@ -0,0 +1,46 @@
<?php
namespace Pecee\Controllers;
interface IRestController {
/**
* @return void
*/
function index();
/**
* @param mixed $id
* @return void
*/
function show($id);
/**
* @return void
*/
function store();
/**
* @return void
*/
function create();
/**
* View
* @param mixed $id
* @return void
*/
function edit($id);
/**
* @param mixed $id
* @return void
*/
function update($id);
/**
* @param mixed $id
* @return void
*/
function destroy($id);
}
+57 -51
View File
@@ -1,62 +1,68 @@
<?php <?php
namespace Pecee; namespace Pecee;
class CsrfToken { class CsrfToken
{
const CSRF_KEY = 'XSRF-TOKEN';
const CSRF_KEY = 'XSRF-TOKEN'; protected $token;
protected $token; /**
* Generate random identifier for CSRF token
*
* @return string
*/
public static function generateToken()
{
if (function_exists('mcrypt_create_iv')) {
return bin2hex(mcrypt_create_iv(32, MCRYPT_DEV_URANDOM));
}
return bin2hex(openssl_random_pseudo_bytes(32));
}
/** /**
* Generate random identifier for CSRF token * Validate valid CSRF token
* @return string *
*/ * @param string $token
public static function generateToken() { * @return bool
if (function_exists('mcrypt_create_iv')) { */
return bin2hex(mcrypt_create_iv(32, MCRYPT_DEV_URANDOM)); public function validate($token)
} {
return bin2hex(openssl_random_pseudo_bytes(32)); if ($token !== null && $this->getToken() !== null) {
} return hash_equals($token, $this->getToken());
}
return false;
}
/** /**
* Validate valid CSRF token * Set csrf token cookie
* *
* @param string $token * @param $token
* @return bool */
*/ public function setToken($token)
public function validate($token) { {
if($token !== null && $this->getToken() !== null) { setcookie(static::CSRF_KEY, $token, time() + 60 * 120, '/');
return hash_equals($token, $this->getToken()); }
}
return false;
}
/** /**
* Set csrf token cookie * Get csrf token
* * @return string|null
* @param $token */
*/ public function getToken()
public function setToken($token) { {
setcookie(static::CSRF_KEY, $token, time() + 60 * 120, '/'); if ($this->hasToken()) {
} return $_COOKIE[static::CSRF_KEY];
}
return null;
}
/** /**
* Get csrf token * Returns whether the csrf token has been defined
* @return string|null * @return bool
*/ */
public function getToken(){ public function hasToken()
if($this->hasToken()) { {
return $_COOKIE[static::CSRF_KEY]; return isset($_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]);
}
} }
-4
View File
@@ -1,4 +0,0 @@
<?php
namespace Pecee\Exception;
class RouterException extends \Exception { }
@@ -1,4 +0,0 @@
<?php
namespace Pecee\Exception;
class TokenMismatchException extends \Exception {}
-17
View File
@@ -1,17 +0,0 @@
<?php
namespace Pecee\Handler;
use Pecee\Http\Request;
use Pecee\SimpleRouter\RouterEntry;
interface IExceptionHandler {
/**
* @param Request $request
* @param RouterEntry|null $route
* @param \Exception $error
* @return Request|null
*/
public function handleError(Request $request, RouterEntry &$route = null, \Exception $error);
}
+17
View File
@@ -0,0 +1,17 @@
<?php
namespace Pecee\Handlers;
use Pecee\Http\Request;
use Pecee\SimpleRouter\Route\ILoadableRoute;
interface IExceptionHandler
{
/**
* @param Request $request
* @param ILoadableRoute $route
* @param \Exception $error
* @return Request|null
*/
public function handleError(Request $request, ILoadableRoute &$route = null, \Exception $error);
}
+160 -162
View File
@@ -3,211 +3,209 @@ namespace Pecee\Http\Input;
use Pecee\Http\Request; use Pecee\Http\Request;
class Input { class Input
{
/**
* @var \Pecee\Http\Input\InputCollection
*/
public $get;
/** /**
* @var \Pecee\Http\Input\InputCollection * @var \Pecee\Http\Input\InputCollection
*/ */
public $get; public $post;
/** /**
* @var \Pecee\Http\Input\InputCollection * @var \Pecee\Http\Input\InputCollection
*/ */
public $post; public $file;
/** /**
* @var \Pecee\Http\Input\InputCollection * @var Request
*/ */
public $file; protected $request;
/** public function __construct(Request $request)
* @var Request {
*/ $this->request = $request;
protected $request; $this->setGet();
$this->setPost();
$this->setFile();
}
public function __construct(Request $request) { /**
$this->request = $request; * Get all get/post items
$this->setGet(); * @param array|null $filter Only take items in filter
$this->setPost(); * @return array
$this->setFile(); */
} public function all(array $filter = null)
{
$output = $_POST;
/** if ($this->request->getMethod() === 'post') {
* Get all get/post items
* @param array|null $filter Only take items in filter
* @return array
*/
public function all(array $filter = null) {
$output = $_POST; $contents = file_get_contents('php://input');
if($this->request->getMethod() === 'post') { if (stripos(trim($contents), '{') === 0) {
$output = json_decode($contents, true);
if ($output === false) {
$output = array();
}
}
}
$contents = file_get_contents('php://input'); $output = array_merge($_GET, $output);
if (stripos(trim($contents), '{') === 0) { if ($filter !== null) {
$output = json_decode($contents, true); $output = array_filter($output, function ($key) use ($filter) {
if($output === false) { if (in_array($key, $filter)) {
$output = array(); return true;
} }
}
}
$output = array_merge($_GET, $output); return false;
}, ARRAY_FILTER_USE_KEY);
}
if($filter !== null) { return $output;
$output = array_filter($output, function ($key) use ($filter) { }
if (in_array($key, $filter)) {
return true;
}
return false; public function getObject($index, $default = null)
}, ARRAY_FILTER_USE_KEY); {
} $key = (strpos($index, '[') > -1) ? substr($index, strpos($index, '[') + 1, strpos($index, ']') - strlen($index)) : null;
$index = (strpos($index, '[') > -1) ? substr($index, 0, strpos($index, '[')) : $index;
return $output; $element = $this->get->findFirst($index);
}
public function getObject($index, $default = null) { if ($element !== null) {
$key = (strpos($index, '[') > -1) ? substr($index, strpos($index, '[')+1, strpos($index, ']') - strlen($index)) : null; return ($key !== null) ? $element[$key] : $element;
$index = (strpos($index, '[') > -1) ? substr($index, 0, strpos($index, '[')) : $index; }
$element = $this->get->findFirst($index); if ($this->request->getMethod() !== 'get') {
if($element !== null) { $element = $this->post->findFirst($index);
return ($key !== null) ? $element[$key] : $element; if ($element !== null) {
} return ($key !== null) ? $element[$key] : $element;
}
if($this->request->getMethod() !== 'get') { $element = $this->file->findFirst($index);
if ($element !== null) {
return ($key !== null) ? $element[$key] : $element;
}
}
$element = $this->post->findFirst($index); return $default;
}
if ($element !== null) { /**
return ($key !== null) ? $element[$key] : $element; * 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);
$element = $this->file->findFirst($index); if ($item !== null) {
if ($element !== null) {
return ($key !== null) ? $element[$key] : $element;
}
}
return $default; if ($item instanceof InputCollection || $item instanceof InputFile) {
} return $item;
}
/** return (trim($item->getValue()) === '') ? $default : $item->getValue();
* 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); return $default;
}
if($item !== null) { public function exists($index)
{
return ($this->getObject($index) !== null);
}
if($item instanceof InputCollection || $item instanceof InputFile) { public function setGet()
return $item; {
} $this->get = new InputCollection();
return (trim($item->getValue()) === '') ? $default : $item->getValue(); if (count($_GET) > 0) {
} foreach ($_GET as $key => $get) {
if (is_array($get) === false) {
$this->get->{$key} = new InputItem($key, $get);
continue;
}
return $default; $output = new InputCollection();
}
public function exists($index) { foreach ($get as $k => $g) {
return ($this->getObject($index) !== null); $output->{$k} = new InputItem($k, $g);
} }
public function setGet() { $this->get->{$key} = $output;
$this->get = new InputCollection(); }
}
}
if(count($_GET)) { public function setPost()
foreach($_GET as $key => $get) { {
if(!is_array($get)) { $this->post = new InputCollection();
$this->get->{$key} = new InputItem($key, $get);
continue;
}
$output = new InputCollection(); $postVars = $_POST;
foreach($get as $k => $g) { if (in_array($this->request->getMethod(), ['put', 'patch', 'delete']) === true) {
$output->{$k} = new InputItem($k, $g); parse_str(file_get_contents('php://input'), $postVars);
} }
$this->get->{$key} = $output; if (count($postVars) > 0) {
}
}
}
public function setPost() { foreach ($postVars as $key => $post) {
$this->post = new InputCollection(); if (is_array($post) === false) {
$this->post->{strtolower($key)} = new InputItem($key, $post);
continue;
}
$postVars = $_POST; $output = new InputCollection();
if(in_array($this->request->getMethod(), ['put', 'patch', 'delete'])) { foreach ($post as $k => $p) {
parse_str(file_get_contents('php://input'), $postVars); $output->{$k} = new InputItem($k, $p);
} }
if(count($postVars)) { $this->post->{strtolower($key)} = $output;
}
}
}
foreach($postVars as $key => $post) { public function setFile()
if(!is_array($post)) { {
$this->post->{strtolower($key)} = new InputItem($key, $post); $this->file = new InputCollection();
continue;
}
$output = new InputCollection(); if (count($_FILES) > 0) {
foreach ($_FILES as $key => $values) {
foreach($post as $k => $p) { // Handle array input
$output->{$k} = new InputItem($k, $p); if (is_array($values['name']) === false && trim($values['error']) !== '4') {
} $values['index'] = $key;
$this->file->{strtolower($key)} = InputFile::createFromArray($values);
continue;
}
$this->post->{strtolower($key)} = $output; $output = new InputCollection();
}
}
}
public function setFile() { foreach ($values['name'] as $k => $val) {
$this->file = new InputCollection(); if (trim($val['error'][$k]) !== '4') {
$output->{$k} = InputFile::createFromArray([
'index' => $k,
'error' => $val['error'][$k],
'tmp_name' => $val['tmp_name'][$k],
'type' => $val['type'][$k],
'size' => $val['size'][$k],
'name' => $val['name'][$k]
]);
}
}
if(count($_FILES)) { $this->file->{strtolower($key)} = $output;
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;
}
}
}
} }
+75 -66
View File
@@ -1,85 +1,94 @@
<?php <?php
namespace Pecee\Http\Input; namespace Pecee\Http\Input;
class InputCollection implements \IteratorAggregate { class InputCollection implements \IteratorAggregate
{
protected $data = array();
protected $data = array(); /**
* Search for input element matching index.
*
* @param string $index
* @param string|null $default
* @return InputItem|mixed
*/
public function findFirst($index, $default = null)
{
if (count($this->data) > 0) {
/** if (isset($this->data[$index])) {
* Search for input element matching index. return $this->data[$index];
* Useful for searching for finding items where $index doesn't contain form name. }
*
* @param string $index
* @param string|null $defaultValue
* @return mixed
*/
public function findFirst($index, $defaultValue = null) {
if(count($this->data)) {
if(isset($this->data[$index])) { foreach ($this->data as $key => $input) {
return $this->data[$index]; if (strtolower($index) === strtolower($key)) {
} return $input;
}
}
}
foreach($this->data as $key => $value) { return $default;
if(strtolower($index) === strtolower($key)) { }
return $value;
}
}
}
return $defaultValue; /**
} * Get input element value matching index
*
* @param string $index
* @param string|null $default
* @return string|null
*/
public function get($index, $default = null)
{
$input = $this->findFirst($index);
public function getValue($index, $defaultValue = null) { if($input !== null && trim($input->getValue()) !== '') {
if(count($this->data)) { return $input->getValue();
}
if(isset($this->data[$index])) { return $default;
return $this->data[$index]->getValue(); }
}
foreach($this->data as $key => $value) { public function getValue($index, $default = null) {
if(strtolower($index) === strtolower($key)) {
return $value->getValue();
}
}
}
return $defaultValue; }
}
/** /**
* @param $index * @param string $index
* @throws \InvalidArgumentException * @throws \InvalidArgumentException
* @return InputItem * @return InputItem
*/ */
public function __get($index) { public function __get($index)
$item = $this->findFirst($index); {
// Ensure that item are always available $item = $this->findFirst($index);
if($item === null) { // Ensure that item are always available
$this->data[$index] = new InputItem($index, null); if ($item === null) {
return $this->data[$index]; $this->data[$index] = new InputItem($index, null);
} return $this->data[$index];
}
return $item; return $item;
} }
public function __set($index, $value) { public function __set($index, $value)
$this->data[$index] = $value; {
} $this->data[$index] = $value;
}
public function getData() { public function getData()
return $this->data; {
} return $this->data;
}
/** /**
* Retrieve an external iterator * Retrieve an external iterator
* @link http://php.net/manual/en/iteratoraggregate.getiterator.php * @link http://php.net/manual/en/iteratoraggregate.getiterator.php
* @return \Traversable An instance of an object implementing <b>Iterator</b> or * @return \Traversable An instance of an object implementing <b>Iterator</b> or
* <b>Traversable</b> * <b>Traversable</b>
* @since 5.0.0 * @since 5.0.0
*/ */
public function getIterator() { public function getIterator()
return new \ArrayIterator($this->data); {
} return new \ArrayIterator($this->data);
}
} }
+95 -27
View File
@@ -1,68 +1,136 @@
<?php <?php
namespace Pecee\Http\Input; namespace Pecee\Http\Input;
class InputFile extends InputItem { class InputFile extends InputItem
{
protected $name; public $size;
protected $size; public $type;
protected $type; public $error;
protected $error; public $tmpName;
protected $tmpName;
/** /**
* @return string * @return string
*/ */
public function getSize() { public function getSize()
{
return $this->size; return $this->size;
} }
/** /**
* @return string * @return string
*/ */
public function getType() { public function getType()
{
return $this->type; return $this->type;
} }
/** /**
* @return string * @return string
*/ */
public function getError() { public function getError()
{
return $this->error; return $this->error;
} }
public function getMime()
{
return $this->getType();
}
/** /**
* @return string * @return string
*/ */
public function getTmpName() { public function getTmpName()
{
return $this->tmpName; return $this->tmpName;
} }
public function getExtension() { public function getExtension()
{
return pathinfo($this->getName(), PATHINFO_EXTENSION); return pathinfo($this->getName(), PATHINFO_EXTENSION);
} }
public function move($destination) { public function move($destination)
{
return move_uploaded_file($this->tmpName, $destination); return move_uploaded_file($this->tmpName, $destination);
} }
public function getContents() { public function getContents()
{
return file_get_contents($this->tmpName); return file_get_contents($this->tmpName);
} }
public function setTmpName($name) { /**
$this->tmpName = $name; * Set file temp. name
} * @param string $name
* @return static $this
*/
public function setTmpName($name)
{
$this->tmpName = $name;
return $this;
}
public function setSize($size) { /**
$this->size = $size; * Set file size
} * @param int $size
* @return static $this
*/
public function setSize($size)
{
$this->size = $size;
return $this;
}
public function setType($type) { /**
$this->type = $type; * Set type
} * @param string $type
* @return static $this
*/
public function setType($type)
{
$this->type = $type;
return $this;
}
public function setError($error) { /**
$this->error = $error; * Set error
} * @param int $error
* @return static $this
*/
public function setError($error)
{
$this->error = $error;
return $this;
}
/**
* Create from array
* @param array $values
* @return static
*/
public static function createFromArray(array $values)
{
if(!isset($values['index'])) {
throw new \InvalidArgumentException('Index key is required');
}
$input = new static($values['index']);
$input->setTmpName((isset($values['error']) ? $values['error'] : null));
$input->setName((isset($values['name']) ? $values['name'] : null));
$input->setSize((isset($values['size']) ? $values['size'] : null));
$input->setType((isset($values['type']) ? $values['type'] : null));
$input->setError((isset($values['tmp_name']) ? $values['tmp_name'] : null));
return $input;
}
/**
* @return string
*/
public function getValue()
{
return $this->tmpName;
}
} }
+57 -50
View File
@@ -1,63 +1,70 @@
<?php <?php
namespace Pecee\Http\Input; namespace Pecee\Http\Input;
class InputItem { class InputItem
{
public $index;
public $name;
public $value;
protected $index; public function __construct($index, $value = null)
protected $name; {
protected $value; $this->index = $index;
$this->value = $value;
public function __construct($index, $value = null) { // Make the name human friendly, by replace _ with space
$this->index = $index; $this->name = ucfirst(str_replace('_', ' ', $this->index));
$this->value = $value; }
// Make the name human friendly, by replace _ with space /**
$this->name = ucfirst(str_replace('_', ' ', $this->index)); * @return string
} */
public function getName()
{
return $this->name;
}
/** /**
* @return array * @return string
*/ */
public function getName() { public function getValue()
return $this->name; {
} return $this->value;
}
/** /**
* @return array * @return string
*/ */
public function getValue() { public function getIndex()
return $this->value; {
} return $this->index;
}
/** /**
* @return string * Set input name
*/ * @param string $name
public function getIndex() { * @return static $this
return $this->index; */
} public function setName($name)
{
$this->name = $name;
return $this;
}
/** /**
* Set input name * Set input value
* @param string $name * @param string $value
* @return static $this * @return static $this
*/ */
public function setName($name) { public function setValue($value)
$this->name = $name; {
return $this; $this->value = $value;
} return $this;
}
/** public function __toString()
* Set input value {
* @param string $value return (string)$this->value;
* @return static $this }
*/
public function setValue($value) {
$this->value = $value;
return $this;
}
public function __toString() {
return (string)$this->getValue();
}
} }
+69 -64
View File
@@ -2,89 +2,94 @@
namespace Pecee\Http\Middleware; namespace Pecee\Http\Middleware;
use Pecee\CsrfToken; use Pecee\CsrfToken;
use Pecee\Exception\TokenMismatchException; use Pecee\Exceptions\TokenMismatchException;
use Pecee\Http\Request; use Pecee\Http\Request;
use Pecee\SimpleRouter\RouterEntry; use Pecee\SimpleRouter\Route\ILoadableRoute;
class BaseCsrfVerifier implements IMiddleware { class BaseCsrfVerifier implements IMiddleware
{
const POST_KEY = 'csrf-token';
const HEADER_KEY = 'X-CSRF-TOKEN';
const POST_KEY = 'csrf-token'; protected $except;
const HEADER_KEY = 'X-CSRF-TOKEN'; protected $csrfToken;
protected $token;
protected $except; public function __construct()
protected $csrfToken; {
protected $token; $this->csrfToken = new CsrfToken();
public function __construct() { // Generate or get the CSRF-Token from Cookie.
$this->csrfToken = new CsrfToken(); $this->token = (!$this->hasToken()) ? $this->generateToken() : $this->csrfToken->getToken();
}
// Generate or get the CSRF-Token from Cookie. /**
$this->token = (!$this->hasToken()) ? $this->generateToken() : $this->csrfToken->getToken(); * Check if the url matches the urls in the except property
} * @param Request $request
* @return bool
*/
protected function skip(Request $request)
{
if ($this->except === null || is_array($this->except) === false) {
return false;
}
/** foreach ($this->except as $url) {
* Check if the url matches the urls in the except property $url = rtrim($url, '/');
* @param Request $request if ($url[strlen($url) - 1] === '*') {
* @return bool $url = rtrim($url, '*');
*/ $skip = (stripos($request->getUri(), $url) === 0);
protected function skip(Request $request) { } else {
$skip = ($url === rtrim($request->getUri(), '/'));
}
if($this->except === null || !is_array($this->except)) { if ($skip) {
return false; return true;
} }
}
foreach($this->except as $url) { return false;
$url = rtrim($url, '/'); }
if($url[strlen($url)-1] === '*') {
$url = rtrim($url, '*');
$skip = (stripos($request->getUri(), $url) === 0);
} else {
$skip = ($url === rtrim($request->getUri(), '/'));
}
if($skip) { public function handle(Request $request, ILoadableRoute &$route = null)
return true; {
}
}
return false; if ($request->getMethod() !== 'get' && !$this->skip($request)) {
}
public function handle(Request $request, RouterEntry &$route = null) { $token = $request->getInput()->post->get(static::POST_KEY);
if($request->getMethod() !== 'get' && !$this->skip($request)) { // If the token is not posted, check headers for valid x-csrf-token
if ($token === null) {
$token = $request->getHeader(static::HEADER_KEY);
}
$token = $request->getInput()->post->getValue(static::POST_KEY); if (!$this->csrfToken->validate($token)) {
throw new TokenMismatchException('Invalid csrf-token.');
}
// If the token is not posted, check headers for valid x-csrf-token }
if($token === null) {
$token = $request->getHeader(static::HEADER_KEY);
}
if( !$this->csrfToken->validate($token) ) { }
throw new TokenMismatchException('Invalid csrf-token.');
}
} public function generateToken()
{
$token = $this->csrfToken->generateToken();
$this->csrfToken->setToken($token);
return $token;
}
} public function hasToken()
{
if ($this->token != null) {
return true;
}
public function generateToken() { return $this->csrfToken->hasToken();
$token = $this->csrfToken->generateToken(); }
$this->csrfToken->setToken($token);
return $token;
}
public function hasToken() { public function getToken()
if($this->token != null) { {
return true; return $this->token;
} }
return $this->csrfToken->hasToken();
}
public function getToken() {
return $this->token;
}
} }
@@ -0,0 +1,7 @@
<?php
namespace Pecee\Exceptions;
class TokenMismatchException extends \Exception
{
}
+9 -9
View File
@@ -2,15 +2,15 @@
namespace Pecee\Http\Middleware; namespace Pecee\Http\Middleware;
use Pecee\Http\Request; use Pecee\Http\Request;
use Pecee\SimpleRouter\RouterEntry; use Pecee\SimpleRouter\Route\ILoadableRoute;
interface IMiddleware { interface IMiddleware
{
/** /**
* @param Request $request * @param Request $request
* @param RouterEntry $route * @param ILoadableRoute $route
* @return Request|null * @return Request|null
*/ */
public function handle(Request $request, RouterEntry &$route); public function handle(Request $request, ILoadableRoute &$route);
} }
+174 -154
View File
@@ -3,184 +3,204 @@ namespace Pecee\Http;
use Pecee\Http\Input\Input; use Pecee\Http\Input\Input;
class Request { class Request
{
protected $data = array();
protected $headers;
protected $host;
protected $uri;
protected $method;
protected $input;
protected $data = array(); public function __construct()
protected $headers; {
protected $host; $this->parseHeaders();
protected $uri; $this->input = new Input($this);
protected $method; $this->host = $this->getHeader('http-host');;
protected $input; $this->uri = $this->getHeader('request-uri');
$this->method = strtolower($this->input->post->findFirst('_method', $this->getHeader('request-method')));
}
protected function parseHeaders()
{
$this->headers = array();
public function __construct() { foreach ($_SERVER as $name => $value) {
$this->parseHeaders(); $this->headers[strtolower($name)] = $value;
$this->input = new Input($this); $this->headers[strtolower(str_replace('_', '-', $name))] = $value;
}
}
$this->host = $this->getHeader('http_host');; public function isSecure()
$this->uri = $this->getHeader('request_uri'); {
$this->method = strtolower($this->input->post->findFirst('_method', $this->getHeader('request_method'))); if ($this->getHeader('http-x-forwarded-proto') === 'https' || $this->getHeader('https') !== null || $this->getHeader('server-port') === 443) {
} return true;
}
protected function parseHeaders() { return false;
$this->headers = array(); }
foreach ($_SERVER as $name => $value) { /**
$this->headers[strtolower($name)] = $value; * @return string
} */
} public function getUri()
{
return $this->uri;
}
public function isSecure() { /**
if($this->getHeader('http_x_forwarded_proto') === 'https') { * @return string
return true; */
} public function getHost()
{
return $this->host;
}
if($this->getHeader('https') !== null) { /**
return true; * @return string
} */
public function getMethod()
{
return $this->method;
}
return ($this->getHeader('server_port') === 443); /**
} * Get http basic auth user
* @return string|null
*/
public function getUser()
{
return $this->getHeader('php-auth-user');
}
/** /**
* @return string * Get http basic auth password
*/ * @return string|null
public function getUri() { */
return $this->uri; public function getPassword()
} {
return $this->getHeader('php-auth-pw');
}
/** /**
* @return string * Get all headers
*/ * @return array
public function getHost() { */
return $this->host; public function getHeaders()
} {
return $this->headers;
}
/** /**
* @return string * Get id address
*/ * @return string
public function getMethod() { */
return $this->method; public function getIp()
} {
if ($this->getHeader('http-cf-connecting-ip') !== null) {
return $this->getHeader('http-cf-connecting-ip');
}
/** if ($this->getHeader('http-x-forwarded-for') !== null && strlen($this->getHeader('http-x-forwarded-for'))) {
* Get http basic auth user return $this->getHeader('http-x-forwarded_for');
* @return string|null }
*/
public function getUser() {
return $this->getHeader('php_auth_user');
}
/** return $this->getHeader('remote-addr');
* Get http basic auth password }
* @return string|null
*/
public function getPassword() {
return $this->getHeader('php_auth_pw');
}
/** /**
* Get all headers * Get referer
* @return array * @return string
*/ */
public function getHeaders() { public function getReferer()
return $this->headers; {
} return $this->getHeader('http-referer');
}
/** /**
* Get id address * Get user agent
* @return string * @return string
*/ */
public function getIp() { public function getUserAgent()
if($this->getHeader('http_cf_connecting_ip') !== null) { {
return $this->getHeader('http_cf_connecting_ip'); return $this->getHeader('http-user-agent');
} }
if($this->getHeader('http_x_forwarded_for') !== null && strlen($this->getHeader('http_x_forwarded_for'))) { /**
return $this->getHeader('http_x_forwarded_for'); * Get header value by name
} *
* @param string $name
* @param object|null $defaultValue
*
* @return string|null
*/
public function getHeader($name, $defaultValue = null)
{
return isset($this->headers[strtolower($name)]) ? $this->headers[strtolower($name)] : $defaultValue;
}
return $this->getHeader('remote_addr'); /**
} * Get input class
* @return Input
*/
public function getInput()
{
return $this->input;
}
/** /**
* Get referer * Is format accepted
* @return string *
*/ * @param string $format
public function getReferer() { *
return $this->getHeader('http_referer'); * @return bool
} */
public function isFormatAccepted($format)
{
return ($this->getHeader('http-accept') !== null && stripos($this->getHeader('http-accept'), $format) > -1);
}
/** /**
* Get user agent * Get accept formats
* @return string * @return array
*/ */
public function getUserAgent() { public function getAcceptFormats()
return $this->getHeader('http_user_agent'); {
} return explode(',', $this->getHeader('http-accept'));
}
/** /**
* Get header value by name * @param string $uri
* @param string $name */
* @param object|null $defaultValue public function setUri($uri)
* @return string|null {
*/ $this->uri = $uri;
public function getHeader($name, $defaultValue = null) { }
return isset($this->headers[strtolower($name)]) ? $this->headers[strtolower($name)] : $defaultValue;
}
/** /**
* Get input class * @param string $host
* @return Input */
*/ public function setHost($host)
public function getInput() { {
return $this->input; $this->host = $host;
} }
/** /**
* Is format accepted * @param string $method
* @param string $format */
* @return bool public function setMethod($method)
*/ {
public function isFormatAccepted($format) { $this->method = $method;
return ($this->getHeader('http_accept') !== null && stripos($this->getHeader('http_accept'), $format) > -1); }
}
/** public function __set($name, $value = null)
* Get accept formats {
* @return array $this->data[$name] = $value;
*/ }
public function getAcceptFormats() {
return explode(',', $this->getHeader('http_accept'));
}
/** public function __get($name)
* @param string $uri {
*/ return isset($this->data[$name]) ? $this->data[$name] : null;
public function setUri($uri) { }
$this->uri = $uri;
}
/**
* @param string $host
*/
public function setHost($host) {
$this->host = $host;
}
/**
* @param string $method
*/
public function setMethod($method) {
$this->method = $method;
}
public function __set($name, $value = null) {
$this->data[$name] = $value;
}
public function __get($name) {
return isset($this->data[$name]) ? $this->data[$name] : null;
}
} }
+98 -89
View File
@@ -1,109 +1,118 @@
<?php <?php
namespace Pecee\Http; namespace Pecee\Http;
class Response { class Response
{
protected $request;
protected $request; public function __construct(Request $request)
{
$this->request = $request;
}
public function __construct(Request $request) { /**
$this->request = $request; * Set the http status code
} *
* @param int $code
* @return static
*/
public function httpCode($code)
{
http_response_code($code);
return $this;
}
/** /**
* Set the http status code * Redirect the response
* *
* @param int $code * @param string $url
* @return static * @param int $httpCode
*/ */
public function httpCode($code) { public function redirect($url, $httpCode = null)
http_response_code($code); {
return $this; if ($httpCode !== null) {
} $this->httpCode($httpCode);
}
/** $this->header('location: ' . $url);
* Redirect the response die();
* }
* @param string $url
* @param int $httpCode
*/
public function redirect($url, $httpCode = null) {
if($httpCode !== null) {
$this->httpCode($httpCode);
}
$this->header('location: ' . $url); public function refresh()
die(); {
} $this->redirect($this->request->getUri());
}
public function refresh() { /**
$this->redirect($this->request->getUri()); * Add http authorisation
} * @param string $name
* @return static
*/
public function auth($name = '')
{
$this->headers([
'WWW-Authenticate: Basic realm="' . $name . '"',
'HTTP/1.0 401 Unauthorized'
]);
return $this;
}
/** public function cache($eTag, $lastModified = 2592000)
* Add http authorisation {
* @param string $name
* @return static
*/
public function auth($name = '') {
$this->headers([
'WWW-Authenticate: Basic realm="' . $name . '"',
'HTTP/1.0 401 Unauthorized'
]);
return $this;
}
public function cache($eTag, $lastModified = 2592000) { $this->headers([
'Cache-Control: public',
'Last-Modified: ' . gmdate("D, d M Y H:i:s", $lastModified) . ' GMT',
'Etag: ' . $eTag
]);
$this->headers([ if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) === $lastModified ||
'Cache-Control: public', isset($_SERVER['HTTP_IF_NONE_MATCH']) && $_SERVER['HTTP_IF_NONE_MATCH'] === $eTag
'Last-Modified: ' . gmdate("D, d M Y H:i:s", $lastModified) . ' GMT', ) {
'Etag: ' . $eTag
]);
if(isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) === $lastModified || $this->headers([
isset($_SERVER['HTTP_IF_NONE_MATCH']) && $_SERVER['HTTP_IF_NONE_MATCH'] === $eTag) { 'HTTP/1.1 304 Not Modified'
]);
$this->headers([ exit();
'HTTP/1.1 304 Not Modified' }
]);
exit(); return $this;
} }
return $this; /**
} * Json encode array
* @param array $value
*/
public function json(array $value)
{
$this->header('Content-type: application/json');
echo json_encode($value);
die();
}
/** /**
* Json encode array * Add header to response
* @param array $value * @param string $value
*/ * @return static
public function json(array $value) { */
$this->header('Content-type: application/json'); public function header($value)
echo json_encode($value); {
die(); header($value);
} return $this;
}
/** /**
* Add header to response * Add multiple headers to response
* @param string $value * @param array $headers
* @return static * @return static
*/ */
public function header($value) { public function headers(array $headers)
header($value); {
return $this; foreach ($headers as $header) {
} header($header);
}
/** return $this;
* Add multiple headers to response }
* @param array $headers
* @return static
*/
public function headers(array $headers) {
foreach($headers as $header) {
header($header);
}
return $this;
}
} }
@@ -0,0 +1,7 @@
<?php
namespace Pecee\SimpleRouter\Exceptions;
class HttpException extends \Exception
{
}
@@ -0,0 +1,6 @@
<?php
namespace Pecee\SimpleRouter\Exceptions;
class NotFoundHttpException extends HttpException
{
}
@@ -1,11 +0,0 @@
<?php
namespace Pecee\SimpleRouter;
interface IControllerRoute {
public function getController();
public function setController($controller);
public function getMethod();
public function setMethod($method);
}
@@ -1,9 +0,0 @@
<?php
namespace Pecee\SimpleRouter;
interface ILoadableRoute {
public function getUrl();
public function setUrl($url);
}
@@ -0,0 +1,15 @@
<?php
namespace Pecee\SimpleRouter;
use Pecee\Http\Request;
interface IRouterBootManager
{
/**
* Called when router loads it's routes
*
* @param Request $request
* @return Request
*/
public function boot(Request $request);
}
-84
View File
@@ -1,84 +0,0 @@
<?php
namespace Pecee\SimpleRouter;
abstract class LoadableRoute extends RouterEntry implements ILoadableRoute {
const PARAMETERS_REGEX_MATCH = '{([A-Za-z\-\_]*?)\?{0,1}}';
protected $url;
protected $alias;
public function getUrl() {
return $this->url;
}
/**
* Set url
*
* @param string $url
* @return static
*/
public function setUrl($url) {
$this->url = '/' . trim($url, '/') . '/';
if(preg_match_all('/' . static::PARAMETERS_REGEX_MATCH . '/is', $this->url, $matches)) {
if (count($matches[1])) {
foreach ($matches[1] as $key) {
$this->parameters[$key] = null;
}
}
}
return $this;
}
/**
* Get alias for the url which can be used when getting the url route.
* @return string|array
*/
public function getAlias(){
return $this->alias;
}
/**
* Check if route has given alias.
*
* @param string $name
* @return bool
*/
public function hasAlias($name) {
if ($this->getAlias() !== null) {
if (is_array($this->getAlias())) {
foreach ($this->getAlias() as $alias) {
if (strtolower($alias) === strtolower($name)) {
return true;
}
}
}
return strtolower($this->getAlias()) === strtolower($name);
}
return false;
}
/**
* Set the url alias for easier getting the url route.
* @param string|array $alias
* @return static
*/
public function setAlias($alias){
$this->alias = $alias;
return $this;
}
public function setData(array $settings) {
// Change as to alias
if(isset($settings['as'])) {
$this->setAlias($settings['as']);
}
return parent::setData($settings);
}
}
@@ -0,0 +1,36 @@
<?php
namespace Pecee\SimpleRouter\Route;
interface IControllerRoute extends IRoute
{
/**
* Get controller class-name
*
* @return string
*/
public function getController();
/**
* Set controller class-name
*
* @param string $controller
* @return static
*/
public function setController($controller);
/**
* Return active method
*
* @return string
*/
public function getMethod();
/**
* Set active method
*
* @param string $method
* @return static
*/
public function setMethod($method);
}
@@ -0,0 +1,60 @@
<?php
namespace Pecee\SimpleRouter\Route;
use Pecee\Http\Request;
interface IGroupRoute extends IRoute
{
/**
* Method called to check if a domain matches
*
* @param Request $request
* @return bool
*/
public function matchDomain(Request $request);
/**
* Set exception-handlers for group
*
* @param array $handlers
* @return static $this
*/
public function setExceptionHandlers(array $handlers);
/**
* Get exception-handlers for group
*
* @return array
*/
public function getExceptionHandlers();
/**
* Get domains for domain.
*
* @return array
*/
public function getDomains();
/**
* Set allowed domains for group.
*
* @param array $domains
* @return $this
*/
public function setDomains(array $domains);
/**
* Set prefix that child-routes will inherit.
*
* @param string $prefix
* @return string
*/
public function setPrefix($prefix);
/**
* Get prefix.
*
* @return string
*/
public function getPrefix();
}
@@ -0,0 +1,54 @@
<?php
namespace Pecee\SimpleRouter\Route;
use Pecee\Http\Request;
interface ILoadableRoute extends IRoute
{
/**
* Find url that matches method, parameters or name.
* Used when calling the url() helper.
*
* @param string|null $method
* @param array|null $parameters
* @param string|null $name
* @return string
*/
public function findUrl($method = null, $parameters = null, $name = null);
/**
* Loads and renders middlewares-classes
*
* @param Request $request
* @param ILoadableRoute $route
*/
public function loadMiddleware(Request $request, ILoadableRoute &$route);
public function getUrl();
public function setUrl($url);
/**
* Returns the provided name for the router.
*
* @return string
*/
public function getName();
/**
* Check if route has given name.
*
* @param string $name
* @return bool
*/
public function hasName($name);
/**
* Sets the router name, which makes it easier to obtain the url or router at a later point.
*
* @param string $name
* @return static $this
*/
public function setName($name);
}
+199
View File
@@ -0,0 +1,199 @@
<?php
namespace Pecee\SimpleRouter\Route;
use Pecee\Http\Request;
interface IRoute
{
/**
* Method called to check if a domain matches
*
* @param Request $request
* @return bool
*/
public function matchRoute(Request $request);
/**
* Called when route is matched.
* Returns class to be rendered.
*
* @param Request $request
* @return object
*/
public function renderRoute(Request $request);
/**
* Returns callback name/identifier for the current route based on the callback.
* Useful if you need to get a unique identifier for the loaded route, for instance
* when using translations etc.
*
* @return string
*/
public function getIdentifier();
/**
* Set allowed request methods
*
* @param array $methods
* @return static $this
*/
public function setRequestMethods(array $methods);
/**
* Get allowed request methods
*
* @return array
*/
public function getRequestMethods();
/**
* @return IRoute|null
*/
public function getParent();
/**
* Get the group for the route.
*
* @return IGroupRoute|null
*/
public function getGroup();
/**
* Set group
*
* @param IGroupRoute $group
* @return static $this
*/
public function setGroup(IGroupRoute $group);
/**
* Set parent route
*
* @param IRoute $parent
* @return static $this
*/
public function setParent(IRoute $parent);
/**
* Set callback
*
* @param string $callback
* @return static
*/
public function setCallback($callback);
/**
* @return string
*/
public function getCallback();
public function getMethod();
public function getClass();
public function setMethod($method);
/**
* @param string $namespace
* @return static $this
*/
public function setNamespace($namespace);
/**
* @return string
*/
public function getNamespace();
/**
* @param string $namespace
* @return static $this
*/
public function setDefaultNamespace($namespace);
public function getDefaultNamespace();
/**
* Get regular expression match used for matching route (if defined).
*
* @return string
*/
public function getMatch();
/**
* Add regular expression match for the entire route.
*
* @param string $regex
* @return static
*/
public function setMatch($regex);
/**
* Get parameter names.
*
* @return array
*/
public function getWhere();
/**
* Set parameter names.
*
* @param array $options
* @return static
*/
public function setWhere(array $options);
/**
* Get parameters
*
* @return array
*/
public function getParameters();
/**
* Get parameters
*
* @param array $parameters
* @return static $this
*/
public function setParameters(array $parameters);
/**
* Merge with information from another route.
*
* @param array $settings
* @param bool $merge
* @return static $this
*/
public function setSettings(array $settings, $merge = false);
/**
* Export route settings to array so they can be merged with another route.
*
* @return array
*/
public function toArray();
/**
* Get middlewares array
*
* @return array
*/
public function getMiddlewares();
/**
* Set middleware class-name
*
* @param string $middleware
* @return static
*/
public function setMiddleware($middleware);
/**
* Set middlewares array
*
* @param array $middlewares
* @return $this
*/
public function setMiddlewares(array $middlewares);
}
@@ -0,0 +1,183 @@
<?php
namespace Pecee\SimpleRouter\Route;
use Pecee\Http\Middleware\IMiddleware;
use Pecee\Http\Request;
use Pecee\SimpleRouter\Exceptions\HttpException;
abstract class LoadableRoute extends Route implements ILoadableRoute
{
const PARAMETERS_REGEX_MATCH = '%s([\w\-\_]*?)\%s{0,1}%s';
protected $url;
protected $name;
/**
* Loads and renders middlewares-classes
*
* @param Request $request
* @param ILoadableRoute $route
* @throws HttpException
*/
public function loadMiddleware(Request $request, ILoadableRoute &$route)
{
if (count($this->getMiddlewares()) > 0) {
foreach ($this->getMiddlewares() as $middleware) {
$middleware = $this->loadClass($middleware);
if (!($middleware instanceof IMiddleware)) {
throw new HttpException($middleware . ' must be instance of Middleware');
}
$middleware->handle($request, $route);
}
}
}
/**
* Set url
*
* @param string $url
* @return static
*/
public function setUrl($url)
{
$this->url = ($url === '/') ? '/' : '/' . trim($url, '/') . '/';
$regex = sprintf(static::PARAMETERS_REGEX_MATCH, $this->paramModifiers[0], $this->paramOptionalSymbol, $this->paramModifiers[1]);
if (preg_match_all('/' . $regex . '/is', $this->url, $matches)) {
foreach ($matches[1] as $key) {
$this->parameters[$key] = null;
}
}
return $this;
}
public function getUrl()
{
return $this->url;
}
/**
* Find url that matches method, parameters or name.
* Used when calling the url() helper.
*
* @param string|null $method
* @param array|null $parameters
* @param string|null $name
* @return string
*/
public function findUrl($method = null, $parameters = null, $name = null)
{
$url = '';
$parameters = (array)$parameters;
if ($this->getGroup() !== null && count($this->getGroup()->getDomains()) > 0) {
$url .= '//' . $this->getGroup()->getDomains()[0];
}
$url .= $this->getUrl();
$params = array_merge($this->getParameters(), $parameters);
/* Url that contains parameters that aren't recognized */
$unknownParams = [];
/* Create the param string - {} */
$param1 = $this->paramModifiers[0] . '%s' . $this->paramModifiers[1];
/* Create the param string with the optional symbol - {?} */
$param2 = $this->paramModifiers[0] . '%s' . $this->paramOptionalSymbol . $this->paramModifiers[1];
/* Let's parse the values of any {} parameter in the url */
foreach ($params as $param => $value) {
$value = (isset($parameters[$param])) ? $parameters[$param] : $value;
if (stripos($url, $param1) !== false || stripos($url, $param) !== false) {
$url = str_ireplace([sprintf($param1, $param), sprintf($param2, $param)], $value, $url);
} else {
$unknownParams[$param] = $value;
}
}
$url .= join('/', $unknownParams);
return rtrim($url, '/') . '/';
}
/**
* Returns the provided name for the router.
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Check if route has given name.
*
* @param string $name
* @return bool
*/
public function hasName($name)
{
return (strtolower($this->name) === strtolower($name));
}
/**
* Sets the router name, which makes it easier to obtain the url or router at a later point.
* Alias for LoadableRoute::setName().
*
* @see LoadableRoute::setName()
* @param string|array $name
* @return static
*/
public function name($name)
{
return $this->setName($name);
}
/**
* Sets the router name, which makes it easier to obtain the url or router at a later point.
*
* @param string $name
* @return static $this
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Merge with information from another route.
*
* @param array $values
* @param bool $merge
* @return static
*/
public function setSettings(array $values, $merge = false)
{
if (isset($values['as'])) {
if ($this->name !== null && $merge !== false) {
$this->setName($values['as'] . '.' . $this->name);
} else {
$this->setName($values['as']);
}
}
if (isset($values['prefix'])) {
$this->setUrl($values['prefix'] . $this->getUrl());
}
parent::setSettings($values, $merge);
return $this;
}
}
+517
View File
@@ -0,0 +1,517 @@
<?php
namespace Pecee\SimpleRouter\Route;
use Pecee\Http\Request;
use Pecee\SimpleRouter\Exceptions\HttpException;
use Pecee\SimpleRouter\Exceptions\NotFoundHttpException;
abstract class Route implements IRoute
{
const REQUEST_TYPE_GET = 'get';
const REQUEST_TYPE_POST = 'post';
const REQUEST_TYPE_PUT = 'put';
const REQUEST_TYPE_PATCH = 'patch';
const REQUEST_TYPE_OPTIONS = 'options';
const REQUEST_TYPE_DELETE = 'delete';
public static $requestTypes = [
self::REQUEST_TYPE_GET,
self::REQUEST_TYPE_POST,
self::REQUEST_TYPE_PUT,
self::REQUEST_TYPE_PATCH,
self::REQUEST_TYPE_OPTIONS,
self::REQUEST_TYPE_DELETE,
];
protected $paramModifiers = '{}';
protected $paramOptionalSymbol = '?';
protected $group;
protected $parent;
protected $callback;
protected $defaultNamespace;
/* Default options */
protected $namespace;
protected $regex;
protected $requestMethods = [];
protected $where = [];
protected $parameters = [];
protected $middlewares = [];
public function renderRoute(Request $request)
{
if ($this->getCallback() !== null && is_callable($this->getCallback())) {
/* When the callback is a function */
call_user_func_array($this->getCallback(), $this->getParameters());
} else {
/* When the callback is a method */
$controller = explode('@', $this->getCallback());
$className = $this->getNamespace() . '\\' . $controller[0];
$class = $this->loadClass($className);
$method = $controller[1];
if (!method_exists($class, $method)) {
throw new NotFoundHttpException(sprintf('Method %s does not exist in class %s', $method, $className), 404);
}
$parameters = array_filter($this->getParameters(), function ($var) {
return ($var !== null);
});
call_user_func_array([$class, $method], $parameters);
return $class;
}
return null;
}
protected function parseParameters($route, $url, $parameterRegex = '[\w]+')
{
$parameterNames = [];
$regex = '';
$lastCharacter = '';
$isParameter = false;
$parameter = '';
for ($i = 0; $i < strlen($route); $i++) {
$character = $route[$i];
if ($character === '{') {
/* Remove "/" and "\" from regex */
if (substr($regex, strlen($regex) - 1) === '/') {
$regex = substr($regex, 0, strlen($regex) - 2);
}
$isParameter = true;
} elseif ($isParameter && $character === '}') {
$required = true;
/* Check for optional parameter and use custom parameter regex if it exists */
if (is_array($this->where) === true && isset($this->where[$parameter])) {
$parameterRegex = $this->where[$parameter];
}
if ($lastCharacter === '?') {
$parameter = substr($parameter, 0, strlen($parameter) - 1);
$regex .= '(?:\/?(?P<' . $parameter . '>' . $parameterRegex . ')[^\/]?)?';
$required = false;
} else {
$regex .= '\/?(?P<' . $parameter . '>' . $parameterRegex . ')[^\/]?';
}
$parameterNames[] = [
'name' => $parameter,
'required' => $required,
];
$parameter = '';
$isParameter = false;
} elseif ($isParameter) {
$parameter .= $character;
} elseif ($character === '/') {
$regex .= '\\' . $character;
} else {
$regex .= str_replace('.', '\\.', $character);
}
$lastCharacter = $character;
}
$parameterValues = [];
if (preg_match('/^' . $regex . '\/?$/is', $url, $parameterValues)) {
$parameters = [];
foreach ($parameterNames as $name) {
$parameterValue = isset($parameterValues[$name['name']]) ? $parameterValues[$name['name']] : null;
if ($name['required'] && $parameterValue === null) {
throw new HttpException('Missing required parameter ' . $name['name'], 404);
}
if ($name['required'] === false && $parameterValue === null) {
continue;
}
$parameters[$name['name']] = $parameterValue;
}
return $parameters;
}
return null;
}
protected function loadClass($name)
{
if (!class_exists($name)) {
throw new HttpException(sprintf('Class %s does not exist', $name), 500);
}
return new $name();
}
/**
* Returns callback name/identifier for the current route based on the callback.
* Useful if you need to get a unique identifier for the loaded route, for instance
* when using translations etc.
*
* @return string
*/
public function getIdentifier()
{
if (strpos($this->callback, '@') !== false) {
return $this->callback;
}
return 'function_' . md5($this->callback);
}
/**
* Set allowed request methods
*
* @param array $methods
* @return static $this
*/
public function setRequestMethods(array $methods)
{
$this->requestMethods = $methods;
return $this;
}
/**
* Get allowed request methods
*
* @return array
*/
public function getRequestMethods()
{
return $this->requestMethods;
}
/**
* @return IRoute|null
*/
public function getParent()
{
return $this->parent;
}
/**
* Get the group for the route.
*
* @return IGroupRoute|null
*/
public function getGroup()
{
return $this->group;
}
/**
* Set group
*
* @param IGroupRoute $group
* @return static $this
*/
public function setGroup(IGroupRoute $group)
{
$this->group = $group;
return $this;
}
/**
* Set parent route
*
* @param IRoute $parent
* @return static $this
*/
public function setParent(IRoute $parent)
{
$this->parent = $parent;
return $this;
}
/**
* Set callback
*
* @param string $callback
* @return static
*/
public function setCallback($callback)
{
$this->callback = $callback;
return $this;
}
/**
* @return string
*/
public function getCallback()
{
return $this->callback;
}
public function getMethod()
{
if (strpos($this->callback, '@') !== false) {
$tmp = explode('@', $this->callback);
return $tmp[1];
}
return null;
}
public function getClass()
{
if (strpos($this->callback, '@') !== false) {
$tmp = explode('@', $this->callback);
return $tmp[0];
}
return null;
}
public function setMethod($method)
{
$this->callback = sprintf('%s@%s', $this->getClass(), $method);
return $this;
}
public function setClass($class)
{
$this->callback = sprintf('%s@%s', $class, $this->getMethod());
return $this;
}
/**
* @param string $namespace
* @return static $this
*/
public function setNamespace($namespace)
{
$this->namespace = $namespace;
return $this;
}
/**
* @param string $namespace
* @return static $this
*/
public function setDefaultNamespace($namespace)
{
$this->defaultNamespace = $namespace;
return $this;
}
public function getDefaultNamespace()
{
return $this->defaultNamespace;
}
/**
* @return string
*/
public function getNamespace()
{
return ($this->namespace === null) ? $this->defaultNamespace : $this->namespace;
}
/**
* Add regular expression match for the entire route.
*
* @param string $regex
* @return static
*/
public function setMatch($regex)
{
$this->regex = $regex;
return $this;
}
/**
* Get regular expression match used for matching route (if defined).
*
* @return string
*/
public function getMatch()
{
return $this->regex;
}
/**
* Export route settings to array so they can be merged with another route.
*
* @return array
*/
public function toArray()
{
$values = [];
if ($this->namespace !== null) {
$values['namespace'] = $this->namespace;
}
if (count($this->requestMethods) > 0) {
$values['method'] = $this->requestMethods;
}
if (count($this->where) > 0) {
$values['where'] = $this->where;
}
if (count($this->parameters) > 0) {
$values['parameters'] = $this->parameters;
}
if (count($this->middlewares) > 0) {
$values['middleware'] = $this->middlewares;
}
return $values;
}
/**
* Merge with information from another route.
*
* @param array $values
* @param bool $merge
* @return static $this
*/
public function setSettings(array $values, $merge = false)
{
if (isset($values['namespace']) && $this->namespace === null) {
$this->setNamespace($values['namespace']);
}
if (isset($values['method'])) {
$this->setRequestMethods(array_merge($this->requestMethods, (array)$values['method']));
}
if (isset($values['where'])) {
$this->setWhere(array_merge($this->where, (array)$values['where']));
}
if (isset($values['parameters'])) {
$this->setParameters(array_merge($this->parameters, (array)$values['parameters']));
}
// Push middleware if multiple
if (isset($values['middleware'])) {
$this->setMiddlewares(array_merge((array)$values['middleware'], $this->middlewares));
}
return $this;
}
/**
* Get parameter names.
*
* @return array
*/
public function getWhere()
{
return $this->where;
}
/**
* Set parameter names.
*
* @param array $options
* @return static
*/
public function setWhere(array $options)
{
$this->where = $options;
return $this;
}
/**
* Add regular expression parameter match.
* Alias for LoadableRoute::where()
*
* @see LoadableRoute::where()
* @param array $options
* @return static
*/
public function where(array $options)
{
return $this->where($options);
}
/**
* Get parameters
*
* @return array
*/
public function getParameters()
{
return $this->parameters;
}
/**
* Get parameters
*
* @param array $parameters
* @return static $this
*/
public function setParameters(array $parameters)
{
$this->parameters = $parameters;
return $this;
}
/**
* Set middleware class-name
*
* @param string $middleware
* @return static
*/
public function setMiddleware($middleware)
{
$this->middlewares[] = $middleware;
return $this;
}
/**
* Set middlewares array
*
* @param array $middlewares
* @return $this
*/
public function setMiddlewares(array $middlewares)
{
$this->middlewares = $middlewares;
return $this;
}
/**
* @return string|array
*/
public function getMiddlewares()
{
return $this->middlewares;
}
}
@@ -0,0 +1,199 @@
<?php
namespace Pecee\SimpleRouter\Route;
use Pecee\Http\Request;
use Pecee\SimpleRouter\Exceptions\NotFoundHttpException;
class RouteController extends LoadableRoute implements IControllerRoute
{
protected $defaultMethod = 'index';
protected $controller;
protected $method;
protected $names = [];
public function __construct($url, $controller)
{
$this->setUrl($url);
$this->setName(trim(str_replace('/', '.', $url), '/'));
$this->controller = $controller;
}
/**
* Check if route has given name.
*
* @param string $name
* @return bool
*/
public function hasName($name)
{
if ($this->name === null) {
return false;
}
/* Remove method/type */
if (stripos($name, '.') !== false) {
$method = substr($name, strrpos($name, '.') + 1);
$newName = substr($name, 0, strrpos($name, '.'));
if (strtolower($this->name) === strtolower($newName) && in_array($method, $this->names)) {
return true;
}
}
return parent::hasName($name);
}
public function findUrl($method = null, $parameters = null, $name = null)
{
if (stripos($name, '.') !== false) {
$found = array_search(substr($name, strrpos($name, '.') + 1), $this->names);
if ($found !== false) {
$method = $found;
}
}
$url = '';
$parameters = (array)$parameters;
/* Remove requestType from method-name, if it exists */
if ($method !== null) {
foreach (static::$requestTypes as $requestType) {
if (stripos($method, $requestType) === 0) {
$method = substr($method, strlen($requestType));
break;
}
}
$method .= '/';
}
if ($this->getGroup() !== null && count($this->getGroup()->getDomains()) > 0) {
$url .= '//' . $this->getGroup()->getDomains()[0];
}
$url .= '/' . trim($this->getUrl(), '/') . '/' . strtolower($method) . join('/', $parameters);
return '/' . trim($url, '/') . '/';
}
public function renderRoute(Request $request)
{
if ($this->getCallback() !== null && is_callable($this->getCallback())) {
// When the callback is a function
call_user_func_array($this->getCallback(), $this->getParameters());
} else {
// When the callback is a method
$controller = explode('@', $this->getCallback());
$className = $this->getNamespace() . '\\' . $controller[0];
$class = $this->loadClass($className);
$method = $request->getMethod() . ucfirst($controller[1]);
if (!method_exists($class, $method)) {
throw new NotFoundHttpException(sprintf('Method %s does not exist in class %s', $method, $className), 404);
}
call_user_func_array([$class, $method], $this->getParameters());
return $class;
}
return null;
}
public function matchRoute(Request $request)
{
$url = parse_url(urldecode($request->getUri()), PHP_URL_PATH);
$url = rtrim($url, '/') . '/';
if (strtolower($url) == strtolower($this->url) || stripos($url, $this->url) === 0) {
$strippedUrl = trim(str_ireplace($this->url, '/', $url), '/');
$path = explode('/', $strippedUrl);
if (count($path) > 0) {
$method = (!isset($path[0]) || trim($path[0]) === '') ? $this->defaultMethod : $path[0];
$this->method = $method;
array_shift($path);
$this->parameters = $path;
// Set callback
$this->setCallback($this->controller . '@' . $this->method);
return true;
}
}
return null;
}
/**
* Get controller class-name.
*
* @return string
*/
public function getController()
{
return $this->controller;
}
/**
* Get controller class-name.
*
* @param string $controller
* @return static
*/
public function setController($controller)
{
$this->controller = $controller;
return $this;
}
/**
* Return active method
*
* @return string
*/
public function getMethod()
{
return $this->method;
}
/**
* Set active method
*
* @param string $method
* @return static
*/
public function setMethod($method)
{
$this->method = $method;
return $this;
}
/**
* Merge with information from another route.
*
* @param array $values
* @param bool $merge
* @return static
*/
public function setSettings(array $values, $merge = false)
{
if (isset($values['names'])) {
$this->names = $values['names'];
}
parent::setSettings($values, $merge);
return $this;
}
}
+177
View File
@@ -0,0 +1,177 @@
<?php
namespace Pecee\SimpleRouter\Route;
use Pecee\Http\Request;
class RouteGroup extends Route implements IGroupRoute
{
protected $prefix;
protected $name;
protected $domains = [];
protected $exceptionHandlers = [];
/**
* Method called to check if a domain matches
*
* @param Request $request
* @return bool
*/
public function matchDomain(Request $request)
{
if (count($this->domains) > 0) {
foreach ($this->domains as $domain) {
$parameters = $this->parseParameters($domain, $request->getHost(), '.*');
if ($parameters !== null) {
$this->parameters = $parameters;
return true;
}
}
return false;
}
return true;
}
/**
* Method called to check if route matches
*
* @param Request $request
* @return bool
*/
public function matchRoute(Request $request)
{
// Skip if prefix doesn't match
if ($this->prefix !== null && stripos($request->getUri(), $this->prefix) === false) {
return false;
}
return $this->matchDomain($request);
}
/**
* Set exception-handlers for group
*
* @param array $handlers
* @return static $this
*/
public function setExceptionHandlers(array $handlers)
{
$this->exceptionHandlers = $handlers;
return $this;
}
/**
* Get exception-handlers for group
*
* @return array
*/
public function getExceptionHandlers()
{
return $this->exceptionHandlers;
}
/**
* Get allowed domains for domain.
*
* @return array
*/
public function getDomains()
{
return $this->domains;
}
/**
* Set allowed domains for group.
*
* @param array $domains
* @return $this
*/
public function setDomains(array $domains)
{
$this->domains = $domains;
return $this;
}
/**
* @param string $prefix
* @return static
*/
public function setPrefix($prefix)
{
$this->prefix = '/' . trim($prefix, '/');
return $this;
}
/**
* Set prefix that child-routes will inherit.
*
* @return string
*/
public function getPrefix()
{
return $this->prefix;
}
/**
* Merge with information from another route.
*
* @param array $values
* @param bool $merge
* @return static
*/
public function setSettings(array $values, $merge = false)
{
if (isset($values['prefix'])) {
$this->setPrefix($values['prefix'] . $this->prefix);
}
if (isset($values['exceptionHandler'])) {
$this->setExceptionHandlers((array)$values['exceptionHandler']);
}
if (isset($values['domain'])) {
$this->setDomains((array)$values['domain']);
}
if (isset($values['as'])) {
if ($this->name !== null && $merge !== false) {
$this->name = $values['as'] . '.' . $this->name;
} else {
$this->name = $values['as'];
}
}
parent::setSettings($values, $merge);
return $this;
}
/**
* Export route settings to array so they can be merged with another route.
*
* @return array
*/
public function toArray()
{
$values = [];
if ($this->prefix !== null) {
$values['prefix'] = $this->getPrefix();
}
if ($this->name !== null) {
$values['as'] = $this->name;
}
return array_merge($values, parent::toArray());
}
}
@@ -0,0 +1,203 @@
<?php
namespace Pecee\SimpleRouter\Route;
use Pecee\Http\Request;
use Pecee\SimpleRouter\Exceptions\NotFoundHttpException;
class RouteResource extends LoadableRoute implements IControllerRoute
{
protected $urls = [
'index' => '',
'create' => 'create',
'store' => '',
'show' => '',
'edit' => 'edit',
'update' => '',
'destroy' => '',
];
protected $names = [];
protected $controller;
public function __construct($url, $controller)
{
$this->setUrl($url);
$this->controller = $controller;
$this->setName(trim(str_replace('/', '.', $url), '/'));
}
/**
* Check if route has given name.
*
* @param string $name
* @return bool
*/
public function hasName($name)
{
if ($this->name === null) {
return false;
}
if (strtolower($this->name) === strtolower($name)) {
return true;
}
/* Remove method/type */
if (stripos($name, '.') !== false) {
$name = substr($name, 0, strrpos($name, '.'));
}
return (strtolower($this->name) === strtolower($name));
}
public function findUrl($method = null, $parameters = null, $name = null)
{
$method = array_search($name, $this->names);
if ($method !== false) {
return rtrim($this->url . $this->urls[$method], '/') . '/';
}
return $this->url;
}
public function renderRoute(Request $request)
{
if ($this->getCallback() !== null && is_callable($this->getCallback())) {
// When the callback is a function
call_user_func_array($this->getCallback(), $this->getParameters());
} else {
// When the callback is a method
$controller = explode('@', $this->getCallback());
$className = $this->getNamespace() . '\\' . $controller[0];
$class = $this->loadClass($className);
$method = strtolower($controller[1]);
if (!method_exists($class, $method)) {
throw new NotFoundHttpException(sprintf('Method %s does not exist in class %s', $method, $className), 404);
}
call_user_func_array([$class, $method], $this->getParameters());
return $class;
}
return null;
}
protected function call($method, $parameters)
{
$this->setCallback($this->controller . '@' . $method);
$this->parameters = $parameters;
return true;
}
public function matchRoute(Request $request)
{
$url = parse_url(urldecode($request->getUri()), PHP_URL_PATH);
$url = rtrim($url, '/') . '/';
$route = rtrim($this->url, '/') . '/{id?}/{action?}';
$parameters = $this->parseParameters($route, $url);
if ($parameters !== null) {
$parameters = array_merge($this->parameters, (array)$parameters);
$action = isset($parameters['action']) ? $parameters['action'] : null;
unset($parameters['action']);
$method = request()->getMethod();
// Delete
if (isset($parameters['id']) && $method === static::REQUEST_TYPE_DELETE) {
return $this->call('destroy', $parameters);
}
// Update
if (isset($parameters['id']) && in_array($method, [static::REQUEST_TYPE_PATCH, static::REQUEST_TYPE_PUT])) {
return $this->call('update', $parameters);
}
// Edit
if (isset($parameters['id']) && strtolower($action) === 'edit' && $method === static::REQUEST_TYPE_GET) {
return $this->call('edit', $parameters);
}
// Create
if (strtolower($action) === 'create' && $method === static::REQUEST_TYPE_GET) {
return $this->call('create', $parameters);
}
// Save
if ($method === static::REQUEST_TYPE_POST) {
return $this->call('store', $parameters);
}
// Show
if (isset($parameters['id']) && $method === static::REQUEST_TYPE_GET) {
return $this->call('show', $parameters);
}
// Index
return $this->call('index', $parameters);
}
return null;
}
/**
* @return string
*/
public function getController()
{
return $this->controller;
}
/**
* @param string $controller
* @return static
*/
public function setController($controller)
{
$this->controller = $controller;
return $this;
}
public function setName($name)
{
$this->name = $name;
$this->names = [
'index' => $this->name . '.index',
'create' => $this->name . '.create',
'store' => $this->name . '.store',
'show' => $this->name . '.show',
'edit' => $this->name . '.edit',
'update' => $this->name . '.update',
'destroy' => $this->name . '.destroy',
];
return $this;
}
/**
* Merge with information from another route.
*
* @param array $values
* @param bool $merge
* @return static
*/
public function setSettings(array $values, $merge = false)
{
if (isset($values['names'])) {
$this->names = $values['names'];
}
parent::setSettings($values, $merge);
return $this;
}
}
+45
View File
@@ -0,0 +1,45 @@
<?php
namespace Pecee\SimpleRouter\Route;
use Pecee\Http\Request;
class RouteUrl extends LoadableRoute
{
public function __construct($url, $callback)
{
$this->setUrl($url);
$this->setCallback($callback);
}
public function matchRoute(Request $request)
{
$url = parse_url(urldecode($request->getUri()), PHP_URL_PATH);
$url = rtrim($url, '/') . '/';
// Match on custom defined regular expression
if ($this->regex !== null) {
$parameters = [];
if (preg_match('/(' . $this->regex . ')/is', $request->getHost() . $url, $parameters)) {
$this->parameters = (array)$parameters[0];
return true;
}
return null;
}
// Make regular expression based on route
$route = rtrim($this->url, '/') . '/';
$parameters = $this->parseParameters($route, $url);
if ($parameters !== null) {
$this->parameters = array_merge($this->parameters, $parameters);
return true;
}
return null;
}
}
+516
View File
@@ -0,0 +1,516 @@
<?php
namespace Pecee\SimpleRouter;
use Pecee\Handlers\IExceptionHandler;
use Pecee\Http\Middleware\BaseCsrfVerifier;
use Pecee\Http\Request;
use Pecee\SimpleRouter\Exceptions\HttpException;
use Pecee\SimpleRouter\Exceptions\NotFoundHttpException;
use Pecee\SimpleRouter\Route\IControllerRoute;
use Pecee\SimpleRouter\Route\IGroupRoute;
use Pecee\SimpleRouter\Route\ILoadableRoute;
use Pecee\SimpleRouter\Route\IRoute;
class Router
{
/**
* The instance of this class
* @var static
*/
protected static $instance;
/**
* Current request
* @var Request
*/
protected $request;
/**
* Defines if a route is currently being processed.
* @var bool
*/
protected $processingRoute;
/**
* All added routes
* @var array
*/
protected $routes;
/**
* List of processed routes
* @var array
*/
protected $processedRoutes;
/**
* Stack of routes used to keep track of sub-routes added
* when a route is being processed.
* @var array
*/
protected $routeStack;
/**
* List of added bootmanagers
* @var array
*/
protected $bootManagers;
/**
* Csrf verifier class
* @var BaseCsrfVerifier
*/
protected $csrfVerifier;
/**
* Get exception handlers
* @var array
*/
protected $exceptionHandlers;
/**
* The current loaded route
* @var ILoadableRoute|null
*/
protected $loadedRoute;
/**
* List over route changes (to avoid endless-looping)
* @var array
*/
protected $routeRewrites = [];
/**
* If the route has been rewritten/changed this property will contain the original url.
* @var string
*/
protected $originalUrl;
/**
* Get current router instance
* @return static
*/
public static function getInstance()
{
if (static::$instance === null) {
static::$instance = new static();
}
return static::$instance;
}
public function __construct()
{
$this->reset();
}
public function reset()
{
$this->processingRoute = false;
$this->request = new Request();
$this->routes = [];
$this->bootManagers = [];
$this->routeStack = [];
$this->processedRoutes = [];
$this->exceptionHandlers = [];
}
/**
* Add route
* @param IRoute $route
* @return IRoute
*/
public function addRoute(IRoute $route)
{
/*
* If a route is currently being processed, that means that the
* route being added are rendered from the parent routes callback,
* so we add them to the stack instead.
*/
if ($this->processingRoute === true) {
$this->routeStack[] = $route;
} else {
$this->routes[] = $route;
}
return $route;
}
protected function processRoutes(array $routes, IGroupRoute $group = null, IRoute $parent = null)
{
// Loop through each route-request
/* @var $route IRoute */
foreach ($routes as $route) {
if ($route instanceof IGroupRoute) {
$group = $route;
if ($route->getCallback() !== null && is_callable($route->getCallback())) {
$this->processingRoute = true;
$route->renderRoute($this->request);
$this->processingRoute = false;
if ($route->matchRoute($this->request)) {
/* Add exceptionhandlers */
if (count($route->getExceptionHandlers()) > 0) {
$this->exceptionHandlers = array_merge($route->getExceptionHandlers(), $this->exceptionHandlers);
}
}
}
}
if ($group !== null) {
/* Add the parent group */
$route->setGroup($group);
}
if ($parent !== null) {
/* Add the parent route */
$route->setParent($parent);
/* Add/merge parent settings with child */
$route->setSettings($parent->toArray(), true);
}
if ($route instanceof ILoadableRoute) {
/* Add the route to the map, so we can find the active one when all routes has been loaded */
$this->processedRoutes[] = $route;
}
if (count($this->routeStack) > 0) {
/* Pop and grap the routes added when executing group callback earlier */
$stack = $this->routeStack;
$this->routeStack = [];
/* Route any routes added to the stack */
$this->processRoutes($stack, $route, $group);
}
}
}
public function routeRequest($rewrite = false)
{
$this->loadedRoute = null;
$routeNotAllowed = false;
try {
/* Initialize boot-managers */
if (count($this->bootManagers) > 0) {
/* @var $manager IRouterBootManager */
foreach ($this->bootManagers as $manager) {
$this->request = $manager->boot($this->request);
if (!($this->request instanceof Request)) {
throw new HttpException('Bootmanager "' . get_class($manager) . '" must return instance of ' . Request::class, 500);
}
}
}
if ($rewrite === false) {
/* Loop through each route-request */
$this->processRoutes($this->routes);
if ($this->csrfVerifier !== null) {
// Verify csrf token for request
$this->csrfVerifier->handle($this->request);
}
$this->originalUrl = $this->request->getUri();
}
/* @var $route IRoute */
foreach ($this->processedRoutes as $route) {
/* If the route matches */
if ($route->matchRoute($this->request)) {
/* Check if request method matches */
if (count($route->getRequestMethods()) > 0 && !in_array($this->request->getMethod(), $route->getRequestMethods())) {
$routeNotAllowed = true;
continue;
}
$this->loadedRoute = $route;
$this->loadedRoute->loadMiddleware($this->request, $this->loadedRoute);
/* If the request has changed, we reinitialize the router */
if ($this->request->getUri() !== $this->originalUrl && !in_array($this->request->getUri(), $this->routeRewrites)) {
$this->routeRewrites[] = $this->request->getUri();
$this->routeRequest(true);
return;
}
/* Render route */
$routeNotAllowed = false;
$this->request->setUri($this->originalUrl);
$this->loadedRoute->renderRoute($this->request);
break;
}
}
} catch (\Exception $e) {
$this->handleException($e);
}
if ($routeNotAllowed === true) {
$this->handleException(new HttpException('Route or method not allowed', 403));
}
if ($this->loadedRoute === null) {
$this->handleException(new NotFoundHttpException('Route not found: ' . $this->request->getUri(), 404));
}
}
protected function handleException(\Exception $e)
{
/* @var $handler IExceptionHandler */
foreach ($this->exceptionHandlers as $handler) {
$handler = new $handler();
if (!($handler instanceof IExceptionHandler)) {
throw new HttpException('Exception handler must implement the IExceptionHandler interface.', 500);
}
$request = $handler->handleError($this->request, $this->loadedRoute, $e);
/* If the request has changed */
if ($request !== null && $this->request->getUri() !== $this->originalUrl && !in_array($request->getUri(), $this->routeRewrites)) {
$this->request = $request;
$this->routeRewrites[] = $request->getUri();
$this->routeRequest(true);
return;
}
}
throw $e;
}
public function arrayToParams(array $getParams = [], $includeEmpty = true)
{
if (count($getParams) > 0) {
if ($includeEmpty === false) {
$getParams = array_filter($getParams, function ($item) {
return (!empty($item));
});
}
return '?' . http_build_query($getParams);
}
return '';
}
/**
* Find route by alias, class, callback or method.
*
* @param string $name
* @return ILoadableRoute|null
*/
public function findRoute($name)
{
/* @var $route ILoadableRoute */
foreach ($this->processedRoutes as $route) {
/* Check if the name matches with a name on the route. Should match either router alias or controller alias. */
if ($route->hasName($name)) {
return $route;
}
/* Direct match to controller */
if ($route instanceof IControllerRoute && strtolower($route->getController()) === strtolower($name)) {
return $route;
}
/* Using @ is most definitely a controller@method or alias@method */
if (strpos($name, '@') !== false) {
list($controller, $method) = array_map('strtolower', explode('@', $name));
if ($controller === strtolower($route->getClass()) && $method === strtolower($route->getMethod())) {
return $route;
}
}
/* Check if callback matches (if it's not a function) */
if (strpos($name, '@') !== false && strpos($route->getCallback(), '@') !== false && !is_callable($route->getCallback())) {
/* Check if the entire callback is matching */
if (strtolower($route->getCallback()) === strtolower($name) || strpos($route->getCallback(), $name) === 0) {
return $route;
}
/* Check if the class part of the callback matches (class@method) */
if (strtolower($name) === strtolower($route->getClass())) {
return $route;
}
}
}
return null;
}
/**
* Get url for a route by using either name/alias, class or method name.
*
* The name parameter supports the following values:
* - Route name
* - Controller/resource name (with or without method)
* - Controller class name
*
* When searching for controller/resource by name, you can use this syntax "route.name@method".
* You can also use the same syntax when searching for a specific controller-class "MyController@home".
* If no arguments is specified, it will return the url for the current loaded route.
*
* @param string|null $name
* @param string|array|null $parameters
* @param array|null $getParams
* @return string
*/
public function getUrl($name = null, $parameters = null, $getParams = [])
{
if ($getParams !== null && is_array($getParams) === false) {
throw new \InvalidArgumentException('Invalid type for getParams. Must be array or null');
}
if ($getParams === null) {
$getParams = $_GET;
}
/* Return current route if no options has been specified */
if ($name === null && $parameters === null) {
return '/' . trim(parse_url($this->request->getUri(), PHP_URL_PATH), '/') . $this->arrayToParams($getParams);
}
/* If nothing is defined and a route is loaded we use that */
if ($name === null && $this->loadedRoute !== null) {
return $this->loadedRoute->findUrl($this->loadedRoute->getMethod(), $parameters, $name) . $this->arrayToParams($getParams);
}
/* We try to find a match on the given name */
$route = $this->findRoute($name);
if ($route !== null) {
return $route->findUrl($route->getMethod(), $parameters, $name) . $this->arrayToParams($getParams);
}
/* Using @ is most definitely a controller@method or alias@method */
if (stripos($name, '@') !== false) {
list($controller, $method) = explode('@', $name);
/* Loop through all the routes to see if we can find a match */
/* @var $route ILoadableRoute */
foreach ($this->processedRoutes as $route) {
/* Check if the route contains the name/alias */
if ($route->hasName($controller)) {
return $route->findUrl($method, $parameters, $name) . $this->arrayToParams($getParams);
}
/* Check if the route controller is equal to the name */
if ($route instanceof IControllerRoute && strtolower($route->getController()) === strtolower($controller)) {
return $route->findUrl($method, $parameters, $name) . $this->arrayToParams($getParams);
}
}
}
/* No result so we assume that someone is using a hardcoded url and join everything together. */
return '/' . trim(join('/', array_merge((array)$name, (array)$parameters)), '/') . $this->arrayToParams($getParams);
}
/**
* Get bootmanagers
* @return array
*/
public function getBootManagers()
{
return $this->bootManagers;
}
/**
* Set bootmanagers
* @param array $bootManagers
*/
public function setBootManagers(array $bootManagers)
{
$this->bootManagers = $bootManagers;
}
/**
* Add bootmanager
* @param IRouterBootManager $bootManager
*/
public function addBootManager(IRouterBootManager $bootManager)
{
$this->bootManagers[] = $bootManager;
}
/**
* @return array
*/
public function getRoutes()
{
return $this->routes;
}
/**
* Get current request
*
* @return Request
*/
public function getRequest()
{
return $this->request;
}
/**
* Get csrf verifier class
* @return BaseCsrfVerifier
*/
public function getCsrfVerifier()
{
return $this->csrfVerifier;
}
/**
* Set csrf verifier class
*
* @param BaseCsrfVerifier $csrfVerifier
* @return static
*/
public function setCsrfVerifier(BaseCsrfVerifier $csrfVerifier)
{
$this->csrfVerifier = $csrfVerifier;
return $this;
}
/**
* Get loaded route
* @return ILoadableRoute|null
*/
public function getLoadedRoute()
{
return $this->loadedRoute;
}
}
-549
View File
@@ -1,549 +0,0 @@
<?php
namespace Pecee\SimpleRouter;
use Pecee\Exception\RouterException;
use Pecee\Handler\IExceptionHandler;
use Pecee\Http\Middleware\BaseCsrfVerifier;
use Pecee\Http\Request;
use Pecee\Http\Response;
class RouterBase {
protected static $instance;
/**
* Current request
* @var Request
*/
protected $request;
/**
* Response
* @var Response
*/
protected $response;
/**
* Used to keep track of whether or not a should should be added to
* the backstack-list for group-processing or not.
* @var bool
*/
protected $processingRoute;
/**
* All added routes
* @var array
*/
protected $routes;
/**
* List of
* @var array
*/
protected $controllerUrlMap;
/**
* Backstack array used to keep track of sub-routes
* @var array
*/
protected $backStack;
/**
* The default namespace that all routes will inherit
* @var string
*/
protected $defaultNamespace;
/**
* List of added bootmanagers
* @var array
*/
protected $bootManagers;
/**
* Csrf verifier class
* @var BaseCsrfVerifier
*/
protected $csrfVerifier;
/**
* Get exception handlers
* @var array
*/
protected $exceptionHandlers;
/**
* The current loaded route
* @var RouterRoute|null
*/
protected $loadedRoute;
/**
* List over route changes (to avoid endless-looping)
* @var array
*/
protected $routeRewrites = array();
protected $originalUrl;
public function __construct() {
$this->reset();
}
public function reset() {
$this->processingRoute = false;
$this->request = new Request();
$this->response = new Response($this->request);
$this->routes = array();
$this->bootManagers = array();
$this->backStack = array();
$this->controllerUrlMap = array();
$this->exceptionHandlers = array();
}
/**
* Add route
* @param RouterEntry $route
* @return RouterEntry
*/
public function addRoute(RouterEntry $route) {
if($this->processingRoute) {
$this->backStack[] = $route;
} else {
$this->routes[] = $route;
}
return $route;
}
protected function processRoutes(array $routes, array $settings = array(), array $prefixes = array(), RouterEntry $parent = null) {
// Loop through each route-request
/* @var $route RouterEntry */
for($i = 0; $i < count($routes); $i++) {
$route = $routes[$i];
$route->setData($settings);
if($parent !== null) {
if($parent instanceof RouterGroup) {
if ($parent->getPrefix() !== null && trim($parent->getPrefix(), '/') !== '') {
$prefixes[] = trim($parent->getPrefix(), '/');
}
}
$route->setParent($parent);
}
if($route->getNamespace() === null && $this->defaultNamespace !== null) {
$namespace = $this->defaultNamespace;
if ($route->getNamespace()) {
$namespace .= '\\' . $route->getNamespace();
}
$route->setNamespace($namespace);
}
if($route instanceof ILoadableRoute) {
$route->setUrl( trim(join('/', $prefixes) . $route->getUrl(), '/') );
$this->controllerUrlMap[] = $route;
} elseif($route instanceof RouterGroup) {
if ($route->getCallback() !== null && is_callable($route->getCallback())) {
$this->processingRoute = true;
$route->renderRoute($this->request);
$this->processingRoute = false;
if ($route->matchRoute($this->request)) {
$settings = array_merge($settings, $route->getMergeableData());
// Add ExceptionHandler
if (count($route->getExceptionHandlers())) {
$this->exceptionHandlers = array_merge($route->getExceptionHandlers(), $this->exceptionHandlers);
}
}
}
}
if(count($this->backStack)) {
$backStack = $this->backStack;
$this->backStack = array();
// Route any routes added to the backstack
$this->processRoutes($backStack, $settings, $prefixes, $route);
}
$prefixes = [];
}
}
public function routeRequest($rewrite = false) {
$this->loadedRoute = null;
$routeNotAllowed = false;
try {
// Initialize boot-managers
if(count($this->bootManagers)) {
/* @var $manager RouterBootManager */
foreach($this->bootManagers as $manager) {
$this->request = $manager->boot($this->request);
if(!($this->request instanceof Request)) {
throw new RouterException('Custom router bootmanager "'. get_class($manager) .'" must return instance of Request.');
}
}
}
if($rewrite === false) {
// Loop through each route-request
$this->processRoutes($this->routes);
if($this->csrfVerifier !== null) {
// Verify csrf token for request
$this->csrfVerifier->handle($this->request);
}
$this->originalUrl = $this->request->getUri();
}
/* @var $route RouterEntry */
for ($i = 0; $i < count($this->controllerUrlMap); $i++) {
$route = $this->controllerUrlMap[$i];
if ($route->matchRoute($this->request)) {
if (!in_array($this->request->getMethod(), $route->getRequestMethods())) {
$routeNotAllowed = true;
continue;
}
$this->loadedRoute = $route;
$this->loadedRoute->loadMiddleware($this->request, $this->loadedRoute);
if($this->request->getUri() !== $this->originalUrl && !in_array($this->request->getUri(), $this->routeRewrites)) {
$this->routeRewrites[] = $this->request->getUri();
$this->routeRequest(true);
return;
}
$routeNotAllowed = false;
$this->request->setUri($this->originalUrl);
$this->loadedRoute->renderRoute($this->request);
break;
}
}
} catch(\Exception $e) {
$this->handleException($e);
}
if($routeNotAllowed) {
$this->handleException(new RouterException('Route or method not allowed', 403));
}
if($this->loadedRoute === null) {
$this->handleException(new RouterException(sprintf('Route not found: %s', $this->request->getUri()), 404));
}
}
protected function handleException(\Exception $e) {
/* @var $handler IExceptionHandler */
foreach ($this->exceptionHandlers as $handler) {
$handler = new $handler();
if (!($handler instanceof IExceptionHandler)) {
throw new RouterException('Exception handler must implement the IExceptionHandler interface.');
}
$request = $handler->handleError($this->request, $this->loadedRoute, $e);
if($request !== null && $request->getUri() !== $this->originalUrl && !in_array($request->getUri(), $this->routeRewrites)) {
$this->routeRewrites[] = $request->getUri();
$this->routeRequest(true);
return;
}
}
throw $e;
}
/**
* Get default namespace
* @return string
*/
public function getDefaultNamespace(){
return $this->defaultNamespace;
}
/**
* Set the main default namespace that all routes will inherit
* @param string $defaultNamespace
* @return static
*/
public function setDefaultNamespace($defaultNamespace) {
$this->defaultNamespace = $defaultNamespace;
return $this;
}
/**
* Get bootmanagers
* @return array
*/
public function getBootManagers() {
return $this->bootManagers;
}
/**
* Set bootmanagers
* @param array $bootManagers
*/
public function setBootManagers(array $bootManagers) {
$this->bootManagers = $bootManagers;
}
/**
* Add bootmanager
* @param RouterBootManager $bootManager
*/
public function addBootManager(RouterBootManager $bootManager) {
$this->bootManagers[] = $bootManager;
}
/**
* @return array
*/
public function getRoutes(){
return $this->routes;
}
/**
* Get current request
*
* @return Request
*/
public function getRequest() {
return $this->request;
}
/**
* Get response
* @return Response
*/
public function getResponse() {
return $this->response;
}
/**
* Get csrf verifier class
* @return BaseCsrfVerifier
*/
public function getCsrfVerifier() {
return $this->csrfVerifier;
}
/**
* Set csrf verifier class
*
* @param BaseCsrfVerifier $csrfVerifier
* @return static
*/
public function setCsrfVerifier(BaseCsrfVerifier $csrfVerifier) {
$this->csrfVerifier = $csrfVerifier;
return $this;
}
public function arrayToParams(array $getParams = null, $includeEmpty = true) {
if(is_array($getParams) && count($getParams)) {
if ($includeEmpty === false) {
$getParams = array_filter($getParams, function ($item) {
return (!empty($item));
});
}
return '?' . http_build_query($getParams);
}
return '';
}
protected function processUrl(RouterRoute $route, $method = null, $parameters = null, $getParams = null) {
$domain = '';
$parent = $route->getParent();
if($parent !== null && $parent instanceof RouterGroup && count($parent->getDomains())) {
$domain = $parent->getDomains();
$domain = '//' . $domain[0];
}
$url = $domain . '/' . trim($route->getUrl(), '/');
if($route instanceof IControllerRoute && $method !== null) {
$url .= $method;
if(count($parameters)) {
$url .= join('/', $parameters);
}
} else {
if($parameters !== null && is_array($parameters)) {
$params = array_merge($route->getParameters(), $parameters);
} else {
$params = $route->getParameters();
}
$otherParams = array();
$i = 0;
foreach($params as $param => $value) {
$value = (isset($parameters[$param])) ? $parameters[$param] : $value;
if(stripos($url, '{' . $param. '}') !== false || stripos($url, '{' . $param . '?}') !== false) {
$url = str_ireplace(array('{' . $param . '}', '{' . $param . '?}'), $value, $url);
} else {
$otherParams[$param] = $value;
}
$i++;
}
$url = rtrim($url, '/') . '/' . join('/', $otherParams);
}
$url = rtrim($url, '/') . '/';
if($getParams !== null) {
$url .= $this->arrayToParams($getParams);
}
return $url;
}
public function getRoute($controller = null, $parameters = null, $getParams = null) {
if($parameters !== null && !is_array($parameters)) {
throw new \InvalidArgumentException('Invalid type for parameter. Must be array or null');
}
if($getParams !== null && !is_array($getParams)) {
throw new \InvalidArgumentException('Invalid type for getParams. Must be array or null');
}
// Return current route if no options has been specified
if($controller === null && $parameters === null) {
$getParams = ($getParams !== null && is_array($getParams)) ? array_merge($_GET, $getParams) : $_GET;
$url = parse_url($this->request->getUri(), PHP_URL_PATH);
if($getParams !== null) {
$url .= $this->arrayToParams($getParams);
}
return $url;
}
if($controller === null && $this->loadedRoute !== null) {
return $this->processUrl($this->loadedRoute, $this->loadedRoute->getMethod(), $parameters, $getParams);
}
$c = '';
$method = null;
$max = count($this->controllerUrlMap);
/* @var $route RouterRoute */
for($i = 0; $i < $max; $i++) {
$route = $this->controllerUrlMap[$i];
// Check an alias exist, if the matches - use it
if($route instanceof LoadableRoute) {
// Check for alias
if ($route->hasAlias($controller)) {
return $this->processUrl($route, $route->getMethod(), $parameters, $getParams);
}
// Use controller name
if($route instanceof RouterController) {
$c = $route->getController();
} else {
// Use callback if it's not a function
if (stripos($route->getCallback(), '@') !== false && !is_callable($route->getCallback())) {
$c = $route->getCallback();
}
}
}
if($c === $controller || strpos($c, $controller) === 0) {
return $this->processUrl($route, $route->getMethod(), $parameters, $getParams);
}
}
$c = '';
// No match has yet been found, let's try to guess what url that should be returned
for($i = 0; $i < $max; $i++) {
$route = $this->controllerUrlMap[$i];
if($route instanceof IControllerRoute) {
$c = $route->getController();
} else if(!is_callable($route->getCallback()) && stripos($route->getCallback(), '@') !== false) {
$c = $route->getClass();
}
if(stripos($controller, '@') !== false) {
$tmp = explode('@', $controller);
$controller = $tmp[0];
$method = $tmp[1];
}
if($controller === $c) {
return $this->processUrl($route, $method, $parameters, $getParams);
}
}
$controller = ($controller === null) ? '/' : $controller;
$url = array($controller);
if($parameters !== null && is_array($parameters) && count($parameters)) {
$url = array_merge($url, $parameters);
}
$url = '/' . trim(join('/', $url), '/') . '/';
if($getParams !== null) {
$url .= $this->arrayToParams($getParams);
}
return $url;
}
/**
* Get current router instance
* @return static
*/
public static function getInstance() {
if(static::$instance === null) {
static::$instance = new static();
}
return static::$instance;
}
}
@@ -1,10 +0,0 @@
<?php
namespace Pecee\SimpleRouter;
use Pecee\Http\Request;
abstract class RouterBootManager {
abstract public function boot(Request $request);
}
-103
View File
@@ -1,103 +0,0 @@
<?php
namespace Pecee\SimpleRouter;
use Pecee\Exception\RouterException;
use Pecee\Http\Request;
class RouterController extends LoadableRoute implements IControllerRoute {
const DEFAULT_METHOD = 'index';
protected $controller;
protected $method;
public function __construct($url, $controller) {
$this->setUrl($url);
$this->controller = $controller;
}
public function renderRoute(Request $request) {
if($this->getCallback() !== null && is_callable($this->getCallback())) {
// When the callback is a function
call_user_func_array($this->getCallback(), $this->getParameters());
} else {
// When the callback is a method
$controller = explode('@', $this->getCallback());
$className = $this->getNamespace() . '\\' . $controller[0];
$class = $this->loadClass($className);
$method = $request->getMethod() . ucfirst($controller[1]);
if (!method_exists($class, $method)) {
throw new RouterException(sprintf('Method %s does not exist in class %s', $method, $className), 404);
}
call_user_func_array(array($class, $method), $this->getParameters());
return $class;
}
return null;
}
public function matchRoute(Request $request) {
$url = parse_url(urldecode($request->getUri()), PHP_URL_PATH);
$url = rtrim($url, '/') . '/';
if(strtolower($url) == strtolower($this->url) || stripos($url, $this->url) === 0) {
$strippedUrl = trim(str_ireplace($this->url, '/', $url), '/');
$path = explode('/', $strippedUrl);
if(count($path)) {
$method = (!isset($path[0]) || trim($path[0]) === '') ? static::DEFAULT_METHOD : $path[0];
$this->method = $method;
array_shift($path);
$this->parameters = $path;
// Set callback
$this->setCallback($this->controller . '@' . $this->method);
return true;
}
}
return null;
}
/**
* @return string
*/
public function getController() {
return $this->controller;
}
/**
* @param string $controller
* @return static
*/
public function setController($controller) {
$this->controller = $controller;
return $this;
}
/**
* @return string
*/
public function getMethod() {
return $this->method;
}
/**
* @param string $method
* @return static
*/
public function setMethod($method) {
$this->method = $method;
return $this;
}
}
-391
View File
@@ -1,391 +0,0 @@
<?php
namespace Pecee\SimpleRouter;
use Pecee\Exception\RouterException;
use Pecee\Http\Middleware\IMiddleware;
use Pecee\Http\Request;
abstract class RouterEntry {
const REQUEST_TYPE_GET = 'get';
const REQUEST_TYPE_POST = 'post';
const REQUEST_TYPE_PUT = 'put';
const REQUEST_TYPE_PATCH = 'patch';
const REQUEST_TYPE_OPTIONS = 'options';
const REQUEST_TYPE_DELETE = 'delete';
public static $allowedRequestTypes = [
self::REQUEST_TYPE_GET,
self::REQUEST_TYPE_POST,
self::REQUEST_TYPE_PUT,
self::REQUEST_TYPE_PATCH,
self::REQUEST_TYPE_OPTIONS,
self::REQUEST_TYPE_DELETE,
];
protected $parent;
protected $callback;
protected $namespace;
protected $regex;
protected $requestMethods = array();
protected $where = array();
protected $parameters = array();
protected $middlewares = array();
protected function loadClass($name) {
if(!class_exists($name)) {
throw new RouterException(sprintf('Class %s does not exist', $name));
}
return new $name();
}
protected function parseParameters($route, $url, $parameterRegex = '[\w]+') {
$parameterNames = array();
$regex = '';
$lastCharacter = '';
$isParameter = false;
$parameter = '';
$routeLength = strlen($route);
for($i = 0; $i < $routeLength; $i++) {
$character = $route[$i];
if($character === '{') {
// Remove "/" and "\" from regex
if(substr($regex, strlen($regex)-1) === '/') {
$regex = substr($regex, 0, strlen($regex) - 2);
}
$isParameter = true;
} elseif($isParameter && $character === '}') {
$required = true;
// Check for optional parameter
// Use custom parameter regex if it exists
if(is_array($this->where) && isset($this->where[$parameter])) {
$parameterRegex = $this->where[$parameter];
}
if($lastCharacter === '?') {
$parameter = substr($parameter, 0, strlen($parameter)-1);
$regex .= '(?:\/?(?P<' . $parameter . '>'. $parameterRegex .')[^\/]?)?';
$required = false;
} else {
$regex .= '\/?(?P<' . $parameter . '>'. $parameterRegex .')[^\/]?';
}
$parameterNames[] = [
'name' => $parameter,
'required' => $required
];
$parameter = '';
$isParameter = false;
} elseif($isParameter) {
$parameter .= $character;
} elseif($character === '/') {
$regex .= '\\' . $character;
} else {
$regex .= str_replace('.', '\\.', $character);
}
$lastCharacter = $character;
}
$parameterValues = array();
if(preg_match('/^'.$regex.'\/?$/is', $url, $parameterValues)) {
$parameters = array();
$max = count($parameterNames);
for($i = 0; $i < $max; $i++) {
$name = $parameterNames[$i];
$parameterValue = isset($parameterValues[$name['name']]) ? $parameterValues[$name['name']] : null;
if($name['required'] && $parameterValue === null) {
throw new RouterException('Missing required parameter ' . $name['name'], 404);
}
if(!$name['required'] && $parameterValue === null) {
continue;
}
$parameters[$name['name']] = $parameterValue;
}
return $parameters;
}
return null;
}
public function loadMiddleware(Request $request, RouterEntry &$route) {
if(count($this->getMiddlewares())) {
foreach($this->getMiddlewares() as $middleware) {
$middleware = $this->loadClass($middleware);
if (!($middleware instanceof IMiddleware)) {
throw new RouterException($middleware . ' must be instance of Middleware');
}
/* @var $class IMiddleware */
$middleware->handle($request, $route);
}
}
}
public function renderRoute(Request $request) {
if($this->getCallback() !== null && is_callable($this->getCallback())) {
// When the callback is a function
call_user_func_array($this->getCallback(), $this->getParameters());
} else {
// When the callback is a method
$controller = explode('@', $this->getCallback());
$className = $this->getNamespace() . '\\' . $controller[0];
$class = $this->loadClass($className);
$method = $controller[1];
if (!method_exists($class, $method)) {
throw new RouterException(sprintf('Method %s does not exist in class %s', $method, $className), 404);
}
$parameters = array_filter($this->getParameters(), function($var){
return ($var !== null);
});
call_user_func_array(array($class, $method), $parameters);
return $class;
}
return null;
}
/**
* Set allowed request methods
*
* @param array $methods
* @return static $this
*/
public function setRequestMethods(array $methods) {
$this->requestMethods = $methods;
return $this;
}
/**
* Get allowed request methods
*
* @return array
*/
public function getRequestMethods() {
return $this->requestMethods;
}
/**
* @return RouterEntry
*/
public function getParent() {
return $this->parent;
}
/**
* Set parent route
* @param RouterEntry $parent
* @return static $this
*/
public function setParent(RouterEntry $parent) {
$this->parent = $parent;
return $this;
}
/**
* @param string $callback
* @return static
*/
public function setCallback($callback) {
$this->callback = $callback;
return $this;
}
/**
* @return mixed
*/
public function getCallback() {
return $this->callback;
}
public function getMethod() {
if(strpos($this->callback, '@') !== false) {
$tmp = explode('@', $this->callback);
return $tmp[1];
}
return null;
}
public function getClass() {
if(strpos($this->callback, '@') !== false) {
$tmp = explode('@', $this->callback);
return $tmp[0];
}
return null;
}
public function setMethod($method) {
$this->callback = sprintf('%s@%s', $this->getClass(), $method);
return $this;
}
public function setClass($class) {
$this->callback = sprintf('%s@%s', $class, $this->getMethod());
return $this;
}
/**
* @param string $middleware
* @return static
*/
public function setMiddleware($middleware) {
$this->middlewares[] = $middleware;
return $this;
}
public function setMiddlewares(array $middlewares) {
$this->middlewares = $middlewares;
return $this;
}
/**
* @param string $namespace
* @return static
*/
public function setNamespace($namespace) {
$this->namespace = $namespace;
return $this;
}
/**
* @return string|array
*/
public function getMiddlewares() {
return $this->middlewares;
}
/**
* @return string
*/
public function getNamespace() {
return $this->namespace;
}
/**
* @return array
*/
public function getParameters(){
return $this->parameters;
}
/**
* @param mixed $parameters
* @return static
*/
public function setParameters($parameters) {
$this->parameters = $parameters;
return $this;
}
/**
* Add regular expression parameter match
*
* @param array $options
* @return static
*/
public function where(array $options) {
$this->where = $options;
return $this;
}
/**
* Add regular expression match for url
*
* @param string $regex
* @return static
*/
public function match($regex) {
$this->regex = $regex;
return $this;
}
/**
* Get arguments that can be inherited by child routes.
*
* @return array
*/
public function getMergeableData() {
$output = array();
if($this->namespace !== null) {
$output['namespace'] = $this->namespace;
}
if(count($this->middlewares)) {
$output['middleware'] = $this->middlewares;
}
if(count($this->where)) {
$output['where'] = $this->where;
}
if(count($this->requestMethods)) {
$output['method'] = $this->requestMethods;
}
if(count($this->parameters)) {
$output['parameters'] = $this->parameters;
}
return $output;
}
/**
* Set arguments/data by array
*
* @param array $settings
* @return static
*/
public function setData(array $settings) {
if (isset($settings['namespace']) && $this->namespace === null) {
$this->setNamespace($settings['namespace']);
}
// Push middleware if multiple
if (isset($settings['middleware'])) {
$this->middlewares = array_merge((array)$settings['middleware'], $this->middlewares);
}
if(isset($settings['method'])) {
$this->setRequestMethods((array)$settings['method']);
}
if(isset($settings['where'])) {
$this->where($settings['where']);
}
if(isset($settings['parameters'])) {
$this->setParameters($settings['parameters']);
}
return $this;
}
abstract function matchRoute(Request $request);
}
-93
View File
@@ -1,93 +0,0 @@
<?php
namespace Pecee\SimpleRouter;
use Pecee\Http\Request;
class RouterGroup extends RouterEntry {
protected $prefix;
protected $domains = array();
protected $exceptionHandlers = array();
public function matchDomain(Request $request) {
if(count($this->domains)) {
for($i = 0; $i < count($this->domains); $i++) {
$domain = $this->domains[$i];
$parameters = $this->parseParameters($domain, $request->getHost(), '.*');
if($parameters !== null) {
$this->parameters = $parameters;
return true;
}
}
return false;
}
return true;
}
public function matchRoute(Request $request) {
// Skip if prefix doesn't match
if($this->prefix !== null && stripos($request->getUri(), $this->prefix) === false) {
return false;
}
return $this->matchDomain($request);
}
public function setExceptionHandlers(array $handlers) {
$this->exceptionHandlers = $handlers;
return $this;
}
public function getExceptionHandlers() {
return $this->exceptionHandlers;
}
public function getDomains() {
return $this->domains;
}
public function setDomains(array $domains) {
$this->domains = $domains;
return $this;
}
/**
* @param string $prefix
* @return static
*/
public function setPrefix($prefix) {
$this->prefix = '/' . trim($prefix, '/');
return $this;
}
/**
* @return string
*/
public function getPrefix() {
return $this->prefix;
}
public function setData(array $settings) {
if(isset($settings['prefix'])) {
$this->setPrefix($settings['prefix']);
}
if(isset($settings['exceptionHandler'])) {
$this->setExceptionHandlers((array)$settings['exceptionHandler']);
}
if(isset($settings['domain'])) {
$this->setDomains((array)$settings['domain']);
}
return parent::setData($settings);
}
}
-115
View File
@@ -1,115 +0,0 @@
<?php
namespace Pecee\SimpleRouter;
use Pecee\Exception\RouterException;
use Pecee\Http\Request;
class RouterResource extends LoadableRoute implements IControllerRoute {
protected $controller;
public function __construct($url, $controller) {
$this->setUrl($url);
$this->controller = $controller;
}
public function renderRoute(Request $request) {
if($this->getCallback() !== null && is_callable($this->getCallback())) {
// When the callback is a function
call_user_func_array($this->getCallback(), $this->getParameters());
} else {
// When the callback is a method
$controller = explode('@', $this->getCallback());
$className = $this->getNamespace() . '\\' . $controller[0];
$class = $this->loadClass($className);
$method = strtolower($controller[1]);
if (!method_exists($class, $method)) {
throw new RouterException(sprintf('Method %s does not exist in class %s', $method, $className), 404);
}
call_user_func_array(array($class, $method), $this->getParameters());
return $class;
}
return null;
}
protected function call($method, $parameters) {
$this->setCallback($this->controller . '@' . $method);
$this->parameters = $parameters;
return true;
}
public function matchRoute(Request $request) {
$url = parse_url(urldecode($request->getUri()), PHP_URL_PATH);
$url = rtrim($url, '/') . '/';
$route = rtrim($this->url, '/') . '/{id?}/{action?}';
$parameters = $this->parseParameters($route, $url);
if($parameters !== null) {
if(is_array($parameters)) {
$parameters = array_merge($this->parameters, $parameters);
}
$action = isset($parameters['action']) ? $parameters['action'] : null;
unset($parameters['action']);
// Delete
if($request->getMethod() === static::REQUEST_TYPE_DELETE && $request->getMethod() === static::REQUEST_TYPE_POST) {
return $this->call('destroy', $parameters);
}
// Update
if(in_array($request->getMethod(), array(static::REQUEST_TYPE_PATCH, static::REQUEST_TYPE_PUT)) && $request->getMethod() === static::REQUEST_TYPE_POST) {
return $this->call('update', $parameters);
}
// Edit
if(isset($action) && strtolower($action) === 'edit' && $request->getMethod() === static::REQUEST_TYPE_GET) {
return $this->call('edit', $parameters);
}
// Create
if(strtolower($action) === 'create' && $request->getMethod() === static::REQUEST_TYPE_GET) {
return $this->call('create', $parameters);
}
// Save
if($request->getMethod() === static::REQUEST_TYPE_POST) {
return $this->call('store', $parameters);
}
// Show
if(isset($parameters['id']) && $request->getMethod() === static::REQUEST_TYPE_GET) {
return $this->call('show', $parameters);
}
// Index
return $this->call('index', $parameters);
}
return null;
}
/**
* @return string
*/
public function getController() {
return $this->controller;
}
/**
* @param string $controller
* @return static
*/
public function setController($controller) {
$this->controller = $controller;
return $this;
}
}
-42
View File
@@ -1,42 +0,0 @@
<?php
namespace Pecee\SimpleRouter;
use Pecee\Http\Request;
class RouterRoute extends LoadableRoute {
public function __construct($url, $callback) {
$this->setUrl($url);
$this->setCallback($callback);
}
public function matchRoute(Request $request) {
$url = parse_url(urldecode($request->getUri()), PHP_URL_PATH);
$url = rtrim($url, '/') . '/';
// Match on custom defined regular expression
if($this->regex !== null) {
$parameters = array();
if(preg_match('/(' . $this->regex . ')/is', $request->getHost() . $url, $parameters)) {
$this->parameters = (!is_array($parameters[0]) ? array($parameters[0]) : $parameters[0]);
return true;
}
return null;
}
// Make regular expression based on route
$route = rtrim($this->url, '/') . '/';
$parameters = $this->parseParameters($route, $url);
if($parameters !== null) {
$this->parameters = array_merge($this->parameters, $parameters);
return true;
}
return null;
}
}
+327 -111
View File
@@ -1,158 +1,374 @@
<?php <?php
/** /**
* --------------------------- * ---------------------------
* Router helper class * Router helper class
* --------------------------- * ---------------------------
* This class is added so calls can be made statically like Router::get() making the code look more pretty. *
* This class is added so calls can be made statically like Router::get() making the code look pretty.
* It also adds some extra functionality like default-namespace.
*/ */
namespace Pecee\SimpleRouter; namespace Pecee\SimpleRouter;
use Pecee\Http\Middleware\BaseCsrfVerifier; use Pecee\Http\Middleware\BaseCsrfVerifier;
use Pecee\Http\Response;
use Pecee\SimpleRouter\Exceptions\HttpException;
use Pecee\SimpleRouter\Exceptions\NotFoundHttpException;
use Pecee\SimpleRouter\Route\IRoute;
use Pecee\SimpleRouter\Route\RouteController;
use Pecee\SimpleRouter\Route\RouteGroup;
use Pecee\SimpleRouter\Route\RouteResource;
use Pecee\SimpleRouter\Route\RouteUrl;
class SimpleRouter { class SimpleRouter
{
/**
* Default namespace added to all routes
* @var string
*/
protected static $defaultNamespace;
/** /**
* Start/route request * The response object
* @throws \Pecee\Exception\RouterException * @var Response
*/ */
public static function start() { protected static $response;
RouterBase::getInstance()->routeRequest();
}
/** /**
* Set default namespace for all routes * Start/route request
* @param string $defaultNamespace *
*/ * @throws HttpException
public static function setDefaultNamespace($defaultNamespace) { * @throws NotFoundHttpException
RouterBase::getInstance()->setDefaultNamespace($defaultNamespace); */
} public static function start()
{
static::router()->routeRequest();
}
/** /**
* Set base csrf verifier * Set default namespace which will be prepended to all routes.
* @param BaseCsrfVerifier $baseCsrfVerifier *
*/ * @param string $defaultNamespace
public static function csrfVerifier(BaseCsrfVerifier $baseCsrfVerifier) { */
RouterBase::getInstance()->setCsrfVerifier($baseCsrfVerifier); public static function setDefaultNamespace($defaultNamespace)
} {
static::$defaultNamespace = $defaultNamespace;
}
public static function addBootManager(RouterBootManager $bootManager) { /**
RouterBase::getInstance()->addBootManager($bootManager); * Base CSRF verifier
} *
* @param BaseCsrfVerifier $baseCsrfVerifier
*/
public static function csrfVerifier(BaseCsrfVerifier $baseCsrfVerifier)
{
static::router()->setCsrfVerifier($baseCsrfVerifier);
}
public static function get($url, $callback, array $settings = null) { /**
return static::match(['get'], $url, $callback, $settings); * Boot managers allows you to alter the routes before the routing occurs.
} * Perfect if you want to load pretty-urls from a file or database.
*
* @param IRouterBootManager $bootManager
*/
public static function addBootManager(IRouterBootManager $bootManager)
{
static::router()->addBootManager($bootManager);
}
public static function post($url, $callback, array $settings = null) { /**
return static::match(['post'], $url, $callback, $settings); * Route the given url to your callback on GET request method.
} *
* @param string $url
* @param string|\Closure $callback
* @param array|null $settings
* @return RouteUrl
*/
public static function get($url, $callback, array $settings = null)
{
return static::match(['get'], $url, $callback, $settings);
}
public static function put($url, $callback, array $settings = null) { /**
return static::match(['put'], $url, $callback, $settings); * Route the given url to your callback on POST request method.
} *
* @param string $url
* @param string|\Closure $callback
* @param array|null $settings
* @return RouteUrl
*/
public static function post($url, $callback, array $settings = null)
{
return static::match(['post'], $url, $callback, $settings);
}
public static function patch($url, $callback, array $settings = null) { /**
return static::match(['patch'], $url, $callback, $settings); * Route the given url to your callback on PUT request method.
} *
* @param string $url
* @param string|\Closure $callback
* @param array|null $settings
* @return RouteUrl
*/
public static function put($url, $callback, array $settings = null)
{
return static::match(['put'], $url, $callback, $settings);
}
public static function options($url, $callback, array $settings = null) { /**
return static::match(['options'], $url, $callback, $settings); * Route the given url to your callback on PATCH request method.
} *
* @param string $url
* @param string|\Closure $callback
* @param array|null $settings
* @return RouteUrl
*/
public static function patch($url, $callback, array $settings = null)
{
return static::match(['patch'], $url, $callback, $settings);
}
public static function delete($url, $callback, array $settings = null) { /**
return static::match(['delete'], $url, $callback, $settings); * Route the given url to your callback on OPTIONS request method.
} *
* @param string $url
* @param string|\Closure $callback
* @param array|null $settings
* @return RouteUrl
*/
public static function options($url, $callback, array $settings = null)
{
return static::match(['options'], $url, $callback, $settings);
}
public static function group($settings = array(), $callback) { /**
$group = new RouterGroup(); * Route the given url to your callback on DELETE request method.
$group->setCallback($callback); *
* @param string $url
* @param string|\Closure $callback
* @param array|null $settings
* @return RouteUrl
*/
public static function delete($url, $callback, array $settings = null)
{
return static::match(['delete'], $url, $callback, $settings);
}
if($settings !== null && is_array($settings)) { /**
$group->setData($settings); * Groups allows for encapsulating routes with special settings.
} *
* @param array $settings
* @param \Closure $callback
* @throws \InvalidArgumentException
* @return RouteGroup
*/
public static function group(array $settings = [], \Closure $callback)
{
$group = new RouteGroup();
$group->setCallback($callback);
$group->setSettings($settings);
RouterBase::getInstance()->addRoute($group); if (is_callable($callback) === false) {
throw new \InvalidArgumentException('Invalid callback provided. Only functions or methods supported');
}
return $group; static::router()->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 static::match(['get', 'post'], $url, $callback, $settings);
}
public static function match(array $requestMethods, $url, $callback, array $settings = null) { /**
$route = new RouterRoute($url, $callback); * Alias for the form method
$route->setRequestMethods($requestMethods); *
* @param string $url
* @param callable $callback
* @param array|null $settings
* @see SimpleRouter::form
* @return RouteUrl
*/
public static function basic($url, $callback, array $settings = null)
{
return static::match(['get', 'post'], $url, $callback, $settings);
}
if($settings !== null) { /**
$route->setData($settings); * This type will route the given url to your callback on the provided request methods.
} * Route the given url to your callback on POST and GET request method.
*
* @param string $url
* @param string|\Closure $callback
* @param array|null $settings
* @see SimpleRouter::form
* @return RouteUrl
*/
public static function form($url, $callback, array $settings = null)
{
return static::match(['get', 'post'], $url, $callback, $settings);
}
RouterBase::getInstance()->addRoute($route); /**
* This type will route the given url to your callback on the provided request methods.
*
* @param array $requestMethods
* @param string $url
* @param string|\Closure $callback
* @param array|null $settings
* @return RouteUrl
*/
public static function match(array $requestMethods, $url, $callback, array $settings = null)
{
$route = new RouteUrl($url, $callback);
$route->setRequestMethods($requestMethods);
$route = static::addDefaultNamespace($route);
return $route; if ($settings !== null) {
} $route->setSettings($settings);
}
public static function all($url, $callback, array $settings = null) { static::router()->addRoute($route);
$route = new RouterRoute($url, $callback);
if($settings !== null) { return $route;
$route->setData($settings); }
}
RouterBase::getInstance()->addRoute($route); /**
* This type will route the given url to your callback and allow any type of request method
*
* @param string $url
* @param string|\Closure $callback
* @param array|null $settings
* @return RouteUrl
*/
public static function all($url, $callback, array $settings = null)
{
$route = new RouteUrl($url, $callback);
$route = static::addDefaultNamespace($route);
return $route; if ($settings !== null) {
} $route->setSettings($settings);
}
public static function controller($url, $controller, array $settings = null) { static::router()->addRoute($route);
$route = new RouterController($url, $controller);
if($settings !== null) { return $route;
$route->setData($settings); }
}
RouterBase::getInstance()->addRoute($route); /**
* This route will route request from the given url to the controller.
*
* @param string $url
* @param string $controller
* @param array|null $settings
* @return RouteController
*/
public static function controller($url, $controller, array $settings = null)
{
$route = new RouteController($url, $controller);
$route = static::addDefaultNamespace($route);
return $route; if ($settings !== null) {
} $route->setSettings($settings);
}
public static function resource($url, $controller, array $settings = null) { static::router()->addRoute($route);
$route = new RouterResource($url, $controller);
if($settings !== null) { return $route;
$route->setData($settings); }
}
static::router()->addRoute($route); /**
* This type will route all REST-supported requests to different methods in the provided controller.
*
* @param string $url
* @param string $controller
* @param array|null $settings
* @return RouteResource
*/
public static function resource($url, $controller, array $settings = null)
{
$route = new RouteResource($url, $controller);
return $route; if ($settings !== null) {
} $route->setSettings($settings);
}
public static function getRoute($controller = null, $parameters = null, $getParams = null) { static::router()->addRoute($route);
return static::router()->getRoute($controller, $parameters, $getParams);
}
public static function request() { return $route;
return static::router()->getRequest(); }
}
public static function response() { /**
return static::router()->getResponse(); * Get url for a route by using either name/alias, class or method name.
} *
* The name parameter supports the following values:
* - Route name
* - Controller/resource name (with or without method)
* - Controller class name
*
* When searching for controller/resource by name, you can use this syntax "route.name@method".
* You can also use the same syntax when searching for a specific controller-class "MyController@home".
* If no arguments is specified, it will return the url for the current loaded route.
*
* @param string|null $name
* @param string|array|null $parameters
* @param array|null $getParams
* @return string
*/
public static function getUrl($name = null, $parameters = null, $getParams = [])
{
return static::router()->getUrl($name, $parameters, $getParams);
}
protected static function router() { /**
return RouterBase::getInstance(); * Get the request
} *
* @return \Pecee\Http\Request
*/
public static function request()
{
return static::router()->getRequest();
}
/**
* Get the response object
*
* @return Response
*/
public static function response()
{
if (static::$response === null) {
static::$response = new Response(static::request());
}
return static::$response;
}
/**
* Returns the router instance
*
* @return Router
*/
public static function router()
{
return Router::getInstance();
}
/**
* Prepends the default namespace to all new routes added.
*
* @param IRoute $route
* @return IRoute
*/
protected static function addDefaultNamespace(IRoute $route)
{
if (static::$defaultNamespace !== null) {
$namespace = static::$defaultNamespace;
if ($route->getNamespace() !== null) {
$namespace .= '\\' . $route->getNamespace();
}
$route->setDefaultNamespace($namespace);
}
return $route;
}
} }
+18 -11
View File
@@ -1,18 +1,25 @@
<?php <?php
class DummyController { class DummyController
{
public function start()
{
echo static::class . '@' . 'start() OK';
}
public function start() { public function param($params = null)
echo static::class . '@' .'start() OK'; {
} $params = func_get_args();
echo 'Params: ' . join(', ', $params);
}
public function param($params = null) { public function notFound()
$params = func_get_args(); {
echo 'Params: ' . join(', ', $params); echo 'not found';
} }
public function notFound() { public function silent() {
echo 'not found';
} }
} }
+6 -6
View File
@@ -1,14 +1,14 @@
<?php <?php
require_once 'Exceptions/MiddlewareLoadedException.php'; require_once 'Exceptions/MiddlewareLoadedException.php';
use Pecee\Http\Middleware\IMiddleware; use Pecee\Http\Middleware\IMiddleware;
use Pecee\Http\Request; use Pecee\Http\Request;
class DummyMiddleware implements IMiddleware { class DummyMiddleware implements IMiddleware
{
public function handle(Request $request, \Pecee\SimpleRouter\RouterEntry &$route = null) { public function handle(Request $request, \Pecee\SimpleRouter\Route\ILoadableRoute &$route)
throw new MiddlewareLoadedException('Middleware loaded!'); {
} throw new MiddlewareLoadedException('Middleware loaded!');
}
} }
@@ -1,2 +1,4 @@
<?php <?php
class MiddlewareLoadedException extends \Exception {} class MiddlewareLoadedException extends \Exception
{
}
+6 -4
View File
@@ -1,8 +1,10 @@
<?php <?php
class ExceptionHandler implements \Pecee\Handler\IExceptionHandler {
public function handleError(\Pecee\Http\Request $request, \Pecee\SimpleRouter\RouterEntry &$route = null, \Exception $error){ class ExceptionHandler implements \Pecee\Handlers\IExceptionHandler
throw $error; {
} public function handleError(\Pecee\Http\Request $request, \Pecee\SimpleRouter\Route\ILoadableRoute &$route = null, \Exception $error)
{
throw $error;
}
} }
+66 -52
View File
@@ -3,79 +3,93 @@
require_once 'Dummy/DummyMiddleware.php'; require_once 'Dummy/DummyMiddleware.php';
require_once 'Dummy/DummyController.php'; require_once 'Dummy/DummyController.php';
class GroupTest extends PHPUnit_Framework_TestCase { use Pecee\SimpleRouter\SimpleRouter as SimpleRouter;
protected $result; class GroupTest extends PHPUnit_Framework_TestCase
{
protected $result;
public function testGroupLoad() { public function testGroupLoad()
{
$this->result = false;
$this->result = false; SimpleRouter::group(['prefix' => '/group'], function () {
$this->result = true;
});
\Pecee\SimpleRouter\SimpleRouter::group(['prefix' => '/group'], function() { try {
$this->result = true; SimpleRouter::start();
}); } catch (Exception $e) {
// ignore RouteNotFound exception
}
try { $this->assertTrue($this->result);
\Pecee\SimpleRouter\SimpleRouter::start(); }
} catch(Exception $e) {
// ignore RouteNotFound exception
}
$this->assertTrue($this->result); public function testNestedGroup()
} {
public function testNestedGroup() { SimpleRouter::router()->reset();
SimpleRouter::request()->setUri('/api/v1/test');
SimpleRouter::request()->setMethod('get');
\Pecee\SimpleRouter\RouterBase::getInstance()->reset(); SimpleRouter::group(['prefix' => '/api'], function () {
\Pecee\SimpleRouter\SimpleRouter::request()->setUri('/api/v1/test');
\Pecee\SimpleRouter\SimpleRouter::request()->setMethod('get');
\Pecee\SimpleRouter\SimpleRouter::group(['prefix' => '/api'], function() { SimpleRouter::group(['prefix' => '/v1'], function () {
\Pecee\SimpleRouter\SimpleRouter::group(['prefix' => '/v1'], function() { SimpleRouter::get('/test', 'DummyController@start');
\Pecee\SimpleRouter\SimpleRouter::get('/test', 'DummyController@start'); });
});
});
\Pecee\SimpleRouter\SimpleRouter::start(); });
}
public function testManyRoutes() { SimpleRouter::start();
}
\Pecee\SimpleRouter\RouterBase::getInstance()->reset(); public function testManyRoutes()
\Pecee\SimpleRouter\SimpleRouter::request()->setUri('/my/match'); {
\Pecee\SimpleRouter\SimpleRouter::request()->setMethod('get');
\Pecee\SimpleRouter\SimpleRouter::group(['prefix' => '/api'], function() { SimpleRouter::router()->reset();
\Pecee\SimpleRouter\SimpleRouter::group(['prefix' => '/v1'], function() { SimpleRouter::request()->setUri('/my/match');
\Pecee\SimpleRouter\SimpleRouter::get('/test', 'DummyController@start'); SimpleRouter::request()->setMethod('get');
});
});
\Pecee\SimpleRouter\SimpleRouter::get('/my/match', 'DummyController@start'); SimpleRouter::group(['prefix' => '/api'], function () {
\Pecee\SimpleRouter\SimpleRouter::group(['prefix' => '/service'], function() { SimpleRouter::group(['prefix' => '/v1'], function () {
\Pecee\SimpleRouter\SimpleRouter::group(['prefix' => '/v1'], function() { SimpleRouter::get('/test', 'DummyController@start');
\Pecee\SimpleRouter\SimpleRouter::get('/no-match', 'DummyController@start'); });
});
});
\Pecee\SimpleRouter\SimpleRouter::start(); });
}
public function testUrls() { SimpleRouter::get('/my/match', 'DummyController@start');
\Pecee\SimpleRouter\RouterBase::getInstance()->reset(); SimpleRouter::group(['prefix' => '/service'], function () {
\Pecee\SimpleRouter\SimpleRouter::request()->setUri('/my/fancy/url/1');
\Pecee\SimpleRouter\SimpleRouter::request()->setMethod('get');
\Pecee\SimpleRouter\SimpleRouter::get('/my/fancy/url/1', 'DummyController@start', ['as' => 'fancy1']); SimpleRouter::group(['prefix' => '/v1'], function () {
\Pecee\SimpleRouter\SimpleRouter::get('/my/fancy/url/2', 'DummyController@start')->setAlias('fancy2'); SimpleRouter::get('/no-match', 'DummyController@start');
});
\Pecee\SimpleRouter\SimpleRouter::start(); });
$this->assertTrue((\Pecee\SimpleRouter\SimpleRouter::getRoute('fancy1') === '/my/fancy/url/1/')); SimpleRouter::start();
$this->assertTrue((\Pecee\SimpleRouter\SimpleRouter::getRoute('fancy2') === '/my/fancy/url/2/')); }
} public function testUrls()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setUri('/my/fancy/url/1');
SimpleRouter::request()->setMethod('get');
// Test array name
SimpleRouter::get('/my/fancy/url/1', 'DummyController@start', ['as' => 'fancy1']);
// Test method name
SimpleRouter::get('/my/fancy/url/2', 'DummyController@start')->setName('fancy2');
SimpleRouter::start();
$this->assertEquals('/my/fancy/url/1/', SimpleRouter::getUrl('fancy1'));
$this->assertEquals('/my/fancy/url/2/', SimpleRouter::getUrl('fancy2'));
}
} }
+19 -18
View File
@@ -4,28 +4,29 @@ require_once 'Dummy/DummyMiddleware.php';
require_once 'Dummy/DummyController.php'; require_once 'Dummy/DummyController.php';
require_once 'Dummy/Handler/ExceptionHandler.php'; require_once 'Dummy/Handler/ExceptionHandler.php';
class MiddlewareTest extends PHPUnit_Framework_TestCase { use Pecee\SimpleRouter\SimpleRouter as SimpleRouter;
public function testMiddlewareFound() { class MiddlewareTest extends PHPUnit_Framework_TestCase
{
public function testMiddlewareFound()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setMethod('get');
SimpleRouter::request()->setUri('/my/test/url');
\Pecee\SimpleRouter\RouterBase::getInstance()->reset(); SimpleRouter::group(['exceptionHandler' => 'ExceptionHandler'], function () {
\Pecee\SimpleRouter\SimpleRouter::request()->setMethod('get'); SimpleRouter::get('/my/test/url', 'DummyController@start', ['middleware' => 'DummyMiddleware']);
\Pecee\SimpleRouter\SimpleRouter::request()->setUri('/my/test/url'); });
\Pecee\SimpleRouter\SimpleRouter::group(['exceptionHandler' => 'ExceptionHandler'], function() { $found = false;
\Pecee\SimpleRouter\SimpleRouter::get('/my/test/url', 'DummyController@start', ['middleware' => 'DummyMiddleware']);
});
$found = false; try {
SimpleRouter::start();
} catch (\Exception $e) {
$found = ($e instanceof MiddlewareLoadedException);
}
try { $this->assertTrue($found);
\Pecee\SimpleRouter\SimpleRouter::start(); }
}catch(\Exception $e) {
$found = ($e instanceof MiddlewareLoadedException);
}
$this->assertTrue($found);
}
} }
+104 -101
View File
@@ -4,135 +4,138 @@ require_once 'Dummy/DummyMiddleware.php';
require_once 'Dummy/DummyController.php'; require_once 'Dummy/DummyController.php';
require_once 'Dummy/Handler/ExceptionHandler.php'; require_once 'Dummy/Handler/ExceptionHandler.php';
class RouterRouteTest extends PHPUnit_Framework_TestCase { use Pecee\SimpleRouter\SimpleRouter as SimpleRouter;
use Pecee\SimpleRouter\Exceptions\NotFoundHttpException as NotFoundHttpException;
protected $result = false; class RouterRouteTest extends PHPUnit_Framework_TestCase
{
protected $result = false;
public function testNotFound() { public function testNotFound()
\Pecee\SimpleRouter\RouterBase::getInstance()->reset(); {
\Pecee\SimpleRouter\SimpleRouter::request()->setMethod('get'); SimpleRouter::router()->reset();
\Pecee\SimpleRouter\SimpleRouter::request()->setUri('/test-param1-param2'); SimpleRouter::request()->setMethod('get');
SimpleRouter::request()->setUri('/test-param1-param2');
\Pecee\SimpleRouter\SimpleRouter::group(['exceptionHandler' => 'ExceptionHandler'], function() { SimpleRouter::group(['exceptionHandler' => 'ExceptionHandler'], function () {
\Pecee\SimpleRouter\SimpleRouter::get('/non-existing-path', 'DummyController@start'); SimpleRouter::get('/non-existing-path', 'DummyController@start');
}); });
$found = false; $found = false;
try { try {
\Pecee\SimpleRouter\SimpleRouter::start(); SimpleRouter::start();
}catch(\Exception $e) { } catch (\Exception $e) {
$found = ($e instanceof \Pecee\Exception\RouterException && $e->getCode() == 404); $found = ($e instanceof NotFoundHttpException && $e->getCode() == 404);
} }
$this->assertTrue($found); $this->assertTrue($found);
}
} public function testGet()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setUri('/my/test/url');
SimpleRouter::request()->setMethod('get');
public function testGet() { SimpleRouter::get('/my/test/url', 'DummyController@start');
\Pecee\SimpleRouter\RouterBase::getInstance()->reset(); SimpleRouter::start();
\Pecee\SimpleRouter\SimpleRouter::request()->setUri('/my/test/url'); }
\Pecee\SimpleRouter\SimpleRouter::request()->setMethod('get');
\Pecee\SimpleRouter\SimpleRouter::get('/my/test/url', 'DummyController@start'); public function testPost()
\Pecee\SimpleRouter\SimpleRouter::start(); {
} SimpleRouter::router()->reset();
SimpleRouter::request()->setUri('/my/test/url');
SimpleRouter::request()->setMethod('post');
public function testPost() { SimpleRouter::post('/my/test/url', 'DummyController@start');
\Pecee\SimpleRouter\RouterBase::getInstance()->reset(); SimpleRouter::start();
\Pecee\SimpleRouter\SimpleRouter::request()->setUri('/my/test/url'); }
\Pecee\SimpleRouter\SimpleRouter::request()->setMethod('post');
\Pecee\SimpleRouter\SimpleRouter::post('/my/test/url', 'DummyController@start'); public function testPut()
\Pecee\SimpleRouter\SimpleRouter::start(); {
} SimpleRouter::router()->reset();
SimpleRouter::request()->setUri('/my/test/url');
SimpleRouter::request()->setMethod('put');
public function testPut() { SimpleRouter::put('/my/test/url', 'DummyController@start');
\Pecee\SimpleRouter\RouterBase::getInstance()->reset(); SimpleRouter::start();
\Pecee\SimpleRouter\SimpleRouter::request()->setUri('/my/test/url'); }
\Pecee\SimpleRouter\SimpleRouter::request()->setMethod('put');
\Pecee\SimpleRouter\SimpleRouter::put('/my/test/url', 'DummyController@start'); public function testDelete()
\Pecee\SimpleRouter\SimpleRouter::start(); {
} SimpleRouter::router()->reset();
SimpleRouter::request()->setUri('/my/test/url');
SimpleRouter::request()->setMethod('delete');
public function testDelete() { SimpleRouter::delete('/my/test/url', 'DummyController@start');
\Pecee\SimpleRouter\RouterBase::getInstance()->reset(); SimpleRouter::start();
\Pecee\SimpleRouter\SimpleRouter::request()->setUri('/my/test/url'); }
\Pecee\SimpleRouter\SimpleRouter::request()->setMethod('delete');
\Pecee\SimpleRouter\SimpleRouter::delete('/my/test/url', 'DummyController@start'); public function testMethodNotAllowed()
\Pecee\SimpleRouter\SimpleRouter::start(); {
SimpleRouter::router()->reset();
SimpleRouter::request()->setUri('/my/test/url');
SimpleRouter::request()->setMethod('post');
} SimpleRouter::get('/my/test/url', 'DummyController@start');
public function testMethodNotAllowed() { try {
\Pecee\SimpleRouter\RouterBase::getInstance()->reset(); SimpleRouter::start();
\Pecee\SimpleRouter\SimpleRouter::request()->setUri('/my/test/url'); } catch (\Exception $e) {
\Pecee\SimpleRouter\SimpleRouter::request()->setMethod('post'); $this->assertEquals(403, $e->getCode());
}
}
\Pecee\SimpleRouter\SimpleRouter::get('/my/test/url', 'DummyController@start'); public function testSimpleParam()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setMethod('get');
SimpleRouter::request()->setUri('/test-param1');
try { SimpleRouter::get('/test-{param1}', 'DummyController@param');
\Pecee\SimpleRouter\SimpleRouter::start(); SimpleRouter::start();
} catch(\Exception $e) { }
$this->assertEquals(403, $e->getCode());
}
} public function testMultiParam()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setMethod('get');
SimpleRouter::request()->setUri('/test-param1-param2');
public function testSimpleParam() { SimpleRouter::get('/test-{param1}-{param2}', 'DummyController@param');
SimpleRouter::start();
}
\Pecee\SimpleRouter\RouterBase::getInstance()->reset(); public function testPathParamRegex()
\Pecee\SimpleRouter\SimpleRouter::request()->setMethod('get'); {
\Pecee\SimpleRouter\SimpleRouter::request()->setUri('/test-param1'); SimpleRouter::router()->reset();
SimpleRouter::request()->setMethod('get');
SimpleRouter::request()->setUri('/test/path/123123');
\Pecee\SimpleRouter\SimpleRouter::get('/test-{param1}', 'DummyController@param'); SimpleRouter::get('/test/path/{myParam}', 'DummyController@param', ['where' => ['myParam' => '([0-9]+)']]);
\Pecee\SimpleRouter\SimpleRouter::start(); SimpleRouter::start();
}
} public function testDomainRoute()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setMethod('get');
SimpleRouter::request()->setUri('/test');
SimpleRouter::request()->setHost('hello.world.com');
public function testMultiParam() { $this->result = false;
\Pecee\SimpleRouter\RouterBase::getInstance()->reset(); SimpleRouter::group(['domain' => '{subdomain}.world.com'], function () {
\Pecee\SimpleRouter\SimpleRouter::request()->setMethod('get'); SimpleRouter::get('/test', function ($subdomain = null) {
\Pecee\SimpleRouter\SimpleRouter::request()->setUri('/test-param1-param2'); $this->result = ($subdomain === 'hello');
});
});
\Pecee\SimpleRouter\SimpleRouter::get('/test-{param1}-{param2}', 'DummyController@param'); SimpleRouter::start();
\Pecee\SimpleRouter\SimpleRouter::start();
} $this->assertTrue($this->result);
public function testPathParamRegex() { }
\Pecee\SimpleRouter\RouterBase::getInstance()->reset();
\Pecee\SimpleRouter\SimpleRouter::request()->setMethod('get');
\Pecee\SimpleRouter\SimpleRouter::request()->setUri('/test/path/123123');
\Pecee\SimpleRouter\SimpleRouter::get('/test/path/{myParam}', 'DummyController@param', ['where' => ['myParam' => '([0-9]+)']]);
\Pecee\SimpleRouter\SimpleRouter::start();
}
public function testDomainRoute() {
\Pecee\SimpleRouter\RouterBase::getInstance()->reset();
\Pecee\SimpleRouter\SimpleRouter::request()->setMethod('get');
\Pecee\SimpleRouter\SimpleRouter::request()->setUri('/test');
\Pecee\SimpleRouter\SimpleRouter::request()->setHost('hello.world.com');
$this->result = false;
\Pecee\SimpleRouter\SimpleRouter::group(['domain' => '{subdomain}.world.com'], function() {
\Pecee\SimpleRouter\SimpleRouter::get('test', function($subdomain = null) {
$this->result = ($subdomain === 'hello');
});
});
\Pecee\SimpleRouter\SimpleRouter::start();
$this->assertTrue($this->result);
}
} }
+101
View File
@@ -0,0 +1,101 @@
<?php
require_once 'Dummy/DummyMiddleware.php';
require_once 'Dummy/DummyController.php';
require_once 'Dummy/Handler/ExceptionHandler.php';
use Pecee\SimpleRouter\SimpleRouter as SimpleRouter;
class RouterUrlTest extends PHPUnit_Framework_TestCase
{
protected $result = false;
protected function getUrl($name = null, $parameters = null, array $getParams = []) {
return SimpleRouter::getUrl($name, $parameters, $getParams);
}
public function testUrls()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setMethod('get');
SimpleRouter::request()->setUri('/');
// Match normal route on alias
SimpleRouter::get('/', 'DummyController@silent', ['as' => 'home']);
SimpleRouter::get('/about', 'DummyController@about');
SimpleRouter::group(['prefix' => '/admin', 'as' => 'admin'], function() {
// Match route with prefix on alias
SimpleRouter::get('/{id?}', 'DummyController@start', ['as' => 'home']);
// Match controller with prefix and alias
SimpleRouter::controller('/users', 'DummyController', ['as' => 'users']);
// Match controller with prefix and NO alias
SimpleRouter::controller('/pages', 'DummyController');
});
SimpleRouter::group(['prefix' => 'api', 'as' => 'api'], function() {
// Match resource controller
SimpleRouter::resource('phones', 'DummyController');
});
SimpleRouter::controller('gadgets', 'DummyController', ['names' => ['getIphoneInfo' => 'iphone']]);
// Match controller with no prefix and no alias
SimpleRouter::controller('/cats', 'CatsController');
// Pretend to load page
SimpleRouter::start();
$this->assertEquals('/gadgets/iphoneinfo/', $this->getUrl('gadgets.iphone'));
$this->assertEquals('/api/phones/create/', $this->getUrl('api.phones.create'));
// Should match /
$this->assertEquals('/', $this->getUrl('home'));
// Should match /about/
$this->assertEquals('/about/', $this->getUrl('DummyController@about'));
// Should match /admin/
$this->assertEquals('/admin/', $this->getUrl('DummyController@start'));
// Should match /admin/
$this->assertEquals('/admin/', $this->getUrl('admin.home'));
// Should match /admin/2/
$this->assertEquals('/admin/2/', $this->getUrl('admin.home', ['id' => 2]));
// Should match /admin/users/
$this->assertEquals('/admin/users/', $this->getUrl('admin.users'));
// Should match /admin/users/home/
$this->assertEquals('/admin/users/home/', $this->getUrl('admin.users@home'));
// Should match /cats/
$this->assertEquals('/cats/', $this->getUrl('CatsController'));
// Should match /cats/view/
$this->assertEquals('/cats/view/', $this->getUrl('CatsController', 'view'));
// Should match /cats/view/
//$this->assertEquals('/cats/view/', $this->getUrl('CatsController', ['view']));
// Should match /cats/view/666
$this->assertEquals('/cats/view/666/', $this->getUrl('CatsController@getView', ['666']));
// Should match /funny/man/
$this->assertEquals('/funny/man/', $this->getUrl('/funny/man'));
// Should match /?jackdaniels=true&cola=yeah
$this->assertEquals('/?jackdaniels=true&cola=yeah', $this->getUrl('home', null, ['jackdaniels' => 'true', 'cola' => 'yeah']));
}
}