Compare commits

..

33 Commits

Author SHA1 Message Date
Simon Sessingø ea243f2c89 Merge pull request #190 from skipperbent/v2-development
Development
2016-11-25 18:28:26 +01:00
Simon Sessingø 56653568e4 Merge pull request #188 from skipperbent/v2-development
Development
2016-11-25 12:55:08 +01:00
Simon Sessingø 5d330643e7 Merge pull request #186 from skipperbent/v2-development
Development
2016-11-25 03:28:49 +02:00
Simon Sessingø 57aa8eac1e Merge pull request #184 from skipperbent/v2-development
Fixed Exception when using Request
2016-11-25 03:05:17 +02:00
Simon Sessingø 4efc72d013 Merge pull request #182 from skipperbent/v2-development
Development
2016-11-25 02:58:43 +02:00
Simon Sessingø 49fc991f9a Merge pull request #180 from skipperbent/v2-development
Updated documentation
2016-11-24 14:51:07 +02:00
Simon Sessingø 20353c6e4d Merge pull request #178 from skipperbent/v2-development
Updated documentation to use Demo instead of MyWebsite
2016-11-24 14:44:56 +02:00
Simon Sessingø 4c62f86a26 Merge pull request #176 from skipperbent/v2-development
Fixed HttpException not thrown as NotFoundHttpException
2016-11-24 13:18:52 +02:00
Simon Sessingø 6445746324 Merge pull request #174 from skipperbent/v2-development
Optimisations + bugfixes
2016-11-24 11:05:57 +02:00
Simon Sessingø 1a2921acb4 Merge pull request #173 from skipperbent/v2-development
Development
2016-11-21 09:30:58 +02:00
Simon Sessingø 258d9d05c7 Merge pull request #171 from skipperbent/v2-development
Enhancements
2016-11-21 09:04:22 +02:00
Simon Sessingø 7e7319de06 Merge pull request #169 from skipperbent/v2-development
Fixed // on currentRoute urls.
2016-11-21 05:27:53 +02:00
Simon Sessingø 531b35532b Merge pull request #167 from skipperbent/v2-development
Bugfixes
2016-11-21 05:07:59 +02:00
Simon Sessingø c258f937e8 Merge pull request #165 from skipperbent/v2-development
V 2.4.0 development
2016-11-21 04:09:26 +02:00
Simon Sessingø 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ø d04c74ccad Merge pull request #161 from skipperbent/v2-development
V2 development
2016-11-19 20:41:26 +02:00
Simon Sessingø 02809a4daf Merge pull request #159 from skipperbent/v2-development
Bugfixes
2016-11-19 10:15:56 +02:00
Simon Sessingø da6b5af19f Merge pull request #157 from skipperbent/v2-development
Bugfixes
2016-11-19 09:47:19 +02:00
Simon Sessingø 914ec9d1b7 Merge pull request #155 from skipperbent/v2-development
Small optimisations
2016-11-19 07:02:23 +02:00
Simon Sessingø 5f72755a98 Merge pull request #153 from skipperbent/v2-development
V2 development
2016-11-19 06:25:08 +02:00
Simon Sessingø 41d15d3acd Merge pull request #151 from skipperbent/v2-development
Re-added missing methods from version 1.
2016-11-17 19:01:17 +02:00
Simon Sessingø ded9c8ebe0 Merge pull request #149 from skipperbent/v2-development
Updated documentation
2016-11-17 17:33:49 +02:00
Simon Sessingø 394f7beb8b Merge pull request #147 from skipperbent/v2-development
V2 development
2016-11-17 17:25:30 +02:00
Simon Sessingø baab004482 Merge pull request #145 from skipperbent/v2-development
Added default null parameter value to RouterController and RouterResource
2016-11-17 16:04:38 +02:00
Simon Sessingø 00c0cad211 Merge pull request #143 from skipperbent/v2-development
Bugfix: set parameters to null when new url is set
2016-11-17 15:21:24 +02:00
Simon Sessingø c6e85676da Merge pull request #141 from skipperbent/v2-development
Bugfixes
2016-11-17 06:37:03 +02:00
Simon Sessingø 5a74c9d27d Merge pull request #139 from skipperbent/v2-development
Development
2016-11-17 05:03:30 +02:00
Simon Sessingø b5eef6f3ee Merge pull request #137 from skipperbent/v2-development
Bugfixes
2016-11-16 16:16:22 +02:00
Simon Sessingø ad0eceb814 Merge pull request #135 from skipperbent/v2-development
Bugfixes
2016-11-16 15:32:32 +02:00
Simon Sessingø 4bec2bc5fb Merge pull request #133 from skipperbent/v2-development
Csrf bugfixes
2016-11-15 09:46:54 +02:00
Simon Sessingø 4f47463497 Merge pull request #131 from skipperbent/v2-development
V2 development
2016-11-15 08:13:09 +02:00
Simon Sessingø e5c86c1822 Merge pull request #129 from skipperbent/v2-development
Bugfixes
2016-11-15 01:12:41 +02:00
Simon Sessingø 32c305bd2c Merge pull request #124 from skipperbent/v2-development
V2 development
2016-11-08 18:22:49 +02:00
41 changed files with 1073 additions and 1754 deletions
+197 -270
View File
@@ -15,72 +15,71 @@ If you want a great new feature or experience any issues what-so-ever, please fe
## Table of Contents
- [Getting started](#getting-started)
- [Requirements](#requirements)
- [Notes](#notes-1)
- [Features](#features)
- [Installation](#installation)
- [Setting up Apache](#setting-up-apache)
- [Setting up Nginx](#setting-up-nginx)
- [Configuration](#configuration)
- [Helper functions](#helper-functions)
- Gettings started
- Requirements
- Notes
- Features
- Installation
- Setting up Apache
- Setting up Nginx
- Setting up simple-php-router
- Helper methods
- [Routes](#routes)
- [Basic routing](#basic-routing)
- [Available methods](#available-methods)
- [Multiple HTTP-verbs](#multiple-http-verbs)
- [Route parameters](#route-parameters)
- [Required parameters](#required-parameters)
- [Optional parameters](#optional-parameters)
- [Regular expression constraints](#regular-expression-constraints)
- [Regular expression route-match](#regular-expression-route-match)
- [Named routes](#named-routes)
- [Generating URLs To Named Routes](#generating-urls-to-named-routes)
- [Router groups](#router-groups)
- [Middleware](#middleware)
- [Namespaces](#namespaces)
- [Subdomain-routing](#subdomain-routing)
- [Route prefixes](#route-prefixes)
- [Form Method Spoofing](#form-method-spoofing)
- [Accessing The Current Route](#accessing-the-current-route)
- [Other examples](#other-examples)
- Routes
- Basic routing
- Available methods
- Multiple HTTP-verbs
- Route parameters
- Required parameters
- Optional parameters
- Regular expression constraints
- Regular expression route-match
- Named routes
- Generating URLs To Named Routes
- Router groups
- Middleware
- Namespaces
- Subdomain-routing
- Route prefixes
- Form Method Spoofing
- Accessing The Current Route
- Other examples
- [CSRF-protection](#csrf-protection)
- [Adding CSRF-verifier](#adding-csrf-verifier)
- [Getting CSRF-token](#getting-csrf-token)
- CSRF-protection
- Adding CSRF-verifier
- Getting CSRF-token
- [Middlewares](#middlewares)
- [Example](#example)
- [ExceptionHandler](#exceptionhandler)
- [Example](#example-1)
- Middleware
- Example
- ExceptionHandler
- Example
- [Urls](#urls)
- [Get by name (single route)](#get-by-name-single-route)
- [Get by name (controller route)](#get-by-name-controller-route)
- [Get by class](#get-by-class)
- [Get by custom names for methods on a controller/resource route](#using-custom-names-for-methods-on-a-controllerresource-route)
- [Getting REST/resource controller urls](#getting-restresource-controller-urls)
- [Get the current url](#get-the-current-url)
- Urls
- Get by name (single route)
- Get by name (controller route)
- Get by class
- Get by custom names for methods on a controller/resource route
- Getting REST/resource controller urls
- [Input & parameters](#input--parameters)
- [Using the Input class to manage parameters](#using-the-input-class-to-manage-parameters)
- [Get single parameter value](#get-single-parameter-value)
- [Get parameter object](#get-parameter-object)
- [Managing files](#managing-files)
- [Get all parameters](#get-all-parameters)
- Input & parameters
- Return single parameter value
- Return single parameter object
- Managing files
- Return all parameters
- [Advanced](#advanced)
- [Bootmanager: loading routes dynamically](#bootmanager-loading-routes-dynamically)
- [Url rewriting](#url-rewriting)
- [Rewrite using callback](#rewrite-using-callback)
- [Rewrite using url](#rewrite-using-url)
- Advanced
- Bootmanager: loading routes dynamically
- Ovewrite route about to be loaded
- Examples
- Rewriting to new route
- Changing callback
- [Adding routes manually](#adding-routes-manually)
- [Extending](#extending)
- Adding routes manually
- Extending
- [Credits](#credits)
- [Sites](#sites)
- [License](#license)
- Credits
- Sites
- License
___
@@ -93,7 +92,8 @@ composer require pecee/simple-router
## Requirements
- PHP 5.5 or greater
- PHP 5.4 or greater
- mcrypt extension
## Notes
@@ -155,21 +155,7 @@ location / {
Nothing special is required for Apache to work. We've include the `.htaccess` file in the `public` folder. If rewriting is not working for you, please check that the `mod_rewrite` module (htaccess support) is enabled in the Apache configuration.
#### .htaccess example
Below is an example of an working `.htaccess` file used by simple-php-router.
Simply create a new `.htaccess` file in your projects `public` directory and paste the contents below in your newly created file. This will redirect all requests to your `index.php` file (see Configuration section below).
```
RewriteEngine on
RewriteCond %{SCRIPT_FILENAME} !-f
RewriteCond %{SCRIPT_FILENAME} !-d
RewriteCond %{SCRIPT_FILENAME} !-l
RewriteRule ^(.*)$ index.php/$1
```
### Configuration
### Setting up simple-php-router
Create a new file, name it `routes.php` and place it in your library folder. This will be the file where you define all the routes for your project.
@@ -207,6 +193,8 @@ To implement the functions below, simply copy the code to a new file and require
```php
<?php
use Pecee\SimpleRouter\SimpleRouter;
/**
* Get url for a route by using either name/alias, class or method name.
*
@@ -224,43 +212,41 @@ To implement the functions below, simply copy the code to a new file and require
* @param array|null $getParams
* @return string
*/
function url($name = null, $parameters = null, $getParams = null)
{
return SimpleRouter::getUrl($name, $parameters, $getParams);
function url($name = null, $parameters = null, $getParams = null) {
SimpleRouter::getUrl($name, $parameters, $getParams);
}
/**
* @return \Pecee\Http\Response
* Get current csrf-token
* @return null|string
*/
function response()
{
return SimpleRouter::response();
function csrf_token() {
$token = new \Pecee\CsrfToken();
return $token->getToken();
}
/**
* Get request object
* @return \Pecee\Http\Request
*/
function request()
{
function request() {
return SimpleRouter::request();
}
/**
* Get response object
* @return \Pecee\Http\Response
*/
function response() {
return SimpleRouter::response();
}
/**
* Get input class
* @return \Pecee\Http\Input\Input
*/
function input()
{
return request()->getInput();
}
function redirect($url, $code = null)
{
if ($code !== null) {
response()->httpCode($code);
}
response()->redirect($url);
function input() {
return SimpleRouter::request()->getInput();
}
```
@@ -436,7 +422,7 @@ Route groups allow you to share route attributes, such as middleware or namespac
To assign middleware to all routes within a group, you may use the middleware key in the group attribute array. Middleware are executed in the order they are listed in the array:
```php
SimpleRouter::group(['middleware' => \Demo\Middleware\Auth::class], function () {
SimpleRouter::group(['middleware' => '\Demo\Middleware\Auth'], function () {
SimpleRouter::get('/', function () {
// Uses Auth Middleware
});
@@ -451,11 +437,6 @@ SimpleRouter::group(['middleware' => \Demo\Middleware\Auth::class], function ()
Another common use-case for route groups is assigning the same PHP namespace to a group of controllers using the `namespace` parameter in the group array:
#### Note
Group namespaces will only be added to routes with relative callbacks.
For example if your route has an absolute callback like `\Demo\Controller\DefaultController@home`, the namespace from the route will not be prepended.
To fix this you can make the callback relative by removing the `\` in the beginning of the callback.
```php
SimpleRouter::group(['namespace' => 'Admin'], function () {
// Controllers Within The "App\Http\Controllers\Admin" Namespace
@@ -499,8 +480,7 @@ HTML forms do not support `PUT`, `PATCH` or `DELETE` actions. So, when defining
You can access information about the current route loaded by using the following method:
```php
SimpleRouter::request()->getLoadedRoute();
request()->getLoadedRoute();
SimpleRouter::router()->getLoadedRoute();
```
## Other examples
@@ -514,7 +494,7 @@ use Pecee\SimpleRouter\SimpleRouter;
/* Adding custom csrfVerifier here */
SimpleRouter::csrfVerifier(new \Demo\Middlewares\CsrfVerifier());
SimpleRouter::group(['middleware' => \Demo\Middlewares\Site::class, 'exceptionHandler' => \Demo\Handlers\CustomExceptionHandler::class], function() {
SimpleRouter::group(['middleware' => '\Demo\Middlewares\Site', 'exceptionHandler' => 'Handlers\CustomExceptionHandler'], function() {
SimpleRouter::get('/answers/{id}', 'ControllerAnswers@show', ['where' => ['id' => '[0-9]+']]);
@@ -524,7 +504,7 @@ SimpleRouter::group(['middleware' => \Demo\Middlewares\Site::class, 'exceptionHa
* Restful resource (see IRestController interface for available methods)
*/
SimpleRouter::resource('/rest', ControllerRessource::class);
SimpleRouter::resource('/rest', 'ControllerRessource');
/**
@@ -540,7 +520,7 @@ SimpleRouter::group(['middleware' => \Demo\Middlewares\Site::class, 'exceptionHa
* etc.
*/
SimpleRouter::controller('/animals', ControllerAnimals::class);
SimpleRouter::controller('/animals', 'ControllerAnimals');
});
@@ -605,19 +585,13 @@ namespace Demo\Middlewares;
use Pecee\Http\Middleware\IMiddleware;
use Pecee\Http\Request;
use Pecee\SimpleRouter\Route\ILoadableRoute;
class CustomMiddleware implements Middleware {
public function handle(Request $request) {
public function handle(Request $request, ILoadableRoute &$route) {
// Authenticate user, will be available using request()->user
$request->user = User::authenticate();
// If authentication failed, redirect request to user-login page.
if($request->user === null) {
$request->setRewriteUrl(url('user.login'));
return $request;
}
$request->setUri(url('home'));
}
}
@@ -641,10 +615,11 @@ namespace Demo\Handlers;
use Pecee\Handlers\IExceptionHandler;
use Pecee\Http\Request;
use Pecee\SimpleRouter\Exceptions\NotFoundHttpException;
use Pecee\SimpleRouter\Route\ILoadableRoute;
class CustomExceptionHandler implements IExceptionHandler
{
public function handleError(Request $request, \Exception $error)
public function handleError(Request $request, ILoadableRoute &$route = null, \Exception $error)
{
/* You can use the exception handler to format errors depending on the request and type. */
@@ -661,9 +636,14 @@ class CustomExceptionHandler implements IExceptionHandler
/* The router will throw the NotFoundHttpException on 404 */
if($error instanceof NotFoundHttpException) {
// Render custom 404-page
/*
* 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->setRewriteCallback('Demo\Controllers\PageController@notFound');
$request->setUri(url('page.notfound'));
return $request;
}
@@ -681,7 +661,7 @@ class CustomExceptionHandler implements IExceptionHandler
By default all controller and resource routes will use a simplified version of their url as name.
### Get by name (single route)
### Get routes using custom name (single route)
```php
SimpleRouter::get('/product-view/{id}', 'ProductsController@show', ['as' => 'product']);
@@ -694,10 +674,10 @@ url('product', null, ['category' => 'shoes']);
# /product-view/?category=shoes
```
### Get by name (controller route)
### Getting the url using the name (controller route)
```php
SimpleRouter::controller('/images', ImagesController::class, ['as' => 'picture']);
SimpleRouter::controller('/images', 'ImagesController', ['as' => 'picture']);
url('picture@getView', null, ['category' => 'shoes']);
url('picture', 'getView', ['category' => 'shoes']);
@@ -709,7 +689,7 @@ url('picture', 'view');
# /images/view/
```
### Get by class
### Getting the url using class
```php
SimpleRouter::get('/product-view/{id}', 'ProductsController@show', ['as' => 'product']);
@@ -726,7 +706,7 @@ url('ImagesController@getImage', null, ['id' => 22]);
### Using custom names for methods on a controller/resource route
```php
SimpleRouter::controller('gadgets', GadgetsController::class, ['names' => ['getIphoneInfo' => 'iphone']]);
SimpleRouter::controller('gadgets', 'GadgetsController', ['names' => ['getIphoneInfo' => 'iphone']]);
url('gadgets.iphone');
@@ -737,7 +717,7 @@ url('gadgets.iphone');
### Getting REST/resource controller urls
```php
SimpleRouter::resource('/phones', PhonesController::class);
SimpleRouter::resource('/phones', 'PhonesController');
url('phones');
url('phones.index');
@@ -752,7 +732,7 @@ url('phones.edit');
# /phones/edit/
```
### Get the current url
### Return the current url
```php
url();
@@ -769,17 +749,17 @@ url(null, null, ['q' => 'cars']);
We've added the `Input` class to easy access and manage parameters from your Controller-classes.
### Get single parameter value:
**Return single parameter value (matches both GET, POST, FILE):**
If items is grouped in the html, it will return an array of items.
**Note:** `get` will automatically trim the value and ensure that it's not empty. If it's empty the `$defaultValue` will be returned.
```php
$value = input()->get($index, $defaultValue, $methods);
$value = input()->get($index, $defaultValue);
```
### Get parameter object
**Return parameter object (matches both GET, POST, FILE):**
Will return an instance of `InputItem` or `InputFile` depending on the type.
@@ -792,10 +772,10 @@ If items is grouped in the html, it will return an array of items.
**Note:** `getObject` will only return `$defaultValue` if the item doesn't exist. If you want `$defaultValue` to be returned if the item is empty, please use `input()->get()` instead.
```php
$object = input()->getObject($index, $defaultValue = null, $methods = null);
$object = input()->getObject($index);
```
### 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
# -- match any (default) --
@@ -819,11 +799,9 @@ $object = input()->get($index, $defaultValue, 'file');
$object = input()->findGet($index, $defaultValue);
$object = input()->findPost($index, $defaultValue);
$object = input()->findFile($index, $defaultValue);
```
### Managing files
# -- examples --
```php
/**
* In this small example we loop through a collection of files
* added on the page like this
@@ -841,10 +819,9 @@ foreach(input()->get('images', []) as $image)
}
}
```
### Get all parameters
**Return all parameters:**
```php
// Get all
@@ -876,140 +853,15 @@ All object implements the `IInputItem` interface and will always contain these m
Below example requires you to have the helper functions added. Please refer to the helper functions section in the documentation.
```php
/* Get parameter site_id or default-value 2 from either post-value or query-string */
$siteId = input()->get('site_id', 2, ['post', 'get']);
// Get parameter site_id or default-value 2
$siteId = input()->get('site_id', 2);
```
---
# Advanced
## Url rewriting
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.
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()`.
```php
use Pecee\SimpleRouter;
$request = SimpleRouter::request();
$request->setRewriteCallback('Example\MyCustomClass@hello');
// -- or you can rewrite by url --
$request->setRewriteUrl('/my-rewrite-url');
```
### Examples
It's only possible to change the route BEFORE the route has initially been rendered. You can use the `Request` object to manipulate the route which are about to be loaded.
#### Rewrite using callback
This method is most efficient, as it will render the route immediately.
This method is useful for rendering 404-pages etc.
You can also change the callback by modifying the `$route` parameter. This is perfect if you just want to display a view quickly - or change the callback depending
on some criteria's for the request.
The callback below will fire immediately after the `Middleware` or `ExceptionHandler` has been loaded, as they are loaded before the route is rendered.
If you wish to change the callback from outside, please have this in mind.
The example below will render `DefaultController@notFound` regardless of the url.
**NOTE: Use this method if you want to load another controller. No additional middlewares or rules will be loaded.**
#### Middleware example
```php
namespace Demo\Middlewares;
use Pecee\Http\Middleware\IMiddleware;
use Pecee\Http\Request;
class CustomMiddleware implements IMiddleware {
public function handle(Request $request) {
$request->setRewriteCallback('Demo\Controllers\DefaultController@notFound');
return $request;
}
}
```
#### Exception handler example
```php
namespace Demo\Handlers;
use Pecee\Handlers\IExceptionHandler;
use Pecee\Http\Request;
use Pecee\SimpleRouter\Exceptions\NotFoundHttpException;
class CustomExceptionHandler implements IExceptionHandler
{
public function handleError(Request $request, \Exception $error)
{
/* 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->setRewriteCallback('Demo\Controllers\DefaultController@notFound');
return $request;
}
throw $error;
}
}
```
#### Rewrite using url
The example below will cause the router to reload the request and reinitialize all the routes. This method is slower, but will ensure that all middlewares and rules for the route is loaded.
This method is useful if you want to redirect a url to another-url which is dependent on a middleware. You can also add a custom rule by calling `$request->setRewriteRoute($route)` if
you want to customize request-methods or use another route-type like `RouteController` etc.
We are using the `url()` helper function to get the uri to another route added in the `routes.php` file.
**NOTE: Use this method if you want to fully load another route using it's settings (request method, middlewares etc).**
#### Middleware example
The example below will redirect the request to the `home`-route.
```php
namespace Demo\Middlewares;
use Pecee\Http\Middleware\IMiddleware;
use Pecee\Http\Request;
class CustomMiddleware implements IMiddleware {
public function handle(Request $request) {
$request->setRewriteUrl(url('home'));
return $request;
}
}
```
# Bootmanager: loading routes dynamically
## Load routes dynamicially using the router bootmanager
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.
@@ -1025,7 +877,7 @@ class CustomRouterRules implement IRouterBootManager {
$rewriteRules = [
'/my-cat-is-beatiful' => '/article/view/1',
'/horses-are-great' => '/article/view/2',
'/horses-are-great' => '/article/view/2'
];
foreach($rewriteRules as $url => $rule) {
@@ -1033,11 +885,11 @@ class CustomRouterRules implement IRouterBootManager {
// If the current uri matches the url, we use our custom route
if($request->getUri() === $url) {
$request->setRewriteUrl($rule);
return $request;
$request->setUri($rule);
}
}
return $request;
}
}
@@ -1055,6 +907,82 @@ The last thing we need to do, is to add our custom boot-manager to the ```routes
SimpleRouter::addBootManager(new CustomRouterRules());
```
## Easily overwrite 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.
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()`.
```php
use Pecee\SimpleRouter;
$route = SimpleRouter::router()->getLoadedRoute();
$route->setCallback('Example\MyCustomClass@hello');
// -- or you can rewrite by doing --
$route->setClass('Example\MyCustomClass');
$route->setMethod('hello');
```
### 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
modify the `IRoute` object from a `Middleware` or `ExceptionHandler`, like the examples below.
#### 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.
**NOTE: Use this method if you want to fully load another route using it's settings (request method etc).**
```php
namespace Demo\Middlewares;
use Pecee\Http\Middleware\IMiddleware;
use Pecee\Http\Request;
use Pecee\SimpleRouter\Route\ILoadableRoute;
class CustomMiddleware implements Middleware {
public function handle(Request $request, ILoadableRoute &$route) {
$request->setUri(url('home'));
}
}
```
#### Changing callback
You can also change the callback by modifying the `$route` parameter. This is perfect if you just want to display a view quickly - or change the callback depending
on some criteria's for the request.
The callback below will fire immediately after the `Middleware` or `ExceptionHandler` has been loaded, as they are loaded before the route is rendered.
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.**
```php
namespace Demo\Middlewares;
use Pecee\Http\Middleware\IMiddleware;
use Pecee\Http\Request;
use Pecee\SimpleRouter\Route\ILoadableRoute;
class CustomMiddleware implements Middleware {
public function handle(Request $request, ILoadableRoute &$route) {
$route->callback('DefaultController@home');
}
}
```
## Adding routes manually
The ```SimpleRouter``` class referenced in the previous example, is just a simple helper class that knows how to communicate with the ```Router``` class.
@@ -1064,8 +992,8 @@ If you are up for a challenge, want the full control or simply just want to crea
use \Pecee\SimpleRouter\Router;
use \Pecee\SimpleRouter\Route\RouteUrl;
/* Create new Router instance */
$router = new Router();
/* Grap the router instance */
$router = Router::getInstance();
$route = new RouteUrl('/answer/1', function() {
@@ -1073,7 +1001,7 @@ $route = new RouteUrl('/answer/1', function() {
});
$route->setMiddleware(\Demo\Middlewares\AuthMiddleware::class);
$route->setMiddleware('\Demo\Middlewares\AuthMiddleware');
$route->setNamespace('\Demo\Controllers');
$route->setPrefix('v1');
@@ -1115,7 +1043,6 @@ class Router extends SimpleRouter {
# Credits
## Sites
This is some sites that uses the simple-router project in production.
- [holla.dk](http://www.holla.dk)
+23 -29
View File
@@ -1,32 +1,26 @@
{
"name": "pecee/simple-router",
"description": "Simple, fast PHP router that is easy to get integrated and in almost any project. Heavily inspired by the Laravel router.",
"keywords": [
"router",
"routing",
"laravel",
"pecee",
"route"
],
"license": "MIT",
"support": {
"source": "https://github.com/skipperbent/simple-php-router/issues"
},
"authors": [
{
"name": "Simon Sessingø",
"email": "simon.sessingoe@gmail.com"
"name": "pecee/simple-router",
"description": "Simple, fast PHP router that is easy to get integrated and in almost any project. Heavily inspired by the Laravel router.",
"keywords": [ "router", "routing", "laravel", "pecee" ],
"license": "MIT",
"support": {
"source": "https://github.com/skipperbent/simple-php-router/issues"
},
"authors": [
{
"name": "Simon Sessingø",
"email": "simon.sessingoe@gmail.com"
}
],
"require": {
"php": ">=5.4.0"
},
"require-dev": {
"phpunit/phpunit": "4.7.7"
},
"autoload": {
"psr-4": {
"Pecee\\": "src/Pecee/"
}
}
],
"require": {
"php": ">=5.4.0"
},
"require-dev": {
"phpunit/phpunit": "4.7.7"
},
"autoload": {
"psr-4": {
"Pecee\\": "src/Pecee/"
}
}
}
@@ -1,47 +1,47 @@
<?php
namespace Pecee\Controllers;
interface IResourceController
interface IRestController
{
/**
* @return void
*/
public function index();
function index();
/**
* @param mixed $id
* @return void
*/
public function show($id);
function show($id);
/**
* @return void
*/
public function store();
function store();
/**
* @return void
*/
public function create();
function create();
/**
* View
* @param mixed $id
* @return void
*/
public function edit($id);
function edit($id);
/**
* @param mixed $id
* @return void
*/
public function update($id);
function update($id);
/**
* @param mixed $id
* @return void
*/
public function destroy($id);
function destroy($id);
}
+1 -9
View File
@@ -10,7 +10,6 @@ class CsrfToken
/**
* Generate random identifier for CSRF token
*
* @throws \RuntimeException
* @return string
*/
public static function generateToken()
@@ -19,14 +18,7 @@ class CsrfToken
return bin2hex(random_bytes(32));
}
$isSourceStrong = false;
$random = openssl_random_pseudo_bytes(32, $isSourceStrong);
if ($isSourceStrong === false || $random === false) {
throw new \RuntimeException('IV generation failed');
}
return $random;
return bin2hex(openssl_random_pseudo_bytes(32));
}
/**
+3 -1
View File
@@ -2,14 +2,16 @@
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, \Exception $error);
public function handleError(Request $request, ILoadableRoute &$route = null, \Exception $error);
}
+2
View File
@@ -14,6 +14,8 @@ interface IInputItem
public function getValue();
public function setValue($value);
public function __toString();
}
+65 -142
View File
@@ -32,7 +32,7 @@ class Input
$this->parseInputs();
}
public function parseInputs()
protected function parseInputs()
{
/* Parse get requests */
if (count($_GET) > 0) {
@@ -42,7 +42,7 @@ class Input
/* Parse post requests */
$postVars = $_POST;
if (in_array($this->request->getMethod(), ['put', 'patch', 'delete'], false) === true) {
if (in_array($this->request->getMethod(), ['put', 'patch', 'delete']) === true) {
parse_str(file_get_contents('php://input'), $postVars);
}
@@ -51,97 +51,49 @@ class Input
}
/* Parse get requests */
if (count($_FILES) > 0) {
$this->file = $this->parseFiles();
}
}
public function parseFiles()
{
$list = [];
$max = count($_FILES) - 1;
$keys = array_keys($_FILES);
foreach ((array)$_FILES as $key => $value) {
for ($i = $max; $i >= 0; $i--) {
// Handle array input
if (is_array($value['name']) === false) {
$values['index'] = $key;
$list[$key] = InputFile::createFromArray(array_merge($value, $values));
continue;
}
$key = $keys[$i];
$value = $_FILES[$key];
$keys = [];
$files = $this->rearrangeFiles($value['name'], $keys, $value);
if (isset($list[$key])) {
$list[$key][] = $files;
} else {
$list[$key] = $files;
}
}
return $list;
}
protected function rearrangeFiles(array $values, &$index, $original)
{
$output = [];
$getItem = function ($key, $property = 'name') use ($original, $index) {
$path = $original[$property];
$fileValues = array_values($index);
foreach ($fileValues as $i) {
$path = $path[$i];
}
return $path[$key];
};
foreach ($values as $key => $value) {
if (is_array($getItem($key)) === false) {
$file = InputFile::createFromArray([
'index' => $key,
'error' => $getItem($key, 'error'),
'tmp_name' => $getItem($key, 'tmp_name'),
'type' => $getItem($key, 'type'),
'size' => $getItem($key, 'size'),
'filename' => $getItem($key, 'name'),
]);
if (isset($output[$key])) {
$output[$key][] = $file;
} else {
$output[$key] = $file;
// Handle array input
if (is_array($value['name']) === false) {
$values['index'] = $key;
$this->file[$key] = InputFile::createFromArray(array_merge($value, $values));
continue;
}
continue;
$subMax = count($value['name']) - 1;
$keys = array_keys($value['name']);
$output = [];
for ($i = $subMax; $i >= 0; $i--) {
$output[$keys[$i]] = InputFile::createFromArray([
'index' => $key,
'error' => $value['error'][$keys[$i]],
'tmp_name' => $value['tmp_name'][$keys[$i]],
'type' => $value['type'][$keys[$i]],
'size' => $value['size'][$keys[$i]],
'name' => $value['name'][$keys[$i]],
]);
}
$this->file[$key] = $output;
}
$index[] = $key;
$files = $this->rearrangeFiles($value, $index, $original);
if (isset($output[$key])) {
$output[$key][] = $files;
} else {
$output[$key] = $files;
}
}
return $output;
}
protected function handleGetPost(array $array)
protected function handleGetPost($array)
{
$list = [];
$tmp = [];
$max = count($array) - 1;
$keys = array_keys($array);
@@ -153,110 +105,77 @@ class Input
// Handle array input
if (is_array($value) === false) {
$list[$key] = new InputItem($key, $value);
$tmp[$key] = new InputItem($key, $value);
continue;
}
$output = $this->handleGetPost($value);
$subMax = count($value) - 1;
$keys = array_keys($value);
$output = [];
$list[$key] = $output;
for ($i = $subMax; $i >= 0; $i--) {
$output[$keys[$i]] = new InputItem($key, $value[$keys[$i]]);
}
$tmp[$key] = $output;
}
return $list;
return $tmp;
}
/**
* Find post-value by index or return default value.
*
* @param string $index
* @param string|null $defaultValue
* @return InputItem|string
*/
public function findPost($index, $defaultValue = null)
public function findPost($index, $default = null)
{
return isset($this->post[$index]) ? $this->post[$index] : $defaultValue;
return isset($this->post[$index]) ? $this->post[$index] : $default;
}
/**
* Find file by index or return default value.
*
* @param string $index
* @param string|null $defaultValue
* @return InputFile|string
*/
public function findFile($index, $defaultValue = null)
public function findFile($index, $default = null)
{
return isset($this->file[$index]) ? $this->file[$index] : $defaultValue;
return isset($this->file[$index]) ? $this->file[$index] : $default;
}
/**
* Find parameter/query-string by index or return default value.
*
* @param string $index
* @param string|null $defaultValue
* @return InputItem|string
*/
public function findGet($index, $defaultValue = null)
public function findGet($index, $default = null)
{
return isset($this->get[$index]) ? $this->get[$index] : $defaultValue;
return isset($this->get[$index]) ? $this->get[$index] : $default;
}
/**
* Get input object
*
* @param string $index
* @param string|null $defaultValue
* @param array|string|null $methods
* @return IInputItem|string
*/
public function getObject($index, $defaultValue = null, $methods = null)
public function getObject($index, $default = null, $method = null)
{
if ($methods !== null && is_string($methods) === true) {
$methods = [$methods];
}
$element = null;
if ($methods === null || in_array('get', $methods)) {
if ($method === null || strtolower($method) === 'get') {
$element = $this->findGet($index);
}
if (($element === null && $methods === null) || ($methods !== null && in_array('post', $methods))) {
if ($element === null && $method === null || strtolower($method) === 'post') {
$element = $this->findPost($index);
}
if (($element === null && $methods === null) || ($methods !== null && in_array('file', $methods))) {
if ($element === null && $method === null || strtolower($method) === 'file') {
$element = $this->findFile($index);
}
return ($element !== null) ? $element : $defaultValue;
return ($element === null) ? $default : $element;
}
/**
* Get input element value matching index
*
* @param string $index
* @param string|null $defaultValue
* @param array|string|null $methods
* @param string|null $default
* @param string|null $method
* @return InputItem|string
*/
public function get($index, $defaultValue = null, $methods = null)
public function get($index, $default = null, $method = null)
{
$input = $this->getObject($index, $defaultValue, $methods);
$input = $this->getObject($index, $default, $method);
if ($input instanceof InputItem) {
return (trim($input->getValue()) === '') ? $defaultValue : $input->getValue();
return (trim($input->getValue()) === '') ? $default : $input->getValue();
}
return $input;
}
/**
* Check if a input-item exist
*
* @param string $index
* @return bool
*/
public function exists($index)
{
return ($this->getObject($index) !== null);
@@ -275,7 +194,7 @@ class Input
$contents = file_get_contents('php://input');
if (strpos(trim($contents), '{') === 0) {
if (stripos(trim($contents), '{') === 0) {
$output = json_decode($contents, true);
if ($output === false) {
$output = [];
@@ -287,7 +206,11 @@ class Input
if ($filter !== null) {
$output = array_filter($output, function ($key) use ($filter) {
return (in_array($key, $filter) === true);
if (in_array($key, $filter)) {
return true;
}
return false;
}, ARRAY_FILTER_USE_KEY);
}
+37 -107
View File
@@ -4,8 +4,8 @@ namespace Pecee\Http\Input;
class InputFile implements IInputItem
{
public $index;
public $value;
public $name;
public $filename;
public $size;
public $type;
public $error;
@@ -21,9 +21,7 @@ class InputFile implements IInputItem
/**
* Create from array
*
* @param array $values
* @throws \InvalidArgumentException
* @return static
*/
public static function createFromArray(array $values)
@@ -32,23 +30,14 @@ class InputFile implements IInputItem
throw new \InvalidArgumentException('Index key is required');
}
/* Easy way of ensuring that all indexes-are set and not filling the screen with isset() */
$values = array_merge([
'tmp_name' => null,
'type' => null,
'size' => null,
'name' => null,
'error' => null,
], $values);
return (new static($values['index']))
->setSize($values['size'])
->setError($values['error'])
->setType($values['type'])
->setTmpName($values['tmp_name'])
->setFilename($values['name']);
$input = new static($values['index']);
$input->setError(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->setTmpName(isset($values['tmp_name']) ? $values['tmp_name'] : null);
return $input;
}
/**
@@ -59,11 +48,6 @@ class InputFile implements IInputItem
return $this->index;
}
/**
* Set input index
* @param string $index
* @return static $this
*/
public function setIndex($index)
{
$this->index = $index;
@@ -91,10 +75,6 @@ class InputFile implements IInputItem
return $this;
}
/**
* Get mime-type of file
* @return string
*/
public function getMime()
{
return $this->getType();
@@ -120,19 +100,12 @@ class InputFile implements IInputItem
return $this;
}
/**
* Returns extension without "."
*
* @return string
*/
public function getExtension()
{
return pathinfo($this->getFilename(), PATHINFO_EXTENSION);
return pathinfo($this->getName(), PATHINFO_EXTENSION);
}
/**
* Get human friendly name
*
* @return string
*/
public function getName()
@@ -140,13 +113,6 @@ class InputFile implements IInputItem
return $this->name;
}
/**
* Set human friendly name.
* Useful for adding validation etc.
*
* @param string $name
* @return static $this
*/
public function setName($name)
{
$this->name = $name;
@@ -154,63 +120,29 @@ class InputFile implements IInputItem
return $this;
}
/**
* Set filename
*
* @param string $name
* @return static $this
*/
public function setFilename($name)
{
$this->filename = $name;
return $this;
}
/**
* Get filename
*
* @return string mixed
*/
public function getFilename()
{
return $this->filename;
}
/**
* Move the uploaded temporary file to it's new home
*
* @param string $destination
* @return bool
*/
public function move($destination)
{
return move_uploaded_file($this->tmpName, $destination);
}
/**
* Get file contents
*
* @return string
*/
public function getContents()
{
return file_get_contents($this->tmpName);
}
/**
* Return true if an upload error occurred.
*
* @return bool
*/
public function setValue($value)
{
$this->value = $value;
return $this;
}
public function hasError()
{
return ($this->getError() !== 0);
}
/**
* Get upload-error code.
*
* @return string
*/
public function getError()
@@ -220,7 +152,6 @@ class InputFile implements IInputItem
/**
* Set error
*
* @param int $error
* @return static $this
*/
@@ -231,6 +162,27 @@ class InputFile implements IInputItem
return $this;
}
public function toArray()
{
return [
'tmp_name' => $this->tmpName,
'type' => $this->type,
'size' => $this->size,
'name' => $this->name,
'error' => $this->error,
];
}
public function __toString()
{
return $this->getValue();
}
public function getValue()
{
return $this->getTmpName();
}
/**
* @return string
*/
@@ -250,26 +202,4 @@ class InputFile implements IInputItem
return $this;
}
public function __toString()
{
return $this->getTmpName();
}
public function getValue()
{
return $this->getFilename();
}
public function toArray()
{
return [
'tmp_name' => $this->tmpName,
'type' => $this->type,
'size' => $this->size,
'name' => $this->filename,
'error' => $this->error,
];
}
}
-1
View File
@@ -75,5 +75,4 @@ class InputItem implements IInputItem
{
return (string)$this->value;
}
}
@@ -4,6 +4,7 @@ namespace Pecee\Http\Middleware;
use Pecee\CsrfToken;
use Pecee\Http\Middleware\Exceptions\TokenMismatchException;
use Pecee\Http\Request;
use Pecee\SimpleRouter\Route\ILoadableRoute;
class BaseCsrfVerifier implements IMiddleware
{
@@ -54,10 +55,10 @@ class BaseCsrfVerifier implements IMiddleware
return false;
}
public function handle(Request $request)
public function handle(Request $request, ILoadableRoute &$route = null)
{
if ($this->skip($request) === false && in_array($request->getMethod(), ['post', 'put', 'delete'], false) === true) {
if (in_array($request->getMethod(), ['post', 'put', 'delete']) === true && $this->skip($request) === false) {
$token = $request->getInput()->get(static::POST_KEY, null, 'post');
@@ -76,7 +77,7 @@ class BaseCsrfVerifier implements IMiddleware
public function generateToken()
{
$token = CsrfToken::generateToken();
$token = $this->csrfToken->generateToken();
$this->csrfToken->setToken($token);
return $token;
@@ -84,7 +85,7 @@ class BaseCsrfVerifier implements IMiddleware
public function hasToken()
{
if ($this->token !== null) {
if ($this->token != null) {
return true;
}
+3 -1
View File
@@ -2,13 +2,15 @@
namespace Pecee\Http\Middleware;
use Pecee\Http\Request;
use Pecee\SimpleRouter\Route\ILoadableRoute;
interface IMiddleware
{
/**
* @param Request $request
* @param ILoadableRoute $route
* @return Request|null
*/
public function handle(Request $request);
public function handle(Request $request, ILoadableRoute &$route);
}
+9 -132
View File
@@ -2,9 +2,6 @@
namespace Pecee\Http;
use Pecee\Http\Input\Input;
use Pecee\SimpleRouter\Route\ILoadableRoute;
use Pecee\SimpleRouter\Route\RouteUrl;
use Pecee\SimpleRouter\SimpleRouter;
class Request
{
@@ -15,24 +12,13 @@ class Request
protected $method;
protected $input;
/**
* @var ILoadableRoute|null
*/
protected $rewriteRoute;
protected $rewriteUrl;
/**
* @var ILoadableRoute|null
*/
protected $loadedRoute;
public function __construct()
{
$this->parseHeaders();
$this->host = $this->getHeader('http-host');
$this->host = $this->getHeader('http-host');;
$this->uri = $this->getHeader('request-uri');
$this->input = new Input($this);
$this->method = strtolower($this->input->get('_method', $this->getHeader('request-method'), 'post'));
$this->method = strtolower($this->input->get('_method', $this->getHeader('request-method')));
}
protected function parseHeaders()
@@ -54,7 +40,11 @@ class Request
public function isSecure()
{
return $this->getHeader('http-x-forwarded-proto') === 'https' || $this->getHeader('https') !== null || $this->getHeader('server-port') === 443;
if ($this->getHeader('http-x-forwarded-proto') === 'https' || $this->getHeader('https') !== null || $this->getHeader('server-port') === 443) {
return true;
}
return false;
}
/**
@@ -118,24 +108,13 @@ class Request
return $this->getHeader('http-cf-connecting-ip');
}
if ($this->getHeader('http-x-forwarded-for') !== null) {
if ($this->getHeader('http-x-forwarded-for') !== null && strlen($this->getHeader('http-x-forwarded-for'))) {
return $this->getHeader('http-x-forwarded_for');
}
return $this->getHeader('remote-addr');
}
/**
* Get remote address/ip
*
* @alias static::getIp
* @return string
*/
public function getRemoteAddr()
{
return $this->getIp();
}
/**
* Get referer
* @return string
@@ -158,7 +137,7 @@ class Request
* Get header value by name
*
* @param string $name
* @param string|null $defaultValue
* @param object|null $defaultValue
*
* @return string|null
*/
@@ -238,108 +217,6 @@ class Request
$this->method = $method;
}
/**
* Set rewrite route
*
* @param ILoadableRoute $route
* @return static
*/
public function setRewriteRoute(ILoadableRoute $route)
{
$this->rewriteRoute = $route;
$callback = $route->getCallback();
/* Only add default namespace on relative callbacks */
if ($callback === null || $callback[0] !== '\\') {
$namespace = SimpleRouter::getDefaultNamespace();
if ($namespace !== null) {
if ($this->rewriteRoute->getNamespace() !== null) {
$namespace .= '\\' . $this->rewriteRoute->getNamespace();
}
$this->rewriteRoute->setDefaultNamespace($namespace);
}
}
return $this;
}
/**
* Get rewrite route
*
* @return ILoadableRoute|null
*/
public function getRewriteRoute()
{
return $this->rewriteRoute;
}
/**
* Get rewrite url
*
* @return string
*/
public function getRewriteUrl()
{
return $this->rewriteUrl;
}
/**
* Set rewrite url
*
* @param string $rewriteUrl
* @return static
*/
public function setRewriteUrl($rewriteUrl)
{
$this->rewriteUrl = $rewriteUrl;
return $this;
}
/**
* Set rewrite callback
* @param string $callback
* @return static
*/
public function setRewriteCallback($callback)
{
return $this->setRewriteRoute(new RouteUrl($this->uri, $callback));
}
/**
* Get loaded route
* @return ILoadableRoute|null
*/
public function getLoadedRoute()
{
return $this->loadedRoute;
}
/**
* Set loaded route
*
* @param ILoadableRoute $route
* @return static
*/
public function setLoadedRoute(ILoadableRoute $route)
{
$this->loadedRoute = $route;
return $this;
}
public function __isset($name)
{
return array_key_exists($name, $this->data);
}
public function __set($name, $value = null)
{
$this->data[$name] = $value;
+5 -4
View File
@@ -64,14 +64,14 @@ class Response
$this->headers([
'Cache-Control: public',
'Last-Modified: ' . gmdate('D, d M Y H:i:s', $lastModified) . ' GMT',
'Last-Modified: ' . gmdate("D, d M Y H:i:s", $lastModified) . ' GMT',
'Etag: ' . $eTag,
]);
$httpModified = $this->request->getHeader('http-if-modified-since');
$httpIfNoneMatch = $this->request->getHeader('http-if-none-match');
if (($httpIfNoneMatch !== null && $httpIfNoneMatch === $eTag) || ($httpModified !== null && strtotime($httpModified) === $lastModified)) {
if ($httpModified !== null && strtotime($httpModified) == $lastModified || $httpIfNoneMatch !== null && $httpIfNoneMatch === $eTag) {
$this->header('HTTP/1.1 304 Not Modified');
@@ -111,8 +111,9 @@ class Response
*/
public function headers(array $headers)
{
foreach ($headers as $header) {
$this->header($header);
$max = count($headers);
for ($i = 0; $i < $max; $i++) {
$this->header($headers[$i]);
}
return $this;
@@ -3,5 +3,4 @@ namespace Pecee\SimpleRouter\Exceptions;
class NotFoundHttpException extends HttpException
{
}
@@ -20,8 +20,9 @@ interface ILoadableRoute extends IRoute
* Loads and renders middlewares-classes
*
* @param Request $request
* @param ILoadableRoute $route
*/
public function loadMiddleware(Request $request);
public function loadMiddleware(Request $request, ILoadableRoute &$route);
public function getUrl();
@@ -50,19 +51,4 @@ interface ILoadableRoute extends IRoute
*/
public function setName($name);
/**
* Get regular expression match used for matching route (if defined).
*
* @return string
*/
public function getMatch();
/**
* Add regular expression match for the entire route.
*
* @param string $regex
* @return static
*/
public function setMatch($regex);
}
+17 -4
View File
@@ -8,19 +8,17 @@ interface IRoute
/**
* Method called to check if a domain matches
*
* @param string $route
* @param Request $request
* @return bool
*/
public function matchRoute($route, Request $request);
public function matchRoute(Request $request);
/**
* Called when route is matched.
* Returns class to be rendered.
*
* @param Request $request
* @throws \Pecee\SimpleRouter\Exceptions\NotFoundHttpException
* @return void
* @return object
*/
public function renderRoute(Request $request);
@@ -114,6 +112,21 @@ interface IRoute
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.
*
+31 -74
View File
@@ -7,66 +7,38 @@ use Pecee\SimpleRouter\Exceptions\HttpException;
abstract class LoadableRoute extends Route implements ILoadableRoute
{
/**
* @var string
*/
const PARAMETERS_REGEX_MATCH = '%s([\w\-\_]*?)\%s{0,1}%s';
protected $url;
/**
* @var string
*/
protected $name;
protected $regex;
/**
* Loads and renders middlewares-classes
*
* @param Request $request
* @param ILoadableRoute $route
* @throws HttpException
*/
public function loadMiddleware(Request $request)
public function loadMiddleware(Request $request, ILoadableRoute &$route)
{
$max = count($this->getMiddlewares());
if (count($this->getMiddlewares()) > 0) {
if ($max > 0) {
$max = count($this->getMiddlewares());
for ($i = 0; $i < $max; $i++) {
$middleware = $this->getMiddlewares()[$i];
$middleware = $this->loadClass($middleware);
if (($middleware instanceof IMiddleware) === false) {
if (!($middleware instanceof IMiddleware)) {
throw new HttpException($middleware . ' must be instance of Middleware');
}
$middleware->handle($request);
$middleware->handle($request, $route);
}
}
}
public function matchRegex(Request $request, $url)
{
/* Match on custom defined regular expression */
if ($this->regex === null) {
return null;
}
$parameters = [];
if (preg_match($this->regex, $request->getHost() . $url, $parameters) > 0) {
/* Remove global match */
$this->parameters = array_slice($parameters, 1);
return true;
}
return false;
}
/**
* Set url
*
@@ -81,9 +53,16 @@ abstract class LoadableRoute extends Route implements ILoadableRoute
$regex = sprintf(static::PARAMETERS_REGEX_MATCH, $this->paramModifiers[0], $this->paramOptionalSymbol, $this->paramModifiers[1]);
if (preg_match_all('/' . $regex . '/', $this->url, $matches)) {
$this->parameters = array_fill_keys($matches[1], null);
if (preg_match_all('/' . $regex . '/is', $this->url, $matches)) {
$max = count($matches[1]);
for ($i = 0; $i < $max; $i++) {
$this->parameters[$matches[1][$i]] = null;
}
}
}
return $this;
@@ -105,38 +84,39 @@ abstract class LoadableRoute extends Route implements ILoadableRoute
*/
public function findUrl($method = null, $parameters = null, $name = null)
{
$url = $this->getUrl();
$url = '';
$parameters = (array)$parameters;
if ($this->getGroup() !== null && count($this->getGroup()->getDomains()) > 0) {
$url = '//' . $this->getGroup()->getDomains()[0] . $url;
$url .= '//' . $this->getGroup()->getDomains()[0];
}
/* Contains parameters that aren't recognized and will be appended at the end of the url */
$url .= $this->getUrl();
$params = array_merge($this->getParameters(), $parameters);
/* Url that contains parameters that aren't recognized */
$unknownParams = [];
/* Create the param string - {parameter} */
/* Create the param string - {} */
$param1 = $this->paramModifiers[0] . '%s' . $this->paramModifiers[1];
/* Create the param string with the optional symbol - {parameter?} */
/* Create the param string with the optional symbol - {?} */
$param2 = $this->paramModifiers[0] . '%s' . $this->paramOptionalSymbol . $this->paramModifiers[1];
/* Replace any {parameter} in the url with the correct value */
/* Let's parse the values of any {} parameter in the url */
$params = $this->getParameters();
$max = count($params) - 1;
$keys = array_keys($params);
for ($i = $max; $i >= 0; $i--) {
$param = $keys[$i];
$value = $value = ($parameters !== null && array_key_exists($param, $parameters)) ? $parameters[$param] : $params[$param];
$value = $params[$param];
/* If parameter is specifically set to null - use the original-defined value */
if ($value === null && isset($this->originalParameters[$param])) {
$value = $this->originalParameters[$param];
}
$value = (isset($parameters[$param])) ? $parameters[$param] : $value;
if (stripos($url, $param1) !== false || stripos($url, $param) !== false) {
/* Add parameter to the correct position */
$url = str_ireplace([sprintf($param1, $param), sprintf($param2, $param)], $value, $url);
} else {
$unknownParams[$param] = $value;
@@ -169,29 +149,6 @@ abstract class LoadableRoute extends Route implements ILoadableRoute
return (strtolower($this->name) === strtolower($name));
}
/**
* Add regular expression match for the entire route.
*
* @param string $regex
* @return static
*/
public function setMatch($regex)
{
$this->regex = $regex;
return $this;
}
/**
* Get regular expression match used for matching route (if defined).
*
* @return string
*/
public function getMatch()
{
return $this->regex;
}
/**
* Sets the router name, which makes it easier to obtain the url or router at a later point.
* Alias for LoadableRoute::setName().
+120 -84
View File
@@ -2,12 +2,11 @@
namespace Pecee\SimpleRouter\Route;
use Pecee\Http\Request;
use Pecee\SimpleRouter\Exceptions\HttpException;
use Pecee\SimpleRouter\Exceptions\NotFoundHttpException;
abstract class Route implements IRoute
{
const PARAMETERS_REGEX_MATCH = '%s([\w]+)(\%s?)%s';
const REQUEST_TYPE_GET = 'get';
const REQUEST_TYPE_POST = 'post';
const REQUEST_TYPE_PUT = 'put';
@@ -24,13 +23,6 @@ abstract class Route implements IRoute
self::REQUEST_TYPE_DELETE,
];
/**
* If enabled parameters containing null-value
* will not be passed along to the callback.
*
* @var bool
*/
protected $filterEmptyParams = false;
protected $paramModifiers = '{}';
protected $paramOptionalSymbol = '?';
protected $group;
@@ -40,109 +32,138 @@ abstract class Route implements IRoute
/* Default options */
protected $namespace;
protected $regex;
protected $requestMethods = [];
protected $where = [];
protected $parameters = [];
protected $originalParameters = [];
protected $middlewares = [];
protected function loadClass($name)
{
if (class_exists($name) === false) {
throw new NotFoundHttpException(sprintf('Class %s does not exist', $name), 404);
}
return new $name();
}
public function renderRoute(Request $request)
{
$callback = $this->getCallback();
if ($callback !== null && is_callable($callback)) {
if ($this->getCallback() !== null && is_callable($this->getCallback())) {
/* When the callback is a function */
call_user_func_array($callback, $this->getParameters());
call_user_func_array($this->getCallback(), $this->getParameters());
} else {
/* When the callback is a method */
$controller = explode('@', $callback);
$namespace = $this->getNamespace();
$className = ($namespace !== null && $controller[0][0] !== '\\') ? $namespace . '\\' . $controller[0] : $controller[0];
$controller = explode('@', $this->getCallback());
$className = $this->getNamespace() . '\\' . $controller[0];
$class = $this->loadClass($className);
$method = $controller[1];
if (method_exists($class, $method) === false) {
if (!method_exists($class, $method)) {
throw new NotFoundHttpException(sprintf('Method %s does not exist in class %s', $method, $className), 404);
}
$parameters = $this->getParameters();
/* Filter parameters with null-value */
if ($this->filterEmptyParams === true) {
$parameters = array_filter($parameters, function ($var) {
return ($var !== null);
});
}
$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]+')
{
$regex = sprintf(static::PARAMETERS_REGEX_MATCH, $this->paramModifiers[0], $this->paramOptionalSymbol, $this->paramModifiers[1]);
$parameterNames = [];
$regex = '';
$lastCharacter = '';
$isParameter = false;
$parameter = '';
$routeLength = strlen($route) - 1;
$parameters = [];
for ($i = $routeLength; $i >= 0; $i--) {
if (preg_match_all('/' . $regex . '/', $route, $parameters)) {
$urlParts = preg_split('/((\-?\/?)\{[^}]+\})/', rtrim($route, '/'));
foreach ($urlParts as $key => $t) {
$regex = '';
if ($key < count($parameters[1])) {
$name = $parameters[1][$key];
$regex = isset($this->where[$name]) ? $this->where[$name] : $parameterRegex;
$regex = sprintf('\-?\/?(?P<%s>%s)', $name, $regex) . $parameters[2][$key];
$character = strrev($route)[$i];
if ($character === '{') {
/* Remove "/" and "\" from regex */
if (substr($regex, strlen($regex) - 1) === '/') {
$regex = substr($regex, 0, strlen($regex) - 2);
}
$urlParts[$key] = preg_quote($t, '/') . $regex;
$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);
}
$urlRegex = join('', $urlParts);
} else {
$urlRegex = preg_quote($route, '/');
$lastCharacter = $character;
}
if (preg_match('/^' . $urlRegex . '(\/?)$/', $url, $matches) > 0) {
$parameterValues = [];
$values = [];
if (preg_match('/^' . $regex . '\/?$/is', $url, $parameterValues)) {
if (isset($parameters[1])) {
$parameters = [];
/* Only take matched parameters with name */
foreach ($parameters[1] as $name) {
$values[$name] = (isset($matches[$name]) && $matches[$name] !== '') ? $matches[$name] : null;
$max = count($parameterNames) - 1;
for ($i = $max; $i >= 0; $i--) {
$name = $parameterNames[$i];
$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 $values;
return $parameters;
}
return null;
}
protected function loadClass($name)
{
if (!class_exists($name)) {
throw new NotFoundHttpException(sprintf('Class %s does not exist', $name), 404);
}
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
@@ -318,6 +339,29 @@ abstract class Route implements IRoute
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.
*
@@ -339,6 +383,13 @@ abstract class Route implements IRoute
$values['where'] = $this->where;
}
if (count($this->parameters) > 0) {
/* Ensure the right order + values */
$parameters = ($values['parameters'] + $this->parameters);
$parameters = array_merge($parameters, $this->parameters);
$this->setParameters($parameters);
}
if (count($this->middlewares) > 0) {
$values['middleware'] = $this->middlewares;
}
@@ -355,7 +406,7 @@ abstract class Route implements IRoute
*/
public function setSettings(array $values, $merge = false)
{
if ($this->namespace === null && isset($values['namespace'])) {
if (isset($values['namespace']) && $this->namespace === null) {
$this->setNamespace($values['namespace']);
}
@@ -412,7 +463,7 @@ abstract class Route implements IRoute
*/
public function where(array $options)
{
return $this->setWhere($options);
return $this->where($options);
}
/**
@@ -422,14 +473,7 @@ abstract class Route implements IRoute
*/
public function getParameters()
{
/* Sort the parameters after the user-defined param order, if any */
$parameters = [];
if (count($this->originalParameters) > 0) {
$parameters = $this->originalParameters;
}
return array_merge($parameters, $this->parameters);
return $this->parameters;
}
/**
@@ -440,15 +484,7 @@ abstract class Route implements IRoute
*/
public function setParameters(array $parameters)
{
/*
* If this is the first time setting parameters we store them so we
* later can organize the array, in case somebody tried to sort the array.
*/
if (count($parameters) > 0 && count($this->originalParameters) === 0) {
$this->originalParameters = $parameters;
}
$this->parameters = array_merge($this->parameters, $parameters);
$this->parameters = $parameters;
return $this;
}
@@ -2,6 +2,7 @@
namespace Pecee\SimpleRouter\Route;
use Pecee\Http\Request;
use Pecee\SimpleRouter\Exceptions\NotFoundHttpException;
class RouteController extends LoadableRoute implements IControllerRoute
{
@@ -30,11 +31,11 @@ class RouteController extends LoadableRoute implements IControllerRoute
}
/* Remove method/type */
if (strpos($name, '.') !== false) {
if (stripos($name, '.') !== false) {
$method = substr($name, strrpos($name, '.') + 1);
$newName = substr($name, 0, strrpos($name, '.'));
if (in_array($method, $this->names, false) === true && strtolower($this->name) === strtolower($newName)) {
if (strtolower($this->name) === strtolower($newName) && in_array($method, $this->names)) {
return true;
}
}
@@ -42,31 +43,31 @@ class RouteController extends LoadableRoute implements IControllerRoute
return parent::hasName($name);
}
/**
* @param string|null $method
* @param string|array|null $parameters
* @param string|null $name
* @return string
*/
public function findUrl($method = null, $parameters = null, $name = null)
{
if (strpos($name, '.') !== false) {
$found = array_search(substr($name, strrpos($name, '.') + 1), $this->names, false);
if (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) {
/* Remove requestType from method-name, if it exists */
foreach (static::$requestTypes as $requestType) {
$max = count(static::$requestTypes);
for ($i = 0; $i < $max; $i++) {
$requestType = static::$requestTypes[$i];
if (stripos($method, $requestType) === 0) {
$method = (string)substr($method, strlen($requestType));
$method = substr($method, strlen($requestType));
break;
}
}
@@ -83,35 +84,59 @@ class RouteController extends LoadableRoute implements IControllerRoute
return '/' . trim($url, '/') . '/';
}
public function matchRoute($url, Request $request)
public function renderRoute(Request $request)
{
$url = parse_url(urldecode($url), PHP_URL_PATH);
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, '/') . '/';
/* Match global regular-expression for route */
$regexMatch = $this->matchRegex($request, $url);
if (strtolower($url) == strtolower($this->url) || stripos($url, $this->url) === 0) {
if ($regexMatch === false || (stripos($url, $this->url) !== 0 && strtolower($url) !== strtolower($this->url))) {
return false;
$strippedUrl = trim(str_ireplace($this->url, '/', $url), '/');
$path = explode('/', $strippedUrl);
if (count($path) > 0) {
$method = (!isset($path[0]) || 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;
}
}
$strippedUrl = trim(str_ireplace($this->url, '/', $url), '/');
$path = explode('/', $strippedUrl);
if (count($path) > 0) {
$method = (isset($path[0]) === false || trim($path[0]) === '') ? $this->defaultMethod : $path[0];
$this->method = $request->getMethod() . ucfirst($method);
$this->parameters = array_slice($path, 1);
// Set callback
$this->setCallback($this->controller . '@' . $this->method);
return true;
}
return false;
return null;
}
/**
+15 -17
View File
@@ -18,36 +18,38 @@ class RouteGroup extends Route implements IGroupRoute
*/
public function matchDomain(Request $request)
{
if (count($this->domains) === 0) {
return true;
}
if (count($this->domains) > 0) {
foreach ($this->domains as $domain) {
$max = count($this->domains) - 1;
$parameters = $this->parseParameters($domain, $request->getHost(), '.*');
for ($i = $max; $i >= 0; $i--) {
if ($parameters !== null && count($parameters) > 0) {
$domain = $this->domains[$i];
$parameters = $this->parseParameters($domain, $request->getHost(), '.*');
$this->parameters = $parameters;
if ($parameters !== null) {
$this->parameters = $parameters;
return true;
return true;
}
}
return false;
}
return false;
return true;
}
/**
* Method called to check if route matches
*
* @param string $url
* @param Request $request
* @return bool
*/
public function matchRoute($url, Request $request)
public function matchRoute(Request $request)
{
/* Skip if prefix doesn't match */
if ($this->prefix !== null && stripos($url, $this->prefix) === false) {
// Skip if prefix doesn't match
if ($this->prefix !== null && stripos($request->getUri(), $this->prefix) === false) {
return false;
}
@@ -173,10 +175,6 @@ class RouteGroup extends Route implements IGroupRoute
$values['as'] = $this->name;
}
if (count($this->parameters) > 0) {
$values['parameters'] = $this->parameters;
}
return array_merge($values, parent::toArray());
}
+78 -96
View File
@@ -2,6 +2,7 @@
namespace Pecee\SimpleRouter\Route;
use Pecee\Http\Request;
use Pecee\SimpleRouter\Exceptions\NotFoundHttpException;
class RouteResource extends LoadableRoute implements IControllerRoute
{
@@ -14,17 +15,6 @@ class RouteResource extends LoadableRoute implements IControllerRoute
'update' => '',
'destroy' => '',
];
protected $methodNames = [
'index' => 'index',
'create' => 'create',
'store' => 'store',
'show' => 'show',
'edit' => 'edit',
'update' => 'update',
'destroy' => 'destroy',
];
protected $names = [];
protected $controller;
@@ -52,8 +42,8 @@ class RouteResource extends LoadableRoute implements IControllerRoute
}
/* Remove method/type */
if (strpos($name, '.') !== false) {
$name = (string)substr($name, 0, strrpos($name, '.'));
if (stripos($name, '.') !== false) {
$name = substr($name, 0, strrpos($name, '.'));
}
return (strtolower($this->name) === strtolower($name));
@@ -61,80 +51,99 @@ class RouteResource extends LoadableRoute implements IControllerRoute
public function findUrl($method = null, $parameters = null, $name = null)
{
$url = array_search($name, $this->names, false);
if ($url !== false) {
return rtrim($this->url . $this->urls[$url], '/') . '/';
$method = array_search($name, $this->names);
if ($method !== false) {
return rtrim($this->url . $this->urls[$method], '/') . '/';
}
return $this->url;
}
protected function call($method)
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($url, Request $request)
public function matchRoute(Request $request)
{
$url = parse_url(urldecode($url), PHP_URL_PATH);
$url = parse_url(urldecode($request->getUri()), PHP_URL_PATH);
$url = rtrim($url, '/') . '/';
/* Match global regular-expression for route */
$regexMatch = $this->matchRegex($request, $url);
if ($regexMatch === false || (stripos($url, $this->url) !== 0 && strtolower($url) !== strtolower($this->url))) {
return false;
}
$route = rtrim($this->url, '/') . '/{id?}/{action?}';
$this->parameters = $this->parseParameters($route, $url);
if ($this->parameters === null) {
return false;
$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);
}
$action = strtolower(trim($this->parameters['action']));
$id = $this->parameters['id'];
// Remove action parameter
unset($this->parameters['action']);
$method = $request->getMethod();
// Delete
if ($method === static::REQUEST_TYPE_DELETE && $id !== null) {
return $this->call($this->methodNames['destroy']);
}
// Update
if ($id !== null && in_array($method, [static::REQUEST_TYPE_PATCH, static::REQUEST_TYPE_PUT], false) === true) {
return $this->call($this->methodNames['update']);
}
// Edit
if ($method === static::REQUEST_TYPE_GET && $id !== null && $action === 'edit') {
return $this->call($this->methodNames['edit']);
}
// Create
if ($method === static::REQUEST_TYPE_GET && $id === 'create') {
return $this->call($this->methodNames['create']);
}
// Save
if ($method === static::REQUEST_TYPE_POST) {
return $this->call($this->methodNames['store']);
}
// Show
if ($method === static::REQUEST_TYPE_GET && $id !== null) {
return $this->call($this->methodNames['show']);
}
// Index
return $this->call($this->methodNames['index']);
return null;
}
/**
@@ -173,29 +182,6 @@ class RouteResource extends LoadableRoute implements IControllerRoute
return $this;
}
/**
* Define custom method name for resource controller
*
* @param array $names
* @return static $this
*/
public function setMethodNames(array $names)
{
$this->methodNames = $names;
return $this;
}
/**
* Get method names
*
* @return array $this
*/
public function getMethodNames()
{
return $this->methodNames;
}
/**
* Merge with information from another route.
*
@@ -209,10 +195,6 @@ class RouteResource extends LoadableRoute implements IControllerRoute
$this->names = $values['names'];
}
if (isset($values['methods'])) {
$this->methodNames = $values['methods'];
}
parent::setSettings($values, $merge);
return $this;
+26 -13
View File
@@ -11,26 +11,39 @@ class RouteUrl extends LoadableRoute
$this->setCallback($callback);
}
public function matchRoute($url, Request $request)
public function matchRoute(Request $request)
{
$url = parse_url(urldecode($url), PHP_URL_PATH);
$url = parse_url(urldecode($request->getUri()), PHP_URL_PATH);
$url = rtrim($url, '/') . '/';
/* Match global regular-expression for route */
$regexMatch = $this->matchRegex($request, $url);
if ($regexMatch === false) {
return false;
// Match on custom defined regular expression
if ($this->regex !== null) {
$parameters = [];
if (preg_match($this->regex, $request->getHost() . $url, $parameters)) {
/* Remove global match */
if (count($parameters) > 1) {
array_shift($parameters);
$this->parameters = $parameters;
}
return true;
}
return null;
}
/* Make regular expression based on route */
$parameters = $this->parseParameters($this->url, $url);
if ($parameters === null) {
return false;
// 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;
}
$this->setParameters($parameters);
return true;
return null;
}
}
+97 -100
View File
@@ -69,6 +69,37 @@ class Router
*/
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();
@@ -106,29 +137,16 @@ class Router
return $route;
}
/**
* Process added routes.
*
* @param array $routes
* @param IGroupRoute|null $group
* @param IRoute|null $parent
* @throws NotFoundHttpException
*/
protected function processRoutes(array $routes, IGroupRoute $group = null, IRoute $parent = null)
{
// Loop through each route-request
$max = count($routes) - 1;
$exceptionHandlers = [];
$url = ($this->request->getRewriteUrl() !== null) ? $this->request->getRewriteUrl() : $this->request->getUri();
/* @var $route IRoute */
for ($i = $max; $i >= 0; $i--) {
$route = $routes[$i];
/* @var $route IGroupRoute */
if ($route instanceof IGroupRoute) {
$group = $route;
@@ -139,11 +157,11 @@ class Router
$route->renderRoute($this->request);
$this->processingRoute = false;
if ($route->matchRoute($url, $this->request) === true) {
if ($route->matchRoute($this->request)) {
/* Add exception handlers */
/* Add exceptionhandlers */
if (count($route->getExceptionHandlers()) > 0) {
$exceptionHandlers += $route->getExceptionHandlers();
$this->exceptionHandlers = array_merge($route->getExceptionHandlers(), $this->exceptionHandlers);
}
}
@@ -154,6 +172,7 @@ class Router
/* Add the parent group */
$route->setGroup($group);
}
if ($parent !== null) {
@@ -174,7 +193,7 @@ class Router
if (count($this->routeStack) > 0) {
/* Pop and grab the routes added when executing group callback earlier */
/* Pop and grap the routes added when executing group callback earlier */
$stack = $this->routeStack;
$this->routeStack = [];
@@ -182,84 +201,69 @@ class Router
$this->processRoutes($stack, $route, $group);
}
}
$this->exceptionHandlers = array_unique(array_merge($exceptionHandlers, $this->exceptionHandlers));
}
/**
* Load routes
* @throws NotFoundHttpException
* @return void
*/
public function loadRoutes()
{
/* Initialize boot-managers */
if (count($this->bootManagers) > 0) {
$max = count($this->bootManagers) - 1;
/* @var $manager IRouterBootManager */
for ($i = $max; $i >= 0; $i--) {
$manager = $this->bootManagers[$i];
$manager->boot($this->request);
}
}
/* Loop through each route-request */
$this->processRoutes($this->routes);
}
public function routeRequest($rewrite = false)
{
$this->loadedRoute = null;
$routeNotAllowed = false;
try {
if ($rewrite === false) {
$this->loadRoutes();
/* Initialize boot-managers */
if (count($this->bootManagers) > 0) {
if ($this->csrfVerifier !== null) {
$max = count($this->bootManagers) - 1;
/* Verify csrf token for request */
$this->csrfVerifier->handle($this->request);
/* @var $manager IRouterBootManager */
for ($i = $max; $i >= 0; $i--) {
$manager = $this->bootManagers[$i];
$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);
}
}
}
$url = ($this->request->getRewriteUrl() !== null) ? $this->request->getRewriteUrl() : $this->request->getUri();
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();
}
$max = count($this->processedRoutes) - 1;
/* @var $route ILoadableRoute */
/* @var $route IRoute */
for ($i = $max; $i >= 0; $i--) {
$route = $this->processedRoutes[$i];
/* If the route matches */
if ($route->matchRoute($url, $this->request) === true) {
if ($route->matchRoute($this->request)) {
/* Check if request method matches */
if (count($route->getRequestMethods()) > 0 && in_array($this->request->getMethod(), $route->getRequestMethods(), false) === false) {
if (count($route->getRequestMethods()) > 0 && !in_array($this->request->getMethod(), $route->getRequestMethods())) {
$routeNotAllowed = true;
continue;
}
$route->loadMiddleware($this->request);
$this->loadedRoute = $route;
$this->loadedRoute->loadMiddleware($this->request, $this->loadedRoute);
$rewriteRoute = $this->request->getRewriteRoute();
if ($rewriteRoute !== null) {
$rewriteRoute->loadMiddleware($this->request);
$rewriteRoute->renderRoute($this->request);
return;
}
/* If the request has changed */
$rewriteUrl = $this->request->getRewriteUrl();
if ($rewriteUrl !== null && $rewriteUrl !== $url) {
unset($this->processedRoutes[$i]);
$this->processedRoutes = array_values($this->processedRoutes);
/* 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;
@@ -267,8 +271,8 @@ class Router
/* Render route */
$routeNotAllowed = false;
$this->request->setLoadedRoute($route);
$route->renderRoute($this->request);
$this->request->setUri($this->originalUrl);
$this->loadedRoute->renderRoute($this->request);
break;
}
@@ -282,48 +286,35 @@ class Router
$this->handleException(new HttpException('Route or method not allowed', 403));
}
if ($this->request->getLoadedRoute() === null) {
if ($this->loadedRoute === null) {
$this->handleException(new NotFoundHttpException('Route not found: ' . $this->request->getUri(), 404));
}
}
protected function handleException(\Exception $e)
{
$url = ($this->request->getRewriteUrl() !== null) ? $this->request->getRewriteUrl() : $this->request->getUri();
$max = count($this->exceptionHandlers);
/* @var $handler IExceptionHandler */
for ($i = 0; $i < $max; $i++) {
$handler = $this->exceptionHandlers[$i];
$handler = new $handler();
if (($handler instanceof IExceptionHandler) === false) {
if (!($handler instanceof IExceptionHandler)) {
throw new HttpException('Exception handler must implement the IExceptionHandler interface.', 500);
}
if ($handler->handleError($this->request, $e) !== null) {
$request = $handler->handleError($this->request, $this->loadedRoute, $e);
$rewriteRoute = $this->request->getRewriteRoute();
/* 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);
if ($rewriteRoute !== null) {
$rewriteRoute->loadMiddleware($this->request);
$rewriteRoute->renderRoute($this->request);
return;
}
$rewriteUrl = $this->request->getRewriteUrl();
/* If the request has changed */
if ($rewriteUrl !== null && $rewriteUrl !== $url) {
unset($this->exceptionHandlers[$i]);
$this->exceptionHandlers = array_values($this->exceptionHandlers);
$this->routeRequest(true);
return;
}
return;
}
}
@@ -336,7 +327,7 @@ class Router
if ($includeEmpty === false) {
$getParams = array_filter($getParams, function ($item) {
return (trim($item) !== '');
return (!empty($item));
});
}
@@ -384,7 +375,7 @@ class Router
if (strpos($name, '@') !== false && strpos($route->getCallback(), '@') !== false && !is_callable($route->getCallback())) {
/* Check if the entire callback is matching */
if (strpos($route->getCallback(), $name) === 0 || strtolower($route->getCallback()) === strtolower($name)) {
if (strtolower($route->getCallback()) === strtolower($name) || strpos($route->getCallback(), $name) === 0) {
return $route;
}
@@ -413,7 +404,6 @@ class Router
* @param string|null $name
* @param string|array|null $parameters
* @param array|null $getParams
* @throws \InvalidArgumentException
* @return string
*/
public function getUrl($name = null, $parameters = null, $getParams = null)
@@ -436,11 +426,9 @@ class Router
return (($url === '') ? '/' : $url . '/') . $this->arrayToParams($getParams);
}
$loadedRoute = $this->request->getLoadedRoute();
/* If nothing is defined and a route is loaded we use that */
if ($name === null && $loadedRoute !== null) {
return $loadedRoute->findUrl($loadedRoute->getMethod(), $parameters, $name) . $this->arrayToParams($getParams);
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 */
@@ -451,7 +439,7 @@ class Router
}
/* Using @ is most definitely a controller@method or alias@method */
if (strpos($name, '@') !== false) {
if (stripos($name, '@') !== false) {
list($controller, $method) = explode('@', $name);
/* Loop through all the routes to see if we can find a match */
@@ -549,4 +537,13 @@ class Router
return $this;
}
/**
* Get loaded route
* @return ILoadableRoute|null
*/
public function getLoadedRoute()
{
return $this->loadedRoute;
}
}
+10 -41
View File
@@ -33,12 +33,6 @@ class SimpleRouter
*/
protected static $response;
/**
* Router instance
* @var Router
*/
protected static $router;
/**
* Start/route request
*
@@ -218,7 +212,7 @@ class SimpleRouter
* @param string $url
* @param string|\Closure $callback
* @param array|null $settings
* @return RouteUrl|IRoute
* @return RouteUrl
*/
public static function match(array $requestMethods, $url, $callback, array $settings = null)
{
@@ -241,7 +235,7 @@ class SimpleRouter
* @param string $url
* @param string|\Closure $callback
* @param array|null $settings
* @return RouteUrl|IRoute
* @return RouteUrl
*/
public static function all($url, $callback, array $settings = null)
{
@@ -263,7 +257,7 @@ class SimpleRouter
* @param string $url
* @param string $controller
* @param array|null $settings
* @return RouteController|IRoute
* @return RouteController
*/
public static function controller($url, $controller, array $settings = null)
{
@@ -285,12 +279,11 @@ class SimpleRouter
* @param string $url
* @param string $controller
* @param array|null $settings
* @return RouteResource|IRoute
* @return RouteResource
*/
public static function resource($url, $controller, array $settings = null)
{
$route = new RouteResource($url, $controller);
$route = static::addDefaultNamespace($route);
if ($settings !== null) {
$route->setSettings($settings);
@@ -316,7 +309,6 @@ class SimpleRouter
* @param string|null $name
* @param string|array|null $parameters
* @param array|null $getParams
* @throws \Exception
* @return string
*/
public static function getUrl($name = null, $parameters = null, $getParams = null)
@@ -355,11 +347,7 @@ class SimpleRouter
*/
public static function router()
{
if(static::$router === null) {
static::$router = new Router();
}
return static::$router;
return Router::getInstance();
}
/**
@@ -371,35 +359,16 @@ class SimpleRouter
protected static function addDefaultNamespace(IRoute $route)
{
if (static::$defaultNamespace !== null) {
$namespace = static::$defaultNamespace;
$callback = $route->getCallback();
/* Only add default namespace on relative callbacks */
if ($callback === null || $callback[0] !== '\\') {
$namespace = static::$defaultNamespace;
$currentNamespace = $route->getNamespace();
if ($currentNamespace !== null) {
$namespace .= '\\' . $currentNamespace;
}
$route->setDefaultNamespace($namespace);
if ($route->getNamespace() !== null) {
$namespace .= '\\' . $route->getNamespace();
}
$route->setDefaultNamespace($namespace);
}
return $route;
}
/**
* Get default namespace
* @return string
*/
public static function getDefaultNamespace()
{
return static::$defaultNamespace;
}
}
+10 -20
View File
@@ -2,34 +2,24 @@
class DummyController
{
public function method1()
public function start()
{
echo static::class . '@' . 'start() OK';
}
public function method2()
{
}
public function param($params = null)
{
echo join(', ', func_get_args());
$params = func_get_args();
echo 'Params: ' . join(', ', $params);
}
public function getTest()
{
echo 'getTest';
}
public function notFound()
{
echo 'not found';
}
public function postTest()
{
echo 'postTest';
}
public function silent() {
public function putTest()
{
echo 'putTest';
}
}
}
+3 -2
View File
@@ -1,11 +1,12 @@
<?php
require_once 'Exceptions/MiddlewareLoadedException.php';
use Pecee\Http\Middleware\IMiddleware;
use Pecee\Http\Request;
class DummyMiddleware implements \Pecee\Http\Middleware\IMiddleware
class DummyMiddleware implements IMiddleware
{
public function handle(Request $request)
public function handle(Request $request, \Pecee\SimpleRouter\Route\ILoadableRoute &$route)
{
throw new MiddlewareLoadedException('Middleware loaded!');
}
@@ -1,4 +0,0 @@
<?php
class ExceptionHandlerException extends \Exception
{
}
@@ -1,18 +0,0 @@
<?php
class ResponseException extends \Exception
{
protected $response;
public function __construct($response)
{
$this->response = $response;
parent::__construct('', 0);
}
public function getResponse()
{
return $this->response;
}
}
+2 -2
View File
@@ -2,9 +2,9 @@
class ExceptionHandler implements \Pecee\Handlers\IExceptionHandler
{
public function handleError(\Pecee\Http\Request $request, \Exception $error)
public function handleError(\Pecee\Http\Request $request, \Pecee\SimpleRouter\Route\ILoadableRoute &$route = null, \Exception $error)
{
echo $error->getMessage();
throw $error;
}
}
@@ -1,14 +0,0 @@
<?php
class ExceptionHandlerFirst implements \Pecee\Handlers\IExceptionHandler
{
public function handleError(\Pecee\Http\Request $request, \Exception $error)
{
global $stack;
$stack[] = static::class;
$request->setUri('/');
return $request;
}
}
@@ -1,14 +0,0 @@
<?php
class ExceptionHandlerSecond implements \Pecee\Handlers\IExceptionHandler
{
public function handleError(\Pecee\Http\Request $request, \Exception $error)
{
global $stack;
$stack[] = static::class;
$request->setUri('/');
return $request;
}
}
@@ -1,13 +0,0 @@
<?php
class ExceptionHandlerThird implements \Pecee\Handlers\IExceptionHandler
{
public function handleError(\Pecee\Http\Request $request, \Exception $error)
{
global $stack;
$stack[] = static::class;
throw new ResponseException('ExceptionHandler loaded');
}
}
-39
View File
@@ -1,39 +0,0 @@
<?php
class ResourceController implements \Pecee\Controllers\IResourceController
{
public function index()
{
echo 'index';
}
public function show($id)
{
echo 'show ' . $id;
}
public function store()
{
echo 'store';
}
public function create()
{
echo 'create';
}
public function edit($id)
{
echo 'edit ' . $id;
}
public function update($id)
{
echo 'update ' . $id;
}
public function destroy($id)
{
echo 'destroy ' . $id;
}
}
+61 -49
View File
@@ -2,82 +2,94 @@
require_once 'Dummy/DummyMiddleware.php';
require_once 'Dummy/DummyController.php';
require_once 'Helpers/TestRouter.php';
use Pecee\SimpleRouter\SimpleRouter as SimpleRouter;
class GroupTest extends PHPUnit_Framework_TestCase
{
protected $result;
protected $result;
public function testGroupLoad()
{
$this->result = false;
public function testGroupLoad()
{
$this->result = false;
TestRouter::group(['prefix' => '/group'], function () {
$this->result = true;
});
SimpleRouter::group(['prefix' => '/group'], function () {
$this->result = true;
});
try {
TestRouter::debug('/', 'get');
} catch(\Exception $e) {
try {
SimpleRouter::start();
} catch (Exception $e) {
// ignore RouteNotFound exception
}
}
$this->assertTrue($this->result);
}
$this->assertTrue($this->result);
}
public function testNestedGroup()
{
public function testNestedGroup()
{
TestRouter::group(['prefix' => '/api'], function () {
SimpleRouter::router()->reset();
SimpleRouter::request()->setUri('/api/v1/test');
SimpleRouter::request()->setMethod('get');
TestRouter::group(['prefix' => '/v1'], function () {
TestRouter::get('/test', 'DummyController@method1');
});
SimpleRouter::group(['prefix' => '/api'], function () {
});
SimpleRouter::group(['prefix' => '/v1'], function () {
SimpleRouter::get('/test', 'DummyController@start');
});
TestRouter::debug('/api/v1/test', 'get');
});
}
SimpleRouter::start();
}
public function testMultipleRoutes()
{
public function testManyRoutes()
{
TestRouter::group(['prefix' => '/api'], function () {
SimpleRouter::router()->reset();
SimpleRouter::request()->setUri('/my/match');
SimpleRouter::request()->setMethod('get');
TestRouter::group(['prefix' => '/v1'], function () {
TestRouter::get('/test', 'DummyController@method1');
});
SimpleRouter::group(['prefix' => '/api'], function () {
});
SimpleRouter::group(['prefix' => '/v1'], function () {
SimpleRouter::get('/test', 'DummyController@start');
});
TestRouter::get('/my/match', 'DummyController@method1');
});
TestRouter::group(['prefix' => '/service'], function () {
SimpleRouter::get('/my/match', 'DummyController@start');
TestRouter::group(['prefix' => '/v1'], function () {
TestRouter::get('/no-match', 'DummyController@method1');
});
SimpleRouter::group(['prefix' => '/service'], function () {
});
SimpleRouter::group(['prefix' => '/v1'], function () {
SimpleRouter::get('/no-match', 'DummyController@start');
});
TestRouter::debug('/my/match', 'get');
}
});
public function testUrls()
{
// Test array name
TestRouter::get('/my/fancy/url/1', 'DummyController@method1', ['as' => 'fancy1']);
SimpleRouter::start();
}
// Test method name
TestRouter::get('/my/fancy/url/2', 'DummyController@method1')->setName('fancy2');
public function testUrls()
{
TestRouter::debugNoReset('/my/fancy/url/1');
SimpleRouter::router()->reset();
SimpleRouter::request()->setUri('/my/fancy/url/1');
SimpleRouter::request()->setMethod('get');
$this->assertEquals('/my/fancy/url/1/', TestRouter::getUrl('fancy1'));
$this->assertEquals('/my/fancy/url/2/', TestRouter::getUrl('fancy2'));
// Test array name
SimpleRouter::get('/my/fancy/url/1', 'DummyController@start', ['as' => 'fancy1']);
TestRouter::router()->reset();
// 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'));
}
}
-41
View File
@@ -1,41 +0,0 @@
<?php
class TestRouter extends \Pecee\SimpleRouter\SimpleRouter
{
public static function debugNoReset($testUri, $testMethod = 'get')
{
static::request()->setUri($testUri);
static::request()->setMethod($testMethod);
static::start();
}
public static function debug($testUri, $testMethod = 'get')
{
try {
static::debugNoReset($testUri, $testMethod);
} catch(\Exception $e) {
static::router()->reset();
throw $e;
}
static::router()->reset();
}
public static function debugOutput($testUri, $testMethod = 'get')
{
$response = null;
// Route request
ob_start();
static::debug($testUri, $testMethod);
$response = ob_get_contents();
ob_end_clean();
// Return response
return $response;
}
}
+18 -20
View File
@@ -3,32 +3,30 @@
require_once 'Dummy/DummyMiddleware.php';
require_once 'Dummy/DummyController.php';
require_once 'Dummy/Handler/ExceptionHandler.php';
require_once 'Helpers/TestRouter.php';
use Pecee\SimpleRouter\SimpleRouter as SimpleRouter;
class MiddlewareTest extends PHPUnit_Framework_TestCase
{
public function testMiddlewareFound()
{
$this->setExpectedException(MiddlewareLoadedException::class);
public function testMiddlewareFound()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setMethod('get');
SimpleRouter::request()->setUri('/my/test/url');
TestRouter::group(['exceptionHandler' => 'ExceptionHandler'], function () {
TestRouter::get('/my/test/url', 'DummyController@method1', ['middleware' => 'DummyMiddleware']);
});
SimpleRouter::group(['exceptionHandler' => 'ExceptionHandler'], function () {
SimpleRouter::get('/my/test/url', 'DummyController@start', ['middleware' => 'DummyMiddleware']);
});
TestRouter::debug('/my/test/url', 'get');
$found = false;
}
try {
SimpleRouter::start();
} catch (\Exception $e) {
$found = ($e instanceof MiddlewareLoadedException);
}
public function testNestedMiddlewareDontLoad()
{
TestRouter::group(['exceptionHandler' => 'ExceptionHandler', 'middleware' => 'DummyMiddleware'], function () {
TestRouter::get('/middleware', 'DummyController@method1');
});
TestRouter::get('/my/test/url', 'DummyController@method1');
TestRouter::debug('/my/test/url', 'get');
}
$this->assertTrue($found);
}
}
-42
View File
@@ -1,42 +0,0 @@
<?php
require_once 'Dummy/DummyController.php';
require_once 'Helpers/TestRouter.php';
class RouterControllerTest extends PHPUnit_Framework_TestCase
{
public function testGet()
{
// Match normal route on alias
TestRouter::controller('/url', 'DummyController');
$response = TestRouter::debugOutput('/url/test', 'get');
$this->assertEquals('getTest', $response);
}
public function testPost()
{
// Match normal route on alias
TestRouter::controller('/url', 'DummyController');
$response = TestRouter::debugOutput('/url/test', 'post');
$this->assertEquals('postTest', $response);
}
public function testPut()
{
// Match normal route on alias
TestRouter::controller('/url', 'DummyController');
$response = TestRouter::debugOutput('/url/test', 'put');
$this->assertEquals('putTest', $response);
}
}
-70
View File
@@ -1,70 +0,0 @@
<?php
require_once 'Dummy/ResourceController.php';
require_once 'Helpers/TestRouter.php';
class RouterResourceTest extends PHPUnit_Framework_TestCase
{
public function testResourceStore()
{
TestRouter::resource('/resource', 'ResourceController');
$response = TestRouter::debugOutput('/resource', 'post');
$this->assertEquals('store', $response);
}
public function testResourceCreate()
{
TestRouter::resource('/resource', 'ResourceController');
$response = TestRouter::debugOutput('/resource/create', 'get');
$this->assertEquals('create', $response);
}
public function testResourceIndex()
{
TestRouter::resource('/resource', 'ResourceController');
$response = TestRouter::debugOutput('/resource', 'get');
$this->assertEquals('index', $response);
}
public function testResourceDestroy()
{
TestRouter::resource('/resource', 'ResourceController');
$response = TestRouter::debugOutput('/resource/38', 'delete');
$this->assertEquals('destroy 38', $response);
}
public function testResourceEdit()
{
TestRouter::resource('/resource', 'ResourceController');
$response = TestRouter::debugOutput('/resource/38/edit', 'get');
$this->assertEquals('edit 38', $response);
}
public function testResourceUpdate()
{
TestRouter::resource('/resource', 'ResourceController');
$response = TestRouter::debugOutput('/resource/38', 'put');
$this->assertEquals('update 38', $response);
}
public function testResourceGet()
{
TestRouter::resource('/resource', 'ResourceController');
$response = TestRouter::debugOutput('/resource/38', 'get');
$this->assertEquals('show 38', $response);
}
}
-60
View File
@@ -1,60 +0,0 @@
<?php
require_once 'Dummy/Exceptions/ResponseException.php';
require_once 'Dummy/Handler/ExceptionHandlerFirst.php';
require_once 'Dummy/Handler/ExceptionHandlerSecond.php';
require_once 'Dummy/Handler/ExceptionHandlerThird.php';
require_once 'Helpers/TestRouter.php';
class RouteRewriteTest extends PHPUnit_Framework_TestCase
{
/**
* Redirects to another route through 3 exception handlers.
*
* You will see "ExceptionHandler 1 loaded" 2 times. This happen because
* the exceptionhandler is asking the router to reload.
*
* That means that the exceptionhandler is loaded again, but this time
* the router ignores the same rewrite-route to avoid loop - loads
* the second which have same behavior and is also ignored before
* throwing the final Exception in ExceptionHandler 3.
*
* So this tests:
* 1. If ExceptionHandlers loads
* 2. If ExceptionHandlers load in the correct order
* 3. If ExceptionHandlers can rewrite the page on error
* 4. If the router can avoid redirect-loop due to developer has started loop.
* 5. And finally if we reaches the last exception-handler and that the correct
* exception-type is being thrown.
*/
public function testExceptionHandlerRewrite()
{
global $stack;
$stack = [];
TestRouter::group(['exceptionHandler' => [ExceptionHandlerFirst::class, ExceptionHandlerSecond::class]], function () {
TestRouter::group(['exceptionHandler' => ExceptionHandlerThird::class], function () {
TestRouter::get('/my-path', 'DummyController@method1');
});
});
try {
TestRouter::debug('/my-non-existing-path', 'get');
} catch(\ResponseException $e) {
}
$expectedStack = [
ExceptionHandlerFirst::class,
ExceptionHandlerSecond::class,
ExceptionHandlerThird::class,
];
$this->assertEquals($expectedStack, $stack);
}
}
+104 -91
View File
@@ -2,127 +2,140 @@
require_once 'Dummy/DummyMiddleware.php';
require_once 'Dummy/DummyController.php';
require_once 'Dummy/Exceptions/ExceptionHandlerException.php';
require_once 'Helpers/TestRouter.php';
require_once 'Dummy/Handler/ExceptionHandler.php';
use Pecee\SimpleRouter\SimpleRouter as SimpleRouter;
use Pecee\SimpleRouter\Exceptions\NotFoundHttpException as NotFoundHttpException;
class RouterRouteTest extends PHPUnit_Framework_TestCase
{
protected $result = false;
protected $result = false;
public function testMultiParam()
{
TestRouter::get('/test-{param1}-{param2}', function ($param1, $param2) {
public function testNotFound()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setMethod('get');
SimpleRouter::request()->setUri('/test-param1-param2');
if ($param1 === 'param1' && $param2 === 'param2') {
$this->result = true;
}
SimpleRouter::group(['exceptionHandler' => 'ExceptionHandler'], function () {
SimpleRouter::get('/non-existing-path', 'DummyController@start');
});
});
$found = false;
TestRouter::debug('/test-param1-param2', 'get');
try {
SimpleRouter::start();
} catch (\Exception $e) {
$found = ($e instanceof NotFoundHttpException && $e->getCode() == 404);
}
$this->assertTrue($this->result);
$this->assertTrue($found);
}
}
public function testGet()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setUri('/my/test/url');
SimpleRouter::request()->setMethod('get');
public function testNotFound()
{
$this->setExpectedException('\Pecee\SimpleRouter\Exceptions\NotFoundHttpException');
TestRouter::get('/non-existing-path', 'DummyController@method1');
TestRouter::debug('/test-param1-param2', 'post');
}
SimpleRouter::get('/my/test/url', 'DummyController@start');
SimpleRouter::start();
}
public function testGet()
{
TestRouter::get('/my/test/url', 'DummyController@method1');
TestRouter::debug('/my/test/url', 'get');
}
public function testPost()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setUri('/my/test/url');
SimpleRouter::request()->setMethod('post');
public function testPost()
{
TestRouter::post('/my/test/url', 'DummyController@method1');
TestRouter::debug('/my/test/url', 'post');
}
SimpleRouter::post('/my/test/url', 'DummyController@start');
SimpleRouter::start();
}
public function testPut()
{
TestRouter::put('/my/test/url', 'DummyController@method1');
TestRouter::debug('/my/test/url', 'put');
}
public function testPut()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setUri('/my/test/url');
SimpleRouter::request()->setMethod('put');
public function testDelete()
{
TestRouter::delete('/my/test/url', 'DummyController@method1');
TestRouter::debug('/my/test/url', 'delete');
}
SimpleRouter::put('/my/test/url', 'DummyController@start');
SimpleRouter::start();
}
public function testMethodNotAllowed()
{
TestRouter::get('/my/test/url', 'DummyController@method1');
public function testDelete()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setUri('/my/test/url');
SimpleRouter::request()->setMethod('delete');
try {
TestRouter::debug('/my/test/url', 'post');
} catch (\Exception $e) {
$this->assertEquals(403, $e->getCode());
}
}
SimpleRouter::delete('/my/test/url', 'DummyController@start');
SimpleRouter::start();
}
public function testSimpleParam()
{
TestRouter::get('/test-{param1}', 'DummyController@param');
$response = TestRouter::debugOutput('/test-param1', 'get');
public function testMethodNotAllowed()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setUri('/my/test/url');
SimpleRouter::request()->setMethod('post');
$this->assertEquals('param1', $response);
}
SimpleRouter::get('/my/test/url', 'DummyController@start');
public function testPathParamRegex()
{
TestRouter::get('/{lang}/productscategories/{name}', 'DummyController@param', ['where' => ['lang' => '[a-z]+', 'name' => '[A-Za-z0-9\-]+']]);
$response = TestRouter::debugOutput('/it/productscategories/system', 'get');
try {
SimpleRouter::start();
} catch (\Exception $e) {
$this->assertEquals(403, $e->getCode());
}
}
$this->assertEquals('it, system', $response);
}
public function testSimpleParam()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setMethod('get');
SimpleRouter::request()->setUri('/test-param1');
public function testDomainAllowedRoute()
{
$this->result = false;
SimpleRouter::get('/test-{param1}', 'DummyController@param');
SimpleRouter::start();
}
TestRouter::group(['domain' => '{subdomain}.world.com'], function () {
TestRouter::get('/test', function ($subdomain = null) {
$this->result = ($subdomain === 'hello');
});
});
public function testMultiParam()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setMethod('get');
SimpleRouter::request()->setUri('/test-param1-param2');
TestRouter::request()->setHost('hello.world.com');
TestRouter::debug('/test', 'get');
SimpleRouter::get('/test-{param1}-{param2}', 'DummyController@param');
SimpleRouter::start();
}
$this->assertTrue($this->result);
public function testPathParamRegex()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setMethod('get');
SimpleRouter::request()->setUri('/test/path/123123');
}
SimpleRouter::get('/test/path/{myParam}', 'DummyController@param', ['where' => ['myParam' => '([0-9]+)']]);
SimpleRouter::start();
}
public function testDomainNotAllowedRoute()
{
$this->result = false;
public function testDomainRoute()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setMethod('get');
SimpleRouter::request()->setUri('/test');
SimpleRouter::request()->setHost('hello.world.com');
TestRouter::group(['domain' => '{subdomain}.world.com'], function () {
TestRouter::get('/test', function ($subdomain = null) {
$this->result = ($subdomain === 'hello');
});
});
$this->result = false;
TestRouter::request()->setHost('other.world.com');
SimpleRouter::group(['domain' => '{subdomain}.world.com'], function () {
SimpleRouter::get('/test', function ($subdomain = null) {
$this->result = ($subdomain === 'hello');
});
});
SimpleRouter::start();
TestRouter::debug('/test', 'get');
$this->assertTrue($this->result);
$this->assertFalse($this->result);
}
public function testRegEx()
{
TestRouter::get('/my/{path}', 'DummyController@method1')->where(['path' => '[a-zA-Z\-]+']);
TestRouter::debug('/my/custom-path', 'get');
}
}
}
+60 -65
View File
@@ -3,104 +3,99 @@
require_once 'Dummy/DummyMiddleware.php';
require_once 'Dummy/DummyController.php';
require_once 'Dummy/Handler/ExceptionHandler.php';
require_once 'Helpers/TestRouter.php';
use Pecee\SimpleRouter\SimpleRouter as SimpleRouter;
class RouterUrlTest extends PHPUnit_Framework_TestCase
{
protected $result = false;
public function testSimularUrls()
{
// Match normal route on alias
TestRouter::resource('/url11', 'DummyController@method1');
TestRouter::resource('/url1', 'DummyController@method1', ['as' => 'match']);
protected function getUrl($name = null, $parameters = null, array $getParams = []) {
return SimpleRouter::getUrl($name, $parameters, $getParams);
}
TestRouter::debugNoReset('/url1', 'get');
public function testUrls()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setMethod('get');
SimpleRouter::request()->setUri('/');
$this->assertEquals(TestRouter::getUrl('match'), TestRouter::getUrl());
// Match normal route on alias
SimpleRouter::get('/', 'DummyController@silent', ['as' => 'home']);
TestRouter::router()->reset();
}
SimpleRouter::get('/about', 'DummyController@about');
public function testUrls()
{
// Match normal route on alias
TestRouter::get('/', 'DummyController@method1', ['as' => 'home']);
SimpleRouter::group(['prefix' => '/admin', 'as' => 'admin'], function() {
TestRouter::get('/about', 'DummyController@about');
// Match route with prefix on alias
SimpleRouter::get('/{id?}', 'DummyController@start', ['as' => 'home']);
TestRouter::group(['prefix' => '/admin', 'as' => 'admin'], function () {
// Match controller with prefix and alias
SimpleRouter::controller('/users', 'DummyController', ['as' => 'users']);
// Match route with prefix on alias
TestRouter::get('/{id?}', 'DummyController@method2', ['as' => 'home']);
// Match controller with prefix and NO alias
SimpleRouter::controller('/pages', 'DummyController');
// Match controller with prefix and alias
TestRouter::controller('/users', 'DummyController', ['as' => 'users']);
});
// Match controller with prefix and NO alias
TestRouter::controller('/pages', 'DummyController');
SimpleRouter::group(['prefix' => 'api', 'as' => 'api'], function() {
});
// Match resource controller
SimpleRouter::resource('phones', 'DummyController');
TestRouter::group(['prefix' => 'api', 'as' => 'api'], function () {
});
// Match resource controller
TestRouter::resource('phones', 'DummyController');
SimpleRouter::controller('gadgets', 'DummyController', ['names' => ['getIphoneInfo' => 'iphone']]);
});
// Match controller with no prefix and no alias
SimpleRouter::controller('/cats', 'CatsController');
TestRouter::controller('gadgets', 'DummyController', ['names' => ['getIphoneInfo' => 'iphone']]);
// Pretend to load page
SimpleRouter::start();
// Match controller with no prefix and no alias
TestRouter::controller('/cats', 'CatsController');
$this->assertEquals('/gadgets/iphoneinfo/', $this->getUrl('gadgets.iphone'));
// Pretend to load page
TestRouter::debugNoReset('/', 'get');
$this->assertEquals('/api/phones/create/', $this->getUrl('api.phones.create'));
$this->assertEquals('/gadgets/iphoneinfo/', TestRouter::getUrl('gadgets.iphone'));
// Should match /
$this->assertEquals('/', $this->getUrl('home'));
$this->assertEquals('/api/phones/create/', TestRouter::getUrl('api.phones.create'));
// Should match /about/
$this->assertEquals('/about/', $this->getUrl('DummyController@about'));
// Should match /
$this->assertEquals('/', TestRouter::getUrl('home'));
// Should match /admin/
$this->assertEquals('/admin/', $this->getUrl('DummyController@start'));
// Should match /about/
$this->assertEquals('/about/', TestRouter::getUrl('DummyController@about'));
// Should match /admin/
$this->assertEquals('/admin/', $this->getUrl('admin.home'));
// Should match /admin/
$this->assertEquals('/admin/', TestRouter::getUrl('DummyController@method2'));
// Should match /admin/2/
$this->assertEquals('/admin/2/', $this->getUrl('admin.home', ['id' => 2]));
// Should match /admin/
$this->assertEquals('/admin/', TestRouter::getUrl('admin.home'));
// Should match /admin/users/
$this->assertEquals('/admin/users/', $this->getUrl('admin.users'));
// Should match /admin/2/
$this->assertEquals('/admin/2/', TestRouter::getUrl('admin.home', ['id' => 2]));
// Should match /admin/users/home/
$this->assertEquals('/admin/users/home/', $this->getUrl('admin.users@home'));
// Should match /admin/users/
$this->assertEquals('/admin/users/', TestRouter::getUrl('admin.users'));
// Should match /cats/
$this->assertEquals('/cats/', $this->getUrl('CatsController'));
// Should match /admin/users/home/
$this->assertEquals('/admin/users/home/', TestRouter::getUrl('admin.users@home'));
// Should match /cats/view/
$this->assertEquals('/cats/view/', $this->getUrl('CatsController', 'view'));
// Should match /cats/
$this->assertEquals('/cats/', TestRouter::getUrl('CatsController'));
// Should match /cats/view/
//$this->assertEquals('/cats/view/', $this->getUrl('CatsController', ['view']));
// Should match /cats/view/
$this->assertEquals('/cats/view/', TestRouter::getUrl('CatsController', 'view'));
// Should match /cats/view/666
$this->assertEquals('/cats/view/666/', $this->getUrl('CatsController@getView', ['666']));
// Should match /cats/view/
//$this->assertEquals('/cats/view/', TestRouter::getUrl('CatsController', ['view']));
// Should match /funny/man/
$this->assertEquals('/funny/man/', $this->getUrl('/funny/man'));
// Should match /cats/view/666
$this->assertEquals('/cats/view/666/', TestRouter::getUrl('CatsController@getView', ['666']));
// Should match /?jackdaniels=true&cola=yeah
$this->assertEquals('/?jackdaniels=true&cola=yeah', $this->getUrl('home', null, ['jackdaniels' => 'true', 'cola' => 'yeah']));
// Should match /funny/man/
$this->assertEquals('/funny/man/', TestRouter::getUrl('/funny/man'));
// Should match /?jackdaniels=true&cola=yeah
$this->assertEquals('/?jackdaniels=true&cola=yeah', TestRouter::getUrl('home', null, ['jackdaniels' => 'true', 'cola' => 'yeah']));
TestRouter::router()->reset();
}
}
}