Compare commits

...

81 Commits

Author SHA1 Message Date
Simon Sessingø 75029b330a Merge pull request #78 from skipperbent/development
[BUGFIX] Fixed nested groups not merging settings to routes
2016-04-07 19:34:02 +02:00
Simon Sessingø 27371dfa11 [FEATURE] Added support for multiple aliases 2016-04-07 19:33:04 +02:00
Simon Sessingø 6e102711a8 [BUGFIX] Fixed nested groups not merging settings to routes 2016-04-07 01:38:03 +02:00
Simon Sessingø 4d58fbf7b5 [TASK] Added rewrite_uri parameter to Request class 2016-03-19 19:14:07 +01:00
Simon Sessingø fd5d893040 Merge pull request #77 from skipperbent/development
[TASK] Added rewrite_uri parameter to Request class
2016-03-19 19:12:42 +01:00
Simon Sessingø 35bb58818e [BUGFIX] Readded rendering of groups 2016-03-19 19:00:23 +01:00
Simon Sessingø c1512740af Merge pull request #76 from skipperbent/development
[BUGFIX] Readded rendering of groups
2016-03-19 18:59:01 +01:00
Simon Sessingø 212ae133de Merge pull request #75 from skipperbent/development
[TASK] Readded merging
2016-03-19 17:40:26 +01:00
Simon Sessingø 438d3c258b [TASK] Readded merging 2016-03-19 17:11:54 +01:00
Simon Sessingø ae58231fa1 Merge pull request #74 from skipperbent/development
Custom boot-managers
2016-03-19 16:29:03 +01:00
Simon Sessingø a58ec1544d Merge branch 'development' of https://github.com/skipperbent/simple-php-router into development 2016-03-19 16:27:54 +01:00
Simon Sessingø 1bafbab56b [TASK] Moved RouterException. Readded lost stuff from Request. 2016-03-19 16:27:25 +01:00
Simon Sessingø 6395801a06 [OPTIMISATIONS] Documentation 2016-03-19 16:19:04 +01:00
Simon Sessingø 060479f1fe Update README.md 2016-03-19 16:18:33 +01:00
Simon Sessingø b29f2ce37d [FEATURE] Added custom boot managers 2016-03-19 16:14:36 +01:00
Simon Sessingø 358b25d4f1 Merge pull request #73 from skipperbent/development
Development
2016-03-18 17:55:31 +01:00
Simon Sessingø 5483ffe458 [TASK] Readded group match inheritence .
- Settings on routes now have higher priority when merging settings from
  group.
2016-03-18 17:43:57 +01:00
Simon Sessingø 6d8e95fcaa [TASK] Match on group are no longer merged to child routes. 2016-03-18 15:21:45 +01:00
Simon Sessingø 3d45851d9b Merge pull request #72 from skipperbent/development
[BUGFIX] Only render group if prefix matches.
2016-03-16 19:58:03 +01:00
Simon Sessingø db78135d19 [BUGFIX] Only render group if prefix matches. 2016-03-16 19:57:54 +01:00
Simon Sessingø c4fcf750d4 Added support for x-forwarded-proto http header 2016-03-14 01:09:35 +01:00
Simon Sessingø ee5c2207f8 Merge pull request #71 from skipperbent/development
Added support for x-forwarded-proto http header
2016-03-14 01:08:25 +01:00
Simon Sessingø a92b6008fa [TASK] Added http code to redirect method. 2016-03-14 01:00:19 +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ø 115c8e510a [BUGFIX] Added urldecode to RouterRessource, RouterRoute and
RouterController class to ensure that urls with special letters can be
picked up by a custom regular expression (read documentation: match).
2016-03-01 22:50:26 +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ø 35ee79d02c [TASK] Fixed regex causing optional parameters to sometimes catch required
parameters value.
2016-01-23 16:10:44 +01:00
Simon Sessingø 315fe05769 Merge pull request #67 from skipperbent/development
Development
2016-01-17 04:57:39 +01:00
Simon Sessingø 6306b604e8 Merge branch 'development' of https://github.com/skipperbent/simple-php-router into development 2016-01-17 04:56:56 +01:00
Simon Sessingø 4dd0739df9 [BUGFIX] Fixed minor errors with optional parameter when using getRoute(). 2016-01-17 04:56:29 +01:00
Simon Sessingø a57113309a Merge pull request #66 from skipperbent/development
Development
2016-01-15 11:56:04 +01:00
Simon Sessingø 0bd234d996 Merge pull request #65 from skipperbent/master
Latest development
2016-01-15 11:55:47 +01:00
Simon Sessingø 27d24758b1 [OPTIMISATION] Ensured action parameter was set before using it. 2016-01-15 11:55:01 +01:00
Simon Sessingø 4d08f08c68 Merge pull request #64 from skipperbent/development
Development
2016-01-15 11:09:00 +01:00
Simon Sessingø aeacda1812 [FEATURE] Added option to change route and get information about current
route through the Request object.

- Updated documentation to relfect new changes.
2016-01-15 11:06:13 +01:00
Simon Sessingø 765204f552 [FEATURE] Moved loadedRoute to request so it can be easily overwritten
from middleware.
2016-01-15 10:34:59 +01:00
Simon Sessingø 4267cb8751 Merge pull request #63 from skipperbent/development
[OPTIMISATION] Parameters matching optimisations.
2016-01-15 10:22:14 +01:00
Simon Sessingø 714edf7902 [OPTIMISATION] Parameters matching optimisations. 2016-01-15 10:20:50 +01:00
Simon Sessingø c3b12ba053 Merge pull request #62 from skipperbent/development
Development
2016-01-15 10:07:51 +01:00
Simon Sessingø b096742d6b [BUGFIX] Enchanched regular expression for optinal parameters to
completely ignore path seperators (/).
2016-01-15 10:06:13 +01:00
Simon Sessingø 3298970798 Merge pull request #61 from skipperbent/development
[BUGFIX] Fixed some ressources not working after latest update.
2016-01-15 09:39:28 +01:00
Simon Sessingø bb6f56ef8c [BUGFIX] Fixed some ressources not working after latest update.
- Optimisations + cleanup.
2016-01-15 09:37:23 +01:00
Simon Sessingø 6c675124fa Merge pull request #60 from skipperbent/development
[BUGFIX] Minor bugfixes and optimisations
2016-01-14 16:41:37 +01:00
Simon Sessingø 14a030294e [BUGFIX] Minor bugfixes and optimisations
- Fixed getRoute sometimes not passing current loaded route.
- Fixed optional parameters in some occasions not working properly.
2016-01-14 16:37:36 +01:00
Simon Sessingø 8bc8124366 Merge pull request #59 from skipperbent/development
[TASK] Simplified regex for matching parameters.
2016-01-05 05:50:35 +01:00
Simon Sessingø 1332ef7139 [TASK] Optimised regex for matching parameters. 2016-01-05 04:52:12 +01:00
Simon Sessingø 12bbc98a82 Merge pull request #58 from skipperbent/development
[BUGFIX] Custom parameters regex is now working again.
2015-12-24 00:48:27 +01:00
Simon Sessingø 69b0f4320e [BUGFIX] Fixed show not always called if using nested ressources. 2015-12-24 00:39:21 +01:00
Simon Sessingø 4c60055c7e [BUGFIX] Custom parameters regex is now working again.
- Fixed custom parameters regex not working.
- Renamed setting "parameterRegex" to "match" to match the Laravel
  documentation.
2015-12-23 20:25:26 +01:00
Simon Sessingø 89aeeeb593 Merge pull request #57 from skipperbent/development
[TASK]
2015-12-19 20:25:36 +01:00
Simon Sessingø 849749969d [OPTIMISATION] Made getAllHeaders protected. 2015-12-19 20:20:42 +01:00
Simon Sessingø c37a7159d2 [OPTIMISATION] No reason to str_replace twice. 2015-12-19 20:18:35 +01:00
Simon Sessingø df2545dd37 [TASK]
- Added support for setups where getallheaders function is not availible.
- Made matchDomain method in RouterGroup always return boolean.
2015-12-19 20:15:10 +01:00
Simon Sessingø bd0a6af41f Merge pull request #56 from skipperbent/development
[BUGFIX] Wrong validation on domain
2015-12-19 18:25:56 +01:00
Simon Sessingø 3510c4c5a4 [BUGFIX] Wrong validation on domain 2015-12-19 18:25:18 +01:00
Simon Sessingø 7ff88bc658 Merge pull request #55 from skipperbent/development
[BUGFIX] Bugfixes.
2015-12-19 18:09:08 +01:00
Simon Sessingø ce27196083 [BUGFIX] Bugfixes.
- Fixed not validating domain on
- Other small changes.
2015-12-19 18:07:18 +01:00
Simon Sessingø 9304b9f866 Merge pull request #54 from skipperbent/development
Development
2015-12-19 16:28:17 +01:00
Simon Sessingø ed886cb7b3 Merge pull request #53 from skipperbent/feature-resource
[FEATURE] ResourceControllers now support nested ressources
2015-12-19 16:28:06 +01:00
Simon Sessingø cbaa0bcaac [FEATURE] getRoute now support domains.
- Optimisations + bugfixes.
2015-12-19 09:21:12 +01:00
Simon Sessingø 98b1759967 [TASK] Updated documentation. 2015-12-19 07:19:49 +01:00
Simon Sessingø b9be7695a7 [TASK] Fixed parameter matching regex when using multiple optional parameters. 2015-12-19 07:15:32 +01:00
Simon Sessingø 97e2edd207 [FEATURE] ResourceControllers now support nested ressources 2015-12-17 14:39:47 +01:00
Simon Sessingø 87fbbcbba2 Merge pull request #52 from skipperbent/development
[TASK] Csrf-token fixes + readded BaseCsrfVerifier.
2015-12-14 13:40:19 +01:00
Simon Sessingø 5a501db767 [TASK] Csrf-token fixes + readded BaseCsrfVerifier.
- Readded BaseCsrfVerifier middleware.
- Csrf-token expire time is now updated on each page refresh.
- CSRF-token update now happens after the route has been loaded, to ensure
  no faulty "Invalid csrf-token" exceptions.
2015-12-14 13:36:38 +01:00
Simon Sessingø 669bfa0a86 Merge pull request #51 from skipperbent/development
Latest development
2015-12-13 03:07:20 +01:00
Simon Sessingø 4f07f38cf5 [BUGFIX] Fixed getting current route not always returning current url. 2015-12-12 23:38:48 +01:00
Simon Sessingø c67ab20ddd [BUGFIX] Merge current parameters with new provided ones. 2015-12-12 22:53:25 +01:00
Simon Sessingø ad1ce21c66 [BUGFIX] Fixed getParams not being passed when using getRoute on current
route.
2015-12-12 20:42:06 +01:00
Simon Sessingø 5a19d6339c Merge pull request #50 from skipperbent/development
Development
2015-12-11 18:23:53 +01:00
Simon Sessingø 23ee53060e Merge branch 'development' of https://github.com/skipperbent/simple-php-router into development
Conflicts:
	src/Pecee/SimpleRouter/RouterBase.php
2015-12-11 18:23:19 +01:00
Simon Sessingø 77ba9f165e [BUGFIX] Fixed get params warning. 2015-12-11 18:21:00 +01:00
Simon Sessingø 4dd6417f71 Merge pull request #49 from skipperbent/development
Development
2015-12-11 18:11:03 +01:00
Simon Sessingø 3012435caa Merge pull request #48 from skipperbent/feature-optimisations
[OPTIMISATION] Optimisations + bugfixes.
2015-12-11 18:10:53 +01:00
Simon Sessingø 0bec524606 [OPTIMISATION] Optimisations + bugfixes.
- Fixed references to Pecee framework.
- Fixed variables not being initialised before usage.
- Cleaned up duplicate checks.
- Other small optimisations and bugfixes.
2015-12-11 18:06:52 +01:00
Simon Sessingø d4e9ef7744 Merge pull request #47 from skipperbent/development
[BUGFIX] Fixed error with RouterGroup.
2015-12-10 21:23:05 +01:00
Simon Sessingø cede827a45 [BUGFIX] Fixed error with RouterGroup. 2015-12-10 21:22:14 +01:00
Simon Sessingø 9da90d1435 Merge pull request #46 from skipperbent/development
Development
2015-12-10 20:05:49 +01:00
Simon Sessingø 4d70efcc4d Merge branch 'development' of https://github.com/skipperbent/simple-php-router into development 2015-12-10 19:56:12 +01:00
Simon Sessingø e1c549bfdb [OPTIMISATION] Added missing return statement 2015-12-10 19:55:48 +01:00
14 changed files with 467 additions and 191 deletions
+88 -22
View File
@@ -1,18 +1,11 @@
# Simple PHP router
Simple, fast and yet powerful PHP router that is easy to get integrated and in any project. Heavily inspired by the Laravel router.
Simple, fast and yet powerful PHP router that is easy to get integrated and in any project. Heavily inspired by the way Laravel handles routing, with both simplicity and expandability in mind.
## Installation
Add the latest version pf Simple PHP Router to your ```composer.json```
Add the latest version of Simple PHP Router running this command.
```json
{
"require": {
"pecee/simple-php-router": "1.*"
},
"require-dev": {
"pecee/simple-php-router": "1.*"
}
}
```
composer require pecee/framework
```
## Notes
@@ -32,10 +25,7 @@ The goal of this project is to create a router that is 100% compatible with the
- CSRF protection.
- Optional parameters
- Sub-domain routing
### Features currently "in-the-works"
- Global Constraints
- Custom boot managers to redirect urls to other routes
## Initialising the router
@@ -77,7 +67,7 @@ SimpleRouter::group(['prefix' => 'v1', 'middleware' => '\MyWebsite\Middleware\So
SimpleRouter::group(['prefix' => '/services', 'exceptionHandler' => '\MyProject\Handler\CustomExceptionHandler'], function() {
SimpleRouter::get('/answers/{id}', 'ControllerAnswers@show')->where(['id' => '[0-9]+');
// Optional parameter
SimpleRouter::get('/answers/{id?}', 'ControllerAnswers@show');
@@ -175,10 +165,10 @@ class Router extends SimpleRouter {
$exceptionHandler = null;
// Load and use exception-handler defined on group
if($route && $route->getGroup()) {
$exceptionHandler = $route->getGroup()->getExceptionHandler();
if($exceptionHandler !== null) {
$class = new $exceptionHandler();
$class->handleError(RouterBase::getInstance()->getRequest(), $route, $e);
@@ -186,7 +176,7 @@ class Router extends SimpleRouter {
}
// Otherwise use the fallback default exceptions handler
if(self::$defaultExceptionHandler !== null) {
$class = new self::$defaultExceptionHandler();
$class->handleError(RouterBase::getInstance()->getRequest(), $route, $e);
@@ -203,7 +193,8 @@ class Router extends SimpleRouter {
}
```
This is a basic example of a helper function for generating urls.
#### Helper functions examples
**This is a basic example of a helper function for generating urls.**
```php
use Pecee\SimpleRouter\RouterBase;
@@ -212,7 +203,7 @@ function url($controller, $parameters = null, $getParams = null) {
}
```
This is a basic example for getting the current csrf token
**This is a basic example for getting the current csrf token**
```php
/**
@@ -269,6 +260,78 @@ Register the new class in your ```routes.php```, custom ```Router``` class or wh
SimpleRouter::csrfVerifier(new \Demo\Middleware\CsrfVerifier());
```
## Using router bootmanager to make custom rewrite rules
Sometimes it can be necessary to keep urls stored in the database, file or similar. In this example, we want the url ```/my-cat-is-beatiful``` to load the route ```/article/view/1``` which the router knows, because it's defined in the ```routes.php``` file.
To interfere with the router, we create a class that inherits from ```RouterBootManager```. This class will be loaded before any other rules in ```routes.php``` and allow us to "change" the current route, if any of our criteria are fulfilled (like comming from the url ```/my-cat-is-beatiful```).
```php
use Pecee\Http\Request;
use Pecee\SimpleRouter\RouterBootManager;
class CustomRouterRules extends RouterBootManager{
public function boot(Request $request) {
$rewriteRules = [
'/my-cat-is-beatiful' => '/article/view/1',
'/horses-are-great' => '/article/view/2'
];
foreach($rewriteRules as $url => $rule) {
// If the current uri matches the url, we use our custom route
if($request->getUri() === $url) {
$request->setUri($rule);
}
}
return $request;
}
}
```
The above should be pretty self-explanatory and can easily be changed to loop through urls store in the database, file or cache.
What happens is that if the current route matches the route defined in the index of our ```$rewriteRules``` array, we set the route to the array value instead.
By doing this the route will now load the url ```/article/view/1``` instead of ```/my-cat-is-beatiful```.
The last thing we need to do, is to add our custom boot-manager to the ```routes.php``` file. You can create as many bootmanagers as you like and easily add them in your ```routes.php``` file.
**routes.php example:**
```php
// Add new bootmanager
SimpleRouter::addBootManager(new CustomRouterRules());
// This rule is what our custom bootmanager will use.
SimpleRouter::get('/article/view/{id}', 'ControllerArticle@view');
```
## Easily overwrite route about to be loaded
Sometimes it can be useful to manipulate the route that's about to be loaded, for instance if a user is not authenticated or if an error occurred within your Middleware that requires
some other route to be initialised. Simple PHP Router allows you to easily change the route about to be executed. All information about the current route is stored in
the ```\Pecee\SimpleRouter\Http\Request``` object.
**Note:** Please note that it's only possible to change the route BEFORE any route has initially been loaded, so doing this in your custom ExceptionHandler or Middleware is highly recommended.
```php
$route = Request::getInstance()->getLoadedRoute();
$route->setCallback('Example\MyCustomClass@hello');
// -- or --
$route->setClass('Example\MyCustomClass');
$route->setMethod('hello');
```
## Documentation
While I work on a better documentation, please refer to the Laravel 5 routing documentation here:
@@ -277,9 +340,12 @@ http://laravel.com/docs/5.1/routing
## Easily extendable
The router can be easily extended to customize your needs.
## Ideas and issues
If you want a great new feature or experience any issues what-so-ever, please feel free to leave an issue and i'll look into it whenever possible.
## The MIT License (MIT)
Copyright (c) 2015 Simon Sessingø / simple-php-router
Copyright (c) 2016 Simon Sessingø / simple-php-router
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+9 -7
View File
@@ -7,12 +7,6 @@ class CsrfToken {
protected $token;
public function __construct() {
if($this->getToken() === null) {
$this->setToken($this->generateToken());
}
}
/**
* Generate random identifier for CSRF token
* @return string
@@ -51,10 +45,18 @@ class CsrfToken {
* @return string|null
*/
public function getToken(){
if(isset($_COOKIE[self::CSRF_KEY])) {
if($this->hasToken()) {
return $_COOKIE[self::CSRF_KEY];
}
return null;
}
/**
* Returns whether the csrf token has been defined
* @return bool
*/
public function hasToken() {
return isset($_COOKIE[self::CSRF_KEY]);
}
}
@@ -1,3 +1,3 @@
<?php
namespace Pecee\SimpleRouter;
namespace Pecee\Exception;
class RouterException extends \Exception { }
+54 -5
View File
@@ -6,10 +6,6 @@ class Request {
protected static $instance;
protected $data;
protected $uri;
protected $host;
protected $method;
protected $headers;
/**
* Return new instance
@@ -27,10 +23,23 @@ class Request {
$this->host = $_SERVER['HTTP_HOST'];
$this->uri = $_SERVER['REQUEST_URI'];
$this->method = (isset($_POST['_method'])) ? strtolower($_POST['_method']) : strtolower($_SERVER['REQUEST_METHOD']);
$this->headers = array_change_key_case(getallheaders(), CASE_LOWER);
$this->headers = $this->getAllHeaders();
}
protected function getAllHeaders() {
$headers = array();
foreach ($_SERVER as $name => $value) {
if (substr($name, 0, 5) === 'HTTP_') {
$headers[strtolower(str_replace('_', '-', substr($name, 5)))] = $value;
}
}
return $headers;
}
public function getIsSecure() {
if(isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) === 'https') {
return true;
}
return isset($_SERVER['HTTPS']) ? true : (isset($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] === 443);
}
@@ -122,6 +131,17 @@ class Request {
return (isset($_REQUEST[$name]) ? $_REQUEST[$name] : $defaultValue);
}
public function isFormatAccepted($format) {
return (isset($_SERVER['HTTP_ACCEPT']) && stripos($_SERVER['HTTP_ACCEPT'], $format) > -1);
}
public function getAcceptFormats() {
if(isset($_SERVER['HTTP_ACCEPT'])) {
return explode(',', $_SERVER['HTTP_ACCEPT']);
}
return array();
}
public function __set($name, $value = null) {
$this->data[$name] = $value;
}
@@ -130,4 +150,33 @@ class Request {
return isset($this->data[$name]) ? $this->data[$name] : null;
}
/**
* Get the currently loaded route.
* @return \Pecee\SimpleRouter\RouterEntry
*/
public function getLoadedRoute() {
return $this->loadedRoute;
}
/**
* @param mixed $uri
*/
public function setUri($uri) {
$this->uri = $uri;
}
/**
* @param mixed $host
*/
public function setHost($host) {
$this->host = $host;
}
/**
* @param mixed $method
*/
public function setMethod($method) {
$this->method = $method;
}
}
+8 -3
View File
@@ -19,14 +19,19 @@ class Response {
* Redirect the response
*
* @param string $url
* @param int $httpCode
*/
public function redirect($url) {
$this->header('Location: ' . $url);
public function redirect($url, $httpCode = null) {
if($httpCode !== null) {
$this->httpCode($httpCode);
}
$this->header('location: ' . $url);
die();
}
public function refresh() {
$this->redirect(url());
$this->redirect(Request::getInstance()->getUri());
}
/**
-7
View File
@@ -1,7 +0,0 @@
<?php
namespace Pecee\SimpleRouter;
interface IRouteEntry {
}
+137 -46
View File
@@ -1,10 +1,10 @@
<?php
namespace Pecee\SimpleRouter;
use Pecee\ArrayUtil;
use Pecee\CsrfToken;
use Pecee\Exception\RouterException;
use Pecee\Http\Middleware\BaseCsrfVerifier;
use Pecee\Http\Request;
use Pecee\Url;
class RouterBase {
@@ -15,24 +15,29 @@ class RouterBase {
protected $routes;
protected $processedRoutes;
protected $controllerUrlMap;
protected $backstack;
protected $loadedRoute;
protected $backStack;
protected $defaultNamespace;
protected $bootManagers;
protected $baseCsrfVerifier;
// TODO: make interface for controller routers, so they can be easily detected
// TODO: clean up - cut some of the methods down to smaller pieces
public function __construct() {
$this->routes = array();
$this->backstack = array();
$this->backStack = array();
$this->controllerUrlMap = array();
$this->baseCsrfVerifier = new BaseCsrfVerifier();
$this->request = Request::getInstance();
$this->bootManagers = array();
$csrf = new CsrfToken();
$token = ($csrf->hasToken()) ? $csrf->getToken() : $csrf->generateToken();
$csrf->setToken($token);
}
public function addRoute(RouterEntry $route) {
if($this->currentRoute !== null) {
$this->backstack[] = $route;
$this->backStack[] = $route;
} else {
$this->routes[] = $route;
}
@@ -41,16 +46,19 @@ class RouterBase {
protected function processRoutes(array $routes, array $settings = array(), array $prefixes = array(), $backStack = false, $group = null) {
// Loop through each route-request
$activeGroup = null;
$routesCount = count($routes);
$mergedSettings = array();
/* @var $route RouterEntry */
for($i = 0; $i < $routesCount; $i++) {
$route = $routes[$i];
$route->setGroup($group);
$route->addSettings($settings);
if($backStack) {
$route->setGroup($group);
}
if($this->defaultNamespace && !$route->getNamespace()) {
$namespace = null;
@@ -69,38 +77,51 @@ class RouterBase {
array_push($newPrefixes, rtrim($route->getPrefix(), '/'));
}
$route->addSettings($settings);
if(!($route instanceof RouterGroup)) {
if(is_array($newPrefixes) && count($newPrefixes) && $backStack) {
$route->setUrl( join('/', $newPrefixes) . $route->getUrl() );
}
$group = null;
$this->controllerUrlMap[] = $route;
}
$this->currentRoute = $route;
if($route instanceof RouterGroup && is_callable($route->getCallback())) {
$group = $route;
$route->renderRoute($this->request);
$activeGroup = $route;
$mergedSettings = array_merge($route->getMergeableSettings(), $settings);
$mergedSettings = array_merge($settings, $route->getMergeableSettings());
}
$this->currentRoute = null;
if(count($this->backstack)) {
$backStack = $this->backstack;
$this->backstack = array();
if(count($this->backStack)) {
$backStack = $this->backStack;
$this->backStack = array();
// Route any routes added to the backstack
$this->processRoutes($backStack, $mergedSettings, $newPrefixes, true, $activeGroup);
$this->processRoutes($backStack, $mergedSettings, $newPrefixes, true, $group);
}
}
}
public function routeRequest() {
$originalUri = $this->request->getUri();
// Initialize boot-managers
if(count($this->bootManagers)) {
/* @var $manager RouterBootManager */
foreach($this->bootManagers as $manager) {
$this->request = $manager->boot($this->request);
if(!($this->request instanceof Request)) {
throw new RouterException('Custom router bootmanager "'. get_class($manager) .'" must return instance of Request.');
}
}
}
// Verify csrf token for request
if($this->baseCsrfVerifier !== null) {
/* @var $csrfVerifier BaseCsrfVerifier */
@@ -123,7 +144,7 @@ class RouterBase {
$routeMatch = $route->matchRoute($this->request);
if($routeMatch && !($routeMatch instanceof RouterGroup)) {
if($routeMatch) {
if(count($route->getRequestMethods()) && !in_array($this->request->getMethod(), $route->getRequestMethods())) {
$routeNotAllowed = true;
@@ -132,9 +153,13 @@ class RouterBase {
$routeNotAllowed = false;
$this->loadedRoute = $routeMatch;
$routeMatch->loadMiddleware($this->request);
$routeMatch->renderRoute($this->request);
$this->request->rewrite_uri = $this->request->uri;
$this->request->setUri($originalUri);
$this->request->loadedRoute = $route;
$route->loadMiddleware($this->request);
$this->request->loadedRoute->renderRoute($this->request);
break;
}
}
@@ -143,7 +168,7 @@ class RouterBase {
throw new RouterException('Route or method not allowed', 403);
}
if(!$this->loadedRoute) {
if(!$this->request->loadedRoute) {
throw new RouterException(sprintf('Route not found: %s', $this->request->getUri()), 404);
}
}
@@ -162,12 +187,30 @@ class RouterBase {
$this->defaultNamespace = $defaultNamespace;
}
/**
* @return array
*/
public function getBootManagers() {
return $this->bootManagers;
}
/**
* @param array $bootManagers
*/
public function setBootManagers(array $bootManagers) {
$this->bootManagers = $bootManagers;
}
public function addBootManager(RouterBootManager $bootManager) {
$this->bootManagers[] = $bootManager;
}
/**
* @return RouterEntry
*/
public function getLoadedRoute() {
if(!($this->loadedRoute instanceof RouterGroup)) {
return $this->loadedRoute;
if(!($this->request->loadedRoute instanceof RouterGroup)) {
return $this->request->loadedRoute;
}
return null;
}
@@ -176,7 +219,7 @@ class RouterBase {
* @return array
*/
public function getBackstack() {
return $this->backstack;
return $this->backStack;
}
/**
@@ -221,9 +264,34 @@ class RouterBase {
return $this;
}
public function arrayToParams(array $getParams = null, $includeEmpty = true) {
if (is_array($getParams) && count($getParams) > 0) {
foreach ($getParams as $key => $val) {
if (!empty($val) || empty($val) && $includeEmpty) {
$getParams[$key] = $key . '=' . $val;
}
}
return join('&', $getParams);
}
return '';
}
protected function processUrl($route, $method = null, $parameters = null, $getParams = null) {
$url = '/' . trim($route->getUrl(), '/');
$domain = '';
if($route->getGroup() !== null && $route->getGroup()->getDomain() !== null) {
if(is_array($route->getGroup()->getDomain())) {
$domains = $route->getGroup()->getDomain();
$domain = array_shift($domains);
} else {
$domain = $route->getGroup()->getDomain();
}
$domain = '//' . $domain;
}
$url = $domain . '/' . trim($route->getUrl(), '/');
if(($route instanceof RouterController || $route instanceof RouterResource) && $method !== null) {
$url .= $method;
@@ -235,26 +303,32 @@ class RouterBase {
}
} else {
/* @var $route RouterEntry */
$params = $route->getParameters();
if(count($params)) {
$i = 0;
foreach($params as $param => $value) {
$value = (isset($parameters[$param])) ? $parameters[$param] : $value;
$url = str_ireplace(array('{' . $param. '}', '{' . $param. '?}'), $value, $url);
$i++;
}
if(is_array($parameters)) {
$params = array_merge($route->getParameters(), $parameters);
} else {
// If no parameters are specified in the route, assume that the provided parameters should be used.
if(count($parameters)) {
$url = rtrim($url, '/') . '/' . join('/', $parameters);
}
$params = $route->getParameters();
}
$otherParams = [];
$i = 0;
foreach($params as $param => $value) {
$value = (isset($parameters[$param])) ? $parameters[$param] : $value;
if(stripos($url, '{' . $param. '}') !== false || stripos($url, '{' . $param . '?}') !== false) {
$url = str_ireplace(array('{' . $param . '}', '{' . $param . '?}'), $value, $url);
} else {
$otherParams[$param] = $value;
}
$i++;
}
$url = rtrim($url, '/') . '/' . join('/', $otherParams);
}
$url = rtrim($url, '/') . '/';
if($getParams !== null && count($getParams)) {
$url .= '?'.Url::arrayToParams($getParams);
$url .= '?' . $this->arrayToParams($getParams);
}
return $url;
@@ -270,8 +344,22 @@ class RouterBase {
throw new \InvalidArgumentException('Invalid type for getParams. Must be array or null');
}
if($controller === null && $parameters === null && $this->loadedRoute !== null) {
return $this->processUrl($this->loadedRoute, null, $getParams);
// Return current route if no options has been specified
if($controller === null && $parameters === null) {
$getParams = (is_array($getParams)) ? array_merge($_GET, $getParams) : $_GET;
$url = parse_url(Request::getInstance()->getUri());
$url = $url['path'];
if(count($getParams)) {
$url .= '?' . $this->arrayToParams($getParams);
}
return $url;
}
if($controller === null && $this->request->loadedRoute !== null) {
return $this->processUrl($this->request->loadedRoute, $this->request->loadedRoute->getMethod(), $parameters, $getParams);
}
$c = '';
@@ -285,7 +373,7 @@ class RouterBase {
$route = $this->controllerUrlMap[$i];
// Check an alias exist, if the matches - use it
if($route instanceof RouterRoute && strtolower($route->getAlias()) === strtolower($controller)) {
if($route instanceof RouterRoute && $route->hasAlias($controller)) {
return $this->processUrl($route, $route->getMethod(), $parameters, $getParams);
}
@@ -328,13 +416,16 @@ class RouterBase {
$url = array($controller);
if(is_array($parameters)) {
ArrayUtil::append($url, $parameters);
foreach($parameters as $key => $value) {
array_push($url,$value);
}
}
$url = '/' . trim(join('/', $url), '/') . '/';
if(is_array($getParams)) {
$url .= '?' . Url::arrayToParams($getParams);
if($getParams !== null && count($getParams)) {
$url .= '?' . $this->arrayToParams($getParams);
}
return $url;
@@ -0,0 +1,10 @@
<?php
namespace Pecee\SimpleRouter;
use Pecee\Http\Request;
abstract class RouterBootManager {
abstract public function boot(Request $request);
}
+5 -2
View File
@@ -1,6 +1,7 @@
<?php
namespace Pecee\SimpleRouter;
use Pecee\Exception\RouterException;
use Pecee\Http\Request;
class RouterController extends RouterEntry {
@@ -38,10 +39,12 @@ class RouterController extends RouterEntry {
return $class;
}
return null;
}
public function matchRoute(Request $request) {
$url = parse_url($request->getUri());
$url = parse_url(urldecode($request->getUri()));
$url = rtrim($url['path'], '/') . '/';
if(strtolower($url) == strtolower($this->url) || stripos($url, $this->url) === 0) {
@@ -61,7 +64,7 @@ class RouterController extends RouterEntry {
// Set callback
$this->setCallback($this->controller . '@' . $this->method);
return $this;
return true;
}
}
return null;
+34 -18
View File
@@ -2,6 +2,7 @@
namespace Pecee\SimpleRouter;
use Pecee\Exception\RouterException;
use Pecee\Http\Middleware\IMiddleware;
use Pecee\Http\Request;
@@ -27,7 +28,7 @@ abstract class RouterEntry {
public function __construct() {
$this->settings = array();
$this->settings['requestMethods'] = array();
$this->settings['parametersRegex'] = array();
$this->settings['where'] = array();
$this->settings['parameters'] = array();
}
@@ -77,6 +78,16 @@ abstract class RouterEntry {
return null;
}
public function setMethod($method) {
$this->callback = sprintf('%s@%s', $this->getClass(), $method);
return $this;
}
public function setClass($class) {
$this->callback = sprintf('%s@%s', $class, $this->getMethod());
return $this;
}
/**
* @param string $prefix
* @return self
@@ -155,7 +166,7 @@ abstract class RouterEntry {
* @return self
*/
public function where(array $options) {
$this->parametersRegex = array_merge($this->parametersRegex, $options);
$this->where = array_merge($this->where, $options);
return $this;
}
@@ -178,10 +189,6 @@ abstract class RouterEntry {
public function getMergeableSettings() {
$settings = $this->settings;
/*if(isset($settings['middleware'])) {
unset($settings['middleware']);
}*/
if(isset($settings['prefix'])) {
unset($settings['prefix']);
}
@@ -242,18 +249,13 @@ abstract class RouterEntry {
return new $name();
}
protected function parseParameters($route, $url, $parameterRegex = '[a-z0-9]*?') {
protected function parseParameters($route, $url, $parameterRegex = '[a-z0-9]+') {
$parameterNames = array();
$regex = '';
$lastCharacter = '';
$isParameter = false;
$parameter = '';
// Use custom parameter regex if it exists
if(is_array($this->parametersRegex) && isset($this->parametersRegex[$parameter])) {
$parameterRegex = $this->parametersRegex[$parameter];
}
$routeLength = strlen($route);
for($i = 0; $i < $routeLength; $i++) {
@@ -275,12 +277,18 @@ abstract class RouterEntry {
} elseif($isParameter && $character === '}') {
$required = true;
// Check for optional parameter
// Use custom parameter regex if it exists
if(is_array($this->where) && isset($this->where[$parameter])) {
$parameterRegex = $this->where[$parameter];
}
if($lastCharacter === '?') {
$parameter = substr($parameter, 0, strlen($parameter)-1);
$regex .= '(?:(?:\/{0,1}(?P<'.$parameter.'>'.$parameterRegex.')){0,1}\\/{0,1})';
$regex .= '(?:\\/?(?P<'.$parameter.'>[^\/]+)?\\/?)';
$required = false;
} else {
$regex .= '(?:\\/{0,1}(?P<' . $parameter . '>'. $parameterRegex .')\\/{0,1})';
$regex .= '\\/(?P<' . $parameter . '>'. $parameterRegex .')\\/';
}
$parameterNames[] = array('name' => $parameter, 'required' => $required);
$parameter = '';
@@ -304,7 +312,7 @@ abstract class RouterEntry {
$max = count($parameterNames);
if(count($max)) {
if($max) {
for($i = 0; $i < $max; $i++) {
$name = $parameterNames[$i];
$parameterValue = (isset($parameterValues[$name['name']]) && !empty($parameterValues[$name['name']])) ? $parameterValues[$name['name']] : null;
@@ -313,6 +321,10 @@ abstract class RouterEntry {
throw new RouterException('Missing required parameter ' . $name['name'], 404);
}
if(!$name['required'] && $parameterValue === null) {
continue;
}
$parameters[$name['name']] = $parameterValue;
}
}
@@ -332,7 +344,7 @@ abstract class RouterEntry {
throw new RouterException($middleware . ' must be instance of Middleware');
}
/* @var $class Middleware */
/* @var $class IMiddleware */
$middleware->handle($request);
}
} else {
@@ -341,7 +353,7 @@ abstract class RouterEntry {
throw new RouterException($this->getMiddleware() . ' must be instance of Middleware');
}
/* @var $class Middleware */
/* @var $class IMiddleware */
$middleware->handle($request);
}
}
@@ -365,7 +377,11 @@ abstract class RouterEntry {
throw new RouterException(sprintf('Method %s does not exist in class %s', $method, $className), 404);
}
call_user_func_array(array($class, $method), $this->getParameters());
$parameters = array_filter($this->getParameters(), function($var){
return !is_null($var);
});
call_user_func_array(array($class, $method), $parameters);
return $class;
}
+33 -9
View File
@@ -2,6 +2,7 @@
namespace Pecee\SimpleRouter;
use Pecee\Exception\RouterException;
use Pecee\Http\Request;
class RouterGroup extends RouterEntry {
@@ -10,7 +11,7 @@ class RouterGroup extends RouterEntry {
parent::__construct();
}
protected function matchDomain() {
public function matchDomain(Request $request) {
if($this->domain !== null) {
if(is_array($this->domain)) {
@@ -20,7 +21,7 @@ class RouterGroup extends RouterEntry {
for($i = 0; $i < $max; $i++) {
$domain = $this->domain[$i];
$parameters = $this->parseParameters($domain, request()->getHost(), '[^.]*');
$parameters = $this->parseParameters($domain, $request->getHost(), '[^.]*');
if($parameters !== null) {
$this->parameters = $parameters;
@@ -28,18 +29,20 @@ class RouterGroup extends RouterEntry {
}
}
return null;
return false;
}
$parameters = $this->parseParameters($this->domain, request()->getHost(), '[^.]*');
$parameters = $this->parseParameters($this->domain, $request->getHost(), '[^.]*');
if ($parameters !== null) {
$this->parameters = $parameters;
return true;
}
return false;
}
return null;
return true;
}
public function renderRoute(Request $request) {
@@ -58,15 +61,18 @@ class RouterGroup extends RouterEntry {
throw new RouterException('Method not allowed');
}
if($this->matchDomain() === null) {
return null;
}
$this->matchDomain($request);
return parent::renderRoute($request);
}
public function matchRoute(Request $request) {
return null;
// Skip if prefix doesn't match
if($this->getPrefix() !== null && stripos($request->getUri(), $this->getPrefix()) === false) {
return false;
}
return $this->matchDomain($request);
}
public function setExceptionHandler($class) {
@@ -78,4 +84,22 @@ class RouterGroup extends RouterEntry {
return $this->exceptionHandler;
}
public function getDomain() {
return $this->domain;
}
/**
* @param array $settings
* @return self
*/
public function addSettings(array $settings = null) {
if(isset($settings['namespace'])) {
unset($settings['namespace']);
}
if(is_array($settings)) {
$this->settings = array_merge($this->settings, $settings);
}
return $this;
}
}
+41 -61
View File
@@ -1,15 +1,13 @@
<?php
namespace Pecee\SimpleRouter;
use Pecee\Exception\RouterException;
use Pecee\Http\Request;
class RouterResource extends RouterEntry {
const DEFAULT_METHOD = 'index';
protected $url;
protected $controller;
protected $method;
protected $postMethod;
public function __construct($url, $controller) {
@@ -45,62 +43,58 @@ class RouterResource extends RouterEntry {
protected function call($method, $parameters) {
$this->setCallback($this->controller . '@' . $method);
$this->parameters = $parameters;
return $this;
return true;
}
public function matchRoute(Request $request) {
$url = parse_url($request->getUri());
$url = parse_url(urldecode($request->getUri()));
$url = rtrim($url['path'], '/') . '/';
if(strtolower($url) == strtolower($this->url) || stripos($url, $this->url) === 0) {
$url = rtrim($url, '/');
$route = rtrim($this->url, '/') . '/{id?}/{action?}';
$strippedUrl = trim(substr($url, strlen($this->url)), '/');
$path = explode('/', $strippedUrl);
$parameters = $this->parseParameters($route, $url, '[0-9]+?');
$args = $path;
$action = '';
if($parameters !== null) {
if(count($args)) {
$action = $args[0];
array_shift($args);
if(is_array($parameters)) {
$parameters = array_merge($this->parameters, $parameters);
}
if (count($path)) {
$action = isset($parameters['action']) ? $parameters['action'] : null;
unset($parameters['action']);
// Delete
if($request->getMethod() === self::REQUEST_TYPE_DELETE && $this->postMethod === self::REQUEST_TYPE_POST) {
return $this->call('destroy', $args);
}
// Update
if(in_array($request->getMethod(), array('put', 'patch')) && $this->postMethod === self::REQUEST_TYPE_POST) {
return $this->call('update', array_merge(array($action), $args));
}
// Edit
if(isset($args[0]) && strtolower($args[0]) === 'edit' && $this->postMethod === self::REQUEST_TYPE_GET) {
return $this->call('edit', array_merge(array($action), array_slice($args, 1)));
}
// Create
if(strtolower($action) === 'create' && $request->getMethod() === self::REQUEST_TYPE_GET) {
return $this->call('create', $args);
}
// Save
if($this->postMethod === self::REQUEST_TYPE_POST) {
return $this->call('store', $args);
}
// Show
if($action && $this->postMethod === self::REQUEST_TYPE_GET) {
return $this->call('show', array_merge(array($action), $args));
}
// Index
return $this->call('index', $args);
// Delete
if($request->getMethod() === self::REQUEST_TYPE_DELETE && $this->postMethod === self::REQUEST_TYPE_POST) {
return $this->call('destroy', $parameters);
}
// Update
if(in_array($request->getMethod(), array(self::REQUEST_TYPE_PATCH, self::REQUEST_TYPE_PUT)) && $this->postMethod === self::REQUEST_TYPE_POST) {
return $this->call('update', $parameters);
}
// Edit
if(isset($action) && strtolower($action) === 'edit' && $this->postMethod === self::REQUEST_TYPE_GET) {
return $this->call('edit', $parameters);
}
// Create
if(strtolower($action) === 'create' && $request->getMethod() === self::REQUEST_TYPE_GET) {
return $this->call('create', $parameters);
}
// Save
if($this->postMethod === self::REQUEST_TYPE_POST) {
return $this->call('store', $parameters);
}
// Show
if(isset($parameters['id']) && $this->postMethod === self::REQUEST_TYPE_GET) {
return $this->call('show', $parameters);
}
// Index
return $this->call('index', $parameters);
}
return null;
@@ -135,18 +129,4 @@ class RouterResource extends RouterEntry {
$this->controller = $controller;
}
/**
* @return string
*/
public function getMethod() {
return $this->method;
}
/**
* @param string $method
*/
public function setMethod($method) {
$this->method = $method;
}
}
+30 -9
View File
@@ -2,6 +2,7 @@
namespace Pecee\SimpleRouter;
use Pecee\ArrayUtil;
use Pecee\Http\Request;
class RouterRoute extends RouterEntry {
@@ -14,21 +15,19 @@ class RouterRoute extends RouterEntry {
parent::__construct();
$this->setUrl($url);
$this->setCallback($callback);
$this->settings['aliases'] = array();
}
public function matchRoute(Request $request) {
$url = parse_url($request->getUri());
$url = parse_url(urldecode($request->getUri()));
$url = rtrim($url['path'], '/') . '/';
// Match on custom defined regular expression
if($this->regexMatch) {
$parameters = array();
if(preg_match('/('.$this->regexMatch.')/is', request()->getHost() . $url, $parameters)) {
if(preg_match('/('.$this->regexMatch.')/is', $request->getHost() . $url, $parameters)) {
$this->parameters = (!is_array($parameters[0]) ? array($parameters[0]) : $parameters[0]);
return $this;
return true;
}
return null;
}
@@ -46,7 +45,7 @@ class RouterRoute extends RouterEntry {
$this->parameters = $parameters;
}
return $this;
return true;
}
return null;
@@ -74,7 +73,7 @@ class RouterRoute extends RouterEntry {
if(count($parameters)) {
$tmp = array();
foreach($parameters as $param) {
$tmp[$param] = '';
$tmp[$param] = null;
}
$this->parameters = $tmp;
}
@@ -85,15 +84,37 @@ class RouterRoute extends RouterEntry {
/**
* Get alias for the url which can be used when getting the url route.
* @return string
* @return string|array
*/
public function getAlias(){
return $this->alias;
}
/**
* Check if route has given alias.
*
* @param $name
* @return bool
*/
public function hasAlias($name) {
if(is_array($this->alias)) {
foreach($this->alias as $alias) {
if(strtolower($alias) === strtolower($name)) {
return true;
}
}
} else {
if(strtolower($this->getAlias()) === strtolower($name)) {
return true;
}
}
return false;
}
/**
* Set the url alias for easier getting the url route.
* @param string $alias
* @param string|array $alias
* @return self
*/
public function setAlias($alias){
+17 -1
View File
@@ -19,7 +19,7 @@ class SimpleRouter {
* @throws RouterException
*/
public static function start($defaultNamespace = null) {
$router = RouterBase::GetInstance();
$router = RouterBase::getInstance();
$router->setDefaultNamespace($defaultNamespace);
$router->routeRequest();
}
@@ -32,6 +32,10 @@ class SimpleRouter {
RouterBase::getInstance()->setBaseCsrfVerifier($baseCsrfVerifier);
}
public static function addBootManager(RouterBootManager $bootManager) {
RouterBase::getInstance()->addBootManager($bootManager);
}
public static function get($url, $callback, array $settings = null) {
$route = new RouterRoute($url, $callback);
$route->addSettings($settings);
@@ -90,6 +94,18 @@ class SimpleRouter {
return $group;
}
/**
* Adds get + post route
*
* @param string $url
* @param function $callback
* @param array|null $settings
* @return RouterRoute
*/
public static function basic($url, $callback, array $settings = null) {
return self::match(['get', 'post'], $url, $callback, $settings);
}
public static function match(array $requestMethods, $url, $callback, array $settings = null) {
$route = new RouterRoute($url, $callback);
$route->setRequestMethods($requestMethods);