Compare commits

...

9 Commits

Author SHA1 Message Date
Abel Hoogeveen 016fa03ac2
Updated to PHP 8.0.
continuous-integration/drone/push Build is passing Details
- Release version 1.3.3.
- Most property, argument and return types have been defined and are now enforced.
- Drone now performs tests for both PHP 7.4 and 8.0. Extend for future versions.
2021-11-24 19:31:59 +01:00
Abel Hoogeveen 51153520e2
Added absolute pathing for unit tests.
continuous-integration/drone/push Build is passing Details
2021-01-28 16:07:48 +01:00
Abel Hoogeveen 3f522c55fa
Added categories to the router, without breaking (most) backwards compatibility.
continuous-integration/drone/push Build is failing Details
Categories can be added using the routeConfig, by using a 'category' key. Router::route() can then distinguish between them using its second parameter.
Also added drone support.
2021-01-28 15:42:38 +01:00
Abel Hoogeveen 90e6f64c40
Added 'addParameter()' method to routerLoadViewAndControllerEvent, and to routerCallViewEvent.
This allows the developer to add parameters retrieved outside of the matching system.
Requested by CLIComponent.
2021-01-24 13:49:34 +01:00
Abel Hoogeveen 9d86c03f02
Fixed critical security bug, risking unprotected views to be exposed.
Had to make some design concessions. ViewType can no longer be distilled using the routeMatches, but must instead now be provided by the more static routeConfig. This way, the chosen ViewType shall always be one that is chosen by the developer.

Also added some other smaller features, as requested by other components and plugins:
- Now added a 'namespacePrefix' for Router::defaultCallable. Allows for the usage of Views and Controllers from a namespace different from '\\Application', from within the router sphere.
- Added a $routes = [] parameter to Router::route(), allowing the developer to bypass the global routes list if necessary.
2021-01-19 21:16:39 +01:00
Abel Hoogeveen 55e0848a70 Made models able to load models. 2020-07-12 11:59:38 +02:00
Abel Hoogeveen 9572d27043
Allowed the view to load other views and other controllers. 2020-06-13 11:54:44 +02:00
Abel Hoogeveen d6fcf1c750
Release version 1.2.2.
Now supports loading Controllers, Models and Views using the autoloader, which for some reason wasn't supported.
2020-06-07 12:18:34 +02:00
Abel Hoogeveen e30d4ad366 Fixed bug where controller was not changed after a routerCallViewEvent
Router::defaultCallable did not change the controller in the view upon a change in the RouterCallViewEvent.
This has been remedied by adding a check and performing the change when that check results in a discrepency.
2020-05-12 13:10:50 +02:00
39 changed files with 573 additions and 378 deletions

30
.drone.yml Normal file
View File

@ -0,0 +1,30 @@
kind: pipeline
type: docker
name: test
steps:
- name: composer
image: composer:latest
commands:
- composer install
- name: php74test
image: registry.i15.nl/i15/fuzephp:7.4-alpine
commands:
- docker-php-ext-enable xdebug
- vendor/bin/phpunit -c test/phpunit.xml
- name: php80test
image: registry.i15.nl/i15/fuzephp:8.0-alpine
commands:
- docker-php-ext-enable xdebug
- vendor/bin/phpunit -c test/phpunit.xml
- name: coverage
image: registry.i15.nl/i15/fuzephp:8.0-alpine
commands:
- docker-php-ext-enable xdebug
- vendor/bin/phpunit -c test/phpunit.xml --coverage-text
image_pull_secrets:
- dockerconfig

1
.gitattributes vendored
View File

@ -1,4 +1,5 @@
.gitattributes export-ignore
.gitignore export-ignore
.gitlab-ci.yml export-ignore
.drone.yml export-ignore
test/ export-ignore

1
.gitignore vendored
View File

@ -4,3 +4,4 @@ composer.phar
build/
test/temp/
vendor/
*.cache

View File

@ -1,82 +0,0 @@
before_script:
# Install dependencies
- set -xe
- apt-get update -yqq
- apt-get install git zip unzip -yqq
stages:
- build
- test
- deploy
build:composer:
image: php:7.2
stage: build
script:
- curl -sS https://getcomposer.org/installer | php
- php composer.phar install
cache:
key: "$CI_BUILD_REF_$CI_BUILD_REF_NAME"
paths:
- vendor/
test:7.1:
stage: test
image: php:7.1
script:
- vendor/bin/phpunit -c test/phpunit.xml
cache:
key: "$CI_BUILD_REF_$CI_BUILD_REF_NAME"
paths:
- vendor
test:7.2:
stage: test
image: php:7.2
script:
- vendor/bin/phpunit -c test/phpunit.xml
cache:
key: "$CI_BUILD_REF_$CI_BUILD_REF_NAME"
paths:
- vendor/
test:7.3:
stage: test
image: php:7.3
script:
- vendor/bin/phpunit -c test/phpunit.xml
cache:
key: "$CI_BUILD_REF_$CI_BUILD_REF_NAME"
paths:
- vendor/
test:coverage:
stage: test
image: php:7.2
script:
- pecl install xdebug
- docker-php-ext-enable xdebug
- vendor/bin/phpunit -c test/phpunit.xml --coverage-text
cache:
key: "$CI_BUILD_REF_$CI_BUILD_REF_NAME"
paths:
- vendor/
release:
stage: deploy
image: php:7.2
only:
- master
script:
- 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:
- build/
expire_in: 3 weeks
cache:
key: "$CI_BUILD_REF_$CI_BUILD_REF_NAME"
paths:
- vendor/

View File

@ -1,12 +0,0 @@
language: php
php:
- 7.1
- 7.2
- 7.3
script:
- php vendor/bin/phpunit -v -c test/phpunit.xml --coverage-text
before_script:
- composer install

View File

@ -4,21 +4,17 @@
"license": ["MIT"],
"authors": [
{
"name": "TechFuze",
"homepage": "https://techfuze.net"
},
{
"name": "FuzeWorks Community",
"homepage": "https://techfuze.net/fuzeworks/contributors"
"name": "Abel Hoogeveen",
"homepage": "https://i15.nl"
}
],
"require": {
"php": ">=7.1.0",
"fuzeworks/core": "~1.2.0"
"php": ">=7.4.0",
"fuzeworks/core": "~1.3.0"
},
"require-dev": {
"phpunit/phpunit": "^7",
"mikey179/vfsstream": "1.6.5"
"phpunit/phpunit": "^9",
"mikey179/vfsstream": "~1.6.0"
},
"autoload": {
"psr-4": {

View File

@ -31,7 +31,7 @@
* @link http://techfuze.net/fuzeworks
* @since Version 1.2.0
*
* @version Version 1.2.0
* @version Version 1.3.3
*/
/**
@ -44,7 +44,7 @@
* Custom callable: Adds a route that sends all matches to the provided callable. Allows user to replace defaultCallable
* 'routingString' => array('callable' => array(CALLABLE))
*
* Dynamic rewrite: Adds a route that rewrites an URL to a specific controller and method configuration, using a callable. The callable can dynamically determine which page to load.
* Dynamic rewrite: Adds a route that rewrites a URL to a specific controller and method configuration, using a callable. The callable can dynamically determine which page to load.
* 'routingString' => CALLABLE
*
* Static rewrite: Adds a route that rewrites and URL to a specific controller and method using a fixed route. This allows for pre-determined rewrites of pages.

View File

@ -39,5 +39,6 @@ return array(
'default_view' => 'index',
'default_viewType' => 'standard',
'default_viewMethod' => 'index',
'default_namespacePrefix' => '\Application\\'
);

View File

@ -31,7 +31,7 @@
* @link http://techfuze.net/fuzeworks
* @since Version 1.2.0
*
* @version Version 1.2.0
* @version Version 1.3.3
*/
namespace FuzeWorks;
@ -49,22 +49,22 @@ abstract class Controller
/**
* @var Plugins
*/
protected $plugins;
protected Plugins $plugins;
/**
* @var Libraries
*/
protected $libraries;
protected Libraries $libraries;
/**
* @var Helpers
*/
protected $helpers;
protected Helpers $helpers;
/**
* @var Config
*/
protected $config;
protected Config $config;
/**
* @var Controllers

View File

@ -31,7 +31,7 @@
* @link http://techfuze.net/fuzeworks
* @since Version 1.2.0
*
* @version Version 1.2.0
* @version Version 1.3.3
*/
namespace FuzeWorks;
@ -45,7 +45,7 @@ use FuzeWorks\Exception\NotFoundException;
* Controllers Class.
*
* Simple loader class for MVC Controllers.
* Typically loads controllers from Application\Controller unless otherwise specified.
* Typically, loads controllers from Application\Controller unless otherwise specified.
*
* @author Abel Hoogeveen <abel@techfuze.net>
* @copyright Copyright (c) 2013 - 2019, TechFuze. (http://techfuze.net)
@ -128,7 +128,7 @@ class Controllers
// If the class already exists, return a new instance directly
$class = ucfirst($class);
$className = $namespace . $class . 'Controller';
if (class_exists($className, false))
if (class_exists($className, true))
{
$controller = new $className(...$arguments);
if (!$controller instanceof Controller)

View File

@ -31,7 +31,7 @@
* @link http://techfuze.net/fuzeworks
* @since Version 1.2.0
*
* @version Version 1.2.0
* @version Version 1.3.3
*/
namespace FuzeWorks\Event;
@ -52,28 +52,28 @@ class ControllerGetEvent extends Event
*
* @var array
*/
public $controllerPaths = array();
public array $controllerPaths = array();
/**
* The name of the controller to be loaded.
*
* @var string|null
*/
public $controllerName = null;
public ?string $controllerName = null;
/**
* The namespace of the controller to be loaded. Defaults to Application\Controller
*
* @var string
*/
public $namespace = '\Application\Controller\\';
public string $namespace = '\Application\Controller\\';
/**
* Arguments provided to the constructor
*
* @var array
*/
public $arguments = [];
public array $arguments = [];
public function init($controllerName, $controllerPaths, $namespace, $arguments)
{

View File

@ -31,7 +31,7 @@
* @link http://techfuze.net/fuzeworks
* @since Version 1.2.0
*
* @version Version 1.2.0
* @version Version 1.3.3
*/
namespace FuzeWorks\Event;
@ -53,28 +53,28 @@ class ModelGetEvent extends Event
*
* @var array
*/
public $modelPaths = array();
public array $modelPaths = array();
/**
* The name of the model to be loaded.
*
* @var string|null
*/
public $modelName = null;
public ?string $modelName = null;
/**
* The namespace of the model to be loaded. Defaults to Application\Model
*
* @var string
*/
public $namespace = '\Application\Model\\';
public string $namespace = '\Application\Model\\';
/**
* Arguments provided to the constructor
*
* @var array
*/
public $arguments = [];
public array $arguments = [];
public function init($modelName, $modelPaths, $namespace, $arguments)
{

View File

@ -31,7 +31,7 @@
* @link http://techfuze.net/fuzeworks
* @since Version 1.2.0
*
* @version Version 1.2.0
* @version Version 1.3.3
*/
namespace FuzeWorks\Event;
@ -46,7 +46,7 @@ use FuzeWorks\View;
*
* Use this to cancel the calling of a view method.
*
* Currently only used by Router::defaultCallable();
* 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
@ -61,37 +61,37 @@ class RouterCallViewEvent extends Event
*
* @var array
*/
public $viewMethods;
public array $viewMethods;
/**
* The parameters that will be provided to the function in the view
*
* @var string
* @var array
*/
public $viewParameters;
public array $viewParameters;
/**
* The route that resulted in this controller and view
*
* @var string
*/
public $route;
public string $route;
/**
* The view the method will be called on
*
* @var View
*/
public $view;
public View $view;
/**
* The controller that's associated with this View
*
* @var Controller
*/
public $controller;
public Controller $controller;
public function init(View $view, Controller $controller, array $viewMethods, string $viewParameters, string $route)
public function init(View $view, Controller $controller, array $viewMethods, array $viewParameters, string $route)
{
$this->view = $view;
$this->controller = $controller;
@ -114,4 +114,14 @@ class RouterCallViewEvent extends Event
if (!isset($this->viewMethods[$priority][$method]))
$this->viewMethods[$priority][] = $method;
}
/**
* Add a parameter to call the view with
*
* @param $parameter
*/
public function addParameter($parameter)
{
$this->viewParameters[] = $parameter;
}
}

View File

@ -31,7 +31,7 @@
* @link http://techfuze.net/fuzeworks
* @since Version 1.2.0
*
* @version Version 1.2.0
* @version Version 1.3.3
*/
namespace FuzeWorks\Event;
@ -41,9 +41,9 @@ 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
* Use this to cancel the modification the loading of a custom callable or the defaultCallable
*
* Currently only used by Router::loadCallable();
* Currently, only used by Router::loadCallable();
*
* @author Abel Hoogeveen <abel@techfuze.net>
* @copyright Copyright (c) 2013 - 2019, TechFuze. (http://techfuze.net)
@ -63,19 +63,27 @@ class RouterLoadCallableEvent extends Event
*
* @var array
*/
public $matches;
public array $matches;
/**
* The static route configuration
*
* @var array
*/
public array $routeData;
/**
* The route which resulted in this callable being loaded
*
* @var string
*/
public $route;
public string $route;
public function init(callable $callable, array $matches, string $route)
public function init(callable $callable, array $matches, array $routeData, string $route)
{
$this->callable = $callable;
$this->matches = $matches;
$this->routeData = $routeData;
$this->route = $route;
}

View File

@ -31,7 +31,7 @@
* @link http://techfuze.net/fuzeworks
* @since Version 1.2.0
*
* @version Version 1.2.0
* @version Version 1.3.3
*/
namespace FuzeWorks\Event;
@ -44,7 +44,7 @@ use FuzeWorks\Priority;
*
* Use this to cancel the loading of a combination, or change the details of what is loaded.
*
* Currently only used by Router::defaultCallable();
* Currently, only used by Router::defaultCallable();
*
* @author Abel Hoogeveen <abel@techfuze.net>
* @copyright Copyright (c) 2013 - 2019, TechFuze. (http://techfuze.net)
@ -56,49 +56,57 @@ class RouterLoadViewAndControllerEvent extends Event
*
* @var string
*/
public $viewName;
public string $viewName;
/**
* The type of view to be loaded
*
* @var string
*/
public $viewType;
public string $viewType;
/**
* The function that will be loaded in the view
*
* @var array
*/
public $viewMethods;
public array $viewMethods;
/**
* The parameters that will be provided to the function in the view
*
* @var array
*/
public array $viewParameters;
/**
* The namespace to use to load the View and Controller
*
* @var string
*/
public $viewParameters;
public string $namespacePrefix;
/**
* The route that resulted in this controller and view
*
* @var string
*/
public $route;
public string $route;
/**
* A controller to be injected.
*
* @var Controller|null
*/
public $controller;
public ?Controller $controller = null;
public function init(string $viewName, string $viewType, array $viewMethods, string $viewParameters, string $route)
public function init(string $viewName, string $viewType, array $viewMethods, array $viewParameters, string $namespacePrefix, string $route)
{
$this->viewName = $viewName;
$this->viewType = $viewType;
$this->viewMethods = $viewMethods;
$this->viewParameters = $viewParameters;
$this->namespacePrefix = $namespacePrefix;
$this->route = $route;
}
@ -117,6 +125,16 @@ class RouterLoadViewAndControllerEvent extends Event
$this->viewMethods[$priority][] = $method;
}
/**
* Add a parameter to call the view with
*
* @param $parameter
*/
public function addParameter($parameter)
{
$this->viewParameters[] = $parameter;
}
/**
* Override the controller to be provided to the view.
*

View File

@ -31,7 +31,7 @@
* @link http://techfuze.net/fuzeworks
* @since Version 1.2.0
*
* @version Version 1.2.0
* @version Version 1.3.3
*/
namespace FuzeWorks\Event;
@ -53,40 +53,40 @@ class ViewGetEvent extends Event
*
* @var array
*/
public $viewPaths = [];
public array $viewPaths = [];
/**
* The name of the view to be loaded.
*
* @var string|null
*/
public $viewName = null;
public ?string $viewName = null;
/**
* The type of view to be loaded. Eg: html, json, cli.
*
* @var string|null
*/
public $viewType = null;
public ?string $viewType = null;
/**
* The namespace of the View to be loaded. Defaults to Application\View
*
* @var string
*/
public $namespace = '\Application\View\\';
public string $namespace = '\Application\View\\';
/**
* Arguments provided to the constructor
*
* @var array
*/
public $arguments = [];
public array $arguments = [];
/**
* @var Controller
*/
public $controller;
public Controller $controller;
public function init($viewName, $viewType, $viewPaths, $namespace, $controller, $arguments)
{

View File

@ -31,7 +31,7 @@
* @link http://techfuze.net/fuzeworks
* @since Version 1.2.0
*
* @version Version 1.2.0
* @version Version 1.3.3
*/
namespace FuzeWorks;
@ -50,22 +50,27 @@ abstract class Model
/**
* @var Plugins
*/
protected $plugins;
protected Plugins $plugins;
/**
* @var Libraries
*/
protected $libraries;
protected Libraries $libraries;
/**
* @var Helpers
*/
protected $helpers;
protected Helpers $helpers;
/**
* @var Config
*/
protected $config;
protected Config $config;
/**
* @var Models
*/
protected $models;
public function __construct()
{
@ -73,6 +78,7 @@ abstract class Model
$this->libraries = Factory::getInstance()->libraries;
$this->helpers = Factory::getInstance()->helpers;
$this->config = Factory::getInstance()->config;
$this->models = Factory::getInstance()->models;
}
}

View File

@ -31,7 +31,7 @@
* @link http://techfuze.net/fuzeworks
* @since Version 1.2.0
*
* @version Version 1.2.0
* @version Version 1.3.3
*/
namespace FuzeWorks;
@ -45,7 +45,7 @@ use FuzeWorks\Exception\NotFoundException;
* Models Class.
*
* Simple loader class for MVC Models.
* Typically loads models from Application\Model unless otherwise specified.
* Typically, loads models from Application\Model unless otherwise specified.
*
* @author Abel Hoogeveen <abel@techfuze.net>
* @copyright Copyright (c) 2013 - 2019, TechFuze. (http://techfuze.net)
@ -128,7 +128,7 @@ class Models
// If the class already exists, return a new instance directly
$class = ucfirst($class);
$className = $namespace . $class . 'Model';
if (class_exists($className, false)) {
if (class_exists($className, true)) {
$model = new $className(...$arguments);
if (!$model instanceof Model)
throw new ModelException("Could not load model. Provided modelName is not instance of \FuzeWorks\Model");

View File

@ -31,7 +31,7 @@
* @link http://techfuze.net/fuzeworks
* @since Version 1.2.0
*
* @version Version 1.2.0
* @version Version 1.3.3
*/
namespace FuzeWorks;
@ -54,7 +54,7 @@ class Router
*
* @var array
*/
protected $routes = [];
protected array $routes = [];
/**
* The current callable used
@ -68,43 +68,48 @@ class Router
*
* @var array|null
*/
protected $matches = null;
protected ?array $matches = null;
/**
* @var array|Closure|null
*/
protected $routeData = null;
/**
* The current route used
*
* @var string|null
*/
protected $route = null;
protected ?string $route = null;
/**
* The current View
*
* @var View|null
*/
protected $view = null;
protected ?View $view = null;
/**
* The current Controller
*
* @var Controller|null
*/
protected $controller = null;
protected ?Controller $controller = null;
/**
* @var Config
*/
private $config;
private Config $config;
/**
* @var Controllers
*/
private $controllers;
private Controllers $controllers;
/**
* @var Views
*/
private $views;
private Views $views;
/**
* Router constructor.
@ -151,7 +156,7 @@ class Router
$routeConfig = ['callable' => [$this, 'defaultCallable']];
}
// Finally add the route
// Finally, add the route
$this->addRoute($route, $routeConfig);
}
}
@ -160,7 +165,7 @@ class Router
* Add a route to the Router
*
* @param string $route
* @param null $routeConfig
* @param mixed $routeConfig
* @param int $priority
*/
public function addRoute(string $route, $routeConfig = null, int $priority = Priority::NORMAL)
@ -169,14 +174,19 @@ class Router
if (is_null($routeConfig))
$routeConfig = ['callable' => [$this, 'defaultCallable']];
// Select the category
$category = is_array($routeConfig) && isset($routeConfig['category']) ? $routeConfig['category'] : 'default';
if (!isset($this->routes[$category]))
$this->routes[$category] = [];
// Convert wildcards to Regex
$route = str_replace([':any',':num'], ['[^/]+', '[0-9]+'], $route);
if (!isset($this->routes[$priority]))
$this->routes[$priority] = [];
if (!isset($this->routes[$category][$priority]))
$this->routes[$category][$priority] = [];
if (!isset($this->routes[$priority][$route]))
$this->routes[$priority][$route] = $routeConfig;
if (!isset($this->routes[$category][$priority][$route]))
$this->routes[$category][$priority][$route] = $routeConfig;
Logger::log('Route added with ' . Priority::getPriority($priority) . ": '" . $route."'");
}
@ -184,33 +194,40 @@ class Router
/**
* Removes a route from the array based on the given route.
*
* @param $route string The route to remove
* @param int $priority
* @param string $route The route to remove
* @param string $category The category to remove it from (defaults to 'default')
* @param int $priority The priority to remove it from (defaults to Priority::NORMAL)
*/
public function removeRoute(string $route, int $priority = Priority::NORMAL)
public function removeRoute(string $route, string $category = 'default', int $priority = Priority::NORMAL)
{
if (!isset($this->routes[$priority][$route]))
if (!isset($this->routes[$category][$priority][$route]))
return;
unset($this->routes[$priority][$route]);
unset($this->routes[$category][$priority][$route]);
Logger::log('Route removed: '.$route);
}
/**
* @param string $path
* @return mixed
* @throws NotFoundException
* @throws RouterException
* @throws HaltException
* @param string $path The string to route using Router
* @param string $category The category of routes to search in (defaults to 'default')
* @param array $routes Alternative routes if using global routes is not desired
* @return mixed The output of the callable
* @throws NotFoundException Thrown if route failed to deliver
* @throws RouterException Thrown if things fatally break
* @throws HaltException Thrown if route results in an unauthorised request
*/
public function route(string $path)
public function route(string $path, string $category = 'default', array $routes = [])
{
// Select the routes to use
$globalRoutes = $this->routes[$category] ?? [];
$routes = empty($routes) ? $globalRoutes : $routes;
// Check all the provided custom paths, ordered by priority
for ($i=Priority::getHighestPriority(); $i<=Priority::getLowestPriority(); $i++) {
if (!isset($this->routes[$i]))
if (!isset($routes[$i]))
continue;
foreach ($this->routes[$i] as $route => $routeConfig)
foreach ($routes[$i] as $route => $routeConfig)
{
// Match the path against the routes
if (!preg_match('#^'.$route.'$#', $path, $matches))
@ -219,29 +236,30 @@ class Router
// Save the matches
Logger::log("Route matched: '" . $route . "' with " . Priority::getPriority($i));
$this->matches = $matches;
$this->routeData = $routeConfig;
$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 (is_callable($this->routeData))
$this->routeData = call_user_func_array($this->routeData, [$matches]);
// If routeConfig is an array, multiple things might be at hand
if (is_array($routeConfig))
if (is_array($this->routeData))
{
// 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 (isset($this->routeData['callable']) && is_callable($this->routeData['callable']))
$this->callable = $this->routeData['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);
$this->matches = array_merge($this->matches, $this->routeData);
}
// If no custom callable is provided, use default
@ -250,7 +268,7 @@ class Router
$this->callable = [$this, 'defaultCallable'];
// Attempt and load callable. If false, continue
$output = $this->loadCallable($this->callable, $this->matches, $route);
$output = $this->loadCallable($this->callable, $this->matches, $this->routeData, $route);
if (is_bool($output) && $output === FALSE)
{
Logger::log('Callable not satisfied, skipping to next callable');
@ -267,12 +285,13 @@ class Router
/**
* @param callable $callable
* @param array $matches
* @param array $routeData
* @param string $route
* @return mixed
* @throws RouterException
* @throws HaltException
* @throws RouterException
*/
protected function loadCallable(callable $callable, array $matches, string $route)
protected function loadCallable(callable $callable, array $matches, array $routeData, string $route)
{
// Log the input to the logger
Logger::newLevel('Loading callable with matches:');
@ -286,6 +305,7 @@ class Router
$event = Events::fireEvent('routerLoadCallableEvent',
$callable,
$matches,
$routeData,
$route
);
} catch (EventException $e) {
@ -297,29 +317,34 @@ class Router
throw new HaltException("Will not load callable. Cancelled by routerLoadCallableEvent.");
// Invoke callable
$output = call_user_func_array($event->callable, [$event->matches, $event->route]);
$output = call_user_func_array($event->callable, [$event->matches, $event->routeData, $event->route]);
Logger::stopLevel();
return $output;
}
/**
* @param array $matches
* @param array $routeData
* @param string $route
* @return mixed
* @throws HaltException
* @throws RouterException
* @todo Use $route and send it to the view
*/
public function defaultCallable(array $matches, string $route)
public function defaultCallable(array $matches, array $routeData, string $route)
{
Logger::log('defaultCallable called');
// Prepare variables
// Variables from matches
$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'] : '';
// Variables from routeData
$viewType = !empty($routeData['viewType']) ? $routeData['viewType'] : $this->config->routing->default_viewType;
$namespacePrefix = !empty($routeData['namespacePrefix']) ? $routeData['namespacePrefix'] : $this->config->routing->default_namespacePrefix;
try {
/** @var RouterLoadViewAndControllerEvent $event */
$event = Events::fireEvent('routerLoadViewAndControllerEvent',
@ -327,7 +352,8 @@ class Router
$viewType,
// ViewMethod is provided as a Priority::NORMAL method
[3 => [$viewMethod]],
$viewParameters,
[$viewParameters],
$namespacePrefix,
$route
);
} catch (EventException $e) {
@ -340,7 +366,7 @@ class Router
// First receive the controller
try {
$this->controller = (!is_null($event->controller) ? $event->controller : $this->controllers->get($event->viewName));
$this->controller = (!is_null($event->controller) ? $event->controller : $this->controllers->get($event->viewName, [], $event->namespacePrefix . 'Controller\\'));
} catch (ControllerException $e) {
throw new RouterException("Could not load view. Controllers::get threw ControllerException: '".$e->getMessage()."'");
} catch (NotFoundException $e) {
@ -350,7 +376,7 @@ class Router
// Then try and receive the view
try {
$this->view = $this->views->get($event->viewName, $this->controller, $event->viewType);
$this->view = $this->views->get($event->viewName, $this->controller, $event->viewType, [], $event->namespacePrefix. 'View\\');
} catch (ViewException $e) {
throw new RouterException("Could not load view. Views::get threw ViewException: '".$e->getMessage()."'");
} catch (NotFoundException $e) {
@ -371,7 +397,11 @@ class Router
// Reset vars
$this->view = $event->view;
$this->controller = $event->controller;
if ($this->controller !== $event->controller)
{
$this->controller = $event->controller;
$this->view->setController($this->controller);
}
} catch (EventException $e) {
throw new RouterException("Could not load view. routerCallViewEvent threw exception: '".$e->getMessage()."'");
}
@ -393,8 +423,8 @@ class Router
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::newLevel("Calling method '$method' on " . get_class($this->view) . ' with ' . get_class($this->controller));
$output = call_user_func_array([$this->view, $method], $event->viewParameters);
Logger::stopLevel();
return $output;
}
@ -411,13 +441,17 @@ class Router
/**
* Returns an array with all the routes.
*
* @param string $category
* @param int $priority
* @return array
* @codeCoverageIgnore
*/
public function getRoutes(int $priority = Priority::NORMAL): array
public function getRoutes(string $category = 'default', int $priority = Priority::NORMAL): array
{
return $this->routes[$priority];
if (isset($this->routes[$category][$priority]))
return $this->routes[$category][$priority];
return [];
}
/**
@ -426,7 +460,7 @@ class Router
* @return string|null
* @codeCoverageIgnore
*/
public function getCurrentRoute()
public function getCurrentRoute(): ?string
{
return $this->route;
}
@ -437,7 +471,7 @@ class Router
* @return null|array
* @codeCoverageIgnore
*/
public function getCurrentMatches()
public function getCurrentMatches(): ?array
{
return $this->matches;
}
@ -448,7 +482,7 @@ class Router
* @return View|null
* @codeCoverageIgnore
*/
public function getCurrentView()
public function getCurrentView(): ?View
{
return $this->view;
}
@ -459,7 +493,7 @@ class Router
* @return Controller|null
* @codeCoverageIgnore
*/
public function getCurrentController()
public function getCurrentController(): ?Controller
{
return $this->controller;
}

View File

@ -31,7 +31,7 @@
* @link http://techfuze.net/fuzeworks
* @since Version 1.2.0
*
* @version Version 1.2.0
* @version Version 1.3.3
*/
namespace FuzeWorks;
@ -48,32 +48,42 @@ namespace FuzeWorks;
abstract class View
{
/**
* @var Controllers
*/
protected $controllers;
/**
* @var Views
*/
protected $views;
/**
* @var Plugins
*/
protected $plugins;
protected Plugins $plugins;
/**
* @var Libraries
*/
protected $libraries;
protected Libraries $libraries;
/**
* @var Helpers
*/
protected $helpers;
protected Helpers $helpers;
/**
* @var Config
*/
protected $config;
protected Config $config;
/**
* The controller associated with this view
*
* @var Controller
*/
protected $controller;
protected Controller $controller;
/**
* Provide the View with its associated Controller
@ -87,6 +97,8 @@ abstract class View
public function __construct()
{
$this->controllers = Factory::getInstance()->controllers;
$this->views = Factory::getInstance()->views;
$this->plugins = Factory::getInstance()->plugins;
$this->libraries = Factory::getInstance()->libraries;
$this->helpers = Factory::getInstance()->helpers;

View File

@ -31,7 +31,7 @@
* @link http://techfuze.net/fuzeworks
* @since Version 1.2.0
*
* @version Version 1.2.0
* @version Version 1.3.3
*/
namespace FuzeWorks;
@ -44,7 +44,7 @@ use FuzeWorks\Exception\ViewException;
* Views Class.
*
* Simple loader class for MVC Views.
* Typically loads views from Application\View unless otherwise specified.
* Typically, loads views from Application\View unless otherwise specified.
*
* @author Abel Hoogeveen <abel@techfuze.net>
* @copyright Copyright (c) 2013 - 2019, TechFuze. (http://techfuze.net)
@ -130,8 +130,8 @@ class Views
// If the class already exists, return a new instance directly
$class = ucfirst($class);
$className = $namespace . $class . $viewType . 'View';
if (class_exists($className, false)) {
$className = $namespace . $class . ucfirst($viewType) . 'View';
if (class_exists($className, true)) {
/** @var View $view */
$view = new $className(...$arguments);
if (!$view instanceof View)

View File

@ -38,10 +38,7 @@
chdir(dirname(__DIR__));
// Load the FuzeWorks container
$container = require('test/bootstrap.php');
// Load the test abstract
require_once 'mcr/MVCRTestAbstract.php';
$container = require('bootstrap.php');
// Reset error and exception handlers
restore_error_handler();

View File

@ -31,9 +31,11 @@
* @link http://techfuze.net/fuzeworks
* @since Version 0.0.1
*
* @version Version 1.2.0
* @version Version 1.3.3
*/
use FuzeWorks\MVCRComponent;
require_once(dirname(__DIR__) . '/vendor/autoload.php');
$configurator = new FuzeWorks\Configurator();
@ -44,15 +46,11 @@ $configurator->setLogDirectory(dirname(__FILE__) . '/temp');
// Other values
$configurator->setTimeZone('Europe/Amsterdam');
$configurator->enableDebugMode(false);
$configurator->setDebugAddress('NONE');
$configurator->setDebugAddress();
// Implement the MVCR Component
$configurator->addComponent(new \FuzeWorks\MVCRComponent());
$configurator->addComponent(new MVCRComponent());
// Create container
$container = $configurator->createContainer();
// And return the result
return $container;
return $configurator->createContainer();

View File

@ -40,5 +40,5 @@ use FuzeWorks\Controller;
class TestDifferentComponentPathPriorityController extends Controller
{
public $type = 'highest';
public string $type = 'highest';
}

View File

@ -40,5 +40,5 @@ use FuzeWorks\Controller;
class TestDifferentComponentPathPriorityController extends Controller
{
public $type = 'lowest';
public string $type = 'lowest';
}

View File

@ -29,32 +29,16 @@
* @license https://opensource.org/licenses/MIT MIT License
*
* @link http://techfuze.net/fuzeworks
* @since Version 0.0.1
* @since Version 1.2.0
*
* @version Version 1.2.0
*/
use FuzeWorks\Events;
use PHPUnit\Framework\TestCase;
use FuzeWorks\Factory;
use FuzeWorks\Core;
namespace Custom\Controller;
/**
* Class MVCRTestAbstract.
*
* Resets core components to their original state
*/
abstract class MVCRTestAbstract extends TestCase
use FuzeWorks\Controller;
class TestDefaultCallableCustomNamespaceController extends Controller
{
/**
* Remove all listeners before the next test starts.
*/
public function tearDown()
{
// Clear all events created by tests
Events::$listeners = array();
// Reset all config files
Factory::getInstance()->config->discardConfigFiles();
}
}
}

View File

@ -38,24 +38,39 @@ use FuzeWorks\Controller;
use FuzeWorks\Controllers;
use FuzeWorks\Event\ControllerGetEvent;
use FuzeWorks\Events;
use FuzeWorks\Exception\ControllerException;
use FuzeWorks\Exception\NotFoundException;
use FuzeWorks\Factory;
use FuzeWorks\Priority;
use PHPUnit\Framework\TestCase;
/**
* Class ControllersTest
* @coversDefaultClass \FuzeWorks\Controllers
*/
class ControllersTest extends MVCRTestAbstract
class ControllersTest extends TestCase
{
/**
* @var Controllers
*/
protected $controllers;
protected Controllers $controllers;
public function setUp()
public function setUp(): void
{
$this->controllers = new Controllers();
$this->controllers->addComponentPath('test'.DS.'controllers');
$this->controllers->addComponentPath(dirname(__DIR__) . DS . 'controllers');
}
/**
* Remove all listeners before the next test starts.
*/
public function tearDown(): void
{
// Clear all events created by tests
Events::$listeners = array();
// Reset all config files
Factory::getInstance()->config->discardConfigFiles();
}
/**
@ -77,7 +92,6 @@ class ControllersTest extends MVCRTestAbstract
* @depends testGetControllerFromClass
* @covers ::get
* @covers ::loadController
* @expectedException \FuzeWorks\Exception\ControllerException
*/
public function testGetControllerFromClassInvalidInstance()
{
@ -87,6 +101,7 @@ class ControllersTest extends MVCRTestAbstract
class_alias($mockFakeControllerClass, $mockFakeControllerClass . 'Controller');
// Try and fetch
$this->expectException(ControllerException::class);
$this->controllers->get($mockFakeControllerClass, [], '\\');
}
@ -120,10 +135,10 @@ class ControllersTest extends MVCRTestAbstract
/**
* @covers ::get
* @expectedException \FuzeWorks\Exception\ControllerException
*/
public function testGetControllerInvalidName()
{
$this->expectException(ControllerException::class);
$this->controllers->get('', [], '\\');
}
@ -141,10 +156,10 @@ class ControllersTest extends MVCRTestAbstract
* @depends testGetControllerFromFile
* @covers ::get
* @covers ::loadController
* @expectedException \FuzeWorks\Exception\ControllerException
*/
public function testGetControllerFromFileInvalidInstance()
{
$this->expectException(ControllerException::class);
$this->controllers->get('ControllerInvalidInstance');
}
@ -156,8 +171,8 @@ class ControllersTest extends MVCRTestAbstract
public function testDifferentComponentPathPriority()
{
// Add the directories for this test
$this->controllers->addComponentPath('test'.DS.'controllers'.DS.'TestDifferentComponentPathPriority'.DS.'Lowest', Priority::LOWEST);
$this->controllers->addComponentPath('test'.DS.'controllers'.DS.'TestDifferentComponentPathPriority'.DS.'Highest', Priority::HIGHEST);
$this->controllers->addComponentPath(dirname(__DIR__) . DS . 'controllers'.DS.'TestDifferentComponentPathPriority'.DS.'Lowest', Priority::LOWEST);
$this->controllers->addComponentPath(dirname(__DIR__) . DS . 'controllers'.DS.'TestDifferentComponentPathPriority'.DS.'Highest', Priority::HIGHEST);
// Load the controller and assert it is the correct type
$controller = $this->controllers->get('TestDifferentComponentPathPriority');
@ -182,10 +197,10 @@ class ControllersTest extends MVCRTestAbstract
* @depends testGetControllerFromFile
* @covers ::get
* @covers ::loadController
* @expectedException \FuzeWorks\Exception\NotFoundException
*/
public function testControllerNotFound()
{
$this->expectException(NotFoundException::class);
$this->controllers->get('NotFound');
}
@ -193,7 +208,6 @@ class ControllersTest extends MVCRTestAbstract
* @depends testGetControllerFromClass
* @covers ::get
* @covers \FuzeWorks\Event\ControllerGetEvent::init
* @expectedException \FuzeWorks\Exception\ControllerException
*/
public function testControllerGetEvent()
{
@ -208,13 +222,13 @@ class ControllersTest extends MVCRTestAbstract
$event->setCancelled(true);
}, 'controllerGetEvent', Priority::NORMAL);
$this->expectException(ControllerException::class);
$this->controllers->get('SomeControllerName', ['some_path'], 'SomeNamespace', 'Some Argument');
}
/**
* @depends testControllerGetEvent
* @covers ::get
* @expectedException \FuzeWorks\Exception\ControllerException
*/
public function testCancelGetController()
{
@ -223,6 +237,7 @@ class ControllersTest extends MVCRTestAbstract
$event->setCancelled(true);
}, 'controllerGetEvent', Priority::NORMAL);
$this->expectException(ControllerException::class);
$this->controllers->get('SomeController', [], '\\');
}

View File

@ -36,26 +36,42 @@
use FuzeWorks\Event\ModelGetEvent;
use FuzeWorks\Events;
use FuzeWorks\Exception\ModelException;
use FuzeWorks\Exception\NotFoundException;
use FuzeWorks\Factory;
use FuzeWorks\Model;
use FuzeWorks\Models;
use FuzeWorks\Priority;
use PHPUnit\Framework\TestCase;
/**
* Class ModelsTest
* @coversDefaultClass \FuzeWorks\Models
*/
class ModelsTest extends MVCRTestAbstract
class ModelsTest extends TestCase
{
/**
* @var Models
*/
protected $models;
protected Models $models;
public function setUp()
public function setUp(): void
{
$this->models = new Models();
$this->models->addComponentPath('test'.DS.'models');
$this->models->addComponentPath(dirname(__DIR__) . DS . 'models');
}
/**
* Remove all listeners before the next test starts.
*/
public function tearDown(): void
{
// Clear all events created by tests
Events::$listeners = array();
// Reset all config files
Factory::getInstance()->config->discardConfigFiles();
}
/**
@ -77,7 +93,6 @@ class ModelsTest extends MVCRTestAbstract
* @depends testGetModelFromClass
* @covers ::get
* @covers ::loadModel
* @expectedException \FuzeWorks\Exception\ModelException
*/
public function testGetModelFromClassInvalidInstance()
{
@ -87,6 +102,7 @@ class ModelsTest extends MVCRTestAbstract
class_alias($mockFakeModelClass, $mockFakeModelClass . 'Model');
// Try and fetch
$this->expectException(ModelException::class);
$this->models->get($mockFakeModelClass, [], '\\');
}
@ -120,10 +136,10 @@ class ModelsTest extends MVCRTestAbstract
/**
* @covers ::get
* @expectedException \FuzeWorks\Exception\ModelException
*/
public function testGetModelInvalidName()
{
$this->expectException(ModelException::class);
$this->models->get('', [], '\\');
}
@ -141,10 +157,10 @@ class ModelsTest extends MVCRTestAbstract
* @depends testGetModelFromFile
* @covers ::get
* @covers ::loadModel
* @expectedException \FuzeWorks\Exception\ModelException
*/
public function testGetModelFromFileInvalidInstance()
{
$this->expectException(ModelException::class);
$this->models->get('ModelInvalidInstance');
}
@ -156,8 +172,8 @@ class ModelsTest extends MVCRTestAbstract
public function testDifferentComponentPathPriority()
{
// Add the directories for this test
$this->models->addComponentPath('test'.DS.'models'.DS.'TestDifferentComponentPathPriority'.DS.'Lowest', Priority::LOWEST);
$this->models->addComponentPath('test'.DS.'models'.DS.'TestDifferentComponentPathPriority'.DS.'Highest', Priority::HIGHEST);
$this->models->addComponentPath(dirname(__DIR__) . DS . 'models'.DS.'TestDifferentComponentPathPriority'.DS.'Lowest', Priority::LOWEST);
$this->models->addComponentPath(dirname(__DIR__) . DS . 'models'.DS.'TestDifferentComponentPathPriority'.DS.'Highest', Priority::HIGHEST);
// Load the model and assert it is the correct type
$model = $this->models->get('TestDifferentComponentPathPriority');
@ -182,10 +198,10 @@ class ModelsTest extends MVCRTestAbstract
* @depends testGetModelFromFile
* @covers ::get
* @covers ::loadModel
* @expectedException \FuzeWorks\Exception\NotFoundException
*/
public function testModelNotFound()
{
$this->expectException(NotFoundException::class);
$this->models->get('NotFound');
}
@ -193,7 +209,6 @@ class ModelsTest extends MVCRTestAbstract
* @depends testGetModelFromClass
* @covers ::get
* @covers \FuzeWorks\Event\ModelGetEvent::init
* @expectedException \FuzeWorks\Exception\ModelException
*/
public function testModelGetEvent()
{
@ -208,13 +223,13 @@ class ModelsTest extends MVCRTestAbstract
$event->setCancelled(true);
}, 'modelGetEvent', Priority::NORMAL);
$this->expectException(ModelException::class);
$this->models->get('SomeModelName', ['some_path'], 'SomeNamespace', 'Some Argument');
}
/**
* @depends testModelGetEvent
* @covers ::get
* @expectedException \FuzeWorks\Exception\ModelException
*/
public function testCancelGetModel()
{
@ -223,6 +238,7 @@ class ModelsTest extends MVCRTestAbstract
$event->setCancelled(true);
}, 'modelGetEvent', Priority::NORMAL);
$this->expectException(ModelException::class);
$this->models->get('SomeModel', [], '\\');
}

View File

@ -36,15 +36,18 @@
use FuzeWorks\Config;
use FuzeWorks\Events;
use FuzeWorks\Exception\HaltException;
use FuzeWorks\Exception\NotFoundException;
use FuzeWorks\Factory;
use FuzeWorks\Priority;
use FuzeWorks\Router;
use PHPUnit\Framework\TestCase;
/**
* Class RouterTest
* @coversDefaultClass \FuzeWorks\Router
*/
class RouterTest extends MVCRTestAbstract
class RouterTest extends TestCase
{
/**
@ -52,24 +55,36 @@ class RouterTest extends MVCRTestAbstract
*
* @var Router
*/
protected $router;
protected Router $router;
/**
* Holds the Config object
*
* @var Config
*/
protected $config;
protected Config $config;
public function setUp()
public function setUp(): void
{
// Get required classes
$this->router = new Router();
$this->config = Factory::getInstance()->config;
// Append required routes
Factory::getInstance()->controllers->addComponentPath('test' . DS . 'controllers');
Factory::getInstance()->views->addComponentPath('test' . DS . 'views');
Factory::getInstance()->controllers->addComponentPath(dirname(__DIR__) . DS . 'controllers');
Factory::getInstance()->views->addComponentPath(dirname(__DIR__) . DS . 'views');
}
/**
* Remove all listeners before the next test starts.
*/
public function tearDown(): void
{
// Clear all events created by tests
Events::$listeners = array();
// Reset all config files
Factory::getInstance()->config->discardConfigFiles();
}
/**
@ -124,10 +139,25 @@ class RouterTest extends MVCRTestAbstract
// Test if the order is correct
// First for Priority::NORMAL
$this->assertSame(['testRoute' => $testRouteFunction], $this->router->getRoutes(Priority::NORMAL));
$this->assertSame(['testRoute' => $testRouteFunction], $this->router->getRoutes('default', Priority::NORMAL));
// Then for Priority::LOW
$this->assertSame(['testAppendRoute' => $testAppendRouteFunction], $this->router->getRoutes(Priority::LOW));
$this->assertSame(['testAppendRoute' => $testAppendRouteFunction], $this->router->getRoutes('default', Priority::LOW));
}
/**
* @depends testAddRoutes
* @covers ::addRoute
* @covers ::getRoutes
*/
public function testRouteCategories()
{
$this->router->addRoute('defaultRoute', []);
$this->router->addRoute('defaultRoute2', ['category' => 'default']);
$this->router->addRoute('otherRoute', ['category' => 'other']);
$this->assertEquals(['defaultRoute' => [], 'defaultRoute2' => ['category' => 'default']], $this->router->getRoutes('default'));
$this->assertEquals(['otherRoute' => ['category' => 'other']], $this->router->getRoutes('other'));
}
/**
@ -139,13 +169,22 @@ class RouterTest extends MVCRTestAbstract
public function testRemoveRoutes()
{
// First add routes
$this->router->addRoute('testRemoveRoute', function () {
});
$this->router->addRoute('testRemoveRoute', function () {});
$this->assertArrayHasKey('testRemoveRoute', $this->router->getRoutes());
// Add a route with a category
$this->router->addRoute('categoryRoute', ['category' => 'other']);
$this->assertArrayHasKey('categoryRoute', $this->router->getRoutes('other'));
// Check if the categories do not cross
$this->assertArrayNotHasKey('testRemoveRoute', $this->router->getRoutes('other'));
$this->assertArrayNotHasKey('categoryRoute', $this->router->getRoutes('default'));
// Then remove
$this->router->removeRoute('testRemoveRoute');
$this->assertArrayNotHasKey('testRemoveRoute', $this->router->getRoutes());
$this->router->removeRoute('categoryRoute', 'other');
$this->assertArrayNotHasKey('categoryRoute', $this->router->getRoutes('other'));
}
/**
@ -203,13 +242,16 @@ class RouterTest extends MVCRTestAbstract
{
$matches = [
'viewName' => 'TestDefaultCallable',
'viewType' => 'test',
'viewMethod' => 'someMethod'
];
$data = [
'viewType' => 'test'
];
$this->assertNull($this->router->getCurrentController());
$this->assertNull($this->router->getCurrentView());
$this->assertEquals('Verify Output', $this->router->defaultCallable($matches, '.*$'));
$this->assertEquals('Verify Output', $this->router->defaultCallable($matches, $data, '.*$'));
$this->assertInstanceOf('\Application\Controller\TestDefaultCallableController', $this->router->getCurrentController());
$this->assertInstanceOf('\Application\View\TestDefaultCallableTestView', $this->router->getCurrentView());
}
@ -222,13 +264,16 @@ class RouterTest extends MVCRTestAbstract
{
$matches = [
'viewName' => 'TestDefaultCallable',
'viewType' => 'test',
'viewMethod' => 'missing'
];
$data = [
'viewType' => 'test'
];
$this->assertNull($this->router->getCurrentController());
$this->assertNull($this->router->getCurrentView());
$this->assertFalse($this->router->defaultCallable($matches, '.*$'));
$this->assertFalse($this->router->defaultCallable($matches, $data, '.*$'));
$this->assertInstanceOf('\Application\Controller\TestDefaultCallableController', $this->router->getCurrentController());
$this->assertInstanceOf('\Application\View\TestDefaultCallableTestView', $this->router->getCurrentView());
}
@ -241,13 +286,16 @@ class RouterTest extends MVCRTestAbstract
{
$matches = [
'viewName' => 'TestDefaultCallableMissingView',
'viewType' => 'test',
'viewMethod' => 'missing'
];
$data = [
'viewType' => 'test'
];
$this->assertNull($this->router->getCurrentController());
$this->assertNull($this->router->getCurrentView());
$this->assertFalse($this->router->defaultCallable($matches, '.*$'));
$this->assertFalse($this->router->defaultCallable($matches, $data,'.*$'));
$this->assertInstanceOf('\Application\Controller\TestDefaultCallableMissingViewController', $this->router->getCurrentController());
$this->assertNull($this->router->getCurrentView());
}
@ -260,13 +308,16 @@ class RouterTest extends MVCRTestAbstract
{
$matches = [
'viewName' => 'TestDefaultCallableMissingController',
'viewType' => 'test',
'viewMethod' => 'missing'
];
$data = [
'viewType' => 'test'
];
$this->assertNull($this->router->getCurrentController());
$this->assertNull($this->router->getCurrentView());
$this->assertFalse($this->router->defaultCallable($matches, '.*$'));
$this->assertFalse($this->router->defaultCallable($matches, $data, '.*$'));
$this->assertNull($this->router->getCurrentController());
$this->assertNull($this->router->getCurrentView());
}
@ -274,19 +325,23 @@ class RouterTest extends MVCRTestAbstract
/**
* @depends testDefaultCallable
* @covers ::defaultCallable
* @expectedException \FuzeWorks\Exception\HaltException
*/
public function testDefaultCallableHaltByView()
{
$matches = [
'viewName' => 'TestDefaultCallableHalt',
'viewType' => 'test',
'viewMethod' => 'someMethod'
];
$data = [
'viewType' => 'test'
];
$this->assertNull($this->router->getCurrentController());
$this->assertNull($this->router->getCurrentView());
$this->router->defaultCallable($matches, '.*$');
$this->expectException(HaltException::class);
$this->router->defaultCallable($matches, $data,'.*$');
$this->assertInstanceOf('\Application\Controller\TestDefaultCallableHaltController', $this->router->getCurrentController());
$this->assertInstanceOf('\Application\View\TestDefaultCallableHaltTestView', $this->router->getCurrentView());
}
@ -298,31 +353,59 @@ class RouterTest extends MVCRTestAbstract
public function testDefaultCallableEmptyName()
{
$matches = [
'viewType' => 'test',
'viewMethod' => 'someMethod'
];
$data = [
'viewType' => 'test'
];
$this->assertNull($this->router->getCurrentController());
$this->assertNull($this->router->getCurrentView());
$this->assertFalse($this->router->defaultCallable($matches, '.*$'));
$this->assertFalse($this->router->defaultCallable($matches, $data, '.*$'));
$this->assertNull($this->router->getCurrentController());
$this->assertNull($this->router->getCurrentView());
}
/**
* @depends testDefaultCallable
* @covers ::defaultCallable
*/
public function testDefaultCallableCustomNamespace()
{
$matches = [
'viewName' => 'TestDefaultCallableCustomNamespace',
'viewMethod' => 'someMethod'
];
$data = [
'viewType' => 'test',
'namespacePrefix' => '\Custom\\'
];
$this->assertNull($this->router->getCurrentController());
$this->assertNull($this->router->getCurrentView());
$this->assertEquals('Verify Output, with custom namespace!', $this->router->defaultCallable($matches, $data, '.*$'));
$this->assertInstanceOf('\Custom\Controller\TestDefaultCallableCustomNamespaceController', $this->router->getCurrentController());
$this->assertInstanceOf('\Custom\View\TestDefaultCallableCustomNamespaceTestView', $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'
];
$data = [
'viewType' => 'test'
];
$this->assertNull($this->router->getCurrentController());
$this->assertNull($this->router->getCurrentView());
@ -335,7 +418,8 @@ class RouterTest extends MVCRTestAbstract
$event->setCancelled(true);
}, 'routerLoadViewAndControllerEvent');
$this->router->defaultCallable($matches, '.*$');
$this->expectException(HaltException::class);
$this->router->defaultCallable($matches, $data, '.*$');
}
/**
@ -347,10 +431,13 @@ class RouterTest extends MVCRTestAbstract
{
$matches = [
'viewName' => 'TestDefaultCallable',
'viewType' => 'test',
'viewMethod' => 'missing'
];
$data = [
'viewType' => 'test'
];
$this->assertNull($this->router->getCurrentController());
$this->assertNull($this->router->getCurrentView());
@ -361,7 +448,7 @@ class RouterTest extends MVCRTestAbstract
$event->overrideController($mockController);
}, 'routerLoadViewAndControllerEvent', Priority::NORMAL, $mockController);
$this->router->defaultCallable($matches, '.*$');
$this->router->defaultCallable($matches, $data, '.*$');
$this->assertEquals($mockController, $this->router->getCurrentController());
}
@ -374,21 +461,27 @@ class RouterTest extends MVCRTestAbstract
{
$matches = [
'viewName' => 'TestDefaultCallableChangeMethod',
'viewType' => 'test',
'viewMethod' => 'index'
'viewMethod' => 'index',
'viewParameters' => 'parameter1'
];
$data = [
'viewType' => 'test'
];
$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);
$event->addParameter('parameter2');
}, 'routerLoadViewAndControllerEvent', Priority::NORMAL, $mockController);
$this->assertEquals('Altered!', $this->router->defaultCallable($matches, '.*$'));
$this->assertEquals(['Altered', 'parameter1', 'parameter2'], $this->router->defaultCallable($matches, $data, '.*$'));
}
/* route() ------------------------------------------------------------ */
@ -401,7 +494,7 @@ class RouterTest extends MVCRTestAbstract
public function testRoute()
{
// Add route first
$this->router->addRoute('(?P<viewName>.*?)(|\/(?P<viewMethod>.*?)(|\/(?P<viewParameters>.*?)))(|\.(?P<viewType>.*?))');
$this->router->addRoute('(?P<viewName>.*?)(|\/(?P<viewMethod>.*?)(|\/(?P<viewParameters>.*?))).test', ['viewType' => 'test']);
// Create mock view and controller
$mockController = $this->getMockBuilder('\FuzeWorks\Controller')->getMock();
@ -418,20 +511,41 @@ class RouterTest extends MVCRTestAbstract
/**
* @depends testRoute
* @covers ::route
* @expectedException \FuzeWorks\Exception\NotFoundException
*/
public function testDistinctRoute()
{
// 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\TestDistinctRouteController');
class_alias(get_class($mockView), '\Application\View\TestDistinctRouteTestView');
$this->assertNull($this->router->route('testDistinctRoute/testMethod/testParameters.test', 'default', [
3 => [
'(?P<viewName>.*?)(|\/(?P<viewMethod>.*?)(|\/(?P<viewParameters>.*?))).test' => ['viewType' => 'test']
]
]));
$this->assertInstanceOf('\Application\Controller\TestDistinctRouteController', $this->router->getCurrentController());
$this->assertInstanceOf('\Application\View\TestDistinctRouteTestView', $this->router->getCurrentView());
}
/**
* @depends testRoute
* @covers ::route
*/
public function testRouteNotFound()
{
$this->expectException(NotFoundException::class);
$this->router->route('NotFound');
}
/**
* @depends testRouteNotFound
* @covers ::route
* @expectedException \FuzeWorks\Exception\NotFoundException
*/
public function testRouteNotMatched()
{
$this->expectException(NotFoundException::class);
$this->router->addRoute('NotMatched');
$this->router->route('NotFound');
}
@ -505,7 +619,7 @@ class RouterTest extends MVCRTestAbstract
public function testRouteCustomCallable()
{
// Create custom callable
$callable = function(array $matches, string $route){
$callable = function(array $matches, array $data, string $route){
$this->assertEquals('customCallable', $route);
$this->assertEquals([0=>'customCallable'], $matches);
};
@ -525,12 +639,11 @@ class RouterTest extends MVCRTestAbstract
* @depends testRouteStaticRewrite
* @covers ::route
* @covers ::loadCallable
* @expectedException \FuzeWorks\Exception\NotFoundException
*/
public function testRouteUnsatisfiedCallable()
{
// Create custom callable
$callable = function(array $matches, string $route){
$callable = function(array $matches, array $data, string $route){
$this->assertEquals('unsatisfiedCallable', $route);
$this->assertEquals([0=>'unsatisfiedCallable'], $matches);
return false;
@ -544,6 +657,7 @@ class RouterTest extends MVCRTestAbstract
]
);
$this->expectException(NotFoundException::class);
$this->assertNull($this->router->route('unsatisfiedCallable'));
}

View File

@ -37,31 +37,35 @@
use FuzeWorks\Controller;
use FuzeWorks\Event\ViewGetEvent;
use FuzeWorks\Events;
use FuzeWorks\Exception\NotFoundException;
use FuzeWorks\Exception\ViewException;
use FuzeWorks\Factory;
use FuzeWorks\Priority;
use FuzeWorks\View;
use FuzeWorks\Views;
use PHPUnit\Framework\TestCase;
/**
* Class ViewsTest
* @coversDefaultClass \FuzeWorks\Views
*/
class ViewsTest extends MVCRTestAbstract
class ViewsTest extends TestCase
{
/**
* @var Views
*/
protected $views;
protected Views $views;
/**
* @var Controller
*/
protected $mockController;
public function setUp()
public function setUp(): void
{
$this->views = new Views();
$this->views->addComponentPath('test'.DS.'views');
$this->views->addComponentPath(dirname(__DIR__) . DS . 'views');
$this->mockController = $this->getMockBuilder(Controller::class)->getMock();
}
@ -80,11 +84,22 @@ class ViewsTest extends MVCRTestAbstract
$this->assertInstanceOf($mockViewClass, $this->views->get($mockViewClass, $this->mockController, 'Standard', [], '\\'));
}
/**
* Remove all listeners before the next test starts.
*/
public function tearDown(): void
{
// Clear all events created by tests
Events::$listeners = array();
// Reset all config files
Factory::getInstance()->config->discardConfigFiles();
}
/**
* @depends testGetViewFromClass
* @covers ::get
* @covers ::loadView
* @expectedException \FuzeWorks\Exception\ViewException
*/
public function testGetViewFromClassInvalidInstance()
{
@ -94,6 +109,7 @@ class ViewsTest extends MVCRTestAbstract
class_alias($mockFakeViewClass, $mockFakeViewClass . 'StandardView');
// Try and fetch
$this->expectException(ViewException::class);
$this->views->get($mockFakeViewClass, $this->mockController, 'Standard', [], '\\');
}
@ -127,10 +143,10 @@ class ViewsTest extends MVCRTestAbstract
/**
* @covers ::get
* @expectedException \FuzeWorks\Exception\ViewException
*/
public function testGetViewInvalidName()
{
$this->expectException(ViewException::class);
$this->views->get('', $this->mockController, 'Standard', [], '\\');
}
@ -148,10 +164,10 @@ class ViewsTest extends MVCRTestAbstract
* @depends testGetViewFromFile
* @covers ::get
* @covers ::loadView
* @expectedException \FuzeWorks\Exception\ViewException
*/
public function testGetViewFromFileInvalidInstance()
{
$this->expectException(ViewException::class);
$this->views->get('ViewInvalidInstance', $this->mockController);
}
@ -163,8 +179,8 @@ class ViewsTest extends MVCRTestAbstract
public function testDifferentComponentPathPriority()
{
// Add the directories for this test
$this->views->addComponentPath('test'.DS.'views'.DS.'TestDifferentComponentPathPriority'.DS.'Lowest', Priority::LOWEST);
$this->views->addComponentPath('test'.DS.'views'.DS.'TestDifferentComponentPathPriority'.DS.'Highest', Priority::HIGHEST);
$this->views->addComponentPath(dirname(__DIR__) . DS . 'views'.DS.'TestDifferentComponentPathPriority'.DS.'Lowest', Priority::LOWEST);
$this->views->addComponentPath(dirname(__DIR__) . DS . 'views'.DS.'TestDifferentComponentPathPriority'.DS.'Highest', Priority::HIGHEST);
// Load the view and assert it is the correct type
$view = $this->views->get('TestDifferentComponentPathPriority', $this->mockController);
@ -189,10 +205,10 @@ class ViewsTest extends MVCRTestAbstract
* @depends testGetViewFromFile
* @covers ::get
* @covers ::loadView
* @expectedException \FuzeWorks\Exception\NotFoundException
*/
public function testViewNotFound()
{
$this->expectException(NotFoundException::class);
$this->views->get('NotFound', $this->mockController);
}
@ -200,7 +216,6 @@ class ViewsTest extends MVCRTestAbstract
* @depends testGetViewFromClass
* @covers ::get
* @covers \FuzeWorks\Event\ViewGetEvent::init
* @expectedException \FuzeWorks\Exception\ViewException
*/
public function testViewGetEvent()
{
@ -217,13 +232,13 @@ class ViewsTest extends MVCRTestAbstract
$event->setCancelled(true);
}, 'viewGetEvent', Priority::NORMAL);
$this->expectException(ViewException::class);
$this->views->get('SomeViewName', $this->mockController, 'Other', ['some_path'], 'SomeNamespace', 'Some Argument');
}
/**
* @depends testViewGetEvent
* @covers ::get
* @expectedException \FuzeWorks\Exception\ViewException
*/
public function testCancelGetView()
{
@ -232,6 +247,7 @@ class ViewsTest extends MVCRTestAbstract
$event->setCancelled(true);
}, 'viewGetEvent', Priority::NORMAL);
$this->expectException(ViewException::class);
$this->views->get('SomeView', $this->mockController, 'Standard', [], '\\');
}

View File

@ -40,5 +40,5 @@ use FuzeWorks\Model;
class TestDifferentComponentPathPriorityModel extends Model
{
public $type = 'highest';
public string $type = 'highest';
}

View File

@ -40,5 +40,5 @@ use FuzeWorks\Model;
class TestDifferentComponentPathPriorityModel extends Model
{
public $type = 'lowest';
public string $type = 'lowest';
}

View File

@ -1,34 +1,17 @@
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.5/phpunit.xsd"
bootstrap="autoload.php"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
stopOnError="false"
stopOnFailure="false"
stopOnIncomplete="false"
stopOnSkipped="false"
colors="false">
<?xml version="1.0"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd" bootstrap="bootstrap.php" convertErrorsToExceptions="true" convertNoticesToExceptions="true" convertWarningsToExceptions="true" stopOnError="false" stopOnFailure="false" stopOnIncomplete="false" stopOnSkipped="false" colors="false">
<coverage processUncoveredFiles="false">
<include>
<directory suffix=".php">../</directory>
</include>
<exclude>
<directory suffix=".php">../vendor/</directory>
<directory suffix=".php">../test/</directory>
</exclude>
</coverage>
<testsuites>
<testsuite name="Core Functionality">
<testsuite name="MVCR Suite">
<directory>./</directory>
</testsuite>
</testsuites>
<logging>
<log type="json" target="../build/phpunit/logfile.json"/>
<log type="junit" target="../build/phpunit/logfile.xml"/>
<log type="testdox-html" target="../build/phpunit/testdox.html"/>
<log type="testdox-text" target="../build/phpunit/testdox.txt"/>
</logging>
<filter>
<whitelist processUncoveredFilesFromWhitelist="false">
<directory suffix=".php">../</directory>
<exclude>
<directory suffix=".php">../vendor/</directory>
<directory suffix=".php">../test/</directory>
</exclude>
</whitelist>
</filter>
</phpunit>

View File

@ -31,7 +31,7 @@
* @link http://techfuze.net/fuzeworks
* @since Version 1.2.0
*
* @version Version 1.2.0
* @version Version 1.3.3
*/
namespace Application\View;
@ -40,5 +40,5 @@ use FuzeWorks\View;
class TestDifferentComponentPathPriorityStandardView extends View
{
public $type = 'highest';
public string $type = 'highest';
}

View File

@ -31,7 +31,7 @@
* @link http://techfuze.net/fuzeworks
* @since Version 1.2.0
*
* @version Version 1.2.0
* @version Version 1.3.3
*/
namespace Application\View;
@ -40,5 +40,5 @@ use FuzeWorks\View;
class TestDifferentComponentPathPriorityStandardView extends View
{
public $type = 'lowest';
public string $type = 'lowest';
}

View File

@ -31,7 +31,7 @@
* @link http://techfuze.net/fuzeworks
* @since Version 1.2.0
*
* @version Version 1.2.0
* @version Version 1.3.3
*/
namespace Application\View;
@ -41,7 +41,7 @@ use FuzeWorks\View;
class TestDefaultCallableTestView extends View
{
public function someMethod()
public function someMethod(): string
{
return "Verify Output";
}

View File

@ -31,7 +31,7 @@
* @link http://techfuze.net/fuzeworks
* @since Version 1.2.0
*
* @version Version 1.2.0
* @version Version 1.3.3
*/
namespace Application\View;
@ -40,13 +40,13 @@ use FuzeWorks\View;
class TestDefaultCallableChangeMethodTestView extends View
{
public function index()
public function index(): string
{
return "Not altered!";
}
public function altered()
public function altered(string $param1, string $param2): array
{
return "Altered!";
return ["Altered", $param1, $param2];
}
}

View File

@ -0,0 +1,49 @@
<?php
/**
* FuzeWorks Framework MVCR Component.
*
* The FuzeWorks PHP FrameWork
*
* Copyright (C) 2013-2018 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 - 2018, 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.3.3
*/
namespace Custom\View;
use FuzeWorks\View;
class TestDefaultCallableCustomNamespaceTestView extends View
{
public function someMethod(): string
{
return "Verify Output, with custom namespace!";
}
}

View File

@ -31,7 +31,7 @@
* @link http://techfuze.net/fuzeworks
* @since Version 1.2.0
*
* @version Version 1.2.0
* @version Version 1.3.3
*/
namespace Application\View;
@ -40,6 +40,6 @@ use FuzeWorks\View;
class TestDefaultCallableHaltTestView extends View
{
public $halt = true;
public bool $halt = true;
}