Compare commits

...

74 Commits

Author SHA1 Message Date
Simon Sessingø 0ab15839a9 Merge pull request #216 from skipperbent/v2-development
Fixed: replace empty parameter-values with null.
2017-02-12 20:47:47 +01:00
Simon Sessingø 5977efc942 Fixed: replace empty parameter-values with null. 2017-02-12 20:45:56 +01:00
Simon Sessingø ed98519152 Merge pull request #209 from skipperbent/v2-development
Fixed getExtension method in InputFile retuning empty value.
2017-02-01 07:52:29 +01:00
Simon Sessingø bf069c2d42 Fixed getExtension method in InputFile retuning empty value. 2017-02-01 07:51:35 +01:00
Simon Sessingø 8509062e00 Merge pull request #207 from skipperbent/v2-development
Development
2017-01-19 18:41:43 +01:00
Simon Sessingø 523d49359b Development
- Fixed where method calling itself instead of setWhere.
- Added unit-tests for regex.
2017-01-19 18:40:47 +01:00
Simon Sessingø 9617cacc31 Merge pull request #204 from skipperbent/v2-development
getRoute optimisations.
2016-11-28 14:14:29 +01:00
Simon Sessingø 7cc2652bcd getRoute optimisations. 2016-11-28 14:13:58 +01:00
Simon Sessingø 99088719ed Merge pull request #202 from skipperbent/v2-development
`getUrl` will now use default-parameter value if specifically set to null when using `url()`.
2016-11-28 14:00:59 +01:00
Simon Sessingø e26d55a810 getUrl will now use default-parameter value if specificially set to null when using url(). 2016-11-28 09:37:05 +01:00
Simon Sessingø 3e41ee28b6 Merge pull request #200 from skipperbent/v2-development
Optimised route-match behavior
2016-11-28 06:45:05 +01:00
Simon Sessingø 8f3ce68a5e Optimised route-match behavior 2016-11-28 06:43:26 +01:00
Simon Sessingø 751b4444ae Merge pull request #198 from skipperbent/v2-development
Fixed parameters not merged with default values
2016-11-28 05:53:26 +01:00
Simon Sessingø d25351f4f9 Fixed parameters not merged with default values 2016-11-28 05:53:02 +01:00
Simon Sessingø 8cd42a2c4b Merge pull request #196 from skipperbent/v2-development
2.6.0
2016-11-28 05:26:04 +01:00
Simon Sessingø e8f19fbeae Simplified PARAMETERS_REGEX_MATCH in Route class. 2016-11-28 05:24:12 +01:00
Simon Sessingø 5c89ae2aaf Moved regex/match functionality to LoadableRoute. 2016-11-28 04:45:46 +01:00
Simon Sessingø b694a7c0c9 More cleanup 2016-11-28 04:39:34 +01:00
Simon Sessingø 7847b71bbc Cleanup 2016-11-28 04:38:38 +01:00
Simon Sessingø d9b97ccf42 Bugfixes 2016-11-28 04:20:34 +01:00
Simon Sessingø 74351e0330 Bugfixes 2016-11-27 01:07:11 +01:00
Simon Sessingø f5b03e106c Bugfixes 2016-11-27 00:55:47 +01:00
Simon Sessingø 2c5221051e Development 2016-11-26 10:40:42 +01:00
Simon Sessingø e8e1471bab Merge pull request #194 from skipperbent/v2-development
Input optimisations
2016-11-26 05:22:02 +01:00
Simon Sessingø aad11ac581 Input optimisations 2016-11-26 05:21:09 +01:00
Simon Sessingø 4de1498723 Merge pull request #192 from skipperbent/v2-development
V2 development
2016-11-26 05:03:36 +01:00
Simon Sessingø c1835152b6 Removed PHP 7 specific functionality. 2016-11-26 04:53:05 +01:00
Simon Sessingø 6213f2fb75 Development
- Optimised Input-classes.
- `get` and `getObject` methods on `Input` now supports filtering on multiple method-types when using the `$method` parameter.
- Input classes now know how to parse that stupid nested $_FILES array.
- It's now possible to change method-names on ResourceControllers.
- Removed `getValue` and `setValue` from `InputFile` classes.
- Ensured that request-method are only parsed from $_POST or $_SERVER.
- Fixed minor parameter-issues with subdomain routing.
- Added PHPDocs.
- Added even more unit-tests.
- Many small optimisations tweaks.
2016-11-26 04:30:00 +01:00
Simon Sessingø ea243f2c89 Merge pull request #190 from skipperbent/v2-development
Development
2016-11-25 18:28:26 +01:00
Simon Sessingø 68fc6b76c0 Development
- It's now possible to adjust the load-order for parameters on routes.
- Replaced PHP 7.1 deprecated `mcrypt_create_iv` function with `random_bytes` if it is available (requires > PHP7)
- Added `setIndex`, `setName` and `setValue` methods to `IInputItem` to allow for extendability.
- Cleanup and bug fixes.
2016-11-25 18:26:50 +01:00
Simon Sessingø 56653568e4 Merge pull request #188 from skipperbent/v2-development
Development
2016-11-25 12:55:08 +01:00
Simon Sessingø 1c515119b4 Development
- Ensure that request-method is always lowercase.
- Fixed spaces instead of tabs to comply with PSR-2.
2016-11-25 12:51:45 +01:00
Simon Sessingø 5d330643e7 Merge pull request #186 from skipperbent/v2-development
Development
2016-11-25 03:28:49 +02:00
Simon Sessingø 2dd2d95af5 Development
- Fixed BootManager not loading.
- Optimised for-loops.
2016-11-25 02:27:46 +01:00
Simon Sessingø 57aa8eac1e Merge pull request #184 from skipperbent/v2-development
Fixed Exception when using Request
2016-11-25 03:05:17 +02:00
Simon Sessingø 7edee8e6d3 Fixed Exception when using Request 2016-11-25 02:04:42 +01:00
Simon Sessingø 4efc72d013 Merge pull request #182 from skipperbent/v2-development
Development
2016-11-25 02:58:43 +02:00
Simon Sessingø e360fb5438 Removed demo-project. 2016-11-25 01:57:39 +01:00
Simon Sessingø c6bce8a420 Development
- Began work on new documentation.
- BaseCsrfVerifier now only matches `POST`, `PUT` and `DELETE`.
- Parameters are now parsed on custom regex-matches.
- Added `$type` option to `get` method in `Input` class.
2016-11-25 01:53:02 +01:00
Simon Sessingø fb6da37963 Simplified parseInput method with new handleGetPost helper method. 2016-11-24 22:58:32 +01:00
Simon Sessingø abe427ff59 Development
- Optimised Input and Input-related features.
- Removed InputCollection class.
- Changed more foreach to for.
- Updated documentation.
2016-11-24 22:44:58 +01:00
Simon Sessingø 49fc991f9a Merge pull request #180 from skipperbent/v2-development
Updated documentation
2016-11-24 14:51:07 +02:00
Simon Sessingø b2f23c6c7d Updated documentation 2016-11-24 13:50:45 +01:00
Simon Sessingø 20353c6e4d Merge pull request #178 from skipperbent/v2-development
Updated documentation to use Demo instead of MyWebsite
2016-11-24 14:44:56 +02:00
Simon Sessingø 53ece9a7fd Updated documentation to use Demo instead of MyWebsite 2016-11-24 13:44:20 +01:00
Simon Sessingø 4c62f86a26 Merge pull request #176 from skipperbent/v2-development
Fixed HttpException not thrown as NotFoundHttpException
2016-11-24 13:18:52 +02:00
Simon Sessingø 132cf1a10d Fixed HttpException not thrown as NotFoundHttpException 2016-11-24 12:17:47 +01:00
Simon Sessingø 6445746324 Merge pull request #174 from skipperbent/v2-development
Optimisations + bugfixes
2016-11-24 11:05:57 +02:00
Simon Sessingø ff1f027bda Make sure parameter-modifier is found before parsing parameters. 2016-11-24 10:04:47 +01:00
Simon Sessingø 9418d54c8e No need to return getValue as it will be returned on render. 2016-11-24 09:56:21 +01:00
Simon Sessingø e4ab14a2cb Ensure that setError is always int. 2016-11-24 09:42:37 +01:00
Simon Sessingø 258e0e0f13 Optimisations + bugfixes 2016-11-24 09:36:10 +01:00
Simon Sessingø 1a2921acb4 Merge pull request #173 from skipperbent/v2-development
Development
2016-11-21 09:30:58 +02:00
Simon Sessingø f1a9a50ee5 Development
- Only add $_GET param is every other parameter is null when calling getUrl.
2016-11-21 08:29:26 +01:00
Simon Sessingø 258d9d05c7 Merge pull request #171 from skipperbent/v2-development
Enhancements
2016-11-21 09:04:22 +02:00
Simon Sessingø 2cc97c120f Enhancements
- Added example on how to get current url in documentation.
- Fixed so urls always ends with /.
2016-11-21 07:56:37 +01:00
Simon Sessingø 7e7319de06 Merge pull request #169 from skipperbent/v2-development
Fixed // on currentRoute urls.
2016-11-21 05:27:53 +02:00
Simon Sessingø 5ad7dcf9fd Fixed // on currentRoute urls. 2016-11-21 04:27:06 +01:00
Simon Sessingø 531b35532b Merge pull request #167 from skipperbent/v2-development
Bugfixes
2016-11-21 05:07:59 +02:00
Simon Sessingø dfd32c0904 Bugfixes
- Moved middleware stuff back to Route class.
- Fixed Tests not working due to invalid method.
- Updated depricated method-calls in demo-project.
2016-11-21 04:06:45 +01:00
Simon Sessingø c258f937e8 Merge pull request #165 from skipperbent/v2-development
V 2.4.0 development
2016-11-21 04:09:26 +02:00
Simon Sessingø efe5767220 Development
- Removed yet another depricated method.
- Fixed $_GET parameters being merged on default when calling getUrl.
- Fixed double / that could appear on urls sometimes.
2016-11-21 03:05:43 +01:00
Simon Sessingø 5415d73f4d Added phpDocs 2016-11-21 02:15:25 +01:00
Simon Sessingø 60c0bd6355 Removed getRoutes depricated class from Route class. 2016-11-21 01:53:53 +01:00
Simon Sessingø 8370d3d94e Development
- Made easier to extend.
- Added IRoute class.
- Changed namespace for Route classes.
- Moved find-url related stuff to Route classes itself.
- Added more tests for finding urls.
- Added support for custom names on RouteController and RouteResource.
- Removed depricated methods.
- Updated documentation.
- Updated demo-project to reflect changes.
- Other small bugfixes and improvements.
2016-11-21 01:50:28 +01:00
Simon Sessingø df3acb6605 Enchacements
- Added more doctype and descriptions on functionality.
- Renamed methods and properties to make better sense.
- Added IRoute interface for easier extendability.
2016-11-20 18:05:57 +01:00
Simon Sessingø 88d58cd7b7 Merge pull request #163 from skipperbent/v2-development
Fixed: wrong argument-type in getUrl method
2016-11-19 21:12:57 +02:00
Simon Sessingø 4a48a3fcf9 Fixed: wrong argument-type in getUrl method 2016-11-19 20:12:06 +01:00
Simon Sessingø d04c74ccad Merge pull request #161 from skipperbent/v2-development
V2 development
2016-11-19 20:41:26 +02:00
Simon Sessingø 9922dcc552 Updated documentation 2016-11-19 19:37:59 +01:00
Simon Sessingø 87d619ca24 More optimisations 2016-11-19 19:28:10 +01:00
Simon Sessingø 7e63197252 Major overhaul 2016-11-19 19:24:05 +01:00
Simon Sessingø 02809a4daf Merge pull request #159 from skipperbent/v2-development
Bugfixes
2016-11-19 10:15:56 +02:00
Simon Sessingø ba719cf880 Bugfixes 2016-11-19 09:15:34 +01:00
64 changed files with 4850 additions and 3538 deletions
+829 -287
View File
File diff suppressed because it is too large Load Diff
-100
View File
@@ -1,100 +0,0 @@
# Simple PHP router demo project
This project is here to give you a basic understanding of how to setup and using simple-php-router.
Please note that this demo-project only covers how to integrate the `simple-php-router` in a project without a framework. If you are using some sort of PHP framework in your project the implementation might vary.
**What we won't cover:**
- How to setup a solution that fits your need. This is a basic demo to help you get started.
- Understanding of MVC; including Controllers, Middlewares or ExceptionHandlers.
- How to integrate into third party frameworks.
**What we cover:**
- How to get up and running fast - from scratch.
- How to get ExceptionHandlers, Middlewares and Controllers working.
- How to setup your webservers.
## Installation
- Navigate to the `demo-project` folder in terminal and run `composer update` to install the latest version.
- Point your webserver to `demo-project/public`.
### Setting up Nginx
If you are using Nginx please make sure that url-rewriting is enabled.
You can easily enable url-rewriting by adding the following configuration for the Nginx configuration-file for the demo-project.
```
location / {
try_files $uri $uri/ /index.php?$query_string;
}
```
### Setting up Apache
Nothing special is required for Apache to work. We've include the `.htaccess` file in the `public` folder. If rewriting is not working for you, please check that the `mod_rewrite` module (htaccess support) is enabled in the Apache configuration.
## Folder structure
| Folder | Description |
| ------------- |-------------|
| app |Contains projects-specific PHP classes|
| public |Public folder which are accessible through the web.|
## Notes
The demo project has it's own `Router` class implementation which extends the `SimpleRouter` class with further functionality.
This class can be useful adding additional functionality that are required before and after routing occurs or any extra functionality belonging to the router itself.
In this project we also use our custom router-class to autoload the `routes.php` file from our custom location (`app/routes.php`).
Please check the `routes.php` file in `demo-project/app` for all the urls/rules available in the project.
### CSRF-verifier
For the purpose of this demo, we've added a custom CSRF-verifier middleware called `CsrfVerifier` and disabled CSRF checks for all calls to `/api/*`. This will ensure that CSRF form-checks are not applied when calling our demo api url.
### Exception handlers
The included `CustomExceptionHandler` class returns a very basic json response for errors received on calls to `/api/*` or otherwise just a simple formatted error response.
### Middlewares
`ApiVerification` class is added to all calls to `/api/*`. This simple class just adds some data to the `Request` object, which is returned in one of the methods in the `ApiController` class. We've added this class to demonstrate that you can use middlewares to ensure that the user has the correct authentication - before router loads the controller itself.
### Urls
Please see `routes.php` for all routes and rules.
| URL |
| ------------- |
| / |
| /api/demo |
| /companies |
| /companies/[id] |
| /contact |
## The MIT License (MIT)
Copyright (c) 2016 Simon Sessingø / simple-php-router
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@@ -1,16 +0,0 @@
<?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');
echo json_encode([
'authenticated' => request()->authenticated
]);
}
}
@@ -1,27 +0,0 @@
<?php
namespace Demo\Controllers;
class DefaultController
{
public function index()
{
// implement
echo sprintf('DefaultController -> index (?fun=%s)', input()->get('fun'));
}
public function contact()
{
echo 'DefaultController -> contact';
}
public function companies($id = null)
{
echo 'DefaultController -> companies -> id: ' . $id;
}
public function notFound()
{
echo 'Page not found';
}
}
@@ -1,34 +0,0 @@
<?php
namespace Demo\Handlers;
use Pecee\Handler\IExceptionHandler;
use Pecee\Http\Request;
use Pecee\SimpleRouter\RouterEntry;
class CustomExceptionHandler implements IExceptionHandler
{
public function handleError(Request $request, RouterEntry &$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();
}
// else we just throw the error
if ($error->getCode() == 404) {
// Return 404 path
$request->setUri('/404');
return $request;
}
throw $error;
}
}
@@ -1,18 +0,0 @@
<?php
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;
return $request;
}
}
@@ -1,13 +0,0 @@
<?php
namespace Demo\Middlewares;
use Pecee\Http\Middleware\BaseCsrfVerifier;
class CsrfVerifier extends BaseCsrfVerifier
{
/**
* CSRF validation will be ignored on the following urls.
*/
protected $except = ['/api/*'];
}
-26
View File
@@ -1,26 +0,0 @@
<?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';
// Load our custom routes
require_once 'routes.php';
parent::setDefaultNamespace('\Demo');
// Do initial stuff
parent::start();
}
}
-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();
}
-23
View File
@@ -1,23 +0,0 @@
<?php
/**
* This file contains all the routes for the project
*/
use Demo\Router;
Router::csrfVerifier(new \Demo\Middlewares\CsrfVerifier());
Router::group(['exceptionHandler' => 'Demo\Handlers\CustomExceptionHandler'], function () {
Router::get('/', 'DefaultController@index')->setAlias('home');
Router::get('/contact', 'DefaultController@contact')->setAlias('contact');
Router::get('/404', 'DefaultController@notFound')->setAlias('404');
Router::basic('/companies', 'DefaultController@companies')->setAlias('companies');
Router::basic('/companies/{id}', 'DefaultController@companies')->setAlias('companies');
// Api
Router::group(['prefix' => '/api', 'middleware' => 'Demo\Middlewares\ApiVerification'], function () {
Router::resource('/demo', 'ApiController');
});
});
-26
View File
@@ -1,26 +0,0 @@
{
"name": "pecee/simple-router-demo",
"description": "Simple router demo project",
"keywords": [
"simple-router",
"php",
"php-simple-router"
],
"license": "MIT",
"type": "project",
"require": {
"php": ">=5.4.0",
"pecee/simple-router": "2.*"
},
"require-dev": {
},
"config": {
"preferred-install": "dist"
},
"autoload": {
"psr-4": {
"Demo\\": "app/"
}
}
}
-5
View File
@@ -1,5 +0,0 @@
RewriteEngine on
RewriteCond %{SCRIPT_FILENAME} !-f
RewriteCond %{SCRIPT_FILENAME} !-d
RewriteCond %{SCRIPT_FILENAME} !-l
RewriteRule ^(.*)$ index.php/$1
-7
View File
@@ -1,7 +0,0 @@
<?php
// load composer dependencies
require '../vendor/autoload.php';
// Start the routing
\Demo\Router::start();
-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);
}
+47
View File
@@ -0,0 +1,47 @@
<?php
namespace Pecee\Controllers;
interface IRestController
{
/**
* @return void
*/
public function index();
/**
* @param mixed $id
* @return void
*/
public function show($id);
/**
* @return void
*/
public function store();
/**
* @return void
*/
public function create();
/**
* View
* @param mixed $id
* @return void
*/
public function edit($id);
/**
* @param mixed $id
* @return void
*/
public function update($id);
/**
* @param mixed $id
* @return void
*/
public function destroy($id);
}
+58 -55
View File
@@ -3,66 +3,69 @@ namespace Pecee;
class CsrfToken
{
const CSRF_KEY = 'XSRF-TOKEN';
const CSRF_KEY = 'XSRF-TOKEN';
protected $token;
protected $token;
/**
* Generate random identifier for CSRF token
*
* @return string
*/
public static function generateToken()
{
if (function_exists('mcrypt_create_iv')) {
return bin2hex(mcrypt_create_iv(32, MCRYPT_DEV_URANDOM));
}
return bin2hex(openssl_random_pseudo_bytes(32));
}
/**
* Generate random identifier for CSRF token
*
* @return string
*/
public static function generateToken()
{
if (function_exists('random_bytes')) {
return bin2hex(random_bytes(32));
}
/**
* 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;
}
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());
}
/**
* Get csrf token
* @return string|null
*/
public function getToken()
{
if ($this->hasToken()) {
return $_COOKIE[static::CSRF_KEY];
}
return null;
}
return false;
}
/**
* Returns whether the csrf token has been defined
* @return bool
*/
public function hasToken()
{
return isset($_COOKIE[static::CSRF_KEY]);
}
/**
* Set csrf token cookie
*
* @param $token
*/
public function setToken($token)
{
setcookie(static::CSRF_KEY, $token, time() + 60 * 120, '/');
}
/**
* 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]);
}
}
-6
View File
@@ -1,6 +0,0 @@
<?php
namespace Pecee\Exception;
class RouterException extends \Exception
{
}
-17
View File
@@ -1,17 +0,0 @@
<?php
namespace Pecee\Handler;
use Pecee\Http\Request;
use Pecee\SimpleRouter\RouterEntry;
interface IExceptionHandler
{
/**
* @param Request $request
* @param RouterEntry|null $route
* @param \Exception $error
* @return Request|null
*/
public function handleError(Request $request, RouterEntry &$route = null, \Exception $error);
}
+17
View File
@@ -0,0 +1,17 @@
<?php
namespace Pecee\Handlers;
use Pecee\Http\Request;
use Pecee\SimpleRouter\Route\ILoadableRoute;
interface IExceptionHandler
{
/**
* @param Request $request
* @param ILoadableRoute $route
* @param \Exception $error
* @return Request|null
*/
public function handleError(Request $request, ILoadableRoute &$route = null, \Exception $error);
}
+17
View File
@@ -0,0 +1,17 @@
<?php
namespace Pecee\Http\Input;
interface IInputItem
{
public function getIndex();
public function setIndex($index);
public function getName();
public function setName($name);
public function __toString();
}
+242 -158
View File
@@ -5,207 +5,291 @@ use Pecee\Http\Request;
class Input
{
/**
* @var \Pecee\Http\Input\InputCollection
*/
public $get;
/**
* @var array
*/
public $get = [];
/**
* @var \Pecee\Http\Input\InputCollection
*/
public $post;
/**
* @var array
*/
public $post = [];
/**
* @var \Pecee\Http\Input\InputCollection
*/
public $file;
/**
* @var array
*/
public $file = [];
/**
* @var Request
*/
protected $request;
/**
* @var Request
*/
protected $request;
public function __construct(Request $request)
{
$this->request = $request;
$this->setGet();
$this->setPost();
$this->setFile();
}
public function __construct(Request $request)
{
$this->request = $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;
$this->parseInputs();
}
if ($this->request->getMethod() === 'post') {
public function parseInputs()
{
/* Parse get requests */
if (count($_GET) > 0) {
$this->get = $this->handleGetPost($_GET);
}
$contents = file_get_contents('php://input');
/* Parse post requests */
$postVars = $_POST;
if (stripos(trim($contents), '{') === 0) {
$output = json_decode($contents, true);
if ($output === false) {
$output = array();
}
}
}
if (in_array($this->request->getMethod(), ['put', 'patch', 'delete'], false) === true) {
parse_str(file_get_contents('php://input'), $postVars);
}
$output = array_merge($_GET, $output);
if (count($postVars) > 0) {
$this->post = $this->handleGetPost($postVars);
}
if ($filter !== null) {
$output = array_filter($output, function ($key) use ($filter) {
if (in_array($key, $filter)) {
return true;
}
/* Parse get requests */
if (count($_FILES) > 0) {
$this->file = $this->parseFiles();
}
}
return false;
}, ARRAY_FILTER_USE_KEY);
}
public function parseFiles()
{
$list = [];
return $output;
}
foreach ($_FILES as $key => $value) {
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;
// Handle array input
if (is_array($value['name']) === false) {
$values['index'] = $key;
$list[$key] = InputFile::createFromArray(array_merge($value, $values));
continue;
}
$element = $this->get->findFirst($index);
$keys = [];
if ($element !== null) {
return ($key !== null) ? $element[$key] : $element;
}
$files = $this->rearrangeFiles($value['name'], $keys, $value);
if ($this->request->getMethod() !== 'get') {
if (isset($list[$key])) {
$list[$key][] = $files;
} else {
$list[$key] = $files;
}
$element = $this->post->findFirst($index);
if ($element !== null) {
return ($key !== null) ? $element[$key] : $element;
}
}
$element = $this->file->findFirst($index);
if ($element !== null) {
return ($key !== null) ? $element[$key] : $element;
}
}
return $list;
}
return $default;
}
protected function rearrangeFiles(array $values, &$index, $original)
{
/**
* 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);
$output = [];
if ($item !== null) {
$getItem = function ($key, $property = 'name') use ($original, $index) {
if ($item instanceof InputCollection || $item instanceof InputFile) {
return $item;
}
$path = $original[$property];
return (trim($item->getValue()) === '') ? $default : $item->getValue();
}
foreach (array_values($index) as $i) {
$path = $path[$i];
}
return $default;
}
return $path[$key];
};
public function exists($index)
{
return ($this->getObject($index) !== null);
}
foreach ($values as $key => $value) {
public function setGet()
{
$this->get = new InputCollection();
if (is_array($getItem($key)) === false) {
if (count($_GET) > 0) {
foreach ($_GET as $key => $get) {
if (is_array($get) === false) {
$this->get->{$key} = new InputItem($key, $get);
continue;
}
$file = InputFile::createFromArray([
'index' => $key,
'error' => $getItem($key, 'error'),
'tmp_name' => $getItem($key, 'tmp_name'),
'type' => $getItem($key, 'type'),
'size' => $getItem($key, 'size'),
'filename' => $getItem($key, 'name'),
]);
$output = new InputCollection();
if (isset($output[$key])) {
$output[$key][] = $file;
} else {
$output[$key] = $file;
}
foreach ($get as $k => $g) {
$output->{$k} = new InputItem($k, $g);
}
continue;
}
$this->get->{$key} = $output;
}
}
}
$index[] = $key;
public function setPost()
{
$this->post = new InputCollection();
$files = $this->rearrangeFiles($value, $index, $original);
$postVars = $_POST;
if (isset($output[$key])) {
$output[$key][] = $files;
} else {
$output[$key] = $files;
}
if (in_array($this->request->getMethod(), ['put', 'patch', 'delete']) === true) {
parse_str(file_get_contents('php://input'), $postVars);
}
}
if (count($postVars) > 0) {
return $output;
}
foreach ($postVars as $key => $post) {
if (is_array($post) === false) {
$this->post->{strtolower($key)} = new InputItem($key, $post);
continue;
}
protected function handleGetPost(array $array)
{
$list = [];
$output = new InputCollection();
$max = count($array) - 1;
$keys = array_keys($array);
foreach ($post as $k => $p) {
$output->{$k} = new InputItem($k, $p);
}
for ($i = $max; $i >= 0; $i--) {
$this->post->{strtolower($key)} = $output;
}
}
}
$key = $keys[$i];
$value = $array[$key];
public function setFile()
{
$this->file = new InputCollection();
// Handle array input
if (is_array($value) === false) {
$list[$key] = new InputItem($key, $value);
continue;
}
if (count($_FILES) > 0) {
foreach ($_FILES as $key => $values) {
$output = $this->handleGetPost($value);
// Handle array input
if (is_array($values['name']) === false && trim($values['error']) !== '4') {
$values['index'] = $key;
$this->file->{strtolower($key)} = InputFile::createFromArray($values);
continue;
}
$list[$key] = $output;
}
$output = new InputCollection();
return $list;
}
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]
]);
}
}
/**
* Find post-value by index or return default value.
*
* @param string $index
* @param string|null $defaultValue
* @return InputItem|string
*/
public function findPost($index, $defaultValue = null)
{
return isset($this->post[$index]) ? $this->post[$index] : $defaultValue;
}
$this->file->{strtolower($key)} = $output;
}
}
}
/**
* Find file by index or return default value.
*
* @param string $index
* @param string|null $defaultValue
* @return InputFile|string
*/
public function findFile($index, $defaultValue = null)
{
return isset($this->file[$index]) ? $this->file[$index] : $defaultValue;
}
/**
* Find parameter/query-string by index or return default value.
*
* @param string $index
* @param string|null $defaultValue
* @return InputItem|string
*/
public function findGet($index, $defaultValue = null)
{
return isset($this->get[$index]) ? $this->get[$index] : $defaultValue;
}
/**
* Get input object
*
* @param string $index
* @param string|null $defaultValue
* @param array|string|null $methods
* @return IInputItem|string
*/
public function getObject($index, $defaultValue = null, $methods = null)
{
if ($methods !== null && is_string($methods) === true) {
$methods = [$methods];
}
$element = null;
if ($methods === null || in_array('get', $methods)) {
$element = $this->findGet($index);
}
if (($element === null && $methods === null) || ($methods !== null && in_array('post', $methods))) {
$element = $this->findPost($index);
}
if (($element === null && $methods === null) || ($methods !== null && in_array('file', $methods))) {
$element = $this->findFile($index);
}
return ($element !== null) ? $element : $defaultValue;
}
/**
* Get input element value matching index
*
* @param string $index
* @param string|null $defaultValue
* @param array|string|null $methods
* @return InputItem|string
*/
public function get($index, $defaultValue = null, $methods = null)
{
$input = $this->getObject($index, $defaultValue, $methods);
if ($input instanceof InputItem) {
return (trim($input->getValue()) === '') ? $defaultValue : $input->getValue();
}
return $input;
}
/**
* Check if a input-item exist
*
* @param string $index
* @return bool
*/
public function exists($index)
{
return ($this->getObject($index) !== null);
}
/**
* Get all get/post items
* @param array|null $filter Only take items in filter
* @return array
*/
public function all(array $filter = null)
{
$output = $_POST;
if ($this->request->getMethod() === 'post') {
$contents = file_get_contents('php://input');
if (strpos(trim($contents), '{') === 0) {
$output = json_decode($contents, true);
if ($output === false) {
$output = [];
}
}
}
$output = array_merge($_GET, $output);
if ($filter !== null) {
$output = array_filter($output, function ($key) use ($filter) {
return (in_array($key, $filter) === true);
}, ARRAY_FILTER_USE_KEY);
}
return $output;
}
}
-94
View File
@@ -1,94 +0,0 @@
<?php
namespace Pecee\Http\Input;
class InputCollection implements \IteratorAggregate
{
protected $data = array();
/**
* Search for input element matching index.
*
* @param string $index
* @param string|null $default
* @return InputItem|mixed
*/
public function findFirst($index, $default = null)
{
if (count($this->data) > 0) {
if (isset($this->data[$index])) {
return $this->data[$index];
}
foreach ($this->data as $key => $input) {
if (strtolower($index) === strtolower($key)) {
return $input;
}
}
}
return $default;
}
/**
* 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);
if($input !== null && trim($input->getValue()) !== '') {
return $input->getValue();
}
return $default;
}
public function getValue($index, $default = null) {
}
/**
* @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);
}
}
+248 -114
View File
@@ -1,136 +1,270 @@
<?php
namespace Pecee\Http\Input;
class InputFile extends InputItem
class InputFile implements IInputItem
{
public $size;
public $type;
public $error;
public $tmpName;
public $index;
public $name;
public $filename;
public $size;
public $type;
public $error;
public $tmpName;
/**
* @return string
*/
public function getSize()
{
return $this->size;
}
public function __construct($index)
{
$this->index = $index;
/**
* @return string
*/
public function getType()
{
return $this->type;
}
// Make the name human friendly, by replace _ with space
$this->name = ucfirst(str_replace('_', ' ', $this->index));
}
/**
* @return string
*/
public function getError()
{
return $this->error;
}
/**
* Create from array
*
* @param array $values
* @throws \InvalidArgumentException
* @return static
*/
public static function createFromArray(array $values)
{
if (!isset($values['index'])) {
throw new \InvalidArgumentException('Index key is required');
}
public function getMime()
{
return $this->getType();
}
/* Easy way of ensuring that all indexes-are set and not filling the screen with isset() */
/**
* @return string
*/
public function getTmpName()
{
return $this->tmpName;
}
$values = array_merge([
'tmp_name' => null,
'type' => null,
'size' => null,
'name' => null,
'error' => null,
], $values);
public function getExtension()
{
return pathinfo($this->getName(), PATHINFO_EXTENSION);
}
return (new static($values['index']))
->setError($values['error'])
->setSize($values['size'])
->setType($values['type'])
->setTmpName($values['tmp_name'])
->setFilename($values['name']);
public function move($destination)
{
return move_uploaded_file($this->tmpName, $destination);
}
}
public function getContents()
{
return file_get_contents($this->tmpName);
}
/**
* @return string
*/
public function getIndex()
{
return $this->index;
}
/**
* Set file temp. name
* @param string $name
* @return static $this
*/
public function setTmpName($name)
{
$this->tmpName = $name;
return $this;
}
/**
* Set input index
* @param string $index
* @return static $this
*/
public function setIndex($index)
{
$this->index = $index;
/**
* Set file size
* @param int $size
* @return static $this
*/
public function setSize($size)
{
$this->size = $size;
return $this;
}
return $this;
}
/**
* Set type
* @param string $type
* @return static $this
*/
public function setType($type)
{
$this->type = $type;
return $this;
}
/**
* @return string
*/
public function getSize()
{
return $this->size;
}
/**
* Set error
* @param int $error
* @return static $this
*/
public function setError($error)
{
$this->error = $error;
return $this;
}
/**
* Set file size
* @param int $size
* @return static $this
*/
public function setSize($size)
{
$this->size = $size;
/**
* 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');
}
return $this;
}
$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));
/**
* Get mime-type of file
* @return string
*/
public function getMime()
{
return $this->getType();
}
return $input;
}
/**
* @return string
*/
public function getType()
{
return $this->type;
}
/**
* @return string
*/
public function getValue()
{
return $this->tmpName;
}
/**
* Set type
* @param string $type
* @return static $this
*/
public function setType($type)
{
$this->type = $type;
return $this;
}
/**
* Returns extension without "."
*
* @return string
*/
public function getExtension()
{
return pathinfo($this->getFilename(), PATHINFO_EXTENSION);
}
/**
* Get human friendly name
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Set human friendly name.
* Useful for adding validation etc.
*
* @param string $name
* @return static $this
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Set filename
*
* @param string $name
* @return static $this
*/
public function setFilename($name)
{
$this->filename = $name;
return $this;
}
/**
* Get filename
*
* @return string mixed
*/
public function getFilename()
{
return $this->filename;
}
/**
* Move the uploaded temporary file to it's new home
*
* @param string $destination
* @return bool
*/
public function move($destination)
{
return move_uploaded_file($this->tmpName, $destination);
}
/**
* Get file contents
*
* @return string
*/
public function getContents()
{
return file_get_contents($this->tmpName);
}
/**
* Return true if an upload error occured.
*
* @return bool
*/
public function hasError()
{
return ($this->getError() !== 0);
}
/**
* Get upload-error code.
*
* @return string
*/
public function getError()
{
return $this->error;
}
/**
* Set error
*
* @param int $error
* @return static $this
*/
public function setError($error)
{
$this->error = (int)$error;
return $this;
}
/**
* @return string
*/
public function getTmpName()
{
return $this->tmpName;
}
/**
* Set file temp. name
* @param string $name
* @return static $this
*/
public function setTmpName($name)
{
$this->tmpName = $name;
return $this;
}
public function __toString()
{
return $this->getTmpName();
}
public function toArray()
{
return [
'tmp_name' => $this->tmpName,
'type' => $this->type,
'size' => $this->size,
'name' => $this->filename,
'error' => $this->error,
];
}
}
+65 -56
View File
@@ -1,70 +1,79 @@
<?php
namespace Pecee\Http\Input;
class InputItem
class InputItem implements IInputItem
{
public $index;
public $name;
public $value;
public $index;
public $name;
public $value;
public function __construct($index, $value = null)
{
$this->index = $index;
$this->value = $value;
public function __construct($index, $value = null)
{
$this->index = $index;
$this->value = $value;
// Make the name human friendly, by replace _ with space
$this->name = ucfirst(str_replace('_', ' ', $this->index));
}
// Make the name human friendly, by replace _ with space
$this->name = ucfirst(str_replace('_', ' ', $this->index));
}
/**
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @return string
*/
public function getIndex()
{
return $this->index;
}
/**
* @return string
*/
public function getValue()
{
return $this->value;
}
public function setIndex($index)
{
$this->index = $index;
/**
* @return string
*/
public function getIndex()
{
return $this->index;
}
return $this;
}
/**
* Set input name
* @param string $name
* @return static $this
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* 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;
public function __toString()
{
return (string)$this->value;
}
return $this;
}
/**
* @return string
*/
public function getValue()
{
return $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->value;
}
}
+72 -67
View File
@@ -2,94 +2,99 @@
namespace Pecee\Http\Middleware;
use Pecee\CsrfToken;
use Pecee\Exception\TokenMismatchException;
use Pecee\Http\Middleware\Exceptions\TokenMismatchException;
use Pecee\Http\Request;
use Pecee\SimpleRouter\RouterEntry;
use Pecee\SimpleRouter\Route\ILoadableRoute;
class BaseCsrfVerifier implements IMiddleware
{
const POST_KEY = 'csrf-token';
const HEADER_KEY = 'X-CSRF-TOKEN';
const POST_KEY = 'csrf-token';
const HEADER_KEY = 'X-CSRF-TOKEN';
protected $except;
protected $csrfToken;
protected $token;
protected $except;
protected $csrfToken;
protected $token;
public function __construct()
{
$this->csrfToken = new CsrfToken();
public function __construct()
{
$this->csrfToken = new CsrfToken();
// Generate or get the CSRF-Token from Cookie.
$this->token = (!$this->hasToken()) ? $this->generateToken() : $this->csrfToken->getToken();
}
// Generate or get the CSRF-Token from Cookie.
$this->token = ($this->hasToken() === false) ? $this->generateToken() : $this->csrfToken->getToken();
}
/**
* Check if the url matches the urls in the except property
* @param Request $request
* @return bool
*/
protected function skip(Request $request)
{
if ($this->except === null || is_array($this->except) === false) {
return false;
}
/**
* Check if the url matches the urls in the except property
* @param Request $request
* @return bool
*/
protected function skip(Request $request)
{
if ($this->except === null || is_array($this->except) === false) {
return false;
}
foreach ($this->except as $url) {
$url = rtrim($url, '/');
if ($url[strlen($url) - 1] === '*') {
$url = rtrim($url, '*');
$skip = (stripos($request->getUri(), $url) === 0);
} else {
$skip = ($url === rtrim($request->getUri(), '/'));
}
$max = count($this->except) - 1;
if ($skip) {
return true;
}
}
for ($i = $max; $i >= 0; $i--) {
$url = $this->except[$i];
return false;
}
$url = rtrim($url, '/');
if ($url[strlen($url) - 1] === '*') {
$url = rtrim($url, '*');
$skip = (stripos($request->getUri(), $url) === 0);
} else {
$skip = ($url === rtrim($request->getUri(), '/'));
}
public function handle(Request $request, RouterEntry &$route = null)
{
if ($skip === true) {
return true;
}
}
if ($request->getMethod() !== 'get' && !$this->skip($request)) {
return false;
}
$token = $request->getInput()->post->get(static::POST_KEY);
public function handle(Request $request, ILoadableRoute &$route = 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->skip($request) === false && in_array($request->getMethod(), ['post', 'put', 'delete'], false) === true) {
if (!$this->csrfToken->validate($token)) {
throw new TokenMismatchException('Invalid csrf-token.');
}
$token = $request->getInput()->get(static::POST_KEY, null, 'post');
}
// 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) === false) {
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 = CsrfToken::generateToken();
$this->csrfToken->setToken($token);
public function getToken()
{
return $this->token;
}
return $token;
}
public function hasToken()
{
if ($this->token !== null) {
return true;
}
return $this->csrfToken->hasToken();
}
public function getToken()
{
return $this->token;
}
}
@@ -1,6 +1,7 @@
<?php
namespace Pecee\Exception;
namespace Pecee\Http\Middleware\Exceptions;
class TokenMismatchException extends \Exception
{
}
+7 -7
View File
@@ -2,15 +2,15 @@
namespace Pecee\Http\Middleware;
use Pecee\Http\Request;
use Pecee\SimpleRouter\RouterEntry;
use Pecee\SimpleRouter\Route\ILoadableRoute;
interface IMiddleware
{
/**
* @param Request $request
* @param RouterEntry $route
* @return Request|null
*/
public function handle(Request $request, RouterEntry &$route);
/**
* @param Request $request
* @param ILoadableRoute $route
* @return Request|null
*/
public function handle(Request $request, ILoadableRoute &$route);
}
+197 -172
View File
@@ -5,202 +5,227 @@ use Pecee\Http\Input\Input;
class Request
{
protected $data = array();
protected $headers;
protected $host;
protected $uri;
protected $method;
protected $input;
protected $data = [];
protected $headers;
protected $host;
protected $uri;
protected $method;
protected $input;
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')));
}
public function __construct()
{
$this->parseHeaders();
$this->host = $this->getHeader('http-host');
$this->uri = $this->getHeader('request-uri');
$this->input = new Input($this);
$this->method = strtolower($this->input->get('_method', $this->getHeader('request-method'), 'post'));
}
protected function parseHeaders()
{
$this->headers = array();
protected function parseHeaders()
{
$this->headers = [];
foreach ($_SERVER as $name => $value) {
$this->headers[strtolower($name)] = $value;
$this->headers[strtolower(str_replace('_', '-', $name))] = $value;
}
}
$max = count($_SERVER) - 1;
$keys = array_keys($_SERVER);
public function isSecure()
{
if ($this->getHeader('http-x-forwarded-proto') === 'https' || $this->getHeader('https') !== null || $this->getHeader('server-port') === 443) {
return true;
}
for ($i = $max; $i >= 0; $i--) {
$key = $keys[$i];
$value = $_SERVER[$key];
return false;
}
$this->headers[strtolower($key)] = $value;
$this->headers[strtolower(str_replace('_', '-', $key))] = $value;
}
/**
* @return string
*/
public function getUri()
{
return $this->uri;
}
}
/**
* @return string
*/
public function getHost()
{
return $this->host;
}
public function isSecure()
{
return $this->getHeader('http-x-forwarded-proto') === 'https' || $this->getHeader('https') !== null || $this->getHeader('server-port') === 443;
}
/**
* @return string
*/
public function getMethod()
{
return $this->method;
}
/**
* @return string
*/
public function getUri()
{
return $this->uri;
}
/**
* Get http basic auth user
* @return string|null
*/
public function getUser()
{
return $this->getHeader('php-auth-user');
}
/**
* @return string
*/
public function getHost()
{
return $this->host;
}
/**
* Get http basic auth password
* @return string|null
*/
public function getPassword()
{
return $this->getHeader('php-auth-pw');
}
/**
* @return string
*/
public function getMethod()
{
return $this->method;
}
/**
* Get all headers
* @return array
*/
public function getHeaders()
{
return $this->headers;
}
/**
* Get http basic auth user
* @return string|null
*/
public function getUser()
{
return $this->getHeader('php-auth-user');
}
/**
* Get id address
* @return string
*/
public function getIp()
{
if ($this->getHeader('http-cf-connecting-ip') !== null) {
return $this->getHeader('http-cf-connecting-ip');
}
/**
* Get http basic auth password
* @return string|null
*/
public function getPassword()
{
return $this->getHeader('php-auth-pw');
}
if ($this->getHeader('http-x-forwarded-for') !== null && strlen($this->getHeader('http-x-forwarded-for'))) {
return $this->getHeader('http-x-forwarded_for');
}
/**
* Get all headers
* @return array
*/
public function getHeaders()
{
return $this->headers;
}
return $this->getHeader('remote-addr');
}
/**
* Get id address
* @return string
*/
public function getIp()
{
if ($this->getHeader('http-cf-connecting-ip') !== null) {
return $this->getHeader('http-cf-connecting-ip');
}
/**
* Get referer
* @return string
*/
public function getReferer()
{
return $this->getHeader('http-referer');
}
if ($this->getHeader('http-x-forwarded-for') !== null) {
return $this->getHeader('http-x-forwarded_for');
}
/**
* Get user agent
* @return string
*/
public function getUserAgent()
{
return $this->getHeader('http-user-agent');
}
return $this->getHeader('remote-addr');
}
/**
* 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 referer
* @return string
*/
public function getReferer()
{
return $this->getHeader('http-referer');
}
/**
* Get input class
* @return Input
*/
public function getInput()
{
return $this->input;
}
/**
* Get user agent
* @return string
*/
public function getUserAgent()
{
return $this->getHeader('http-user-agent');
}
/**
* 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);
}
/**
* Get header value by name
*
* @param string $name
* @param string|null $defaultValue
*
* @return string|null
*/
public function getHeader($name, $defaultValue = null)
{
if (isset($this->headers[strtolower($name)])) {
return $this->headers[strtolower($name)];
}
/**
* Get accept formats
* @return array
*/
public function getAcceptFormats()
{
return explode(',', $this->getHeader('http-accept'));
}
$max = count($_SERVER) - 1;
$keys = array_keys($_SERVER);
/**
* @param string $uri
*/
public function setUri($uri)
{
$this->uri = $uri;
}
for ($i = $max; $i >= 0; $i--) {
/**
* @param string $host
*/
public function setHost($host)
{
$this->host = $host;
}
$key = $keys[$i];
$name = $_SERVER[$key];
/**
* @param string $method
*/
public function setMethod($method)
{
$this->method = $method;
}
if ($key === $name) {
return $name;
}
}
public function __set($name, $value = null)
{
$this->data[$name] = $value;
}
return $defaultValue;
}
public function __get($name)
{
return isset($this->data[$name]) ? $this->data[$name] : null;
}
/**
* Get input class
* @return Input
*/
public function getInput()
{
return $this->input;
}
/**
* 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);
}
/**
* Get accept formats
* @return array
*/
public function getAcceptFormats()
{
return explode(',', $this->getHeader('http-accept'));
}
/**
* @param string $uri
*/
public function setUri($uri)
{
$this->uri = $uri;
}
/**
* @param string $host
*/
public function setHost($host)
{
$this->host = $host;
}
/**
* @param string $method
*/
public function setMethod($method)
{
$this->method = $method;
}
public function __isset($name)
{
return array_key_exists($name, $this->data);
}
public function __set($name, $value = null)
{
$this->data[$name] = $value;
}
public function __get($name)
{
return isset($this->data[$name]) ? $this->data[$name] : null;
}
}
+99 -96
View File
@@ -3,116 +3,119 @@ namespace Pecee\Http;
class Response
{
protected $request;
protected $request;
public function __construct(Request $request)
{
$this->request = $request;
}
public function __construct(Request $request)
{
$this->request = $request;
}
/**
* Set the http status code
*
* @param int $code
* @return static
*/
public function httpCode($code)
{
http_response_code($code);
return $this;
}
/**
* Set the http status code
*
* @param int $code
* @return static
*/
public function httpCode($code)
{
http_response_code($code);
/**
* Redirect the response
*
* @param string $url
* @param int $httpCode
*/
public function redirect($url, $httpCode = null)
{
if ($httpCode !== null) {
$this->httpCode($httpCode);
}
return $this;
}
$this->header('location: ' . $url);
die();
}
/**
* Redirect the response
*
* @param string $url
* @param int $httpCode
*/
public function redirect($url, $httpCode = null)
{
if ($httpCode !== null) {
$this->httpCode($httpCode);
}
public function refresh()
{
$this->redirect($this->request->getUri());
}
$this->header('location: ' . $url);
die();
}
/**
* 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 refresh()
{
$this->redirect($this->request->getUri());
}
public function cache($eTag, $lastModified = 2592000)
{
/**
* Add http authorisation
* @param string $name
* @return static
*/
public function auth($name = '')
{
$this->headers([
'WWW-Authenticate: Basic realm="' . $name . '"',
'HTTP/1.0 401 Unauthorized',
]);
$this->headers([
'Cache-Control: public',
'Last-Modified: ' . gmdate("D, d M Y H:i:s", $lastModified) . ' GMT',
'Etag: ' . $eTag
]);
return $this;
}
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
) {
public function cache($eTag, $lastModified = 2592000)
{
$this->headers([
'HTTP/1.1 304 Not Modified'
]);
$this->headers([
'Cache-Control: public',
'Last-Modified: ' . gmdate('D, d M Y H:i:s', $lastModified) . ' GMT',
'Etag: ' . $eTag,
]);
exit();
}
$httpModified = $this->request->getHeader('http-if-modified-since');
$httpIfNoneMatch = $this->request->getHeader('http-if-none-match');
return $this;
}
if (($httpIfNoneMatch !== null && $httpIfNoneMatch === $eTag) || ($httpModified !== null && strtotime($httpModified) === $lastModified)) {
/**
* Json encode array
* @param array $value
*/
public function json(array $value)
{
$this->header('Content-type: application/json');
echo json_encode($value);
die();
}
$this->header('HTTP/1.1 304 Not Modified');
/**
* Add header to response
* @param string $value
* @return static
*/
public function header($value)
{
header($value);
return $this;
}
exit();
}
/**
* Add multiple headers to response
* @param array $headers
* @return static
*/
public function headers(array $headers)
{
foreach ($headers as $header) {
header($header);
}
return $this;
}
return $this;
}
/**
* Json encode array
* @param array $value
*/
public function json(array $value)
{
$this->header('Content-Type: application/json');
echo json_encode($value);
die();
}
/**
* Add header to response
* @param string $value
* @return static
*/
public function header($value)
{
header($value);
return $this;
}
/**
* Add multiple headers to response
* @param array $headers
* @return static
*/
public function headers(array $headers)
{
foreach ($headers as $header) {
$this->header($header);
}
return $this;
}
}
@@ -0,0 +1,7 @@
<?php
namespace Pecee\SimpleRouter\Exceptions;
class HttpException extends \Exception
{
}
@@ -0,0 +1,6 @@
<?php
namespace Pecee\SimpleRouter\Exceptions;
class NotFoundHttpException extends HttpException
{
}
@@ -1,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);
}
@@ -0,0 +1,15 @@
<?php
namespace Pecee\SimpleRouter;
use Pecee\Http\Request;
interface IRouterBootManager
{
/**
* Called when router loads it's routes
*
* @param Request $request
* @return Request
*/
public function boot(Request $request);
}
-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;
}
}
@@ -0,0 +1,36 @@
<?php
namespace Pecee\SimpleRouter\Route;
interface IControllerRoute extends IRoute
{
/**
* Get controller class-name
*
* @return string
*/
public function getController();
/**
* Set controller class-name
*
* @param string $controller
* @return static
*/
public function setController($controller);
/**
* Return active method
*
* @return string
*/
public function getMethod();
/**
* Set active method
*
* @param string $method
* @return static
*/
public function setMethod($method);
}
@@ -0,0 +1,60 @@
<?php
namespace Pecee\SimpleRouter\Route;
use Pecee\Http\Request;
interface IGroupRoute extends IRoute
{
/**
* Method called to check if a domain matches
*
* @param Request $request
* @return bool
*/
public function matchDomain(Request $request);
/**
* Set exception-handlers for group
*
* @param array $handlers
* @return static $this
*/
public function setExceptionHandlers(array $handlers);
/**
* Get exception-handlers for group
*
* @return array
*/
public function getExceptionHandlers();
/**
* Get domains for domain.
*
* @return array
*/
public function getDomains();
/**
* Set allowed domains for group.
*
* @param array $domains
* @return $this
*/
public function setDomains(array $domains);
/**
* Set prefix that child-routes will inherit.
*
* @param string $prefix
* @return string
*/
public function setPrefix($prefix);
/**
* Get prefix.
*
* @return string
*/
public function getPrefix();
}
@@ -0,0 +1,69 @@
<?php
namespace Pecee\SimpleRouter\Route;
use Pecee\Http\Request;
interface ILoadableRoute extends IRoute
{
/**
* Find url that matches method, parameters or name.
* Used when calling the url() helper.
*
* @param string|null $method
* @param array|null $parameters
* @param string|null $name
* @return string
*/
public function findUrl($method = null, $parameters = null, $name = null);
/**
* Loads and renders middlewares-classes
*
* @param Request $request
* @param ILoadableRoute $route
*/
public function loadMiddleware(Request $request, ILoadableRoute $route);
public function getUrl();
public function setUrl($url);
/**
* Returns the provided name for the router.
*
* @return string
*/
public function getName();
/**
* Check if route has given name.
*
* @param string $name
* @return bool
*/
public function hasName($name);
/**
* Sets the router name, which makes it easier to obtain the url or router at a later point.
*
* @param string $name
* @return static $this
*/
public function setName($name);
/**
* Get regular expression match used for matching route (if defined).
*
* @return string
*/
public function getMatch();
/**
* Add regular expression match for the entire route.
*
* @param string $regex
* @return static
*/
public function setMatch($regex);
}
+185
View File
@@ -0,0 +1,185 @@
<?php
namespace Pecee\SimpleRouter\Route;
use Pecee\Http\Request;
interface IRoute
{
/**
* Method called to check if a domain matches
*
* @param Request $request
* @return bool
*/
public function matchRoute(Request $request);
/**
* Called when route is matched.
* Returns class to be rendered.
*
* @param Request $request
* @throws \Pecee\SimpleRouter\Exceptions\NotFoundHttpException
* @return void
*/
public function renderRoute(Request $request);
/**
* Returns callback name/identifier for the current route based on the callback.
* Useful if you need to get a unique identifier for the loaded route, for instance
* when using translations etc.
*
* @return string
*/
public function getIdentifier();
/**
* Set allowed request methods
*
* @param array $methods
* @return static $this
*/
public function setRequestMethods(array $methods);
/**
* Get allowed request methods
*
* @return array
*/
public function getRequestMethods();
/**
* @return IRoute|null
*/
public function getParent();
/**
* Get the group for the route.
*
* @return IGroupRoute|null
*/
public function getGroup();
/**
* Set group
*
* @param IGroupRoute $group
* @return static $this
*/
public function setGroup(IGroupRoute $group);
/**
* Set parent route
*
* @param IRoute $parent
* @return static $this
*/
public function setParent(IRoute $parent);
/**
* Set callback
*
* @param string $callback
* @return static
*/
public function setCallback($callback);
/**
* @return string
*/
public function getCallback();
public function getMethod();
public function getClass();
public function setMethod($method);
/**
* @param string $namespace
* @return static $this
*/
public function setNamespace($namespace);
/**
* @return string
*/
public function getNamespace();
/**
* @param string $namespace
* @return static $this
*/
public function setDefaultNamespace($namespace);
public function getDefaultNamespace();
/**
* Get parameter names.
*
* @return array
*/
public function getWhere();
/**
* Set parameter names.
*
* @param array $options
* @return static
*/
public function setWhere(array $options);
/**
* Get parameters
*
* @return array
*/
public function getParameters();
/**
* Get parameters
*
* @param array $parameters
* @return static $this
*/
public function setParameters(array $parameters);
/**
* Merge with information from another route.
*
* @param array $settings
* @param bool $merge
* @return static $this
*/
public function setSettings(array $settings, $merge = false);
/**
* Export route settings to array so they can be merged with another route.
*
* @return array
*/
public function toArray();
/**
* Get middlewares array
*
* @return array
*/
public function getMiddlewares();
/**
* Set middleware class-name
*
* @param string $middleware
* @return static
*/
public function setMiddleware($middleware);
/**
* Set middlewares array
*
* @param array $middlewares
* @return $this
*/
public function setMiddlewares(array $middlewares);
}
@@ -0,0 +1,251 @@
<?php
namespace Pecee\SimpleRouter\Route;
use Pecee\Http\Middleware\IMiddleware;
use Pecee\Http\Request;
use Pecee\SimpleRouter\Exceptions\HttpException;
abstract class LoadableRoute extends Route implements ILoadableRoute
{
/**
* @var string
*/
protected $url;
/**
* @var string
*/
protected $name;
protected $regex;
/**
* Loads and renders middlewares-classes
*
* @param Request $request
* @param ILoadableRoute $route
* @throws HttpException
*/
public function loadMiddleware(Request $request, ILoadableRoute $route)
{
if (count($this->getMiddlewares()) > 0) {
$max = count($this->getMiddlewares());
for ($i = 0; $i < $max; $i++) {
$middleware = $this->getMiddlewares()[$i];
$middleware = $this->loadClass($middleware);
if (!($middleware instanceof IMiddleware)) {
throw new HttpException($middleware . ' must be instance of Middleware');
}
$middleware->handle($request, $route);
}
}
}
public function matchRegex(Request $request, $url)
{
/* Match on custom defined regular expression */
if ($this->regex === null) {
return null;
}
$parameters = [];
if (preg_match($this->regex, $request->getHost() . $url, $parameters) > 0) {
/* Remove global match */
$this->parameters = array_slice($parameters, 1);
return true;
}
return false;
}
/**
* Set url
*
* @param string $url
* @return static
*/
public function setUrl($url)
{
$this->url = ($url === '/') ? '/' : '/' . trim($url, '/') . '/';
if (strpos($this->url, $this->paramModifiers[0]) !== false) {
$regex = sprintf(static::PARAMETERS_REGEX_MATCH, $this->paramModifiers[0], $this->paramOptionalSymbol, $this->paramModifiers[1]);
if (preg_match_all('/' . $regex . '/is', $this->url, $matches)) {
$this->parameters = array_fill_keys($matches[1], null);
}
}
return $this;
}
public function getUrl()
{
return $this->url;
}
/**
* Find url that matches method, parameters or name.
* Used when calling the url() helper.
*
* @param string|null $method
* @param array|null $parameters
* @param string|null $name
* @return string
*/
public function findUrl($method = null, $parameters = null, $name = null)
{
$url = $this->getUrl();
if ($this->getGroup() !== null && count($this->getGroup()->getDomains()) > 0) {
$url = '//' . $this->getGroup()->getDomains()[0] . $url;
}
/* Contains parameters that aren't recognized and will be appended at the end of the url */
$unknownParams = [];
/* Create the param string - {parameter} */
$param1 = $this->paramModifiers[0] . '%s' . $this->paramModifiers[1];
/* Create the param string with the optional symbol - {parameter?} */
$param2 = $this->paramModifiers[0] . '%s' . $this->paramOptionalSymbol . $this->paramModifiers[1];
/* Replace any {parameter} in the url with the correct value */
$params = $this->getParameters();
$max = count($params) - 1;
$keys = array_keys($params);
for ($i = $max; $i >= 0; $i--) {
$param = $keys[$i];
$value = $value = ($parameters !== null && array_key_exists($param, $parameters)) ? $parameters[$param] : $params[$param];
/* If parameter is specifically set to null - use the original-defined value */
if ($value === null && isset($this->originalParameters[$param])) {
$value = $this->originalParameters[$param];
}
if (stripos($url, $param1) !== false || stripos($url, $param) !== false) {
/* Add parameter to the correct position */
$url = str_ireplace([sprintf($param1, $param), sprintf($param2, $param)], $value, $url);
} else {
$unknownParams[$param] = $value;
}
}
$url .= join('/', $unknownParams);
return rtrim($url, '/') . '/';
}
/**
* Returns the provided name for the router.
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Check if route has given name.
*
* @param string $name
* @return bool
*/
public function hasName($name)
{
return (strtolower($this->name) === strtolower($name));
}
/**
* Add regular expression match for the entire route.
*
* @param string $regex
* @return static
*/
public function setMatch($regex)
{
$this->regex = $regex;
return $this;
}
/**
* Get regular expression match used for matching route (if defined).
*
* @return string
*/
public function getMatch()
{
return $this->regex;
}
/**
* Sets the router name, which makes it easier to obtain the url or router at a later point.
* Alias for LoadableRoute::setName().
*
* @see LoadableRoute::setName()
* @param string|array $name
* @return static
*/
public function name($name)
{
return $this->setName($name);
}
/**
* Sets the router name, which makes it easier to obtain the url or router at a later point.
*
* @param string $name
* @return static $this
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Merge with information from another route.
*
* @param array $values
* @param bool $merge
* @return static
*/
public function setSettings(array $values, $merge = false)
{
if (isset($values['as'])) {
if ($this->name !== null && $merge !== false) {
$this->setName($values['as'] . '.' . $this->name);
} else {
$this->setName($values['as']);
}
}
if (isset($values['prefix'])) {
$this->setUrl($values['prefix'] . $this->getUrl());
}
parent::setSettings($values, $merge);
return $this;
}
}
+480
View File
@@ -0,0 +1,480 @@
<?php
namespace Pecee\SimpleRouter\Route;
use Pecee\Http\Request;
use Pecee\SimpleRouter\Exceptions\NotFoundHttpException;
abstract class Route implements IRoute
{
const PARAMETERS_REGEX_MATCH = '%s([\w]+)(\%s?)%s';
const REQUEST_TYPE_GET = 'get';
const REQUEST_TYPE_POST = 'post';
const REQUEST_TYPE_PUT = 'put';
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,
];
/**
* If enabled parameters containing null-value
* will not be passed along to the callback.
*
* @var bool
*/
protected $filterEmptyParams = false;
protected $paramModifiers = '{}';
protected $paramOptionalSymbol = '?';
protected $group;
protected $parent;
protected $callback;
protected $defaultNamespace;
/* Default options */
protected $namespace;
protected $requestMethods = [];
protected $where = [];
protected $parameters = [];
protected $originalParameters = [];
protected $middlewares = [];
protected function loadClass($name)
{
if (class_exists($name) === false) {
throw new NotFoundHttpException(sprintf('Class %s does not exist', $name), 404);
}
return new $name();
}
public function renderRoute(Request $request)
{
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) === false) {
throw new NotFoundHttpException(sprintf('Method %s does not exist in class %s', $method, $className), 404);
}
$parameters = $this->getParameters();
/* Filter parameters with null-value */
if ($this->filterEmptyParams === true) {
$parameters = array_filter($parameters, function ($var) {
return ($var !== null);
});
}
call_user_func_array([$class, $method], $parameters);
}
}
protected function parseParameters($route, $url, $parameterRegex = '[\w]+')
{
$regex = sprintf(static::PARAMETERS_REGEX_MATCH, $this->paramModifiers[0], $this->paramOptionalSymbol, $this->paramModifiers[1]);
if (preg_match_all('/' . $regex . '/is', $route, $parameters)) {
$urlParts = preg_split('/((\-?\/?)\{[^}]+\})/is', rtrim($route, '/'));
foreach ($urlParts as $key => $t) {
$regex = '';
if ($key < (count($parameters[1]))) {
$name = $parameters[1][$key];
$regex = isset($this->where[$name]) ? $this->where[$name] : $parameterRegex;
$regex = sprintf('\-?\/?(?P<%s>%s)', $name, $regex) . $parameters[2][$key];
}
$urlParts[$key] = preg_quote($t, '/') . $regex;
}
$urlRegex = join('', $urlParts);
} else {
$urlRegex = preg_quote($route, '/');
}
if (preg_match('/^' . $urlRegex . '(\/?)$/is', $url, $matches) > 0) {
$values = [];
/* Only take matched parameters with name */
foreach ($parameters[1] as $name) {
$values[$name] = (isset($matches[$name]) && $matches[$name] !== '') ? $matches[$name] : null;
}
return $values;
}
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 IRoute|null
*/
public function getParent()
{
return $this->parent;
}
/**
* Get the group for the route.
*
* @return IGroupRoute|null
*/
public function getGroup()
{
return $this->group;
}
/**
* Set group
*
* @param IGroupRoute $group
* @return static $this
*/
public function setGroup(IGroupRoute $group)
{
$this->group = $group;
return $this;
}
/**
* Set parent route
*
* @param IRoute $parent
* @return static $this
*/
public function setParent(IRoute $parent)
{
$this->parent = $parent;
return $this;
}
/**
* Set callback
*
* @param string $callback
* @return static
*/
public function setCallback($callback)
{
$this->callback = $callback;
return $this;
}
/**
* @return string
*/
public function getCallback()
{
return $this->callback;
}
public function getMethod()
{
if (strpos($this->callback, '@') !== false) {
$tmp = explode('@', $this->callback);
return $tmp[1];
}
return null;
}
public function getClass()
{
if (strpos($this->callback, '@') !== false) {
$tmp = explode('@', $this->callback);
return $tmp[0];
}
return null;
}
public function setMethod($method)
{
$this->callback = sprintf('%s@%s', $this->getClass(), $method);
return $this;
}
public function setClass($class)
{
$this->callback = sprintf('%s@%s', $class, $this->getMethod());
return $this;
}
/**
* @param string $namespace
* @return static $this
*/
public function setNamespace($namespace)
{
$this->namespace = $namespace;
return $this;
}
/**
* @param string $namespace
* @return static $this
*/
public function setDefaultNamespace($namespace)
{
$this->defaultNamespace = $namespace;
return $this;
}
public function getDefaultNamespace()
{
return $this->defaultNamespace;
}
/**
* @return string
*/
public function getNamespace()
{
return ($this->namespace === null) ? $this->defaultNamespace : $this->namespace;
}
/**
* Export route settings to array so they can be merged with another route.
*
* @return array
*/
public function toArray()
{
$values = [];
if ($this->namespace !== null) {
$values['namespace'] = $this->namespace;
}
if (count($this->requestMethods) > 0) {
$values['method'] = $this->requestMethods;
}
if (count($this->where) > 0) {
$values['where'] = $this->where;
}
if (count($this->middlewares) > 0) {
$values['middleware'] = $this->middlewares;
}
return $values;
}
/**
* Merge with information from another route.
*
* @param array $values
* @param bool $merge
* @return static $this
*/
public function setSettings(array $values, $merge = false)
{
if ($this->namespace === null && isset($values['namespace'])) {
$this->setNamespace($values['namespace']);
}
if (isset($values['method'])) {
$this->setRequestMethods(array_merge($this->requestMethods, (array)$values['method']));
}
if (isset($values['where'])) {
$this->setWhere(array_merge($this->where, (array)$values['where']));
}
if (isset($values['parameters'])) {
$this->setParameters(array_merge($this->parameters, (array)$values['parameters']));
}
// Push middleware if multiple
if (isset($values['middleware'])) {
$this->setMiddlewares(array_merge((array)$values['middleware'], $this->middlewares));
}
return $this;
}
/**
* Get parameter names.
*
* @return array
*/
public function getWhere()
{
return $this->where;
}
/**
* Set parameter names.
*
* @param array $options
* @return static
*/
public function setWhere(array $options)
{
$this->where = $options;
return $this;
}
/**
* Add regular expression parameter match.
* Alias for LoadableRoute::where()
*
* @see LoadableRoute::where()
* @param array $options
* @return static
*/
public function where(array $options)
{
return $this->setWhere($options);
}
/**
* Get parameters
*
* @return array
*/
public function getParameters()
{
/* Sort the parameters after the user-defined param order, if any */
$parameters = [];
if (count($this->originalParameters) > 0) {
$parameters = $this->originalParameters;
}
return array_merge($parameters, $this->parameters);
}
/**
* Get parameters
*
* @param array $parameters
* @return static $this
*/
public function setParameters(array $parameters)
{
/*
* If this is the first time setting parameters we store them so we
* later can organize the array, in case somebody tried to sort the array.
*/
if (count($parameters) > 0 && count($this->originalParameters) === 0) {
$this->originalParameters = $parameters;
}
$this->parameters = array_merge($this->parameters, $parameters);
return $this;
}
/**
* Set middleware class-name
*
* @param string $middleware
* @return static
*/
public function setMiddleware($middleware)
{
$this->middlewares[] = $middleware;
return $this;
}
/**
* Set middlewares array
*
* @param array $middlewares
* @return $this
*/
public function setMiddlewares(array $middlewares)
{
$this->middlewares = $middlewares;
return $this;
}
/**
* @return string|array
*/
public function getMiddlewares()
{
return $this->middlewares;
}
}
@@ -0,0 +1,183 @@
<?php
namespace Pecee\SimpleRouter\Route;
use Pecee\Http\Request;
class RouteController extends LoadableRoute implements IControllerRoute
{
protected $defaultMethod = 'index';
protected $controller;
protected $method;
protected $names = [];
public function __construct($url, $controller)
{
$this->setUrl($url);
$this->setName(trim(str_replace('/', '.', $url), '/'));
$this->controller = $controller;
}
/**
* Check if route has given name.
*
* @param string $name
* @return bool
*/
public function hasName($name)
{
if ($this->name === null) {
return false;
}
/* Remove method/type */
if (strpos($name, '.') !== false) {
$method = substr($name, strrpos($name, '.') + 1);
$newName = substr($name, 0, strrpos($name, '.'));
if (in_array($method, $this->names, false) === true && strtolower($this->name) === strtolower($newName)) {
return true;
}
}
return parent::hasName($name);
}
/**
* @param string|null $method
* @param string|array|null $parameters
* @param string|null $name
* @return string
*/
public function findUrl($method = null, $parameters = null, $name = null)
{
if (strpos($name, '.') !== false) {
$found = array_search(substr($name, strrpos($name, '.') + 1), $this->names, false);
if ($found !== false) {
$method = $found;
}
}
$url = '';
$parameters = (array)$parameters;
if ($method !== null) {
/* Remove requestType from method-name, if it exists */
foreach (static::$requestTypes as $requestType) {
if (stripos($method, $requestType) === 0) {
$method = substr($method, strlen($requestType));
break;
}
}
$method .= '/';
}
if ($this->getGroup() !== null && count($this->getGroup()->getDomains()) > 0) {
$url .= '//' . $this->getGroup()->getDomains()[0];
}
$url .= '/' . trim($this->getUrl(), '/') . '/' . strtolower($method) . join('/', $parameters);
return '/' . trim($url, '/') . '/';
}
public function matchRoute(Request $request)
{
$url = parse_url(urldecode($request->getUri()), PHP_URL_PATH);
$url = rtrim($url, '/') . '/';
/* Match global regular-expression for route */
if ($this->matchRegex($request, $url) === true) {
return true;
}
if (stripos($url, $this->url) === 0 && strtolower($url) === strtolower($this->url)) {
$strippedUrl = trim(str_ireplace($this->url, '/', $url), '/');
$path = explode('/', $strippedUrl);
if (count($path) > 0) {
$method = (!isset($path[0]) || trim($path[0]) === '') ? $this->defaultMethod : $path[0];
$this->method = $method;
$this->parameters = array_slice($path, 1);
// Set callback
$this->setCallback($this->controller . '@' . $this->method);
return true;
}
}
return false;
}
/**
* Get controller class-name.
*
* @return string
*/
public function getController()
{
return $this->controller;
}
/**
* Get controller class-name.
*
* @param string $controller
* @return static
*/
public function setController($controller)
{
$this->controller = $controller;
return $this;
}
/**
* Return active method
*
* @return string
*/
public function getMethod()
{
return $this->method;
}
/**
* Set active method
*
* @param string $method
* @return static
*/
public function setMethod($method)
{
$this->method = $method;
return $this;
}
/**
* Merge with information from another route.
*
* @param array $values
* @param bool $merge
* @return static
*/
public function setSettings(array $values, $merge = false)
{
if (isset($values['names'])) {
$this->names = $values['names'];
}
parent::setSettings($values, $merge);
return $this;
}
}
+182
View File
@@ -0,0 +1,182 @@
<?php
namespace Pecee\SimpleRouter\Route;
use Pecee\Http\Request;
class RouteGroup extends Route implements IGroupRoute
{
protected $prefix;
protected $name;
protected $domains = [];
protected $exceptionHandlers = [];
/**
* Method called to check if a domain matches
*
* @param Request $request
* @return bool
*/
public function matchDomain(Request $request)
{
if (count($this->domains) === 0) {
return true;
}
foreach ($this->domains as $domain) {
$parameters = $this->parseParameters($domain, $request->getHost(), '.*');
if ($parameters !== null && count($parameters) > 0) {
$this->parameters = $parameters;
return true;
}
}
return false;
}
/**
* Method called to check if route matches
*
* @param Request $request
* @return bool
*/
public function matchRoute(Request $request)
{
/* Skip if prefix doesn't match */
if ($this->prefix !== null && stripos($request->getUri(), $this->prefix) === false) {
return false;
}
return $this->matchDomain($request);
}
/**
* Set exception-handlers for group
*
* @param array $handlers
* @return static $this
*/
public function setExceptionHandlers(array $handlers)
{
$this->exceptionHandlers = $handlers;
return $this;
}
/**
* Get exception-handlers for group
*
* @return array
*/
public function getExceptionHandlers()
{
return $this->exceptionHandlers;
}
/**
* Get allowed domains for domain.
*
* @return array
*/
public function getDomains()
{
return $this->domains;
}
/**
* Set allowed domains for group.
*
* @param array $domains
* @return $this
*/
public function setDomains(array $domains)
{
$this->domains = $domains;
return $this;
}
/**
* @param string $prefix
* @return static
*/
public function setPrefix($prefix)
{
$this->prefix = '/' . trim($prefix, '/');
return $this;
}
/**
* Set prefix that child-routes will inherit.
*
* @return string
*/
public function getPrefix()
{
return $this->prefix;
}
/**
* Merge with information from another route.
*
* @param array $values
* @param bool $merge
* @return static
*/
public function setSettings(array $values, $merge = false)
{
if (isset($values['prefix'])) {
$this->setPrefix($values['prefix'] . $this->prefix);
}
if (isset($values['exceptionHandler'])) {
$this->setExceptionHandlers((array)$values['exceptionHandler']);
}
if (isset($values['domain'])) {
$this->setDomains((array)$values['domain']);
}
if (isset($values['as'])) {
if ($this->name !== null && $merge !== false) {
$this->name = $values['as'] . '.' . $this->name;
} else {
$this->name = $values['as'];
}
}
parent::setSettings($values, $merge);
return $this;
}
/**
* Export route settings to array so they can be merged with another route.
*
* @return array
*/
public function toArray()
{
$values = [];
if ($this->prefix !== null) {
$values['prefix'] = $this->getPrefix();
}
if ($this->name !== null) {
$values['as'] = $this->name;
}
if (count($this->parameters) > 0) {
$values['parameters'] = $this->parameters;
}
return array_merge($values, parent::toArray());
}
}
@@ -0,0 +1,219 @@
<?php
namespace Pecee\SimpleRouter\Route;
use Pecee\Http\Request;
class RouteResource extends LoadableRoute implements IControllerRoute
{
protected $urls = [
'index' => '',
'create' => 'create',
'store' => '',
'show' => '',
'edit' => 'edit',
'update' => '',
'destroy' => '',
];
protected $methodNames = [
'index' => 'index',
'create' => 'create',
'store' => 'store',
'show' => 'show',
'edit' => 'edit',
'update' => 'update',
'destroy' => 'destroy',
];
protected $names = [];
protected $controller;
public function __construct($url, $controller)
{
$this->setUrl($url);
$this->controller = $controller;
$this->setName(trim(str_replace('/', '.', $url), '/'));
}
/**
* Check if route has given name.
*
* @param string $name
* @return bool
*/
public function hasName($name)
{
if ($this->name === null) {
return false;
}
if (strtolower($this->name) === strtolower($name)) {
return true;
}
/* Remove method/type */
if (strpos($name, '.') !== false) {
$name = substr($name, 0, strrpos($name, '.'));
}
return (strtolower($this->name) === strtolower($name));
}
public function findUrl($method = null, $parameters = null, $name = null)
{
$method = array_search($name, $this->names, false);
if ($method !== false) {
return rtrim($this->url . $this->urls[$method], '/') . '/';
}
return $this->url;
}
protected function call($method)
{
$this->setCallback($this->controller . '@' . $method);
return true;
}
public function matchRoute(Request $request)
{
$url = parse_url(urldecode($request->getUri()), PHP_URL_PATH);
$url = rtrim($url, '/') . '/';
/* Match global regular-expression for route */
$domainMatch = $this->matchRegex($request, $url);
if ($domainMatch !== null) {
return $domainMatch;
}
$route = rtrim($this->url, '/') . '/{id?}/{action?}';
$parameters = $this->parseParameters($route, $url);
if ($parameters === null) {
return false;
}
$this->parameters = (array)$parameters;
$action = isset($this->parameters['action']) ? $this->parameters['action'] : null;
unset($this->parameters['action']);
$method = $request->getMethod();
// Delete
if ($method === static::REQUEST_TYPE_DELETE && isset($this->parameters['id'])) {
return $this->call($this->methodNames['destroy']);
}
// Update
if (isset($this->parameters['id']) && in_array($method, [static::REQUEST_TYPE_PATCH, static::REQUEST_TYPE_PUT], false)) {
return $this->call($this->methodNames['update']);
}
// Edit
if ($method === static::REQUEST_TYPE_GET && isset($this->parameters['id']) && strtolower($action) === 'edit') {
return $this->call($this->methodNames['edit']);
}
// Create
if ($method === static::REQUEST_TYPE_GET && strtolower($action) === 'create') {
return $this->call($this->methodNames['create']);
}
// Save
if ($method === static::REQUEST_TYPE_POST) {
return $this->call($this->methodNames['store']);
}
// Show
if ($method === static::REQUEST_TYPE_GET && isset($this->parameters['id'])) {
return $this->call($this->methodNames['show']);
}
// Index
return $this->call($this->methodNames['index']);
}
/**
* @return string
*/
public function getController()
{
return $this->controller;
}
/**
* @param string $controller
* @return static
*/
public function setController($controller)
{
$this->controller = $controller;
return $this;
}
public function setName($name)
{
$this->name = $name;
$this->names = [
'index' => $this->name . '.index',
'create' => $this->name . '.create',
'store' => $this->name . '.store',
'show' => $this->name . '.show',
'edit' => $this->name . '.edit',
'update' => $this->name . '.update',
'destroy' => $this->name . '.destroy',
];
return $this;
}
/**
* Define custom method name for resource controller
*
* @param array $names
* @return static $this
*/
public function setMethodNames(array $names)
{
$this->methodNames = $names;
return $this;
}
/**
* Get method names
*
* @return array $this
*/
public function getMethodNames()
{
return $this->methodNames;
}
/**
* Merge with information from another route.
*
* @param array $values
* @param bool $merge
* @return static
*/
public function setSettings(array $values, $merge = false)
{
if (isset($values['names'])) {
$this->names = $values['names'];
}
if (isset($values['methods'])) {
$this->methodNames = $values['methods'];
}
parent::setSettings($values, $merge);
return $this;
}
}
+37
View File
@@ -0,0 +1,37 @@
<?php
namespace Pecee\SimpleRouter\Route;
use Pecee\Http\Request;
class RouteUrl extends LoadableRoute
{
public function __construct($url, $callback)
{
$this->setUrl($url);
$this->setCallback($callback);
}
public function matchRoute(Request $request)
{
$url = parse_url(urldecode($request->getUri()), PHP_URL_PATH);
$url = rtrim($url, '/') . '/';
/* Match global regular-expression for route */
$domainMatch = $this->matchRegex($request, $url);
if ($domainMatch !== null) {
return $domainMatch;
}
/* Make regular expression based on route */
$parameters = $this->parseParameters($this->url, $url);
if ($parameters === null) {
return false;
}
$this->setParameters($parameters);
return true;
}
}
+560
View File
@@ -0,0 +1,560 @@
<?php
namespace Pecee\SimpleRouter;
use Pecee\Handlers\IExceptionHandler;
use Pecee\Http\Middleware\BaseCsrfVerifier;
use Pecee\Http\Request;
use Pecee\SimpleRouter\Exceptions\HttpException;
use Pecee\SimpleRouter\Exceptions\NotFoundHttpException;
use Pecee\SimpleRouter\Route\IControllerRoute;
use Pecee\SimpleRouter\Route\IGroupRoute;
use Pecee\SimpleRouter\Route\ILoadableRoute;
use Pecee\SimpleRouter\Route\IRoute;
class Router
{
/**
* The instance of this class
* @var static
*/
protected static $instance;
/**
* Current request
* @var Request
*/
protected $request;
/**
* Defines if a route is currently being processed.
* @var bool
*/
protected $processingRoute;
/**
* All added routes
* @var array
*/
protected $routes;
/**
* List of processed routes
* @var array
*/
protected $processedRoutes;
/**
* Stack of routes used to keep track of sub-routes added
* when a route is being processed.
* @var array
*/
protected $routeStack;
/**
* List of added bootmanagers
* @var array
*/
protected $bootManagers;
/**
* Csrf verifier class
* @var BaseCsrfVerifier
*/
protected $csrfVerifier;
/**
* Get exception handlers
* @var array
*/
protected $exceptionHandlers;
/**
* The current loaded route
* @var ILoadableRoute|null
*/
protected $loadedRoute;
/**
* List over route changes (to avoid endless-looping)
* @var array
*/
protected $routeRewrites = [];
/**
* If the route has been rewritten/changed this property will contain the original url.
* @var string
*/
protected $originalUrl;
/**
* Get current router instance
* @return static
*/
public static function getInstance()
{
if (static::$instance === null) {
static::$instance = new static();
}
return static::$instance;
}
protected function __construct()
{
$this->reset();
}
public function reset()
{
$this->processingRoute = false;
$this->request = new Request();
$this->routes = [];
$this->bootManagers = [];
$this->routeStack = [];
$this->processedRoutes = [];
$this->exceptionHandlers = [];
}
/**
* Add route
* @param IRoute $route
* @return IRoute
*/
public function addRoute(IRoute $route)
{
/*
* If a route is currently being processed, that means that the
* route being added are rendered from the parent routes callback,
* so we add them to the stack instead.
*/
if ($this->processingRoute === true) {
$this->routeStack[] = $route;
} else {
$this->routes[] = $route;
}
return $route;
}
/**
* Process added routes.
*
* @param array $routes
* @param IGroupRoute|null $group
* @param IRoute|null $parent
*/
protected function processRoutes(array $routes, IGroupRoute $group = null, IRoute $parent = null)
{
// Loop through each route-request
$max = count($routes) - 1;
$exceptionHandlers = [];
/* @var $route IRoute */
for ($i = $max; $i >= 0; $i--) {
$route = $routes[$i];
/* @var $route IGroupRoute */
if ($route instanceof IGroupRoute) {
$group = $route;
if ($route->getCallback() !== null && is_callable($route->getCallback())) {
$this->processingRoute = true;
$route->renderRoute($this->request);
$this->processingRoute = false;
if ($route->matchRoute($this->request) === true) {
/* Add exception handlers */
if (count($route->getExceptionHandlers()) > 0) {
$exceptionHandlers += $route->getExceptionHandlers();
}
}
}
}
if ($group !== null) {
/* Add the parent group */
$route->setGroup($group);
}
if ($parent !== null) {
/* Add the parent route */
$route->setParent($parent);
/* Add/merge parent settings with child */
$route->setSettings($parent->toArray(), true);
}
if ($route instanceof ILoadableRoute) {
/* Add the route to the map, so we can find the active one when all routes has been loaded */
$this->processedRoutes[] = $route;
}
if (count($this->routeStack) > 0) {
/* Pop and grap the routes added when executing group callback earlier */
$stack = $this->routeStack;
$this->routeStack = [];
/* Route any routes added to the stack */
$this->processRoutes($stack, $route, $group);
}
}
$this->exceptionHandlers = array_unique(array_merge($exceptionHandlers, $this->exceptionHandlers));
}
public function routeRequest($rewrite = false)
{
$this->loadedRoute = null;
$routeNotAllowed = false;
try {
/* Initialize boot-managers */
if (count($this->bootManagers) > 0) {
$max = count($this->bootManagers) - 1;
/* @var $manager IRouterBootManager */
for ($i = $max; $i >= 0; $i--) {
$manager = $this->bootManagers[$i];
$this->request = $manager->boot($this->request);
if (!($this->request instanceof Request)) {
throw new HttpException('Bootmanager "' . get_class($manager) . '" must return instance of ' . Request::class, 500);
}
}
}
if ($rewrite === false) {
/* Loop through each route-request */
$this->processRoutes($this->routes);
if ($this->csrfVerifier !== null) {
/* Verify csrf token for request */
$this->csrfVerifier->handle($this->request);
}
$this->originalUrl = $this->request->getUri();
}
$max = count($this->processedRoutes) - 1;
/* @var $route IRoute */
for ($i = $max; $i >= 0; $i--) {
$route = $this->processedRoutes[$i];
/* If the route matches */
if ($route->matchRoute($this->request) === true) {
/* Check if request method matches */
if (count($route->getRequestMethods()) > 0 && in_array($this->request->getMethod(), $route->getRequestMethods(), false) === false) {
$routeNotAllowed = true;
continue;
}
$this->loadedRoute = $route;
$this->loadedRoute->loadMiddleware($this->request, $this->loadedRoute);
/* If the request has changed, we reinitialize the router */
if ($this->request->getUri() !== $this->originalUrl && in_array($this->request->getUri(), $this->routeRewrites) === false) {
$this->routeRewrites[] = $this->request->getUri();
$this->routeRequest(true);
return;
}
/* Render route */
$routeNotAllowed = false;
$this->request->setUri($this->originalUrl);
$this->loadedRoute->renderRoute($this->request);
break;
}
}
} catch (\Exception $e) {
$this->handleException($e);
}
if ($routeNotAllowed === true) {
$this->handleException(new HttpException('Route or method not allowed', 403));
}
if ($this->loadedRoute === null) {
$this->handleException(new NotFoundHttpException('Route not found: ' . $this->request->getUri(), 404));
}
}
protected function handleException(\Exception $e)
{
$max = count($this->exceptionHandlers);
/* @var $handler IExceptionHandler */
for ($i = 0; $i < $max; $i++) {
$handler = $this->exceptionHandlers[$i];
$handler = new $handler();
if (!($handler instanceof IExceptionHandler)) {
throw new HttpException('Exception handler must implement the IExceptionHandler interface.', 500);
}
$request = $handler->handleError($this->request, $this->loadedRoute, $e);
/* If the request has changed */
if ($request !== null && $this->request->getUri() !== $this->originalUrl && in_array($request->getUri(), $this->routeRewrites) === false) {
$this->request = $request;
$this->routeRewrites[] = $request->getUri();
$this->routeRequest(true);
return;
}
}
throw $e;
}
public function arrayToParams(array $getParams = [], $includeEmpty = true)
{
if (count($getParams) > 0) {
if ($includeEmpty === false) {
$getParams = array_filter($getParams, function ($item) {
return (!empty($item));
});
}
return '?' . http_build_query($getParams);
}
return '';
}
/**
* Find route by alias, class, callback or method.
*
* @param string $name
* @return ILoadableRoute|null
*/
public function findRoute($name)
{
$max = count($this->processedRoutes) - 1;
/* @var $route ILoadableRoute */
for ($i = $max; $i >= 0; $i--) {
$route = $this->processedRoutes[$i];
/* Check if the name matches with a name on the route. Should match either router alias or controller alias. */
if ($route->hasName($name)) {
return $route;
}
/* Direct match to controller */
if ($route instanceof IControllerRoute && strtolower($route->getController()) === strtolower($name)) {
return $route;
}
/* Using @ is most definitely a controller@method or alias@method */
if (strpos($name, '@') !== false) {
list($controller, $method) = array_map('strtolower', explode('@', $name));
if ($controller === strtolower($route->getClass()) && $method === strtolower($route->getMethod())) {
return $route;
}
}
/* Check if callback matches (if it's not a function) */
if (strpos($name, '@') !== false && strpos($route->getCallback(), '@') !== false && !is_callable($route->getCallback())) {
/* Check if the entire callback is matching */
if (strpos($route->getCallback(), $name) === 0 || strtolower($route->getCallback()) === strtolower($name)) {
return $route;
}
/* Check if the class part of the callback matches (class@method) */
if (strtolower($name) === strtolower($route->getClass())) {
return $route;
}
}
}
return null;
}
/**
* Get url for a route by using either name/alias, class or method name.
*
* The name parameter supports the following values:
* - Route name
* - Controller/resource name (with or without method)
* - Controller class name
*
* When searching for controller/resource by name, you can use this syntax "route.name@method".
* You can also use the same syntax when searching for a specific controller-class "MyController@home".
* If no arguments is specified, it will return the url for the current loaded route.
*
* @param string|null $name
* @param string|array|null $parameters
* @param array|null $getParams
* @throws \InvalidArgumentException
* @return string
*/
public function getUrl($name = null, $parameters = null, $getParams = null)
{
if ($getParams !== null && is_array($getParams) === false) {
throw new \InvalidArgumentException('Invalid type for getParams. Must be array or null');
}
/* Only merge $_GET when all parameters are null */
if ($name === null && $parameters === null && $getParams === null) {
$getParams = $_GET;
} else {
$getParams = (array)$getParams;
}
/* Return current route if no options has been specified */
if ($name === null && $parameters === null) {
$url = rtrim(parse_url($this->request->getUri(), PHP_URL_PATH), '/');
return (($url === '') ? '/' : $url . '/') . $this->arrayToParams($getParams);
}
/* If nothing is defined and a route is loaded we use that */
if ($name === null && $this->loadedRoute !== null) {
return $this->loadedRoute->findUrl($this->loadedRoute->getMethod(), $parameters, $name) . $this->arrayToParams($getParams);
}
/* We try to find a match on the given name */
$route = $this->findRoute($name);
if ($route !== null) {
return $route->findUrl($route->getMethod(), $parameters, $name) . $this->arrayToParams($getParams);
}
/* Using @ is most definitely a controller@method or alias@method */
if (strpos($name, '@') !== false) {
list($controller, $method) = explode('@', $name);
/* Loop through all the routes to see if we can find a match */
$max = count($this->processedRoutes) - 1;
/* @var $route ILoadableRoute */
for ($i = $max; $i >= 0; $i--) {
$route = $this->processedRoutes[$i];
/* Check if the route contains the name/alias */
if ($route->hasName($controller)) {
return $route->findUrl($method, $parameters, $name) . $this->arrayToParams($getParams);
}
/* Check if the route controller is equal to the name */
if ($route instanceof IControllerRoute && strtolower($route->getController()) === strtolower($controller)) {
return $route->findUrl($method, $parameters, $name) . $this->arrayToParams($getParams);
}
}
}
/* No result so we assume that someone is using a hardcoded url and join everything together. */
$url = trim(join('/', array_merge((array)$name, (array)$parameters)), '/');
return (($url === '') ? '/' : '/' . $url . '/') . $this->arrayToParams($getParams);
}
/**
* Get bootmanagers
* @return array
*/
public function getBootManagers()
{
return $this->bootManagers;
}
/**
* Set bootmanagers
* @param array $bootManagers
*/
public function setBootManagers(array $bootManagers)
{
$this->bootManagers = $bootManagers;
}
/**
* Add bootmanager
* @param IRouterBootManager $bootManager
*/
public function addBootManager(IRouterBootManager $bootManager)
{
$this->bootManagers[] = $bootManager;
}
/**
* @return array
*/
public function getRoutes()
{
return $this->routes;
}
/**
* Get current request
*
* @return Request
*/
public function getRequest()
{
return $this->request;
}
/**
* Get csrf verifier class
* @return BaseCsrfVerifier
*/
public function getCsrfVerifier()
{
return $this->csrfVerifier;
}
/**
* Set csrf verifier class
*
* @param BaseCsrfVerifier $csrfVerifier
* @return static
*/
public function setCsrfVerifier(BaseCsrfVerifier $csrfVerifier)
{
$this->csrfVerifier = $csrfVerifier;
return $this;
}
/**
* Get loaded route
* @return ILoadableRoute|null
*/
public function getLoadedRoute()
{
return $this->loadedRoute;
}
}
-561
View File
@@ -1,561 +0,0 @@
<?php
namespace Pecee\SimpleRouter;
use Pecee\Exception\RouterException;
use Pecee\Handler\IExceptionHandler;
use Pecee\Http\Middleware\BaseCsrfVerifier;
use Pecee\Http\Request;
use Pecee\Http\Response;
class RouterBase
{
/**
* @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)
{
// Loop through each route-request
/* @var $route RouterEntry */
foreach ($routes as $route) {
$newPrefixes = $prefixes;
$newSettings = $settings;
if($parent !== null) {
$route->setParent($parent);
}
if ($route instanceof RouterGroup) {
if ($route->getCallback() !== null && is_callable($route->getCallback())) {
$this->processingRoute = true;
$route->renderRoute($this->request);
$this->processingRoute = false;
if ($route->matchRoute($this->request)) {
// Add ExceptionHandler
if (count($route->getExceptionHandlers()) > 0) {
$this->exceptionHandlers = array_merge($route->getExceptionHandlers(), $this->exceptionHandlers);
}
}
}
}
if ($route instanceof RouterGroup) {
$newPrefixes[] = trim($route->getPrefix(), '/');
$newSettings = array_merge($settings, $route->toArray());
} else {
if (count($settings)) {
$route->merge($settings);
}
}
if ($route instanceof ILoadableRoute) {
if (count($prefixes)) {
$route->setUrl(trim(join('/', $prefixes) . $route->getUrl(), '/'));
}
$this->controllerUrlMap[] = $route;
}
if (count($this->backStack) > 0) {
$backStack = $this->backStack;
$this->backStack = [];
// Route any routes added to the backstack
$this->processRoutes($backStack, $newSettings, $newPrefixes, $route);
}
}
}
public function routeRequest($rewrite = false)
{
$this->loadedRoute = null;
$routeNotAllowed = false;
try {
// Initialize boot-managers
if (count($this->bootManagers) > 0) {
/* @var $manager RouterBootManager */
foreach ($this->bootManagers as $manager) {
$this->request = $manager->boot($this->request);
if (!($this->request instanceof Request)) {
throw new RouterException('Custom router bootmanager "' . get_class($manager) . '" must return instance of Request.');
}
}
}
if ($rewrite === false) {
// Loop through each route-request
$this->processRoutes($this->routes);
if ($this->csrfVerifier !== null) {
// Verify csrf token for request
$this->csrfVerifier->handle($this->request);
}
$this->originalUrl = $this->request->getUri();
}
/* @var $route RouterEntry */
foreach ($this->controllerUrlMap as $route) {
if ($route->matchRoute($this->request)) {
if (count($route->getRequestMethods()) > 0 && !in_array($this->request->getMethod(), $route->getRequestMethods())) {
$routeNotAllowed = true;
continue;
}
$this->loadedRoute = $route;
$this->loadedRoute->loadMiddleware($this->request, $this->loadedRoute);
if ($this->request->getUri() !== $this->originalUrl && !in_array($this->request->getUri(), $this->routeRewrites)) {
$this->routeRewrites[] = $this->request->getUri();
$this->routeRequest(true);
return;
}
$routeNotAllowed = false;
$this->request->setUri($this->originalUrl);
$this->loadedRoute->renderRoute($this->request);
break;
}
}
} catch (\Exception $e) {
$this->handleException($e);
}
if ($routeNotAllowed) {
$this->handleException(new RouterException('Route or method not allowed', 403));
}
if ($this->loadedRoute === null) {
$this->handleException(new RouterException(sprintf('Route not found: %s', $this->request->getUri()), 404));
}
}
protected function handleException(\Exception $e)
{
/* @var $handler IExceptionHandler */
foreach ($this->exceptionHandlers as $handler) {
$handler = new $handler();
if (!($handler instanceof IExceptionHandler)) {
throw new RouterException('Exception handler must implement the IExceptionHandler interface.');
}
$request = $handler->handleError($this->request, $this->loadedRoute, $e);
if ($request !== null && $request->getUri() !== $this->originalUrl && !in_array($request->getUri(), $this->routeRewrites)) {
$this->routeRewrites[] = $request->getUri();
$this->routeRequest(true);
return;
}
}
throw $e;
}
public function arrayToParams(array $getParams = null, $includeEmpty = true)
{
if (is_array($getParams) === true && count($getParams) > 0) {
if ($includeEmpty === false) {
$getParams = array_filter($getParams, function ($item) {
return (!empty($item));
});
}
return '?' . http_build_query($getParams);
}
return '';
}
protected function processUrl(LoadableRoute $route, $method = null, $parameters = null, $getParams = null)
{
$domain = '';
$parent = $route->getParent();
$parameters = (array)$parameters;
if ($parent !== null && $parent instanceof RouterGroup && count($parent->getDomains()) > 0) {
$domain = $parent->getDomains();
$domain = '//' . $domain[0];
}
$url = $domain . '/' . trim($route->getUrl(), '/');
if ($route instanceof IControllerRoute && $method !== null) {
$url .= '/' . $method . '/';
if (count($parameters) > 0) {
$url .= join('/', (array)$parameters);
}
} else {
if ($parameters !== null && count($parameters) > 0) {
$params = array_merge($route->getParameters(), (array)$parameters);
} else {
$params = $route->getParameters();
}
$otherParams = array();
foreach ($params as $param => $value) {
$value = (isset($parameters[$param])) ? $parameters[$param] : $value;
$param1 = LoadableRoute::PARAMETER_MODIFIERS[0] . $param . LoadableRoute::PARAMETER_MODIFIERS[1];
$param2 = LoadableRoute::PARAMETER_MODIFIERS[0] . $param . LoadableRoute::PARAMETER_OPTIONAL_SYMBOL . LoadableRoute::PARAMETER_MODIFIERS[1];
if (stripos($url, $param1) !== false || stripos($url, $param) !== false) {
$url = str_ireplace([$param1, $param2], $value, $url);
} else {
$otherParams[$param] = $value;
}
}
$url = rtrim($url, '/') . '/' . join('/', $otherParams);
}
$url = rtrim($url, '/') . '/';
if ($getParams !== null) {
$url .= $this->arrayToParams($getParams);
}
return $url;
}
/**
* 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) {
// Check an alias exist, if the matches - use it
// Matches either Router alias or controller alias.
if ($route->hasAlias($query)) {
return $route;
}
// Direct match to controller
if ($route instanceof IControllerRoute) {
if (strtolower($route->getController()) === strtolower($query)) {
return $route;
}
}
// Using @ is most definitely a controller@method or alias@method
if (strpos($query, '@') !== false) {
list($controller, $method) = array_map('strtolower', explode('@', $query));
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;
}
}
@@ -1,9 +0,0 @@
<?php
namespace Pecee\SimpleRouter;
use Pecee\Http\Request;
abstract class RouterBootManager
{
abstract public function boot(Request $request);
}
-110
View File
@@ -1,110 +0,0 @@
<?php
namespace Pecee\SimpleRouter;
use Pecee\Exception\RouterException;
use Pecee\Http\Request;
class RouterController extends LoadableRoute implements IControllerRoute
{
protected $defaultMethod = 'index';
protected $controller;
protected $method;
public function __construct($url, $controller)
{
$this->setUrl($url);
$this->controller = $controller;
}
public function renderRoute(Request $request)
{
if ($this->getCallback() !== null && is_callable($this->getCallback())) {
// When the callback is a function
call_user_func_array($this->getCallback(), $this->getParameters());
} else {
// When the callback is a method
$controller = explode('@', $this->getCallback());
$className = $this->getNamespace() . '\\' . $controller[0];
$class = $this->loadClass($className);
$method = $request->getMethod() . ucfirst($controller[1]);
if (!method_exists($class, $method)) {
throw new RouterException(sprintf('Method %s does not exist in class %s', $method, $className), 404);
}
call_user_func_array(array($class, $method), $this->getParameters());
return $class;
}
return null;
}
public function matchRoute(Request $request)
{
$url = parse_url(urldecode($request->getUri()), PHP_URL_PATH);
$url = rtrim($url, '/') . '/';
if (strtolower($url) == strtolower($this->url) || stripos($url, $this->url) === 0) {
$strippedUrl = trim(str_ireplace($this->url, '/', $url), '/');
$path = explode('/', $strippedUrl);
if (count($path) > 0) {
$method = (!isset($path[0]) || trim($path[0]) === '') ? $this->defaultMethod : $path[0];
$this->method = $method;
array_shift($path);
$this->parameters = $path;
// Set callback
$this->setCallback($this->controller . '@' . $this->method);
return true;
}
}
return null;
}
/**
* @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;
}
}
-442
View File
@@ -1,442 +0,0 @@
<?php
namespace Pecee\SimpleRouter;
use Pecee\Exception\RouterException;
use Pecee\Http\Middleware\IMiddleware;
use Pecee\Http\Request;
abstract class RouterEntry
{
const REQUEST_TYPE_GET = 'get';
const REQUEST_TYPE_POST = 'post';
const REQUEST_TYPE_PUT = 'put';
const REQUEST_TYPE_PATCH = 'patch';
const REQUEST_TYPE_OPTIONS = 'options';
const REQUEST_TYPE_DELETE = 'delete';
public static $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 $defaultNamespace;
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 $this
*/
public function setNamespace($namespace)
{
$this->namespace = $namespace;
return $this;
}
/**
* @param string $namespace
* @return static $this
*/
public function setDefaultNamespace($namespace) {
$this->defaultNamespace = $namespace;
return $this;
}
public function getDefaultNamespace() {
return $this->defaultNamespace;
}
/**
* @return string|array
*/
public function getMiddlewares()
{
return $this->middlewares;
}
/**
* @return string
*/
public function getNamespace()
{
return ($this->namespace === null) ? $this->defaultNamespace : $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);
}
-106
View File
@@ -1,106 +0,0 @@
<?php
namespace Pecee\SimpleRouter;
use Pecee\Http\Request;
class RouterGroup extends RouterEntry
{
protected $prefix;
protected $domains = array();
protected $exceptionHandlers = array();
public function matchDomain(Request $request)
{
if (count($this->domains) > 0) {
foreach ($this->domains as $domain) {
$parameters = $this->parseParameters($domain, $request->getHost(), '.*');
if ($parameters !== null) {
$this->parameters = $parameters;
return true;
}
}
return false;
}
return true;
}
public function matchRoute(Request $request)
{
// Skip if prefix doesn't match
if ($this->prefix !== null && stripos($request->getUri(), $this->prefix) === false) {
return false;
}
return $this->matchDomain($request);
}
public function setExceptionHandlers(array $handlers)
{
$this->exceptionHandlers = $handlers;
return $this;
}
public function getExceptionHandlers()
{
return $this->exceptionHandlers;
}
public function getDomains()
{
return $this->domains;
}
public function setDomains(array $domains)
{
$this->domains = $domains;
return $this;
}
/**
* @param string $prefix
* @return static
*/
public function setPrefix($prefix)
{
$this->prefix = '/' . trim($prefix, '/');
return $this;
}
/**
* @return string
*/
public function getPrefix()
{
return $this->prefix;
}
/**
* 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 (isset($values['exceptionHandler'])) {
$this->setExceptionHandlers((array)$values['exceptionHandler']);
}
if (isset($values['domain'])) {
$this->setDomains((array)$values['domain']);
}
parent::merge($values);
return $this;
}
}
-123
View File
@@ -1,123 +0,0 @@
<?php
namespace Pecee\SimpleRouter;
use Pecee\Exception\RouterException;
use Pecee\Http\Request;
class RouterResource extends LoadableRoute implements IControllerRoute
{
protected $controller;
public function __construct($url, $controller)
{
$this->setUrl($url);
$this->controller = $controller;
}
public function renderRoute(Request $request)
{
if ($this->getCallback() !== null && is_callable($this->getCallback())) {
// When the callback is a function
call_user_func_array($this->getCallback(), $this->getParameters());
} else {
// When the callback is a method
$controller = explode('@', $this->getCallback());
$className = $this->getNamespace() . '\\' . $controller[0];
$class = $this->loadClass($className);
$method = strtolower($controller[1]);
if (!method_exists($class, $method)) {
throw new RouterException(sprintf('Method %s does not exist in class %s', $method, $className), 404);
}
call_user_func_array([$class, $method], $this->getParameters());
return $class;
}
return null;
}
protected function call($method, $parameters)
{
$this->setCallback($this->controller . '@' . $method);
$this->parameters = $parameters;
return true;
}
public function matchRoute(Request $request)
{
$url = parse_url(urldecode($request->getUri()), PHP_URL_PATH);
$url = rtrim($url, '/') . '/';
$route = rtrim($this->url, '/') . '/{id?}/{action?}';
$parameters = $this->parseParameters($route, $url);
if ($parameters !== null) {
$parameters = array_merge($this->parameters, (array)$parameters);
$action = isset($parameters['action']) ? $parameters['action'] : null;
unset($parameters['action']);
$method = request()->getMethod();
// Delete
if (isset($parameters['id']) && $method === static::REQUEST_TYPE_DELETE) {
return $this->call('destroy', $parameters);
}
// Update
if (isset($parameters['id']) && in_array($method, [static::REQUEST_TYPE_PATCH, static::REQUEST_TYPE_PUT])) {
return $this->call('update', $parameters);
}
// Edit
if (isset($parameters['id']) && strtolower($action) === 'edit' && $method === static::REQUEST_TYPE_GET) {
return $this->call('edit', $parameters);
}
// Create
if (strtolower($action) === 'create' && $method === static::REQUEST_TYPE_GET) {
return $this->call('create', $parameters);
}
// Save
if ($method === static::REQUEST_TYPE_POST) {
return $this->call('store', $parameters);
}
// Show
if (isset($parameters['id']) && $method === static::REQUEST_TYPE_GET) {
return $this->call('show', $parameters);
}
// Index
return $this->call('index', $parameters);
}
return null;
}
/**
* @return string
*/
public function getController()
{
return $this->controller;
}
/**
* @param string $controller
* @return static
*/
public function setController($controller)
{
$this->controller = $controller;
return $this;
}
}
-42
View File
@@ -1,42 +0,0 @@
<?php
namespace Pecee\SimpleRouter;
use Pecee\Http\Request;
class RouterRoute extends LoadableRoute
{
public function __construct($url, $callback)
{
$this->setUrl($url);
$this->setCallback($callback);
}
public function matchRoute(Request $request)
{
$url = parse_url(urldecode($request->getUri()), PHP_URL_PATH);
$url = rtrim($url, '/') . '/';
// Match on custom defined regular expression
if ($this->regex !== null) {
$parameters = array();
if (preg_match('/(' . $this->regex . ')/is', $request->getHost() . $url, $parameters)) {
$this->parameters = (array)$parameters[0];
return true;
}
return null;
}
// Make regular expression based on route
$route = rtrim($this->url, '/') . '/';
$parameters = $this->parseParameters($route, $url);
if ($parameters !== null) {
$this->parameters = array_merge($this->parameters, $parameters);
return true;
}
return null;
}
}
+319 -287
View File
@@ -3,341 +3,373 @@
* ---------------------------
* Router helper class
* ---------------------------
* This class is added so calls can be made statically like Router::get() making the code look more pretty.
*
* This class is added so calls can be made statically like Router::get() making the code look pretty.
* It also adds some extra functionality like default-namespace.
*/
namespace Pecee\SimpleRouter;
use Pecee\Exception\RouterException;
use Pecee\Http\Middleware\BaseCsrfVerifier;
use Pecee\Http\Response;
use Pecee\SimpleRouter\Exceptions\HttpException;
use Pecee\SimpleRouter\Exceptions\NotFoundHttpException;
use Pecee\SimpleRouter\Route\IRoute;
use Pecee\SimpleRouter\Route\RouteController;
use Pecee\SimpleRouter\Route\RouteGroup;
use Pecee\SimpleRouter\Route\RouteResource;
use Pecee\SimpleRouter\Route\RouteUrl;
class SimpleRouter
{
protected static $defaultNamespace;
/**
* Default namespace added to all routes
* @var string
*/
protected static $defaultNamespace;
/**
* Start/route request
*
* @throws \Pecee\Exception\RouterException
*/
public static function start()
{
static::router()->routeRequest();
}
/**
* The response object
* @var Response
*/
protected static $response;
/**
* Set default namespace which will be prepended to all routes.
*
* @param string $defaultNamespace
*/
public static function setDefaultNamespace($defaultNamespace)
{
static::$defaultNamespace = $defaultNamespace;
}
/**
* Start/route request
*
* @throws HttpException
* @throws NotFoundHttpException
*/
public static function start()
{
static::router()->routeRequest();
}
/**
* Base CSRF verifier
*
* @param BaseCsrfVerifier $baseCsrfVerifier
*/
public static function csrfVerifier(BaseCsrfVerifier $baseCsrfVerifier)
{
static::router()->setCsrfVerifier($baseCsrfVerifier);
}
/**
* Set default namespace which will be prepended to all routes.
*
* @param string $defaultNamespace
*/
public static function setDefaultNamespace($defaultNamespace)
{
static::$defaultNamespace = $defaultNamespace;
}
/**
* 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);
}
/**
* Base CSRF verifier
*
* @param BaseCsrfVerifier $baseCsrfVerifier
*/
public static function csrfVerifier(BaseCsrfVerifier $baseCsrfVerifier)
{
static::router()->setCsrfVerifier($baseCsrfVerifier);
}
/**
* 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);
}
/**
* Boot managers allows you to alter the routes before the routing occurs.
* Perfect if you want to load pretty-urls from a file or database.
*
* @param IRouterBootManager $bootManager
*/
public static function addBootManager(IRouterBootManager $bootManager)
{
static::router()->addBootManager($bootManager);
}
/**
* 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);
}
/**
* Route the given url to your callback on GET request method.
*
* @param string $url
* @param string|\Closure $callback
* @param array|null $settings
* @return RouteUrl
*/
public static function get($url, $callback, array $settings = null)
{
return static::match(['get'], $url, $callback, $settings);
}
/**
* 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);
}
/**
* Route the given url to your callback on POST request method.
*
* @param string $url
* @param string|\Closure $callback
* @param array|null $settings
* @return RouteUrl
*/
public static function post($url, $callback, array $settings = null)
{
return static::match(['post'], $url, $callback, $settings);
}
/**
* 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);
}
/**
* Route the given url to your callback on PUT request method.
*
* @param string $url
* @param string|\Closure $callback
* @param array|null $settings
* @return RouteUrl
*/
public static function put($url, $callback, array $settings = null)
{
return static::match(['put'], $url, $callback, $settings);
}
/**
* 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);
}
/**
* Route the given url to your callback on PATCH request method.
*
* @param string $url
* @param string|\Closure $callback
* @param array|null $settings
* @return RouteUrl
*/
public static function patch($url, $callback, array $settings = null)
{
return static::match(['patch'], $url, $callback, $settings);
}
/**
* 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);
}
/**
* Route the given url to your callback on OPTIONS request method.
*
* @param string $url
* @param string|\Closure $callback
* @param array|null $settings
* @return RouteUrl
*/
public static function options($url, $callback, array $settings = null)
{
return static::match(['options'], $url, $callback, $settings);
}
/**
* Groups allows for encapsulating routes with special settings.
*
* @param array $settings
* @param \Closure $callback
* @throws RouterException
* @return RouterGroup
*/
public static function group(array $settings = array(), \Closure $callback)
{
$group = new RouterGroup();
$group->setCallback($callback);
$group->merge($settings);
/**
* Route the given url to your callback on DELETE request method.
*
* @param string $url
* @param string|\Closure $callback
* @param array|null $settings
* @return RouteUrl
*/
public static function delete($url, $callback, array $settings = null)
{
return static::match(['delete'], $url, $callback, $settings);
}
if (is_callable($callback) === false) {
throw new RouterException('Invalid callback provided. Only functions or methods supported');
}
/**
* Groups allows for encapsulating routes with special settings.
*
* @param array $settings
* @param \Closure $callback
* @throws \InvalidArgumentException
* @return RouteGroup
*/
public static function group(array $settings = [], \Closure $callback)
{
$group = new RouteGroup();
$group->setCallback($callback);
$group->setSettings($settings);
static::router()->addRoute($group);
if (is_callable($callback) === false) {
throw new \InvalidArgumentException('Invalid callback provided. Only functions or methods supported');
}
return $group;
}
static::router()->addRoute($group);
/**
* 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 $group;
}
/**
* 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);
}
/**
* Alias for the form method
*
* @param string $url
* @param callable $callback
* @param array|null $settings
* @see SimpleRouter::form
* @return RouteUrl
*/
public static function basic($url, $callback, array $settings = null)
{
return static::match(['get', 'post'], $url, $callback, $settings);
}
/**
* 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);
/**
* This type will route the given url to your callback on the provided request methods.
* Route the given url to your callback on POST and GET request method.
*
* @param string $url
* @param string|\Closure $callback
* @param array|null $settings
* @see SimpleRouter::form
* @return RouteUrl
*/
public static function form($url, $callback, array $settings = null)
{
return static::match(['get', 'post'], $url, $callback, $settings);
}
if ($settings !== null) {
$route->merge($settings);
}
/**
* This type will route the given url to your callback on the provided request methods.
*
* @param array $requestMethods
* @param string $url
* @param string|\Closure $callback
* @param array|null $settings
* @return RouteUrl
*/
public static function match(array $requestMethods, $url, $callback, array $settings = null)
{
$route = new RouteUrl($url, $callback);
$route->setRequestMethods($requestMethods);
$route = static::addDefaultNamespace($route);
static::router()->addRoute($route);
if ($settings !== null) {
$route->setSettings($settings);
}
return $route;
}
static::router()->addRoute($route);
/**
* This type will route the given url to your callback and allow any type of request method
*
* @param string $url
* @param string|\Closure $callback
* @param array|null $settings
* @return RouterRoute
*/
public static function all($url, $callback, array $settings = null)
{
$route = new RouterRoute($url, $callback);
return $route;
}
$route = static::addDefaultNamespace($route);
/**
* This type will route the given url to your callback and allow any type of request method
*
* @param string $url
* @param string|\Closure $callback
* @param array|null $settings
* @return RouteUrl
*/
public static function all($url, $callback, array $settings = null)
{
$route = new RouteUrl($url, $callback);
$route = static::addDefaultNamespace($route);
if ($settings !== null) {
$route->merge($settings);
}
if ($settings !== null) {
$route->setSettings($settings);
}
static::router()->addRoute($route);
static::router()->addRoute($route);
return $route;
}
return $route;
}
/**
* 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);
/**
* This route will route request from the given url to the controller.
*
* @param string $url
* @param string $controller
* @param array|null $settings
* @return RouteController
*/
public static function controller($url, $controller, array $settings = null)
{
$route = new RouteController($url, $controller);
$route = static::addDefaultNamespace($route);
$route = static::addDefaultNamespace($route);
if ($settings !== null) {
$route->setSettings($settings);
}
if ($settings !== null) {
$route->merge($settings);
}
static::router()->addRoute($route);
static::router()->addRoute($route);
return $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 RouteResource
*/
public static function resource($url, $controller, array $settings = null)
{
$route = new RouteResource($url, $controller);
/**
* 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->setSettings($settings);
}
if ($settings !== null) {
$route->merge($settings);
}
static::router()->addRoute($route);
static::router()->addRoute($route);
return $route;
}
return $route;
}
/**
* Get url for a route by using either name/alias, class or method name.
*
* The name parameter supports the following values:
* - Route name
* - Controller/resource name (with or without method)
* - Controller class name
*
* When searching for controller/resource by name, you can use this syntax "route.name@method".
* You can also use the same syntax when searching for a specific controller-class "MyController@home".
* If no arguments is specified, it will return the url for the current loaded route.
*
* @param string|null $name
* @param string|array|null $parameters
* @param array|null $getParams
* @throws \Exception
* @return string
*/
public static function getUrl($name = null, $parameters = null, $getParams = null)
{
return static::router()->getUrl($name, $parameters, $getParams);
}
/**
* 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 request
*
* @return \Pecee\Http\Request
*/
public static function request()
{
return static::router()->getRequest();
}
/**
* Get the response object
*
* @return Response
*/
public static function response()
{
if (static::$response === null) {
static::$response = new Response(static::request());
}
/**
* Get the response object
*
* @return \Pecee\Http\Response
*/
public static function response()
{
return static::router()->getResponse();
}
return static::$response;
}
/**
* Returns the router instance
*
* @return RouterBase
*/
public static function router()
{
return RouterBase::getInstance();
}
/**
* Returns the router instance
*
* @return Router
*/
public static function router()
{
return Router::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;
/**
* Prepends the default namespace to all new routes added.
*
* @param IRoute $route
* @return IRoute
*/
protected static function addDefaultNamespace(IRoute $route)
{
if (static::$defaultNamespace !== null) {
$namespace = static::$defaultNamespace;
if ($route->getNamespace() !== null) {
$namespace .= '\\' . $route->getNamespace();
}
if ($route->getNamespace() !== null) {
$namespace .= '\\' . $route->getNamespace();
}
$route->setDefaultNamespace($namespace);
}
$route->setDefaultNamespace($namespace);
}
return $route;
}
return $route;
}
}
+1 -1
View File
@@ -6,7 +6,7 @@ use Pecee\Http\Request;
class DummyMiddleware implements IMiddleware
{
public function handle(Request $request, \Pecee\SimpleRouter\RouterEntry &$route = null)
public function handle(Request $request, \Pecee\SimpleRouter\Route\ILoadableRoute &$route)
{
throw new MiddlewareLoadedException('Middleware loaded!');
}
@@ -0,0 +1,4 @@
<?php
class ExceptionHandlerException extends \Exception
{
}
+3 -3
View File
@@ -1,10 +1,10 @@
<?php
class ExceptionHandler implements \Pecee\Handler\IExceptionHandler
class ExceptionHandler implements \Pecee\Handlers\IExceptionHandler
{
public function handleError(\Pecee\Http\Request $request, \Pecee\SimpleRouter\RouterEntry &$route = null, \Exception $error)
public function handleError(\Pecee\Http\Request $request, \Pecee\SimpleRouter\Route\ILoadableRoute &$route = null, \Exception $error)
{
throw $error;
echo $error->getMessage();
}
}
@@ -0,0 +1,13 @@
<?php
class TestExceptionHandlerFirst implements \Pecee\Handlers\IExceptionHandler
{
public function handleError(\Pecee\Http\Request $request, \Pecee\SimpleRouter\Route\ILoadableRoute &$route = null, \Exception $error)
{
echo 'ExceptionHandler 1 loaded' . chr(10);
$request->setUri('/');
return $request;
}
}
@@ -0,0 +1,13 @@
<?php
class TestExceptionHandlerSecond implements \Pecee\Handlers\IExceptionHandler
{
public function handleError(\Pecee\Http\Request $request, \Pecee\SimpleRouter\Route\ILoadableRoute &$route = null, \Exception $error)
{
echo 'ExceptionHandler 2 loaded' . chr(10);
$request->setUri('/');
return $request;
}
}
@@ -0,0 +1,12 @@
<?php
class TestExceptionHandlerThird implements \Pecee\Handlers\IExceptionHandler
{
public function handleError(\Pecee\Http\Request $request, \Pecee\SimpleRouter\Route\ILoadableRoute &$route = null, \Exception $error)
{
echo 'ExceptionHandler 3 loaded' . chr(10);
throw new ExceptionHandlerException('All good!', 666);
}
}
+58 -55
View File
@@ -7,86 +7,89 @@ use Pecee\SimpleRouter\SimpleRouter as SimpleRouter;
class GroupTest extends PHPUnit_Framework_TestCase
{
protected $result;
protected $result;
public function testGroupLoad()
{
$this->result = false;
public function testGroupLoad()
{
$this->result = false;
SimpleRouter::group(['prefix' => '/group'], function () {
$this->result = true;
});
SimpleRouter::group(['prefix' => '/group'], function () {
$this->result = true;
});
try {
SimpleRouter::start();
} catch (Exception $e) {
// ignore RouteNotFound exception
}
try {
SimpleRouter::start();
} catch (Exception $e) {
// ignore RouteNotFound exception
}
$this->assertTrue($this->result);
}
$this->assertTrue($this->result);
}
public function testNestedGroup()
{
public function testNestedGroup()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setUri('/api/v1/test');
SimpleRouter::request()->setMethod('get');
SimpleRouter::router()->reset();
SimpleRouter::request()->setUri('/api/v1/test');
SimpleRouter::request()->setMethod('get');
SimpleRouter::group(['prefix' => '/api'], function () {
SimpleRouter::group(['prefix' => '/api'], function () {
SimpleRouter::group(['prefix' => '/v1'], function () {
SimpleRouter::get('/test', 'DummyController@start');
});
SimpleRouter::group(['prefix' => '/v1'], function () {
SimpleRouter::get('/test', 'DummyController@start');
});
});
});
SimpleRouter::start();
}
SimpleRouter::start();
}
public function testManyRoutes()
{
public function testManyRoutes()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setUri('/my/match');
SimpleRouter::request()->setMethod('get');
SimpleRouter::router()->reset();
SimpleRouter::request()->setUri('/my/match');
SimpleRouter::request()->setMethod('get');
SimpleRouter::group(['prefix' => '/api'], function () {
SimpleRouter::group(['prefix' => '/api'], function () {
SimpleRouter::group(['prefix' => '/v1'], function () {
SimpleRouter::get('/test', 'DummyController@start');
});
SimpleRouter::group(['prefix' => '/v1'], function () {
SimpleRouter::get('/test', 'DummyController@start');
});
});
});
SimpleRouter::get('/my/match', 'DummyController@start');
SimpleRouter::get('/my/match', 'DummyController@start');
SimpleRouter::group(['prefix' => '/service'], function () {
SimpleRouter::group(['prefix' => '/service'], function () {
SimpleRouter::group(['prefix' => '/v1'], function () {
SimpleRouter::get('/no-match', 'DummyController@start');
});
SimpleRouter::group(['prefix' => '/v1'], function () {
SimpleRouter::get('/no-match', 'DummyController@start');
});
});
});
SimpleRouter::start();
}
SimpleRouter::start();
}
public function testUrls()
{
public function testUrls()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setUri('/my/fancy/url/1');
SimpleRouter::request()->setMethod('get');
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');
// Test array name
SimpleRouter::get('/my/fancy/url/1', 'DummyController@start', ['as' => 'fancy1']);
SimpleRouter::start();
// Test method name
SimpleRouter::get('/my/fancy/url/2', 'DummyController@start')->setName('fancy2');
$this->assertTrue((SimpleRouter::getRoute('fancy1') === '/my/fancy/url/1/'));
$this->assertTrue((SimpleRouter::getRoute('fancy2') === '/my/fancy/url/2/'));
SimpleRouter::start();
}
$this->assertEquals('/my/fancy/url/1/', SimpleRouter::getUrl('fancy1'));
$this->assertEquals('/my/fancy/url/2/', SimpleRouter::getUrl('fancy2'));
}
}
+25 -16
View File
@@ -8,25 +8,34 @@ use Pecee\SimpleRouter\SimpleRouter as SimpleRouter;
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()
{
$this->setExpectedException('MiddlewareLoadedException');
SimpleRouter::group(['exceptionHandler' => 'ExceptionHandler'], function () {
SimpleRouter::get('/my/test/url', 'DummyController@start', ['middleware' => 'DummyMiddleware']);
});
SimpleRouter::router()->reset();
SimpleRouter::request()->setMethod('get');
SimpleRouter::request()->setUri('/my/test/url');
$found = false;
SimpleRouter::group(['exceptionHandler' => 'ExceptionHandler'], function () {
SimpleRouter::get('/my/test/url', 'DummyController@start', ['middleware' => 'DummyMiddleware']);
});
try {
SimpleRouter::start();
} catch (\Exception $e) {
$found = ($e instanceof MiddlewareLoadedException);
}
SimpleRouter::start();
}
$this->assertTrue($found);
}
public function testNestedMiddlewareLoad()
{
$this->setExpectedException('MiddlewareLoadedException');
SimpleRouter::router()->reset();
SimpleRouter::request()->setMethod('get');
SimpleRouter::request()->setUri('/my/test/url');
SimpleRouter::group(['exceptionHandler' => 'ExceptionHandler', 'middleware' => 'DummyMiddleware'], function () {
SimpleRouter::get('/my/test/url', 'DummyController@start');
});
SimpleRouter::start();
}
}
+133 -101
View File
@@ -2,139 +2,171 @@
require_once 'Dummy/DummyMiddleware.php';
require_once 'Dummy/DummyController.php';
require_once 'Dummy/Handler/ExceptionHandler.php';
require_once 'Dummy/Exceptions/ExceptionHandlerException.php';
require_once 'Dummy/Handler/TestExceptionHandlerFirst.php';
require_once 'Dummy/Handler/TestExceptionHandlerSecond.php';
require_once 'Dummy/Handler/TestExceptionHandlerThird.php';
use Pecee\SimpleRouter\Exceptions\NotFoundHttpException as NotFoundHttpException;
use Pecee\SimpleRouter\SimpleRouter as SimpleRouter;
class RouterRouteTest extends PHPUnit_Framework_TestCase
{
protected $result = false;
protected $result = false;
public function testNotFound()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setMethod('get');
SimpleRouter::request()->setUri('/test-param1-param2');
public function testMultiParam()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setMethod('get');
SimpleRouter::request()->setUri('/test-param1-param2');
SimpleRouter::group(['exceptionHandler' => 'ExceptionHandler'], function () {
SimpleRouter::get('/non-existing-path', 'DummyController@start');
});
SimpleRouter::get('/test-{param1}-{param2}', function($param1, $param2) {
$found = false;
if($param1 === 'param1' && $param2 === 'param2') {
$this->result = true;
}
try {
SimpleRouter::start();
} catch (\Exception $e) {
$found = ($e instanceof \Pecee\Exception\RouterException && $e->getCode() == 404);
}
});
$this->assertTrue($found);
}
SimpleRouter::start();
public function testGet()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setUri('/my/test/url');
SimpleRouter::request()->setMethod('get');
$this->assertTrue($this->result);
SimpleRouter::get('/my/test/url', 'DummyController@start');
SimpleRouter::start();
}
}
public function testPost()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setUri('/my/test/url');
SimpleRouter::request()->setMethod('post');
/**
* Redirects to another route through 3 exception handlers.
*
* You will see "ExceptionHandler 1 loaded" 2 times. This happen because
* the exceptionhandler is asking the router to reload.
*
* That means that the exceptionhandler is loaded again, but this time
* the router ignores the same rewrite-route to avoid loop - loads
* the second which have same behavior and is also ignored before
* throwing the final Exception in ExceptionHandler 3.
*
* So this tests:
* 1. If ExceptionHandlers loads
* 2. If ExceptionHandlers load in the correct order
* 3. If ExceptionHandlers can rewrite the page on error
* 4. If the router can avoid redirect-loop due to developer has started loop.
* 5. And finally if we reaches the last exception-handler and that the correct
* exception-type is being thrown.
*/
public function testNotFound()
{
$this->setExpectedException('ExceptionHandlerException');
SimpleRouter::post('/my/test/url', 'DummyController@start');
SimpleRouter::start();
}
SimpleRouter::router()->reset();
SimpleRouter::request()->setMethod('get');
SimpleRouter::request()->setUri('/test-param1-param2');
public function testPut()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setUri('/my/test/url');
SimpleRouter::request()->setMethod('put');
SimpleRouter::group(['exceptionHandler' => ['TestExceptionHandlerFirst', 'TestExceptionHandlerSecond']], function () {
SimpleRouter::put('/my/test/url', 'DummyController@start');
SimpleRouter::start();
}
SimpleRouter::group(['exceptionHandler' => 'TestExceptionHandlerThird'], function () {
public function testDelete()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setUri('/my/test/url');
SimpleRouter::request()->setMethod('delete');
SimpleRouter::get('/non-existing-path', 'DummyController@start');
SimpleRouter::delete('/my/test/url', 'DummyController@start');
SimpleRouter::start();
}
});
});
public function testMethodNotAllowed()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setUri('/my/test/url');
SimpleRouter::request()->setMethod('post');
SimpleRouter::start();
}
SimpleRouter::get('/my/test/url', 'DummyController@start');
public function testGet()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setUri('/my/test/url');
SimpleRouter::request()->setMethod('get');
try {
SimpleRouter::start();
} catch (\Exception $e) {
$this->assertEquals(403, $e->getCode());
}
}
SimpleRouter::get('/my/test/url', 'DummyController@start');
SimpleRouter::start();
}
public function testSimpleParam()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setMethod('get');
SimpleRouter::request()->setUri('/test-param1');
public function testPost()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setUri('/my/test/url');
SimpleRouter::request()->setMethod('post');
SimpleRouter::get('/test-{param1}', 'DummyController@param');
SimpleRouter::start();
}
SimpleRouter::post('/my/test/url', 'DummyController@start');
SimpleRouter::start();
}
public function testMultiParam()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setMethod('get');
SimpleRouter::request()->setUri('/test-param1-param2');
public function testPut()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setUri('/my/test/url');
SimpleRouter::request()->setMethod('put');
SimpleRouter::get('/test-{param1}-{param2}', 'DummyController@param');
SimpleRouter::start();
}
SimpleRouter::put('/my/test/url', 'DummyController@start');
SimpleRouter::start();
}
public function testPathParamRegex()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setMethod('get');
SimpleRouter::request()->setUri('/test/path/123123');
public function testDelete()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setUri('/my/test/url');
SimpleRouter::request()->setMethod('delete');
SimpleRouter::get('/test/path/{myParam}', 'DummyController@param', ['where' => ['myParam' => '([0-9]+)']]);
SimpleRouter::start();
}
SimpleRouter::delete('/my/test/url', 'DummyController@start');
SimpleRouter::start();
}
public function testDomainRoute()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setMethod('get');
SimpleRouter::request()->setUri('/test');
SimpleRouter::request()->setHost('hello.world.com');
public function testMethodNotAllowed()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setUri('/my/test/url');
SimpleRouter::request()->setMethod('post');
$this->result = false;
SimpleRouter::get('/my/test/url', 'DummyController@start');
SimpleRouter::group(['domain' => '{subdomain}.world.com'], function () {
SimpleRouter::get('/test', function ($subdomain = null) {
$this->result = ($subdomain === 'hello');
});
});
try {
SimpleRouter::start();
} catch (\Exception $e) {
$this->assertEquals(403, $e->getCode());
}
}
SimpleRouter::start();
public function testSimpleParam()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setMethod('get');
SimpleRouter::request()->setUri('/test-param1');
$this->assertTrue($this->result);
SimpleRouter::get('/test-{param1}', 'DummyController@param');
SimpleRouter::start();
}
}
public function testPathParamRegex()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setMethod('get');
SimpleRouter::request()->setUri('/test/path/123123');
SimpleRouter::get('/test/path/{myParam}', 'DummyController@param', ['where' => ['myParam' => '([0-9]+)']]);
SimpleRouter::start();
}
public function testDomainRoute()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setMethod('get');
SimpleRouter::request()->setUri('/test');
SimpleRouter::request()->setHost('hello.world.com');
$this->result = false;
SimpleRouter::group(['domain' => '{subdomain}.world.com'], function () {
SimpleRouter::get('/test', function ($subdomain = null) {
$this->result = ($subdomain === 'hello');
});
});
SimpleRouter::start();
$this->assertTrue($this->result);
}
}
+79 -48
View File
@@ -8,76 +8,107 @@ use Pecee\SimpleRouter\SimpleRouter as SimpleRouter;
class RouterUrlTest extends PHPUnit_Framework_TestCase
{
protected $result = false;
protected $result = false;
protected function getUrl($controller = null, $parameters = null, $getParams = null) {
return SimpleRouter::getRoute($controller, $parameters, $getParams);
}
protected function getUrl($name = null, $parameters = null, array $getParams = [])
{
return SimpleRouter::getUrl($name, $parameters, $getParams);
}
public function testUrls()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setMethod('get');
SimpleRouter::request()->setUri('/');
public function testUrls()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setMethod('get');
SimpleRouter::request()->setUri('/');
// Match normal route on alias
SimpleRouter::get('/', 'DummyController@silent', ['as' => 'home']);
// Match normal route on alias
SimpleRouter::get('/', 'DummyController@silent', ['as' => 'home']);
SimpleRouter::group(['prefix' => '/admin'], function() {
SimpleRouter::get('/about', 'DummyController@about');
// Match route with prefix on alias
SimpleRouter::get('/{id?}', 'DummyController@start', ['as' => 'admin.home']);
SimpleRouter::group(['prefix' => '/admin', 'as' => 'admin'], function () {
// Match controller with prefix and alias
SimpleRouter::controller('/users', 'DummyController', ['as' => 'admin.users']);
// Match route with prefix on alias
SimpleRouter::get('/{id?}', 'DummyController@start', ['as' => 'home']);
// Match controller with prefix and NO alias
SimpleRouter::controller('/pages', 'DummyController');
// Match controller with prefix and alias
SimpleRouter::controller('/users', 'DummyController', ['as' => '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();
SimpleRouter::group(['prefix' => 'api', 'as' => 'api'], function () {
// Should match /
$this->assertEquals($this->getUrl('home'), '/');
// Match resource controller
SimpleRouter::resource('phones', 'DummyController');
// Should match /admin/
$this->assertEquals($this->getUrl('DummyController@start'), '/admin/');
});
// Should match /admin/
$this->assertEquals($this->getUrl('admin.home'), '/admin/');
SimpleRouter::controller('gadgets', 'DummyController', ['names' => ['getIphoneInfo' => 'iphone']]);
// Should match /admin/2/
$this->assertEquals($this->getUrl('admin.home', ['id' => 2]), '/admin/2/');
// Match controller with no prefix and no alias
SimpleRouter::controller('/cats', 'CatsController');
// Should match /admin/users/
$this->assertEquals($this->getUrl('admin.users'), '/admin/users/');
// Pretend to load page
SimpleRouter::start();
// Should match /admin/users/home/
$this->assertEquals($this->getUrl('admin.users@home'), '/admin/users/home/');
$this->assertEquals('/gadgets/iphoneinfo/', $this->getUrl('gadgets.iphone'));
// Should match /cats/
$this->assertEquals($this->getUrl('CatsController'), '/cats/');
$this->assertEquals('/api/phones/create/', $this->getUrl('api.phones.create'));
// Should match /cats/view/
$this->assertEquals($this->getUrl('CatsController', 'view'), '/cats/view/');
// Should match /
$this->assertEquals('/', $this->getUrl('home'));
// Should match /cats/view/
$this->assertEquals($this->getUrl('CatsController', ['view']), '/cats/view/');
// Should match /about/
$this->assertEquals('/about/', $this->getUrl('DummyController@about'));
// Should match /cats/view/666
$this->assertEquals($this->getUrl('CatsController@view', ['666']), '/cats/view/666/');
// Should match /admin/
$this->assertEquals('/admin/', $this->getUrl('DummyController@start'));
// Should match /funny/man/
$this->assertEquals($this->getUrl('/funny/man'), '/funny/man/');
// Should match /admin/
$this->assertEquals('/admin/', $this->getUrl('admin.home'));
// Should match /?jackdaniels=true
$this->assertEquals($this->getUrl('home', null, ['jackdaniels' => 'true']), '/?jackdaniels=true');
// Should match /admin/2/
$this->assertEquals('/admin/2/', $this->getUrl('admin.home', ['id' => 2]));
}
// Should match /admin/users/
$this->assertEquals('/admin/users/', $this->getUrl('admin.users'));
// Should match /admin/users/home/
$this->assertEquals('/admin/users/home/', $this->getUrl('admin.users@home'));
// Should match /cats/
$this->assertEquals('/cats/', $this->getUrl('CatsController'));
// Should match /cats/view/
$this->assertEquals('/cats/view/', $this->getUrl('CatsController', 'view'));
// Should match /cats/view/
//$this->assertEquals('/cats/view/', $this->getUrl('CatsController', ['view']));
// Should match /cats/view/666
$this->assertEquals('/cats/view/666/', $this->getUrl('CatsController@getView', ['666']));
// Should match /funny/man/
$this->assertEquals('/funny/man/', $this->getUrl('/funny/man'));
// Should match /?jackdaniels=true&cola=yeah
$this->assertEquals('/?jackdaniels=true&cola=yeah', $this->getUrl('home', null, ['jackdaniels' => 'true', 'cola' => 'yeah']));
}
public function testRegEx()
{
SimpleRouter::router()->reset();
SimpleRouter::request()->setMethod('get');
SimpleRouter::request()->setUri('/my/custom-path');
SimpleRouter::get('/my/{path}', 'DummyController@start')->where(['path' => '[a-zA-Z\-]+']);
SimpleRouter::start();
}
}