Move Url stuff in EesyPHP namespace
This commit is contained in:
parent
a303f188f3
commit
27af166466
9 changed files with 710 additions and 642 deletions
|
@ -4,6 +4,7 @@ use EesyPHP\Log;
|
|||
use EesyPHP\SentryIntegration;
|
||||
use EesyPHP\SentrySpan;
|
||||
use EesyPHP\SentryTransaction;
|
||||
use EesyPHP\Url;
|
||||
|
||||
error_reporting(E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED);
|
||||
|
||||
|
@ -27,9 +28,6 @@ set_include_path($root_dir_path.'/includes' . PATH_SEPARATOR . get_include_path(
|
|||
// Load composer autoload.php
|
||||
require("$root_dir_path/vendor/autoload.php");
|
||||
|
||||
// API mode
|
||||
$api_mode = false;
|
||||
|
||||
// Load configuration
|
||||
require_once('translation.php');
|
||||
require_once('config.inc.php');
|
||||
|
@ -74,7 +72,7 @@ require_once('hooks.php');
|
|||
require_once('cli.php');
|
||||
require_once('translation-cli.php');
|
||||
require_once('smarty.php');
|
||||
require_once('url.php');
|
||||
Url::init(isset($public_root_url)?$public_root_url:null);
|
||||
require_once('url-helpers.php');
|
||||
require_once('db.php');
|
||||
require_once('mail.php');
|
||||
|
|
|
@ -299,4 +299,25 @@ function run_external_command($command, $data_stdin=null, $escape_command_args=t
|
|||
return array($return_value, $stdout, $stderr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check an AJAX request and trigger a fatal error on fail
|
||||
*
|
||||
* Check if session key is present and valid and set AJAX
|
||||
* mode.
|
||||
*
|
||||
* @param string|null $session_key string The current session key (optional)
|
||||
*
|
||||
* @return void
|
||||
**/
|
||||
function check_ajax_request($session_key=null) {
|
||||
global $ajax, $debug_ajax;
|
||||
$ajax = true;
|
||||
|
||||
if (check_session_key($session_key))
|
||||
fatal_error('Invalid request');
|
||||
|
||||
if ($debug_ajax)
|
||||
Log :: debug("Ajax Request : ".vardump($_REQUEST));
|
||||
}
|
||||
|
||||
# vim: tabstop=2 shiftwidth=2 softtabstop=2 expandtab
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
use EesyPHP\Log;
|
||||
use EesyPHP\SentrySpan;
|
||||
use EesyPHP\SentryTransaction;
|
||||
use EesyPHP\Url;
|
||||
|
||||
if (php_sapi_name() == "cli")
|
||||
return true;
|
||||
|
@ -188,10 +189,10 @@ function display_template($template, $pagetitle=false) {
|
|||
// If refresh parameter is present, remove it and redirect
|
||||
if (isset($_GET['refresh'])) {
|
||||
unset($_GET['refresh']);
|
||||
$url = get_current_url();
|
||||
$url = Url :: get_current_url();
|
||||
if (!empty($_GET))
|
||||
$url .= '?'.http_build_query($_GET);
|
||||
redirect($url);
|
||||
Url :: redirect($url);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -246,7 +247,7 @@ function display_ajax_return($data=null, $pretty=false) {
|
|||
|
||||
$ajax=false;
|
||||
function fatal_error($error) {
|
||||
global $smarty, $ajax, $api_mode;
|
||||
global $smarty, $ajax;
|
||||
|
||||
// If more than one arguments passed, format error message using sprintf
|
||||
if (func_num_args() > 1) {
|
||||
|
@ -262,7 +263,7 @@ function fatal_error($error) {
|
|||
// Set HTTP reponse code to 500
|
||||
http_response_code(500);
|
||||
|
||||
if ($ajax || $api_mode)
|
||||
if ($ajax || Url :: api_mode())
|
||||
display_ajax_return(array('success' => false, 'error' => $error));
|
||||
|
||||
$smarty->assign('fatal_error', $error);
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
use EesyPHP\Check;
|
||||
use EesyPHP\Log;
|
||||
use EesyPHP\Url;
|
||||
|
||||
use function EesyPHP\vardump;
|
||||
|
||||
|
@ -11,7 +12,7 @@ if (php_sapi_name() == "cli")
|
|||
function handle_homepage($request) {
|
||||
display_template("homepage.tpl", _("Hello world !"));
|
||||
}
|
||||
add_url_handler('#^$#', 'handle_homepage');
|
||||
Url :: add_url_handler('#^$#', 'handle_homepage');
|
||||
|
||||
function handle_search($request) {
|
||||
global $smarty, $status_list;
|
||||
|
@ -29,7 +30,7 @@ function handle_search($request) {
|
|||
'order_direction' => 'ASC',
|
||||
);
|
||||
if (isset($_REQUEST['clear']) && $_REQUEST['clear']=='true')
|
||||
redirect($request -> current_url);
|
||||
Url :: redirect($request -> current_url);
|
||||
}
|
||||
Log :: debug('Request params : '.vardump($_REQUEST));
|
||||
|
||||
|
@ -108,7 +109,7 @@ function handle_search($request) {
|
|||
|
||||
display_template("search.tpl", _("Search"));
|
||||
}
|
||||
add_url_handler('|^item/?$|', 'handle_search');
|
||||
Url :: add_url_handler('|^item/?$|', 'handle_search');
|
||||
|
||||
/*
|
||||
* One item pages
|
||||
|
@ -119,7 +120,7 @@ function handle_show($request) {
|
|||
|
||||
$item = get_item_from_url($request -> id);
|
||||
if (!$item)
|
||||
error_404();
|
||||
Url :: error_404();
|
||||
|
||||
$smarty->assign('item', $item);
|
||||
|
||||
|
@ -134,7 +135,7 @@ function handle_show($request) {
|
|||
(is_array($item)?$item['name']:"#".$request -> id)
|
||||
);
|
||||
}
|
||||
add_url_handler('|^item/(?P<id>[0-9]+)$|', 'handle_show');
|
||||
Url :: add_url_handler('|^item/(?P<id>[0-9]+)$|', 'handle_show');
|
||||
|
||||
function handle_create($request) {
|
||||
global $smarty, $status_list;
|
||||
|
@ -145,7 +146,7 @@ function handle_create($request) {
|
|||
$item = add_item($info);
|
||||
if (is_array($item)) {
|
||||
add_message(_("The element '% s' has been created."), $item['name']);
|
||||
redirect('item/'.$item['id']);
|
||||
Url :: redirect('item/'.$item['id']);
|
||||
}
|
||||
else {
|
||||
add_error(_("An error occurred while saving this item."));
|
||||
|
@ -164,7 +165,7 @@ function handle_create($request) {
|
|||
|
||||
display_template("form.tpl", _("New"));
|
||||
}
|
||||
add_url_handler('|^item/new$|', 'handle_create');
|
||||
Url :: add_url_handler('|^item/new$|', 'handle_create');
|
||||
|
||||
function handle_modify($request) {
|
||||
global $smarty, $status_list;
|
||||
|
@ -173,7 +174,7 @@ function handle_modify($request) {
|
|||
if(is_array($item)) {
|
||||
if (!can_modify($item)) {
|
||||
add_error(_('You cannot edit this item.'));
|
||||
redirect('item/'.$item['id']);
|
||||
Url :: redirect('item/'.$item['id']);
|
||||
}
|
||||
$info = array();
|
||||
$field_errors = handle_item_post_data($info);
|
||||
|
@ -186,11 +187,11 @@ function handle_modify($request) {
|
|||
Log :: debug('Changes : '.vardump($changes));
|
||||
if (empty($changes)) {
|
||||
add_message(_("You have not made any changes to element '% s'."), $item['name']);
|
||||
redirect('item/'.$item['id']);
|
||||
Url :: redirect('item/'.$item['id']);
|
||||
}
|
||||
else if (update_item($item['id'], $changes) === true) {
|
||||
add_message(_("The element '% s' has been updated successfully."), $item['name']);
|
||||
redirect('item/'.$item['id']);
|
||||
Url :: redirect('item/'.$item['id']);
|
||||
}
|
||||
else {
|
||||
add_error(_("An error occurred while updating this item."));
|
||||
|
@ -209,7 +210,7 @@ function handle_modify($request) {
|
|||
$smarty -> assign('status_list', $status_list);
|
||||
}
|
||||
else {
|
||||
error_404();
|
||||
Url :: error_404();
|
||||
}
|
||||
|
||||
display_template(
|
||||
|
@ -217,7 +218,7 @@ function handle_modify($request) {
|
|||
(is_array($item)?$item['name']:"#".$request -> id)
|
||||
);
|
||||
}
|
||||
add_url_handler('|^item/(?P<id>[0-9]+)/modify$|', 'handle_modify');
|
||||
Url :: add_url_handler('|^item/(?P<id>[0-9]+)/modify$|', 'handle_modify');
|
||||
|
||||
function handle_archive($request) {
|
||||
global $smarty;
|
||||
|
@ -225,7 +226,7 @@ function handle_archive($request) {
|
|||
$item = get_item_from_url($request -> id);
|
||||
if(!is_array($item)) {
|
||||
add_error(_("Item #% s not found."), $request -> id);
|
||||
redirect('item');
|
||||
Url :: redirect('item');
|
||||
}
|
||||
elseif ($item['status'] == 'archived') {
|
||||
add_message(_("This item is already archived."));
|
||||
|
@ -239,9 +240,9 @@ function handle_archive($request) {
|
|||
else {
|
||||
add_error(_('An error occurred while archiving this item.'));
|
||||
}
|
||||
redirect('item/'.$item['id']);
|
||||
Url :: redirect('item/'.$item['id']);
|
||||
}
|
||||
add_url_handler('|^item/(?P<id>[0-9]+)/archive$|', 'handle_archive');
|
||||
Url :: add_url_handler('|^item/(?P<id>[0-9]+)/archive$|', 'handle_archive');
|
||||
|
||||
function handle_delete($request) {
|
||||
global $smarty;
|
||||
|
@ -258,10 +259,10 @@ function handle_delete($request) {
|
|||
}
|
||||
else {
|
||||
add_error(_('An error occurred while deleting this item.'));
|
||||
redirect('item/'.$item['id']);
|
||||
Url :: redirect('item/'.$item['id']);
|
||||
}
|
||||
redirect('item');
|
||||
Url :: redirect('item');
|
||||
}
|
||||
add_url_handler('|^item/(?P<id>[0-9]+)/delete$|', 'handle_delete');
|
||||
Url :: add_url_handler('|^item/(?P<id>[0-9]+)/delete$|', 'handle_delete');
|
||||
|
||||
# vim: tabstop=2 shiftwidth=2 softtabstop=2 expandtab
|
||||
|
|
614
includes/url.php
614
includes/url.php
|
@ -1,614 +0,0 @@
|
|||
<?php
|
||||
|
||||
use EesyPHP\Log;
|
||||
use EesyPHP\SentrySpan;
|
||||
use EesyPHP\SentryTransaction;
|
||||
|
||||
use function EesyPHP\format_callable;
|
||||
use function EesyPHP\vardump;
|
||||
|
||||
/*
|
||||
* Configured URL patterns :
|
||||
*
|
||||
* Example :
|
||||
*
|
||||
* array (
|
||||
* '|get/(?P<name>[a-zA-Z0-9]+)$|' => array (
|
||||
* 'handler' => 'get',
|
||||
* 'authenticated' => true,
|
||||
* 'api_mode' => false,
|
||||
* 'methods' => array('GET'),
|
||||
* ),
|
||||
* '|get/all$|' => => array (
|
||||
* 'handler' => 'get_all',
|
||||
* 'authenticated' => true,
|
||||
* 'api_mode' => false,
|
||||
* 'methods' => array('GET', 'POST'),
|
||||
* ),
|
||||
* )
|
||||
*
|
||||
*/
|
||||
$url_patterns =array();
|
||||
|
||||
/**
|
||||
* Add an URL pattern
|
||||
*
|
||||
* @param string|array $pattern The URL pattern or an array of patterns (required)
|
||||
* @param callable $handler The URL pattern handler (must be callable, required)
|
||||
* @param boolean $authenticated Permit to define if this URL is accessible only for
|
||||
* authenticated users (optional, default: true if the
|
||||
* special force_authentication function is defined,
|
||||
* false otherwise)
|
||||
* @param boolean $override Allow override if a command already exists with the
|
||||
* same name (optional, default: false)
|
||||
* @param boolean $api_mode Enable API mode (optional, default: false)
|
||||
* @param array|string|null $methods HTTP method (optional, default: array('GET', 'POST'))
|
||||
**/
|
||||
function add_url_handler($pattern, $handler=null, $authenticated=null, $override=true,
|
||||
$api_mode=false, $methods=null) {
|
||||
$authenticated = (
|
||||
is_null($authenticated)?
|
||||
function_exists('force_authentication'):
|
||||
(bool)$authenticated
|
||||
);
|
||||
if (is_null($methods))
|
||||
$methods = array('GET', 'POST');
|
||||
elseif (!is_array($methods))
|
||||
$methods = array($methods);
|
||||
global $url_patterns;
|
||||
if (is_array($pattern)) {
|
||||
if (is_null($handler))
|
||||
foreach($pattern as $p => $h)
|
||||
add_url_handler($p, $h, $authenticated, $override, $api_mode, $methods);
|
||||
else
|
||||
foreach($pattern as $p)
|
||||
add_url_handler($p, $handler, $authenticated, $override, $api_mode, $methods);
|
||||
}
|
||||
else {
|
||||
if (!isset($url_patterns[$pattern])) {
|
||||
$url_patterns[$pattern] = array(
|
||||
'handler' => $handler,
|
||||
'authenticated' => $authenticated,
|
||||
'api_mode' => $api_mode,
|
||||
'methods' => $methods,
|
||||
);
|
||||
}
|
||||
elseif ($override) {
|
||||
Log :: debug(
|
||||
"URL : override pattern '%s' with handler '%s' (old handler = '%s')".
|
||||
$pattern, format_callable($handler), vardump($url_patterns[$pattern])
|
||||
);
|
||||
$url_patterns[$pattern] = array(
|
||||
'handler' => $handler,
|
||||
'authenticated' => $authenticated,
|
||||
'api_mode' => $api_mode,
|
||||
'methods' => $methods,
|
||||
);
|
||||
}
|
||||
else {
|
||||
Log :: debug("URL : pattern '$pattern' already defined : do not override.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show error page
|
||||
*
|
||||
* @param $request UrlRequest|null The request (optional, default: null)
|
||||
* @param $error_code int|null The HTTP error code (optional, default: 400)
|
||||
*
|
||||
* @return void
|
||||
**/
|
||||
function error_page($request=null, $error_code=null) {
|
||||
global $smarty;
|
||||
$http_errors = array(
|
||||
400 => array(
|
||||
'pagetitle' => _("Bad request"),
|
||||
'message' => _("Invalid request."),
|
||||
),
|
||||
401 => array(
|
||||
'pagetitle' => _("Authentication required"),
|
||||
'message' => _("You have to be authenticated to access to this page."),
|
||||
),
|
||||
403 => array(
|
||||
'pagetitle' => _("Access denied"),
|
||||
'message' => _("You do not have access to this application. If you think this is an error, please contact support."),
|
||||
),
|
||||
404 => array(
|
||||
'pagetitle' => _("Whoops ! Page not found"),
|
||||
'message' => _("The requested page can not be found."),
|
||||
),
|
||||
);
|
||||
$error_code = ($error_code?intval($error_code):400);
|
||||
if (array_key_exists($error_code, $http_errors))
|
||||
$error = $http_errors[intval($error_code)];
|
||||
else
|
||||
$error = array(
|
||||
'pagetitle' => _('Error'),
|
||||
'message' => _('An unknown error occurred. If problem persist, please contact support.'),
|
||||
);
|
||||
http_response_code($error_code);
|
||||
|
||||
$smarty -> assign('message', $error['message']);
|
||||
display_template('error_page.tpl', $error['pagetitle']);
|
||||
exit();
|
||||
}
|
||||
|
||||
/*
|
||||
* Error 404 page
|
||||
*/
|
||||
|
||||
/**
|
||||
* Error 404 handler
|
||||
*
|
||||
* @param UrlRequest|null $request The request (optional, default: null)
|
||||
*
|
||||
* @return void
|
||||
**/
|
||||
function error_404($request=null) {
|
||||
error_page($request, 404);
|
||||
}
|
||||
|
||||
$_404_url_handler = 'error_404';
|
||||
function set_404_url_handler($handler=null) {
|
||||
global $_404_url_handler;
|
||||
$_404_url_handler = $handler;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Interprets the requested URL and return the corresponding UrlRequest object
|
||||
*
|
||||
* @param $default_url string|null The default URL if current one does not
|
||||
* match with any configured pattern.
|
||||
*
|
||||
* @return UrlRequest The UrlRequest object corresponding to the the requested URL.
|
||||
*/
|
||||
function get_request($default_url=null) {
|
||||
global $url_patterns, $_404_url_handler;
|
||||
$current_url = get_current_url();
|
||||
if ($current_url === false) {
|
||||
Log :: fatal(
|
||||
_('Unable to determine the requested page. '.
|
||||
'If the problem persists, please contact support.')
|
||||
);
|
||||
exit();
|
||||
}
|
||||
if (!is_array($url_patterns)) {
|
||||
Log :: fatal('URL : No URL patterns configured !');
|
||||
exit();
|
||||
}
|
||||
|
||||
Log :: debug("URL : current url = '$current_url'");
|
||||
Log :: trace(
|
||||
"URL : check current url with the following URL patterns :\n - ".
|
||||
implode("\n - ", array_keys($url_patterns))
|
||||
);
|
||||
foreach ($url_patterns as $pattern => $handler_infos) {
|
||||
$m = url_match($pattern, $current_url, $handler_infos['methods']);
|
||||
if (is_array($m)) {
|
||||
$request = new UrlRequest($current_url, $handler_infos, $m);
|
||||
// Reset last redirect
|
||||
if (isset($_SESSION['last_redirect']))
|
||||
unset($_SESSION['last_redirect']);
|
||||
Log :: trace("URL : result :\n".vardump($request));
|
||||
return $request;
|
||||
}
|
||||
}
|
||||
if ($default_url !== false) {
|
||||
Log :: debug("Current url match with no pattern. Redirect to default url ('$default_url')");
|
||||
redirect($default_url);
|
||||
exit();
|
||||
}
|
||||
// Error 404
|
||||
$api_mode = (strpos($current_url, 'api/') === 0);
|
||||
Log :: debug(
|
||||
"Current URL match with no pattern. Use error 404 handler (API mode=$api_mode).");
|
||||
return new UrlRequest(
|
||||
$current_url,
|
||||
array(
|
||||
'handler' => $_404_url_handler,
|
||||
'authenticated' => false,
|
||||
'api_mode' => $api_mode,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current requested URL match with a specific pattern
|
||||
*
|
||||
* @param string $pattern The URL pattern
|
||||
* @param string|false $current_url The current URL (optional)
|
||||
* @param array|null $methods HTTP method (optional, default: no check)
|
||||
*
|
||||
* @return array|false The URL info if pattern matched, false otherwise.
|
||||
**/
|
||||
function url_match($pattern, $current_url=false, $methods=null) {
|
||||
if ($methods && !in_array($_SERVER['REQUEST_METHOD'], $methods))
|
||||
return false;
|
||||
if ($current_url === false) {
|
||||
$current_url = get_current_url();
|
||||
if (!$current_url) return False;
|
||||
}
|
||||
if (preg_match($pattern, $current_url, $m)) {
|
||||
Log :: debug(
|
||||
"URL : Match found with pattern '$pattern' :\n\t".
|
||||
str_replace("\n", "\n\t", print_r($m, true)));
|
||||
return $m;
|
||||
}
|
||||
return False;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retreive current requested URL and return it
|
||||
*
|
||||
* @return string|false The current request URL or false if fail
|
||||
**/
|
||||
function get_current_url() {
|
||||
Log :: trace("URL : request URI = '".$_SERVER['REQUEST_URI']."'");
|
||||
|
||||
$base = get_rewrite_base();
|
||||
Log :: trace("URL : rewrite base = '$base'");
|
||||
|
||||
if ($_SERVER['REQUEST_URI'] == $base)
|
||||
return '';
|
||||
|
||||
if (substr($_SERVER['REQUEST_URI'], 0, strlen($base)) != $base) {
|
||||
Log :: error(
|
||||
"URL : request URI (".$_SERVER['REQUEST_URI'].") does not start with rewrite base ($base)");
|
||||
return False;
|
||||
}
|
||||
|
||||
$current_url = substr($_SERVER['REQUEST_URI'], strlen($base));
|
||||
|
||||
// URL contain params ?
|
||||
$params_start = strpos($current_url, '?');
|
||||
if ($params_start !== false)
|
||||
// Params detected, remove it
|
||||
|
||||
// No url / currrent url start by '?' ?
|
||||
if ($params_start == 0)
|
||||
return '';
|
||||
else
|
||||
return substr($current_url, 0, $params_start);
|
||||
|
||||
return $current_url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to detect rewrite base from public root URL
|
||||
*
|
||||
* @return string The detected rewrite base
|
||||
**/
|
||||
function get_rewrite_base() {
|
||||
global $public_root_url;
|
||||
if (preg_match('|^https?://[^/]+/(.*)$|', $public_root_url, $m))
|
||||
return '/'.remove_trailing_slash($m[1]).'/';
|
||||
elseif (preg_match('|^/(.*)$|', $public_root_url, $m))
|
||||
return '/'.remove_trailing_slash($m[1]).'/';
|
||||
return '/';
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger redirect to specified URL (or homepage if omited)
|
||||
*
|
||||
* @param string|false $go The destination URL
|
||||
*
|
||||
* @return void
|
||||
**/
|
||||
function redirect($go=false) {
|
||||
global $public_root_url;
|
||||
|
||||
if ($go===false)
|
||||
$go = "";
|
||||
// If more than one argument passed, format URL using sprintf & urlencode parameters
|
||||
elseif (func_num_args() > 1)
|
||||
$go = call_user_func_array(
|
||||
'sprintf',
|
||||
array_merge(
|
||||
array($go),
|
||||
array_map('urlencode', array_slice(func_get_args(), 1))
|
||||
)
|
||||
);
|
||||
|
||||
if (is_absolute_url($go))
|
||||
$url = $go;
|
||||
elseif (isset($public_root_url) && $public_root_url) {
|
||||
// Check $public_root_url end
|
||||
if (substr($public_root_url, -1)=='/') {
|
||||
$public_root_url=substr($public_root_url, 0, -1);
|
||||
}
|
||||
$url="$public_root_url/$go";
|
||||
}
|
||||
else
|
||||
$url="/$go";
|
||||
|
||||
// Prevent loop
|
||||
if (isset($_SESSION['last_redirect']) && $_SESSION['last_redirect'] == $url)
|
||||
Log :: fatal(
|
||||
_('Unable to determine the requested page (loop detected). '.
|
||||
'If the problem persists, please contact support.'));
|
||||
else
|
||||
$_SESSION['last_redirect'] = $url;
|
||||
|
||||
Log :: debug("redirect($go) => Redirect to : <$url>");
|
||||
header("Location: $url");
|
||||
exit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the current requested URL
|
||||
*
|
||||
* Note: if the route required that user is authenticated, this method will
|
||||
* invoke the force_authentication() special function (or trigger a fatal error
|
||||
* if it's not defined).
|
||||
*
|
||||
* @param string|null $default_url The default URL if current one does not
|
||||
* match with any configured pattern.
|
||||
*
|
||||
* @return void
|
||||
**/
|
||||
function handle_request($default_url=null) {
|
||||
global $smarty, $api_mode;
|
||||
|
||||
$sentry_span = new SentrySpan('http.handle_request', 'Handle the HTTP request');
|
||||
|
||||
$request = get_request($default_url);
|
||||
|
||||
if (!is_callable($request -> handler)) {
|
||||
Log :: error(
|
||||
"URL handler function %s does not exists !",
|
||||
format_callable($request -> handler));
|
||||
Log :: fatal(_("This request cannot be processed."));
|
||||
}
|
||||
|
||||
if ($request -> api_mode)
|
||||
$api_mode = true;
|
||||
if (isset($smarty) && $smarty)
|
||||
$smarty -> assign('request', $request);
|
||||
|
||||
// Check authentication (if need)
|
||||
if($request -> authenticated)
|
||||
if (function_exists('force_authentication'))
|
||||
force_authentication();
|
||||
else
|
||||
Log :: fatal(_("Authentication required but force_authentication function is not defined."));
|
||||
|
||||
try {
|
||||
call_user_func($request -> handler, $request);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
Log :: exception(
|
||||
$e, "An exception occured running URL handler function ".$request -> handler."()");
|
||||
Log :: fatal(_("This request could not be processed correctly."));
|
||||
}
|
||||
$sentry_span->finish();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove trailing slash in specified URL
|
||||
*
|
||||
* @param string $url The URL
|
||||
*
|
||||
* @return string The specified URL without trailing slash
|
||||
**/
|
||||
function remove_trailing_slash($url) {
|
||||
if ($url == '/')
|
||||
return $url;
|
||||
elseif (substr($url, -1) == '/')
|
||||
return substr($url, 0, -1);
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check an AJAX request and trigger a fatal error on fail
|
||||
*
|
||||
* Check if session key is present and valid and set AJAX
|
||||
* mode.
|
||||
*
|
||||
* @param string|null $session_key string The current session key (optional)
|
||||
*
|
||||
* @return void
|
||||
**/
|
||||
function check_ajax_request($session_key=null) {
|
||||
global $ajax, $debug_ajax;
|
||||
$ajax = true;
|
||||
|
||||
if (check_session_key($session_key))
|
||||
fatal_error('Invalid request');
|
||||
|
||||
if ($debug_ajax)
|
||||
Log :: debug("Ajax Request : ".vardump($_REQUEST));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the public absolute URL
|
||||
*
|
||||
* @param string|null $relative_url Relative URL to convert (Default: current URL)
|
||||
*
|
||||
* @return string The public absolute URL
|
||||
**/
|
||||
function get_absolute_url($relative_url=null) {
|
||||
global $public_root_url;
|
||||
if (!is_string($relative_url))
|
||||
$relative_url = get_current_url();
|
||||
if ($public_root_url[0] == '/') {
|
||||
Log :: debug(
|
||||
"URL :: get_absolute_url($relative_url): configured public root URL is relative ".
|
||||
"($public_root_url) => try to detect it from current request infos.");
|
||||
$public_root_url = (
|
||||
'http'.(isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on'?'s':'').'://'.
|
||||
$_SERVER['HTTP_HOST'].$public_root_url);
|
||||
Log :: debug(
|
||||
"URL :: get_absolute_url($relative_url): detected public_root_url: $public_root_url");
|
||||
}
|
||||
|
||||
if (substr($relative_url, 0, 1) == '/')
|
||||
$relative_url = substr($relative_url, 1);
|
||||
$url = remove_trailing_slash($public_root_url)."/$relative_url";
|
||||
Log :: debug("URL :: get_absolute_url($relative_url): result = $url");
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if specified URL is absolute
|
||||
*
|
||||
* @param string $url The URL to check
|
||||
*
|
||||
* @return boolean True if specified URL is absolute, False otherwise
|
||||
**/
|
||||
function is_absolute_url($url) {
|
||||
return boolval(preg_match('#^https?://#', $url));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add parameter in specified URL
|
||||
*
|
||||
* @param string &$url The reference of the URL
|
||||
* @param string $param The parameter name
|
||||
* @param string $value The parameter value
|
||||
* @param boolean $encode Set if parameter value must be URL encoded (optional, default: true)
|
||||
*
|
||||
* @return string The completed URL
|
||||
*/
|
||||
function add_url_parameter(&$url, $param, $value, $encode=true) {
|
||||
if (strpos($url, '?') === false)
|
||||
$url .= '?';
|
||||
else
|
||||
$url .= '&';
|
||||
$url .= "$param=".($encode?urlencode($value):$value);
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* URL request abstraction
|
||||
*
|
||||
* @author Benjamin Renard <brenard@easter-eggs.com>
|
||||
*/
|
||||
class UrlRequest {
|
||||
|
||||
// The URL requested handler
|
||||
private $current_url = null;
|
||||
|
||||
// The URL requested handler
|
||||
private $handler = null;
|
||||
|
||||
// Request need authentication ?
|
||||
private $authenticated = true;
|
||||
|
||||
// API mode enabled ?
|
||||
private $api_mode = false;
|
||||
|
||||
// Parameters detected on requested URL
|
||||
private $url_params = array();
|
||||
|
||||
public function __construct($current_url, $handler_infos, $url_params=array()) {
|
||||
$this -> current_url = $current_url;
|
||||
$this -> handler = $handler_infos['handler'];
|
||||
$this -> authenticated = (
|
||||
isset($handler_infos['authenticated'])?
|
||||
boolval($handler_infos['authenticated']):true);
|
||||
$this -> api_mode = (
|
||||
isset($handler_infos['api_mode'])?
|
||||
boolval($handler_infos['api_mode']):false);
|
||||
$this -> url_params = $url_params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get request info
|
||||
*
|
||||
* @param string $key The name of the info
|
||||
*
|
||||
* @return mixed The value
|
||||
**/
|
||||
public function __get($key) {
|
||||
if ($key == 'current_url')
|
||||
return $this -> current_url;
|
||||
if ($key == 'handler')
|
||||
return $this -> handler;
|
||||
if ($key == 'authenticated')
|
||||
return $this -> authenticated;
|
||||
if ($key == 'api_mode')
|
||||
return $this -> api_mode;
|
||||
if ($key == 'referer')
|
||||
return $this -> get_referer();
|
||||
if ($key == 'http_method')
|
||||
return $_SERVER['REQUEST_METHOD'];
|
||||
if (array_key_exists($key, $this->url_params)) {
|
||||
return urldecode($this->url_params[$key]);
|
||||
}
|
||||
// Unknown key, log warning
|
||||
Log :: warning(
|
||||
"__get($key): invalid property requested\n%s",
|
||||
Log :: get_debug_backtrace_context());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set request info
|
||||
*
|
||||
* @param string $key The name of the info
|
||||
* @param mixed $value The value of the info
|
||||
*
|
||||
* @return void
|
||||
**/
|
||||
public function __set($key, $value) {
|
||||
if ($key == 'referer')
|
||||
$_SERVER['HTTP_REFERER'] = $value;
|
||||
elseif ($key == 'http_method')
|
||||
$_SERVER['REQUEST_METHOD'] = $value;
|
||||
else
|
||||
$this->url_params[$key] = $value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Check is request info is set
|
||||
*
|
||||
* @param string $key The name of the info
|
||||
*
|
||||
* @return bool True is info is set, False otherwise
|
||||
**/
|
||||
public function __isset($key) {
|
||||
if (
|
||||
in_array(
|
||||
$key, array('current_url', 'handler', 'authenticated',
|
||||
'api_mode', 'referer', 'http_method')
|
||||
)
|
||||
)
|
||||
return True;
|
||||
return array_key_exists($key, $this->url_params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get request parameter
|
||||
*
|
||||
* @param string $parameter The name of the parameter
|
||||
* @param bool $decode If true, the parameter value will be urldecoded
|
||||
* (optional, default: true)
|
||||
*
|
||||
* @return mixed The value or false if parameter does not exists
|
||||
**/
|
||||
public function get_param($parameter, $decode=true) {
|
||||
if (array_key_exists($parameter, $this->url_params)) {
|
||||
if ($decode)
|
||||
return urldecode($this->url_params[$parameter]);
|
||||
return $this->url_params[$parameter];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get request referer (if known)
|
||||
*
|
||||
* @return string|null The request referer URL if known, null otherwise
|
||||
*/
|
||||
public function get_referer() {
|
||||
if (isset($_SERVER['HTTP_REFERER']) && $_SERVER['HTTP_REFERER'])
|
||||
return $_SERVER['HTTP_REFERER'];
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
# vim: tabstop=2 shiftwidth=2 softtabstop=2 expandtab
|
|
@ -24,7 +24,6 @@ parameters:
|
|||
message: "#Variable \\$root_dir_path might not be defined\\.#"
|
||||
paths:
|
||||
- includes/config.inc.php
|
||||
- "#Access to private property UrlRequest::\\$(handler|api_mode|authenticated)\\.#"
|
||||
-
|
||||
message: "#Variable \\$status_list might not be defined\\.#"
|
||||
paths:
|
||||
|
|
|
@ -3,7 +3,9 @@
|
|||
include '../includes/core.php';
|
||||
include 'url-public.php';
|
||||
|
||||
use EesyPHP\Url;
|
||||
|
||||
$default_url='';
|
||||
handle_request();
|
||||
Url :: handle_request();
|
||||
|
||||
# vim: tabstop=2 shiftwidth=2 softtabstop=2 expandtab
|
||||
|
|
517
src/Url.php
Normal file
517
src/Url.php
Normal file
|
@ -0,0 +1,517 @@
|
|||
<?php
|
||||
|
||||
namespace EesyPHP;
|
||||
|
||||
use EesyPHP\Log;
|
||||
use EesyPHP\SentrySpan;
|
||||
use EesyPHP\SentryTransaction;
|
||||
use EesyPHP\UrlRequest;
|
||||
|
||||
use function EesyPHP\format_callable;
|
||||
use function EesyPHP\vardump;
|
||||
|
||||
use Exception;
|
||||
|
||||
class Url {
|
||||
|
||||
/**
|
||||
* Configured URL patterns :
|
||||
*
|
||||
* Example :
|
||||
*
|
||||
* array (
|
||||
* '|get/(?P<name>[a-zA-Z0-9]+)$|' => array (
|
||||
* 'handler' => 'get',
|
||||
* 'authenticated' => true,
|
||||
* 'api_mode' => false,
|
||||
* 'methods' => array('GET'),
|
||||
* ),
|
||||
* '|get/all$|' => => array (
|
||||
* 'handler' => 'get_all',
|
||||
* 'authenticated' => true,
|
||||
* 'api_mode' => false,
|
||||
* 'methods' => array('GET', 'POST'),
|
||||
* ),
|
||||
* )
|
||||
* @var array
|
||||
*/
|
||||
protected static $patterns = array();
|
||||
|
||||
/**
|
||||
* Error 404 handler
|
||||
* @var callable
|
||||
*/
|
||||
protected static $error_404_handler = array('\\EesyPHP\\Url', 'error_404');
|
||||
|
||||
/**
|
||||
* Public root URL
|
||||
* @var string|null
|
||||
*/
|
||||
protected static $public_root_url = null;
|
||||
|
||||
/**
|
||||
* Enable/disable API mode
|
||||
* @see self :: initialization()
|
||||
* @var bool
|
||||
*/
|
||||
protected static bool $_api_mode;
|
||||
|
||||
/**
|
||||
* Initialization
|
||||
* @param string|null $public_root_url The application public root URL
|
||||
* @param bool $api_mode Enable/disable API mode
|
||||
* @return void
|
||||
*/
|
||||
public static function init($public_root_url = null, $api_mode=false) {
|
||||
if ($public_root_url) {
|
||||
// Check URL end
|
||||
if (substr(self :: $public_root_url, -1) == '/')
|
||||
$public_root_url = substr($public_root_url, 0, -1);
|
||||
self :: $public_root_url = $public_root_url?$public_root_url:null;
|
||||
}
|
||||
self :: $_api_mode = boolval($api_mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an URL pattern
|
||||
*
|
||||
* @param string|array $pattern The URL pattern or an array of patterns (required)
|
||||
* @param callable $handler The URL pattern handler (must be callable, required)
|
||||
* @param boolean $authenticated Permit to define if this URL is accessible only for
|
||||
* authenticated users (optional, default: true if the
|
||||
* special force_authentication function is defined,
|
||||
* false otherwise)
|
||||
* @param boolean $override Allow override if a command already exists with the
|
||||
* same name (optional, default: false)
|
||||
* @param boolean $api_mode Enable API mode (optional, default: false)
|
||||
* @param array|string|null $methods HTTP method (optional, default: array('GET', 'POST'))
|
||||
**/
|
||||
public static function add_url_handler($pattern, $handler=null, $authenticated=null, $override=true,
|
||||
$api_mode=false, $methods=null) {
|
||||
$authenticated = (
|
||||
is_null($authenticated)?
|
||||
function_exists('force_authentication'):
|
||||
(bool)$authenticated
|
||||
);
|
||||
if (is_null($methods))
|
||||
$methods = array('GET', 'POST');
|
||||
elseif (!is_array($methods))
|
||||
$methods = array($methods);
|
||||
if (is_array($pattern)) {
|
||||
if (is_null($handler))
|
||||
foreach($pattern as $p => $h)
|
||||
self :: add_url_handler($p, $h, $authenticated, $override, $api_mode, $methods);
|
||||
else
|
||||
foreach($pattern as $p)
|
||||
self :: add_url_handler($p, $handler, $authenticated, $override, $api_mode, $methods);
|
||||
}
|
||||
else {
|
||||
if (!isset(self :: $patterns[$pattern])) {
|
||||
self :: $patterns[$pattern] = array(
|
||||
'handler' => $handler,
|
||||
'authenticated' => $authenticated,
|
||||
'api_mode' => $api_mode,
|
||||
'methods' => $methods,
|
||||
);
|
||||
}
|
||||
elseif ($override) {
|
||||
Log :: debug(
|
||||
"URL : override pattern '%s' with handler '%s' (old handler = '%s')".
|
||||
$pattern, format_callable($handler), vardump(self :: $patterns[$pattern])
|
||||
);
|
||||
self :: $patterns[$pattern] = array(
|
||||
'handler' => $handler,
|
||||
'authenticated' => $authenticated,
|
||||
'api_mode' => $api_mode,
|
||||
'methods' => $methods,
|
||||
);
|
||||
}
|
||||
else {
|
||||
Log :: debug("URL : pattern '$pattern' already defined : do not override.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show error page
|
||||
*
|
||||
* @param $request UrlRequest|null The request (optional, default: null)
|
||||
* @param $error_code int|null The HTTP error code (optional, default: 400)
|
||||
*
|
||||
* @return void
|
||||
**/
|
||||
public static function error_page($request=null, $error_code=null) {
|
||||
global $smarty;
|
||||
$http_errors = array(
|
||||
400 => array(
|
||||
'pagetitle' => _("Bad request"),
|
||||
'message' => _("Invalid request."),
|
||||
),
|
||||
401 => array(
|
||||
'pagetitle' => _("Authentication required"),
|
||||
'message' => _("You have to be authenticated to access to this page."),
|
||||
),
|
||||
403 => array(
|
||||
'pagetitle' => _("Access denied"),
|
||||
'message' => _("You do not have access to this application. If you think this is an error, please contact support."),
|
||||
),
|
||||
404 => array(
|
||||
'pagetitle' => _("Whoops ! Page not found"),
|
||||
'message' => _("The requested page can not be found."),
|
||||
),
|
||||
);
|
||||
$error_code = ($error_code?intval($error_code):400);
|
||||
if (array_key_exists($error_code, $http_errors))
|
||||
$error = $http_errors[intval($error_code)];
|
||||
else
|
||||
$error = array(
|
||||
'pagetitle' => _('Error'),
|
||||
'message' => _('An unknown error occurred. If problem persist, please contact support.'),
|
||||
);
|
||||
http_response_code($error_code);
|
||||
|
||||
if (isset($smarty) && $smarty)
|
||||
$smarty -> assign('message', $error['message']);
|
||||
display_template('error_page.tpl', $error['pagetitle']);
|
||||
exit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Error 404 handler
|
||||
*
|
||||
* @param UrlRequest|null $request The request (optional, default: null)
|
||||
*
|
||||
* @return void
|
||||
**/
|
||||
public static function error_404($request=null) {
|
||||
self :: error_page($request, 404);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set unknown URL handler
|
||||
* @param callable|null $handler
|
||||
* @return bool
|
||||
*/
|
||||
public static function set_unknown_url_handler($handler=null) {
|
||||
// @phpstan-ignore-next-line
|
||||
if (is_callable($handler) || is_null($handler)) {
|
||||
self :: $error_404_handler = $handler;
|
||||
return true;
|
||||
}
|
||||
// @phpstan-ignore-next-line
|
||||
Log :: warning(
|
||||
'Url::set_unknown_url_handler(): Invalid URL handler provided: %s',
|
||||
vardump($handler));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Interprets the requested URL and return the corresponding UrlRequest object
|
||||
*
|
||||
* @param $default_url string|null The default URL if current one does not
|
||||
* match with any configured pattern.
|
||||
*
|
||||
* @return UrlRequest The UrlRequest object corresponding to the the requested URL.
|
||||
*/
|
||||
protected static function get_request($default_url=null) {
|
||||
$current_url = self :: get_current_url();
|
||||
if ($current_url === false) {
|
||||
Log :: fatal(
|
||||
_('Unable to determine the requested page. '.
|
||||
'If the problem persists, please contact support.')
|
||||
);
|
||||
exit();
|
||||
}
|
||||
if (!is_array(self :: $patterns)) {
|
||||
Log :: fatal('URL : No URL patterns configured !');
|
||||
exit();
|
||||
}
|
||||
|
||||
Log :: debug("URL : current url = '$current_url'");
|
||||
Log :: trace(
|
||||
"URL : check current url with the following URL patterns :\n - ".
|
||||
implode("\n - ", array_keys(self :: $patterns))
|
||||
);
|
||||
foreach (self :: $patterns as $pattern => $handler_infos) {
|
||||
$m = self :: url_match($pattern, $current_url, $handler_infos['methods']);
|
||||
if (is_array($m)) {
|
||||
$request = new UrlRequest($current_url, $handler_infos, $m);
|
||||
// Reset last redirect
|
||||
if (isset($_SESSION['last_redirect']))
|
||||
unset($_SESSION['last_redirect']);
|
||||
Log :: trace("URL : result :\n".vardump($request));
|
||||
return $request;
|
||||
}
|
||||
}
|
||||
if ($default_url !== false) {
|
||||
Log :: debug("Current url match with no pattern. Redirect to default url ('$default_url')");
|
||||
self :: redirect($default_url);
|
||||
exit();
|
||||
}
|
||||
// Error 404
|
||||
$api_mode = self :: $_api_mode || (strpos($current_url, 'api/') === 0);
|
||||
Log :: debug(
|
||||
"Current URL match with no pattern. Use error 404 handler (API mode=$api_mode).");
|
||||
return new UrlRequest(
|
||||
$current_url,
|
||||
array(
|
||||
'handler' => self :: $error_404_handler,
|
||||
'authenticated' => false,
|
||||
'api_mode' => $api_mode,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current requested URL match with a specific pattern
|
||||
*
|
||||
* @param string $pattern The URL pattern
|
||||
* @param string|false $current_url The current URL (optional)
|
||||
* @param array|null $methods HTTP method (optional, default: no check)
|
||||
*
|
||||
* @return array|false The URL info if pattern matched, false otherwise.
|
||||
**/
|
||||
public static function url_match($pattern, $current_url=false, $methods=null) {
|
||||
if ($methods && !in_array($_SERVER['REQUEST_METHOD'], $methods))
|
||||
return false;
|
||||
if ($current_url === false) {
|
||||
$current_url = self :: get_current_url();
|
||||
if (!$current_url) return False;
|
||||
}
|
||||
if (preg_match($pattern, $current_url, $m)) {
|
||||
Log :: debug(
|
||||
"URL : Match found with pattern '$pattern' :\n\t".
|
||||
str_replace("\n", "\n\t", print_r($m, true)));
|
||||
return $m;
|
||||
}
|
||||
return False;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retreive current requested URL and return it
|
||||
*
|
||||
* @return string|false The current request URL or false if fail
|
||||
**/
|
||||
public static function get_current_url() {
|
||||
Log :: trace("URL : request URI = '".$_SERVER['REQUEST_URI']."'");
|
||||
|
||||
$base = self :: get_rewrite_base();
|
||||
Log :: trace("URL : rewrite base = '$base'");
|
||||
|
||||
if ($_SERVER['REQUEST_URI'] == $base)
|
||||
return '';
|
||||
|
||||
if (substr($_SERVER['REQUEST_URI'], 0, strlen($base)) != $base) {
|
||||
Log :: error(
|
||||
"URL : request URI (".$_SERVER['REQUEST_URI'].") does not start with rewrite base ($base)");
|
||||
return False;
|
||||
}
|
||||
|
||||
$current_url = substr($_SERVER['REQUEST_URI'], strlen($base));
|
||||
|
||||
// URL contain params ?
|
||||
$params_start = strpos($current_url, '?');
|
||||
if ($params_start !== false)
|
||||
// Params detected, remove it
|
||||
|
||||
// No url / currrent url start by '?' ?
|
||||
if ($params_start == 0)
|
||||
return '';
|
||||
else
|
||||
return substr($current_url, 0, $params_start);
|
||||
|
||||
return $current_url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to detect rewrite base from public root URL
|
||||
*
|
||||
* @return string The detected rewrite base
|
||||
**/
|
||||
public static function get_rewrite_base() {
|
||||
if (!self :: $public_root_url) return '/';
|
||||
if (preg_match('|^https?://[^/]+/(.*)$|', self :: $public_root_url, $m))
|
||||
return '/'.self :: remove_trailing_slash($m[1]).'/';
|
||||
if (preg_match('|^/(.*)$|', self :: $public_root_url, $m))
|
||||
return '/'.self :: remove_trailing_slash($m[1]).'/';
|
||||
return '/';
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger redirect to specified URL (or homepage if omited)
|
||||
*
|
||||
* @param string|false $go The destination URL
|
||||
*
|
||||
* @return void
|
||||
**/
|
||||
public static function redirect($go=false) {
|
||||
if ($go===false)
|
||||
$go = "";
|
||||
// If more than one argument passed, format URL using sprintf & urlencode parameters
|
||||
elseif (func_num_args() > 1)
|
||||
$go = call_user_func_array(
|
||||
'sprintf',
|
||||
array_merge(
|
||||
array($go),
|
||||
array_map('urlencode', array_slice(func_get_args(), 1))
|
||||
)
|
||||
);
|
||||
|
||||
if (self :: is_absolute_url($go))
|
||||
$url = $go;
|
||||
elseif (self :: $public_root_url)
|
||||
$url = self :: $public_root_url."/$go";
|
||||
else
|
||||
$url = "/$go";
|
||||
|
||||
// Prevent loop
|
||||
if (isset($_SESSION['last_redirect']) && $_SESSION['last_redirect'] == $url)
|
||||
Log :: fatal(
|
||||
_('Unable to determine the requested page (loop detected). '.
|
||||
'If the problem persists, please contact support.'));
|
||||
else
|
||||
$_SESSION['last_redirect'] = $url;
|
||||
|
||||
Log :: debug("redirect($go) => Redirect to : <$url>");
|
||||
header("Location: $url");
|
||||
exit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the current requested URL
|
||||
*
|
||||
* Note: if the route required that user is authenticated, this method will
|
||||
* invoke the force_authentication() special function (or trigger a fatal error
|
||||
* if it's not defined).
|
||||
*
|
||||
* @param string|null $default_url The default URL if current one does not
|
||||
* match with any configured pattern.
|
||||
*
|
||||
* @return void
|
||||
**/
|
||||
public static function handle_request($default_url=null) {
|
||||
global $smarty;
|
||||
|
||||
$sentry_span = new SentrySpan('http.handle_request', 'Handle the HTTP request');
|
||||
|
||||
$request = self :: get_request($default_url);
|
||||
|
||||
if (!is_callable($request -> handler)) {
|
||||
Log :: error(
|
||||
"URL handler function %s does not exists !",
|
||||
format_callable($request -> handler));
|
||||
Log :: fatal(_("This request cannot be processed."));
|
||||
}
|
||||
|
||||
if ($request -> api_mode)
|
||||
self :: $_api_mode = true;
|
||||
if (isset($smarty) && $smarty)
|
||||
$smarty -> assign('request', $request);
|
||||
|
||||
// Check authentication (if need)
|
||||
if($request -> authenticated)
|
||||
if (function_exists('force_authentication'))
|
||||
force_authentication();
|
||||
else
|
||||
Log :: fatal(_("Authentication required but force_authentication function is not defined."));
|
||||
|
||||
try {
|
||||
call_user_func($request -> handler, $request);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
Log :: exception(
|
||||
$e, "An exception occured running URL handler function %s()",
|
||||
format_callable($request -> handler));
|
||||
Log :: fatal(_("This request could not be processed correctly."));
|
||||
}
|
||||
$sentry_span->finish();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove trailing slash in specified URL
|
||||
*
|
||||
* @param string $url The URL
|
||||
*
|
||||
* @return string The specified URL without trailing slash
|
||||
**/
|
||||
public static function remove_trailing_slash($url) {
|
||||
if ($url == '/')
|
||||
return $url;
|
||||
elseif (substr($url, -1) == '/')
|
||||
return substr($url, 0, -1);
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the public absolute URL
|
||||
*
|
||||
* @param string|null $relative_url Relative URL to convert (Default: current URL)
|
||||
*
|
||||
* @return string The public absolute URL
|
||||
**/
|
||||
public static function get_absolute_url($relative_url=null) {
|
||||
if (!is_string($relative_url))
|
||||
$relative_url = self :: get_current_url();
|
||||
if (self :: $public_root_url[0] == '/') {
|
||||
Log :: debug(
|
||||
"URL :: get_absolute_url($relative_url): configured public root URL is relative ".
|
||||
"(%s) => try to detect it from current request infos.",
|
||||
self :: $public_root_url);
|
||||
self :: $public_root_url = (
|
||||
'http'.(isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on'?'s':'').'://'.
|
||||
$_SERVER['HTTP_HOST'].self :: $public_root_url);
|
||||
Log :: debug(
|
||||
"URL :: get_absolute_url(%s): detected public_root_url: %s",
|
||||
$relative_url, self :: $public_root_url);
|
||||
}
|
||||
|
||||
if (substr($relative_url, 0, 1) == '/')
|
||||
$relative_url = substr($relative_url, 1);
|
||||
$url = self :: remove_trailing_slash(self :: $public_root_url)."/$relative_url";
|
||||
Log :: debug("URL :: get_absolute_url($relative_url): result = $url");
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if specified URL is absolute
|
||||
*
|
||||
* @param string $url The URL to check
|
||||
*
|
||||
* @return boolean True if specified URL is absolute, False otherwise
|
||||
**/
|
||||
public static function is_absolute_url($url) {
|
||||
return boolval(preg_match('#^https?://#', $url));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add parameter in specified URL
|
||||
*
|
||||
* @param string &$url The reference of the URL
|
||||
* @param string $param The parameter name
|
||||
* @param string $value The parameter value
|
||||
* @param boolean $encode Set if parameter value must be URL encoded (optional, default: true)
|
||||
*
|
||||
* @return string The completed URL
|
||||
*/
|
||||
public static function add_url_parameter(&$url, $param, $value, $encode=true) {
|
||||
if (strpos($url, '?') === false)
|
||||
$url .= '?';
|
||||
else
|
||||
$url .= '&';
|
||||
$url .= "$param=".($encode?urlencode($value):$value);
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get/set API mode
|
||||
* @param bool|null $value If boolean, the current API mode will be changed
|
||||
* @return bool Current API mode
|
||||
*/
|
||||
public static function api_mode($value=null) {
|
||||
if (is_bool($value)) self :: $_api_mode = $value;
|
||||
return self :: $_api_mode;
|
||||
}
|
||||
}
|
||||
|
||||
# vim: tabstop=2 shiftwidth=2 softtabstop=2 expandtab
|
143
src/UrlRequest.php
Normal file
143
src/UrlRequest.php
Normal file
|
@ -0,0 +1,143 @@
|
|||
<?php
|
||||
|
||||
namespace EesyPHP;
|
||||
|
||||
use EesyPHP\Log;
|
||||
|
||||
|
||||
/**
|
||||
* URL request abstraction
|
||||
*
|
||||
* @author Benjamin Renard <brenard@easter-eggs.com>
|
||||
* @property-read string|null $current_url;
|
||||
* @property-read array|null $handler;
|
||||
* @property-read bool $authenticated;
|
||||
* @property-read bool $api_mode;
|
||||
*/
|
||||
class UrlRequest {
|
||||
|
||||
// The URL requested handler
|
||||
private $current_url = null;
|
||||
|
||||
// The URL requested handler
|
||||
private $handler = null;
|
||||
|
||||
// Request need authentication ?
|
||||
private $authenticated = true;
|
||||
|
||||
// API mode enabled ?
|
||||
private $api_mode = false;
|
||||
|
||||
// Parameters detected on requested URL
|
||||
private $url_params = array();
|
||||
|
||||
public function __construct($current_url, $handler_infos, $url_params=array()) {
|
||||
$this -> current_url = $current_url;
|
||||
$this -> handler = $handler_infos['handler'];
|
||||
$this -> authenticated = (
|
||||
isset($handler_infos['authenticated'])?
|
||||
boolval($handler_infos['authenticated']):true);
|
||||
$this -> api_mode = (
|
||||
isset($handler_infos['api_mode'])?
|
||||
boolval($handler_infos['api_mode']):false);
|
||||
$this -> url_params = $url_params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get request info
|
||||
*
|
||||
* @param string $key The name of the info
|
||||
*
|
||||
* @return mixed The value
|
||||
**/
|
||||
public function __get($key) {
|
||||
if ($key == 'current_url')
|
||||
return $this -> current_url;
|
||||
if ($key == 'handler')
|
||||
return $this -> handler;
|
||||
if ($key == 'authenticated')
|
||||
return $this -> authenticated;
|
||||
if ($key == 'api_mode')
|
||||
return $this -> api_mode;
|
||||
if ($key == 'referer')
|
||||
return $this -> get_referer();
|
||||
if ($key == 'http_method')
|
||||
return $_SERVER['REQUEST_METHOD'];
|
||||
if (array_key_exists($key, $this->url_params)) {
|
||||
return urldecode($this->url_params[$key]);
|
||||
}
|
||||
// Unknown key, log warning
|
||||
Log :: warning(
|
||||
"__get($key): invalid property requested\n%s",
|
||||
Log :: get_debug_backtrace_context());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set request info
|
||||
*
|
||||
* @param string $key The name of the info
|
||||
* @param mixed $value The value of the info
|
||||
*
|
||||
* @return void
|
||||
**/
|
||||
public function __set($key, $value) {
|
||||
if ($key == 'referer')
|
||||
$_SERVER['HTTP_REFERER'] = $value;
|
||||
elseif ($key == 'http_method')
|
||||
$_SERVER['REQUEST_METHOD'] = $value;
|
||||
else
|
||||
$this->url_params[$key] = $value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Check is request info is set
|
||||
*
|
||||
* @param string $key The name of the info
|
||||
*
|
||||
* @return bool True is info is set, False otherwise
|
||||
**/
|
||||
public function __isset($key) {
|
||||
if (
|
||||
in_array(
|
||||
$key, array('current_url', 'handler', 'authenticated',
|
||||
'api_mode', 'referer', 'http_method')
|
||||
)
|
||||
)
|
||||
return True;
|
||||
return array_key_exists($key, $this->url_params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get request parameter
|
||||
*
|
||||
* @param string $parameter The name of the parameter
|
||||
* @param bool $decode If true, the parameter value will be urldecoded
|
||||
* (optional, default: true)
|
||||
*
|
||||
* @return mixed The value or false if parameter does not exists
|
||||
**/
|
||||
public function get_param($parameter, $decode=true) {
|
||||
if (array_key_exists($parameter, $this->url_params)) {
|
||||
if ($decode)
|
||||
return urldecode($this->url_params[$parameter]);
|
||||
return $this->url_params[$parameter];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get request referer (if known)
|
||||
*
|
||||
* @return string|null The request referer URL if known, null otherwise
|
||||
*/
|
||||
public function get_referer() {
|
||||
if (isset($_SERVER['HTTP_REFERER']) && $_SERVER['HTTP_REFERER'])
|
||||
return $_SERVER['HTTP_REFERER'];
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
# vim: tabstop=2 shiftwidth=2 softtabstop=2 expandtab
|
Loading…
Reference in a new issue