Merge branch 'development' into 'master'
Release version 1.2.0 Closes #5, #3, and #4 See merge request fuzeworks/MVCR!4
This commit is contained in:
commit
cd1e19e784
@ -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:
|
||||
|
@ -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/"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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 <abel@techfuze.net>
|
||||
* @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;
|
||||
}
|
||||
|
||||
|
||||
}
|
117
src/FuzeWorks/Event/RouterCallViewEvent.php
Normal file
117
src/FuzeWorks/Event/RouterCallViewEvent.php
Normal file
@ -0,0 +1,117 @@
|
||||
<?php
|
||||
/**
|
||||
* FuzeWorks Framework MVCR Component.
|
||||
*
|
||||
* The FuzeWorks PHP FrameWork
|
||||
*
|
||||
* Copyright (C) 2013-2019 TechFuze
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
* @author TechFuze
|
||||
* @copyright Copyright (c) 2013 - 2019, TechFuze. (http://techfuze.net)
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
*
|
||||
* @link http://techfuze.net/fuzeworks
|
||||
* @since Version 1.2.0
|
||||
*
|
||||
* @version Version 1.2.0
|
||||
*/
|
||||
|
||||
namespace FuzeWorks\Event;
|
||||
|
||||
use FuzeWorks\Controller;
|
||||
use FuzeWorks\Event;
|
||||
use FuzeWorks\Priority;
|
||||
use FuzeWorks\View;
|
||||
|
||||
/**
|
||||
* Event that gets fired when a view is about to be called by the defaultCallable() in the Router.
|
||||
*
|
||||
* Use this to cancel the calling of a view method.
|
||||
*
|
||||
* Currently only used by Router::defaultCallable();
|
||||
*
|
||||
* This event is currently used in the WebComponent project. It allows the component to stop loading when
|
||||
* a CSRFException is thrown, and the view has no method of handling this request
|
||||
*
|
||||
* @author Abel Hoogeveen <abel@techfuze.net>
|
||||
* @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;
|
||||
}
|
||||
}
|
82
src/FuzeWorks/Event/RouterLoadCallableEvent.php
Normal file
82
src/FuzeWorks/Event/RouterLoadCallableEvent.php
Normal file
@ -0,0 +1,82 @@
|
||||
<?php
|
||||
/**
|
||||
* FuzeWorks Framework MVCR Component.
|
||||
*
|
||||
* The FuzeWorks PHP FrameWork
|
||||
*
|
||||
* Copyright (C) 2013-2019 TechFuze
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
* @author TechFuze
|
||||
* @copyright Copyright (c) 2013 - 2019, TechFuze. (http://techfuze.net)
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
*
|
||||
* @link http://techfuze.net/fuzeworks
|
||||
* @since Version 1.2.0
|
||||
*
|
||||
* @version Version 1.2.0
|
||||
*/
|
||||
|
||||
namespace FuzeWorks\Event;
|
||||
|
||||
use FuzeWorks\Event;
|
||||
|
||||
/**
|
||||
* Event that gets fired when a callable is to be loaded by the Router class
|
||||
*
|
||||
* Use this to cancel the modify the loading of a custom callable or the defaultCallable
|
||||
*
|
||||
* Currently only used by Router::loadCallable();
|
||||
*
|
||||
* @author Abel Hoogeveen <abel@techfuze.net>
|
||||
* @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;
|
||||
}
|
||||
|
||||
}
|
@ -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 <abel@techfuze.net>
|
||||
* @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.
|
||||
*
|
||||
|
@ -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 <abel@techfuze.net>
|
||||
* @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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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 <abel@techfuze.net>
|
||||
* @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;
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
|
@ -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<viewName>.*?)(|\/(?P<viewMethod>.*?)(|\/(?P<viewParameters>.*?)))(|\.(?P<viewType>.*?))');
|
||||
|
||||
// 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'));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -27,7 +27,7 @@
|
||||
<directory suffix=".php">../</directory>
|
||||
<exclude>
|
||||
<directory suffix=".php">../vendor/</directory>
|
||||
<directory suffix=".php">../tests/</directory>
|
||||
<directory suffix=".php">../test/</directory>
|
||||
</exclude>
|
||||
</whitelist>
|
||||
</filter>
|
||||
|
52
test/views/view.test.testdefaultcallablechangemethod.php
Normal file
52
test/views/view.test.testdefaultcallablechangemethod.php
Normal file
@ -0,0 +1,52 @@
|
||||
<?php
|
||||
/**
|
||||
* FuzeWorks CLIComponent.
|
||||
*
|
||||
* The FuzeWorks PHP FrameWork
|
||||
*
|
||||
* Copyright (C) 2013-2019 TechFuze
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
* @author TechFuze
|
||||
* @copyright Copyright (c) 2013 - 2019, TechFuze. (http://techfuze.net)
|
||||
* @license https://opensource.org/licenses/MIT MIT License
|
||||
*
|
||||
* @link http://techfuze.net/fuzeworks
|
||||
* @since Version 1.2.0
|
||||
*
|
||||
* @version Version 1.2.0
|
||||
*/
|
||||
|
||||
namespace Application\View;
|
||||
use FuzeWorks\View;
|
||||
|
||||
class TestDefaultCallableChangeMethodTestView extends View
|
||||
{
|
||||
|
||||
public function index()
|
||||
{
|
||||
return "Not altered!";
|
||||
}
|
||||
|
||||
public function altered()
|
||||
{
|
||||
return "Altered!";
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user