Compare commits

..

52 Commits

Author SHA1 Message Date
Simon Sessingø 4975f24fee Merge pull request #285 from skipperbent/v1-development
Added \JsonSerializable interface to Response->json (issue: #284).
2017-08-31 13:05:26 +02:00
Simon Sessingø 2952f6a3b6 Added \JsonSerializable interface to Response->json (issue: #284). 2017-08-31 11:59:58 +01:00
Simon Sessingø da7348ea82 Merge pull request #127 from skipperbent/v1-development
Development
2016-11-08 18:21:41 +02:00
Simon Sessingø 16e326ad9f Development
- all() in Input class now returns correct array.
- all() now supports json data.
- Minor bugfixes.
2016-11-08 18:16:17 +02:00
Simon Sessingø 0002b45d18 Merge pull request #123 from skipperbent/v1-development
Fixed RouterGroup not pushing multiple middlewares properly
2016-11-07 04:57:30 +01:00
Simon Sessingø d9b2328e82 Fixed RouterGroup not pushing multiple middlewares properly 2016-11-07 04:56:47 +01:00
Simon Sessingø 4a03005c68 Merge pull request #122 from skipperbent/development
Fixed urls in groups not working
2016-11-06 09:04:56 +01:00
Simon Sessingø 52034411cf Merge pull request #121 from skipperbent/development
Development
2016-11-06 08:14:36 +01:00
Simon Sessingø 4c8ed5bb3d Merge pull request #120 from skipperbent/development
Development
2016-11-05 23:09:19 +01:00
Simon Sessingø 9fed6ffb3f Merge pull request #119 from skipperbent/development
Optimised for cli-usage
2016-10-28 07:41:21 +02:00
Simon Sessingø 15da599e82 Merge pull request #118 from skipperbent/development
Minor optimisations
2016-10-27 19:15:59 +02:00
Simon Sessingø 9274acb591 Merge pull request #117 from skipperbent/development
Optimisations and bugfixes
2016-10-27 17:06:24 +02:00
Simon Sessingø 5c7759ab72 Merge pull request #116 from skipperbent/development
Development
2016-10-27 16:45:29 +02:00
Simon Sessingø c7b8593185 Merge pull request #114 from skipperbent/development
Development
2016-10-20 08:38:23 +02:00
Simon Sessingø fb478f475c Merge pull request #113 from skipperbent/development
Development
2016-10-20 08:35:20 +02:00
Simon Sessingø 1142d9d4ce Merge pull request #112 from skipperbent/development
Optimised middleware load order
2016-09-28 12:30:06 +02:00
Simon Sessingø 5d5c96e802 Merge pull request #111 from skipperbent/development
Automatically push middlewares if multiple in nested group.
2016-09-28 12:24:38 +02:00
Simon Sessingø bfdaf8ac52 Merge pull request #110 from skipperbent/development
Updated documentation and added demo-project.
2016-09-26 14:39:50 +02:00
Simon Sessingø 71dc6e172f Merge pull request #108 from skipperbent/development
Allow for default parameters (including A-Z, a-z, 0-9 and "-") when p…
2016-06-14 22:21:20 +02:00
Simon Sessingø 6ee172927f Merge pull request #107 from skipperbent/development
Bugfix
2016-06-04 18:44:59 +02:00
Simon Sessingø bb5e629199 Merge pull request #106 from skipperbent/development
Development
2016-06-04 18:21:16 +02:00
Simon Sessingø 0cc0a59fd5 Merge pull request #105 from skipperbent/development
Development
2016-06-04 15:13:47 +02:00
Simon Sessingø 498fd6b07d Merge pull request #103 from skipperbent/development
Added setValue method to InputItem class.
2016-05-04 13:41:36 +02:00
Simon Sessingø 96ab22a4f8 Merge pull request #102 from skipperbent/development
Added exist method to Input class.
2016-05-03 07:21:16 +02:00
Simon Sessingø 7f528c133b Merge pull request #101 from skipperbent/development
Development
2016-05-01 02:01:47 +02:00
Simon Sessingø 5a50190293 Merge pull request #100 from skipperbent/development
Development
2016-04-25 00:41:50 +02:00
Simon Sessingø 355ef01d63 Merge pull request #99 from skipperbent/development
Development
2016-04-22 15:38:02 +02:00
Simon Sessingø d3162b5a2b Merge pull request #98 from skipperbent/development
Development
2016-04-22 14:30:40 +02:00
Simon Sessingø 810b80487d Merge pull request #97 from skipperbent/development
- Added custom ExceptionHandler example to documentation.
2016-04-21 08:30:34 +02:00
Simon Sessingø 18a9df56ca Merge pull request #96 from skipperbent/development
Development
2016-04-20 08:10:18 +02:00
Simon Sessingø 6e14ded03f Merge pull request #95 from skipperbent/development
Development
2016-04-16 23:23:56 +02:00
Simon Sessingø 899081f8d8 Merge pull request #94 from skipperbent/development
Update README.md
2016-04-16 00:01:22 +02:00
Simon Sessingø e7b9206bc9 Merge pull request #93 from skipperbent/development
Development
2016-04-15 23:21:00 +02:00
Simon Sessingø cd6e800984 Merge pull request #91 from skipperbent/development
Development
2016-04-15 23:14:55 +02:00
Simon Sessingø 11bd5a7d11 Merge pull request #89 from skipperbent/development
[BUGFIX] Fixed notice
2016-04-09 15:39:12 +02:00
Simon Sessingø be32796b01 Merge pull request #88 from skipperbent/development
[TASK] Moved group-middleware rendering to routeRequest to ensure all…
2016-04-09 15:32:45 +02:00
Simon Sessingø 8b3d71a328 Merge pull request #87 from skipperbent/development
[BUGFIX] Fixed only match group route if prefix is set
2016-04-09 10:02:22 +02:00
Simon Sessingø 1fae638aaf Merge pull request #86 from skipperbent/development
Development
2016-04-09 09:50:56 +02:00
Simon Sessingø 37c8bc9f32 Merge pull request #79 from skipperbent/development
[BUGFIX] Bugfixes and optimisations
2016-04-07 23:21:13 +02:00
Simon Sessingø 75029b330a Merge pull request #78 from skipperbent/development
[BUGFIX] Fixed nested groups not merging settings to routes
2016-04-07 19:34:02 +02:00
Simon Sessingø fd5d893040 Merge pull request #77 from skipperbent/development
[TASK] Added rewrite_uri parameter to Request class
2016-03-19 19:12:42 +01:00
Simon Sessingø c1512740af Merge pull request #76 from skipperbent/development
[BUGFIX] Readded rendering of groups
2016-03-19 18:59:01 +01:00
Simon Sessingø 212ae133de Merge pull request #75 from skipperbent/development
[TASK] Readded merging
2016-03-19 17:40:26 +01:00
Simon Sessingø ae58231fa1 Merge pull request #74 from skipperbent/development
Custom boot-managers
2016-03-19 16:29:03 +01:00
Simon Sessingø 358b25d4f1 Merge pull request #73 from skipperbent/development
Development
2016-03-18 17:55:31 +01:00
Simon Sessingø 3d45851d9b Merge pull request #72 from skipperbent/development
[BUGFIX] Only render group if prefix matches.
2016-03-16 19:58:03 +01:00
Simon Sessingø ee5c2207f8 Merge pull request #71 from skipperbent/development
Added support for x-forwarded-proto http header
2016-03-14 01:08:25 +01:00
Simon Sessingø b1ca3fc9ef Merge pull request #70 from skipperbent/development
[FEATURE] Added http code to redirect method.
2016-03-14 00:59:09 +01:00
Simon Sessingø 253c0c70d4 Merge pull request #69 from skipperbent/development
[BUGFIX] Bugfix
2016-03-01 22:52:38 +01:00
Simon Sessingø 53ba2d7ac5 Merge pull request #68 from skipperbent/development
[TASK] Fixed regex causing optional parameters to sometimes catch req…
2016-01-23 16:12:27 +01:00
Simon Sessingø 315fe05769 Merge pull request #67 from skipperbent/development
Development
2016-01-17 04:57:39 +01:00
Simon Sessingø a57113309a Merge pull request #66 from skipperbent/development
Development
2016-01-15 11:56:04 +01:00
42 changed files with 2287 additions and 3072 deletions
+68 -147
View File
@@ -18,7 +18,7 @@ The goal of this project is to create a router that is 100% compatible with the
### Features
- Basic routing (`GET`, `POST`, `PUT`, `DELETE`) with support for custom multiple verbs.
- Basic routing (get, post, put, delete) with support for custom multiple verbs.
- Regular Expression Constraints for parameters.
- Named routes.
- Generating url to routes.
@@ -29,8 +29,8 @@ The goal of this project is to create a router that is 100% compatible with the
- CSRF protection.
- Optional parameters
- Sub-domain routing
- Custom boot managers to rewrite urls to "nicer" ones.
- Input manager; easily manage `GET`, `POST` and `FILE` values.
- Custom boot managers to redirect urls to other routes
- Input manager; to manage `GET`, `POST` params.
## Installation and demo
@@ -49,18 +49,13 @@ This is an example of a basic ```index.php``` file:
```php
use \Pecee\SimpleRouter\SimpleRouter;
// Load external routes file
require_once 'routes.php';
require_once 'routes.php'; // change this to whatever makes sense in your project
/*
* The default namespace for route-callbacks, so we don't have to specify it each time.
* Can be overwritten by using the namespace config option.
*/
SimpleRouter::setDefaultNamespace('MyWebsite\Controller');
// The apps default namespace (so we don't have to specify it each time we use MyController@home)
$defaultControllerNamespace = 'MyWebsite\\Controller';
// Start the routing
SimpleRouter::start();
// Do the routing
SimpleRouter::start($defaultControllerNamespace);
```
## Adding routes
@@ -71,7 +66,6 @@ This router is heavily inspired by the Laravel 5.* router, so anything you find
- ExceptionsHandlers must implement the `IExceptionHandler` interface.
- Middlewares must implement the `IMiddleware` interface.
- Resource controllers can inherit the `IRestController` interface, but is not required.
```php
use Pecee\SimpleRouter\SimpleRouter;
@@ -87,9 +81,7 @@ use Pecee\SimpleRouter\SimpleRouter;
// Add CSRF support (if needed)
SimpleRouter::csrfVerifier(new \Pecee\Http\Middleware\BaseCsrfVerifier());
SimpleRouter::get('/page/404', 'ControllerPage@notFound', ['as' => 'page.notfound']);
SimpleRouter::group(['prefix' => '/v1', 'middleware' => '\MyWebsite\Middleware\SomeMiddlewareClass'], function() {
SimpleRouter::group(['prefix' => 'v1', 'middleware' => '\MyWebsite\Middleware\SomeMiddlewareClass'], function() {
SimpleRouter::group(['prefix' => '/services', 'exceptionHandler' => '\MyProject\Handler\CustomExceptionHandler'], function() {
@@ -104,7 +96,7 @@ SimpleRouter::group(['prefix' => '/v1', 'middleware' => '\MyWebsite\Middleware\S
*/
SimpleRouter::all('/ajax', 'ControllerAjax@process')->match('.*?\\/ajax\\/([A-Za-z0-9\\/]+)');
// Restful resource (see IRestController interface for available methods)
// Restful resource
SimpleRouter::resource('/rest', 'ControllerRessource');
// Load the entire controller (where url matches method names - getIndex(), postIndex() etc)
@@ -135,22 +127,16 @@ class CustomExceptionHandler implements IExceptionHandler {
// If the error-code is 404; show another route which contains the page-not-found
if($error->getCode() === 404) {
// Throw your custom 404-page view
// - or -
// load another route with our 404 page
// - or -
// you can return the $request object to ignore the error and continue on rendering the route.
return $request->setUri(url('page.notfound'));
// Load your custom 404-page view
}
// Output error as json if on api path.
if(stripos($request->getUri(), '/api') !== false) {
response()->json([ 'error' => $error->getMessage() ]);
response()->json(['error' => $error->getMessage()]);
}
// Otherwise default exception will be thrown by the router.
}
}
@@ -204,33 +190,33 @@ use Pecee\SimpleRouter\SimpleRouter;
class Router extends SimpleRouter {
public static function start() {
public static function start($defaultNamespace = null) {
// change this to whatever makes sense in your project
require_once 'routes.php';
// change default namespace for all routes
parent::setDefaultNamespace('\Demo');
// Do initial stuff
parent::start();
parent::start('\\Demo\\Controllers');
}
}
```
## Helper functions
To simplify to use of simple-router functionality, we recommend you add these helper functions to your project.
#### Helper functions examples
**This is a basic example of a helper function for generating urls.**
```php
use Pecee\SimpleRouter\SimpleRouter;
use Pecee\SimpleRouter\RouterBase;
function url($controller, $parameters = null, $getParams = null) {
SimpleRouter::getRoute($controller, $parameters, $getParams);
RouterBase::getInstance()->getRoute($controller, $parameters, $getParams);
}
```
**This is a basic example for getting the current csrf token**
```php
/**
* Get current csrf-token
* @return null|string
@@ -239,30 +225,6 @@ function csrf_token() {
$token = new \Pecee\CsrfToken();
return $token->getToken();
}
/**
* Get request object
* @return \Pecee\Http\Request
*/
function request() {
return SimpleRouter::request();
}
/**
* Get response object
* @return \Pecee\Http\Response
*/
function response() {
return SimpleRouter::response();
}
/**
* Get input class
* @return \Pecee\Http\Input\Input
*/
function input() {
return SimpleRouter::request()->getInput();
}
```
## Getting urls
@@ -298,10 +260,7 @@ use Pecee\Http\Middleware\BaseCsrfVerifier;
class CsrfVerifier extends BaseCsrfVerifier {
protected $except = [
'/companies/*',
'/api'
];
protected $except = ['/companies/*', '/user/save'];
}
```
@@ -355,122 +314,68 @@ By doing this the route will now load the url ```/article/view/1``` instead of `
The last thing we need to do, is to add our custom boot-manager to the ```routes.php``` file. You can create as many bootmanagers as you like and easily add them in your ```routes.php``` file.
## 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\RouterBase``` instance's `loadedRoute` property.
For easy access you can use the shortcut method `\Pecee\SimpleRouter\SimpleRouter::router()`.
**routes.php example:**
```php
use Pecee\SimpleRouter;
$route = SimpleRouter::router()->getLoadedRoute();
// Add new bootmanager
SimpleRouter::addBootManager(new CustomRouterRules());
// This rule is what our custom bootmanager will use.
SimpleRouter::get('/article/view/{id}', 'ControllerArticle@view');
```
## Easily overwrite route about to be loaded
Sometimes it can be useful to manipulate the route that's about to be loaded, for instance if a user is not authenticated or if an error occurred within your Middleware that requires
some other route to be initialised. Simple PHP Router allows you to easily change the route about to be executed. All information about the current route is stored in
the ```\Pecee\SimpleRouter\Http\Request``` object.
**Note:** Please note that it's only possible to change the route BEFORE any route has initially been loaded, so doing this in your custom ExceptionHandler or Middleware is highly recommended.
```php
$route = Request::getInstance()->getLoadedRoute();
$route->setCallback('Example\MyCustomClass@hello');
// -- or you can rewrite by doing --
// -- or --
$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 `RouterEntry` 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\RouterEntry;
class CustomMiddleware implements Middleware {
public function handle(Request $request, RouterEntry &$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\RouterEntry;
class CustomMiddleware implements Middleware {
public function handle(Request $request, RouterEntry &$route) {
$route->callback('DefaultController@home');
}
}
```
## Using the Input class to manage parameters
We've added the `Input` class to easy access parameters from your Controller-classes.
**Return single parameter value (matches both GET, POST, FILE):**
```php
$value = input()->get('name');
$value = Request::getInstance()->getInput()->get('name');
```
**Return parameter object (matches both GET, POST, FILE):**
```php
$object = input()->getObject('name');
$object = Request::getInstance()->getInput()->getObject('name');
```
**Return specific GET parameter (where name is the name of your parameter):**
```php
$object = input()->get->name;
$object = input()->post->name;
$object = input()->file->name;
// -- or --
$object = input()->get->get($key, $defaultValue);
$object = input()->post->get($key, $defaultValue);
$object = input()->file->get($key, $defaultValue);
$object = Request::getInstance()->getInput()->get->name;
$object = Request::getInstance()->getInput()->post->name;
$object = Request::getInstance()->getInput()->file->name;
```
**Return all parameters:**
```php
// Get all
$values = input()->all();
$objects = Request::getInstance()->getInput()->all();
// Only match certain keys
$values = input()->all([
$objects = Request::getInstance()->getInput()->all([
'company_name',
'user_id'
]);
```
All object inherits from `InputItem` class and will always contain these methods:
- `getValue()` - returns the value of the input.
- `getIndex()` - returns the index/key of the input.
- `getName()` - returns a human friendly name for the input (company_name will be Company Name etc).
@@ -483,11 +388,27 @@ All object inherits from `InputItem` class and will always contain these methods
- `getType()` - get mime-type for file.
- `getError()` - get file upload error.
Below example requires you to have the helper functions added. Please refer to the helper functions section in the documentation.
### Easy access your input
Create a helper function to easily get access to the input elements.
Example:
```php
/**
* Get input class
* @return \Pecee\Http\Input\Input
*/
function input() {
return \Pecee\Http\Request::getInstance()->getInput();
}
```
Then you can easily do something like this in your controller:
```php
// Get parameter site_id or default-value 2
$siteId = input()->get('site_id', 2);
$value = input()->get('site_id', '2');
```
## Sites
+16 -10
View File
@@ -1,16 +1,22 @@
<?php
namespace Demo\Controllers;
class ApiController
{
public function index()
{
// The variable authenticated is set to true in the ApiVerification middleware class.
header('content-type: application/json');
use Pecee\Http\Request;
echo json_encode([
'authenticated' => request()->authenticated
]);
}
class ApiController {
public function index() {
// The variable authenticated is set to true in the ApiVerification middleware class.
$request = Request::getInstance();
header('content-type: application/json');
echo json_encode([
'authenticated' => $request->authenticated
]);
}
}
@@ -1,27 +1,29 @@
<?php
namespace Demo\Controllers;
class DefaultController
{
public function index()
{
// implement
echo sprintf('DefaultController -> index (?fun=%s)', input()->get('fun'));
}
class DefaultController {
public function contact()
{
echo 'DefaultController -> contact';
}
public function index() {
public function companies($id = null)
{
echo 'DefaultController -> companies -> id: ' . $id;
}
// implement
echo 'DefaultController -> index';
public function notFound()
{
echo 'Page not found';
}
}
public function contact() {
echo 'DefaultController -> contact';
}
public function companies($id = null) {
echo 'DefaultController -> companies -> id: ' . $id;
}
public function notFound() {
echo 'Page not found';
}
}
@@ -5,30 +5,30 @@ use Pecee\Handler\IExceptionHandler;
use Pecee\Http\Request;
use Pecee\SimpleRouter\RouterEntry;
class CustomExceptionHandler implements IExceptionHandler
{
public function handleError(Request $request, RouterEntry &$route = null, \Exception $error)
{
// Return json errors if we encounter an error on /api.
if (stripos($request->getUri(), '/api') !== false) {
header('content-type: application/json');
echo json_encode([
'error' => $error->getMessage(),
'code' => $error->getCode()
]);
die();
}
class CustomExceptionHandler implements IExceptionHandler {
// else we just throw the error
if ($error->getCode() == 404) {
public function handleError( Request $request, RouterEntry $router = null, \Exception $error) {
// Return 404 path
$request->setUri('/404');
return $request;
// Return json errors if we encounter an error on /api.
if(stripos($request->getUri(), '/api') !== false) {
header('content-type: application/json');
echo json_encode([
'error' => $error->getMessage(),
'code' => $error->getCode()
]);
die();
}
}
// else we just throw the error
if($error->getCode() == 404) {
throw $error;
}
// Return 404 path
$request->setUri('/404');
return $request;
}
throw $error;
}
}
@@ -3,16 +3,14 @@ namespace Demo\Middlewares;
use Pecee\Http\Middleware\IMiddleware;
use Pecee\Http\Request;
use Pecee\SimpleRouter\RouterEntry;
class ApiVerification implements IMiddleware
{
public function handle(Request $request, RouterEntry &$route)
{
// Do authentication
$request->authenticated = true;
class ApiVerification implements IMiddleware {
return $request;
}
public function handle(Request $request) {
// Do authentication
$request->authenticated = true;
}
}
@@ -3,11 +3,11 @@ namespace Demo\Middlewares;
use Pecee\Http\Middleware\BaseCsrfVerifier;
class CsrfVerifier extends BaseCsrfVerifier
{
/**
* CSRF validation will be ignored on the following urls.
*/
protected $except = ['/api/*'];
class CsrfVerifier extends BaseCsrfVerifier {
/**
* CSRF validation will be ignored on the following urls.
*/
protected $except = ['/api/*'];
}
+11 -12
View File
@@ -1,26 +1,25 @@
<?php
/**
* Custom router which handles default middlewares, default exceptions and things
* that should be happen before and after the router is initialised.
*/
namespace Demo;
use Pecee\SimpleRouter\SimpleRouter;
class Router extends SimpleRouter
{
public static function start($defaultNamespace = null)
{
// Load our helpers
require_once 'helpers.php';
class Router extends SimpleRouter {
// Load our custom routes
require_once 'routes.php';
public static function start($defaultNamespace = null) {
parent::setDefaultNamespace('\Demo');
// change this to whatever makes sense in your project
require_once 'routes.php';
// Do initial stuff
parent::start();
}
// Do initial stuff
parent::start('\\Demo\\Controllers');
}
}
-44
View File
@@ -1,44 +0,0 @@
<?php
use Pecee\SimpleRouter\SimpleRouter;
function url($controller, $parameters = null, $getParams = null)
{
SimpleRouter::getRoute($controller, $parameters, $getParams);
}
/**
* Get current csrf-token
* @return null|string
*/
function csrf_token()
{
$token = new \Pecee\CsrfToken();
return $token->getToken();
}
/**
* Get request object
* @return \Pecee\Http\Request
*/
function request()
{
return SimpleRouter::request();
}
/**
* Get response object
* @return \Pecee\Http\Response
*/
function response()
{
return SimpleRouter::response();
}
/**
* Get input class
* @return \Pecee\Http\Input\Input
*/
function input()
{
return SimpleRouter::request()->getInput();
}
+10 -10
View File
@@ -7,17 +7,17 @@ use Demo\Router;
Router::csrfVerifier(new \Demo\Middlewares\CsrfVerifier());
Router::group(['exceptionHandler' => 'Demo\Handlers\CustomExceptionHandler'], function () {
Router::group(['exceptionHandler' => 'Demo\Handlers\CustomExceptionHandler'], function() {
Router::get('/', 'DefaultController@index')->setAlias('home');
Router::get('/contact', 'DefaultController@contact')->setAlias('contact');
Router::get('/404', 'DefaultController@notFound')->setAlias('404');
Router::basic('/companies', 'DefaultController@companies')->setAlias('companies');
Router::basic('/companies/{id}', 'DefaultController@companies')->setAlias('companies');
Router::get('/', 'DefaultController@index')->setAlias('home');
Router::get('/contact', 'DefaultController@contact')->setAlias('contact');
Router::get('/404', 'DefaultController@notFound')->setAlias('404');
Router::basic('/companies', 'DefaultController@companies')->setAlias('companies');
Router::basic('/companies/{id}', 'DefaultController@companies')->setAlias('companies');
// Api
Router::group(['prefix' => '/api', 'middleware' => 'Demo\Middlewares\ApiVerification'], function () {
Router::resource('/demo', 'ApiController');
});
// Api
Router::group(['prefix' => '/api', 'middleware' => 'Demo\Middlewares\ApiVerification'], function() {
Router::resource('/demo', 'ApiController');
});
});
+1 -1
View File
@@ -10,7 +10,7 @@
"type": "project",
"require": {
"php": ">=5.4.0",
"pecee/simple-router": "2.*"
"pecee/simple-router": "1.*"
},
"require-dev": {
-46
View File
@@ -1,46 +0,0 @@
<?php
namespace Pecee\Controller;
interface IRestController {
/**
* @return void
*/
function index();
/**
* @param mixed $id
* @return void
*/
function show($id);
/**
* @return void
*/
function store();
/**
* @return void
*/
function create();
/**
* View
* @param mixed $id
* @return void
*/
function edit($id);
/**
* @param mixed $id
* @return void
*/
function update($id);
/**
* @param mixed $id
* @return void
*/
function destroy($id);
}
+51 -57
View File
@@ -1,68 +1,62 @@
<?php
namespace Pecee;
class CsrfToken
{
const CSRF_KEY = 'XSRF-TOKEN';
class CsrfToken {
protected $token;
const CSRF_KEY = 'XSRF-TOKEN';
/**
* Generate random identifier for CSRF token
*
* @return string
*/
public static function generateToken()
{
if (function_exists('mcrypt_create_iv')) {
return bin2hex(mcrypt_create_iv(32, MCRYPT_DEV_URANDOM));
}
return bin2hex(openssl_random_pseudo_bytes(32));
}
protected $token;
/**
* Validate valid CSRF token
*
* @param string $token
* @return bool
*/
public function validate($token)
{
if ($token !== null && $this->getToken() !== null) {
return hash_equals($token, $this->getToken());
}
return false;
}
/**
* Generate random identifier for CSRF token
* @return string
*/
public static function generateToken() {
if (function_exists('mcrypt_create_iv')) {
return bin2hex(mcrypt_create_iv(32, MCRYPT_DEV_URANDOM));
}
return bin2hex(openssl_random_pseudo_bytes(32));
}
/**
* Set csrf token cookie
*
* @param $token
*/
public function setToken($token)
{
setcookie(static::CSRF_KEY, $token, time() + 60 * 120, '/');
}
/**
* Validate valid CSRF token
*
* @param string $token
* @return bool
*/
public function validate($token) {
if($token !== null && $this->getToken() !== null) {
return hash_equals($token, $this->getToken());
}
return false;
}
/**
* Get csrf token
* @return string|null
*/
public function getToken()
{
if ($this->hasToken()) {
return $_COOKIE[static::CSRF_KEY];
}
return null;
}
/**
* Set csrf token cookie
*
* @param $token
*/
public function setToken($token) {
setcookie(static::CSRF_KEY, $token, time() + 60 * 120, '/');
}
/**
* Returns whether the csrf token has been defined
* @return bool
*/
public function hasToken()
{
return isset($_COOKIE[static::CSRF_KEY]);
}
/**
* Get csrf token
* @return string|null
*/
public function getToken(){
if($this->hasToken()) {
return $_COOKIE[static::CSRF_KEY];
}
return null;
}
/**
* Returns whether the csrf token has been defined
* @return bool
*/
public function hasToken() {
return isset($_COOKIE[static::CSRF_KEY]);
}
}
+1 -4
View File
@@ -1,6 +1,3 @@
<?php
namespace Pecee\Exception;
class RouterException extends \Exception
{
}
class RouterException extends \Exception { }
@@ -1,6 +1,4 @@
<?php
namespace Pecee\Exception;
class TokenMismatchException extends \Exception
{
}
class TokenMismatchException extends \Exception {}
+3 -9
View File
@@ -4,14 +4,8 @@ namespace Pecee\Handler;
use Pecee\Http\Request;
use Pecee\SimpleRouter\RouterEntry;
interface IExceptionHandler
{
/**
* @param Request $request
* @param RouterEntry|null $route
* @param \Exception $error
* @return Request|null
*/
public function handleError(Request $request, RouterEntry &$route = null, \Exception $error);
interface IExceptionHandler {
public function handleError(Request $request, RouterEntry $router = null, \Exception $error);
}
+162 -160
View File
@@ -3,209 +3,211 @@ namespace Pecee\Http\Input;
use Pecee\Http\Request;
class Input
{
/**
* @var \Pecee\Http\Input\InputCollection
*/
public $get;
class Input {
/**
* @var \Pecee\Http\Input\InputCollection
*/
public $post;
/**
* @var \Pecee\Http\Input\InputCollection
*/
public $get;
/**
* @var \Pecee\Http\Input\InputCollection
*/
public $file;
/**
* @var \Pecee\Http\Input\InputCollection
*/
public $post;
/**
* @var Request
*/
protected $request;
/**
* @var \Pecee\Http\Input\InputCollection
*/
public $file;
public function __construct(Request $request)
{
$this->request = $request;
$this->setGet();
$this->setPost();
$this->setFile();
}
/**
* @var Request
*/
protected $request;
/**
* Get all get/post items
* @param array|null $filter Only take items in filter
* @return array
*/
public function all(array $filter = null)
{
$output = $_POST;
public function __construct(Request &$request) {
$this->request = $request;
$this->setGet();
$this->setPost();
$this->setFile();
}
if ($this->request->getMethod() === 'post') {
/**
* Get all get/post items
* @param array|null $filter Only take items in filter
* @return array
*/
public function all(array $filter = null) {
$contents = file_get_contents('php://input');
$output = $_POST;
if (stripos(trim($contents), '{') === 0) {
$output = json_decode($contents, true);
if ($output === false) {
$output = array();
}
}
}
if($this->request->getMethod() === 'post') {
$output = array_merge($_GET, $output);
$contents = file_get_contents('php://input');
if ($filter !== null) {
$output = array_filter($output, function ($key) use ($filter) {
if (in_array($key, $filter)) {
return true;
}
if (stripos(trim($contents), '{') === 0) {
$output = json_decode($contents, true);
if($output === false) {
$output = array();
}
}
}
return false;
}, ARRAY_FILTER_USE_KEY);
}
$output = array_merge($_GET, $output);
return $output;
}
if($filter !== null) {
$output = array_filter($output, function ($key) use ($filter) {
if (in_array($key, $filter)) {
return true;
}
public function getObject($index, $default = null)
{
$key = (strpos($index, '[') > -1) ? substr($index, strpos($index, '[') + 1, strpos($index, ']') - strlen($index)) : null;
$index = (strpos($index, '[') > -1) ? substr($index, 0, strpos($index, '[')) : $index;
return false;
}, ARRAY_FILTER_USE_KEY);
}
$element = $this->get->findFirst($index);
return $output;
}
if ($element !== null) {
return ($key !== null) ? $element[$key] : $element;
}
public function getObject($index, $default = null) {
$key = (strpos($index, '[') > -1) ? substr($index, strpos($index, '[')+1, strpos($index, ']') - strlen($index)) : null;
$index = (strpos($index, '[') > -1) ? substr($index, 0, strpos($index, '[')) : $index;
if ($this->request->getMethod() !== 'get') {
$element = $this->get->findFirst($index);
$element = $this->post->findFirst($index);
if ($element !== null) {
return ($key !== null) ? $element[$key] : $element;
}
if($element !== null) {
return ($key !== null) ? $element[$key] : $element;
}
$element = $this->file->findFirst($index);
if ($element !== null) {
return ($key !== null) ? $element[$key] : $element;
}
}
if($this->request->getMethod() !== 'get') {
return $default;
}
$element = $this->post->findFirst($index);
/**
* Get input element value matching index
* @param string $index
* @param string|null $default
* @return string|null
*/
public function get($index, $default = null)
{
$item = $this->getObject($index);
if ($element !== null) {
return ($key !== null) ? $element[$key] : $element;
}
if ($item !== null) {
$element = $this->file->findFirst($index);
if ($element !== null) {
return ($key !== null) ? $element[$key] : $element;
}
}
if ($item instanceof InputCollection || $item instanceof InputFile) {
return $item;
}
return $default;
}
return (trim($item->getValue()) === '') ? $default : $item->getValue();
}
/**
* Get input element value matching index
* @param string $index
* @param string|null $default
* @return string|null
*/
public function get($index, $default = null) {
return $default;
}
$item = $this->getObject($index);
public function exists($index)
{
return ($this->getObject($index) !== null);
}
if($item !== null) {
public function setGet()
{
$this->get = new InputCollection();
if($item instanceof InputCollection || $item instanceof InputFile) {
return $item;
}
if (count($_GET) > 0) {
foreach ($_GET as $key => $get) {
if (is_array($get) === false) {
$this->get->{$key} = new InputItem($key, $get);
continue;
}
return (trim($item->getValue()) === '') ? $default : $item->getValue();
}
$output = new InputCollection();
return $default;
}
foreach ($get as $k => $g) {
$output->{$k} = new InputItem($k, $g);
}
public function exists($index) {
return ($this->getObject($index) !== null);
}
$this->get->{$key} = $output;
}
}
}
public function setGet() {
$this->get = new InputCollection();
public function setPost()
{
$this->post = new InputCollection();
if(count($_GET)) {
foreach($_GET as $key => $get) {
if(!is_array($get)) {
$this->get->{$key} = new InputItem($key, $get);
continue;
}
$postVars = $_POST;
$output = new InputCollection();
if (in_array($this->request->getMethod(), ['put', 'patch', 'delete']) === true) {
parse_str(file_get_contents('php://input'), $postVars);
}
foreach($get as $k => $g) {
$output->{$k} = new InputItem($k, $g);
}
if (count($postVars) > 0) {
$this->get->{$key} = $output;
}
}
}
foreach ($postVars as $key => $post) {
if (is_array($post) === false) {
$this->post->{strtolower($key)} = new InputItem($key, $post);
continue;
}
public function setPost() {
$this->post = new InputCollection();
$output = new InputCollection();
$postVars = $_POST;
foreach ($post as $k => $p) {
$output->{$k} = new InputItem($k, $p);
}
if(in_array($this->request->getMethod(), ['put', 'patch', 'delete'])) {
parse_str(file_get_contents('php://input'), $postVars);
}
$this->post->{strtolower($key)} = $output;
}
}
}
if(count($postVars)) {
public function setFile()
{
$this->file = new InputCollection();
foreach($postVars as $key => $post) {
if(!is_array($post)) {
$this->post->{strtolower($key)} = new InputItem($key, $post);
continue;
}
if (count($_FILES) > 0) {
foreach ($_FILES as $key => $values) {
$output = new InputCollection();
// Handle array input
if (is_array($values['name']) === false && trim($values['error']) !== '4') {
$values['index'] = $key;
$this->file->{strtolower($key)} = InputFile::createFromArray($values);
continue;
}
foreach($post as $k => $p) {
$output->{$k} = new InputItem($k, $p);
}
$output = new InputCollection();
$this->post->{strtolower($key)} = $output;
}
}
}
foreach ($values['name'] as $k => $val) {
if (trim($val['error'][$k]) !== '4') {
$output->{$k} = InputFile::createFromArray([
'index' => $k,
'error' => $val['error'][$k],
'tmp_name' => $val['tmp_name'][$k],
'type' => $val['type'][$k],
'size' => $val['size'][$k],
'name' => $val['name'][$k]
]);
}
}
public function setFile() {
$this->file = new InputCollection();
$this->file->{strtolower($key)} = $output;
}
}
}
if(count($_FILES)) {
foreach($_FILES as $key => $value) {
// Multiple files
if(!is_array($value['name'])) {
// Strip empty values
if($value['error'] != '4') {
$file = new InputFile($key);
$file->setName($value['name']);
$file->setSize($value['size']);
$file->setType($value['type']);
$file->setTmpName($value['tmp_name']);
$file->setError($value['error']);
$this->file->{strtolower($key)} = $file;
}
continue;
}
$output = new InputCollection();
foreach($value['name'] as $k=>$val) {
// Strip empty values
if($value['error'][$k] != '4') {
$file = new InputFile($k);
$file->setName($value['name'][$k]);
$file->setSize($value['size'][$k]);
$file->setType($value['type'][$k]);
$file->setTmpName($value['tmp_name'][$k]);
$file->setError($value['error'][$k]);
$output->{$k} = $file;
}
}
$this->file->{strtolower($key)} = $output;
}
}
}
}
+52 -79
View File
@@ -1,94 +1,67 @@
<?php
namespace Pecee\Http\Input;
class InputCollection implements \IteratorAggregate
{
protected $data = array();
class InputCollection implements \IteratorAggregate {
/**
* Search for input element matching index.
*
* @param string $index
* @param string|null $default
* @return InputItem|mixed
*/
public function findFirst($index, $default = null)
{
if (count($this->data) > 0) {
protected $data = array();
if (isset($this->data[$index])) {
return $this->data[$index];
}
/**
* Search for input element matching index.
* Useful for searching for finding items where $index doesn't contain form name.
*
* @param string $index
* @return mixed
*/
public function findFirst($index) {
if(count($this->data)) {
foreach ($this->data as $key => $input) {
if (strtolower($index) === strtolower($key)) {
return $input;
}
}
}
if(isset($this->data[$index])) {
return $this->data[$index];
}
return $default;
}
foreach($this->data as $key => $value) {
if(strtolower($index) === strtolower($key)) {
return $value;
}
}
}
/**
* Get input element value matching index
*
* @param string $index
* @param string|null $default
* @return string|null
*/
public function get($index, $default = null)
{
$input = $this->findFirst($index);
return null;
}
if($input !== null && trim($input->getValue()) !== '') {
return $input->getValue();
}
/**
* @param $index
* @throws \InvalidArgumentException
* @return InputItem
*/
public function __get($index) {
$item = $this->findFirst($index);
// Ensure that item are always available
if($item === null) {
$this->data[$index] = new InputItem($index, null);
return $this->data[$index];
}
return $default;
}
return $item;
}
public function getValue($index, $default = null) {
public function __set($index, $value) {
$this->data[$index] = $value;
}
}
public function getData() {
return $this->data;
}
/**
* @param string $index
* @throws \InvalidArgumentException
* @return InputItem
*/
public function __get($index)
{
$item = $this->findFirst($index);
// Ensure that item are always available
if ($item === null) {
$this->data[$index] = new InputItem($index, null);
return $this->data[$index];
}
return $item;
}
public function __set($index, $value)
{
$this->data[$index] = $value;
}
public function getData()
{
return $this->data;
}
/**
* Retrieve an external iterator
* @link http://php.net/manual/en/iteratoraggregate.getiterator.php
* @return \Traversable An instance of an object implementing <b>Iterator</b> or
* <b>Traversable</b>
* @since 5.0.0
*/
public function getIterator()
{
return new \ArrayIterator($this->data);
}
/**
* Retrieve an external iterator
* @link http://php.net/manual/en/iteratoraggregate.getiterator.php
* @return \Traversable An instance of an object implementing <b>Iterator</b> or
* <b>Traversable</b>
* @since 5.0.0
*/
public function getIterator() {
return new \ArrayIterator($this->data);
}
}
+27 -95
View File
@@ -1,136 +1,68 @@
<?php
namespace Pecee\Http\Input;
class InputFile extends InputItem
{
public $size;
public $type;
public $error;
public $tmpName;
class InputFile extends InputItem {
protected $name;
protected $size;
protected $type;
protected $error;
protected $tmpName;
/**
* @return string
*/
public function getSize()
{
public function getSize() {
return $this->size;
}
/**
* @return string
*/
public function getType()
{
public function getType() {
return $this->type;
}
/**
* @return string
*/
public function getError()
{
public function getError() {
return $this->error;
}
public function getMime()
{
return $this->getType();
}
/**
* @return string
*/
public function getTmpName()
{
public function getTmpName() {
return $this->tmpName;
}
public function getExtension()
{
public function getExtension() {
return pathinfo($this->getName(), PATHINFO_EXTENSION);
}
}
public function move($destination)
{
public function move($destination) {
return move_uploaded_file($this->tmpName, $destination);
}
public function getContents()
{
public function getContents() {
return file_get_contents($this->tmpName);
}
/**
* Set file temp. name
* @param string $name
* @return static $this
*/
public function setTmpName($name)
{
$this->tmpName = $name;
return $this;
}
public function setTmpName($name) {
$this->tmpName = $name;
}
/**
* Set file size
* @param int $size
* @return static $this
*/
public function setSize($size)
{
$this->size = $size;
return $this;
}
public function setSize($size) {
$this->size = $size;
}
/**
* Set type
* @param string $type
* @return static $this
*/
public function setType($type)
{
$this->type = $type;
return $this;
}
public function setType($type) {
$this->type = $type;
}
/**
* Set error
* @param int $error
* @return static $this
*/
public function setError($error)
{
$this->error = $error;
return $this;
}
/**
* Create from array
* @param array $values
* @return static
*/
public static function createFromArray(array $values)
{
if(!isset($values['index'])) {
throw new \InvalidArgumentException('Index key is required');
}
$input = new static($values['index']);
$input->setTmpName((isset($values['error']) ? $values['error'] : null));
$input->setName((isset($values['name']) ? $values['name'] : null));
$input->setSize((isset($values['size']) ? $values['size'] : null));
$input->setType((isset($values['type']) ? $values['type'] : null));
$input->setError((isset($values['tmp_name']) ? $values['tmp_name'] : null));
return $input;
}
/**
* @return string
*/
public function getValue()
{
return $this->tmpName;
}
public function setError($error) {
$this->error = $error;
}
}
+50 -57
View File
@@ -1,70 +1,63 @@
<?php
namespace Pecee\Http\Input;
class InputItem
{
public $index;
public $name;
public $value;
class InputItem {
public function __construct($index, $value = null)
{
$this->index = $index;
$this->value = $value;
protected $index;
protected $name;
protected $value;
// Make the name human friendly, by replace _ with space
$this->name = ucfirst(str_replace('_', ' ', $this->index));
}
public function __construct($index, $value = null) {
$this->index = $index;
$this->value = $value;
/**
* @return string
*/
public function getName()
{
return $this->name;
}
// Make the name human friendly, by replace _ with space
$this->name = ucfirst(str_replace('_', ' ', $this->index));
}
/**
* @return string
*/
public function getValue()
{
return $this->value;
}
/**
* @return array
*/
public function getName() {
return $this->name;
}
/**
* @return string
*/
public function getIndex()
{
return $this->index;
}
/**
* @return array
*/
public function getValue() {
return $this->value;
}
/**
* Set input name
* @param string $name
* @return static $this
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* @return string
*/
public function getIndex() {
return $this->index;
}
/**
* Set input value
* @param string $value
* @return static $this
*/
public function setValue($value)
{
$this->value = $value;
return $this;
}
/**
* Set input name
* @param string $name
* @return static $this
*/
public function setName($name) {
$this->name = $name;
return $this;
}
public function __toString()
{
return (string)$this->value;
}
/**
* Set input value
* @param string $value
* @return static $this
*/
public function setValue($value) {
$this->value = $value;
return $this;
}
public function __toString() {
return (string)$this->getValue();
}
}
+62 -68
View File
@@ -4,92 +4,86 @@ namespace Pecee\Http\Middleware;
use Pecee\CsrfToken;
use Pecee\Exception\TokenMismatchException;
use Pecee\Http\Request;
use Pecee\SimpleRouter\RouterEntry;
class BaseCsrfVerifier implements IMiddleware
{
const POST_KEY = 'csrf-token';
const HEADER_KEY = 'X-CSRF-TOKEN';
class BaseCsrfVerifier implements IMiddleware {
protected $except;
protected $csrfToken;
protected $token;
const POST_KEY = 'csrf-token';
const HEADER_KEY = 'X-CSRF-TOKEN';
public function __construct()
{
$this->csrfToken = new CsrfToken();
protected $except;
protected $csrfToken;
protected $token;
// Generate or get the CSRF-Token from Cookie.
$this->token = (!$this->hasToken()) ? $this->generateToken() : $this->csrfToken->getToken();
}
public function __construct() {
$this->csrfToken = new CsrfToken();
/**
* Check if the url matches the urls in the except property
* @param Request $request
* @return bool
*/
protected function skip(Request $request)
{
if ($this->except === null || is_array($this->except) === false) {
return false;
}
// Generate or get the CSRF-Token from Cookie.
$this->token = (!$this->hasToken()) ? $this->generateToken() : $this->csrfToken->getToken();
}
foreach ($this->except as $url) {
$url = rtrim($url, '/');
if ($url[strlen($url) - 1] === '*') {
$url = rtrim($url, '*');
$skip = (stripos($request->getUri(), $url) === 0);
} else {
$skip = ($url === rtrim($request->getUri(), '/'));
}
/**
* Check if the url matches the urls in the except property
* @param Request $request
* @return bool
*/
protected function skip(Request $request) {
if ($skip) {
return true;
}
}
if($this->except === null || !is_array($this->except)) {
return false;
}
return false;
}
foreach($this->except as $url) {
$url = rtrim($url, '/');
if($url[strlen($url)-1] === '*') {
$url = rtrim($url, '*');
$skip = (stripos($request->getUri(), $url) === 0);
} else {
$skip = ($url === rtrim($request->getUri(), '/'));
}
public function handle(Request $request, RouterEntry &$route = null)
{
if($skip) {
return true;
}
}
if ($request->getMethod() !== 'get' && !$this->skip($request)) {
return false;
}
$token = $request->getInput()->post->get(static::POST_KEY);
public function handle(Request $request) {
// If the token is not posted, check headers for valid x-csrf-token
if ($token === null) {
$token = $request->getHeader(static::HEADER_KEY);
}
if($request->getMethod() != 'get' && !$this->skip($request)) {
if (!$this->csrfToken->validate($token)) {
throw new TokenMismatchException('Invalid csrf-token.');
}
$token = (isset($_POST[static::POST_KEY])) ? $_POST[static::POST_KEY] : null;
}
// If the token is not posted, check headers for valid x-csrf-token
if($token === null) {
$token = $request->getHeader(static::HEADER_KEY);
}
}
if( !$this->csrfToken->validate($token) ) {
throw new TokenMismatchException('Invalid csrf-token.');
}
public function generateToken()
{
$token = $this->csrfToken->generateToken();
$this->csrfToken->setToken($token);
return $token;
}
}
public function hasToken()
{
if ($this->token != null) {
return true;
}
}
return $this->csrfToken->hasToken();
}
public function generateToken() {
$token = $this->csrfToken->generateToken();
$this->csrfToken->setToken($token);
return $token;
}
public function getToken()
{
return $this->token;
}
public function hasToken() {
if($this->token != null) {
return true;
}
return $this->csrfToken->hasToken();
}
public function getToken() {
return $this->token;
}
}
+2 -10
View File
@@ -2,15 +2,7 @@
namespace Pecee\Http\Middleware;
use Pecee\Http\Request;
use Pecee\SimpleRouter\RouterEntry;
interface IMiddleware
{
/**
* @param Request $request
* @param RouterEntry $route
* @return Request|null
*/
public function handle(Request $request, RouterEntry &$route);
interface IMiddleware {
public function handle(Request $request);
}
+154 -174
View File
@@ -3,204 +3,184 @@ namespace Pecee\Http;
use Pecee\Http\Input\Input;
class Request
{
protected $data = array();
protected $headers;
protected $host;
protected $uri;
protected $method;
protected $input;
class Request {
public function __construct()
{
$this->parseHeaders();
$this->input = new Input($this);
$this->host = $this->getHeader('http-host');;
$this->uri = $this->getHeader('request-uri');
$this->method = strtolower($this->input->post->findFirst('_method', $this->getHeader('request-method')));
}
protected static $instance;
protected function parseHeaders()
{
$this->headers = array();
protected $data;
foreach ($_SERVER as $name => $value) {
$this->headers[strtolower($name)] = $value;
$this->headers[strtolower(str_replace('_', '-', $name))] = $value;
}
}
/**
* Return new instance
* @return static
*/
public static function getInstance() {
if(self::$instance === null) {
self::$instance = new static();
}
return self::$instance;
}
public function isSecure()
{
if ($this->getHeader('http-x-forwarded-proto') === 'https' || $this->getHeader('https') !== null || $this->getHeader('server-port') === 443) {
return true;
}
public function __construct() {
$this->data = array();
$this->host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : array();
$this->uri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : array();
$this->method = (isset($_POST['_method'])) ? strtolower($_POST['_method']) : (isset($_SERVER['REQUEST_METHOD']) ? strtolower($_SERVER['REQUEST_METHOD']) : array());
$this->headers = $this->getAllHeaders();
$this->input = new Input($this);
}
return false;
}
protected function getAllHeaders() {
$headers = array();
foreach ($_SERVER as $name => $value) {
if (substr($name, 0, 5) === 'HTTP_') {
$headers[strtolower(str_replace('_', '-', substr($name, 5)))] = $value;
}
}
return $headers;
}
/**
* @return string
*/
public function getUri()
{
return $this->uri;
}
public function getIsSecure() {
if(isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) === 'https') {
return true;
}
return isset($_SERVER['HTTPS']) ? true : (isset($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] === 443);
}
/**
* @return string
*/
public function getHost()
{
return $this->host;
}
/**
* @return string
*/
public function getUri() {
return $this->uri;
}
/**
* @return string
*/
public function getMethod()
{
return $this->method;
}
/**
* @return string
*/
public function getHost() {
return $this->host;
}
/**
* Get http basic auth user
* @return string|null
*/
public function getUser()
{
return $this->getHeader('php-auth-user');
}
/**
* @return string
*/
public function getMethod() {
return $this->method;
}
/**
* Get http basic auth password
* @return string|null
*/
public function getPassword()
{
return $this->getHeader('php-auth-pw');
}
/**
* Get http basic auth user
* @return string|null
*/
public function getUser() {
return (isset($_SERVER['PHP_AUTH_USER'])) ? $_SERVER['PHP_AUTH_USER']: null;
}
/**
* Get all headers
* @return array
*/
public function getHeaders()
{
return $this->headers;
}
/**
* Get http basic auth password
* @return string|null
*/
public function getPassword() {
return (isset($_SERVER['PHP_AUTH_PW'])) ? $_SERVER['PHP_AUTH_PW']: null;
}
/**
* Get id address
* @return string
*/
public function getIp()
{
if ($this->getHeader('http-cf-connecting-ip') !== null) {
return $this->getHeader('http-cf-connecting-ip');
}
/**
* Get headers
* @return array
*/
public function getHeaders() {
return $this->headers;
}
if ($this->getHeader('http-x-forwarded-for') !== null && strlen($this->getHeader('http-x-forwarded-for'))) {
return $this->getHeader('http-x-forwarded_for');
}
/**
* Get id address
* @return string
*/
public function getIp() {
if(isset($_SERVER['HTTP_CF_CONNECTING_IP'])) {
return $_SERVER['HTTP_CF_CONNECTING_IP'];
}
return ((isset($_SERVER['HTTP_X_FORWARDED_FOR']) && strlen($_SERVER['HTTP_X_FORWARDED_FOR'])) ? $_SERVER['HTTP_X_FORWARDED_FOR'] : isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : null);
}
return $this->getHeader('remote-addr');
}
/**
* Get referer
* @return string
*/
public function getReferer() {
return isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '';
}
/**
* Get referer
* @return string
*/
public function getReferer()
{
return $this->getHeader('http-referer');
}
/**
* Get user agent
* @return string
*/
public function getUserAgent() {
return isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
}
/**
* Get user agent
* @return string
*/
public function getUserAgent()
{
return $this->getHeader('http-user-agent');
}
/**
* Get header value by name
* @param string $name
* @return string|null
*/
public function getHeader($name) {
return (isset($this->headers[strtolower($name)])) ? $this->headers[strtolower($name)] : null;
}
/**
* Get header value by name
*
* @param string $name
* @param object|null $defaultValue
*
* @return string|null
*/
public function getHeader($name, $defaultValue = null)
{
return isset($this->headers[strtolower($name)]) ? $this->headers[strtolower($name)] : $defaultValue;
}
/**
* Get input class
* @return Input
*/
public function getInput() {
return $this->input;
}
/**
* Get input class
* @return Input
*/
public function getInput()
{
return $this->input;
}
public function isFormatAccepted($format) {
return (isset($_SERVER['HTTP_ACCEPT']) && stripos($_SERVER['HTTP_ACCEPT'], $format) > -1);
}
/**
* Is format accepted
*
* @param string $format
*
* @return bool
*/
public function isFormatAccepted($format)
{
return ($this->getHeader('http-accept') !== null && stripos($this->getHeader('http-accept'), $format) > -1);
}
public function getAcceptFormats() {
if(isset($_SERVER['HTTP_ACCEPT'])) {
return explode(',', $_SERVER['HTTP_ACCEPT']);
}
return array();
}
/**
* Get accept formats
* @return array
*/
public function getAcceptFormats()
{
return explode(',', $this->getHeader('http-accept'));
}
public function __set($name, $value = null) {
$this->data[$name] = $value;
}
/**
* @param string $uri
*/
public function setUri($uri)
{
$this->uri = $uri;
}
public function __get($name) {
return isset($this->data[$name]) ? $this->data[$name] : null;
}
/**
* @param string $host
*/
public function setHost($host)
{
$this->host = $host;
}
/**
* Get the currently loaded route.
* @return \Pecee\SimpleRouter\RouterEntry
*/
public function getLoadedRoute() {
return $this->loadedRoute;
}
/**
* @param string $method
*/
public function setMethod($method)
{
$this->method = $method;
}
/**
* @param mixed $uri
*/
public function setUri($uri) {
$this->uri = $uri;
}
public function __set($name, $value = null)
{
$this->data[$name] = $value;
}
/**
* @param mixed $host
*/
public function setHost($host) {
$this->host = $host;
}
public function __get($name)
{
return isset($this->data[$name]) ? $this->data[$name] : null;
}
/**
* @param mixed $method
*/
public function setMethod($method) {
$this->method = $method;
}
}
+89 -98
View File
@@ -1,118 +1,109 @@
<?php
namespace Pecee\Http;
class Response
{
protected $request;
class Response {
public function __construct(Request $request)
{
$this->request = $request;
}
/**
* Set the http status code
*
* @param int $code
* @return self $this
*/
public function httpCode($code) {
http_response_code($code);
return $this;
}
/**
* Set the http status code
*
* @param int $code
* @return static
*/
public function httpCode($code)
{
http_response_code($code);
return $this;
}
/**
* Redirect the response
*
* @param string $url
* @param int $httpCode
*/
public function redirect($url, $httpCode = null) {
if($httpCode !== null) {
$this->httpCode($httpCode);
}
/**
* Redirect the response
*
* @param string $url
* @param int $httpCode
*/
public function redirect($url, $httpCode = null)
{
if ($httpCode !== null) {
$this->httpCode($httpCode);
}
$this->header('location: ' . $url);
die();
}
$this->header('location: ' . $url);
die();
}
public function refresh() {
$this->redirect(Request::getInstance()->getUri());
}
public function refresh()
{
$this->redirect($this->request->getUri());
}
/**
* Add http authorisation
* @param string $name
* @return self $this
*/
public function auth($name = '') {
$this->headers([
'WWW-Authenticate: Basic realm="' . $name . '"',
'HTTP/1.0 401 Unauthorized'
]);
return $this;
}
/**
* Add http authorisation
* @param string $name
* @return static
*/
public function auth($name = '')
{
$this->headers([
'WWW-Authenticate: Basic realm="' . $name . '"',
'HTTP/1.0 401 Unauthorized'
]);
return $this;
}
public function cache($eTag, $lastModified = 2592000) {
public function cache($eTag, $lastModified = 2592000)
{
$this->headers([
'Cache-Control: public',
'Last-Modified: ' . gmdate("D, d M Y H:i:s", $lastModified) . ' GMT',
'Etag: ' . $eTag
]);
$this->headers([
'Cache-Control: public',
'Last-Modified: ' . gmdate("D, d M Y H:i:s", $lastModified) . ' GMT',
'Etag: ' . $eTag
]);
if(isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) === $lastModified ||
isset($_SERVER['HTTP_IF_NONE_MATCH']) && $_SERVER['HTTP_IF_NONE_MATCH'] === $eTag) {
if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) === $lastModified ||
isset($_SERVER['HTTP_IF_NONE_MATCH']) && $_SERVER['HTTP_IF_NONE_MATCH'] === $eTag
) {
$this->headers([
'HTTP/1.1 304 Not Modified'
]);
$this->headers([
'HTTP/1.1 304 Not Modified'
]);
exit();
}
exit();
}
return $this;
}
return $this;
}
/**
* Json encode
* @param array|\JsonSerializable $value
* @throws \InvalidArgumentException;
*/
public function json($value) {
/**
* Json encode array
* @param array $value
*/
public function json(array $value)
{
$this->header('Content-type: application/json');
echo json_encode($value);
die();
}
if(($value instanceof \JsonSerializable) === false && is_array($value) === false) {
throw new \InvalidArgumentException('Invalid type for parameter "value". Must be of type array or object implementing the \JsonSerializable interface.');
}
/**
* Add header to response
* @param string $value
* @return static
*/
public function header($value)
{
header($value);
return $this;
}
$this->header('Content-type: application/json');
echo json_encode($value);
exit(0);
}
/**
* Add multiple headers to response
* @param array $headers
* @return static
*/
public function headers(array $headers)
{
foreach ($headers as $header) {
header($header);
}
return $this;
}
/**
* Add header to response
* @param string $value
* @return self $this
*/
public function header($value) {
header($value);
return $this;
}
/**
* Add multiple headers to response
* @param array $headers
* @return self $this
*/
public function headers(array $headers) {
foreach($headers as $header) {
header($header);
}
return $this;
}
}
@@ -1,13 +0,0 @@
<?php
namespace Pecee\SimpleRouter;
interface IControllerRoute
{
public function getController();
public function setController($controller);
public function getMethod();
public function setMethod($method);
}
@@ -1,9 +0,0 @@
<?php
namespace Pecee\SimpleRouter;
interface ILoadableRoute
{
public function getUrl();
public function setUrl($url);
}
-97
View File
@@ -1,97 +0,0 @@
<?php
namespace Pecee\SimpleRouter;
abstract class LoadableRoute extends RouterEntry implements ILoadableRoute
{
const PARAMETERS_REGEX_MATCH = '%s([\w\-\_]*?)\%s{0,1}%s';
const PARAMETER_MODIFIERS = '{}';
const PARAMETER_OPTIONAL_SYMBOL = '?';
protected $url;
protected $alias;
public function getUrl()
{
return $this->url;
}
/**
* Set url
*
* @param string $url
* @return static
*/
public function setUrl($url)
{
$this->url = ($url === '/') ? '/' : '/' . trim($url, '/') . '/';
$regex = sprintf(static::PARAMETERS_REGEX_MATCH, static::PARAMETER_MODIFIERS[0], static::PARAMETER_OPTIONAL_SYMBOL, static::PARAMETER_MODIFIERS[1]);
if (preg_match_all('/' . $regex . '/is', $this->url, $matches)) {
foreach ($matches[1] as $key) {
$this->parameters[$key] = null;
}
}
return $this;
}
/**
* Get alias for the url which can be used when getting the url route.
* @return string|array
*/
public function getAlias()
{
return $this->alias;
}
/**
* Check if route has given alias.
*
* @param string $name
* @return bool
*/
public function hasAlias($name)
{
if ($this->getAlias() !== null) {
if (is_array($this->getAlias()) === true) {
foreach ($this->getAlias() as $alias) {
if (strtolower($alias) === strtolower($name)) {
return true;
}
}
}
return strtolower($this->getAlias()) === strtolower($name);
}
return false;
}
/**
* Set the url alias for easier getting the url route.
* @param string|array $alias
* @return static
*/
public function setAlias($alias)
{
$this->alias = $alias;
return $this;
}
/**
* Merge with information from another route.
*
* @param array $values
* @return static
*/
public function merge(array $values)
{
// Change as to alias
if (isset($values['as'])) {
$this->setAlias($values['as']);
}
parent::merge($values);
return $this;
}
}
+426 -498
View File
@@ -5,554 +5,482 @@ use Pecee\Exception\RouterException;
use Pecee\Handler\IExceptionHandler;
use Pecee\Http\Middleware\BaseCsrfVerifier;
use Pecee\Http\Request;
use Pecee\Http\Response;
class RouterBase
{
/**
* @var static
*/
protected static $instance;
/**
* Current request
* @var Request
*/
protected $request;
/**
* Response
* @var Response
*/
protected $response;
/**
* Used to keep track of whether or not a should should be added to
* the backstack-list for group-processing or not.
* @var bool
*/
protected $processingRoute;
/**
* All added routes
* @var array
*/
protected $routes;
/**
* List of
* @var array
*/
protected $controllerUrlMap;
/**
* Backstack array used to keep track of sub-routes
* @var array
*/
protected $backStack;
/**
* List of added bootmanagers
* @var array
*/
protected $bootManagers;
/**
* Csrf verifier class
* @var BaseCsrfVerifier
*/
protected $csrfVerifier;
/**
* Get exception handlers
* @var array
*/
protected $exceptionHandlers;
/**
* The current loaded route
* @var RouterRoute|null
*/
protected $loadedRoute;
/**
* List over route changes (to avoid endless-looping)
* @var array
*/
protected $routeRewrites = [];
/**
* If the route has been rewritten/changed this property will contain the original url.
* @var string
*/
protected $originalUrl;
/**
* Get current router instance
* @return static
*/
public static function getInstance()
{
if (static::$instance === null) {
static::$instance = new static();
}
return static::$instance;
}
public function __construct()
{
$this->reset();
}
public function reset()
{
$this->processingRoute = false;
$this->request = new Request();
$this->response = new Response($this->request);
$this->routes = array();
$this->bootManagers = array();
$this->backStack = array();
$this->controllerUrlMap = array();
$this->exceptionHandlers = array();
}
/**
* Add route
* @param RouterEntry $route
* @return RouterEntry
*/
public function addRoute(RouterEntry $route)
{
if ($this->processingRoute) {
$this->backStack[] = $route;
} else {
$this->routes[] = $route;
}
return $route;
}
protected function processRoutes(array $routes, array $settings = array(), array $prefixes = array(), RouterEntry $parent = null) {
$mergedSettings = [];
// Loop through each route-request
/* @var $route RouterEntry */
foreach ($routes as $route) {
$route->merge($settings);
if ($parent !== null) {
$route->setParent($parent);
}
if ($route instanceof ILoadableRoute) {
if($parent !== null && count($prefixes)) {
$route->setUrl(trim(join('/', $prefixes) . $route->getUrl(), '/'));
}
$this->controllerUrlMap[] = $route;
} elseif ($route instanceof RouterGroup) {
if ($route->getPrefix() !== null && trim($route->getPrefix(), '/') !== '') {
$prefixes[] = trim($route->getPrefix(), '/');
}
if ($route->getCallback() !== null && is_callable($route->getCallback())) {
$this->processingRoute = true;
$route->renderRoute($this->request);
$this->processingRoute = false;
if ($route->matchRoute($this->request)) {
$mergedSettings = array_merge($mergedSettings, $route->toArray());
// Add ExceptionHandler
if (count($route->getExceptionHandlers()) > 0) {
$this->exceptionHandlers = array_merge($route->getExceptionHandlers(),
$this->exceptionHandlers);
}
}
}
}
if (count($this->backStack) > 0) {
$backStack = $this->backStack;
$this->backStack = array();
// Route any routes added to the backstack
$this->processRoutes($backStack, $mergedSettings, $prefixes, $route);
}
}
}
public function routeRequest($rewrite = false)
{
$this->loadedRoute = null;
$routeNotAllowed = false;
try {
class RouterBase {
// Initialize boot-managers
if (count($this->bootManagers) > 0) {
/* @var $manager RouterBootManager */
foreach ($this->bootManagers as $manager) {
$this->request = $manager->boot($this->request);
protected static $instance;
if (!($this->request instanceof Request)) {
throw new RouterException('Custom router bootmanager "' . get_class($manager) . '" must return instance of Request.');
}
}
}
protected $request;
protected $currentRoute;
protected $routes;
protected $processedRoutes;
protected $controllerUrlMap;
protected $backStack;
protected $defaultNamespace;
protected $bootManagers;
protected $baseCsrfVerifier;
protected $exceptionHandlers;
if ($rewrite === false) {
// TODO: clean up - cut some of the methods down to smaller pieces
// Loop through each route-request
$this->processRoutes($this->routes);
public function __construct() {
$this->request = Request::getInstance();
$this->routes = array();
$this->backStack = array();
$this->controllerUrlMap = array();
$this->bootManagers = array();
$this->exceptionHandlers = array();
}
if ($this->csrfVerifier !== null) {
public function addRoute(RouterEntry $route) {
if($this->currentRoute !== null) {
$this->backStack[] = $route;
} else {
$this->routes[] = $route;
}
}
// Verify csrf token for request
$this->csrfVerifier->handle($this->request);
}
protected function processRoutes(array $routes, array $settings = array(), array $prefixes = array(), $backStack = false, $group = null) {
// Loop through each route-request
$this->originalUrl = $this->request->getUri();
}
$routesCount = count($routes);
$mergedSettings = array();
/* @var $route RouterEntry */
foreach ($this->controllerUrlMap as $route) {
/* @var $route RouterEntry */
for($i = 0; $i < $routesCount; $i++) {
if ($route->matchRoute($this->request)) {
$route = $routes[$i];
if (count($route->getRequestMethods()) > 0 && !in_array($this->request->getMethod(), $route->getRequestMethods())) {
$routeNotAllowed = true;
continue;
}
$route->addSettings($settings);
$this->loadedRoute = $route;
$this->loadedRoute->loadMiddleware($this->request, $this->loadedRoute);
if($backStack) {
$route->setGroup($group);
}
if ($this->request->getUri() !== $this->originalUrl && !in_array($this->request->getUri(), $this->routeRewrites)) {
$this->routeRewrites[] = $this->request->getUri();
$this->routeRequest(true);
return;
}
if($this->defaultNamespace && !$route->getNamespace()) {
$namespace = $this->defaultNamespace;
if ($route->getNamespace()) {
$namespace .= '\\' . $route->getNamespace();
}
$routeNotAllowed = false;
$this->request->setUri($this->originalUrl);
$this->loadedRoute->renderRoute($this->request);
$route->setNamespace($namespace);
}
break;
}
}
$newPrefixes = $prefixes;
} catch (\Exception $e) {
$this->handleException($e);
}
if($route->getPrefix() && trim($route->getPrefix(), '/') !== '') {
array_push($newPrefixes, trim($route->getPrefix(), '/'));
}
if ($routeNotAllowed) {
$this->handleException(new RouterException('Route or method not allowed', 403));
}
/* @var $group RouterGroup */
$group = null;
if ($this->loadedRoute === null) {
$this->handleException(new RouterException(sprintf('Route not found: %s', $this->request->getUri()), 404));
}
}
if(!($route instanceof RouterGroup)) {
if(is_array($newPrefixes) && count($newPrefixes) && $backStack) {
$route->setUrl( '/' . join('/', $newPrefixes) . $route->getUrl() );
}
protected function handleException(\Exception $e)
{
/* @var $handler IExceptionHandler */
foreach ($this->exceptionHandlers as $handler) {
$this->controllerUrlMap[] = $route;
}
$handler = new $handler();
$this->currentRoute = $route;
if (!($handler instanceof IExceptionHandler)) {
throw new RouterException('Exception handler must implement the IExceptionHandler interface.');
}
if($route instanceof RouterGroup && is_callable($route->getCallback())) {
$request = $handler->handleError($this->request, $this->loadedRoute, $e);
$route->renderRoute($this->request);
if ($request !== null && $request->getUri() !== $this->originalUrl && !in_array($request->getUri(), $this->routeRewrites)) {
$this->routeRewrites[] = $request->getUri();
$this->routeRequest(true);
return;
}
if($route->matchRoute($this->request)) {
}
$group = $route;
throw $e;
}
$mergedSettings = array_merge($settings, $group->getMergeableSettings());
public function arrayToParams(array $getParams = null, $includeEmpty = true)
{
if (is_array($getParams) === true && count($getParams) > 0) {
// Add ExceptionHandler
if ($group->getExceptionHandler() !== null) {
$this->exceptionHandlers[] = $route;
}
if ($includeEmpty === false) {
$getParams = array_filter($getParams, function ($item) {
return (!empty($item));
});
}
}
return '?' . http_build_query($getParams);
}
}
return '';
}
$this->currentRoute = null;
protected function processUrl(LoadableRoute $route, $method = null, $parameters = null, $getParams = null)
{
$domain = '';
$parent = $route->getParent();
if(count($this->backStack)) {
$backStack = $this->backStack;
$this->backStack = array();
$parameters = (array)$parameters;
// Route any routes added to the backstack
$this->processRoutes($backStack, $mergedSettings, $newPrefixes, true, $group);
}
}
}
if ($parent !== null && $parent instanceof RouterGroup && count($parent->getDomains()) > 0) {
$domain = $parent->getDomains();
$domain = '//' . $domain[0];
}
public function routeRequest($original = true) {
$url = $domain . '/' . trim($route->getUrl(), '/');
$originalUri = $this->request->getUri();
if ($route instanceof IControllerRoute && $method !== null) {
$routeNotAllowed = false;
$url .= '/' . $method . '/';
try {
if (count($parameters) > 0) {
$url .= join('/', (array)$parameters);
}
// Initialize boot-managers
if(count($this->bootManagers)) {
/* @var $manager RouterBootManager */
foreach($this->bootManagers as $manager) {
$this->request = $manager->boot($this->request);
} else {
if(!($this->request instanceof Request)) {
throw new RouterException('Custom router bootmanager "'. get_class($manager) .'" must return instance of Request.');
}
}
}
if ($parameters !== null && count($parameters) > 0) {
$params = array_merge($route->getParameters(), (array)$parameters);
} else {
$params = $route->getParameters();
}
// Loop through each route-request
$this->processRoutes($this->routes);
$otherParams = array();
if($original === true) {
// Verify csrf token for request
if ($this->baseCsrfVerifier !== null) {
$this->baseCsrfVerifier->handle($this->request);
}
}
foreach ($params as $param => $value) {
$value = (isset($parameters[$param])) ? $parameters[$param] : $value;
$max = count($this->controllerUrlMap);
$param1 = LoadableRoute::PARAMETER_MODIFIERS[0] . $param . LoadableRoute::PARAMETER_MODIFIERS[1];
$param2 = LoadableRoute::PARAMETER_MODIFIERS[0] . $param . LoadableRoute::PARAMETER_OPTIONAL_SYMBOL . LoadableRoute::PARAMETER_MODIFIERS[1];
/* @var $route RouterEntry */
for ($i = 0; $i < $max; $i++) {
if (stripos($url, $param1) !== false || stripos($url, $param) !== false) {
$url = str_ireplace([$param1, $param2], $value, $url);
} else {
$otherParams[$param] = $value;
}
}
$route = $this->controllerUrlMap[$i];
$url = rtrim($url, '/') . '/' . join('/', $otherParams);
}
$routeMatch = $route->matchRoute($this->request);
$url = rtrim($url, '/') . '/';
if ($routeMatch) {
if ($getParams !== null) {
$url .= $this->arrayToParams($getParams);
}
if (count($route->getRequestMethods()) && !in_array($this->request->getMethod(), $route->getRequestMethods())) {
$routeNotAllowed = true;
continue;
}
return $url;
}
$routeNotAllowed = false;
/**
* Find route by alias, class, callback or method.
*
* @param string $query
* @return LoadableRoute|null
*/
public function findRoute($query)
{
/* @var $route LoadableRoute */
foreach ($this->controllerUrlMap as $route) {
$this->request->rewrite_uri = $this->request->uri;
$this->request->setUri($originalUri);
// Check an alias exist, if the matches - use it
// Matches either Router alias or controller alias.
if ($route->hasAlias($query)) {
return $route;
}
$this->request->loadedRoute = $route;
$route->loadMiddleware($this->request);
// Direct match to controller
if ($route instanceof IControllerRoute) {
if (strtolower($route->getController()) === strtolower($query)) {
return $route;
}
}
$this->request->loadedRoute->renderRoute($this->request);
// Using @ is most definitely a controller@method or alias@method
if (strpos($query, '@') !== false) {
list($controller, $method) = array_map('strtolower', explode('@', $query));
break;
}
}
if ($controller === strtolower($route->getClass()) && $method === strtolower($route->getMethod())) {
return $route;
}
}
// Use callback if it's not a function
if (strpos($query, '@') !== false && strpos($route->getCallback(), '@') !== false && !is_callable($route->getCallback())) {
if (strtolower($query) === strtolower($route->getClass())) {
return $route;
}
if (strtolower($route->getCallback()) === strtolower($query) || strpos($route->getCallback(), $query) === 0) {
return $route;
}
}
}
return null;
}
public function getRoute($controller = null, $parameters = null, $getParams = null)
{
if ($getParams !== null && is_array($getParams) === false) {
throw new \InvalidArgumentException('Invalid type for getParams. Must be array or null');
}
// Return current route if no options has been specified
if ($controller === null && $parameters === null) {
$getParams = ($getParams !== null) ? $getParams : $_GET;
$url = parse_url($this->request->getUri(), PHP_URL_PATH) . $this->arrayToParams($getParams);
return $url;
}
// If nothing is defined and a route is loaded we use that
if ($controller === null && $this->loadedRoute !== null) {
return $this->processUrl($this->loadedRoute, $this->loadedRoute->getMethod(), $parameters, $getParams);
}
$route = $this->findRoute($controller);
if ($route !== null) {
return $this->processUrl($route, $route->getMethod(), $parameters, $getParams);
}
// Using @ is most definitely a controller@method or alias@method
if (stripos($controller, '@') !== false) {
list($controller, $method) = explode('@', $controller);
/* @var $route LoadableRoute */
foreach ($this->controllerUrlMap as $route) {
if ($route->hasAlias($controller)) {
return $this->processUrl($route, $method, $parameters, $getParams);
}
// Match controllers either by: "alias @ method" or "controller@method"
if ($route instanceof IControllerRoute && strtolower($route->getController()) === strtolower($controller)) {
return $this->processUrl($route, $method, $parameters, $getParams);
}
}
}
$url = [($controller === null) ? '/' : $controller];
if ($parameters !== null && count($parameters) > 0) {
$url = array_merge($url, (array)$parameters);
}
$url = '/' . trim(join('/', $url), '/') . '/';
if ($getParams !== null) {
$url .= $this->arrayToParams($getParams);
}
return $url;
}
/**
* Get bootmanagers
* @return array
*/
public function getBootManagers()
{
return $this->bootManagers;
}
/**
* Set bootmanagers
* @param array $bootManagers
*/
public function setBootManagers(array $bootManagers)
{
$this->bootManagers = $bootManagers;
}
/**
* Add bootmanager
* @param RouterBootManager $bootManager
*/
public function addBootManager(RouterBootManager $bootManager)
{
$this->bootManagers[] = $bootManager;
}
/**
* @return array
*/
public function getRoutes()
{
return $this->routes;
}
/**
* Get current request
*
* @return Request
*/
public function getRequest()
{
return $this->request;
}
/**
* Get response
* @return Response
*/
public function getResponse()
{
return $this->response;
}
/**
* Get csrf verifier class
* @return BaseCsrfVerifier
*/
public function getCsrfVerifier()
{
return $this->csrfVerifier;
}
/**
* Set csrf verifier class
*
* @param BaseCsrfVerifier $csrfVerifier
* @return static
*/
public function setCsrfVerifier(BaseCsrfVerifier $csrfVerifier)
{
$this->csrfVerifier = $csrfVerifier;
return $this;
}
/**
* Get loaded route
* @return RouterRoute|null
*/
public function getLoadedRoute()
{
return $this->loadedRoute;
}
} catch(\Exception $e) {
$this->handleException($e);
}
if($routeNotAllowed) {
$this->handleException(new RouterException('Route or method not allowed', 403));
}
if(!$this->request->loadedRoute) {
$this->handleException(new RouterException(sprintf('Route not found: %s', $this->request->getUri()), 404));
}
}
protected function handleException(\Exception $e) {
$request = null;
/* @var $route RouterGroup */
foreach ($this->exceptionHandlers as $route) {
$route->loadMiddleware($this->request);
$handler = $route->getExceptionHandler();
$handler = new $handler();
if (!($handler instanceof IExceptionHandler)) {
throw new RouterException('Exception handler must implement the IExceptionHandler interface.');
}
$request = $handler->handleError($this->request, $this->request->loadedRoute, $e);
}
if($request !== null) {
$this->request = $request;
$this->routeRequest(false);
return;
}
throw $e;
}
/**
* @return string
*/
public function getDefaultNamespace(){
return $this->defaultNamespace;
}
/**
* @param string $defaultNamespace
* @return static
*/
public function setDefaultNamespace($defaultNamespace) {
$this->defaultNamespace = $defaultNamespace;
return $this;
}
/**
* @return array
*/
public function getBootManagers() {
return $this->bootManagers;
}
/**
* @param array $bootManagers
*/
public function setBootManagers(array $bootManagers) {
$this->bootManagers = $bootManagers;
}
public function addBootManager(RouterBootManager $bootManager) {
$this->bootManagers[] = $bootManager;
}
/**
* @return RouterEntry
*/
public function getLoadedRoute() {
if(!($this->request->loadedRoute instanceof RouterGroup)) {
return $this->request->loadedRoute;
}
return null;
}
/**
* @return array
*/
public function getBackstack() {
return $this->backStack;
}
/**
* @return RouterEntry
*/
public function getCurrentRoute(){
return $this->currentRoute;
}
/**
* @return array
*/
public function getRoutes(){
return $this->routes;
}
/**
* Get current request
*
* @return Request
*/
public function getRequest() {
return $this->request;
}
/**
* Get base csrf verifier class
* @return BaseCsrfVerifier
*/
public function getBaseCsrfVerifier() {
return $this->baseCsrfVerifier;
}
/**
* Set base csrf verifier class
*
* @param BaseCsrfVerifier $baseCsrfVerifier
* @return self
*/
public function setBaseCsrfVerifier(BaseCsrfVerifier $baseCsrfVerifier) {
$this->baseCsrfVerifier = $baseCsrfVerifier;
return $this;
}
public function arrayToParams(array $getParams = null, $includeEmpty = true) {
if(is_array($getParams) && count($getParams)) {
if ($includeEmpty === false) {
$getParams = array_filter($getParams, function ($item) {
return (!empty($item));
});
}
return '?' . http_build_query($getParams);
}
return '';
}
protected function processUrl(RouterRoute $route, $method = null, $parameters = null, $getParams = null) {
$domain = '';
if($route->getGroup() !== null && $route->getGroup()->getDomain() !== null) {
if(is_array($route->getGroup()->getDomain())) {
$domains = $route->getGroup()->getDomain();
$domain = array_shift($domains);
} else {
$domain = $route->getGroup()->getDomain();
}
$domain = '//' . $domain;
}
$url = $domain . '/' . trim($route->getUrl(), '/');
if(($route instanceof RouterController || $route instanceof RouterResource) && $method !== null) {
$url .= $method;
}
if($route instanceof RouterController || $route instanceof RouterResource) {
if(count($parameters)) {
$url .= join('/', $parameters);
}
} else {
/* @var $route RouterEntry */
if(is_array($parameters)) {
$params = array_merge($route->getParameters(), $parameters);
} else {
$params = $route->getParameters();
}
$otherParams = [];
$i = 0;
foreach($params as $param => $value) {
$value = (isset($parameters[$param])) ? $parameters[$param] : $value;
if(stripos($url, '{' . $param. '}') !== false || stripos($url, '{' . $param . '?}') !== false) {
$url = str_ireplace(array('{' . $param . '}', '{' . $param . '?}'), $value, $url);
} else {
$otherParams[$param] = $value;
}
$i++;
}
$url = rtrim($url, '/') . '/' . join('/', $otherParams);
}
$url = rtrim($url, '/') . '/';
if($getParams !== null) {
$url .= $this->arrayToParams($getParams);
}
return $url;
}
public function getRoute($controller = null, $parameters = null, $getParams = null) {
if($parameters !== null && !is_array($parameters)) {
throw new \InvalidArgumentException('Invalid type for parameter. Must be array or null');
}
if($getParams !== null && !is_array($getParams)) {
throw new \InvalidArgumentException('Invalid type for getParams. Must be array or null');
}
// Return current route if no options has been specified
if($controller === null && $parameters === null) {
$getParams = (is_array($getParams)) ? array_merge($_GET, $getParams) : $_GET;
$url = parse_url($this->request->getUri(), PHP_URL_PATH);
if($getParams !== null) {
$url .= $this->arrayToParams($getParams);
}
return $url;
}
if($controller === null && $this->request->loadedRoute !== null) {
return $this->processUrl($this->request->loadedRoute, $this->request->loadedRoute->getMethod(), $parameters, $getParams);
}
$c = '';
$method = null;
$max = count($this->controllerUrlMap);
/* @var $route RouterRoute */
for($i = 0; $i < $max; $i++) {
$route = $this->controllerUrlMap[$i];
// Check an alias exist, if the matches - use it
if($route instanceof RouterRoute && $route->hasAlias($controller)) {
return $this->processUrl($route, $route->getMethod(), $parameters, $getParams);
}
if($route instanceof RouterRoute && !is_callable($route->getCallback()) && stripos($route->getCallback(), '@') !== false) {
$c = $route->getCallback();
} else if($route instanceof RouterController || $route instanceof RouterResource) {
$c = $route->getController();
}
if($c === $controller || strpos($c, $controller) === 0) {
return $this->processUrl($route, $route->getMethod(), $parameters, $getParams);
}
}
$c = '';
// No match has yet been found, let's try to guess what url that should be returned
for($i = 0; $i < $max; $i++) {
$route = $this->controllerUrlMap[$i];
if($route instanceof RouterRoute && !is_callable($route->getCallback()) && stripos($route->getCallback(), '@') !== false) {
$c = $route->getClass();
} else if($route instanceof RouterController || $route instanceof RouterResource) {
$c = $route->getController();
}
if(stripos($controller, '@') !== false) {
$tmp = explode('@', $controller);
$controller = $tmp[0];
$method = $tmp[1];
}
if($controller === $c) {
return $this->processUrl($route, $method, $parameters, $getParams);
}
}
$controller = ($controller === null) ? '/' : $controller;
$url = array($controller);
if(is_array($parameters)) {
foreach($parameters as $key => $value) {
array_push($url,$value);
}
}
$url = '/' . trim(join('/', $url), '/') . '/';
if($getParams !== null) {
$url .= $this->arrayToParams($getParams);
}
return $url;
}
public static function getInstance() {
if(static::$instance === null) {
static::$instance = new static();
}
return static::$instance;
}
}
+4 -3
View File
@@ -3,7 +3,8 @@ namespace Pecee\SimpleRouter;
use Pecee\Http\Request;
abstract class RouterBootManager
{
abstract public function boot(Request $request);
abstract class RouterBootManager {
abstract public function boot(Request $request);
}
+92 -80
View File
@@ -4,107 +4,119 @@ namespace Pecee\SimpleRouter;
use Pecee\Exception\RouterException;
use Pecee\Http\Request;
class RouterController extends LoadableRoute implements IControllerRoute
{
protected $defaultMethod = 'index';
protected $controller;
protected $method;
class RouterController extends RouterEntry {
public function __construct($url, $controller)
{
$this->setUrl($url);
$this->controller = $controller;
}
const DEFAULT_METHOD = 'index';
public function renderRoute(Request $request)
{
if ($this->getCallback() !== null && is_callable($this->getCallback())) {
protected $url;
protected $controller;
protected $method;
// 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];
public function __construct($url, $controller) {
parent::__construct();
$this->url = $url;
$this->controller = $controller;
}
$class = $this->loadClass($className);
$method = $request->getMethod() . ucfirst($controller[1]);
public function renderRoute(Request $request) {
if(is_object($this->getCallback()) && is_callable($this->getCallback())) {
if (!method_exists($class, $method)) {
throw new RouterException(sprintf('Method %s does not exist in class %s', $method, $className), 404);
}
// 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];
call_user_func_array(array($class, $method), $this->getParameters());
$class = $this->loadClass($className);
$method = $request->getMethod() . ucfirst($controller[1]);
return $class;
}
if (!method_exists($class, $method)) {
throw new RouterException(sprintf('Method %s does not exist in class %s', $method, $className), 404);
}
return null;
}
call_user_func_array(array($class, $method), $this->getParameters());
public function matchRoute(Request $request)
{
$url = parse_url(urldecode($request->getUri()), PHP_URL_PATH);
$url = rtrim($url, '/') . '/';
return $class;
}
if (strtolower($url) == strtolower($this->url) || stripos($url, $this->url) === 0) {
return null;
}
$strippedUrl = trim(str_ireplace($this->url, '/', $url), '/');
public function matchRoute(Request $request) {
$url = parse_url(urldecode($request->getUri()));
$url = rtrim($url['path'], '/') . '/';
$path = explode('/', $strippedUrl);
if(strtolower($url) == strtolower($this->url) || stripos($url, $this->url) === 0) {
if (count($path) > 0) {
$strippedUrl = trim(str_ireplace($this->url, '/', $url), '/');
$method = (!isset($path[0]) || trim($path[0]) === '') ? $this->defaultMethod : $path[0];
$this->method = $method;
$path = explode('/', $strippedUrl);
array_shift($path);
$this->parameters = $path;
if(count($path)) {
// Set callback
$this->setCallback($this->controller . '@' . $this->method);
$method = (!isset($path[0]) || trim($path[0]) === '') ? self::DEFAULT_METHOD : $path[0];
$this->method = $method;
return true;
}
}
array_shift($path);
$this->parameters = $path;
return null;
}
// Set callback
$this->setCallback($this->controller . '@' . $this->method);
/**
* @return string
*/
public function getController()
{
return $this->controller;
}
return true;
}
}
return null;
}
/**
* @param string $controller
* @return static
*/
public function setController($controller)
{
$this->controller = $controller;
return $this;
}
/**
* @return string
*/
public function getUrl() {
return $this->url;
}
/**
* @return string
*/
public function getMethod()
{
return $this->method;
}
/**
* @param string $url
* @return static
*/
public function setUrl($url) {
$url = rtrim($url, '/') . '/';
$this->url = $url;
return $this;
}
/**
* @param string $method
* @return static
*/
public function setMethod($method)
{
$this->method = $method;
return $this;
}
/**
* @return string
*/
public function getController() {
return $this->controller;
}
/**
* @param string $controller
* @return static
*/
public function setController($controller) {
$this->controller = $controller;
return $this;
}
/**
* @return string
*/
public function getMethod() {
return $this->method;
}
/**
* @param string $method
* @return static
*/
public function setMethod($method) {
$this->method = $method;
return $this;
}
}
+411 -419
View File
@@ -1,428 +1,420 @@
<?php
namespace Pecee\SimpleRouter;
use Pecee\Exception\RouterException;
use Pecee\Http\Middleware\IMiddleware;
use Pecee\Http\Request;
abstract class RouterEntry
{
const REQUEST_TYPE_GET = 'get';
const REQUEST_TYPE_POST = 'post';
const REQUEST_TYPE_PUT = 'put';
const REQUEST_TYPE_PATCH = 'patch';
const REQUEST_TYPE_OPTIONS = 'options';
const REQUEST_TYPE_DELETE = 'delete';
public static $requestTypes = [
self::REQUEST_TYPE_GET,
self::REQUEST_TYPE_POST,
self::REQUEST_TYPE_PUT,
self::REQUEST_TYPE_PATCH,
self::REQUEST_TYPE_OPTIONS,
self::REQUEST_TYPE_DELETE,
];
protected $parent;
protected $callback;
protected $namespace;
protected $regex;
protected $requestMethods = array();
protected $where = array();
protected $parameters = array();
protected $middlewares = array();
protected function loadClass($name)
{
if (!class_exists($name)) {
throw new RouterException(sprintf('Class %s does not exist', $name));
}
return new $name();
}
protected function parseParameters($route, $url, $parameterRegex = '[\w]+')
{
$parameterNames = array();
$regex = '';
$lastCharacter = '';
$isParameter = false;
$parameter = '';
$routeLength = strlen($route);
for ($i = 0; $i < $routeLength; $i++) {
$character = $route[$i];
if ($character === '{') {
// Remove "/" and "\" from regex
if (substr($regex, strlen($regex) - 1) === '/') {
$regex = substr($regex, 0, strlen($regex) - 2);
}
$isParameter = true;
} elseif ($isParameter && $character === '}') {
$required = true;
// Check for optional parameter and use custom parameter regex if it exists
if (is_array($this->where) === true && isset($this->where[$parameter])) {
$parameterRegex = $this->where[$parameter];
}
if ($lastCharacter === '?') {
$parameter = substr($parameter, 0, strlen($parameter) - 1);
$regex .= '(?:\/?(?P<' . $parameter . '>' . $parameterRegex . ')[^\/]?)?';
$required = false;
} else {
$regex .= '\/?(?P<' . $parameter . '>' . $parameterRegex . ')[^\/]?';
}
$parameterNames[] = [
'name' => $parameter,
'required' => $required
];
$parameter = '';
$isParameter = false;
} elseif ($isParameter) {
$parameter .= $character;
} elseif ($character === '/') {
$regex .= '\\' . $character;
} else {
$regex .= str_replace('.', '\\.', $character);
}
$lastCharacter = $character;
}
$parameterValues = array();
if (preg_match('/^' . $regex . '\/?$/is', $url, $parameterValues)) {
$parameters = array();
foreach ($parameterNames as $name) {
$parameterValue = isset($parameterValues[$name['name']]) ? $parameterValues[$name['name']] : null;
if ($name['required'] && $parameterValue === null) {
throw new RouterException('Missing required parameter ' . $name['name'], 404);
}
if ($name['required'] === false && $parameterValue === null) {
continue;
}
$parameters[$name['name']] = $parameterValue;
}
return $parameters;
}
return null;
}
public function loadMiddleware(Request $request, RouterEntry &$route)
{
if (count($this->getMiddlewares()) > 0) {
foreach ($this->getMiddlewares() as $middleware) {
$middleware = $this->loadClass($middleware);
if (!($middleware instanceof IMiddleware)) {
throw new RouterException($middleware . ' must be instance of Middleware');
}
/* @var $class IMiddleware */
$middleware->handle($request, $route);
}
}
}
public function renderRoute(Request $request)
{
if ($this->getCallback() !== null && is_callable($this->getCallback())) {
// When the callback is a function
call_user_func_array($this->getCallback(), $this->getParameters());
} else {
// When the callback is a method
$controller = explode('@', $this->getCallback());
$className = $this->getNamespace() . '\\' . $controller[0];
$class = $this->loadClass($className);
$method = $controller[1];
if (!method_exists($class, $method)) {
throw new RouterException(sprintf('Method %s does not exist in class %s', $method, $className), 404);
}
$parameters = array_filter($this->getParameters(), function ($var) {
return ($var !== null);
});
call_user_func_array(array($class, $method), $parameters);
return $class;
}
return null;
}
/**
* Returns callback name/identifier for the current route based on the callback.
* Useful if you need to get a unique identifier for the loaded route, for instance
* when using translations etc.
*
* @return string
*/
public function getIdentifier()
{
if (strpos($this->callback, '@') !== false) {
return $this->callback;
}
return 'function_' . md5($this->callback);
}
/**
* Set allowed request methods
*
* @param array $methods
* @return static $this
*/
public function setRequestMethods(array $methods)
{
$this->requestMethods = $methods;
return $this;
}
/**
* Get allowed request methods
* @return array
*/
public function getRequestMethods()
{
return $this->requestMethods;
}
/**
* @return RouterEntry
*/
public function getParent()
{
return $this->parent;
}
/**
* Set parent route
*
* @param RouterEntry $parent
* @return static $this
*/
public function setParent(RouterEntry $parent)
{
$this->parent = $parent;
return $this;
}
/**
* @param string $callback
* @return static
*/
public function setCallback($callback)
{
$this->callback = $callback;
return $this;
}
/**
* @return mixed
*/
public function getCallback()
{
return $this->callback;
}
public function getMethod()
{
if (strpos($this->callback, '@') !== false) {
$tmp = explode('@', $this->callback);
return $tmp[1];
}
return null;
}
public function getClass()
{
if (strpos($this->callback, '@') !== false) {
$tmp = explode('@', $this->callback);
return $tmp[0];
}
return null;
}
public function setMethod($method)
{
$this->callback = sprintf('%s@%s', $this->getClass(), $method);
return $this;
}
public function setClass($class)
{
$this->callback = sprintf('%s@%s', $class, $this->getMethod());
return $this;
}
/**
* @param string $middleware
* @return static
*/
public function setMiddleware($middleware)
{
$this->middlewares[] = $middleware;
return $this;
}
public function setMiddlewares(array $middlewares)
{
$this->middlewares = $middlewares;
return $this;
}
/**
* @param string $namespace
* @return static
*/
public function setNamespace($namespace)
{
$this->namespace = $namespace;
return $this;
}
/**
* @return string|array
*/
public function getMiddlewares()
{
return $this->middlewares;
}
/**
* @return string
*/
public function getNamespace()
{
return $this->namespace;
}
/**
* @return array
*/
public function getParameters()
{
return $this->parameters;
}
/**
* @param mixed $parameters
* @return static
*/
public function setParameters($parameters)
{
$this->parameters = $parameters;
return $this;
}
/**
* Add regular expression parameter match
*
* @param array $options
* @return static
*/
public function where(array $options)
{
$this->where = $options;
return $this;
}
/**
* Add regular expression match for url
*
* @param string $regex
* @return static
*/
public function match($regex)
{
$this->regex = $regex;
return $this;
}
/**
* Export route settings to array so they can be merged with another route.
*
* @return array
*/
public function toArray()
{
$values = array();
if ($this->namespace !== null) {
$values['namespace'] = $this->namespace;
}
if (count($this->middlewares) > 0) {
$values['middleware'] = $this->middlewares;
}
/*if (count($this->where) > 0) {
$values['where'] = $this->where;
}*/
if (count($this->requestMethods) > 0) {
$values['method'] = $this->requestMethods;
}
if (count($this->parameters) > 0) {
$values['parameters'] = $this->parameters;
}
return $values;
}
/**
* Merge with information from another route.
*
* @param array $values
* @return static $this
*/
public function merge(array $values)
{
if (isset($values['namespace'])) {
$this->setNamespace($values['namespace']);
}
// Push middleware if multiple
if (isset($values['middleware'])) {
$this->middlewares = array_merge((array)$values['middleware'], $this->middlewares);
}
if (isset($values['method'])) {
$this->setRequestMethods((array)$values['method']);
}
if (isset($values['where'])) {
$this->where($values['where']);
}
if (isset($values['parameters'])) {
$this->setParameters($values['parameters']);
}
return $this;
}
abstract function matchRoute(Request $request);
abstract class RouterEntry {
const REQUEST_TYPE_POST = 'post';
const REQUEST_TYPE_GET = 'get';
const REQUEST_TYPE_PUT = 'put';
const REQUEST_TYPE_PATCH = 'patch';
const REQUEST_TYPE_DELETE = 'delete';
public static $allowedRequestTypes = array(
self::REQUEST_TYPE_DELETE,
self::REQUEST_TYPE_GET,
self::REQUEST_TYPE_POST,
self::REQUEST_TYPE_PUT,
self::REQUEST_TYPE_PATCH
);
protected $settings;
protected $callback;
public function __construct() {
$this->settings = array();
$this->settings['requestMethods'] = array();
$this->settings['where'] = array();
$this->settings['parameters'] = array();
}
/**
* Returns callback name/identifier for the current route based on the callback.
* Useful if you need to get a unique identifier for the loaded route, for instance
* when using translations etc.
*
* @return string
*/
public function getIdentifier() {
if(strpos($this->callback, '@') !== false) {
return $this->callback;
}
return 'function_' . md5($this->callback);
}
/**
* @param string $callback
* @return self;
*/
public function setCallback($callback) {
$this->callback = $callback;
return $this;
}
/**
* @return mixed
*/
public function getCallback() {
return $this->callback;
}
public function getMethod() {
if(strpos($this->callback, '@') !== false) {
$tmp = explode('@', $this->callback);
return $tmp[1];
}
return null;
}
public function getClass() {
if(strpos($this->callback, '@') !== false) {
$tmp = explode('@', $this->callback);
return $tmp[0];
}
return null;
}
public function setMethod($method) {
$this->callback = sprintf('%s@%s', $this->getClass(), $method);
return $this;
}
public function setClass($class) {
$this->callback = sprintf('%s@%s', $class, $this->getMethod());
return $this;
}
/**
* @param string $prefix
* @return self
*/
public function setPrefix($prefix) {
$this->prefix = '/' . ltrim($prefix, '/');
return $this;
}
/**
* @param string $middleware
* @return self
*/
public function setMiddleware($middleware) {
$this->middleware = $middleware;
return $this;
}
/**
* @param string $namespace
* @return self
*/
public function setNamespace($namespace) {
$this->namespace = $namespace;
return $this;
}
/**
* @return string
*/
public function getPrefix() {
return $this->prefix;
}
/**
* @return string|array
*/
public function getMiddleware() {
return $this->middleware;
}
/**
* @return string
*/
public function getNamespace() {
return $this->namespace;
}
/**
* @return array
*/
public function getSettings() {
return $this->settings;
}
/**
* @return mixed
*/
public function getParameters(){
return ($this->parameters === null) ? array() : $this->parameters;
}
/**
* @param mixed $parameters
* @return self
*/
public function setParameters($parameters) {
$this->parameters = $parameters;
return $this;
}
/**
* Add regular expression parameter match
*
* @param array $options
* @return self
*/
public function where(array $options) {
$this->where = array_merge($this->where, $options);
return $this;
}
/**
* Add regular expression match for url
*
* @param string $regex
* @return self
*/
public function match($regex) {
$this->regexMatch = $regex;
return $this;
}
/**
* Get settings that are allowed to be inherited by child routes.
*
* @return array
*/
public function getMergeableSettings() {
$settings = $this->settings;
if(isset($settings['prefix'])) {
unset($settings['prefix']);
}
return $settings;
}
/**
* @param array $settings
* @return self
*/
public function addSettings(array $settings = null) {
if(is_array($settings)) {
$this->settings = array_merge($this->settings, $settings);
}
return $this;
}
/**
* @param array $settings
* @return self
*/
public function setSettings($settings) {
$this->settings = $settings;
if(isset($settings['prefix'])) {
$this->setPrefix($settings['prefix']);
}
return $this;
}
/**
* Dynamically access settings value
*
* @param $name
* @return mixed|null
*/
public function __get($name) {
return (isset($this->settings[$name]) ? $this->settings[$name] : null);
}
/**
* Dynamically set settings value
*
* @param string $name
* @param mixed|null $value
*/
public function __set($name, $value = null) {
$this->settings[$name] = $value;
}
protected function loadClass($name) {
if(!class_exists($name)) {
throw new RouterException(sprintf('Class %s does not exist', $name));
}
return new $name();
}
protected function parseParameters($route, $url, $parameterRegex = '[\w]+') {
$parameterNames = array();
$regex = '';
$lastCharacter = '';
$isParameter = false;
$parameter = '';
$routeLength = strlen($route);
for($i = 0; $i < $routeLength; $i++) {
$character = $route[$i];
if($character === '{') {
// Remove "/" and "\" from regex
if(substr($regex, strlen($regex)-1) === '/') {
$regex = substr($regex, 0, strlen($regex) - 2);
}
$isParameter = true;
} elseif($isParameter && $character === '}') {
$required = true;
// Check for optional parameter
// Use custom parameter regex if it exists
if(is_array($this->where) && isset($this->where[$parameter])) {
$parameterRegex = $this->where[$parameter];
}
if($lastCharacter === '?') {
$parameter = substr($parameter, 0, strlen($parameter)-1);
$regex .= '(?:\/?(?P<' . $parameter . '>'. $parameterRegex .')[^\/]?)?';
$required = false;
} else {
$regex .= '\/?(?P<' . $parameter . '>'. $parameterRegex .')[^\/]?';
}
$parameterNames[] = array('name' => $parameter, 'required' => $required);
$parameter = '';
$isParameter = false;
} elseif($isParameter) {
$parameter .= $character;
} elseif($character === '/') {
$regex .= '\\' . $character;
} else {
$regex .= str_replace('.', '\\.', $character);
}
$lastCharacter = $character;
}
$parameterValues = array();
if(preg_match('/^'.$regex.'\/?$/is', $url, $parameterValues)) {
$parameters = array();
$max = count($parameterNames);
if($max) {
for($i = 0; $i < $max; $i++) {
$name = $parameterNames[$i];
$parameterValue = isset($parameterValues[$name['name']]) ? $parameterValues[$name['name']] : null;
if($name['required'] && $parameterValue === null) {
throw new RouterException('Missing required parameter ' . $name['name'], 404);
}
if(!$name['required'] && $parameterValue === null) {
continue;
}
$parameters[$name['name']] = $parameterValue;
}
}
return $parameters;
}
return null;
}
public function loadMiddleware(Request $request) {
if($this->getMiddleware()) {
if(is_array($this->getMiddleware())) {
foreach($this->getMiddleware() as $middleware) {
$middleware = $this->loadClass($middleware);
if (!($middleware instanceof IMiddleware)) {
throw new RouterException($middleware . ' must be instance of Middleware');
}
/* @var $class IMiddleware */
$middleware->handle($request);
}
} else {
$middleware = $this->loadClass($this->getMiddleware());
if (!($middleware instanceof IMiddleware)) {
throw new RouterException($this->getMiddleware() . ' must be instance of Middleware');
}
/* @var $class IMiddleware */
$middleware->handle($request);
}
}
}
public function renderRoute(Request $request) {
if(is_object($this->getCallback()) && is_callable($this->getCallback())) {
// When the callback is a function
call_user_func_array($this->getCallback(), $this->getParameters());
} else {
// When the callback is a method
$controller = explode('@', $this->getCallback());
$className = $this->getNamespace() . '\\' . $controller[0];
$class = $this->loadClass($className);
$method = $controller[1];
if (!method_exists($class, $method)) {
throw new RouterException(sprintf('Method %s does not exist in class %s', $method, $className), 404);
}
$parameters = array_filter($this->getParameters(), function($var){
return !is_null($var);
});
call_user_func_array(array($class, $method), $parameters);
return $class;
}
return null;
}
/**
* Set allowed request methods
*
* @param array $methods
* @return self $this
*/
public function setRequestMethods(array $methods) {
$this->settings['requestMethods'] = $methods;
return $this;
}
/**
* Get allowed request methods
*
* @return array
*/
public function getRequestMethods() {
if(!isset($this->settings['requestMethods']) || isset($this->settings['requestMethods']) && !is_array($this->settings['requestMethods'])) {
$value = isset($this->settings['requestMethods']) ? $this->settings['requestMethods'] : null;
return array($value);
}
return $this->settings['requestMethods'];
}
public function getGroup() {
return $this->group;
}
public function setGroup($group) {
$this->group = $group;
return $this;
}
abstract function matchRoute(Request $request);
}
+94 -81
View File
@@ -1,106 +1,119 @@
<?php
namespace Pecee\SimpleRouter;
use Pecee\Exception\RouterException;
use Pecee\Http\Request;
class RouterGroup extends RouterEntry
{
protected $prefix;
protected $domains = array();
protected $exceptionHandlers = array();
class RouterGroup extends RouterEntry {
public function matchDomain(Request $request)
{
if (count($this->domains) > 0) {
foreach ($this->domains as $domain) {
public function matchDomain(Request $request) {
if($this->domain !== null) {
$parameters = $this->parseParameters($domain, $request->getHost(), '.*');
if(is_array($this->domain)) {
if ($parameters !== null) {
$this->parameters = $parameters;
return true;
}
}
$max = count($this->domain);
return false;
}
for($i = 0; $i < $max; $i++) {
$domain = $this->domain[$i];
return true;
}
$parameters = $this->parseParameters($domain, $request->getHost(), '[^.]*');
public function matchRoute(Request $request)
{
// Skip if prefix doesn't match
if ($this->prefix !== null && stripos($request->getUri(), $this->prefix) === false) {
return false;
}
if($parameters !== null) {
$this->parameters = $parameters;
return true;
}
}
return $this->matchDomain($request);
}
return false;
}
public function setExceptionHandlers(array $handlers)
{
$this->exceptionHandlers = $handlers;
return $this;
}
$parameters = $this->parseParameters($this->domain, $request->getHost(), '[^.]*');
public function getExceptionHandlers()
{
return $this->exceptionHandlers;
}
if ($parameters !== null) {
$this->parameters = $parameters;
return true;
}
public function getDomains()
{
return $this->domains;
}
return false;
}
public function setDomains(array $domains)
{
$this->domains = $domains;
return $this;
}
return true;
}
/**
* @param string $prefix
* @return static
*/
public function setPrefix($prefix)
{
$this->prefix = '/' . trim($prefix, '/');
return $this;
}
public function renderRoute(Request $request) {
// Check if request method is allowed
$hasAccess = (!$this->method);
/**
* @return string
*/
public function getPrefix()
{
return $this->prefix;
}
if($this->method) {
if(is_array($this->method)) {
$hasAccess = (in_array($request->getMethod(), $this->getRequestMethods()));
} else {
$hasAccess = strtolower($this->getRequestMethods()) == strtolower($request->getMethod());
}
}
/**
* Merge with information from another route.
*
* @param array $values
* @return static
*/
public function merge(array $values)
{
if (isset($values['prefix'])) {
$this->setPrefix($values['prefix']);
}
if(!$hasAccess) {
throw new RouterException('Method not allowed');
}
if (isset($values['exceptionHandler'])) {
$this->setExceptionHandlers((array)$values['exceptionHandler']);
}
$this->matchDomain($request);
if (isset($values['domain'])) {
$this->setDomains((array)$values['domain']);
}
return parent::renderRoute($request);
}
parent::merge($values);
public function matchRoute(Request $request) {
// Skip if prefix doesn't match
if($this->getPrefix() !== null && stripos($request->getUri(), $this->getPrefix()) === false) {
return false;
}
return $this;
}
return $this->matchDomain($request);
}
public function setExceptionHandler($class) {
$this->exceptionHandler = $class;
return $this;
}
public function getExceptionHandler() {
return $this->exceptionHandler;
}
public function getDomain() {
return $this->domain;
}
/**
* @param array $settings
* @return self
*/
public function addSettings(array $settings = null) {
if($this->getNamespace() !== null && isset($settings['namespace'])) {
unset($settings['namespace']);
}
// Push middleware if multiple
if($this->getMiddleware() !== null && isset($settings['middleware'])) {
if(!is_array($this->getMiddleware())) {
$middlewares = [
$this->getMiddleware(),
$settings['middleware']
];
} else {
$middlewares = array_push($settings['middleware'], $this->getMiddleware());
}
$settings['middleware'] = array_unique(array_reverse($middlewares));
}
if(is_array($settings)) {
$this->settings = array_merge($this->settings, $settings);
}
return $this;
}
}
+102 -89
View File
@@ -4,120 +4,133 @@ namespace Pecee\SimpleRouter;
use Pecee\Exception\RouterException;
use Pecee\Http\Request;
class RouterResource extends LoadableRoute implements IControllerRoute
{
protected $controller;
class RouterResource extends RouterEntry {
public function __construct($url, $controller)
{
$this->setUrl($url);
$this->controller = $controller;
}
protected $url;
protected $controller;
protected $postMethod;
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]);
public function __construct($url, $controller) {
parent::__construct();
$this->url = $url;
$this->controller = $controller;
$this->postMethod = strtolower(($_SERVER['REQUEST_METHOD'] != 'GET') ? 'post' : 'get');
}
if (!method_exists($class, $method)) {
throw new RouterException(sprintf('Method %s does not exist in class %s', $method, $className), 404);
}
public function renderRoute(Request $request) {
if(is_object($this->getCallback()) && is_callable($this->getCallback())) {
// When the callback is a function
call_user_func_array($this->getCallback(), $this->getParameters());
} else {
// When the callback is a method
$controller = explode('@', $this->getCallback());
$className = $this->getNamespace() . '\\' . $controller[0];
$class = $this->loadClass($className);
$method = strtolower($controller[1]);
call_user_func_array([$class, $method], $this->getParameters());
if (!method_exists($class, $method)) {
throw new RouterException(sprintf('Method %s does not exist in class %s', $method, $className), 404);
}
return $class;
}
call_user_func_array(array($class, $method), $this->getParameters());
return null;
}
return $class;
}
protected function call($method, $parameters)
{
$this->setCallback($this->controller . '@' . $method);
$this->parameters = $parameters;
return null;
}
return true;
}
protected function call($method, $parameters) {
$this->setCallback($this->controller . '@' . $method);
$this->parameters = $parameters;
return true;
}
public function matchRoute(Request $request)
{
$url = parse_url(urldecode($request->getUri()), PHP_URL_PATH);
$url = rtrim($url, '/') . '/';
public function matchRoute(Request $request) {
$url = parse_url(urldecode($request->getUri()));
$url = rtrim($url['path'], '/') . '/';
$route = rtrim($this->url, '/') . '/{id?}/{action?}';
$route = rtrim($this->url, '/') . '/{id?}/{action?}';
$parameters = $this->parseParameters($route, $url);
$parameters = $this->parseParameters($route, $url);
if ($parameters !== null) {
if($parameters !== null) {
$parameters = array_merge($this->parameters, (array)$parameters);
if(is_array($parameters)) {
$parameters = array_merge($this->parameters, $parameters);
}
$action = isset($parameters['action']) ? $parameters['action'] : null;
unset($parameters['action']);
$action = isset($parameters['action']) ? $parameters['action'] : null;
unset($parameters['action']);
$method = request()->getMethod();
// Delete
if($request->getMethod() === self::REQUEST_TYPE_DELETE && $this->postMethod === self::REQUEST_TYPE_POST) {
return $this->call('destroy', $parameters);
}
// Delete
if (isset($parameters['id']) && $method === static::REQUEST_TYPE_DELETE) {
return $this->call('destroy', $parameters);
}
// Update
if(in_array($request->getMethod(), array(self::REQUEST_TYPE_PATCH, self::REQUEST_TYPE_PUT)) && $this->postMethod === self::REQUEST_TYPE_POST) {
return $this->call('update', $parameters);
}
// Update
if (isset($parameters['id']) && in_array($method, [static::REQUEST_TYPE_PATCH, static::REQUEST_TYPE_PUT])) {
return $this->call('update', $parameters);
}
// Edit
if(isset($action) && strtolower($action) === 'edit' && $this->postMethod === self::REQUEST_TYPE_GET) {
return $this->call('edit', $parameters);
}
// Edit
if (isset($parameters['id']) && strtolower($action) === 'edit' && $method === static::REQUEST_TYPE_GET) {
return $this->call('edit', $parameters);
}
// Create
if(strtolower($action) === 'create' && $request->getMethod() === self::REQUEST_TYPE_GET) {
return $this->call('create', $parameters);
}
// Create
if (strtolower($action) === 'create' && $method === static::REQUEST_TYPE_GET) {
return $this->call('create', $parameters);
}
// Save
if($this->postMethod === self::REQUEST_TYPE_POST) {
return $this->call('store', $parameters);
}
// Save
if ($method === static::REQUEST_TYPE_POST) {
return $this->call('store', $parameters);
}
// Show
if(isset($parameters['id']) && $this->postMethod === self::REQUEST_TYPE_GET) {
return $this->call('show', $parameters);
}
// Show
if (isset($parameters['id']) && $method === static::REQUEST_TYPE_GET) {
return $this->call('show', $parameters);
}
// Index
return $this->call('index', $parameters);
}
// Index
return $this->call('index', $parameters);
}
return null;
}
return null;
}
/**
* @return string
*/
public function getUrl() {
return $this->url;
}
/**
* @return string
*/
public function getController()
{
return $this->controller;
}
/**
* @param string $url
* @return static
*/
public function setUrl($url) {
$url = rtrim($url, '/') . '/';
$this->url = $url;
return $this;
}
/**
* @param string $controller
* @return static
*/
public function setController($controller)
{
$this->controller = $controller;
/**
* @return string
*/
public function getController() {
return $this->controller;
}
return $this;
}
/**
* @param string $controller
* @return static
*/
public function setController($controller) {
$this->controller = $controller;
return $this;
}
}
+119 -29
View File
@@ -1,42 +1,132 @@
<?php
namespace Pecee\SimpleRouter;
use Pecee\Http\Request;
class RouterRoute extends LoadableRoute
{
public function __construct($url, $callback)
{
$this->setUrl($url);
$this->setCallback($callback);
}
class RouterRoute extends RouterEntry {
public function matchRoute(Request $request)
{
$url = parse_url(urldecode($request->getUri()), PHP_URL_PATH);
$url = rtrim($url, '/') . '/';
const PARAMETERS_REGEX_MATCH = '{([A-Za-z\-\_]*?)\?{0,1}}';
// Match on custom defined regular expression
if ($this->regex !== null) {
$parameters = array();
if (preg_match('/(' . $this->regex . ')/is', $request->getHost() . $url, $parameters)) {
$this->parameters = (array)$parameters[0];
return true;
}
return null;
}
protected $url;
// Make regular expression based on route
$route = rtrim($this->url, '/') . '/';
public function __construct($url, $callback) {
parent::__construct();
$this->setUrl($url);
$this->setCallback($callback);
}
$parameters = $this->parseParameters($route, $url);
public function matchRoute(Request $request) {
if ($parameters !== null) {
$this->parameters = array_merge($this->parameters, $parameters);
return true;
}
$url = parse_url(urldecode($request->getUri()));
$url = rtrim($url['path'], '/') . '/';
return null;
}
// Match on custom defined regular expression
if($this->regexMatch) {
$parameters = array();
if(preg_match('/('.$this->regexMatch.')/is', $request->getHost() . $url, $parameters)) {
$this->parameters = (!is_array($parameters[0]) ? array($parameters[0]) : $parameters[0]);
return true;
}
return null;
}
// Make regular expression based on route
$route = rtrim($this->url, '/') . '/';
$parameters = $this->parseParameters($route, $url);
if($parameters !== null) {
if(is_array($this->parameters)) {
$this->parameters = array_merge($this->parameters, $parameters);
} else {
$this->parameters = $parameters;
}
return true;
}
return null;
}
/**
* @return string
*/
public function getUrl() {
return $this->url;
}
/**
* @param string $url
* @return self
*/
public function setUrl($url) {
$parameters = array();
$matches = array();
if(preg_match_all('/'.self::PARAMETERS_REGEX_MATCH.'/is', $url, $matches)) {
$parameters = $matches[1];
}
if(count($parameters)) {
$tmp = array();
foreach($parameters as $param) {
$tmp[$param] = null;
}
$this->parameters = $tmp;
}
$this->url = $url;
return $this;
}
/**
* Get alias for the url which can be used when getting the url route.
* @return string|array
*/
public function getAlias(){
return $this->alias;
}
/**
* Check if route has given alias.
*
* @param $name
* @return bool
*/
public function hasAlias($name) {
if(is_array($this->alias)) {
foreach($this->alias as $alias) {
if(strtolower($alias) === strtolower($name)) {
return true;
}
}
} else {
return strtolower($this->getAlias()) === strtolower($name);
}
return false;
}
/**
* Set the url alias for easier getting the url route.
* @param string|array $alias
* @return self
*/
public function setAlias($alias){
$this->alias = $alias;
return $this;
}
public function setSettings($settings) {
// Change as to alias
if(isset($settings{'as'})) {
$this->setAlias($settings['as']);
}
return parent::setSettings($settings);
}
}
+89 -304
View File
@@ -1,346 +1,131 @@
<?php
/**
* ---------------------------
* Router helper class
* ---------------------------
* This class is added so calls can be made statically like Router::get() making the code look more pretty.
*/
namespace Pecee\SimpleRouter;
use Pecee\Exception\RouterException;
use Pecee\Http\Middleware\BaseCsrfVerifier;
class SimpleRouter
{
protected static $defaultNamespace;
class SimpleRouter {
/**
* Start/route request
*
* @throws \Pecee\Exception\RouterException
*/
public static function start()
{
static::router()->routeRequest();
}
/**
* Start/route request
* @param null $defaultNamespace
* @throws \Pecee\Exception\RouterException
*/
public static function start($defaultNamespace = null) {
RouterBase::getInstance()->setDefaultNamespace($defaultNamespace)->routeRequest();
}
/**
* Set default namespace which will be prepended to all routes.
*
* @param string $defaultNamespace
*/
public static function setDefaultNamespace($defaultNamespace)
{
static::$defaultNamespace = $defaultNamespace;
}
/**
* Set base csrf verifier
* @param BaseCsrfVerifier $baseCsrfVerifier
*/
public static function csrfVerifier(BaseCsrfVerifier $baseCsrfVerifier) {
RouterBase::getInstance()->setBaseCsrfVerifier($baseCsrfVerifier);
}
/**
* Base CSRF verifier
*
* @param BaseCsrfVerifier $baseCsrfVerifier
*/
public static function csrfVerifier(BaseCsrfVerifier $baseCsrfVerifier)
{
static::router()->setCsrfVerifier($baseCsrfVerifier);
}
public static function addBootManager(RouterBootManager $bootManager) {
RouterBase::getInstance()->addBootManager($bootManager);
}
/**
* Boot managers allows you to alter the routes before the routing occurs.
* Perfect if you want to load pretty-urls from a file or database.
*
* @param RouterBootManager $bootManager
*/
public static function addBootManager(RouterBootManager $bootManager)
{
static::router()->addBootManager($bootManager);
}
public static function get($url, $callback, array $settings = null) {
return self::match(['get'], $url, $callback, $settings);
}
/**
* Route the given url to your callback on GET request method.
*
* @param string $url
* @param string|\Closure $callback
* @param array|null $settings
* @return RouterRoute
*/
public static function get($url, $callback, array $settings = null)
{
return static::match(['get'], $url, $callback, $settings);
}
public static function post($url, $callback, array $settings = null) {
return self::match(['post'], $url, $callback, $settings);
}
/**
* Route the given url to your callback on POST request method.
*
* @param string $url
* @param string|\Closure $callback
* @param array|null $settings
* @return RouterRoute
*/
public static function post($url, $callback, array $settings = null)
{
return static::match(['post'], $url, $callback, $settings);
}
public static function put($url, $callback, array $settings = null) {
return self::match(['put'], $url, $callback, $settings);
}
/**
* Route the given url to your callback on PUT request method.
*
* @param string $url
* @param string|\Closure $callback
* @param array|null $settings
* @return RouterRoute
*/
public static function put($url, $callback, array $settings = null)
{
return static::match(['put'], $url, $callback, $settings);
}
public static function delete($url, $callback, array $settings = null) {
return self::match(['delete'], $url, $callback, $settings);
}
/**
* Route the given url to your callback on PATCH request method.
*
* @param string $url
* @param string|\Closure $callback
* @param array|null $settings
* @return RouterRoute
*/
public static function patch($url, $callback, array $settings = null)
{
return static::match(['patch'], $url, $callback, $settings);
}
public static function group($settings = array(), $callback) {
$group = new RouterGroup();
$group->setCallback($callback);
/**
* Route the given url to your callback on OPTIONS request method.
*
* @param string $url
* @param string|\Closure $callback
* @param array|null $settings
* @return RouterRoute
*/
public static function options($url, $callback, array $settings = null)
{
return static::match(['options'], $url, $callback, $settings);
}
if($settings !== null && is_array($settings)) {
$group->setSettings($settings);
}
/**
* Route the given url to your callback on DELETE request method.
*
* @param string $url
* @param string|\Closure $callback
* @param array|null $settings
* @return RouterRoute
*/
public static function delete($url, $callback, array $settings = null)
{
return static::match(['delete'], $url, $callback, $settings);
}
RouterBase::getInstance()->addRoute($group);
/**
* Groups allows for encapsulating routes with special settings.
*
* @param array $settings
* @param \Closure $callback
* @throws RouterException
* @return RouterGroup
*/
public static function group($settings = array(), \Closure $callback)
{
$group = new RouterGroup();
$group->setCallback($callback);
return $group;
}
if ($settings !== null && is_array($settings) === true) {
$group->merge($settings);
}
/**
* Adds get + post route
*
* @param string $url
* @param callable $callback
* @param array|null $settings
* @return RouterRoute
*/
public static function basic($url, $callback, array $settings = null) {
return self::match(['get', 'post'], $url, $callback, $settings);
}
if (is_callable($callback) === false) {
throw new RouterException('Invalid callback provided. Only functions or methods supported');
}
public static function match(array $requestMethods, $url, $callback, array $settings = null) {
$route = new RouterRoute($url, $callback);
$route->setRequestMethods($requestMethods);
static::router()->addRoute($group);
if($settings !== null) {
$route->addSettings($settings);
}
return $group;
}
RouterBase::getInstance()->addRoute($route);
/**
* Alias for the form method
*
* @param string $url
* @param callable $callback
* @param array|null $settings
* @see SimpleRouter::form
* @return RouterRoute
*/
public static function basic($url, $callback, array $settings = null)
{
return static::match(['get', 'post'], $url, $callback, $settings);
}
return $route;
}
/**
* This type will route the given url to your callback on the provided request methods.
* Route the given url to your callback on POST and GET request method.
*
* @param string $url
* @param string|\Closure $callback
* @param array|null $settings
* @see SimpleRouter::form
* @return RouterRoute
*/
public static function form($url, $callback, array $settings = null)
{
return static::match(['get', 'post'], $url, $callback, $settings);
}
public static function all($url, $callback, array $settings = null) {
$route = new RouterRoute($url, $callback);
/**
* This type will route the given url to your callback on the provided request methods.
*
* @param array $requestMethods
* @param string $url
* @param string|\Closure $callback
* @param array|null $settings
* @return RouterEntry|RouterRoute
*/
public static function match(array $requestMethods, $url, $callback, array $settings = null)
{
$route = new RouterRoute($url, $callback);
$route->setRequestMethods($requestMethods);
$route = static::addDefaultNamespace($route);
if($settings !== null) {
$route->addSettings($settings);
}
if ($settings !== null) {
$route->merge($settings);
}
RouterBase::getInstance()->addRoute($route);
static::router()->addRoute($route);
return $route;
}
return $route;
}
public static function controller($url, $controller, array $settings = null) {
$route = new RouterController($url, $controller);
/**
* This type will route the given url to your callback and allow any type of request method
*
* @param string $url
* @param string|\Closure $callback
* @param array|null $settings
* @return RouterRoute
*/
public static function all($url, $callback, array $settings = null)
{
$route = new RouterRoute($url, $callback);
if($settings !== null) {
$route->addSettings($settings);
}
$route = static::addDefaultNamespace($route);
RouterBase::getInstance()->addRoute($route);
if ($settings !== null) {
$route->merge($settings);
}
return $route;
}
static::router()->addRoute($route);
public static function resource($url, $controller, array $settings = null) {
$route = new RouterResource($url, $controller);
return $route;
}
if($settings !== null) {
$route->addSettings($settings);
}
/**
* This route will route request from the given url to the controller.
*
* @param string $url
* @param string $controller
* @param array|null $settings
* @return RouterController
*/
public static function controller($url, $controller, array $settings = null)
{
$route = new RouterController($url, $controller);
RouterBase::getInstance()->addRoute($route);
$route = static::addDefaultNamespace($route);
return $route;
}
if ($settings !== null) {
$route->merge($settings);
}
static::router()->addRoute($route);
return $route;
}
/**
* This type will route all REST-supported requests to different methods in the provided controller.
*
* @param string $url
* @param string $controller
* @param array|null $settings
* @return RouterResource
*/
public static function resource($url, $controller, array $settings = null)
{
$route = new RouterResource($url, $controller);
if ($settings !== null) {
$route->merge($settings);
}
static::router()->addRoute($route);
return $route;
}
/**
* Get url by controller or alias.
*
* @param string $controller
* @param array|null $parameters
* @param array|null $getParams
* @return string
*/
public static function getRoute($controller = null, $parameters = null, $getParams = null)
{
return static::router()->getRoute($controller, $parameters, $getParams);
}
/**
* Get the request
*
* @return \Pecee\Http\Request
*/
public static function request()
{
return static::router()->getRequest();
}
/**
* Get the response object
*
* @return \Pecee\Http\Response
*/
public static function response()
{
return static::router()->getResponse();
}
/**
* Returns the router instance
*
* @return RouterBase
*/
public static function router()
{
return RouterBase::getInstance();
}
/**
* Prepends the default namespace to all new routes added.
*
* @param RouterEntry $route
* @return RouterEntry
*/
protected static function addDefaultNamespace(RouterEntry $route)
{
if (static::$defaultNamespace !== null) {
$namespace = static::$defaultNamespace;
if ($route->getNamespace() !== null) {
$namespace .= '\\' . $route->getNamespace();
}
$route->setNamespace($namespace);
}
return $route;
}
public static function getRoute($controller = null, $parameters = null, $getParams = null) {
return RouterBase::getInstance()->getRoute($controller, $parameters, $getParams);
}
}
+11 -18
View File
@@ -1,25 +1,18 @@
<?php
class DummyController
{
public function start()
{
echo static::class . '@' . 'start() OK';
}
class DummyController {
public function param($params = null)
{
$params = func_get_args();
echo 'Params: ' . join(', ', $params);
}
public function start() {
echo static::class . '@' .'start() OK';
}
public function notFound()
{
echo 'not found';
}
public function param($params = null) {
$params = func_get_args();
echo 'Params: ' . join(', ', $params);
}
public function silent() {
}
public function notFound() {
echo 'not found';
}
}
+6 -6
View File
@@ -1,14 +1,14 @@
<?php
require_once 'Exceptions/MiddlewareLoadedException.php';
use Pecee\Http\Middleware\IMiddleware;
use Pecee\Http\Request;
class DummyMiddleware implements IMiddleware
{
public function handle(Request $request, \Pecee\SimpleRouter\RouterEntry &$route = null)
{
throw new MiddlewareLoadedException('Middleware loaded!');
}
class DummyMiddleware implements IMiddleware {
public function handle(Request $request) {
throw new MiddlewareLoadedException('Middleware loaded!');
}
}
@@ -1,4 +1,2 @@
<?php
class MiddlewareLoadedException extends \Exception
{
}
class MiddlewareLoadedException extends \Exception {}
+4 -6
View File
@@ -1,10 +1,8 @@
<?php
class ExceptionHandler implements \Pecee\Handler\IExceptionHandler {
class ExceptionHandler implements \Pecee\Handler\IExceptionHandler
{
public function handleError(\Pecee\Http\Request $request, \Pecee\SimpleRouter\RouterEntry &$route = null, \Exception $error)
{
throw $error;
}
public function handleError(\Pecee\Http\Request $request, \Pecee\SimpleRouter\RouterEntry $router = null, \Exception $error){
throw $error;
}
}
+24 -73
View File
@@ -3,90 +3,41 @@
require_once 'Dummy/DummyMiddleware.php';
require_once 'Dummy/DummyController.php';
use Pecee\SimpleRouter\SimpleRouter as SimpleRouter;
class GroupTest extends PHPUnit_Framework_TestCase {
class GroupTest extends PHPUnit_Framework_TestCase
{
protected $result;
protected $result;
public function testGroupLoad()
{
$this->result = false;
protected function group() {
$this->result = true;
}
SimpleRouter::group(['prefix' => '/group'], function () {
$this->result = true;
});
public function testGroup() {
try {
SimpleRouter::start();
} catch (Exception $e) {
// ignore RouteNotFound exception
}
$this->result = false;
$this->assertTrue($this->result);
}
\Pecee\SimpleRouter\SimpleRouter::group(['prefix' => '/group'], $this->group());
public function testNestedGroup()
{
try {
\Pecee\SimpleRouter\SimpleRouter::start();
} catch(Exception $e) {
SimpleRouter::router()->reset();
SimpleRouter::request()->setUri('/api/v1/test');
SimpleRouter::request()->setMethod('get');
}
SimpleRouter::group(['prefix' => '/api'], function () {
$this->assertTrue($this->result);
}
SimpleRouter::group(['prefix' => '/v1'], function () {
SimpleRouter::get('/test', 'DummyController@start');
});
public function testNestedGroup() {
});
\Pecee\SimpleRouter\RouterBase::getInstance()->getRequest()->setUri('/api/v1/test');
\Pecee\SimpleRouter\RouterBase::getInstance()->getRequest()->setMethod('get');
SimpleRouter::start();
}
\Pecee\SimpleRouter\SimpleRouter::group(['prefix' => '/api'], function() {
\Pecee\SimpleRouter\SimpleRouter::group(['prefix' => '/v1'], function() {
\Pecee\SimpleRouter\SimpleRouter::get('/test', 'DummyController@start');
});
});
public function testManyRoutes()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setUri('/my/match');
SimpleRouter::request()->setMethod('get');
SimpleRouter::group(['prefix' => '/api'], function () {
SimpleRouter::group(['prefix' => '/v1'], function () {
SimpleRouter::get('/test', 'DummyController@start');
});
});
SimpleRouter::get('/my/match', 'DummyController@start');
SimpleRouter::group(['prefix' => '/service'], function () {
SimpleRouter::group(['prefix' => '/v1'], function () {
SimpleRouter::get('/no-match', 'DummyController@start');
});
});
SimpleRouter::start();
}
public function testUrls()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setUri('/my/fancy/url/1');
SimpleRouter::request()->setMethod('get');
SimpleRouter::get('/my/fancy/url/1', 'DummyController@start', ['as' => 'fancy1']);
SimpleRouter::get('/my/fancy/url/2', 'DummyController@start')->setAlias('fancy2');
SimpleRouter::start();
$this->assertTrue((SimpleRouter::getRoute('fancy1') === '/my/fancy/url/1/'));
$this->assertTrue((SimpleRouter::getRoute('fancy2') === '/my/fancy/url/2/'));
}
\Pecee\SimpleRouter\SimpleRouter::start();
}
}
+17 -19
View File
@@ -4,29 +4,27 @@ require_once 'Dummy/DummyMiddleware.php';
require_once 'Dummy/DummyController.php';
require_once 'Dummy/Handler/ExceptionHandler.php';
use Pecee\SimpleRouter\SimpleRouter as SimpleRouter;
class MiddlewareTest extends PHPUnit_Framework_TestCase {
class MiddlewareTest extends PHPUnit_Framework_TestCase
{
public function testMiddlewareFound()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setMethod('get');
SimpleRouter::request()->setUri('/my/test/url');
public function testMiddlewareFound() {
SimpleRouter::group(['exceptionHandler' => 'ExceptionHandler'], function () {
SimpleRouter::get('/my/test/url', 'DummyController@start', ['middleware' => 'DummyMiddleware']);
});
\Pecee\Http\Request::getInstance()->setMethod('get');
\Pecee\Http\Request::getInstance()->setUri('/my/test/url');
$found = false;
\Pecee\SimpleRouter\SimpleRouter::group(['exceptionHandler' => 'ExceptionHandler'], function() {
\Pecee\SimpleRouter\SimpleRouter::get('/my/test/url', 'DummyController@start', ['middleware' => 'DummyMiddleware']);
});
try {
SimpleRouter::start();
} catch (\Exception $e) {
$found = ($e instanceof MiddlewareLoadedException);
}
$found = false;
$this->assertTrue($found);
}
try {
\Pecee\SimpleRouter\SimpleRouter::start();
}catch(\Exception $e) {
$found = ($e instanceof MiddlewareLoadedException);
}
$this->assertTrue($found);
}
}
+72 -103
View File
@@ -4,137 +4,106 @@ require_once 'Dummy/DummyMiddleware.php';
require_once 'Dummy/DummyController.php';
require_once 'Dummy/Handler/ExceptionHandler.php';
use Pecee\SimpleRouter\SimpleRouter as SimpleRouter;
class RouterRouteTest extends PHPUnit_Framework_TestCase {
class RouterRouteTest extends PHPUnit_Framework_TestCase
{
protected $result = false;
public function testNotFound() {
public function testNotFound()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setMethod('get');
SimpleRouter::request()->setUri('/test-param1-param2');
\Pecee\Http\Request::getInstance()->setMethod('get');
\Pecee\Http\Request::getInstance()->setUri('/test-param1-param2');
SimpleRouter::group(['exceptionHandler' => 'ExceptionHandler'], function () {
SimpleRouter::get('/non-existing-path', 'DummyController@start');
});
\Pecee\SimpleRouter\SimpleRouter::group(['exceptionHandler' => 'ExceptionHandler'], function() {
\Pecee\SimpleRouter\SimpleRouter::get('/non-existing-path', 'DummyController@start');
});
$found = false;
$found = false;
try {
SimpleRouter::start();
} catch (\Exception $e) {
$found = ($e instanceof \Pecee\Exception\RouterException && $e->getCode() == 404);
}
try {
\Pecee\SimpleRouter\SimpleRouter::start();
}catch(\Exception $e) {
$found = ($e instanceof \Pecee\Exception\RouterException && $e->getCode() == 404);
}
$this->assertTrue($found);
}
$this->assertTrue($found);
public function testGet()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setUri('/my/test/url');
SimpleRouter::request()->setMethod('get');
}
SimpleRouter::get('/my/test/url', 'DummyController@start');
SimpleRouter::start();
}
public function testGet() {
public function testPost()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setUri('/my/test/url');
SimpleRouter::request()->setMethod('post');
\Pecee\SimpleRouter\RouterBase::getInstance()->getRequest()->setUri('/my/test/url');
\Pecee\SimpleRouter\RouterBase::getInstance()->getRequest()->setMethod('get');
SimpleRouter::post('/my/test/url', 'DummyController@start');
SimpleRouter::start();
}
\Pecee\SimpleRouter\SimpleRouter::get('/my/test/url', 'DummyController@start');
\Pecee\SimpleRouter\SimpleRouter::start();
}
public function testPut()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setUri('/my/test/url');
SimpleRouter::request()->setMethod('put');
public function testPost() {
\Pecee\SimpleRouter\RouterBase::getInstance()->getRequest()->setUri('/my/test/url');
\Pecee\Http\Request::getInstance()->setMethod('post');
SimpleRouter::put('/my/test/url', 'DummyController@start');
SimpleRouter::start();
}
\Pecee\SimpleRouter\SimpleRouter::post('/my/test/url', 'DummyController@start');
\Pecee\SimpleRouter\SimpleRouter::start();
}
public function testDelete()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setUri('/my/test/url');
SimpleRouter::request()->setMethod('delete');
public function testPut() {
\Pecee\SimpleRouter\RouterBase::getInstance()->getRequest()->setUri('/my/test/url');
\Pecee\Http\Request::getInstance()->setMethod('put');
SimpleRouter::delete('/my/test/url', 'DummyController@start');
SimpleRouter::start();
}
\Pecee\SimpleRouter\SimpleRouter::put('/my/test/url', 'DummyController@start');
\Pecee\SimpleRouter\SimpleRouter::start();
}
public function testMethodNotAllowed()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setUri('/my/test/url');
SimpleRouter::request()->setMethod('post');
public function testDelete() {
\Pecee\SimpleRouter\RouterBase::getInstance()->getRequest()->setUri('/my/test/url');
\Pecee\Http\Request::getInstance()->setMethod('delete');
SimpleRouter::get('/my/test/url', 'DummyController@start');
\Pecee\SimpleRouter\SimpleRouter::delete('/my/test/url', 'DummyController@start');
\Pecee\SimpleRouter\SimpleRouter::start();
try {
SimpleRouter::start();
} catch (\Exception $e) {
$this->assertEquals(403, $e->getCode());
}
}
}
public function testSimpleParam()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setMethod('get');
SimpleRouter::request()->setUri('/test-param1');
public function testMethodNotAllowed() {
\Pecee\SimpleRouter\RouterBase::getInstance()->getRequest()->setUri('/my/test/url');
\Pecee\Http\Request::getInstance()->setMethod('post');
SimpleRouter::get('/test-{param1}', 'DummyController@param');
SimpleRouter::start();
}
\Pecee\SimpleRouter\SimpleRouter::get('/my/test/url', 'DummyController@start');
public function testMultiParam()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setMethod('get');
SimpleRouter::request()->setUri('/test-param1-param2');
try {
\Pecee\SimpleRouter\SimpleRouter::start();
} catch(\Exception $e) {
$this->assertEquals(403, $e->getCode());
}
SimpleRouter::get('/test-{param1}-{param2}', 'DummyController@param');
SimpleRouter::start();
}
}
public function testPathParamRegex()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setMethod('get');
SimpleRouter::request()->setUri('/test/path/123123');
public function testSimpleParam() {
SimpleRouter::get('/test/path/{myParam}', 'DummyController@param', ['where' => ['myParam' => '([0-9]+)']]);
SimpleRouter::start();
}
\Pecee\Http\Request::getInstance()->setMethod('get');
\Pecee\Http\Request::getInstance()->setUri('/test-param1');
public function testDomainRoute()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setMethod('get');
SimpleRouter::request()->setUri('/test');
SimpleRouter::request()->setHost('hello.world.com');
\Pecee\SimpleRouter\SimpleRouter::get('/test-{param1}', 'DummyController@param');
\Pecee\SimpleRouter\SimpleRouter::start();
$this->result = false;
}
SimpleRouter::group(['domain' => '{subdomain}.world.com'], function () {
SimpleRouter::get('test', function ($subdomain = null) {
$this->result = ($subdomain === 'hello');
});
});
public function testMultiParam() {
SimpleRouter::start();
\Pecee\Http\Request::getInstance()->setMethod('get');
\Pecee\Http\Request::getInstance()->setUri('/test-param1-param2');
$this->assertTrue($this->result);
\Pecee\SimpleRouter\SimpleRouter::get('/test-{param1}-{param2}', 'DummyController@param');
\Pecee\SimpleRouter\SimpleRouter::start();
}
}
public function testPathParam() {
\Pecee\Http\Request::getInstance()->setMethod('get');
\Pecee\Http\Request::getInstance()->setUri('/test/path/param1');
\Pecee\SimpleRouter\SimpleRouter::get('/test/path/{param}', 'DummyController@param');
\Pecee\SimpleRouter\SimpleRouter::start();
}
}
-83
View File
@@ -1,83 +0,0 @@
<?php
require_once 'Dummy/DummyMiddleware.php';
require_once 'Dummy/DummyController.php';
require_once 'Dummy/Handler/ExceptionHandler.php';
use Pecee\SimpleRouter\SimpleRouter as SimpleRouter;
class RouterUrlTest extends PHPUnit_Framework_TestCase
{
protected $result = false;
protected function getUrl($controller = null, $parameters = null, $getParams = null) {
return SimpleRouter::getRoute($controller, $parameters, $getParams);
}
public function testUrls()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setMethod('get');
SimpleRouter::request()->setUri('/');
// Match normal route on alias
SimpleRouter::get('/', 'DummyController@silent', ['as' => 'home']);
SimpleRouter::group(['prefix' => '/admin'], function() {
// Match route with prefix on alias
SimpleRouter::get('/{id?}', 'DummyController@start', ['as' => 'admin.home']);
// Match controller with prefix and alias
SimpleRouter::controller('/users', 'DummyController', ['as' => 'admin.users']);
// Match controller with prefix and NO alias
SimpleRouter::controller('/pages', 'DummyController');
});
// Match controller with no prefix and no alias
SimpleRouter::controller('/cats', 'CatsController');
// Pretend to load page
SimpleRouter::start();
// Should match /
$this->assertEquals($this->getUrl('home'), '/');
// Should match /admin/
$this->assertEquals($this->getUrl('DummyController@start'), '/admin/');
// Should match /admin/
$this->assertEquals($this->getUrl('admin.home'), '/admin/');
// Should match /admin/2/
$this->assertEquals($this->getUrl('admin.home', ['id' => 2]), '/admin/2/');
// Should match /admin/users/
$this->assertEquals($this->getUrl('admin.users'), '/admin/users/');
// Should match /admin/users/home/
$this->assertEquals($this->getUrl('admin.users@home'), '/admin/users/home/');
// Should match /cats/
$this->assertEquals($this->getUrl('CatsController'), '/cats/');
// Should match /cats/view/
$this->assertEquals($this->getUrl('CatsController', 'view'), '/cats/view/');
// Should match /cats/view/
$this->assertEquals($this->getUrl('CatsController', ['view']), '/cats/view/');
// Should match /cats/view/666
$this->assertEquals($this->getUrl('CatsController@view', ['666']), '/cats/view/666/');
// Should match /funny/man/
$this->assertEquals($this->getUrl('/funny/man'), '/funny/man/');
// Should match /?jackdaniels=true
$this->assertEquals($this->getUrl('home', null, ['jackdaniels' => 'true']), '/?jackdaniels=true');
}
}