Move some classes to namespace Core\Http
This commit is contained in:
163
app/Core/Http/Client.php
Normal file
163
app/Core/Http/Client.php
Normal file
@@ -0,0 +1,163 @@
|
||||
<?php
|
||||
|
||||
namespace Kanboard\Core\Http;
|
||||
|
||||
use Kanboard\Core\Base;
|
||||
|
||||
/**
|
||||
* HTTP client
|
||||
*
|
||||
* @package http
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Client extends Base
|
||||
{
|
||||
/**
|
||||
* HTTP connection timeout in seconds
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
const HTTP_TIMEOUT = 5;
|
||||
|
||||
/**
|
||||
* Number of maximum redirections for the HTTP client
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
const HTTP_MAX_REDIRECTS = 2;
|
||||
|
||||
/**
|
||||
* HTTP client user agent
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const HTTP_USER_AGENT = 'Kanboard';
|
||||
|
||||
/**
|
||||
* Send a GET HTTP request and parse JSON response
|
||||
*
|
||||
* @access public
|
||||
* @param string $url
|
||||
* @param string[] $headers
|
||||
* @return array
|
||||
*/
|
||||
public function getJson($url, array $headers = array())
|
||||
{
|
||||
$response = $this->doRequest('GET', $url, '', array_merge(array('Accept: application/json'), $headers));
|
||||
return json_decode($response, true) ?: array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a POST HTTP request encoded in JSON
|
||||
*
|
||||
* @access public
|
||||
* @param string $url
|
||||
* @param array $data
|
||||
* @param string[] $headers
|
||||
* @return string
|
||||
*/
|
||||
public function postJson($url, array $data, array $headers = array())
|
||||
{
|
||||
return $this->doRequest(
|
||||
'POST',
|
||||
$url,
|
||||
json_encode($data),
|
||||
array_merge(array('Content-type: application/json'), $headers)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a POST HTTP request encoded in www-form-urlencoded
|
||||
*
|
||||
* @access public
|
||||
* @param string $url
|
||||
* @param array $data
|
||||
* @param string[] $headers
|
||||
* @return string
|
||||
*/
|
||||
public function postForm($url, array $data, array $headers = array())
|
||||
{
|
||||
return $this->doRequest(
|
||||
'POST',
|
||||
$url,
|
||||
http_build_query($data),
|
||||
array_merge(array('Content-type: application/x-www-form-urlencoded'), $headers)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the HTTP request
|
||||
*
|
||||
* @access private
|
||||
* @param string $method
|
||||
* @param string $url
|
||||
* @param string $content
|
||||
* @param string[] $headers
|
||||
* @return string
|
||||
*/
|
||||
private function doRequest($method, $url, $content, array $headers)
|
||||
{
|
||||
if (empty($url)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$stream = @fopen(trim($url), 'r', false, stream_context_create($this->getContext($method, $content, $headers)));
|
||||
$response = '';
|
||||
|
||||
if (is_resource($stream)) {
|
||||
$response = stream_get_contents($stream);
|
||||
} else {
|
||||
$this->logger->error('HttpClient: request failed');
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
$this->logger->debug('HttpClient: url='.$url);
|
||||
$this->logger->debug('HttpClient: payload='.$content);
|
||||
$this->logger->debug('HttpClient: metadata='.var_export(@stream_get_meta_data($stream), true));
|
||||
$this->logger->debug('HttpClient: response='.$response);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get stream context
|
||||
*
|
||||
* @access private
|
||||
* @param string $method
|
||||
* @param string $content
|
||||
* @param string[] $headers
|
||||
* @return array
|
||||
*/
|
||||
private function getContext($method, $content, array $headers)
|
||||
{
|
||||
$default_headers = array(
|
||||
'User-Agent: '.self::HTTP_USER_AGENT,
|
||||
'Connection: close',
|
||||
);
|
||||
|
||||
if (HTTP_PROXY_USERNAME) {
|
||||
$default_headers[] = 'Proxy-Authorization: Basic '.base64_encode(HTTP_PROXY_USERNAME.':'.HTTP_PROXY_PASSWORD);
|
||||
}
|
||||
|
||||
$headers = array_merge($default_headers, $headers);
|
||||
|
||||
$context = array(
|
||||
'http' => array(
|
||||
'method' => $method,
|
||||
'protocol_version' => 1.1,
|
||||
'timeout' => self::HTTP_TIMEOUT,
|
||||
'max_redirects' => self::HTTP_MAX_REDIRECTS,
|
||||
'header' => implode("\r\n", $headers),
|
||||
'content' => $content
|
||||
)
|
||||
);
|
||||
|
||||
if (HTTP_PROXY_HOSTNAME) {
|
||||
$context['http']['proxy'] = 'tcp://'.HTTP_PROXY_HOSTNAME.':'.HTTP_PROXY_PORT;
|
||||
$context['http']['request_fulluri'] = true;
|
||||
}
|
||||
|
||||
return $context;
|
||||
}
|
||||
}
|
||||
243
app/Core/Http/Request.php
Normal file
243
app/Core/Http/Request.php
Normal file
@@ -0,0 +1,243 @@
|
||||
<?php
|
||||
|
||||
namespace Kanboard\Core\Http;
|
||||
|
||||
use Kanboard\Core\Base;
|
||||
|
||||
/**
|
||||
* Request class
|
||||
*
|
||||
* @package http
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Request extends Base
|
||||
{
|
||||
/**
|
||||
* Get URL string parameter
|
||||
*
|
||||
* @access public
|
||||
* @param string $name Parameter name
|
||||
* @param string $default_value Default value
|
||||
* @return string
|
||||
*/
|
||||
public function getStringParam($name, $default_value = '')
|
||||
{
|
||||
return isset($_GET[$name]) ? $_GET[$name] : $default_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get URL integer parameter
|
||||
*
|
||||
* @access public
|
||||
* @param string $name Parameter name
|
||||
* @param integer $default_value Default value
|
||||
* @return integer
|
||||
*/
|
||||
public function getIntegerParam($name, $default_value = 0)
|
||||
{
|
||||
return isset($_GET[$name]) && ctype_digit($_GET[$name]) ? (int) $_GET[$name] : $default_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a form value
|
||||
*
|
||||
* @access public
|
||||
* @param string $name Form field name
|
||||
* @return string|null
|
||||
*/
|
||||
public function getValue($name)
|
||||
{
|
||||
$values = $this->getValues();
|
||||
return isset($values[$name]) ? $values[$name] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get form values and check for CSRF token
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getValues()
|
||||
{
|
||||
if (! empty($_POST) && ! empty($_POST['csrf_token']) && $this->token->validateCSRFToken($_POST['csrf_token'])) {
|
||||
unset($_POST['csrf_token']);
|
||||
return $_POST;
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the raw body of the HTTP request
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getBody()
|
||||
{
|
||||
return file_get_contents('php://input');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Json request body
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getJson()
|
||||
{
|
||||
return json_decode($this->getBody(), true) ?: array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the content of an uploaded file
|
||||
*
|
||||
* @access public
|
||||
* @param string $name Form file name
|
||||
* @return string
|
||||
*/
|
||||
public function getFileContent($name)
|
||||
{
|
||||
if (isset($_FILES[$name])) {
|
||||
return file_get_contents($_FILES[$name]['tmp_name']);
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path of an uploaded file
|
||||
*
|
||||
* @access public
|
||||
* @param string $name Form file name
|
||||
* @return string
|
||||
*/
|
||||
public function getFilePath($name)
|
||||
{
|
||||
return isset($_FILES[$name]['tmp_name']) ? $_FILES[$name]['tmp_name'] : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the HTTP request is sent with the POST method
|
||||
*
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function isPost()
|
||||
{
|
||||
return isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'POST';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the HTTP request is an Ajax request
|
||||
*
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function isAjax()
|
||||
{
|
||||
return $this->getHeader('X-Requested-With') === 'XMLHttpRequest';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the page is requested through HTTPS
|
||||
*
|
||||
* Note: IIS return the value 'off' and other web servers an empty value when it's not HTTPS
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @return boolean
|
||||
*/
|
||||
public static function isHTTPS()
|
||||
{
|
||||
return isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== '' && $_SERVER['HTTPS'] !== 'off';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a HTTP header value
|
||||
*
|
||||
* @access public
|
||||
* @param string $name Header name
|
||||
* @return string
|
||||
*/
|
||||
public function getHeader($name)
|
||||
{
|
||||
$name = 'HTTP_'.str_replace('-', '_', strtoupper($name));
|
||||
return isset($_SERVER[$name]) ? $_SERVER[$name] : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns current request's query string, useful for redirecting
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getQueryString()
|
||||
{
|
||||
return isset($_SERVER['QUERY_STRING']) ? $_SERVER['QUERY_STRING'] : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns uri
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getUri()
|
||||
{
|
||||
return isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user agent
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public static function getUserAgent()
|
||||
{
|
||||
return empty($_SERVER['HTTP_USER_AGENT']) ? t('Unknown') : $_SERVER['HTTP_USER_AGENT'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the real IP address of the user
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @param bool $only_public Return only public IP address
|
||||
* @return string
|
||||
*/
|
||||
public static function getIpAddress($only_public = false)
|
||||
{
|
||||
$keys = array(
|
||||
'HTTP_CLIENT_IP',
|
||||
'HTTP_X_FORWARDED_FOR',
|
||||
'HTTP_X_FORWARDED',
|
||||
'HTTP_X_CLUSTER_CLIENT_IP',
|
||||
'HTTP_FORWARDED_FOR',
|
||||
'HTTP_FORWARDED',
|
||||
'REMOTE_ADDR'
|
||||
);
|
||||
|
||||
foreach ($keys as $key) {
|
||||
if (isset($_SERVER[$key])) {
|
||||
foreach (explode(',', $_SERVER[$key]) as $ip_address) {
|
||||
$ip_address = trim($ip_address);
|
||||
|
||||
if ($only_public) {
|
||||
|
||||
// Return only public IP address
|
||||
if (filter_var($ip_address, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false) {
|
||||
return $ip_address;
|
||||
}
|
||||
} else {
|
||||
return $ip_address;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return t('Unknown');
|
||||
}
|
||||
}
|
||||
273
app/Core/Http/Response.php
Normal file
273
app/Core/Http/Response.php
Normal file
@@ -0,0 +1,273 @@
|
||||
<?php
|
||||
|
||||
namespace Kanboard\Core\Http;
|
||||
|
||||
use Kanboard\Core\Base;
|
||||
|
||||
/**
|
||||
* Response class
|
||||
*
|
||||
* @package http
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Response extends Base
|
||||
{
|
||||
/**
|
||||
* Send no cache headers
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function nocache()
|
||||
{
|
||||
header('Pragma: no-cache');
|
||||
header('Expires: Sat, 26 Jul 1997 05:00:00 GMT');
|
||||
|
||||
// Use no-store due to a Chrome bug: https://code.google.com/p/chromium/issues/detail?id=28035
|
||||
header('Cache-Control: no-store, must-revalidate');
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a custom Content-Type header
|
||||
*
|
||||
* @access public
|
||||
* @param string $mimetype Mime-type
|
||||
*/
|
||||
public function contentType($mimetype)
|
||||
{
|
||||
header('Content-Type: '.$mimetype);
|
||||
}
|
||||
|
||||
/**
|
||||
* Force the browser to download an attachment
|
||||
*
|
||||
* @access public
|
||||
* @param string $filename File name
|
||||
*/
|
||||
public function forceDownload($filename)
|
||||
{
|
||||
header('Content-Disposition: attachment; filename="'.$filename.'"');
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a custom HTTP status code
|
||||
*
|
||||
* @access public
|
||||
* @param integer $status_code HTTP status code
|
||||
*/
|
||||
public function status($status_code)
|
||||
{
|
||||
header('Status: '.$status_code);
|
||||
header($_SERVER['SERVER_PROTOCOL'].' '.$status_code);
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirect to another URL
|
||||
*
|
||||
* @access public
|
||||
* @param string $url Redirection URL
|
||||
*/
|
||||
public function redirect($url)
|
||||
{
|
||||
if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest') {
|
||||
header('X-Ajax-Redirect: '.$url);
|
||||
} else {
|
||||
header('Location: '.$url);
|
||||
}
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a CSV response
|
||||
*
|
||||
* @access public
|
||||
* @param array $data Data to serialize in csv
|
||||
* @param integer $status_code HTTP status code
|
||||
*/
|
||||
public function csv(array $data, $status_code = 200)
|
||||
{
|
||||
$this->status($status_code);
|
||||
$this->nocache();
|
||||
|
||||
header('Content-Type: text/csv');
|
||||
Csv::output($data);
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a Json response
|
||||
*
|
||||
* @access public
|
||||
* @param array $data Data to serialize in json
|
||||
* @param integer $status_code HTTP status code
|
||||
*/
|
||||
public function json(array $data, $status_code = 200)
|
||||
{
|
||||
$this->status($status_code);
|
||||
$this->nocache();
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode($data);
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a text response
|
||||
*
|
||||
* @access public
|
||||
* @param string $data Raw data
|
||||
* @param integer $status_code HTTP status code
|
||||
*/
|
||||
public function text($data, $status_code = 200)
|
||||
{
|
||||
$this->status($status_code);
|
||||
$this->nocache();
|
||||
header('Content-Type: text/plain; charset=utf-8');
|
||||
echo $data;
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a HTML response
|
||||
*
|
||||
* @access public
|
||||
* @param string $data Raw data
|
||||
* @param integer $status_code HTTP status code
|
||||
*/
|
||||
public function html($data, $status_code = 200)
|
||||
{
|
||||
$this->status($status_code);
|
||||
$this->nocache();
|
||||
header('Content-Type: text/html; charset=utf-8');
|
||||
echo $data;
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a XML response
|
||||
*
|
||||
* @access public
|
||||
* @param string $data Raw data
|
||||
* @param integer $status_code HTTP status code
|
||||
*/
|
||||
public function xml($data, $status_code = 200)
|
||||
{
|
||||
$this->status($status_code);
|
||||
$this->nocache();
|
||||
header('Content-Type: text/xml; charset=utf-8');
|
||||
echo $data;
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a javascript response
|
||||
*
|
||||
* @access public
|
||||
* @param string $data Raw data
|
||||
* @param integer $status_code HTTP status code
|
||||
*/
|
||||
public function js($data, $status_code = 200)
|
||||
{
|
||||
$this->status($status_code);
|
||||
|
||||
header('Content-Type: text/javascript; charset=utf-8');
|
||||
echo $data;
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a css response
|
||||
*
|
||||
* @access public
|
||||
* @param string $data Raw data
|
||||
* @param integer $status_code HTTP status code
|
||||
*/
|
||||
public function css($data, $status_code = 200)
|
||||
{
|
||||
$this->status($status_code);
|
||||
|
||||
header('Content-Type: text/css; charset=utf-8');
|
||||
echo $data;
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a binary response
|
||||
*
|
||||
* @access public
|
||||
* @param string $data Raw data
|
||||
* @param integer $status_code HTTP status code
|
||||
*/
|
||||
public function binary($data, $status_code = 200)
|
||||
{
|
||||
$this->status($status_code);
|
||||
$this->nocache();
|
||||
header('Content-Transfer-Encoding: binary');
|
||||
header('Content-Type: application/octet-stream');
|
||||
echo $data;
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the security header: Content-Security-Policy
|
||||
*
|
||||
* @access public
|
||||
* @param array $policies CSP rules
|
||||
*/
|
||||
public function csp(array $policies = array())
|
||||
{
|
||||
$policies['default-src'] = "'self'";
|
||||
$values = '';
|
||||
|
||||
foreach ($policies as $policy => $acl) {
|
||||
$values .= $policy.' '.trim($acl).'; ';
|
||||
}
|
||||
|
||||
header('Content-Security-Policy: '.$values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the security header: X-Content-Type-Options
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function nosniff()
|
||||
{
|
||||
header('X-Content-Type-Options: nosniff');
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the security header: X-XSS-Protection
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function xss()
|
||||
{
|
||||
header('X-XSS-Protection: 1; mode=block');
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the security header: Strict-Transport-Security (only if we use HTTPS)
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function hsts()
|
||||
{
|
||||
if (Request::isHTTPS()) {
|
||||
header('Strict-Transport-Security: max-age=31536000');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the security header: X-Frame-Options (deny by default)
|
||||
*
|
||||
* @access public
|
||||
* @param string $mode Frame option mode
|
||||
* @param array $urls Allowed urls for the given mode
|
||||
*/
|
||||
public function xframe($mode = 'DENY', array $urls = array())
|
||||
{
|
||||
header('X-Frame-Options: '.$mode.' '.implode(' ', $urls));
|
||||
}
|
||||
}
|
||||
230
app/Core/Http/Router.php
Normal file
230
app/Core/Http/Router.php
Normal file
@@ -0,0 +1,230 @@
|
||||
<?php
|
||||
|
||||
namespace Kanboard\Core\Http;
|
||||
|
||||
use RuntimeException;
|
||||
use Kanboard\Core\Base;
|
||||
|
||||
/**
|
||||
* Router class
|
||||
*
|
||||
* @package http
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Router extends Base
|
||||
{
|
||||
/**
|
||||
* Controller
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $controller = '';
|
||||
|
||||
/**
|
||||
* Action
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $action = '';
|
||||
|
||||
/**
|
||||
* Store routes for path lookup
|
||||
*
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private $paths = array();
|
||||
|
||||
/**
|
||||
* Store routes for url lookup
|
||||
*
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private $urls = array();
|
||||
|
||||
/**
|
||||
* Get action
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getAction()
|
||||
{
|
||||
return $this->action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get controller
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getController()
|
||||
{
|
||||
return $this->controller;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path to compare patterns
|
||||
*
|
||||
* @access public
|
||||
* @param string $uri
|
||||
* @param string $query_string
|
||||
* @return string
|
||||
*/
|
||||
public function getPath($uri, $query_string = '')
|
||||
{
|
||||
$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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add route
|
||||
*
|
||||
* @access public
|
||||
* @param string $path
|
||||
* @param string $controller
|
||||
* @param string $action
|
||||
* @param array $params
|
||||
*/
|
||||
public function addRoute($path, $controller, $action, array $params = array())
|
||||
{
|
||||
$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),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array('app', 'index');
|
||||
}
|
||||
|
||||
/**
|
||||
* Find route url
|
||||
*
|
||||
* @access public
|
||||
* @param string $controller
|
||||
* @param string $action
|
||||
* @param array $params
|
||||
* @return string
|
||||
*/
|
||||
public function findUrl($controller, $action, array $params = array())
|
||||
{
|
||||
if (! isset($this->urls[$controller][$action])) {
|
||||
return '';
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check controller and action parameter
|
||||
*
|
||||
* @access public
|
||||
* @param string $value Controller or action name
|
||||
* @param string $default_value Default value if validation fail
|
||||
* @return string
|
||||
*/
|
||||
public function sanitize($value, $default_value)
|
||||
{
|
||||
return ! preg_match('/^[a-zA-Z_0-9]+$/', $value) ? $default_value : $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find controller/action from the route table or from get arguments
|
||||
*
|
||||
* @access public
|
||||
* @param string $uri
|
||||
* @param string $query_string
|
||||
*/
|
||||
public function dispatch($uri, $query_string = '')
|
||||
{
|
||||
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 = '\Kanboard\\';
|
||||
$class .= empty($plugin) ? 'Controller\\'.ucfirst($this->controller) : 'Plugin\\'.ucfirst($plugin).'\Controller\\'.ucfirst($this->controller);
|
||||
|
||||
if (! class_exists($class) || ! method_exists($class, $this->action)) {
|
||||
throw new RuntimeException('Controller or method not found for the given url!');
|
||||
}
|
||||
|
||||
$instance = new $class($this->container);
|
||||
$instance->beforeAction($this->controller, $this->action);
|
||||
$instance->{$this->action}();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user