From 6f9af3659c9146a2ac1b08d70610bf96398ec073 Mon Sep 17 00:00:00 2001 From: Frederic Guillot Date: Tue, 22 Dec 2015 19:06:03 +0100 Subject: Added the possiblity to define custom routes from plugins --- app/Core/Http/Request.php | 49 ++++++++++-- app/Core/Http/Route.php | 188 +++++++++++++++++++++++++++++++++++++++++++ app/Core/Http/Router.php | 199 ++++++++++++++++------------------------------ 3 files changed, 298 insertions(+), 138 deletions(-) create mode 100644 app/Core/Http/Route.php (limited to 'app/Core/Http') diff --git a/app/Core/Http/Request.php b/app/Core/Http/Request.php index c626f5b2..da95f2d4 100644 --- a/app/Core/Http/Request.php +++ b/app/Core/Http/Request.php @@ -41,6 +41,16 @@ class Request extends Base $this->cookies = empty($cookies) ? $_COOKIE : $cookies; } + /** + * Set GET parameters + * + * @param array $params + */ + public function setParams(array $params) + { + $this->get = array_merge($this->get, $params); + } + /** * Get query string string parameter * @@ -146,6 +156,17 @@ class Request extends Base return isset($this->files[$name]['tmp_name']) ? $this->files[$name]['tmp_name'] : ''; } + /** + * Return HTTP method + * + * @access public + * @return bool + */ + public function getMethod() + { + return $this->getServerVariable('REQUEST_METHOD'); + } + /** * Return true if the HTTP request is sent with the POST method * @@ -154,7 +175,7 @@ class Request extends Base */ public function isPost() { - return isset($this->server['REQUEST_METHOD']) && $this->server['REQUEST_METHOD'] === 'POST'; + return $this->getServerVariable('REQUEST_METHOD') === 'POST'; } /** @@ -203,7 +224,7 @@ class Request extends Base public function getHeader($name) { $name = 'HTTP_'.str_replace('-', '_', strtoupper($name)); - return isset($this->server[$name]) ? $this->server[$name] : ''; + return $this->getServerVariable($name); } /** @@ -214,18 +235,18 @@ class Request extends Base */ public function getRemoteUser() { - return isset($this->server[REVERSE_PROXY_USER_HEADER]) ? $this->server[REVERSE_PROXY_USER_HEADER] : ''; + return $this->getServerVariable(REVERSE_PROXY_USER_HEADER); } /** - * Returns current request's query string, useful for redirecting + * Returns query string * * @access public * @return string */ public function getQueryString() { - return isset($this->server['QUERY_STRING']) ? $this->server['QUERY_STRING'] : ''; + return $this->getServerVariable('QUERY_STRING'); } /** @@ -236,7 +257,7 @@ class Request extends Base */ public function getUri() { - return isset($this->server['REQUEST_URI']) ? $this->server['REQUEST_URI'] : ''; + return $this->getServerVariable('REQUEST_URI'); } /** @@ -269,7 +290,7 @@ class Request extends Base ); foreach ($keys as $key) { - if (! empty($this->server[$key])) { + if ($this->getServerVariable($key) !== '') { foreach (explode(',', $this->server[$key]) as $ipAddress) { return trim($ipAddress); } @@ -287,6 +308,18 @@ class Request extends Base */ public function getStartTime() { - return isset($this->server['REQUEST_TIME_FLOAT']) ? $this->server['REQUEST_TIME_FLOAT'] : 0; + return $this->getServerVariable('REQUEST_TIME_FLOAT') ?: 0; + } + + /** + * Get server variable + * + * @access public + * @param string $variable + * @return string + */ + public function getServerVariable($variable) + { + return isset($this->server[$variable]) ? $this->server[$variable] : ''; } } diff --git a/app/Core/Http/Route.php b/app/Core/Http/Route.php new file mode 100644 index 00000000..ed831467 --- /dev/null +++ b/app/Core/Http/Route.php @@ -0,0 +1,188 @@ +activated = true; + return $this; + } + + /** + * Add route + * + * @access public + * @param string $path + * @param string $controller + * @param string $action + * @param string $plugin + * @return Route + */ + public function addRoute($path, $controller, $action, $plugin = '') + { + if ($this->activated) { + $path = ltrim($path, '/'); + $items = explode('/', $path); + $params = $this->findParams($items); + + $this->paths[] = array( + 'items' => $items, + 'count' => count($items), + 'controller' => $controller, + 'action' => $action, + 'plugin' => $plugin, + ); + + $this->urls[$plugin][$controller][$action][] = array( + 'path' => $path, + 'params' => $params, + 'count' => count($params), + ); + } + + return $this; + } + + /** + * Find a route according to the given path + * + * @access public + * @param string $path + * @return array + */ + public function findRoute($path) + { + $items = explode('/', ltrim($path, '/')); + $count = count($items); + + foreach ($this->paths as $route) { + if ($count === $route['count']) { + $params = array(); + + for ($i = 0; $i < $count; $i++) { + if ($route['items'][$i]{0} === ':') { + $params[substr($route['items'][$i], 1)] = $items[$i]; + } elseif ($route['items'][$i] !== $items[$i]) { + break; + } + } + + if ($i === $count) { + $this->request->setParams($params); + return array( + 'controller' => $route['controller'], + 'action' => $route['action'], + 'plugin' => $route['plugin'], + ); + } + } + } + + return array( + 'controller' => 'app', + 'action' => 'index', + 'plugin' => '', + ); + } + + /** + * Find route url + * + * @access public + * @param string $controller + * @param string $action + * @param array $params + * @param string $plugin + * @return string + */ + public function findUrl($controller, $action, array $params = array(), $plugin = '') + { + if ($plugin === '' && isset($params['plugin'])) { + $plugin = $params['plugin']; + unset($params['plugin']); + } + + if (! isset($this->urls[$plugin][$controller][$action])) { + return ''; + } + + foreach ($this->urls[$plugin][$controller][$action] as $route) { + if (array_diff_key($params, $route['params']) === array()) { + $url = $route['path']; + $i = 0; + + foreach ($params as $variable => $value) { + $url = str_replace(':'.$variable, $value, $url); + $i++; + } + + if ($i === $route['count']) { + return $url; + } + } + } + + return ''; + } + + /** + * Find url params + * + * @access public + * @param array $items + * @return array + */ + public function findParams(array $items) + { + $params = array(); + + foreach ($items as $item) { + if ($item !== '' && $item{0} === ':') { + $params[substr($item, 1)] = true; + } + } + + return $params; + } +} diff --git a/app/Core/Http/Router.php b/app/Core/Http/Router.php index 0080b23a..8b58a947 100644 --- a/app/Core/Http/Router.php +++ b/app/Core/Http/Router.php @@ -6,7 +6,7 @@ use RuntimeException; use Kanboard\Core\Base; /** - * Router class + * Route Dispatcher * * @package http * @author Frederic Guillot @@ -14,46 +14,38 @@ use Kanboard\Core\Base; class Router extends Base { /** - * Controller + * Plugin name * * @access private * @var string */ - private $controller = ''; + private $plugin = ''; /** - * Action + * Controller * * @access private * @var string */ - private $action = ''; - - /** - * Store routes for path lookup - * - * @access private - * @var array - */ - private $paths = array(); + private $controller = ''; /** - * Store routes for url lookup + * Action * * @access private - * @var array + * @var string */ - private $urls = array(); + private $action = ''; /** - * Get action + * Get plugin name * * @access public * @return string */ - public function getAction() + public function getPlugin() { - return $this->action; + return $this->plugin; } /** @@ -68,163 +60,110 @@ class Router extends Base } /** - * Get the path to compare patterns + * Get action * * @access public - * @param string $uri - * @param string $query_string * @return string */ - public function getPath($uri, $query_string = '') + public function getAction() { - $path = substr($uri, strlen($this->helper->url->dir())); - - if (! empty($query_string)) { - $path = substr($path, 0, - strlen($query_string) - 1); - } - - if (! empty($path) && $path{0} === '/') { - $path = substr($path, 1); - } - - return $path; + return $this->action; } /** - * Add route + * Get the path to compare patterns * * @access public - * @param string $path - * @param string $controller - * @param string $action - * @param array $params + * @return string */ - public function addRoute($path, $controller, $action, array $params = array()) + public function getPath() { - $pattern = explode('/', $path); - - $this->paths[] = array( - 'pattern' => $pattern, - 'count' => count($pattern), - 'controller' => $controller, - 'action' => $action, - ); - - $this->urls[$controller][$action][] = array( - 'path' => $path, - 'params' => array_flip($params), - 'count' => count($params), - ); - } + $path = substr($this->request->getUri(), strlen($this->helper->url->dir())); - /** - * Find a route according to the given path - * - * @access public - * @param string $path - * @return array - */ - public function findRoute($path) - { - $parts = explode('/', $path); - $count = count($parts); - - foreach ($this->paths as $route) { - if ($count === $route['count']) { - $params = array(); - - for ($i = 0; $i < $count; $i++) { - if ($route['pattern'][$i]{0} === ':') { - $params[substr($route['pattern'][$i], 1)] = $parts[$i]; - } elseif ($route['pattern'][$i] !== $parts[$i]) { - break; - } - } - - if ($i === $count) { - $_GET = array_merge($_GET, $params); - return array($route['controller'], $route['action']); - } - } + if ($this->request->getQueryString() !== '') { + $path = substr($path, 0, - strlen($this->request->getQueryString()) - 1); } - return array('app', 'index'); + if ($path !== '' && $path{0} === '/') { + $path = substr($path, 1); + } + + return $path; } /** - * Find route url + * Find controller/action from the route table or from get arguments * * @access public - * @param string $controller - * @param string $action - * @param array $params - * @return string */ - public function findUrl($controller, $action, array $params = array()) + public function dispatch() { - if (! isset($this->urls[$controller][$action])) { - return ''; + $controller = $this->request->getStringParam('controller'); + $action = $this->request->getStringParam('action'); + $plugin = $this->request->getStringParam('plugin'); + + if ($controller === '') { + $route = $this->route->findRoute($this->getPath()); + $controller = $route['controller']; + $action = $route['action']; + $plugin = $route['plugin']; } - foreach ($this->urls[$controller][$action] as $pattern) { - if (array_diff_key($params, $pattern['params']) === array()) { - $url = $pattern['path']; - $i = 0; - - foreach ($params as $variable => $value) { - $url = str_replace(':'.$variable, $value, $url); - $i++; - } - - if ($i === $pattern['count']) { - return $url; - } - } - } + $this->controller = ucfirst($this->sanitize($controller, 'app')); + $this->action = $this->sanitize($action, 'index'); + $this->plugin = ucfirst($this->sanitize($plugin)); - return ''; + return $this->executeAction(); } /** * Check controller and action parameter * * @access public - * @param string $value Controller or action name - * @param string $default_value Default value if validation fail + * @param string $value + * @param string $default * @return string */ - public function sanitize($value, $default_value) + public function sanitize($value, $default = '') { - return ! preg_match('/^[a-zA-Z_0-9]+$/', $value) ? $default_value : $value; + return preg_match('/^[a-zA-Z_0-9]+$/', $value) ? $value : $default; } /** - * Find controller/action from the route table or from get arguments + * Execute controller action * - * @access public - * @param string $uri - * @param string $query_string + * @access private */ - public function dispatch($uri, $query_string = '') + private function executeAction() { - if (! empty($_GET['controller']) && ! empty($_GET['action'])) { - $this->controller = $this->sanitize($_GET['controller'], 'app'); - $this->action = $this->sanitize($_GET['action'], 'index'); - $plugin = ! empty($_GET['plugin']) ? $this->sanitize($_GET['plugin'], '') : ''; - } else { - list($this->controller, $this->action) = $this->findRoute($this->getPath($uri, $query_string)); // TODO: add plugin for routes - $plugin = ''; - } + $class = $this->getControllerClassName(); - $class = '\Kanboard\\'; - $class .= empty($plugin) ? 'Controller\\'.ucfirst($this->controller) : 'Plugin\\'.ucfirst($plugin).'\Controller\\'.ucfirst($this->controller); + if (! class_exists($class)) { + throw new RuntimeException('Controller not found'); + } - if (! class_exists($class) || ! method_exists($class, $this->action)) { - throw new RuntimeException('Controller or method not found for the given url!'); + if (! method_exists($class, $this->action)) { + throw new RuntimeException('Action not implemented'); } $instance = new $class($this->container); $instance->beforeAction($this->controller, $this->action); $instance->{$this->action}(); + return $instance; + } + + /** + * Get controller class name + * + * @access private + * @return string + */ + private function getControllerClassName() + { + if ($this->plugin !== '') { + return '\Kanboard\Plugin\\'.$this->plugin.'\Controller\\'.$this->controller; + } + + return '\Kanboard\Controller\\'.$this->controller; } } -- cgit v1.2.3