diff --git a/README.md b/README.md index 8034920..a592726 100644 --- a/README.md +++ b/README.md @@ -57,12 +57,14 @@ Simple, fast and yet powerful PHP router that is easy to get integrated and in a - [Using custom exception handlers](#using-custom-exception-handlers) - [Urls](#urls) + - [Get the current url](#get-the-current-url) - [Get by name (single route)](#get-by-name-single-route) - [Get by name (controller route)](#get-by-name-controller-route) - [Get by class](#get-by-class) - - [Get by custom names for methods on a controller/resource route](#using-custom-names-for-methods-on-a-controllerresource-route) + - [Using custom names for methods on a controller/resource route](#using-custom-names-for-methods-on-a-controllerresource-route) - [Getting REST/resource controller urls](#getting-restresource-controller-urls) - - [Get the current url](#get-the-current-url) + - [Manipulating url](#manipulating-url) + - [Useful url tricks](#useful-url-tricks) - [Input & parameters](#input--parameters) - [Using the Input class to manage parameters](#using-the-input-class-to-manage-parameters) @@ -70,6 +72,11 @@ Simple, fast and yet powerful PHP router that is easy to get integrated and in a - [Get parameter object](#get-parameter-object) - [Managing files](#managing-files) - [Get all parameters](#get-all-parameters) + +- [Events](#events) + - [Available events](#available-events) + - [Registering new event](#registering-new-event) + - [Custom EventHandlers](#custom-eventhandlers) - [Advanced](#advanced) - [Url rewriting](#url-rewriting) @@ -298,7 +305,12 @@ We recommend that you add these helper functions to your project. These will all To implement the functions below, simply copy the code to a new file and require the file before initializing the router or copy the `helpers.php` we've included in this library. ```php +getInputHandler()->get($index, $defaultValue, $methods); + + if ($defaultValue !== null) { + return request()->getInputHandler()->getValue($index, $defaultValue, $methods); + } + + return request()->getInputHandler()->get($index, $methods); } return request()->getInputHandler(); } -function redirect($url, $code = null) +/** + * @param string $url + * @param int|null $code + */ +function redirect(string $url, ?int $code = null): void { if ($code !== null) { response()->httpCode($code); @@ -371,9 +388,8 @@ function redirect($url, $code = null) /** * Get current csrf-token * @return string|null - * @throws \Pecee\Http\Exceptions\MalformedUrlException */ -function csrf_token() +function csrf_token(): ?string { $baseVerifier = Router::router()->getCsrfVerifier(); if ($baseVerifier !== null) { @@ -805,7 +821,7 @@ class SessionTokenProvider implements ITokenProvider /** * Refresh existing token */ - public function refresh() + public function refresh(): void { // Implement your own functionality here... } @@ -816,7 +832,18 @@ class SessionTokenProvider implements ITokenProvider * @param string $token * @return bool */ - public function validate($token) + public function validate($token): bool + { + // Implement your own functionality here... + } + + /** + * Get token token + * + * @param string|null $defaultValue + * @return string|null + */ + public function getToken(?string $defaultValue = null): ?string { // Implement your own functionality here... } @@ -847,10 +874,11 @@ namespace Demo\Middlewares; use Pecee\Http\Middleware\IMiddleware; use Pecee\Http\Request; -class CustomMiddleware implements Middleware { - - public function handle(Request $request) { +class CustomMiddleware implements IMiddleware { + public function handle(Request $request): void + { + // Authenticate user, will be available using request()->user $request->user = User::authenticate(); @@ -882,9 +910,11 @@ The code should be placed in the file that contains your routes. Router::get('/not-found', 'PageController@notFound'); Router::error(function(Request $request, \Exception $exception) { - if($exception instanceof NotFoundHttpException && $exception->getCode() == 404) { + + if($exception instanceof NotFoundHttpException && $exception->getCode() === 404) { response()->redirect('/not-found'); } + }); ``` @@ -895,13 +925,13 @@ This is a basic example of an ExceptionHandler implementation (please see "[Easi ```php namespace Demo\Handlers; -use Pecee\Handlers\IExceptionHandler; use Pecee\Http\Request; +use Pecee\SimpleRouter\Handlers\IExceptionHandler; use Pecee\SimpleRouter\Exceptions\NotFoundHttpException; class CustomExceptionHandler implements IExceptionHandler { - public function handleError(Request $request, \Exception $error) + public function handleError(Request $request, \Exception $error): void { /* You can use the exception handler to format errors depending on the request and type. */ @@ -937,17 +967,33 @@ class CustomExceptionHandler implements IExceptionHandler By default all controller and resource routes will use a simplified version of their url as name. +You easily use the `url()` shortcut helper function to retrieve urls for your routes or manipulate the current url. + +`url()` will return a `Url` object which will return a `string` when rendered, so it can be used safely in templates etc. but +contains all the useful helpers methods in the `Url` class like `contains`, `indexOf` etc. +Check the [Useful url tricks](#useful-url-tricks) below. + +### Get the current url + +It has never been easier to get and/or manipulate the current url. + +The example below shows you how to get the current url: + +```php +# output: /current-url +url(); +``` + ### Get by name (single route) ```php SimpleRouter::get('/product-view/{id}', 'ProductsController@show', ['as' => 'product']); +# output: /product-view/22/?category=shoes url('product', ['id' => 22], ['category' => 'shoes']); -url('product', null, ['category' => 'shoes']); -# output -# /product-view/22/?category=shoes -# /product-view/?category=shoes +# output: /product-view/?category=shoes +url('product', null, ['category' => 'shoes']); ``` ### Get by name (controller route) @@ -955,14 +1001,14 @@ url('product', null, ['category' => 'shoes']); ```php SimpleRouter::controller('/images', ImagesController::class, ['as' => 'picture']); +# output: /images/view/?category=shows url('picture@getView', null, ['category' => 'shoes']); -url('picture', 'getView', ['category' => 'shoes']); -url('picture', 'view'); -# output -# /images/view/?category=shows -# /images/view/?category=shows -# /images/view/ +# output: /images/view/?category=shows +url('picture', 'getView', ['category' => 'shoes']); + +# output: /images/view/ +url('picture', 'view'); ``` ### Get by class @@ -971,12 +1017,11 @@ url('picture', 'view'); SimpleRouter::get('/product-view/{id}', 'ProductsController@show', ['as' => 'product']); SimpleRouter::controller('/images', 'ImagesController'); +# output: /product-view/22/?category=shoes url('ProductsController@show', ['id' => 22], ['category' => 'shoes']); -url('ImagesController@getImage', null, ['id' => 22]); -# output -# /product-view/22/?category=shoes -# /images/image/?id=22 +# output: /images/image/?id=22 +url('ImagesController@getImage', null, ['id' => 22]); ``` ### Using custom names for methods on a controller/resource route @@ -995,30 +1040,67 @@ url('gadgets.iphone'); ```php SimpleRouter::resource('/phones', PhonesController::class); +# output: /phones/ url('phones'); + +# output: /phones/ url('phones.index'); + +# output: /phones/create/ url('phones.create'); + +# output: /phones/edit/ url('phones.edit'); - -// etc.. - -# output -# /phones/ -# /phones/create/ -# /phones/edit/ ``` -### Get the current url +### Manipulating url + +You can easily manipulate the query-strings, by adding your get param arguments. ```php -url(); -url(null, null, ['q' => 'cars']); +# output: /current-url?q=cars -# output -# /CURRENT-URL/ -# /CURRENT-URL/?q=cars +url(null, null, ['q' => 'cars']); ``` +You can remove a query-string parameter by setting the value to `null`. + +The example below will remove any query-string parameter named `q` from the url but keep all others query-string parameters: + +```php +$url = url()->removeParam('q'); +``` + +For more information please check the [Useful url tricks](#useful-url-tricks) section of the documentation. + +### Useful url tricks + +Calling `url` will always return a `Url` object. Upon rendered it will return a `string` of the relative `url`, so it's safe to use in templates etc. + +However this allow us to use the useful methods on the `Url` object like `indexOf` and `contains` or retrieve specific parts of the url like the path, querystring parameters, host etc. You can also manipulate the url like removing- or adding parameters, changing host and more. + +In the example below, we check if the current url contains the `/api` part. + +```php +if(url()->contains('/api')) { + + // ... do stuff + +} +``` + +As mentioned earlier, you can also use the `Url` object to show specific parts of the url or control what part of the url you want. + +```php +# Grab the query-string parameter id from the current-url. +$id = url()->getParam('id'); + +# Get the absolute url for the current url. +$absoluteUrl = url()->getAbsoluteUrl(); +``` + +For more available methods please check the `Pecee\Http\Url` class. + # Input & parameters ## Using the Input class to manage parameters @@ -1062,7 +1144,7 @@ $object = input()->getObject($index, $defaultValue = null, $methods = null); * $defaultValue is returned if the value is empty. */ -$id = input()->get($index, $defaultValue, $method); +$id = input()->getValue($index, $defaultValue, $method); # -- shortcut to above -- @@ -1079,6 +1161,10 @@ $object = input($index, $defaultValue, 'file'); $object = input()->findGet($index, $defaultValue); $object = input()->findPost($index, $defaultValue); $object = input()->findFile($index, $defaultValue); + +# -- get the full object -- + +$object = input()->get($index, 'post', 'get'); ``` ### Managing files @@ -1143,6 +1229,134 @@ $siteId = input('site_id', 2, ['post', 'get']); --- +# Events + +This section will help you understand how to register your own callbacks to events in the router. +It will also cover the basics of event-handlers; how to use the handlers provided with the router and how to create your own custom event-handlers. + +## Available events + +This section contains all available events that can be registered using the `EventHandler`. + +| Name | Description | +| ------------- |------------- | +| `EventHandler::EVENT_ALL` | Fires when a event is triggered. | +| `EventHandler::EVENT_INIT` | Fires when router is initializing and before routes are loaded. | +| `EventHandler::EVENT_LOAD` | Fires when all routes has been loaded and rendered, just before the output is returned. | +| `EventHandler::EVENT_REWRITE` | Fires when a url-rewrite is and just before the routes are re-initialized. | +| `EventHandler::EVENT_BOOT` | Fires when the router is booting. This happens just before boot-managers are rendered and before any routes has been loaded. | +| `EventHandler::EVENT_RENDER_BOOTMANAGER` | Fires before a boot-manager is rendered. | +| `EventHandler::EVENT_LOAD_ROUTES` | Fires when the router is about to load all routes. | +| `EventHandler::EVENT_FIND_ROUTE` | Fires whenever the `findRoute` method is called within the `Router`. This usually happens when the router tries to find routes that contains a certain url, usually after the `EventHandler::EVENT_GET_URL` event. | +| `EventHandler::EVENT_GET_URL` | Fires whenever the `Router::getUrl` method or `url`-helper function is called and the router tries to find the route. | +| `EventHandler::EVENT_MATCH_ROUTE` | Fires when a route is matched and valid (correct request-type etc). and before the route is rendered. | +| `EventHandler::EVENT_RENDER_ROUTE` | Fires before a route is rendered. | +| `EventHandler::EVENT_LOAD_EXCEPTIONS` | Fires when the router is loading exception-handlers. | +| `EventHandler::EVENT_RENDER_EXCEPTION` | Fires before the router is rendering a exception-handler. | +| `EventHandler::EVENT_RENDER_MIDDLEWARE` | Fires before a middleware is rendered. | +| `EventHandler::EVENT_RENDER_CSRF` | Fires before the CSRF-verifier is rendered. | + +## Registering new event + +To register a new event you need to create a new instance of the `EventHandler` object. On this object you can add as many callbacks as you like by calling the `registerEvent` method. + +When you've registered events, make sure to add it to the router by calling +`SimpleRouter::addEventHandler()`. We recommend that you add your event-handlers within your `routes.php`. + +**Example:** + +```php +use Pecee\SimpleRouter\Handlers\EventHandler; +use Pecee\SimpleRouter\Event\EventArgument; + +// --- your routes goes here --- + +$eventHandler = new EventHandler(); +$eventHandler->register(EventHandler::EVENT_RENDER_ROUTE, function(EventArgument $argument) { + + // Fires when route is rendered ... + +}); + +SimpleRouter::addEventHandler($eventHandler); + +``` + +## Custom EventHandlers + +`EventHandler` is the class that manages events and must inherit from the `IEventHandler` interface. The handler knows how to handle events for the given handler-type. + +Most of the time the basic `\Pecee\SimpleRouter\Handler\EventHandler` class will be more than enough for most people as you simply register an event which fires when triggered. + +Let's go over how to create your very own event-handler class. + +Below is a basic example of a custom event-handler called `DatabaseDebugHandler`. The idea of the sample below is to logs all events to the database when triggered. Hopefully it will be enough to give you an idea on how the event-handlers work. + +```php +namespace Demo\Handlers; + +use Pecee\SimpleRouter\Event\EventArgument; +use Pecee\SimpleRouter\Router; + +class DatabaseDebugHandler implements IEventHandler +{ + + /** + * Debug callback + * @var \Closure + */ + protected $callback; + + public function __construct() + { + $this->callback = function (EventArgument $argument) { + // todo: store log in database + }; + } + + /** + * Get events. + * + * @param string|null $name Filter events by name. + * @return array + */ + public function getEvents(?string $name): array + { + return [ + $name => [ + $this->callback, + ], + ]; + } + + /** + * Fires any events registered with given event-name + * + * @param Router $router Router instance + * @param string $name Event name + * @param array ...$eventArgs Event arguments + */ + public function fireEvents(Router $router, string $name, ...$eventArgs): void + { + $callback = $this->callback; + $callback(new EventArgument($router, $eventArgs)); + } + + /** + * Set debug callback + * + * @param \Closure $event + */ + public function setCallback(\Closure $event): void + { + $this->callback = $event; + } + +} +``` + +--- + # Advanced ## Url rewriting @@ -1173,10 +1387,19 @@ To interfere with the router, we create a class that implements the ```IRouterBo ```php use Pecee\Http\Request; use Pecee\SimpleRouter\IRouterBootManager; +use Pecee\SimpleRouter\Router; -class CustomRouterRules implement IRouterBootManager { +class CustomRouterRules implement IRouterBootManager +{ - public function boot(Request $request) { + /** + * Called when router is booting and before the routes is loaded. + * + * @param \Pecee\SimpleRouter\Router $router + * @param \Pecee\Http\Request $request + */ + public function boot(\Pecee\SimpleRouter\Router $router, \Pecee\Http\Request $request): void + { $rewriteRules = [ '/my-cat-is-beatiful' => '/article/view/1', @@ -1449,196 +1672,10 @@ public function show($username) { ### Debug info ```php -array ( - 'url' => - Pecee\Http\Url::__set_state(array( - 'originalUrl' => NULL, - 'data' => - array ( - 'scheme' => NULL, - 'host' => NULL, - 'port' => NULL, - 'user' => NULL, - 'pass' => NULL, - 'path' => NULL, - 'query' => NULL, - 'fragment' => NULL, - ), - )), - 'method' => '', - 'host' => NULL, - 'loaded_routes' => - array ( - ), - 'all_routes' => - array ( - 0 => - Pecee\SimpleRouter\Route\RouteUrl::__set_state(array( - 'url' => '/user/{name}/', - 'name' => NULL, - 'regex' => NULL, - 'filterEmptyParams' => true, - 'defaultParameterRegex' => NULL, - 'paramModifiers' => '{}', - 'paramOptionalSymbol' => '?', - 'urlRegex' => '/^%s\\/?$/u', - 'group' => NULL, - 'parent' => NULL, - 'callback' => 'UserController@show', - 'defaultNamespace' => NULL, - 'namespace' => NULL, - 'requestMethods' => - array ( - 0 => 'get', - ), - 'where' => - array ( - 'name' => '[\\w]+', - ), - 'parameters' => - array ( - 'name' => NULL, - ), - 'originalParameters' => - array ( - ), - 'middlewares' => - array ( - ), - )), - ), - 'boot_managers' => - array ( - ), - 'csrf_verifier' => NULL, - 'log' => - array ( - 0 => - array ( - 'message' => 'Started routing request (rewrite: no)', - 'time' => '0.0000069141', - 'trace' => - array ( - 'file' => 'E:\\Workspace\\simple-php-router\\src\\Pecee\\SimpleRouter\\SimpleRouter.php', - 'line' => 57, - 'function' => 'routeRequest', - 'class' => 'Pecee\\SimpleRouter\\Router', - 'type' => '->', - ), - ), - 1 => - array ( - 'message' => 'Loading routes', - 'time' => '0.0036418438', - 'trace' => - array ( - 'file' => 'E:\\Workspace\\simple-php-router\\src\\Pecee\\SimpleRouter\\Router.php', - 'line' => 273, - 'function' => 'loadRoutes', - 'class' => 'Pecee\\SimpleRouter\\Router', - 'type' => '->', - ), - ), - 2 => - array ( - 'message' => 'Processing routes', - 'time' => '0.0069010258', - 'trace' => - array ( - 'file' => 'E:\\Workspace\\simple-php-router\\src\\Pecee\\SimpleRouter\\Router.php', - 'line' => 251, - 'function' => 'processRoutes', - 'class' => 'Pecee\\SimpleRouter\\Router', - 'type' => '->', - ), - ), - 3 => - array ( - 'message' => 'Processing route "Pecee\\SimpleRouter\\Route\\RouteUrl"', - 'time' => '0.0099139214', - 'trace' => - array ( - 'file' => 'E:\\Workspace\\simple-php-router\\src\\Pecee\\SimpleRouter\\Router.php', - 'line' => 251, - 'function' => 'processRoutes', - 'class' => 'Pecee\\SimpleRouter\\Router', - 'type' => '->', - ), - ), - 4 => - array ( - 'message' => 'Finished loading routes', - 'time' => '0.0130679607', - 'trace' => - array ( - 'file' => 'E:\\Workspace\\simple-php-router\\src\\Pecee\\SimpleRouter\\Router.php', - 'line' => 273, - 'function' => 'loadRoutes', - 'class' => 'Pecee\\SimpleRouter\\Router', - 'type' => '->', - ), - ), - 5 => - array ( - 'message' => 'Matching route "Pecee\\SimpleRouter\\Route\\RouteUrl"', - 'time' => '0.0160858631', - 'trace' => - array ( - 'file' => 'E:\\Workspace\\simple-php-router\\src\\Pecee\\SimpleRouter\\SimpleRouter.php', - 'line' => 57, - 'function' => 'routeRequest', - 'class' => 'Pecee\\SimpleRouter\\Router', - 'type' => '->', - ), - ), - 6 => - array ( - 'message' => 'Route not found: "/"', - 'time' => '0.0193598270', - 'trace' => - array ( - 'file' => 'E:\\Workspace\\simple-php-router\\src\\Pecee\\SimpleRouter\\SimpleRouter.php', - 'line' => 57, - 'function' => 'routeRequest', - 'class' => 'Pecee\\SimpleRouter\\Router', - 'type' => '->', - ), - ), - 7 => - array ( - 'message' => 'Starting exception handling for "Pecee\\SimpleRouter\\Exceptions\\NotFoundHttpException"', - 'time' => '0.0229449272', - 'trace' => - array ( - 'file' => 'E:\\Workspace\\simple-php-router\\src\\Pecee\\SimpleRouter\\Router.php', - 'line' => 345, - 'function' => 'handleException', - 'class' => 'Pecee\\SimpleRouter\\Router', - 'type' => '->', - ), - ), - 8 => - array ( - 'message' => 'Finished exception handling - exception not handled, throwing', - 'time' => '0.0258929729', - 'trace' => - array ( - 'file' => 'E:\\Workspace\\simple-php-router\\src\\Pecee\\SimpleRouter\\Router.php', - 'line' => 345, - 'function' => 'handleException', - 'class' => 'Pecee\\SimpleRouter\\Router', - 'type' => '->', - ), - ), - ), - 'router_output' => NULL, - 'library_version' => false, - 'php_version' => '7.2.0', - 'server_params' => - array (), -) -``` +[PASTE YOUR DEBUG-INFO HERE] + +``` Remember that a more detailed issue- description and debug-info might suck to write, but it will help others understand- and resolve your issue without asking for the information. diff --git a/helpers.php b/helpers.php index 2c4d86d..f7234a3 100644 --- a/helpers.php +++ b/helpers.php @@ -1,6 +1,9 @@ getInputHandler()->get($index, $defaultValue, $methods); + + if ($defaultValue !== null) { + return request()->getInputHandler()->getValue($index, $defaultValue, $methods); + } + + return request()->getInputHandler()->get($index, $methods); } return request()->getInputHandler(); } -function redirect($url, $code = null) +/** + * @param string $url + * @param int|null $code + */ +function redirect(string $url, ?int $code = null): void { if ($code !== null) { response()->httpCode($code); @@ -73,9 +81,8 @@ function redirect($url, $code = null) /** * Get current csrf-token * @return string|null - * @throws \Pecee\Http\Exceptions\MalformedUrlException */ -function csrf_token() +function csrf_token(): ?string { $baseVerifier = Router::router()->getCsrfVerifier(); if ($baseVerifier !== null) { diff --git a/src/Pecee/Http/Input/InputHandler.php b/src/Pecee/Http/Input/InputHandler.php index 6fee12a..1f1d0e1 100644 --- a/src/Pecee/Http/Input/InputHandler.php +++ b/src/Pecee/Http/Input/InputHandler.php @@ -87,7 +87,6 @@ class InputHandler } $keys = [$key]; - $files = $this->rearrangeFiles($value['name'], $keys, $value); if (isset($list[$key]) === true) { @@ -212,31 +211,26 @@ class InputHandler * Get input object * * @param string $index - * @param string|null $defaultValue - * @param array|string|null $methods - * @return IInputItem|string + * @param array ...$methods + * @return IInputItem|null */ - public function getObject(string $index, ?string $defaultValue = null, $methods = null) + public function get(string $index, ...$methods) : ?IInputItem { - if ($methods !== null && \is_string($methods) === true) { - $methods = [$methods]; - } - $element = null; - if ($methods === null || \in_array('get', $methods, true) === true) { + if (\count($methods) === 0 || \in_array('get', $methods, true) === true) { $element = $this->findGet($index); } - if (($element === null && $methods === null) || ($methods !== null && \in_array('post', $methods, true) === true)) { + if (($element === null && \count($methods) === 0) || (\count($methods) === 0 && \in_array('post', $methods, true) === true)) { $element = $this->findPost($index); } - if (($element === null && $methods === null) || ($methods !== null && \in_array('file', $methods, true) === true)) { + if (($element === null && \count($methods) === 0) || (\count($methods) === 0 && \in_array('file', $methods, true) === true)) { $element = $this->findFile($index); } - return $element ?? $defaultValue; + return $element; } /** @@ -244,14 +238,14 @@ class InputHandler * * @param string $index * @param string|null $defaultValue - * @param array|string|null $methods - * @return InputItem|string + * @param array ...$methods + * @return string */ - public function get(string $index, ?string $defaultValue = null, $methods = null) + public function getValue(string $index, ?string $defaultValue = null, ...$methods) : ?string { - $input = $this->getObject($index, $defaultValue, $methods); + $input = $this->get($index, $methods); - if ($input instanceof InputItem) { + if ($input !== null) { return (trim($input->getValue()) === '') ? $defaultValue : $input->getValue(); } @@ -262,11 +256,12 @@ class InputHandler * Check if a input-item exist * * @param string $index + * @param array ...$method * @return bool */ - public function exists(string $index): bool + public function exists(string $index, ...$method): bool { - return ($this->getObject($index) !== null); + return $this->get($index, $method) !== null; } /** @@ -276,12 +271,16 @@ class InputHandler */ public function all(array $filter = null): array { - $output = $_GET + $_POST; + $output = $_GET; if ($this->request->getMethod() === 'post') { + // Append POST data + $output += $_POST; + $contents = file_get_contents('php://input'); + // Append any PHP-input json if (strpos(trim($contents), '{') === 0) { $post = json_decode($contents, true); if ($post !== false) { diff --git a/src/Pecee/Http/Middleware/BaseCsrfVerifier.php b/src/Pecee/Http/Middleware/BaseCsrfVerifier.php index 613058a..7116cd7 100644 --- a/src/Pecee/Http/Middleware/BaseCsrfVerifier.php +++ b/src/Pecee/Http/Middleware/BaseCsrfVerifier.php @@ -67,14 +67,13 @@ class BaseCsrfVerifier implements IMiddleware if ($this->skip($request) === false && \in_array($request->getMethod(), ['post', 'put', 'delete'], true) === true) { - $token = $request->getInputHandler()->get(static::POST_KEY, null, 'post'); + $token = $request->getInputHandler()->getValue( + static::POST_KEY, + $request->getHeader(static::HEADER_KEY), + '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->tokenProvider->validate($token) === false) { + if ($this->tokenProvider->validate((string)$token) === false) { throw new TokenMismatchException('Invalid CSRF-token.'); } diff --git a/src/Pecee/Http/Request.php b/src/Pecee/Http/Request.php index 6ae3a38..90397e6 100644 --- a/src/Pecee/Http/Request.php +++ b/src/Pecee/Http/Request.php @@ -2,6 +2,7 @@ namespace Pecee\Http; +use Pecee\Http\Exceptions\MalformedUrlException; use Pecee\Http\Input\InputHandler; use Pecee\SimpleRouter\Route\ILoadableRoute; use Pecee\SimpleRouter\Route\RouteUrl; @@ -9,19 +10,58 @@ use Pecee\SimpleRouter\SimpleRouter; class Request { + /** + * Additional data + * + * @var array + */ private $data = []; + + /** + * Server headers + * @var array + */ protected $headers = []; + + /** + * Request host + * @var string + */ protected $host; + + /** + * Current request url + * @var Url + */ protected $url; + + /** + * Request method + * @var string + */ protected $method; + + /** + * Input handler + * @var InputHandler + */ protected $inputHandler; - protected $hasRewrite = false; + /** + * Defines if request has pending rewrite + * @var bool + */ + protected $hasPendingRewrite = false; /** * @var ILoadableRoute|null */ protected $rewriteRoute; + + /** + * Rewrite url + * @var string|null + */ protected $rewriteUrl; /** @@ -31,7 +71,7 @@ class Request /** * Request constructor. - * @throws \Pecee\Http\Exceptions\MalformedUrlException + * @throws MalformedUrlException */ public function __construct() { @@ -43,10 +83,10 @@ class Request $this->setHost($this->getHeader('http-host')); // Check if special IIS header exist, otherwise use default. - $this->setUrl($this->getHeader('unencoded-url', $this->getHeader('request-uri'))); + $this->setUrl(new Url($this->getHeader('unencoded-url', $this->getHeader('request-uri')))); $this->inputHandler = new InputHandler($this); - $this->method = strtolower($this->inputHandler->get('_method', $this->getHeader('request-method'))); + $this->method = strtolower($this->inputHandler->getValue('_method', $this->getHeader('request-method'))); } public function isSecure(): bool @@ -62,6 +102,16 @@ class Request return $this->url; } + /** + * Copy url object + * + * @return Url + */ + public function getUrlCopy(): Url + { + return clone $this->url; + } + /** * @return string|null */ @@ -205,12 +255,15 @@ class Request } /** - * @param string|Url $url - * @throws \Pecee\Http\Exceptions\MalformedUrlException + * @param Url $url */ - public function setUrl($url): void + public function setUrl(Url $url): void { - $this->url = ($url instanceof Url) ? $url : new Url($url); + $this->url = $url; + + if ($this->url->getHost() === null) { + $this->url->setHost((string)$this->getHost()); + } } /** @@ -237,7 +290,7 @@ class Request */ public function setRewriteRoute(ILoadableRoute $route): self { - $this->hasRewrite = true; + $this->hasPendingRewrite = true; $this->rewriteRoute = SimpleRouter::addDefaultNamespace($route); return $this; @@ -271,7 +324,7 @@ class Request */ public function setRewriteUrl(string $rewriteUrl): self { - $this->hasRewrite = true; + $this->hasPendingRewrite = true; $this->rewriteUrl = rtrim($rewriteUrl, '/') . '/'; return $this; @@ -284,7 +337,7 @@ class Request */ public function setRewriteCallback($callback): self { - $this->hasRewrite = true; + $this->hasPendingRewrite = true; return $this->setRewriteRoute(new RouteUrl($this->getUrl()->getPath(), $callback)); } @@ -339,9 +392,9 @@ class Request * * @return bool */ - public function hasRewrite(): bool + public function hasPendingRewrite(): bool { - return $this->hasRewrite; + return $this->hasPendingRewrite; } /** @@ -350,9 +403,9 @@ class Request * @param bool $boolean * @return Request */ - public function setHasRewrite(bool $boolean): self + public function setHasPendingRewrite(bool $boolean): self { - $this->hasRewrite = $boolean; + $this->hasPendingRewrite = $boolean; return $this; } diff --git a/src/Pecee/Http/Security/CookieTokenProvider.php b/src/Pecee/Http/Security/CookieTokenProvider.php index bbe5ccb..b871b35 100644 --- a/src/Pecee/Http/Security/CookieTokenProvider.php +++ b/src/Pecee/Http/Security/CookieTokenProvider.php @@ -63,7 +63,7 @@ class CookieTokenProvider implements ITokenProvider public function setToken(string $token): void { $this->token = $token; - setcookie(static::CSRF_KEY, $token, time() + 60 * $this->cookieTimeoutMinutes, '/'); + setcookie(static::CSRF_KEY, $token, (int)((time() + 60) * $this->cookieTimeoutMinutes), '/', ini_get('session.cookie_domain'), ini_get('session.cookie_secure'), ini_get('session.cookie_httponly')); } /** diff --git a/src/Pecee/Http/Url.php b/src/Pecee/Http/Url.php index 440e1b4..212ae6e 100644 --- a/src/Pecee/Http/Url.php +++ b/src/Pecee/Http/Url.php @@ -7,36 +7,52 @@ use Pecee\Http\Exceptions\MalformedUrlException; class Url { private $originalUrl; - private $data = [ - 'scheme' => null, - 'host' => null, - 'port' => null, - 'user' => null, - 'pass' => null, - 'path' => null, - 'query' => null, - 'fragment' => null, - ]; + + private $scheme; + private $host; + private $port; + private $username; + private $password; + private $path; + private $params; + private $fragment; /** * Url constructor. + * * @param string $url * @throws MalformedUrlException */ public function __construct(?string $url) { $this->originalUrl = $url; - if ($url !== null) { - $this->data = $this->parseUrl($url) + $this->data; - if (isset($this->data['path']) === true && $this->data['path'] !== '/') { - $this->data['path'] = rtrim($this->data['path'], '/') . '/'; + if ($url !== null && $url !== '/') { + $data = $this->parseUrl($url); + + $this->scheme = $data['scheme'] ?? null; + $this->host = $data['host'] ?? null; + $this->port = $data['port'] ?? null; + $this->username = $data['user'] ?? null; + $this->password = $data['pass'] ?? null; + + if (isset($data['path']) === true) { + $this->setPath($data['path']); + } + + $this->fragment = $data['fragment'] ?? null; + + if (isset($data['query']) === true) { + $params = []; + parse_str($data['query'], $params); + $this->setParams($params); } } } /** * Check if url is using a secure protocol like https + * * @return bool */ public function isSecure(): bool @@ -46,6 +62,7 @@ class Url /** * Checks if url is relative + * * @return bool */ public function isRelative(): bool @@ -55,38 +72,94 @@ class Url /** * Get url scheme + * * @return string|null */ public function getScheme(): ?string { - return $this->data['scheme']; + return $this->scheme; + } + + /** + * Set the scheme of the url + * + * @param string $scheme + * @return static + */ + public function setScheme(string $scheme): self + { + $this->scheme = $scheme; + + return $this; } /** * Get url host + * * @return string|null */ public function getHost(): ?string { - return $this->data['host']; + return $this->host; + } + + /** + * Set the host of the url + * + * @param string $host + * @return static + */ + public function setHost(string $host): self + { + $this->host = $host; + + return $this; } /** * Get url port + * * @return int|null */ public function getPort(): ?int { - return ($this->data['port'] !== null) ? (int)$this->data['port'] : null; + return ($this->port !== null) ? (int)$this->port : null; + } + + /** + * Set the port of the url + * + * @param int $port + * @return static + */ + public function setPort(int $port): self + { + $this->port = $port; + + return $this; } /** * Parse username from url + * * @return string|null */ - public function getUserName(): ?string + public function getUsername(): ?string { - return $this->data['user']; + return $this->username; + } + + /** + * Set the username of the url + * + * @param string $username + * @return static + */ + public function setUsername(string $username): self + { + $this->username = $username; + + return $this; } /** @@ -95,7 +168,20 @@ class Url */ public function getPassword(): ?string { - return $this->data['pass']; + return $this->password; + } + + /** + * Set the url password + * + * @param string $password + * @return static + */ + public function setPassword(string $password): self + { + $this->password = $password; + + return $this; } /** @@ -104,25 +190,87 @@ class Url */ public function getPath(): ?string { - return $this->data['path'] ?? '/'; + return $this->path ?? '/'; } /** - * Get querystring from url - * @return string|null + * Set the url path + * + * @param string $path + * @return static */ - public function getQueryString(): ?string + public function setPath(string $path): self { - return $this->data['query']; + $this->path = rtrim($path, '/') . '/'; + + return $this; + } + + /** + * Get query-string from url + * + * @return array + */ + public function getParams(): array + { + return $this->params; + } + + /** + * Merge parameters array + * + * @param array $params + * @return static + */ + public function mergeParams(array $params): self + { + return $this->setParams(array_merge($this->getParams(), $params)); + } + + /** + * Set the url params + * + * @param array $params + * @return static + */ + public function setParams(array $params): self + { + $this->params = $params; + + return $this; + } + + /** + * Get query-string params as string + * + * @return string + */ + public function getQueryString(): string + { + return static::arrayToParams($this->getParams()); } /** * Get fragment from url (everything after #) + * * @return string|null */ public function getFragment(): ?string { - return $this->data['fragment']; + return $this->fragment; + } + + /** + * Set url fragment + * + * @param string $fragment + * @return static + */ + public function setFragment(string $fragment): self + { + $this->fragment = $fragment; + + return $this; } /** @@ -133,32 +281,6 @@ class Url return $this->originalUrl; } - /** - * UTF-8 aware parse_url() replacement. - * @param string $url - * @param int $component - * @throws MalformedUrlException - * @return array - */ - public function parseUrl(string $url, int $component = -1): array - { - $encodedUrl = preg_replace_callback( - '/[^:\/@?&=#]+/u', - function ($matches) { - return urlencode($matches[0]); - }, - $url - ); - - $parts = parse_url($encodedUrl, $component); - - if ($parts === false) { - throw new MalformedUrlException('Malformed URL: ' . $url); - } - - return array_map('urldecode', $parts); - } - /** * Get position of value. * Returns -1 on failure. @@ -185,17 +307,143 @@ class Url } /** - * Returns data array with information about the url - * @return array + * Check if url contains parameter/query string. + * + * @param string $name + * @return bool */ - public function getData(): array + public function hasParam(string $name): bool { - return $this->data; + return \in_array($name, $this->getParams(), true); + } + + /** + * Removes parameter from query-string + * + * @param string $name + */ + public function removeParam(string $name): void + { + if ($this->hasParam($name) === true) { + $params = $this->getParams(); + $key = \array_search($name, $params, true); + + if ($key === true) { + unset($params[$key]); + $this->setParams($params); + } + } + } + + /** + * Get parameter by name. + * Returns parameter value or default value. + * + * @param string $name + * @param string|null $defaultValue + * @return string|null + */ + public function getParam(string $name, ?string $defaultValue = null): ?string + { + $output = null; + + if ($this->hasParam($name) === true) { + $params = $this->getParams(); + $key = \array_search($name, $params, true); + + if ($key === true) { + $output = $params[$key]; + } + } + + return $output ?? $defaultValue; + } + + /** + * UTF-8 aware parse_url() replacement. + * @param string $url + * @param int $component + * @return array + * @throws MalformedUrlException + */ + public function parseUrl(string $url, int $component = -1): array + { + $encodedUrl = preg_replace_callback( + '/[^:\/@?&=#]+/u', + function ($matches) { + return urlencode($matches[0]); + }, + $url + ); + + $parts = parse_url($encodedUrl, $component); + + if ($parts === false) { + throw new MalformedUrlException(sprintf('Failed to parse url: "%s"', $url)); + } + + return array_map('urldecode', $parts); + } + + /** + * Convert array to query-string params + * + * @param array $getParams + * @param bool $includeEmpty + * @return string + */ + public static function arrayToParams(array $getParams = [], bool $includeEmpty = true): string + { + if (\count($getParams) !== 0) { + + if ($includeEmpty === false) { + $getParams = array_filter($getParams, function ($item) { + return (trim($item) !== ''); + }); + } + + return http_build_query($getParams); + } + + return ''; + } + + /** + * Returns the relative url + * + * @return string + */ + public function getRelativeUrl(): string + { + $params = $this->getQueryString(); + + $path = $this->path ?? ''; + $query = $params !== '' ? '?' . $params : ''; + $fragment = $this->fragment !== null ? '#' . $this->fragment : ''; + + return $path . $query . $fragment; + } + + /** + * Returns the absolute url + * + * @return string + */ + public function getAbsoluteUrl(): string + { + $scheme = $this->scheme !== null ? $this->scheme . '://' : ''; + $host = $this->host ?? ''; + $port = $this->port !== null ? ':' . $this->port : ''; + $user = $this->username ?? ''; + $pass = $this->password !== null ? ':' . $this->password : ''; + $pass = ($user || $pass) ? $pass . '@' : ''; + + return $scheme . $user . $pass . $host . $port . $this->getRelativeUrl(); } public function __toString() { - return $this->getOriginalUrl(); + return $this->getRelativeUrl(); } } \ No newline at end of file diff --git a/src/Pecee/SimpleRouter/Event/EventArgument.php b/src/Pecee/SimpleRouter/Event/EventArgument.php new file mode 100644 index 0000000..f4aeed8 --- /dev/null +++ b/src/Pecee/SimpleRouter/Event/EventArgument.php @@ -0,0 +1,83 @@ +eventName = $eventName; + $this->router = $router; + $this->arguments = $arguments; + } + + /** + * Get event name + * + * @return string + */ + public function getEventName(): string + { + return $this->eventName; + } + + /** + * Set the event name + * + * @param string $name + */ + public function setEventName(string $name): void + { + $this->eventName = $name; + } + + /** + * Get the router instance + * + * @return Router + */ + public function getRouter(): Router + { + return $this->router; + } + + /** + * Get the request instance + * + * @return Request + */ + public function getRequest(): Request + { + return $this->getRouter()->getRequest(); + } + + /** + * Get arguments + * + * @return array + */ + public function getArguments(): array + { + return $this->arguments; + } + +} \ No newline at end of file diff --git a/src/Pecee/SimpleRouter/Event/IEventArgument.php b/src/Pecee/SimpleRouter/Event/IEventArgument.php new file mode 100644 index 0000000..a8a6d7e --- /dev/null +++ b/src/Pecee/SimpleRouter/Event/IEventArgument.php @@ -0,0 +1,46 @@ +callback = function (EventArgument $argument) { + // todo: log in database + }; + } + + /** + * Get events. + * + * @param string|null $name Filter events by name. + * @return array + */ + public function getEvents(?string $name): array + { + return [ + $name => [ + $this->callback, + ], + ]; + } + + /** + * Fires any events registered with given event-name + * + * @param Router $router Router instance + * @param string $name Event name + * @param array ...$eventArgs Event arguments + */ + public function fireEvents(Router $router, string $name, ...$eventArgs): void + { + $callback = $this->callback; + $callback(new EventArgument($router, $eventArgs)); + } + + /** + * Set debug callback + * + * @param \Closure $event + */ + public function setCallback(\Closure $event): void + { + $this->callback = $event; + } + +} \ No newline at end of file diff --git a/src/Pecee/SimpleRouter/Handlers/EventHandler.php b/src/Pecee/SimpleRouter/Handlers/EventHandler.php new file mode 100644 index 0000000..7b3b5af --- /dev/null +++ b/src/Pecee/SimpleRouter/Handlers/EventHandler.php @@ -0,0 +1,178 @@ +registeredEvents[$name]) === true) { + $this->registeredEvents[$name][] = $callback; + } else { + $this->registeredEvents[$name] = [$callback]; + } + + return $this; + } + + /** + * Get events. + * + * @param string|null $name Filter events by name. + * @param array ...$names Add multiple names... + * @return array + */ + public function getEvents(?string $name, ...$names): array + { + if ($name === null) { + return $this->registeredEvents; + } + + $names[] = $name; + $events = []; + + foreach ($names as $eventName) { + if(isset($this->registeredEvents[$eventName]) === true) { + $events += $this->registeredEvents[$eventName]; + } + } + + return $events; + } + + /** + * Fires any events registered with given event-name + * + * @param Router $router Router instance + * @param string $name Event name + * @param array ...$eventArgs Event arguments + */ + public function fireEvents(Router $router, string $name, ...$eventArgs): void + { + $events = $this->getEvents(static::EVENT_ALL, $name); + + /* @var $event \Closure */ + foreach ($events as $event) { + $event(new EventArgument($name, $router, $eventArgs)); + } + } + +} \ No newline at end of file diff --git a/src/Pecee/SimpleRouter/Handlers/IEventHandler.php b/src/Pecee/SimpleRouter/Handlers/IEventHandler.php new file mode 100644 index 0000000..2276778 --- /dev/null +++ b/src/Pecee/SimpleRouter/Handlers/IEventHandler.php @@ -0,0 +1,26 @@ +getUrl(); diff --git a/src/Pecee/SimpleRouter/Route/Route.php b/src/Pecee/SimpleRouter/Route/Route.php index 2f349c3..2f0d4eb 100644 --- a/src/Pecee/SimpleRouter/Route/Route.php +++ b/src/Pecee/SimpleRouter/Route/Route.php @@ -136,7 +136,7 @@ abstract class Route implements IRoute $parameters = []; - // Ensures that hostnames/domains will work with parameters + // Ensures that host names/domains will work with parameters $url = '/' . ltrim($url, '/'); if ((bool)preg_match_all('/' . $regex . '/u', $route, $parameters) === false) { diff --git a/src/Pecee/SimpleRouter/Route/RouteController.php b/src/Pecee/SimpleRouter/Route/RouteController.php index b15a402..e090652 100644 --- a/src/Pecee/SimpleRouter/Route/RouteController.php +++ b/src/Pecee/SimpleRouter/Route/RouteController.php @@ -49,7 +49,7 @@ class RouteController extends LoadableRoute implements IControllerRoute * @param string|null $name * @return string */ - public function findUrl($method = null, $parameters = null, $name = null): string + public function findUrl(?string $method = null, $parameters = null, ?string $name = null): string { if (strpos($name, '.') !== false) { $found = array_search(substr($name, strrpos($name, '.') + 1), $this->names, false); diff --git a/src/Pecee/SimpleRouter/Route/RouteGroup.php b/src/Pecee/SimpleRouter/Route/RouteGroup.php index 8c88e6b..3ad9060 100644 --- a/src/Pecee/SimpleRouter/Route/RouteGroup.php +++ b/src/Pecee/SimpleRouter/Route/RouteGroup.php @@ -2,7 +2,7 @@ namespace Pecee\SimpleRouter\Route; -use Pecee\Handlers\IExceptionHandler; +use Pecee\SimpleRouter\Handlers\IExceptionHandler; use Pecee\Http\Request; class RouteGroup extends Route implements IGroupRoute diff --git a/src/Pecee/SimpleRouter/Route/RoutePartialGroup.php b/src/Pecee/SimpleRouter/Route/RoutePartialGroup.php index 9656554..956c248 100644 --- a/src/Pecee/SimpleRouter/Route/RoutePartialGroup.php +++ b/src/Pecee/SimpleRouter/Route/RoutePartialGroup.php @@ -6,7 +6,14 @@ use Pecee\Http\Request; class RoutePartialGroup extends RouteGroup implements IPartialGroupRoute { - protected $urlRegex = '/^%s\/?/u'; + + /** + * RoutePartialGroup constructor. + */ + public function __construct() + { + $this->urlRegex = '/^%s\/?/u'; + } /** * Method called to check if route matches diff --git a/src/Pecee/SimpleRouter/Route/RouteResource.php b/src/Pecee/SimpleRouter/Route/RouteResource.php index 13fe2e7..2096831 100644 --- a/src/Pecee/SimpleRouter/Route/RouteResource.php +++ b/src/Pecee/SimpleRouter/Route/RouteResource.php @@ -60,7 +60,13 @@ class RouteResource extends LoadableRoute implements IControllerRoute return (strtolower($this->name) === strtolower($name)); } - public function findUrl($method = null, $parameters = null, $name = null): string + /** + * @param string|null $method + * @param array|string|null $parameters + * @param string|null $name + * @return string + */ + public function findUrl(?string $method = null, $parameters = null, ?string $name = null): string { $url = array_search($name, $this->names, false); if ($url !== false) { diff --git a/src/Pecee/SimpleRouter/Router.php b/src/Pecee/SimpleRouter/Router.php index 4a96f7a..c2a0eec 100644 --- a/src/Pecee/SimpleRouter/Router.php +++ b/src/Pecee/SimpleRouter/Router.php @@ -3,7 +3,11 @@ namespace Pecee\SimpleRouter; use Pecee\Exceptions\InvalidArgumentException; -use Pecee\Handlers\IExceptionHandler; +use Pecee\Http\Exceptions\MalformedUrlException; +use Pecee\Http\Url; +use Pecee\SimpleRouter\Handlers\EventHandler; +use Pecee\SimpleRouter\Handlers\IEventHandler; +use Pecee\SimpleRouter\Handlers\IExceptionHandler; use Pecee\Http\Middleware\BaseCsrfVerifier; use Pecee\Http\Request; use Pecee\SimpleRouter\Exceptions\HttpException; @@ -27,32 +31,32 @@ class Router * Defines if a route is currently being processed. * @var bool */ - protected $processingRoute; + protected $isProcessingRoute; /** * All added routes * @var array */ - protected $routes; + protected $routes = []; /** * List of processed routes * @var array */ - protected $processedRoutes; + protected $processedRoutes = []; /** * Stack of routes used to keep track of sub-routes added * when a route is being processed. * @var array */ - protected $routeStack; + protected $routeStack = []; /** * List of added bootmanagers * @var array */ - protected $bootManagers; + protected $bootManagers = []; /** * Csrf verifier class @@ -64,7 +68,7 @@ class Router * Get exception handlers * @var array */ - protected $exceptionHandlers; + protected $exceptionHandlers = []; /** * List of loaded exception that has been loaded. @@ -72,7 +76,7 @@ class Router * * @var array */ - protected $loadedExceptionHandlers; + protected $loadedExceptionHandlers = []; /** * Enable or disabled debugging @@ -92,9 +96,14 @@ class Router */ protected $debugList = []; + /** + * Contains any registered event-handler. + * @var array + */ + protected $eventHandlers = []; + /** * Router constructor. - * @throws \Pecee\Http\Exceptions\MalformedUrlException */ public function __construct() { @@ -102,18 +111,28 @@ class Router } /** - * @throws \Pecee\Http\Exceptions\MalformedUrlException + * Resets the router by reloading request and clearing all routes and data. */ public function reset(): void { - $this->processingRoute = false; - $this->request = new Request(); + $this->isProcessingRoute = false; + + try { + $this->request = new Request(); + } catch (MalformedUrlException $e) { + $this->debug(sprintf('Invalid request-uri url: %s', $e->getMessage())); + } + $this->routes = []; $this->bootManagers = []; $this->routeStack = []; $this->processedRoutes = []; $this->exceptionHandlers = []; $this->loadedExceptionHandlers = []; + $this->eventHandlers = []; + $this->debugList = []; + $this->csrfVerifier = null; + $this->debugStartTime = microtime(true); } /** @@ -127,7 +146,7 @@ class Router * 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) { + if ($this->isProcessingRoute === true) { $this->routeStack[] = $route; return $route; @@ -147,9 +166,9 @@ class Router protected function renderAndProcess(IRoute $route): void { - $this->processingRoute = true; + $this->isProcessingRoute = true; $route->renderRoute($this->request, $this); - $this->processingRoute = false; + $this->isProcessingRoute = false; if (\count($this->routeStack) !== 0) { @@ -239,46 +258,78 @@ class Router { $this->debug('Loading routes'); + $this->fireEvents(EventHandler::EVENT_BOOT); + /* Initialize boot-managers */ + /* @var $manager IRouterBootManager */ foreach ($this->bootManagers as $manager) { + $this->debug('Rendering bootmanager %s', \get_class($manager)); - $manager->boot($this->request); + $this->fireEvents(EventHandler::EVENT_RENDER_BOOTMANAGER, $manager); + + /* Render bootmanager */ + $manager->boot($this, $this->request); + $this->debug('Finished rendering bootmanager'); } + $this->fireEvents(EventHandler::EVENT_LOAD_ROUTES); + /* Loop through each route-request */ $this->processRoutes($this->routes); $this->debug('Finished loading routes'); } + /** + * Start the routing + * + * @return string|null + * @throws \Pecee\SimpleRouter\Exceptions\NotFoundHttpException + * @throws \Pecee\Http\Middleware\Exceptions\TokenMismatchException + * @throws HttpException + * @throws \Exception + */ + public function start(): ?string + { + $this->debug('Router starting'); + + $this->fireEvents(EventHandler::EVENT_INIT); + + $this->loadRoutes(); + + if ($this->csrfVerifier !== null) { + + $this->fireEvents(EventHandler::EVENT_RENDER_CSRF); + + /* Verify csrf token for request */ + $this->csrfVerifier->handle($this->request); + } + + $output = $this->routeRequest(); + + $this->fireEvents(EventHandler::EVENT_LOAD); + + $this->debug('Routing complete'); + + return $output; + } + /** * Routes the request * - * @param bool $rewrite * @return string|null * @throws HttpException * @throws \Exception */ - public function routeRequest(bool $rewrite = false): ?string + public function routeRequest(): ?string { - $this->debug('Started routing request (rewrite: %s)', $rewrite === true ? 'yes' : 'no'); + $this->debug('Routing request'); $methodNotAllowed = false; try { - - if ($rewrite === false) { - $this->loadRoutes(); - - if ($this->csrfVerifier !== null) { - - /* Verify csrf token for request */ - $this->csrfVerifier->handle($this->request); - } - } - $url = $this->request->getRewriteUrl() ?? $this->request->getUrl()->getPath(); /* @var $route ILoadableRoute */ @@ -289,6 +340,8 @@ class Router /* If the route matches */ if ($route->matchRoute($url, $this->request) === true) { + $this->fireEvents(EventHandler::EVENT_MATCH_ROUTE); + /* Check if request method matches */ if (\count($route->getRequestMethods()) !== 0 && \in_array($this->request->getMethod(), $route->getRequestMethods(), true) === false) { $this->debug('Method "%s" not allowed', $this->request->getMethod()); @@ -296,6 +349,8 @@ class Router continue; } + $this->fireEvents(EventHandler::EVENT_RENDER_MIDDLEWARE); + $route->loadMiddleware($this->request, $this); $output = $this->handleRouteRewrite($key, $url); @@ -303,13 +358,10 @@ class Router return $output; } - /* Render route */ $methodNotAllowed = false; $this->request->addLoadedRoute($route); - $output = $route->renderRoute($this->request, $this); - if ($output !== null) { return $output; } @@ -360,7 +412,7 @@ class Router protected function handleRouteRewrite($key, string $url): ?string { /* If the request has changed */ - if ($this->request->hasRewrite() === false) { + if ($this->request->hasPendingRewrite() === false) { return null; } @@ -373,9 +425,11 @@ class Router if ($this->request->getRewriteUrl() !== $url) { unset($this->processedRoutes[$key]); - $this->request->setHasRewrite(false); + $this->request->setHasPendingRewrite(false); - return $this->routeRequest(true); + $this->fireEvents(EventHandler::EVENT_REWRITE); + + return $this->routeRequest(); } return null; @@ -391,6 +445,8 @@ class Router { $this->debug('Starting exception handling for "%s"', \get_class($e)); + $this->fireEvents(EventHandler::EVENT_LOAD_EXCEPTIONS); + /* @var $handler IExceptionHandler */ foreach ($this->exceptionHandlers as $key => $handler) { @@ -398,6 +454,8 @@ class Router $handler = new $handler(); } + $this->fireEvents(EventHandler::EVENT_RENDER_EXCEPTION); + $this->debug('Processing exception-handler "%s"', \get_class($handler)); if (($handler instanceof IExceptionHandler) === false) { @@ -410,12 +468,14 @@ class Router $handler->handleError($this->request, $e); $this->debug('Finished rendering exception-handler'); - if (isset($this->loadedExceptionHandlers[$key]) === false && $this->request->hasRewrite() === true) { + if (isset($this->loadedExceptionHandlers[$key]) === false && $this->request->hasPendingRewrite() === true) { $this->loadedExceptionHandlers[$key] = $handler; $this->debug('Exception handler contains rewrite, reloading routes'); - return $this->routeRequest(true); + $this->fireEvents(EventHandler::EVENT_REWRITE); + + return $this->routeRequest(); } } catch (\Exception $e) { @@ -429,22 +489,6 @@ class Router throw $e; } - public function arrayToParams(array $getParams = [], bool $includeEmpty = true): string - { - if (\count($getParams) !== 0) { - - if ($includeEmpty === false) { - $getParams = array_filter($getParams, function ($item) { - return (trim($item) !== ''); - }); - } - - return '?' . http_build_query($getParams); - } - - return ''; - } - /** * Find route by alias, class, callback or method. * @@ -453,8 +497,8 @@ class Router */ public function findRoute(string $name): ?ILoadableRoute { - $this->debug('Finding route by name "%s"', $name); + $this->fireEvents(EventHandler::EVENT_FIND_ROUTE); /* @var $route ILoadableRoute */ foreach ($this->processedRoutes as $route) { @@ -485,10 +529,11 @@ class Router } /* Check if callback matches (if it's not a function) */ - if (\is_string($name) === true && \is_string($route->getCallback()) && strpos($name, '@') !== false && strpos($route->getCallback(), '@') !== false && \is_callable($route->getCallback()) === false) { + $callback = $route->getCallback(); + if (\is_string($name) === true && \is_string($callback) === true && strpos($name, '@') !== false && strpos($callback, '@') !== false && \is_callable($callback) === false) { /* Check if the entire callback is matching */ - if (strpos($route->getCallback(), $name) === 0 || strtolower($route->getCallback()) === strtolower($name)) { + if (strpos($callback, $name) === 0 || strtolower($callback) === strtolower($name)) { $this->debug('Found route "%s" by callback "%s"', $route->getUrl(), $name); return $route; @@ -523,19 +568,22 @@ class Router * @param string|null $name * @param string|array|null $parameters * @param array|null $getParams + * @return Url * @throws InvalidArgumentException - * @return string + * @throws \Pecee\Http\Exceptions\MalformedUrlException */ - public function getUrl(?string $name = null, $parameters = null, $getParams = null): string + public function getUrl(?string $name = null, $parameters = null, ?array $getParams = null): Url { $this->debug('Finding url', \func_get_args()); + $this->fireEvents(EventHandler::EVENT_GET_URL); + if ($getParams !== null && \is_array($getParams) === false) { throw new InvalidArgumentException('Invalid type for getParams. Must be array or null'); } if ($name === '' && $parameters === '') { - return '/'; + return new Url('/'); } /* Only merge $_GET when all parameters are null */ @@ -547,21 +595,29 @@ class Router /* Return current route if no options has been specified */ if ($name === null && $parameters === null) { - return $this->request->getUrl()->getPath() . $this->arrayToParams($getParams); + return $this->request + ->getUrlCopy() + ->setParams($getParams); } $loadedRoute = $this->request->getLoadedRoute(); /* If nothing is defined and a route is loaded we use that */ if ($name === null && $loadedRoute !== null) { - return $loadedRoute->findUrl($loadedRoute->getMethod(), $parameters, $name) . $this->arrayToParams($getParams); + return $this->request + ->getUrlCopy() + ->setPath($loadedRoute->findUrl($loadedRoute->getMethod(), $parameters, $name)) + ->setParams($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); + return $this->request + ->getUrlCopy() + ->setPath($route->findUrl($route->getMethod(), $parameters, $name)) + ->setParams($getParams); } /* Using @ is most definitely a controller@method or alias@method */ @@ -575,12 +631,18 @@ class Router /* Check if the route contains the name/alias */ if ($route->hasName($controller) === true) { - return $route->findUrl($method, $parameters, $name) . $this->arrayToParams($getParams); + return $this->request + ->getUrlCopy() + ->setPath($route->findUrl($method, $parameters, $name)) + ->setParams($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); + return $this->request + ->getUrlCopy() + ->setPath($route->findUrl($method, $parameters, $name)) + ->setParams($getParams); } } @@ -588,8 +650,12 @@ class Router /* No result so we assume that someone is using a hardcoded url and join everything together. */ $url = trim(implode('/', array_merge((array)$name, (array)$parameters)), '/'); + $url = (($url === '') ? '/' : '/' . $url . '/'); - return (($url === '') ? '/' : '/' . $url . '/') . $this->arrayToParams($getParams); + return $this->request + ->getUrlCopy() + ->setPath($url) + ->setParams($getParams); } /** @@ -682,6 +748,47 @@ class Router return $this; } + /** + * Register event handler + * + * @param IEventHandler $handler + * @return static + */ + public function addEventHandler(IEventHandler $handler): self + { + $this->eventHandlers[] = $handler; + + return $this; + } + + /** + * Get registered event-handler. + * + * @return array + */ + public function getEventHandlers(): array + { + return $this->eventHandlers; + } + + /** + * Fire event + * + * @param string $name + * @param array ...$args + */ + protected function fireEvents($name, ...$args): void + { + if (\count($this->eventHandlers) === 0) { + return; + } + + /* @var IEventHandler $eventHandler */ + foreach ($this->eventHandlers as $eventHandler) { + $eventHandler->fireEvents($this, $name, $args); + } + } + /** * Add new debug message * @param string $message @@ -708,10 +815,6 @@ class Router */ public function setDebugEnabled(bool $boolean): void { - if ($boolean === true) { - $this->debugStartTime = microtime(true); - } - $this->debugEnabled = $boolean; } diff --git a/src/Pecee/SimpleRouter/SimpleRouter.php b/src/Pecee/SimpleRouter/SimpleRouter.php index ea0410f..4f495e6 100644 --- a/src/Pecee/SimpleRouter/SimpleRouter.php +++ b/src/Pecee/SimpleRouter/SimpleRouter.php @@ -11,11 +11,14 @@ namespace Pecee\SimpleRouter; use Pecee\Exceptions\InvalidArgumentException; -use Pecee\Handlers\CallbackExceptionHandler; +use Pecee\Http\Exceptions\MalformedUrlException; +use Pecee\Http\Url; +use Pecee\SimpleRouter\Handlers\CallbackExceptionHandler; use Pecee\Http\Middleware\BaseCsrfVerifier; use Pecee\Http\Request; use Pecee\Http\Response; use Pecee\SimpleRouter\Exceptions\HttpException; +use Pecee\SimpleRouter\Handlers\IEventHandler; use Pecee\SimpleRouter\Route\IGroupRoute; use Pecee\SimpleRouter\Route\IPartialGroupRoute; use Pecee\SimpleRouter\Route\IRoute; @@ -48,20 +51,18 @@ class SimpleRouter /** * Start routing * - * @throws \Pecee\Http\Exceptions\MalformedUrlException * @throws HttpException * @throws \Exception */ public static function start(): void { - echo static::router()->routeRequest(); + echo static::router()->start(); } /** * Start the routing an return array with debugging-information * * @return array - * @throws \Pecee\Http\Exceptions\MalformedUrlException */ public static function startDebug(): array { @@ -94,19 +95,23 @@ class SimpleRouter } } + $request = static::request(); + $router = static::router(); + return [ - 'url' => static::request()->getUrl(), - 'method' => static::request()->getMethod(), - 'host' => static::request()->getHost(), - 'loaded_routes' => static::request()->getLoadedRoutes(), - 'all_routes' => static::router()->getRoutes(), - 'boot_managers' => static::router()->getBootManagers(), - 'csrf_verifier' => static::router()->getCsrfVerifier(), - 'log' => static::router()->getDebugLog(), + 'url' => $request->getUrl(), + 'method' => $request->getMethod(), + 'host' => $request->getHost(), + 'loaded_routes' => $request->getLoadedRoutes(), + 'all_routes' => $router->getRoutes(), + 'boot_managers' => $router->getBootManagers(), + 'csrf_verifier' => $router->getCsrfVerifier(), + 'log' => $router->getDebugLog(), + 'event_handlers' => $router->getEventHandlers(), 'router_output' => $routerOutput, 'library_version' => $version, 'php_version' => PHP_VERSION, - 'server_params' => static::request()->getHeaders(), + 'server_params' => $request->getHeaders(), ]; } @@ -124,19 +129,27 @@ class SimpleRouter * Base CSRF verifier * * @param BaseCsrfVerifier $baseCsrfVerifier - * @throws \Pecee\Http\Exceptions\MalformedUrlException */ public static function csrfVerifier(BaseCsrfVerifier $baseCsrfVerifier): void { static::router()->setCsrfVerifier($baseCsrfVerifier); } + /** + * Add new event handler to the router + * + * @param IEventHandler $eventHandler + */ + public static function addEventHandler(IEventHandler $eventHandler): void + { + static::router()->addEventHandler($eventHandler); + } + /** * 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 - * @throws \Pecee\Http\Exceptions\MalformedUrlException */ public static function addBootManager(IRouterBootManager $bootManager): void { @@ -151,7 +164,6 @@ class SimpleRouter * @param array|null $settings * * @return RouteUrl - * @throws \Pecee\Http\Exceptions\MalformedUrlException */ public static function get(string $url, $callback, array $settings = null): IRoute { @@ -165,7 +177,6 @@ class SimpleRouter * @param string|\Closure $callback * @param array|null $settings * @return RouteUrl - * @throws \Pecee\Http\Exceptions\MalformedUrlException */ public static function post(string $url, $callback, array $settings = null): IRoute { @@ -179,7 +190,6 @@ class SimpleRouter * @param string|\Closure $callback * @param array|null $settings * @return RouteUrl - * @throws \Pecee\Http\Exceptions\MalformedUrlException */ public static function put(string $url, $callback, array $settings = null): IRoute { @@ -193,7 +203,6 @@ class SimpleRouter * @param string|\Closure $callback * @param array|null $settings * @return RouteUrl - * @throws \Pecee\Http\Exceptions\MalformedUrlException */ public static function patch(string $url, $callback, array $settings = null): IRoute { @@ -207,7 +216,6 @@ class SimpleRouter * @param string|\Closure $callback * @param array|null $settings * @return RouteUrl - * @throws \Pecee\Http\Exceptions\MalformedUrlException */ public static function options(string $url, $callback, array $settings = null): IRoute { @@ -221,7 +229,6 @@ class SimpleRouter * @param string|\Closure $callback * @param array|null $settings * @return RouteUrl - * @throws \Pecee\Http\Exceptions\MalformedUrlException */ public static function delete(string $url, $callback, array $settings = null): IRoute { @@ -234,7 +241,6 @@ class SimpleRouter * @param array $settings * @param \Closure $callback * @return RouteGroup - * @throws \Pecee\Http\Exceptions\MalformedUrlException * @throws InvalidArgumentException */ public static function group(array $settings = [], \Closure $callback): IGroupRoute @@ -260,7 +266,6 @@ class SimpleRouter * @param \Closure $callback * @param array $settings * @return RoutePartialGroup - * @throws \Pecee\Http\Exceptions\MalformedUrlException * @throws InvalidArgumentException */ public static function partialGroup(string $url, \Closure $callback, array $settings = []): IPartialGroupRoute @@ -288,7 +293,6 @@ class SimpleRouter * @param array|null $settings * @see SimpleRouter::form * @return RouteUrl - * @throws \Pecee\Http\Exceptions\MalformedUrlException */ public static function basic(string $url, $callback, array $settings = null): IRoute { @@ -304,7 +308,6 @@ class SimpleRouter * @param array|null $settings * @see SimpleRouter::form * @return RouteUrl - * @throws \Pecee\Http\Exceptions\MalformedUrlException */ public static function form(string $url, $callback, array $settings = null): IRoute { @@ -319,7 +322,6 @@ class SimpleRouter * @param string|\Closure $callback * @param array|null $settings * @return RouteUrl|IRoute - * @throws \Pecee\Http\Exceptions\MalformedUrlException */ public static function match(array $requestMethods, string $url, $callback, array $settings = null) { @@ -343,7 +345,6 @@ class SimpleRouter * @param string|\Closure $callback * @param array|null $settings * @return RouteUrl|IRoute - * @throws \Pecee\Http\Exceptions\MalformedUrlException */ public static function all(string $url, $callback, array $settings = null) { @@ -366,7 +367,6 @@ class SimpleRouter * @param string $controller * @param array|null $settings * @return RouteController|IRoute - * @throws \Pecee\Http\Exceptions\MalformedUrlException */ public static function controller(string $url, $controller, array $settings = null) { @@ -389,7 +389,6 @@ class SimpleRouter * @param string $controller * @param array|null $settings * @return RouteResource|IRoute - * @throws \Pecee\Http\Exceptions\MalformedUrlException */ public static function resource(string $url, $controller, array $settings = null) { @@ -410,7 +409,6 @@ class SimpleRouter * * @param \Closure $callback * @return CallbackExceptionHandler $callbackHandler - * @throws \Pecee\Http\Exceptions\MalformedUrlException */ public static function error(\Closure $callback): CallbackExceptionHandler { @@ -443,20 +441,28 @@ class SimpleRouter * @param string|null $name * @param string|array|null $parameters * @param array|null $getParams - * @throws \Pecee\Exceptions\InvalidArgumentException - * @return string - * @throws \Pecee\Http\Exceptions\MalformedUrlException + * @return Url */ - public static function getUrl(?string $name = null, $parameters = null, $getParams = null): string + public static function getUrl(?string $name = null, $parameters = null, $getParams = null): Url { - return static::router()->getUrl($name, $parameters, $getParams); + try { + return static::router()->getUrl($name, $parameters, $getParams); + } catch (\Exception $e) { + try { + return new Url('/'); + } catch (MalformedUrlException $e) { + + } + } + + // This will never happen... + return null; } /** * Get the request * * @return \Pecee\Http\Request - * @throws \Pecee\Http\Exceptions\MalformedUrlException */ public static function request(): Request { @@ -467,7 +473,6 @@ class SimpleRouter * Get the response object * * @return Response - * @throws \Pecee\Http\Exceptions\MalformedUrlException */ public static function response(): Response { @@ -482,7 +487,6 @@ class SimpleRouter * Returns the router instance * * @return Router - * @throws \Pecee\Http\Exceptions\MalformedUrlException */ public static function router(): Router { diff --git a/tests/Pecee/SimpleRouter/Dummy/Handler/ExceptionHandler.php b/tests/Pecee/SimpleRouter/Dummy/Handler/ExceptionHandler.php index 2db833f..90dfc1a 100644 --- a/tests/Pecee/SimpleRouter/Dummy/Handler/ExceptionHandler.php +++ b/tests/Pecee/SimpleRouter/Dummy/Handler/ExceptionHandler.php @@ -1,6 +1,6 @@ setUrl('/'); + $request->setUrl(new \Pecee\Http\Url('/')); } } \ No newline at end of file diff --git a/tests/Pecee/SimpleRouter/Dummy/Handler/ExceptionHandlerSecond.php b/tests/Pecee/SimpleRouter/Dummy/Handler/ExceptionHandlerSecond.php index 2360047..274bd3c 100644 --- a/tests/Pecee/SimpleRouter/Dummy/Handler/ExceptionHandlerSecond.php +++ b/tests/Pecee/SimpleRouter/Dummy/Handler/ExceptionHandlerSecond.php @@ -1,13 +1,13 @@ setUrl('/'); + $request->setUrl(new \Pecee\Http\Url('/')); } } \ No newline at end of file diff --git a/tests/Pecee/SimpleRouter/Dummy/Handler/ExceptionHandlerThird.php b/tests/Pecee/SimpleRouter/Dummy/Handler/ExceptionHandlerThird.php index d295157..793746a 100644 --- a/tests/Pecee/SimpleRouter/Dummy/Handler/ExceptionHandlerThird.php +++ b/tests/Pecee/SimpleRouter/Dummy/Handler/ExceptionHandlerThird.php @@ -1,6 +1,6 @@ routes = $routes; + $this->aliasUrl = $aliasUrl; + } + + /** + * Called when router loads it's routes + * + * @param \Pecee\SimpleRouter\Router $router + * @param \Pecee\Http\Request $request + */ + public function boot(\Pecee\SimpleRouter\Router $router, \Pecee\Http\Request $request): void + { + foreach ($this->routes as $url) { + // If the current url matches the rewrite url, we use our custom route + + if ($request->getUrl()->contains($url) === true) { + $request->setRewriteUrl($this->aliasUrl); + } + + } + } +} \ No newline at end of file diff --git a/tests/Pecee/SimpleRouter/Dummy/Security/SilentTokenProvider.php b/tests/Pecee/SimpleRouter/Dummy/Security/SilentTokenProvider.php new file mode 100644 index 0000000..f7dfe52 --- /dev/null +++ b/tests/Pecee/SimpleRouter/Dummy/Security/SilentTokenProvider.php @@ -0,0 +1,41 @@ +refresh(); + } + + /** + * Refresh existing token + */ + public function refresh(): void + { + $this->token = uniqid('', false); + } + + /** + * Validate valid CSRF token + * + * @param string $token + * @return bool + */ + public function validate(string $token): bool + { + return ($token === $this->token); + } + + /** + * Get token token + * + * @param string|null $defaultValue + * @return string|null + */ + public function getToken(?string $defaultValue = null): ?string + { + return $this->token ?? $defaultValue; + } +} \ No newline at end of file diff --git a/tests/Pecee/SimpleRouter/EventHandlerTest.php b/tests/Pecee/SimpleRouter/EventHandlerTest.php new file mode 100644 index 0000000..7e49e6a --- /dev/null +++ b/tests/Pecee/SimpleRouter/EventHandlerTest.php @@ -0,0 +1,75 @@ +register(EventHandler::EVENT_ALL, function(EventArgument $arg) use(&$events) { + $key = \array_search($arg->getEventName(), $events, true); + unset($events[$key]); + }); + + TestRouter::addEventHandler($eventHandler); + + TestRouter::error(function (\Pecee\Http\Request $request, \Exception $error) { + + // Trigger rewrite + $request->setRewriteUrl('/'); + + }); + + TestRouter::get('/', 'DummyController@method1')->name('home'); + + TestRouter::router()->findRoute('home'); + TestRouter::router()->getUrl('home'); + + $csrfVerifier = new \Pecee\Http\Middleware\BaseCsrfVerifier(); + $csrfVerifier->setTokenProvider(new SilentTokenProvider()); + TestRouter::csrfVerifier($csrfVerifier); + + TestRouter::debug('/not-existing'); + + $this->assertEquals($events, []); + + TestRouter::router()->reset(); + + } + + public function testAllEvent() { + + $status = 0; + + $eventHandler = new EventHandler(); + $eventHandler->register(EventHandler::EVENT_ALL, function(EventArgument $arg) use(&$status) { + $status++; + }); + + TestRouter::addEventHandler($eventHandler); + + TestRouter::get('/', 'DummyController@method1'); + TestRouter::debug('/'); + + // All event should fire for each other event + $this->assertEquals(\count(EventHandler::$events), $status); + } + + public function testEvents() { + $this->assertEquals(true, true); + } + +} \ No newline at end of file diff --git a/tests/Pecee/SimpleRouter/InputHandlerTest.php b/tests/Pecee/SimpleRouter/InputHandlerTest.php new file mode 100644 index 0000000..69e36c0 --- /dev/null +++ b/tests/Pecee/SimpleRouter/InputHandlerTest.php @@ -0,0 +1,30 @@ +assertEquals(true, true); + } + + public function testPost() { + $this->assertEquals(true, true); + } + + public function testFile() { + $this->assertEquals(true, true); + } + + public function testFiles() { + $this->assertEquals(true, true); + } + + public function testAll() { + $this->assertEquals(true, true); + } + +} \ No newline at end of file diff --git a/tests/Pecee/SimpleRouter/RouterRouteTest.php b/tests/Pecee/SimpleRouter/RouterRouteTest.php index 63d69f9..cfdd490 100644 --- a/tests/Pecee/SimpleRouter/RouterRouteTest.php +++ b/tests/Pecee/SimpleRouter/RouterRouteTest.php @@ -93,6 +93,7 @@ class RouterRouteTest extends \PHPUnit\Framework\TestCase public function testDomainAllowedRoute() { $this->result = false; + TestRouter::request()->setHost('hello.world.com'); TestRouter::group(['domain' => '{subdomain}.world.com'], function () { TestRouter::get('/test', function ($subdomain = null) { @@ -100,7 +101,7 @@ class RouterRouteTest extends \PHPUnit\Framework\TestCase }); }); - TestRouter::request()->setHost('hello.world.com'); + TestRouter::debug('/test', 'get'); $this->assertTrue($this->result); @@ -109,6 +110,8 @@ class RouterRouteTest extends \PHPUnit\Framework\TestCase public function testDomainNotAllowedRoute() { + TestRouter::request()->setHost('other.world.com'); + $this->result = false; TestRouter::group(['domain' => '{subdomain}.world.com'], function () { @@ -117,9 +120,6 @@ class RouterRouteTest extends \PHPUnit\Framework\TestCase }); }); - TestRouter::request()->setHost('other.world.com'); - - TestRouter::debug('/test', 'get'); $this->assertFalse($this->result); diff --git a/tests/TestRouter.php b/tests/TestRouter.php index 60f820c..a6a5321 100644 --- a/tests/TestRouter.php +++ b/tests/TestRouter.php @@ -5,8 +5,10 @@ class TestRouter extends \Pecee\SimpleRouter\SimpleRouter public static function debugNoReset($testUrl, $testMethod = 'get') { - static::request()->setUrl($testUrl); - static::request()->setMethod($testMethod); + $request = static::request(); + + $request->setUrl((new \Pecee\Http\Url($testUrl))->setHost('local.unitTest')); + $request->setMethod($testMethod); static::start(); }