diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 37621d3..14bb468 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -68,7 +68,9 @@ release: only: - master script: - - vendor/bin/phpunit -c test/phpunit.xml --coverage-text + - pecl install xdebug + - docker-php-ext-enable xdebug + - vendor/bin/phpunit -c test/phpunit.xml --coverage-text artifacts: name: "${CI_BUILD_NAME}_${CI_BUILD_REF_NAME}" paths: diff --git a/composer.json b/composer.json index 7d28cef..0e44c9f 100644 --- a/composer.json +++ b/composer.json @@ -13,17 +13,16 @@ } ], "require": { - "php": ">=7.1.0" + "php": ">=7.1.0", + "fuzeworks/core": "~1.2.0" }, "require-dev": { - "fuzeworks/core": "dev-development", "phpunit/phpunit": "^7", - "mikey179/vfsStream": "1.6.5" + "mikey179/vfsstream": "1.6.5" }, "autoload": { "psr-4": { "FuzeWorks\\": "src/FuzeWorks/" } } - } \ No newline at end of file diff --git a/src/FuzeWorks/Controller.php b/src/FuzeWorks/Controller.php index 3d61683..f171162 100644 --- a/src/FuzeWorks/Controller.php +++ b/src/FuzeWorks/Controller.php @@ -39,11 +39,52 @@ namespace FuzeWorks; /** * Abstract class Controller. * - * Extends all controllers to use the Factory. + * Extends all controllers to use useful classes * * @author Abel Hoogeveen * @copyright Copyright (c) 2013 - 2019, TechFuze. (http://techfuze.net) */ -abstract class Controller extends Factory +abstract class Controller { + /** + * @var Plugins + */ + protected $plugins; + + /** + * @var Libraries + */ + protected $libraries; + + /** + * @var Helpers + */ + protected $helpers; + + /** + * @var Config + */ + protected $config; + + /** + * @var Controllers + */ + protected $controllers; + + /** + * @var Models + */ + protected $models; + + public function __construct() + { + $this->plugins = Factory::getInstance()->plugins; + $this->libraries = Factory::getInstance()->libraries; + $this->helpers = Factory::getInstance()->helpers; + $this->config = Factory::getInstance()->config; + $this->controllers = Factory::getInstance()->controllers; + $this->models = Factory::getInstance()->models; + } + + } \ No newline at end of file diff --git a/src/FuzeWorks/Event/RouterCallViewEvent.php b/src/FuzeWorks/Event/RouterCallViewEvent.php new file mode 100644 index 0000000..8cb6679 --- /dev/null +++ b/src/FuzeWorks/Event/RouterCallViewEvent.php @@ -0,0 +1,117 @@ + + * @copyright Copyright (c) 2013 - 2019, TechFuze. (http://techfuze.net) + */ +class RouterCallViewEvent extends Event +{ + /** + * The function that will be loaded in the view + * + * @var array + */ + public $viewMethods; + + /** + * The parameters that will be provided to the function in the view + * + * @var string + */ + public $viewParameters; + + /** + * The route that resulted in this controller and view + * + * @var string + */ + public $route; + + /** + * The view the method will be called on + * + * @var View + */ + public $view; + + /** + * The controller that's associated with this View + * + * @var Controller + */ + public $controller; + + public function init(View $view, Controller $controller, array $viewMethods, string $viewParameters, string $route) + { + $this->view = $view; + $this->controller = $controller; + $this->viewMethods = $viewMethods; + $this->viewParameters = $viewParameters; + $this->route = $route; + } + + /** + * Add a method which should be tried upon calling the view + * + * @param string $method + * @param int $priority + */ + public function addMethod(string $method, int $priority = Priority::NORMAL) + { + if (!isset($this->viewMethods[$priority])) + $this->viewMethods[$priority] = []; + + if (!isset($this->viewMethods[$priority][$method])) + $this->viewMethods[$priority][] = $method; + } +} \ No newline at end of file diff --git a/src/FuzeWorks/Event/RouterLoadCallableEvent.php b/src/FuzeWorks/Event/RouterLoadCallableEvent.php new file mode 100644 index 0000000..f403b72 --- /dev/null +++ b/src/FuzeWorks/Event/RouterLoadCallableEvent.php @@ -0,0 +1,82 @@ + + * @copyright Copyright (c) 2013 - 2019, TechFuze. (http://techfuze.net) + */ +class RouterLoadCallableEvent extends Event +{ + + /** + * The callable to be loaded + * + * @var callable + */ + public $callable; + + /** + * The matches with which the callable is loaded + * + * @var array + */ + public $matches; + + /** + * The route which resulted in this callable being loaded + * + * @var string + */ + public $route; + + public function init(callable $callable, array $matches, string $route) + { + $this->callable = $callable; + $this->matches = $matches; + $this->route = $route; + } + +} \ No newline at end of file diff --git a/src/FuzeWorks/Event/RouterLoadViewAndControllerEvent.php b/src/FuzeWorks/Event/RouterLoadViewAndControllerEvent.php index 03cc4aa..dcb9c82 100644 --- a/src/FuzeWorks/Event/RouterLoadViewAndControllerEvent.php +++ b/src/FuzeWorks/Event/RouterLoadViewAndControllerEvent.php @@ -37,13 +37,14 @@ namespace FuzeWorks\Event; use FuzeWorks\Controller; use FuzeWorks\Event; +use FuzeWorks\Priority; /** * Event that gets fired when a view and controller are loaded. * * Use this to cancel the loading of a combination, or change the details of what is loaded. * - * Currently only used by OldRouter::defaultCallable(); + * Currently only used by Router::defaultCallable(); * * @author Abel Hoogeveen * @copyright Copyright (c) 2013 - 2019, TechFuze. (http://techfuze.net) @@ -67,9 +68,9 @@ class RouterLoadViewAndControllerEvent extends Event /** * The function that will be loaded in the view * - * @var string + * @var array */ - public $viewMethod; + public $viewMethods; /** * The parameters that will be provided to the function in the view @@ -92,15 +93,30 @@ class RouterLoadViewAndControllerEvent extends Event */ public $controller; - public function init(string $viewName, string $viewType, string $viewMethod, string $viewParameters, string $route) + public function init(string $viewName, string $viewType, array $viewMethods, string $viewParameters, string $route) { $this->viewName = $viewName; $this->viewType = $viewType; - $this->viewMethod = $viewMethod; + $this->viewMethods = $viewMethods; $this->viewParameters = $viewParameters; $this->route = $route; } + /** + * Add a method which should be tried upon calling the view + * + * @param string $method + * @param int $priority + */ + public function addMethod(string $method, int $priority = Priority::NORMAL) + { + if (!isset($this->viewMethods[$priority])) + $this->viewMethods[$priority] = []; + + if (!isset($this->viewMethods[$priority][$method])) + $this->viewMethods[$priority][] = $method; + } + /** * Override the controller to be provided to the view. * diff --git a/src/FuzeWorks/Model.php b/src/FuzeWorks/Model.php index 483cfd0..796bb60 100644 --- a/src/FuzeWorks/Model.php +++ b/src/FuzeWorks/Model.php @@ -40,11 +40,39 @@ namespace FuzeWorks; /** * Abstract class Model. * - * Extends all models to use the Factory. + * Extends all models to use useful classes * * @author Abel Hoogeveen * @copyright Copyright (c) 2013 - 2019, TechFuze. (http://techfuze.net) */ -abstract class Model extends Factory +abstract class Model { + /** + * @var Plugins + */ + protected $plugins; + + /** + * @var Libraries + */ + protected $libraries; + + /** + * @var Helpers + */ + protected $helpers; + + /** + * @var Config + */ + protected $config; + + public function __construct() + { + $this->plugins = Factory::getInstance()->plugins; + $this->libraries = Factory::getInstance()->libraries; + $this->helpers = Factory::getInstance()->helpers; + $this->config = Factory::getInstance()->config; + } + } diff --git a/src/FuzeWorks/Router.php b/src/FuzeWorks/Router.php index 1cf7e7a..cc32824 100644 --- a/src/FuzeWorks/Router.php +++ b/src/FuzeWorks/Router.php @@ -36,7 +36,8 @@ namespace FuzeWorks; - +use FuzeWorks\Event\RouterCallViewEvent; +use FuzeWorks\Event\RouterLoadCallableEvent; use FuzeWorks\Event\RouterLoadViewAndControllerEvent; use FuzeWorks\Exception\ConfigException; use FuzeWorks\Exception\ControllerException; @@ -155,7 +156,14 @@ class Router } } - public function addRoute(string $route, $routeConfig = null, bool $prepend = true) + /** + * Add a route to the Router + * + * @param string $route + * @param null $routeConfig + * @param int $priority + */ + public function addRoute(string $route, $routeConfig = null, int $priority = Priority::NORMAL) { // Set defaultCallable if no value provided if (is_null($routeConfig)) @@ -164,23 +172,27 @@ class Router // Convert wildcards to Regex $route = str_replace([':any',':num'], ['[^/]+', '[0-9]+'], $route); - if ($prepend) - $this->routes = [$route => $routeConfig] + $this->routes; - else - $this->routes[$route] = $routeConfig; + if (!isset($this->routes[$priority])) + $this->routes[$priority] = []; - Logger::log('Route added at '.($prepend ? 'top' : 'bottom').': "'.$route.'"'); + if (!isset($this->routes[$priority][$route])) + $this->routes[$priority][$route] = $routeConfig; + + Logger::log('Route added with ' . Priority::getPriority($priority) . ": '" . $route."'"); } /** * Removes a route from the array based on the given route. * * @param $route string The route to remove + * @param int $priority */ - public function removeRoute(string $route) + public function removeRoute(string $route, int $priority = Priority::NORMAL) { - unset($this->routes[$route]); + if (!isset($this->routes[$priority][$route])) + return; + unset($this->routes[$priority][$route]); Logger::log('Route removed: '.$route); } @@ -188,56 +200,68 @@ class Router * @param string $path * @return mixed * @throws NotFoundException + * @throws RouterException + * @throws HaltException */ public function route(string $path) { - // Check all the provided custom paths - foreach ($this->routes as $route => $routeConfig) - { - // Match the path against the routes - if (!preg_match('#^'.$route.'$#', $path, $matches)) + // Check all the provided custom paths, ordered by priority + for ($i=Priority::getHighestPriority(); $i<=Priority::getLowestPriority(); $i++) { + if (!isset($this->routes[$i])) continue; - // Save the matches - Logger::log('Route matched: '.$route); - $this->matches = $matches; - $this->route = $route; - - // Call callable if routeConfig is callable, so routeConfig can be replaced - // e.g: '.*$' => callable - if (is_callable($routeConfig)) - $routeConfig = call_user_func_array($routeConfig, [$matches]); - - // If routeConfig is an array, multiple things might be at hand - if (is_array($routeConfig)) + foreach ($this->routes[$i] as $route => $routeConfig) { - // Replace defaultCallable if a custom callable is provided - // e.g: '.*$' => ['callable' => [$object, 'method']] - if (isset($routeConfig['callable']) && is_callable($routeConfig['callable'])) - $this->callable = $routeConfig['callable']; + // Match the path against the routes + if (!preg_match('#^'.$route.'$#', $path, $matches)) + continue; - // If the route provides a configuration, use that - // e.g: '.*$' => ['viewName' => 'custom', 'viewType' => 'cli', 'function' => 'index'] - else - $this->matches = array_merge($this->matches, $routeConfig); + // Save the matches + Logger::log("Route matched: '" . $route . "' with " . Priority::getPriority($i)); + $this->matches = $matches; + $this->route = $route; + $this->callable = null; + + // Call callable if routeConfig is callable, so routeConfig can be replaced + // This is an example of 'Dynamic Rewrite' + // e.g: '.*$' => callable + if (is_callable($routeConfig)) + $routeConfig = call_user_func_array($routeConfig, [$matches]); + + // If routeConfig is an array, multiple things might be at hand + if (is_array($routeConfig)) + { + // Replace defaultCallable if a custom callable is provided + // This is an example of 'Custom Callable' + // e.g: '.*$' => ['callable' => [$object, 'method']] + if (isset($routeConfig['callable']) && is_callable($routeConfig['callable'])) + $this->callable = $routeConfig['callable']; + + // If the route provides a configuration, use that + // This is an example of 'Static Rewrite' + // e.g: '.*$' => ['viewName' => 'custom', 'viewType' => 'cli', 'function' => 'index'] + else + $this->matches = array_merge($this->matches, $routeConfig); + } + + // If no custom callable is provided, use default + // This is an example of 'Default Callable' + if (is_null($this->callable)) + $this->callable = [$this, 'defaultCallable']; + + // Attempt and load callable. If false, continue + $output = $this->loadCallable($this->callable, $this->matches, $route); + if (is_bool($output) && $output === FALSE) + { + Logger::log('Callable not satisfied, skipping to next callable'); + continue; + } + + return $output; } - - // If no custom callable is provided, use default - if (is_null($this->callable)) - $this->callable = [$this, 'defaultCallable']; - - // Attempt and load callable. If false, continue - $output = $this->loadCallable($this->callable, $this->matches, $route); - if (is_bool($output) && $output === FALSE) - { - Logger::log('Callable not satisfied, skipping to next callable'); - continue; - } - - return $output; } - throw new NotFoundException("Could not load view. Router could not find matching route."); + throw new NotFoundException("Could not load view. Router could not find matching route with satisfied callable."); } /** @@ -245,6 +269,8 @@ class Router * @param array $matches * @param string $route * @return mixed + * @throws RouterException + * @throws HaltException */ protected function loadCallable(callable $callable, array $matches, string $route) { @@ -254,10 +280,26 @@ class Router if (!is_int($key)) Logger::log($key.': '.var_export($value, true).''); } - Logger::stopLevel(); + + try { + /** @var RouterLoadCallableEvent $event */ + $event = Events::fireEvent('routerLoadCallableEvent', + $callable, + $matches, + $route + ); + } catch (EventException $e) { + throw new RouterException("Could not load callable. routerLoadCallableEvent threw exception: '".$e->getMessage()."'"); + } + + // Halt if cancelled + if ($event->isCancelled()) + throw new HaltException("Will not load callable. Cancelled by routerLoadCallableEvent."); // Invoke callable - return call_user_func_array($callable, [$matches, $route]); + $output = call_user_func_array($event->callable, [$event->matches, $event->route]); + Logger::stopLevel(); + return $output; } /** @@ -273,21 +315,18 @@ class Router Logger::log('defaultCallable called'); // Prepare variables - $viewName = isset($matches['viewName']) ? $matches['viewName'] : null; - $viewType = isset($matches['viewType']) ? $matches['viewType'] : $this->config->routing->default_viewType; - $viewMethod = isset($matches['viewMethod']) ? $matches['viewMethod'] : $this->config->routing->default_viewMethod; - $viewParameters = isset($matches['viewParameters']) ? $matches['viewParameters'] : ''; - - // If nothing is provided, cancel loading - if (is_null($viewName)) - return false; + $viewName = !empty($matches['viewName']) ? $matches['viewName'] : $this->config->routing->default_view; + $viewType = !empty($matches['viewType']) ? $matches['viewType'] : $this->config->routing->default_viewType; + $viewMethod = !empty($matches['viewMethod']) ? $matches['viewMethod'] : $this->config->routing->default_viewMethod; + $viewParameters = !empty($matches['viewParameters']) ? $matches['viewParameters'] : ''; try { /** @var RouterLoadViewAndControllerEvent $event */ $event = Events::fireEvent('routerLoadViewAndControllerEvent', $viewName, $viewType, - $viewMethod, + // ViewMethod is provided as a Priority::NORMAL method + [3 => [$viewMethod]], $viewParameters, $route ); @@ -305,6 +344,7 @@ class Router } catch (ControllerException $e) { throw new RouterException("Could not load view. Controllers::get threw ControllerException: '".$e->getMessage()."'"); } catch (NotFoundException $e) { + Logger::logError("Could not load view. Controller does not exist."); return false; } @@ -314,44 +354,70 @@ class Router } catch (ViewException $e) { throw new RouterException("Could not load view. Views::get threw ViewException: '".$e->getMessage()."'"); } catch (NotFoundException $e) { + Logger::logError("Could not load view. View does not exist."); return false; } + // Fire routerCallViewEvent + try { + /** @var RouterCallViewEvent $event */ + $event = Events::fireEvent('routerCallViewEvent', + $this->view, + $this->controller, + $event->viewMethods, + $event->viewParameters, + $event->route + ); + + // Reset vars + $this->view = $event->view; + $this->controller = $event->controller; + } catch (EventException $e) { + throw new RouterException("Could not load view. routerCallViewEvent threw exception: '".$e->getMessage()."'"); + } + + // Cancel if requested to do so + if ($event->isCancelled()) + throw new HaltException("Will not load view. Cancelled by routerCallViewEvent"); + // If the view does not want a function to be loaded, provide a halt parameter if (isset($this->view->halt)) throw new HaltException("Will not load view. Cancelled by 'halt' attribute in view."); - // Check if requested function or magic method exists in view - if (method_exists($this->view, $event->viewMethod) || method_exists($this->view, '__call')) - { - // Run viewCallMethodEvent. - try { - $methodEvent = Events::fireEvent('viewCallMethodEvent'); - } catch (EventException $e) { - throw new RouterException("Could not load view. viewCallMethodEvent threw exception: '".$e->getMessage()."'"); + // Cycle over every viewMethod until a valid one is found + for ($i=Priority::getHighestPriority(); $i<=Priority::getLowestPriority(); $i++) { + if (!isset($event->viewMethods[$i])) + continue; + + foreach ($event->viewMethods[$i] as $method) { + if (method_exists($this->view, $method)) + { + // Execute this method on the view + Logger::newLevel("Calling method '{$method}' on " . get_class($this->view) . ' with ' . get_class($this->controller)); + $output = $this->view->{$method}($event->viewParameters); + Logger::stopLevel(); + return $output; + } } - - // If cancelled, halt - if ($methodEvent->isCancelled()) - throw new HaltException("Will not load view. Cancelled by viewCallMethodEvent"); - - // Execute the function on the view - return $this->view->{$event->viewMethod}($event->viewParameters); } - // View could not be found + // Otherwise log an error + Logger::logError("Could not load view. View does not have any of the provided methods."); + + // View could not be found. return false; } /** * Returns an array with all the routes. * + * @param int $priority * @return array * @codeCoverageIgnore */ - public function getRoutes(): array + public function getRoutes(int $priority = Priority::NORMAL): array { - return $this->routes; + return $this->routes[$priority]; } /** diff --git a/src/FuzeWorks/View.php b/src/FuzeWorks/View.php index ccb5a81..fa86c8c 100644 --- a/src/FuzeWorks/View.php +++ b/src/FuzeWorks/View.php @@ -40,14 +40,34 @@ namespace FuzeWorks; /** * Abstract class View. * - * Extends all views to use the Factory. + * Extends all views to use useful classes * * @author Abel Hoogeveen * @copyright Copyright (c) 2013 - 2019, TechFuze. (http://techfuze.net) */ -abstract class View extends Factory +abstract class View { + /** + * @var Plugins + */ + protected $plugins; + + /** + * @var Libraries + */ + protected $libraries; + + /** + * @var Helpers + */ + protected $helpers; + + /** + * @var Config + */ + protected $config; + /** * The controller associated with this view * @@ -65,4 +85,12 @@ abstract class View extends Factory $this->controller = $controller; } + public function __construct() + { + $this->plugins = Factory::getInstance()->plugins; + $this->libraries = Factory::getInstance()->libraries; + $this->helpers = Factory::getInstance()->helpers; + $this->config = Factory::getInstance()->config; + } + } \ No newline at end of file diff --git a/src/FuzeWorks/Views.php b/src/FuzeWorks/Views.php index 1a77ce7..b38c968 100644 --- a/src/FuzeWorks/Views.php +++ b/src/FuzeWorks/Views.php @@ -83,7 +83,7 @@ class Views else $arguments = []; - // Fire a view load event + // Fire a viewGetEvent /** @var ViewGetEvent $event */ try { $event = Events::fireEvent('viewGetEvent', $viewName, $viewType, $viewPaths, $namespace, $controller, $arguments); diff --git a/test/mcr/RouterTest.php b/test/mcr/RouterTest.php index 464a23d..de9fe7a 100644 --- a/test/mcr/RouterTest.php +++ b/test/mcr/RouterTest.php @@ -35,7 +35,9 @@ */ use FuzeWorks\Config; +use FuzeWorks\Events; use FuzeWorks\Factory; +use FuzeWorks\Priority; use FuzeWorks\Router; /** @@ -118,19 +120,14 @@ class RouterTest extends MVCRTestAbstract $testAppendRouteFunction = [function () { }]; $this->router->addRoute('testRoute', $testRouteFunction); - $this->router->addRoute('testAppendRoute', $testAppendRouteFunction, false); + $this->router->addRoute('testAppendRoute', $testAppendRouteFunction, Priority::LOW); // Test if the order is correct - $this->assertSame( - ['testRoute' => $testRouteFunction, 'testAppendRoute' => $testAppendRouteFunction], - $this->router->getRoutes() - ); + // First for Priority::NORMAL + $this->assertSame(['testRoute' => $testRouteFunction], $this->router->getRoutes(Priority::NORMAL)); - // Test if the order is not incorrect - $this->assertNotSame( - ['testAppendRoute' => $testAppendRouteFunction, 'testRoute' => $testRouteFunction], - $this->router->getRoutes() - ); + // Then for Priority::LOW + $this->assertSame(['testAppendRoute' => $testAppendRouteFunction], $this->router->getRoutes(Priority::LOW)); } /** @@ -279,7 +276,7 @@ class RouterTest extends MVCRTestAbstract * @covers ::defaultCallable * @expectedException \FuzeWorks\Exception\HaltException */ - public function testDefaultCallableHalt() + public function testDefaultCallableHaltByView() { $matches = [ 'viewName' => 'TestDefaultCallableHalt', @@ -312,8 +309,242 @@ class RouterTest extends MVCRTestAbstract $this->assertNull($this->router->getCurrentView()); } + /** + * @depends testDefaultCallable + * @covers ::defaultCallable + * @covers \FuzeWorks\Event\RouterLoadViewAndControllerEvent::init + * @expectedException \FuzeWorks\Exception\HaltException + */ + public function testDefaultCallableHaltByEvent() + { + $matches = [ + 'viewName' => 'TestDefaultCallable', + 'viewType' => 'test', + 'viewMethod' => 'missing' + ]; + + $this->assertNull($this->router->getCurrentController()); + $this->assertNull($this->router->getCurrentView()); + + // Create listener + Events::addListener(function($event){ + $this->assertInstanceOf('\FuzeWorks\Event\RouterLoadViewAndControllerEvent', $event); + $this->assertEquals('TestDefaultCallable', $event->viewName); + $this->assertEquals('test', $event->viewType); + $this->assertEquals([3=>['missing']], $event->viewMethods); + $event->setCancelled(true); + }, 'routerLoadViewAndControllerEvent'); + + $this->router->defaultCallable($matches, '.*$'); + } + + /** + * @depends testDefaultCallableHaltByEvent + * @covers ::defaultCallable + * @covers \FuzeWorks\Event\RouterLoadViewAndControllerEvent::overrideController + */ + public function testDefaultCallableReplaceController() + { + $matches = [ + 'viewName' => 'TestDefaultCallable', + 'viewType' => 'test', + 'viewMethod' => 'missing' + ]; + + $this->assertNull($this->router->getCurrentController()); + $this->assertNull($this->router->getCurrentView()); + + $mockController = $this->getMockBuilder('\FuzeWorks\Controller')->getMock(); + + // Create listener + Events::addListener(function($event, $mockController){ + $event->overrideController($mockController); + }, 'routerLoadViewAndControllerEvent', Priority::NORMAL, $mockController); + + $this->router->defaultCallable($matches, '.*$'); + $this->assertEquals($mockController, $this->router->getCurrentController()); + } + + /** + * @depends testDefaultCallableReplaceController + * @covers ::defaultCallable + * @covers \FuzeWorks\Event\RouterLoadViewAndControllerEvent::addMethod + */ + public function testDefaultCallableAddMethod() + { + $matches = [ + 'viewName' => 'TestDefaultCallableChangeMethod', + 'viewType' => 'test', + 'viewMethod' => 'index' + ]; + + $this->assertNull($this->router->getCurrentController()); + $this->assertNull($this->router->getCurrentView()); + + $mockController = $this->getMockBuilder('\FuzeWorks\Controller')->getMock(); + // Create listener + Events::addListener(function($event, $mockController){ + $event->overrideController($mockController); + $event->addMethod('altered', Priority::HIGH); + }, 'routerLoadViewAndControllerEvent', Priority::NORMAL, $mockController); + + $this->assertEquals('Altered!', $this->router->defaultCallable($matches, '.*$')); + } + /* route() ------------------------------------------------------------ */ + /** + * @depends testDefaultCallable + * @covers ::route + * @covers ::loadCallable + */ + public function testRoute() + { + // Add route first + $this->router->addRoute('(?P.*?)(|\/(?P.*?)(|\/(?P.*?)))(|\.(?P.*?))'); + // Create mock view and controller + $mockController = $this->getMockBuilder('\FuzeWorks\Controller')->getMock(); + $mockView = $this->getMockBuilder('\FuzeWorks\View')->setMethods(['testMethod'])->getMock(); + class_alias(get_class($mockController), '\Application\Controller\TestRouteController'); + class_alias(get_class($mockView), '\Application\View\TestRouteTestView'); + + // Attempt to route + $this->assertnull($this->router->route('testRoute/testMethod/testParameters.test')); + $this->assertInstanceOf('\Application\Controller\TestRouteController', $this->router->getCurrentController()); + $this->assertInstanceOf('\Application\View\TestRouteTestView', $this->router->getCurrentView()); + } + + /** + * @depends testRoute + * @covers ::route + * @expectedException \FuzeWorks\Exception\NotFoundException + */ + public function testRouteNotFound() + { + $this->router->route('NotFound'); + } + + /** + * @depends testRouteNotFound + * @covers ::route + * @expectedException \FuzeWorks\Exception\NotFoundException + */ + public function testRouteNotMatched() + { + $this->router->addRoute('NotMatched'); + $this->router->route('NotFound'); + } + + /** + * @depends testRoute + * @covers ::route + * @covers ::loadCallable + */ + public function testRouteStaticRewrite() + { + // Add route first + $this->router->addRoute( + 'staticRewrite', + [ + 'viewName' => 'TestStaticRewrite', + 'viewMethod' => 'someMethod', + 'viewType' => 'static' + ] + ); + + // Create mock view and controller + $mockController = $this->getMockBuilder('\FuzeWorks\Controller')->getMock(); + $mockView = $this->getMockBuilder('\FuzeWorks\View')->setMethods(['someMethod'])->getMock(); + class_alias(get_class($mockController), '\Application\Controller\TestStaticRewriteController'); + class_alias(get_class($mockView), '\Application\View\TestStaticRewriteStaticView'); + + // Attempt to route + $this->assertnull($this->router->route('staticRewrite')); + $this->assertInstanceOf('\Application\Controller\TestStaticRewriteController', $this->router->getCurrentController()); + $this->assertInstanceOf('\Application\View\TestStaticRewriteStaticView', $this->router->getCurrentView()); + } + + /** + * @depends testRouteStaticRewrite + * @covers ::route + * @covers ::loadCallable + */ + public function testRouteDynamicRewrite() + { + // Add route first + $this->router->addRoute( + 'dynamicRewrite', + function($matches){ + $this->assertEquals([0=>'dynamicRewrite'], $matches); + return [ + 'viewName' => 'TestDynamicRewrite', + 'viewMethod' => 'someMethod', + 'viewType' => 'static' + ]; + } + ); + + // Create mock view and controller + $mockController = $this->getMockBuilder('\FuzeWorks\Controller')->getMock(); + $mockView = $this->getMockBuilder('\FuzeWorks\View')->setMethods(['someMethod'])->getMock(); + class_alias(get_class($mockController), '\Application\Controller\TestDynamicRewriteController'); + class_alias(get_class($mockView), '\Application\View\TestDynamicRewriteStaticView'); + + // Attempt to route + $this->assertNull($this->router->route('dynamicRewrite')); + $this->assertInstanceOf('\Application\Controller\TestDynamicRewriteController', $this->router->getCurrentController()); + $this->assertInstanceOf('\Application\View\TestDynamicRewriteStaticView', $this->router->getCurrentView()); + } + + /** + * @depends testRouteStaticRewrite + * @covers ::route + * @covers ::loadCallable + */ + public function testRouteCustomCallable() + { + // Create custom callable + $callable = function(array $matches, string $route){ + $this->assertEquals('customCallable', $route); + $this->assertEquals([0=>'customCallable'], $matches); + }; + + // Add route + $this->router->addRoute( + 'customCallable', + [ + 'callable' => $callable + ] + ); + + $this->assertNull($this->router->route('customCallable')); + } + + /** + * @depends testRouteStaticRewrite + * @covers ::route + * @covers ::loadCallable + * @expectedException \FuzeWorks\Exception\NotFoundException + */ + public function testRouteUnsatisfiedCallable() + { + // Create custom callable + $callable = function(array $matches, string $route){ + $this->assertEquals('unsatisfiedCallable', $route); + $this->assertEquals([0=>'unsatisfiedCallable'], $matches); + return false; + }; + + // Add route + $this->router->addRoute( + 'unsatisfiedCallable', + [ + 'callable' => $callable + ] + ); + + $this->assertNull($this->router->route('unsatisfiedCallable')); + } } diff --git a/test/phpunit.xml b/test/phpunit.xml index c758298..7708a45 100644 --- a/test/phpunit.xml +++ b/test/phpunit.xml @@ -27,7 +27,7 @@ ../ ../vendor/ - ../tests/ + ../test/ diff --git a/test/views/view.test.testdefaultcallablechangemethod.php b/test/views/view.test.testdefaultcallablechangemethod.php new file mode 100644 index 0000000..3533d0e --- /dev/null +++ b/test/views/view.test.testdefaultcallablechangemethod.php @@ -0,0 +1,52 @@ +