Simple Route

This commit is contained in:
2024-07-19 15:47:19 +02:00
parent 156d277e77
commit ab1df63788
156 changed files with 13136 additions and 0 deletions
@@ -0,0 +1,49 @@
<?php
require_once 'Dummy/DummyMiddleware.php';
require_once 'Dummy/DummyController.php';
require_once 'Dummy/Handler/ExceptionHandler.php';
require_once 'Dummy/Managers/TestBootManager.php';
require_once 'Dummy/Managers/FindUrlBootManager.php';
class BootManagerTest extends \PHPUnit\Framework\TestCase
{
public function testBootManagerRoutes()
{
$result = false;
TestRouter::get('/', function () use (&$result) {
$result = true;
});
TestRouter::get('/about', 'DummyController@method2');
TestRouter::get('/contact', 'DummyController@method3');
// Add boot-manager
TestRouter::addBootManager(new TestBootManager([
'/con' => '/about',
'/contact' => '/',
]));
TestRouter::debug('/contact');
$this->assertTrue($result);
}
public function testFindUrlFromBootManager()
{
TestRouter::get('/', 'DummyController@method1');
TestRouter::get('/about', 'DummyController@method2')->name('about');
TestRouter::get('/contact', 'DummyController@method3')->name('contact');
$result = false;
// Add boot-manager
TestRouter::addBootManager(new FindUrlBootManager($result));
TestRouter::debug('/');
$this->assertTrue($result);
}
}
@@ -0,0 +1,30 @@
<?php
require_once 'Dummy/DummyMiddleware.php';
require_once 'Dummy/DummyController.php';
require_once 'Dummy/ClassLoader/CustomClassLoader.php';
class ClassLoaderTest extends \PHPUnit\Framework\TestCase
{
public function testCustomClassLoader()
{
$result = false;
TestRouter::setCustomClassLoader(new CustomClassLoader());
TestRouter::get('/', 'NonExistingClass@method3');
TestRouter::get('/test-closure', function($status) use(&$result) {
$result = $status;
});
$classLoaderClass = TestRouter::debugOutput('/', 'get', false);
TestRouter::debugOutput('/test-closure');
$this->assertEquals('method3', $classLoaderClass);
$this->assertTrue($result);
TestRouter::router()->reset();
}
}
@@ -0,0 +1,66 @@
<?php
require_once 'Dummy/CsrfVerifier/DummyCsrfVerifier.php';
require_once 'Dummy/Security/SilentTokenProvider.php';
class CsrfVerifierTest extends \PHPUnit\Framework\TestCase
{
public function testTokenPass()
{
global $_POST;
$tokenProvider = new SilentTokenProvider();
$_POST[DummyCsrfVerifier::POST_KEY] = $tokenProvider->getToken();
TestRouter::router()->reset();
$router = TestRouter::router();
$router->getRequest()->setMethod(\Pecee\Http\Request::REQUEST_TYPE_POST);
$router->getRequest()->setUrl(new \Pecee\Http\Url('/page'));
$csrf = new DummyCsrfVerifier();
$csrf->setTokenProvider($tokenProvider);
$csrf->handle($router->getRequest());
// If handle doesn't throw exception, the test has passed
$this->assertTrue(true);
}
public function testTokenFail()
{
$this->expectException(\Pecee\Http\Middleware\Exceptions\TokenMismatchException::class);
global $_POST;
$tokenProvider = new SilentTokenProvider();
$router = TestRouter::router();
$router->getRequest()->setMethod(\Pecee\Http\Request::REQUEST_TYPE_POST);
$router->getRequest()->setUrl(new \Pecee\Http\Url('/page'));
$csrf = new DummyCsrfVerifier();
$csrf->setTokenProvider($tokenProvider);
$csrf->handle($router->getRequest());
}
public function testExcludeInclude()
{
$router = TestRouter::router();
$csrf = new DummyCsrfVerifier();
$request = $router->getRequest();
$request->setUrl(new \Pecee\Http\Url('/exclude-page'));
$this->assertTrue($csrf->testSkip($router->getRequest()));
$request->setUrl(new \Pecee\Http\Url('/exclude-all/page'));
$this->assertTrue($csrf->testSkip($router->getRequest()));
$request->setUrl(new \Pecee\Http\Url('/exclude-all/include-page'));
$this->assertFalse($csrf->testSkip($router->getRequest()));
$request->setUrl(new \Pecee\Http\Url('/include-page'));
$this->assertFalse($csrf->testSkip($router->getRequest()));
}
}
@@ -0,0 +1,71 @@
<?php
require_once 'Dummy/DummyController.php';
require_once 'Dummy/Middleware/IpRestrictMiddleware.php';
class CustomMiddlewareTest extends \PHPUnit\Framework\TestCase
{
public function testIpBlock() {
$this->expectException(\Pecee\SimpleRouter\Exceptions\HttpException::class);
global $_SERVER;
// Test exact ip
$_SERVER['remote-addr'] = '5.5.5.5';
TestRouter::group(['middleware' => IpRestrictMiddleware::class], function() {
TestRouter::get('/fail', 'DummyController@method1');
});
TestRouter::debug('/fail');
// Test ip-range
$_SERVER['remote-addr'] = '8.8.4.4';
TestRouter::router()->reset();
TestRouter::group(['middleware' => IpRestrictMiddleware::class], function() {
TestRouter::get('/fail', 'DummyController@method1');
});
TestRouter::debug('/fail');
}
public function testIpSuccess() {
global $_SERVER;
// Test ip that is not blocked
$_SERVER['remote-addr'] = '6.6.6.6';
TestRouter::router()->reset();
TestRouter::group(['middleware' => IpRestrictMiddleware::class], function() {
TestRouter::get('/success', 'DummyController@method1');
});
TestRouter::debug('/success');
// Test ip in whitelist
$_SERVER['remote-addr'] = '8.8.2.2';
TestRouter::router()->reset();
TestRouter::group(['middleware' => IpRestrictMiddleware::class], function() {
TestRouter::get('/success', 'DummyController@method1');
});
TestRouter::debug('/success');
$this->assertTrue(true);
}
}
@@ -0,0 +1,26 @@
<?php
class CustomClassLoader implements \Pecee\SimpleRouter\ClassLoader\IClassLoader
{
public function loadClass(string $class)
{
return new DummyController();
}
/**
* Called when loading class method
* @param object $class
* @param string $method
* @param array $parameters
* @return object
*/
public function loadClassMethod($class, string $method, array $parameters)
{
return call_user_func_array([$class, $method], [true]);
}
public function loadClosure(callable $closure, array $parameters)
{
return call_user_func_array($closure, [true]);
}
}
@@ -0,0 +1,18 @@
<?php
class DummyCsrfVerifier extends \Pecee\Http\Middleware\BaseCsrfVerifier {
protected array $except = [
'/exclude-page',
'/exclude-all/*',
];
protected array $include = [
'/exclude-all/include-page',
];
public function testSkip(\Pecee\Http\Request $request) {
return $this->skip($request);
}
}
@@ -0,0 +1,46 @@
<?php
class DummyController
{
public function index()
{
}
public function method1()
{
}
public function method2()
{
}
public function method3()
{
return 'method3';
}
public function param($params = null)
{
echo join(', ', func_get_args());
}
public function getTest()
{
echo 'getTest';
}
public function postTest()
{
echo 'postTest';
}
public function putTest()
{
echo 'putTest';
}
}
@@ -0,0 +1,13 @@
<?php
require_once 'Exception/MiddlewareLoadedException.php';
use Pecee\Http\Request;
class DummyMiddleware implements \Pecee\Http\Middleware\IMiddleware
{
public function handle(Request $request) : void
{
throw new MiddlewareLoadedException('Middleware loaded!');
}
}
@@ -0,0 +1,5 @@
<?php
class ExceptionHandlerException extends \Exception
{
}
@@ -0,0 +1,4 @@
<?php
class MiddlewareLoadedException extends \Exception
{
}
@@ -0,0 +1,18 @@
<?php
class ResponseException extends \Exception
{
protected $response;
public function __construct($response)
{
$this->response = $response;
parent::__construct('', 0);
}
public function getResponse()
{
return $this->response;
}
}
@@ -0,0 +1,10 @@
<?php
class ExceptionHandler implements \Pecee\SimpleRouter\Handlers\IExceptionHandler
{
public function handleError(\Pecee\Http\Request $request, \Exception $error) : void
{
echo $error->getMessage();
}
}
@@ -0,0 +1,13 @@
<?php
class ExceptionHandlerFirst implements \Pecee\SimpleRouter\Handlers\IExceptionHandler
{
public function handleError(\Pecee\Http\Request $request, \Exception $error) : void
{
global $stack;
$stack[] = static::class;
$request->setUrl(new \Pecee\Http\Url('/'));
}
}
@@ -0,0 +1,13 @@
<?php
class ExceptionHandlerSecond implements \Pecee\SimpleRouter\Handlers\IExceptionHandler
{
public function handleError(\Pecee\Http\Request $request, \Exception $error) : void
{
global $stack;
$stack[] = static::class;
$request->setUrl(new \Pecee\Http\Url('/'));
}
}
@@ -0,0 +1,13 @@
<?php
class ExceptionHandlerThird implements \Pecee\SimpleRouter\Handlers\IExceptionHandler
{
public function handleError(\Pecee\Http\Request $request, \Exception $error) : void
{
global $stack;
$stack[] = static::class;
throw new ResponseException('ExceptionHandler loaded');
}
}
@@ -0,0 +1,26 @@
<?php
class FindUrlBootManager implements \Pecee\SimpleRouter\IRouterBootManager
{
protected $result;
public function __construct(&$result)
{
$this->result = &$result;
}
/**
* 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
{
$contact = $router->findRoute('contact');
if($contact !== null) {
$this->result = true;
}
}
}
@@ -0,0 +1,30 @@
<?php
class TestBootManager implements \Pecee\SimpleRouter\IRouterBootManager
{
protected $rewrite;
public function __construct(array $rewrite)
{
$this->rewrite = $rewrite;
}
/**
* 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->rewrite as $url => $rewrite) {
// If the current url matches the rewrite url, we use our custom route
if ($request->getUrl()->contains($url) === true) {
$request->setRewriteUrl($rewrite);
}
}
}
}
@@ -0,0 +1,14 @@
<?php
class IpRestrictMiddleware extends \Pecee\Http\Middleware\IpRestrictAccess {
protected array $ipBlacklist = [
'5.5.5.5',
'8.8.*',
];
protected array $ipWhitelist = [
'8.8.2.2',
];
}
@@ -0,0 +1,16 @@
<?php
use Pecee\Http\Middleware\IMiddleware;
use Pecee\Http\Request;
class RewriteMiddleware implements IMiddleware {
public function handle(Request $request) : void {
$request->setRewriteCallback(function() {
return 'ok';
});
}
}
@@ -0,0 +1,11 @@
<?php
namespace MyNamespace;
class NSController {
public function method()
{
return true;
}
}
@@ -0,0 +1,39 @@
<?php
class ResourceController implements \Pecee\Controllers\IResourceController
{
public function index() : ?string
{
return 'index';
}
public function show($id) : ?string
{
return 'show ' . $id;
}
public function store() : ?string
{
return 'store';
}
public function create() : ?string
{
return 'create';
}
public function edit($id) : ?string
{
return 'edit ' . $id;
}
public function update($id) : ?string
{
return 'update ' . $id;
}
public function destroy($id) : ?string
{
return 'destroy ' . $id;
}
}
@@ -0,0 +1,11 @@
<?php
use Pecee\Http\Request;
class DummyLoadableRoute extends Pecee\SimpleRouter\Route\LoadableRoute {
public function matchRoute(string $url, Request $request): bool
{
return false;
}
}
@@ -0,0 +1,41 @@
<?php
class SilentTokenProvider implements \Pecee\Http\Security\ITokenProvider {
protected $token;
public function __construct()
{
$this->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;
}
}
@@ -0,0 +1,151 @@
<?php
require_once 'Dummy/DummyMiddleware.php';
require_once 'Dummy/DummyController.php';
require_once 'Dummy/Handler/ExceptionHandler.php';
require_once 'Dummy/Security/SilentTokenProvider.php';
require_once 'Dummy/Managers/TestBootManager.php';
use Pecee\SimpleRouter\Event\EventArgument;
use Pecee\SimpleRouter\Handlers\EventHandler;
class EventHandlerTest extends \PHPUnit\Framework\TestCase
{
public function testAllEventTriggered()
{
$events = EventHandler::$events;
// Remove the all event
unset($events[\array_search(EventHandler::EVENT_ALL, $events, true)]);
$eventHandler = new EventHandler();
$eventHandler->register(EventHandler::EVENT_ALL, function (EventArgument $arg) use (&$events) {
$key = \array_search($arg->getEventName(), $events, true);
unset($events[$key]);
});
TestRouter::addEventHandler($eventHandler);
// Add rewrite
TestRouter::error(function (\Pecee\Http\Request $request, \Exception $error) {
// Trigger rewrite
$request->setRewriteUrl('/');
});
TestRouter::get('/', 'DummyController@method1')->name('home');
// Trigger findRoute
TestRouter::router()->findRoute('home');
// Trigger getUrl
TestRouter::router()->getUrl('home');
// Add csrf-verifier
$csrfVerifier = new \Pecee\Http\Middleware\BaseCsrfVerifier();
$csrfVerifier->setTokenProvider(new SilentTokenProvider());
TestRouter::csrfVerifier($csrfVerifier);
// Add boot-manager
TestRouter::addBootManager(new TestBootManager([
'/test' => '/',
]));
// Start router
TestRouter::debug('/non-existing');
$this->assertEquals($events, []);
}
public function testAllEvent()
{
$status = false;
$eventHandler = new EventHandler();
$eventHandler->register(EventHandler::EVENT_ALL, function (EventArgument $arg) use (&$status) {
$status = true;
});
TestRouter::addEventHandler($eventHandler);
TestRouter::get('/', 'DummyController@method1');
TestRouter::debug('/');
// All event should fire for each other event
$this->assertEquals(true, $status);
}
public function testPrefixEvent()
{
$eventHandler = new EventHandler();
$eventHandler->register(EventHandler::EVENT_ADD_ROUTE, function (EventArgument $arg) use (&$status) {
if ($arg->route instanceof \Pecee\SimpleRouter\Route\LoadableRoute) {
$arg->route->prependUrl('/local-path');
}
});
TestRouter::addEventHandler($eventHandler);
$status = false;
TestRouter::get('/', function () use (&$status) {
$status = true;
});
TestRouter::debug('/local-path');
$this->assertTrue($status);
}
public function testCustomBasePath() {
$basePath = '/basepath/';
$eventHandler = new EventHandler();
$eventHandler->register(EventHandler::EVENT_ADD_ROUTE, function(EventArgument $data) use($basePath) {
// Skip routes added by group
if($data->isSubRoute === false) {
switch (true) {
case $data->route instanceof \Pecee\SimpleRouter\Route\ILoadableRoute:
$data->route->prependUrl($basePath);
break;
case $data->route instanceof \Pecee\SimpleRouter\Route\IGroupRoute:
$data->route->prependPrefix($basePath);
break;
}
}
});
$results = [];
TestRouter::addEventHandler($eventHandler);
TestRouter::get('/about', function() use(&$results) {
$results[] = 'about';
});
TestRouter::group(['prefix' => '/admin'], function() use(&$results) {
TestRouter::get('/', function() use(&$results) {
$results[] = 'admin';
});
});
TestRouter::router()->setRenderMultipleRoutes(false);
TestRouter::debugNoReset('/basepath/about');
TestRouter::debugNoReset('/basepath/admin');
$this->assertEquals(['about', 'admin'], $results);
}
}
@@ -0,0 +1,287 @@
<?php
use Pecee\Http\Input\InputFile;
require_once 'Dummy/DummyMiddleware.php';
require_once 'Dummy/DummyController.php';
require_once 'Dummy/Handler/ExceptionHandler.php';
class InputHandlerTest extends \PHPUnit\Framework\TestCase
{
protected $names = [
'Lester',
'Michael',
'Franklin',
'Trevor',
];
protected $brands = [
'Samsung',
'Apple',
'HP',
'Canon',
];
protected $sodas = [
0 => 'Pepsi',
1 => 'Coca Cola',
2 => 'Harboe',
3 => 'Mountain Dew',
];
protected $day = 'monday';
public function testPost()
{
global $_POST;
$_POST = [
'names' => $this->names,
'day' => $this->day,
'sodas' => $this->sodas,
];
$router = TestRouter::router();
$router->reset();
$router->getRequest()->setMethod('post');
$handler = TestRouter::request()->getInputHandler();
$this->assertEquals($this->names, $handler->value('names'));
$this->assertEquals($this->names, $handler->all(['names'])['names']);
$this->assertEquals($this->day, $handler->value('day'));
$this->assertInstanceOf(\Pecee\Http\Input\InputItem::class, $handler->find('day'));
$this->assertInstanceOf(\Pecee\Http\Input\InputItem::class, $handler->post('day'));
$this->assertInstanceOf(\Pecee\Http\Input\InputItem::class, $handler->find('day', 'post'));
// Check non-existing and wrong request-type
$this->assertCount(1, $handler->all(['non-existing']));
$this->assertEmpty($handler->all(['non-existing'])['non-existing']);
$this->assertNull($handler->value('non-existing'));
$this->assertNull($handler->find('non-existing'));
$this->assertNull($handler->value('names', null, 'get'));
$this->assertNull($handler->find('names', 'get'));
$this->assertEquals($this->sodas, $handler->value('sodas'));
$objects = $handler->find('names');
$this->assertInstanceOf(\Pecee\Http\Input\InputItem::class, $objects);
$this->assertCount(4, $objects);
/* @var $object \Pecee\Http\Input\InputItem */
foreach($objects as $i => $object) {
$this->assertInstanceOf(\Pecee\Http\Input\InputItem::class, $object);
$this->assertEquals($this->names[$i], $object->getValue());
}
// Reset
$_POST = [];
}
public function testGet()
{
global $_GET;
$_GET = [
'names' => $this->names,
'day' => $this->day,
];
$router = TestRouter::router();
$router->reset();
$router->getRequest()->setMethod('get');
$handler = TestRouter::request()->getInputHandler();
$this->assertEquals($this->names, $handler->value('names'));
$this->assertEquals($this->names, $handler->all(['names'])['names']);
$this->assertEquals($this->day, $handler->value('day'));
$this->assertInstanceOf(\Pecee\Http\Input\InputItem::class, $handler->find('day'));
$this->assertInstanceOf(\Pecee\Http\Input\InputItem::class, $handler->get('day'));
// Check non-existing and wrong request-type
$this->assertCount(1, $handler->all(['non-existing']));
$this->assertEmpty($handler->all(['non-existing'])['non-existing']);
$this->assertNull($handler->value('non-existing'));
$this->assertNull($handler->find('non-existing'));
$this->assertNull($handler->value('names', null, 'post'));
$this->assertNull($handler->find('names', 'post'));
$objects = $handler->find('names');
$this->assertInstanceOf(\Pecee\Http\Input\InputItem::class, $objects);
$this->assertCount(4, $objects);
/* @var $object \Pecee\Http\Input\InputItem */
foreach($objects as $i => $object) {
$this->assertInstanceOf(\Pecee\Http\Input\InputItem::class, $object);
$this->assertEquals($this->names[$i], $object->getValue());
}
// Reset
$_GET = [];
}
public function testFindInput() {
global $_POST;
$_POST['hello'] = 'motto';
$router = TestRouter::router();
$router->reset();
$router->getRequest()->setMethod('post');
$inputHandler = TestRouter::request()->getInputHandler();
$value = $inputHandler->value('hello', null, \Pecee\Http\Request::$requestTypesPost);
$this->assertEquals($_POST['hello'], $value);
}
public function testFile()
{
global $_FILES;
$testFile = $this->generateFile();
$_FILES = [
'test_input' => $testFile,
];
$router = TestRouter::router();
$router->reset();
$router->getRequest()->setMethod('post');
$inputHandler = TestRouter::request()->getInputHandler();
$testFileContent = md5(uniqid('test', false));
$file = $inputHandler->file('test_input');
$this->assertInstanceOf(InputFile::class, $file);
$this->assertEquals($testFile['name'], $file->getFilename());
$this->assertEquals($testFile['type'], $file->getType());
$this->assertEquals($testFile['tmp_name'], $file->getTmpName());
$this->assertEquals($testFile['error'], $file->getError());
$this->assertEquals($testFile['size'], $file->getSize());
$this->assertEquals(pathinfo($testFile['name'], PATHINFO_EXTENSION), $file->getExtension());
file_put_contents($testFile['tmp_name'], $testFileContent);
$this->assertEquals($testFileContent, $file->getContents());
// Cleanup
unlink($testFile['tmp_name']);
}
public function testFilesArray()
{
global $_FILES;
$testFiles = [
$file = $this->generateFile(),
$file = $this->generateFile(),
$file = $this->generateFile(),
$file = $this->generateFile(),
$file = $this->generateFile(),
];
$_FILES = [
'my_files' => $testFiles,
];
$router = TestRouter::router();
$router->reset();
$router->getRequest()->setMethod('post');
$inputHandler = TestRouter::request()->getInputHandler();
$files = $inputHandler->file('my_files');
$this->assertCount(5, $files);
/* @var $file InputFile */
foreach ($files as $key => $file) {
$testFileContent = md5(uniqid('test', false));
$this->assertInstanceOf(InputFile::class, $file);
$this->assertEquals($testFiles[$key]['name'], $file->getFilename());
$this->assertEquals($testFiles[$key]['type'], $file->getType());
$this->assertEquals($testFiles[$key]['tmp_name'], $file->getTmpName());
$this->assertEquals($testFiles[$key]['error'], $file->getError());
$this->assertEquals($testFiles[$key]['size'], $file->getSize());
$this->assertEquals(pathinfo($testFiles[$key]['name'], PATHINFO_EXTENSION), $file->getExtension());
file_put_contents($testFiles[$key]['tmp_name'], $testFileContent);
$this->assertEquals($testFileContent, $file->getContents());
// Cleanup
unlink($testFiles[$key]['tmp_name']);
}
}
public function testAll()
{
global $_POST;
global $_GET;
$_POST = [
'names' => $this->names,
'is_sad' => true,
];
$_GET = [
'brands' => $this->brands,
'is_happy' => true,
];
$router = TestRouter::router();
$router->reset();
$router->getRequest()->setMethod('post');
$handler = TestRouter::request()->getInputHandler();
// GET
$brandsFound = $handler->all(['brands', 'nothing']);
$this->assertArrayHasKey('brands', $brandsFound);
$this->assertArrayHasKey('nothing', $brandsFound);
$this->assertEquals($this->brands, $brandsFound['brands']);
$this->assertNull($brandsFound['nothing']);
// POST
$namesFound = $handler->all(['names', 'nothing']);
$this->assertArrayHasKey('names', $namesFound);
$this->assertArrayHasKey('nothing', $namesFound);
$this->assertEquals($this->names, $namesFound['names']);
$this->assertNull($namesFound['nothing']);
// DEFAULT VALUE
$nonExisting = $handler->all([
'non-existing'
]);
$this->assertArrayHasKey('non-existing', $nonExisting);
$this->assertNull($nonExisting['non-existing']);
// Reset
$_GET = [];
$_POST = [];
}
protected function generateFile()
{
return [
'name' => uniqid('', false) . '.txt',
'type' => 'text/plain',
'tmp_name' => sys_get_temp_dir() . '/phpYfWUiw',
'error' => 0,
'size' => rand(3, 40),
];
}
protected function generateFileContent()
{
return md5(uniqid('', false));
}
}
@@ -0,0 +1,27 @@
<?php
require_once 'Dummy/Route/DummyLoadableRoute.php';
class LoadableRouteTest extends \PHPUnit\Framework\TestCase
{
public function testSetUrlUpdatesParameters()
{
$route = new DummyLoadableRoute();
$this->assertEmpty($route->getParameters());
$route->setUrl('/');
$this->assertEmpty($route->getParameters());
$expected = ['param' => null, 'optionalParam' => null];
$route->setUrl('/{param}/{optionalParam?}');
$this->assertEquals($expected, $route->getParameters());
$expected = ['otherParam' => null];
$route->setUrl('/{otherParam}');
$this->assertEquals($expected, $route->getParameters());
$expected = [];
$route->setUrl('/');
$this->assertEquals($expected, $route->getParameters());
}
}
@@ -0,0 +1,35 @@
<?php
require_once 'Dummy/DummyMiddleware.php';
require_once 'Dummy/DummyController.php';
require_once 'Dummy/Handler/ExceptionHandler.php';
class MiddlewareTest extends \PHPUnit\Framework\TestCase
{
public function testMiddlewareFound()
{
$this->expectException(MiddlewareLoadedException::class);
TestRouter::group(['exceptionHandler' => 'ExceptionHandler'], function () {
TestRouter::get('/my/test/url', 'DummyController@method1', ['middleware' => 'DummyMiddleware']);
});
TestRouter::debug('/my/test/url', 'get');
}
public function testNestedMiddlewareDontLoad()
{
TestRouter::group(['exceptionHandler' => 'ExceptionHandler', 'middleware' => 'DummyMiddleware'], function () {
TestRouter::get('/middleware', 'DummyController@method1');
});
TestRouter::get('/my/test/url', 'DummyController@method1');
TestRouter::debug('/my/test/url', 'get');
$this->assertTrue(true);
}
}
@@ -0,0 +1,84 @@
<?php
use Pecee\Http\Input\InputFile;
require_once 'Dummy/DummyMiddleware.php';
require_once 'Dummy/DummyController.php';
require_once 'Dummy/Handler/ExceptionHandler.php';
class RequestTest extends \PHPUnit\Framework\TestCase
{
protected function processHeader($name, $value, callable $callback)
{
global $_SERVER;
$_SERVER[$name] = $value;
$router = TestRouter::router();
$router->reset();
$request = $router->getRequest();
$callback($request);
// Reset everything
$_SERVER[$name] = null;
$router->reset();
}
public function testContentTypeParse()
{
global $_SERVER;
// Test normal content-type
$contentType = 'application/x-www-form-urlencoded';
$this->processHeader('content_type', $contentType, function(\Pecee\Http\Request $request) use($contentType) {
$this->assertEquals($contentType, $request->getContentType());
});
// Test special content-type with encoding
$contentTypeWithEncoding = 'application/x-www-form-urlencoded; charset=UTF-8';
$this->processHeader('content_type', $contentTypeWithEncoding, function(\Pecee\Http\Request $request) use($contentType) {
$this->assertEquals($contentType, $request->getContentType());
});
}
public function testGetIp()
{
$ip = '1.1.1.1';
$this->processHeader('remote_addr', $ip, function(\Pecee\Http\Request $request) use($ip) {
$this->assertEquals($ip, $request->getIp());
});
$ip = '2.2.2.2';
$this->processHeader('http-cf-connecting-ip', $ip, function(\Pecee\Http\Request $request) use($ip) {
$this->assertEquals($ip, $request->getIp());
});
$ip = '3.3.3.3';
$this->processHeader('http-client-ip', $ip, function(\Pecee\Http\Request $request) use($ip) {
$this->assertEquals($ip, $request->getIp());
});
$ip = '4.4.4.4';
$this->processHeader('http-x-forwarded-for', $ip, function(\Pecee\Http\Request $request) use($ip) {
$this->assertEquals($ip, $request->getIp());
});
// Test safe
$ip = '5.5.5.5';
$this->processHeader('http-x-forwarded-for', $ip, function(\Pecee\Http\Request $request) {
$this->assertEquals(null, $request->getIp(true));
});
}
// TODO: implement more test-cases
}
@@ -0,0 +1,47 @@
<?php
require_once 'Dummy/DummyMiddleware.php';
require_once 'Dummy/DummyController.php';
require_once 'Dummy/Exception/ExceptionHandlerException.php';
class RouterCallbackExceptionHandlerTest extends \PHPUnit\Framework\TestCase
{
public function testCallbackExceptionHandler()
{
$this->expectException(ExceptionHandlerException::class);
// Match normal route on alias
TestRouter::get('/my-new-url', 'DummyController@method2');
TestRouter::get('/my-url', 'DummyController@method1');
TestRouter::error(function (\Pecee\Http\Request $request, \Exception $exception) {
throw new ExceptionHandlerException();
});
TestRouter::debug('/404-url');
}
public function testExceptionHandlerCallback() {
TestRouter::group(['prefix' => null], function() {
TestRouter::get('/', function() {
return 'Hello world';
});
TestRouter::get('/not-found', 'DummyController@method1');
TestRouter::error(function(\Pecee\Http\Request $request, \Exception $exception) {
if($exception instanceof \Pecee\SimpleRouter\Exceptions\NotFoundHttpException && $exception->getCode() === 404) {
return $request->setRewriteCallback(static function() {
return 'success';
});
}
});
});
$result = TestRouter::debugOutput('/thisdoes-not/existssss', 'get');
$this->assertEquals('success', $result);
}
}
@@ -0,0 +1,41 @@
<?php
require_once 'Dummy/DummyController.php';
class RouterControllerTest extends \PHPUnit\Framework\TestCase
{
public function testGet()
{
// Match normal route on alias
TestRouter::controller('/url', 'DummyController');
$response = TestRouter::debugOutput('/url/test', 'get');
$this->assertEquals('getTest', $response);
}
public function testPost()
{
// Match normal route on alias
TestRouter::controller('/url', 'DummyController');
$response = TestRouter::debugOutput('/url/test', 'post');
$this->assertEquals('postTest', $response);
}
public function testPut()
{
// Match normal route on alias
TestRouter::controller('/url', 'DummyController');
$response = TestRouter::debugOutput('/url/test', 'put');
$this->assertEquals('putTest', $response);
}
}
@@ -0,0 +1,120 @@
<?php
require_once 'Dummy/DummyMiddleware.php';
require_once 'Dummy/DummyController.php';
class RouterGroupTest extends \PHPUnit\Framework\TestCase
{
public function testGroupLoad()
{
$result = false;
TestRouter::group(['prefix' => '/group'], function () use (&$result) {
$result = true;
});
try {
TestRouter::debug('/', 'get');
} catch (\Exception $e) {
}
$this->assertTrue($result);
}
public function testNestedGroup()
{
TestRouter::group(['prefix' => '/api'], function () {
TestRouter::group(['prefix' => '/v1'], function () {
TestRouter::get('/test', 'DummyController@method1');
});
});
TestRouter::debug('/api/v1/test', 'get');
$this->assertTrue(true);
}
public function testMultipleRoutes()
{
TestRouter::group(['prefix' => '/api'], function () {
TestRouter::group(['prefix' => '/v1'], function () {
TestRouter::get('/test', 'DummyController@method1');
});
});
TestRouter::get('/my/match', 'DummyController@method1');
TestRouter::group(['prefix' => '/service'], function () {
TestRouter::group(['prefix' => '/v1'], function () {
TestRouter::get('/no-match', 'DummyController@method1');
});
});
TestRouter::debug('/my/match', 'get');
$this->assertTrue(true);
}
public function testUrls()
{
// Test array name
TestRouter::get('/my/fancy/url/1', 'DummyController@method1', ['as' => 'fancy1']);
// Test method name
TestRouter::get('/my/fancy/url/2', 'DummyController@method1')->setName('fancy2');
TestRouter::debugNoReset('/my/fancy/url/1');
$this->assertEquals('/my/fancy/url/1/', TestRouter::getUrl('fancy1'));
$this->assertEquals('/my/fancy/url/2/', TestRouter::getUrl('fancy2'));
TestRouter::router()->reset();
}
public function testNamespaceExtend()
{
TestRouter::group(['namespace' => '\My\Namespace'], function () use (&$result) {
TestRouter::group(['namespace' => 'Service'], function () use (&$result) {
TestRouter::get('/test', function () use (&$result) {
return \Pecee\SimpleRouter\SimpleRouter::router()->getRequest()->getLoadedRoute()->getNamespace();
});
});
});
$namespace = TestRouter::debugOutput('/test');
$this->assertEquals('\My\Namespace\Service', $namespace);
}
public function testNamespaceOverwrite()
{
TestRouter::group(['namespace' => '\My\Namespace'], function () use (&$result) {
TestRouter::group(['namespace' => '\Service'], function () use (&$result) {
TestRouter::get('/test', function () use (&$result) {
return \Pecee\SimpleRouter\SimpleRouter::router()->getRequest()->getLoadedRoute()->getNamespace();
});
});
});
$namespace = TestRouter::debugOutput('/test');
$this->assertEquals('\Service', $namespace);
}
}
@@ -0,0 +1,116 @@
<?php
require_once 'Dummy/DummyMiddleware.php';
require_once 'Dummy/DummyController.php';
require_once 'Dummy/Handler/ExceptionHandler.php';
class RouterPartialGroupTest extends \PHPUnit\Framework\TestCase
{
public function testParameters()
{
$result1 = null;
$result2 = null;
TestRouter::partialGroup('{param1}/{param2}', function ($param1 = null, $param2 = null) use (&$result1, &$result2) {
$result1 = $param1;
$result2 = $param2;
TestRouter::get('/', 'DummyController@method1');
});
TestRouter::debug('/param1/param2', 'get');
$this->assertEquals('param1', $result1);
$this->assertEquals('param2', $result2);
}
/**
* Fixed issue with partial routes not loading child groups.
* Reported in issue: #456
*/
public function testPartialGroupWithGroup() {
$lang = null;
$route1 = '/lang/da/test/';
$route2 = '/lang/da/auth';
$route3 = '/lang/da/auth/test';
TestRouter::partialGroup(
'/lang/{test}/',
function ($lang = 'en') use($route1, $route2, $route3) {
TestRouter::get('/test/', function () use($route1) {
return $route1;
});
TestRouter::group(['prefix' => '/auth/'], function () use($route2, $route3) {
TestRouter::get('/', function() use($route2) {
return $route2;
});
TestRouter::get('/test', function () use($route3){
return $route3;
});
});
}
);
$test1 = TestRouter::debugOutput('/lang/da/test', 'get', false);
$test2 = TestRouter::debugOutput('/lang/da/auth', 'get', false);
$test3 = TestRouter::debugOutput('/lang/da/auth/test', 'get', false);
$this->assertEquals($test1, $route1);
$this->assertEquals($test2, $route2);
$this->assertEquals($test3, $route3);
}
public function testPhp8CallUserFunc() {
TestRouter::router()->reset();
$result = false;
$lang = 'de';
TestRouter::group(['prefix' => '/lang'], function() use(&$result) {
TestRouter::get('/{lang}', function ($lang) use(&$result) {
$result = $lang;
});
});
TestRouter::debug("/lang/$lang");
$this->assertEquals($lang, $result);
// Test partial group
$lang = 'de';
$userId = 22;
$result1 = false;
$result2 = false;
TestRouter::partialGroup(
'/lang/{lang}/',
function ($lang) use(&$result1, &$result2) {
$result1 = $lang;
TestRouter::get('/user/{userId}', function ($userId) use(&$result2) {
$result2 = $userId;
});
});
TestRouter::debug("/lang/$lang/user/$userId");
$this->assertEquals($lang, $result1);
$this->assertEquals($userId, $result2);
}
}
@@ -0,0 +1,84 @@
<?php
require_once 'Dummy/ResourceController.php';
class RouterResourceTest extends \PHPUnit\Framework\TestCase
{
public function testResourceStore()
{
TestRouter::resource('/resource', 'ResourceController');
$response = TestRouter::debugOutput('/resource', 'post');
$this->assertEquals('store', $response);
}
public function testResourceCreate()
{
TestRouter::resource('/resource', 'ResourceController');
$response = TestRouter::debugOutput('/resource/create', 'get');
$this->assertEquals('create', $response);
}
public function testResourceIndex()
{
TestRouter::resource('/resource', 'ResourceController');
$response = TestRouter::debugOutput('/resource', 'get');
$this->assertEquals('index', $response);
}
public function testResourceDestroy()
{
TestRouter::resource('/resource', 'ResourceController');
$response = TestRouter::debugOutput('/resource/38', 'delete');
$this->assertEquals('destroy 38', $response);
}
public function testResourceEdit()
{
TestRouter::resource('/resource', 'ResourceController');
$response = TestRouter::debugOutput('/resource/38/edit', 'get');
$this->assertEquals('edit 38', $response);
}
public function testResourceUpdate()
{
TestRouter::resource('/resource', 'ResourceController');
$response = TestRouter::debugOutput('/resource/38', 'put');
$this->assertEquals('update 38', $response);
}
public function testResourceGet()
{
TestRouter::resource('/resource', 'ResourceController');
$response = TestRouter::debugOutput('/resource/38', 'get');
$this->assertEquals('show 38', $response);
}
public function testResourceUrls()
{
TestRouter::resource('/resource', 'ResourceController')->name('resource');
TestRouter::debugNoReset('/resource');
$this->assertEquals('/resource/3/create/', TestRouter::router()->getUrl('resource.create', ['id' => 3]));
$this->assertEquals('/resource/5/edit/', TestRouter::router()->getUrl('resource.edit', ['id' => 5]));
$this->assertEquals('/resource/6/', TestRouter::router()->getUrl('resource.update', ['id' => 6]));
$this->assertEquals('/resource/9/', TestRouter::router()->getUrl('resource.destroy', ['id' => 9]));
$this->assertEquals('/resource/12/', TestRouter::router()->getUrl('resource.delete', ['id' => 12]));
$this->assertEquals('/resource/', TestRouter::router()->getUrl('resource'));
TestRouter::router()->reset();
}
}
@@ -0,0 +1,203 @@
<?php
require_once 'Dummy/DummyController.php';
require_once 'Dummy/Exception/ResponseException.php';
require_once 'Dummy/Handler/ExceptionHandlerFirst.php';
require_once 'Dummy/Handler/ExceptionHandlerSecond.php';
require_once 'Dummy/Handler/ExceptionHandlerThird.php';
require_once 'Dummy/Middleware/RewriteMiddleware.php';
class RouterRewriteTest extends \PHPUnit\Framework\TestCase
{
/**
* Redirects to another route through 3 exception handlers.
*
* You will see "ExceptionHandler 1 loaded" 2 times. This happen because
* the exceptionhandler is asking the router to reload.
*
* That means that the exceptionhandler is loaded again, but this time
* the router ignores the same rewrite-route to avoid loop - loads
* the second which have same behavior and is also ignored before
* throwing the final Exception in ExceptionHandler 3.
*
* So this tests:
* 1. If ExceptionHandlers loads
* 2. If ExceptionHandlers load in the correct order
* 3. If ExceptionHandlers can rewrite the page on error
* 4. If the router can avoid redirect-loop due to developer has started loop.
* 5. And finally if we reaches the last exception-handler and that the correct
* exception-type is being thrown.
*/
public function testExceptionHandlerRewrite()
{
global $stack;
$stack = [];
TestRouter::group(['exceptionHandler' => [ExceptionHandlerFirst::class, ExceptionHandlerSecond::class]], function () {
TestRouter::group(['prefix' => '/test', 'exceptionHandler' => ExceptionHandlerThird::class], function () {
TestRouter::get('/my-path', 'DummyController@method1');
});
});
try {
TestRouter::debug('/test/non-existing', 'get');
} catch (\ResponseException $e) {
}
$expectedStack = [
ExceptionHandlerThird::class,
ExceptionHandlerSecond::class,
ExceptionHandlerFirst::class,
];
$this->assertEquals($expectedStack, $stack);
}
public function testStopMergeExceptionHandlers()
{
global $stack;
$stack = [];
TestRouter::group(['prefix' => '/', 'exceptionHandler' => ExceptionHandlerFirst::class], function () {
TestRouter::group(['prefix' => '/admin', 'exceptionHandler' => ExceptionHandlerSecond::class, 'mergeExceptionHandlers' => false], function () {
TestRouter::get('/my-path', 'DummyController@method1');
});
});
try {
TestRouter::debug('/admin/my-path-test', 'get');
} catch (\Pecee\SimpleRouter\Exceptions\NotFoundHttpException $e) {
}
$expectedStack = [
ExceptionHandlerSecond::class,
];
$this->assertEquals($expectedStack, $stack);
}
public function testRewriteExceptionMessage()
{
$this->expectException(\Pecee\SimpleRouter\Exceptions\NotFoundHttpException::class);
TestRouter::error(function (\Pecee\Http\Request $request, \Exception $error) {
if (strtolower($request->getUrl()->getPath()) === '/my/test/') {
$request->setRewriteUrl('/another-non-existing');
}
});
TestRouter::debug('/my/test', 'get');
}
public function testRewriteUrlFromRoute()
{
TestRouter::get('/old', function () {
TestRouter::request()->setRewriteUrl('/new');
});
TestRouter::get('/new', function () {
echo 'ok';
});
TestRouter::get('/new1', function () {
echo 'ok';
});
TestRouter::get('/new2', function () {
echo 'ok';
});
$output = TestRouter::debugOutput('/old');
$this->assertEquals('ok', $output);
}
public function testRewriteCallbackFromRoute()
{
TestRouter::get('/old', function () {
TestRouter::request()->setRewriteUrl('/new');
});
TestRouter::get('/new', function () {
return 'ok';
});
TestRouter::get('/new1', function () {
return 'fail';
});
TestRouter::get('/new/2', function () {
return 'fail';
});
$output = TestRouter::debugOutput('/old');
TestRouter::router()->reset();
$this->assertEquals('ok', $output);
}
public function testRewriteRouteFromRoute()
{
TestRouter::get('/match', function () {
TestRouter::request()->setRewriteRoute(new \Pecee\SimpleRouter\Route\RouteUrl('/match', function () {
return 'ok';
}));
});
TestRouter::get('/old1', function () {
return 'fail';
});
TestRouter::get('/old/2', function () {
return 'fail';
});
TestRouter::get('/new2', function () {
return 'fail';
});
$output = TestRouter::debugOutput('/match');
TestRouter::router()->reset();
$this->assertEquals('ok', $output);
}
public function testMiddlewareRewrite()
{
TestRouter::group(['middleware' => 'RewriteMiddleware'], function () {
TestRouter::get('/', function () {
return 'fail';
});
TestRouter::get('no/match', function () {
return 'fail';
});
});
$output = TestRouter::debugOutput('/');
$this->assertEquals('ok', $output);
}
}
@@ -0,0 +1,335 @@
<?php
require_once 'Dummy/DummyMiddleware.php';
require_once 'Dummy/DummyController.php';
require_once 'Dummy/NSController.php';
require_once 'Dummy/Exception/ExceptionHandlerException.php';
class RouterRouteTest extends \PHPUnit\Framework\TestCase
{
/**
* Issue #421: Incorrectly optional character in route
*
* @throws Exception
*/
public function testOptionalCharacterRoute()
{
$result = false;
TestRouter::get('/api/v1/users/{userid}/projects/{id}/pages/{pageid?}', function () use (&$result) {
$result = true;
});
TestRouter::debug('/api/v1/users/1/projects/8399421535/pages/43/', 'get');
$this->assertTrue($result);
}
public function testMultiParam()
{
$result = false;
TestRouter::get('/test-{param1}-{param2}', function ($param1, $param2) use (&$result) {
if ($param1 === 'param1' && $param2 === 'param2') {
$result = true;
}
});
TestRouter::debug('/test-param1-param2', 'get');
$this->assertTrue($result);
}
public function testNotFound()
{
$this->expectException('\Pecee\SimpleRouter\Exceptions\NotFoundHttpException');
TestRouter::get('/non-existing-path', 'DummyController@method1');
TestRouter::debug('/test-param1-param2', 'post');
}
public function testGet()
{
TestRouter::get('/my/test/url', 'DummyController@method1');
TestRouter::debug('/my/test/url', 'get');
$this->assertTrue(true);
}
public function testPost()
{
TestRouter::post('/my/test/url', 'DummyController@method1');
TestRouter::debug('/my/test/url', 'post');
$this->assertTrue(true);
}
public function testPut()
{
TestRouter::put('/my/test/url', 'DummyController@method1');
TestRouter::debug('/my/test/url', 'put');
$this->assertTrue(true);
}
public function testDelete()
{
TestRouter::delete('/my/test/url', 'DummyController@method1');
TestRouter::debug('/my/test/url', 'delete');
$this->assertTrue(true);
}
public function testMethodNotAllowed()
{
TestRouter::get('/my/test/url', 'DummyController@method1');
try {
TestRouter::debug('/my/test/url', 'post');
} catch (\Exception $e) {
$this->assertEquals(403, $e->getCode());
}
}
public function testSimpleParam()
{
TestRouter::get('/test-{param1}', 'DummyController@param');
$response = TestRouter::debugOutput('/test-param1', 'get');
$this->assertEquals('param1', $response);
}
public function testPathParamRegex()
{
TestRouter::get('/{lang}/productscategories/{name}', 'DummyController@param', ['where' => ['lang' => '[a-z]+', 'name' => '[A-Za-z0-9-]+']]);
$response = TestRouter::debugOutput('/it/productscategories/system', 'get');
$this->assertEquals('it, system', $response);
}
public function testFixedDomain()
{
$result = false;
TestRouter::request()->setHost('admin.world.com');
TestRouter::group(['domain' => 'admin.world.com'], function () use (&$result) {
TestRouter::get('/test', function ($subdomain = null) use (&$result) {
$result = true;
});
});
TestRouter::debug('/test', 'get');
$this->assertTrue($result);
}
public function testFixedNotAllowedDomain()
{
$result = false;
TestRouter::request()->setHost('other.world.com');
TestRouter::group(['domain' => 'admin.world.com'], function () use (&$result) {
TestRouter::get('/', function ($subdomain = null) use (&$result) {
$result = true;
});
});
try {
TestRouter::debug('/', 'get');
} catch(\Exception $e) {
}
$this->assertFalse($result);
}
public function testDomainAllowedRoute()
{
$result = false;
TestRouter::request()->setHost('hello.world.com');
TestRouter::group(['domain' => '{subdomain}.world.com'], function () use (&$result) {
TestRouter::get('/test', function ($subdomain = null) use (&$result) {
$result = ($subdomain === 'hello');
});
});
TestRouter::debug('/test', 'get');
$this->assertTrue($result);
}
public function testDomainNotAllowedRoute()
{
TestRouter::request()->setHost('other.world.com');
$result = false;
TestRouter::group(['domain' => '{subdomain}.world.com'], function () use (&$result) {
TestRouter::get('/test', function ($subdomain = null) use (&$result) {
$result = ($subdomain === 'hello');
});
});
TestRouter::debug('/test', 'get');
$this->assertFalse($result);
}
public function testFixedSubdomainDynamicDomain()
{
TestRouter::request()->setHost('other.world.com');
$result = false;
TestRouter::group(['domain' => 'other.{domain}'], function () use (&$result) {
TestRouter::get('/test', function ($domain = null) use (&$result) {
$result = true;
});
});
TestRouter::debug('/test', 'get');
$this->assertTrue($result);
}
public function testFixedSubdomainDynamicDomainParameter()
{
TestRouter::request()->setHost('other.world.com');
$result = false;
TestRouter::group(['domain' => 'other.{domain}'], function () use (&$result) {
TestRouter::get('/test', 'DummyController@param');
TestRouter::get('/test/{key}', 'DummyController@param');
});
$response = TestRouter::debugOutputNoReset('/test', 'get');
$this->assertEquals('world.com', $response);
$response = TestRouter::debugOutput('/test/unittest', 'get');
$this->assertEquals('unittest, world.com', $response);
}
public function testWrongFixedSubdomainDynamicDomain()
{
TestRouter::request()->setHost('wrong.world.com');
$result = false;
TestRouter::group(['domain' => 'other.{domain}'], function () use (&$result) {
TestRouter::get('/test', function ($domain = null) use (&$result) {
$result = true;
});
});
try {
TestRouter::debug('/test', 'get');
} catch(\Exception $e) {
}
$this->assertFalse($result);
}
public function testRegEx()
{
TestRouter::get('/my/{path}', 'DummyController@method1')->where(['path' => '[a-zA-Z-]+']);
TestRouter::debug('/my/custom-path', 'get');
$this->assertTrue(true);
}
public function testParametersWithDashes()
{
$defaultVariable = null;
TestRouter::get('/my/{path}', function ($path = 'working') use (&$defaultVariable) {
$defaultVariable = $path;
});
TestRouter::debug('/my/hello-motto-man');
$this->assertEquals('hello-motto-man', $defaultVariable);
}
public function testParameterDefaultValue()
{
$defaultVariable = null;
TestRouter::get('/my/{path?}', function ($path = 'working') use (&$defaultVariable) {
$defaultVariable = $path;
});
TestRouter::debug('/my/');
$this->assertEquals('working', $defaultVariable);
}
public function testDefaultParameterRegex()
{
TestRouter::get('/my/{path}', 'DummyController@param', ['defaultParameterRegex' => '[\w-]+']);
$output = TestRouter::debugOutput('/my/custom-regex', 'get');
$this->assertEquals('custom-regex', $output);
}
public function testDefaultParameterRegexGroup()
{
TestRouter::group(['defaultParameterRegex' => '[\w-]+'], function () {
TestRouter::get('/my/{path}', 'DummyController@param');
});
$output = TestRouter::debugOutput('/my/custom-regex', 'get');
$this->assertEquals('custom-regex', $output);
}
public function testClassHint()
{
TestRouter::get('/my/test/url', ['DummyController', 'method1']);
TestRouter::all('/my/test/url', ['DummyController', 'method1']);
TestRouter::match(['put', 'get', 'post'], '/my/test/url', ['DummyController', 'method1']);
TestRouter::debug('/my/test/url', 'get');
$this->assertTrue(true);
}
public function testDefaultNameSpaceOverload()
{
TestRouter::setDefaultNamespace('DefaultNamespace\\Controllers');
TestRouter::get('/test', [\MyNamespace\NSController::class, 'method']);
$result = TestRouter::debugOutput('/test');
$this->assertTrue( (bool)$result);
}
public function testSameRoutes()
{
TestRouter::get('/recipe', 'DummyController@method1')->name('add');
TestRouter::post('/recipe', 'DummyController@method2')->name('edit');
TestRouter::debugNoReset('/recipe', 'post');
TestRouter::debug('/recipe', 'get');
$this->assertTrue(true);
}
}
@@ -0,0 +1,389 @@
<?php
require_once 'Dummy/DummyMiddleware.php';
require_once 'Dummy/DummyController.php';
require_once 'Dummy/Handler/ExceptionHandler.php';
class RouterUrlTest extends \PHPUnit\Framework\TestCase
{
public function testIssue253()
{
TestRouter::get('/', 'DummyController@method1');
TestRouter::get('/page/{id?}', 'DummyController@method1');
TestRouter::get('/test-output', function () {
return 'return value';
});
TestRouter::debugNoReset('/page/22', 'get');
$this->assertEquals('/page/{id?}/', TestRouter::router()->getRequest()->getLoadedRoute()->getUrl());
TestRouter::debugNoReset('/', 'get');
$this->assertEquals('/', TestRouter::router()->getRequest()->getLoadedRoute()->getUrl());
$output = TestRouter::debugOutput('/test-output', 'get');
$this->assertEquals('return value', $output);
TestRouter::router()->reset();
}
public function testLastParameterSlash()
{
TestRouter::get('/test/{param}', function ($param) {
return $param;
})->setSettings(['includeSlash' => true]);
// Test with ending /
$output = TestRouter::debugOutputNoReset('/test/param/');
$this->assertEquals($output, 'param/');
// Test without ending /
$output = TestRouter::debugOutputNoReset('/test/param');
$this->assertEquals($output, 'param');
TestRouter::router()->reset();
}
public function testUnicodeCharacters()
{
// Test spanish characters
TestRouter::get('/cursos/listado/{listado?}/{category?}', 'DummyController@method1', ['defaultParameterRegex' => '[\w\p{L}\s\-]+']);
TestRouter::get('/test/{param}', 'DummyController@method1', ['defaultParameterRegex' => '[\w\p{L}\s\-\í]+']);
TestRouter::debugNoReset('/cursos/listado/especialidad/cirugía local', 'get');
$this->assertEquals('/cursos/listado/{listado?}/{category?}/', TestRouter::router()->getRequest()->getLoadedRoute()->getUrl());
TestRouter::debugNoReset('/test/Dermatología');
$parameters = TestRouter::request()->getLoadedRoute()->getParameters();
$this->assertEquals('Dermatología', $parameters['param']);
// Test danish characters
TestRouter::get('/kategori/økse', 'DummyController@method1', ['defaultParameterRegex' => '[\w\ø]+']);
TestRouter::debugNoReset('/kategori/økse', 'get');
$this->assertEquals('/kategori/økse/', TestRouter::router()->getRequest()->getLoadedRoute()->getUrl());
TestRouter::router()->reset();
}
public function testOptionalParameters()
{
TestRouter::get('/aviso/legal', 'DummyController@method1');
TestRouter::get('/aviso/{aviso}', 'DummyController@method1');
TestRouter::get('/pagina/{pagina}', 'DummyController@method1');
TestRouter::get('/{pagina?}', 'DummyController@method1');
TestRouter::debugNoReset('/aviso/optional', 'get');
$this->assertEquals('/aviso/{aviso}/', TestRouter::router()->getRequest()->getLoadedRoute()->getUrl());
TestRouter::debugNoReset('/pagina/optional', 'get');
$this->assertEquals('/pagina/{pagina}/', TestRouter::router()->getRequest()->getLoadedRoute()->getUrl());
TestRouter::debugNoReset('/optional', 'get');
$this->assertEquals('/{pagina?}/', TestRouter::router()->getRequest()->getLoadedRoute()->getUrl());
TestRouter::debugNoReset('/avisolegal', 'get');
$this->assertNotEquals('/aviso/{aviso}/', TestRouter::router()->getRequest()->getLoadedRoute()->getUrl());
TestRouter::debugNoReset('/avisolegal', 'get');
$this->assertEquals('/{pagina?}/', TestRouter::router()->getRequest()->getLoadedRoute()->getUrl());
TestRouter::router()->reset();
}
public function testSimilarUrls()
{
TestRouter::reset();
// Match normal route on alias
TestRouter::get('/url11', 'DummyController@method1');
TestRouter::get('/url22', 'DummyController@method2');
TestRouter::get('/url33', 'DummyController@method2')->name('match');
TestRouter::debugNoReset('/url33', 'get');
$this->assertEquals(TestRouter::getUrl('match'), TestRouter::getUrl());
TestRouter::router()->reset();
}
public function testUrls()
{
// Match normal route on alias
TestRouter::get('/', 'DummyController@method1', ['as' => 'home']);
TestRouter::get('/about', 'DummyController@about');
TestRouter::group(['prefix' => '/admin', 'as' => 'admin'], function () {
// Match route with prefix on alias
TestRouter::get('/{id?}', 'DummyController@method2', ['as' => 'home']);
// Match controller with prefix and alias
TestRouter::controller('/users', 'DummyController', ['as' => 'users']);
// Match controller with prefix and NO alias
TestRouter::controller('/pages', 'DummyController');
});
TestRouter::group(['prefix' => 'api', 'as' => 'api'], function () {
// Match resource controller
TestRouter::resource('phones', 'DummyController');
});
TestRouter::controller('gadgets', 'DummyController', ['names' => ['getIphoneInfo' => 'iphone']]);
// Match controller with no prefix and no alias
TestRouter::controller('/cats', 'CatsController');
// Pretend to load page
TestRouter::debugNoReset('/', 'get');
$this->assertEquals('/gadgets/iphoneinfo/', TestRouter::getUrl('gadgets.iphone'));
$this->assertEquals('/api/phones/create/', TestRouter::getUrl('api.phones.create'));
// Should match /
$this->assertEquals('/', TestRouter::getUrl('home'));
// Should match /about/
$this->assertEquals('/about/', TestRouter::getUrl('DummyController@about'));
// Should match /admin/
$this->assertEquals('/admin/', TestRouter::getUrl('DummyController@method2'));
// Should match /admin/
$this->assertEquals('/admin/', TestRouter::getUrl('admin.home'));
// Should match /admin/2/
$this->assertEquals('/admin/2/', TestRouter::getUrl('admin.home', ['id' => 2]));
// Should match /admin/users/
$this->assertEquals('/admin/users/', TestRouter::getUrl('admin.users'));
// Should match /admin/users/home/
$this->assertEquals('/admin/users/home/', TestRouter::getUrl('admin.users@home'));
// Should match /cats/
$this->assertEquals('/cats/', TestRouter::getUrl('CatsController'));
// Should match /cats/view/
$this->assertEquals('/cats/view/', TestRouter::getUrl('CatsController', 'view'));
// Should match /cats/view/
//$this->assertEquals('/cats/view/', TestRouter::getUrl('CatsController', ['view']));
// Should match /cats/view/666
$this->assertEquals('/cats/view/666/', TestRouter::getUrl('CatsController@getView', ['666']));
// Should match /funny/man/
$this->assertEquals('/funny/man/', TestRouter::getUrl('/funny/man'));
// Should match /?jackdaniels=true&cola=yeah
$this->assertEquals('/?jackdaniels=true&cola=yeah', TestRouter::getUrl('home', null, ['jackdaniels' => 'true', 'cola' => 'yeah']));
TestRouter::reset();
}
public function testCustomRegex()
{
TestRouter::request()->setHost('google.com');
TestRouter::get('/admin/', function () {
return 'match';
})->setMatch('/^\/admin\/?(.*)/i');
$output = TestRouter::debugOutput('/admin/asd/bec/123', 'get');
$this->assertEquals('match', $output);
TestRouter::router()->reset();
}
public function testCustomRegexWithParameter()
{
TestRouter::request()->setHost('google.com');
$results = '';
TestRouter::get('/tester/{param}', function ($param = null) use ($results) {
return $results = $param;
})->setMatch('/(.*)/i');
$output = TestRouter::debugOutput('/tester/abepik/ko');
$this->assertEquals('/tester/abepik/ko/', $output);
}
public function testRenderMultipleRoutesDisabled()
{
TestRouter::router()->setRenderMultipleRoutes(false);
$result = false;
TestRouter::get('/', function () use (&$result) {
$result = true;
});
TestRouter::get('/', function () use (&$result) {
$result = false;
});
TestRouter::debug('/');
$this->assertTrue($result);
}
public function testRenderMultipleRoutesEnabled()
{
TestRouter::router()->setRenderMultipleRoutes(true);
$result = [];
TestRouter::get('/', function () use (&$result) {
$result[] = 'route1';
});
TestRouter::get('/', function () use (&$result) {
$result[] = 'route2';
});
TestRouter::debug('/');
$this->assertCount(2, $result);
}
public function testDefaultNamespace()
{
TestRouter::setDefaultNamespace('\\Default\\Namespace');
TestRouter::get('/', 'DummyController@method1', ['as' => 'home']);
TestRouter::group([
'namespace' => 'Appended\Namespace',
'prefix' => '/horses',
], function () {
TestRouter::get('/', 'DummyController@method1');
TestRouter::group([
'namespace' => '\\New\\Namespace',
'prefix' => '/race',
], function () {
TestRouter::get('/', 'DummyController@method1');
});
});
// Test appended namespace
$class = null;
try {
TestRouter::debugNoReset('/horses/');
} catch (\Pecee\SimpleRouter\Exceptions\ClassNotFoundHttpException $e) {
$class = $e->getClass();
}
$this->assertEquals('\\Default\\Namespace\\Appended\Namespace\\DummyController', $class);
// Test overwritten namespace
$class = null;
try {
TestRouter::debugNoReset('/horses/race');
} catch (\Pecee\SimpleRouter\Exceptions\ClassNotFoundHttpException $e) {
$class = $e->getClass();
}
$this->assertEquals('\\New\\Namespace\\DummyController', $class);
TestRouter::router()->reset();
}
public function testGroupPrefix()
{
$result = false;
TestRouter::group(['prefix' => '/lang/{lang}'], function () use (&$result) {
TestRouter::get('/test', function () use (&$result) {
$result = true;
});
});
TestRouter::debug('/lang/da/test');
$this->assertTrue($result);
// Test group prefix sub-route
$result = null;
$expectedResult = 28;
TestRouter::group(['prefix' => '/lang/{lang}'], function () use (&$result) {
TestRouter::get('/horse/{horseType}', function ($horseType) use (&$result) {
$result = false;
});
TestRouter::get('/user/{userId}', function ($userId) use (&$result) {
$result = $userId;
});
});
TestRouter::debug("/lang/da/user/$expectedResult");
$this->assertEquals($expectedResult, $result);
}
public function testPassParameter()
{
$result = false;
$expectedLanguage = 'da';
TestRouter::group(['prefix' => '/lang/{lang}'], function ($language) use (&$result) {
TestRouter::get('/test', function ($language) use (&$result) {
$result = $language;
});
});
TestRouter::debug("/lang/$expectedLanguage/test");
$this->assertEquals($expectedLanguage, $result);
}
public function testPassParameterDeep()
{
$result = false;
$expectedLanguage = 'da';
TestRouter::group(['prefix' => '/lang/{lang}'], function ($language) use (&$result) {
TestRouter::group(['prefix' => '/admin'], function ($language) use (&$result) {
TestRouter::get('/test', function ($language) use (&$result) {
$result = $language;
});
});
});
TestRouter::debug("/lang/$expectedLanguage/admin/test");
$this->assertEquals($expectedLanguage, $result);
}
}
+69
View File
@@ -0,0 +1,69 @@
<?php
class TestRouter extends \Pecee\SimpleRouter\SimpleRouter
{
public function __construct()
{
static::request()->setHost('testhost.com');
}
public static function reset(): void
{
static::$router = null;
}
public static function debugNoReset(string $testUrl, string $testMethod = 'get'): void
{
$request = static::request();
$request->setUrl((new \Pecee\Http\Url($testUrl)));
$request->setMethod($testMethod);
static::start();
}
public static function debug(string $testUrl, string $testMethod = 'get', bool $reset = true): void
{
try {
static::debugNoReset($testUrl, $testMethod);
} catch (\Exception $e) {
static::$defaultNamespace = null;
static::router()->reset();
throw $e;
}
if ($reset === true) {
static::$defaultNamespace = null;
static::router()->reset();
}
}
public static function debugOutput(string $testUrl, string $testMethod = 'get', bool $reset = true): string
{
$response = null;
// Route request
ob_start();
static::debug($testUrl, $testMethod, $reset);
$response = ob_get_clean();
// Return response
return $response;
}
public static function debugOutputNoReset(string $testUrl, string $testMethod = 'get', bool $reset = true): string
{
$response = null;
// Route request
ob_start();
static::debugNoReset($testUrl, $testMethod, $reset);
$response = ob_get_clean();
// Return response
return $response;
}
}
+4
View File
@@ -0,0 +1,4 @@
<?php
require_once dirname(__DIR__) . '/vendor/autoload.php';
require_once 'TestRouter.php';
+9
View File
@@ -0,0 +1,9 @@
<?php
require_once dirname(__DIR__) . '/vendor/autoload.php';
use \Pecee\SimpleRouter\SimpleRouter;
SimpleRouter::get('/user/{name}', 'UserController@show')->where(['name' => '[\w]+']);
$debugInfo = SimpleRouter::startDebug();
echo sprintf('<pre>%s</pre>', var_export($debugInfo, true));
exit;