git-svn-id: https://svn.fournier38.fr/svn/ProgSVN/trunk@1819 bf3deb0d-5f1a-0410-827f-c0cc1f45334c
625 lines
19 KiB
PHP
625 lines
19 KiB
PHP
<?php
|
|
/** DomFramework
|
|
@package domframework
|
|
@author Dominique Fournier <dominique@fournier38.fr> */
|
|
|
|
require_once ("domframework/http.php");
|
|
|
|
/** The routing module, base of the DomFramework */
|
|
class route
|
|
{
|
|
/** The baseURL of the site */
|
|
private $baseURL = "";
|
|
/** The baseURL of the module in the site */
|
|
private $baseURLmodule = "";
|
|
/** The method used to ask the page */
|
|
public $method = "";
|
|
/** The module name */
|
|
public $module = NULL;
|
|
/** The debug mode :
|
|
0:NoDebug, 1:routing, 2:more debug (developpement)*/
|
|
public $debug=0;
|
|
//public $defaultOutput = "html"; // Default renderer : html
|
|
|
|
/** Return the baseURL of the site
|
|
Always finish with a slash
|
|
@param string|null $module The module name (if thereis one) */
|
|
function baseURL ($module = FALSE)
|
|
{
|
|
if ($this->module === NULL)
|
|
$this->module = $module;
|
|
if ($this->baseURL !== "")
|
|
return $this->baseURL;
|
|
|
|
if (!isset ($_SERVER["SERVER_PORT"]))
|
|
$_SERVER["SERVER_PORT"] = "80";
|
|
$port = ":".$_SERVER["SERVER_PORT"];
|
|
if (!isset ($_SERVER["HTTPS"]) && $_SERVER["SERVER_PORT"] === "80")
|
|
$port = "";
|
|
if (isset ($_SERVER["HTTPS"]) && $_SERVER["SERVER_PORT"] === "443")
|
|
$port = "";
|
|
|
|
if (isset ($_SERVER["SCRIPT_NAME"]))
|
|
$this->baseURL = dirname ($_SERVER["SCRIPT_NAME"]);
|
|
if (isset ($_SERVER["SERVER_NAME"]))
|
|
$this->baseURL = "//".$_SERVER["SERVER_NAME"].$port.$this->baseURL;
|
|
if (isset ($_SERVER["HTTPS"]))
|
|
$this->baseURL = "https:".$this->baseURL;
|
|
else
|
|
$this->baseURL = "http:".$this->baseURL;
|
|
if (substr ($this->baseURL, -1) !== "/")
|
|
$this->baseURL .= "/";
|
|
$this->baseURLmodule = $this->baseURL;
|
|
// Only != NOT !== (cause : $this->module can be converted in string "0")
|
|
if ($this->module != FALSE)
|
|
{
|
|
$this->baseURLmodule = $this->baseURL;
|
|
$this->baseURL = dirname ($this->baseURL)."/";
|
|
}
|
|
|
|
return $this->baseURL;
|
|
}
|
|
|
|
/** Return the baseURL of the module
|
|
Always finish with a slash */
|
|
function baseURLmodule ()
|
|
{
|
|
if ($this->baseURLmodule !== "")
|
|
return $this->baseURLmodule;
|
|
$this->baseURL ($this->module);
|
|
return $this->baseURLmodule;
|
|
}
|
|
|
|
/** Define the base URL of the site
|
|
@param string $baseURL The base URL of the site */
|
|
function baseURLset ($baseURL)
|
|
{
|
|
$this->baseURL = $baseURL;
|
|
}
|
|
|
|
/** Return the complete URL used to see this page */
|
|
function requestURL ()
|
|
{
|
|
if (isset ($_SERVER["HTTPS"]))
|
|
$url = "https:";
|
|
else
|
|
$url = "http:";
|
|
if (!isset ($_SERVER["SERVER_PORT"]))
|
|
$_SERVER["SERVER_PORT"] = "80";
|
|
$port = ":".$_SERVER["SERVER_PORT"];
|
|
if (!isset ($_SERVER["HTTPS"]) && $_SERVER["SERVER_PORT"] === "80")
|
|
$port = "";
|
|
if (isset ($_SERVER["HTTPS"]) && $_SERVER["SERVER_PORT"] === "443")
|
|
$port = "";
|
|
|
|
if (isset ($_SERVER["SERVER_NAME"]))
|
|
$url .= "//".$_SERVER["SERVER_NAME"].$port;
|
|
if (isset ($_SERVER["REQUEST_URI"]))
|
|
$url .= $_SERVER["REQUEST_URI"];
|
|
return $url;
|
|
}
|
|
|
|
/** Do all the routing with redirections
|
|
$destURL can be absolute or relative
|
|
If module is set, the site is modular and a directory is named with module
|
|
name
|
|
@param string $destURL Do a redirection of the HTTP page
|
|
@param string $module The module name
|
|
@param boolean|null Permanent redirect (false by default) */
|
|
function redirect ($destURL, $module, $permanent = false)
|
|
{
|
|
if (php_sapi_name () === "cli")
|
|
exit;
|
|
$destURL = trim ($destURL);
|
|
$baseURL = $this->baseURL ($module);
|
|
$requestURL = $this->requestURL ();
|
|
if (substr ($requestURL, -1) !== "/")
|
|
$requestURL .= "/";
|
|
if ($destURL[0] === "/")
|
|
{
|
|
// Absolute : return to project base
|
|
$destURL = $baseURL.substr ($destURL, 1);
|
|
}
|
|
if (substr ($destURL, 0, 4) !== "http")
|
|
{
|
|
// Relative to parameters : continue
|
|
$destURL = $requestURL.$destURL;
|
|
}
|
|
|
|
// Else http : keep the complete URL
|
|
if ($destURL === "")
|
|
throw new Exception ("Destination URL is empty", 500);
|
|
// Allow to redirect from POST to GET, but not GET to GET (can loop)
|
|
if ($destURL === $requestURL && $_SERVER["REQUEST_METHOD"] === "GET")
|
|
throw new Exception ("Redirect to myself", 508);
|
|
if (substr ($destURL, 0, strlen ($baseURL)) !== $baseURL)
|
|
throw new Exception ("Can't redirect outside this site (Base $baseURL)",
|
|
405);
|
|
|
|
if ($this->debug)
|
|
{
|
|
echo "<pre>\n";
|
|
echo "==== DEBUG : REDIRECT START ====\n";
|
|
echo "BASEURL=$baseURL\n";
|
|
echo "REQUURL=$requestURL\n";
|
|
echo "destURL=$destURL\n";
|
|
echo " ---> Redirect to <a href='$destURL'>$destURL</a>\n";
|
|
echo "==== DEBUG : REDIRECT END ====\n";
|
|
echo "</pre>\n";
|
|
exit;
|
|
}
|
|
|
|
if (! headers_sent ())
|
|
{
|
|
header ("Cache-Control: no-store, no-cache, must-revalidate");
|
|
header ("Pragma: no-cache");
|
|
if ($permanent)
|
|
header ("HTTP/1.1 301 Moved Permanently");
|
|
header ("Location: $destURL");
|
|
}
|
|
else
|
|
{
|
|
echo "<script type='text/javascript'>\n";
|
|
echo "window.location.replace('$destURL');</script>";
|
|
echo "<a href='$destURL'>Redirect to $destURL</a>";
|
|
}
|
|
|
|
exit;
|
|
}
|
|
|
|
/** Do all the routing actions
|
|
wait an array with search=>action
|
|
- search can be the end of URL and optionnal parameters in {var1} format
|
|
{var1} must be the name of the parameter of the method
|
|
- action is an array with (0=>CLASS, 1=>method, 2...=>parameters) */
|
|
// DISABLED FUNCTION : TOO COMPLEX !!
|
|
/*function routingCollection ($routes)
|
|
{
|
|
if ($this->debug)
|
|
echo "<pre>==== DEBUG : ROUTING START ====\n";
|
|
foreach ($routes as $route=>$action)
|
|
{
|
|
$url = substr ($this->requestURL (), strlen ($this->baseURLmodule ()));
|
|
if ($this->debug)
|
|
{
|
|
echo "==> $url ?? $route => ".$action[0]."->".$action[1]."(";
|
|
echo implode (", ", array_slice ($action, 2)).") : ";
|
|
// Flush to debug redirect if javascript is disabled
|
|
ob_flush ();
|
|
flush ();
|
|
}
|
|
|
|
$class = $action[0];
|
|
$method = $action[1];
|
|
$params = array_slice ($action, 2);
|
|
|
|
/* This part is actually disabled to be simpler
|
|
// Asked output
|
|
$outputAuthorized = array ("html");
|
|
// -- Look at authorized outputs
|
|
$regex = "#\((.+)\)#U";
|
|
unset ($matches);
|
|
$rcRegex = preg_match ($regex, $route, $matchesExt);
|
|
if ($rcRegex !== FALSE && $rcRegex !== 0)
|
|
{
|
|
// Defined output provided
|
|
$outputAuthorized = explode (".", $matchesExt[1]);
|
|
$routeOutput = $matchesExt[0];
|
|
$regexOutput = "(?P<extension>";
|
|
foreach ($outputAuthorized as $k=>$m)
|
|
{
|
|
if ($m === "")
|
|
{
|
|
$regexOutput .= "";
|
|
}
|
|
else
|
|
{
|
|
$outputAuthorized[$k] = ".$m";
|
|
$regexOutput .= "|\\.$m";
|
|
}
|
|
}
|
|
$regexOutput .= ")";
|
|
}
|
|
|
|
if ($this->debug)
|
|
echo "\n";
|
|
if ($this->debug > 1)
|
|
{
|
|
echo " -- AUTHORIZED OUTPUT\n";
|
|
echo "routeOutput = $routeOutput\n";
|
|
echo "regexOutput = ".htmlentities ($regexOutput)."\n";
|
|
echo "outputAuthorized = ";print_r ($outputAuthorized);
|
|
echo " -- LOOK AT EXTENSION IN URL : SAVE IT AND REMOVE IF FOUND\n";
|
|
}
|
|
|
|
$regex = "#^$route$#U";
|
|
$regex = str_replace ("{", "(?P<", $regex);
|
|
$regex = str_replace ("}", ">.+)", $regex);
|
|
$regex = str_replace ($routeOutput, $regexOutput, $regex);
|
|
if ($this->debug > 1)
|
|
echo "regex = ".htmlentities ($regex)."\n";
|
|
unset ($matches);
|
|
$rcRegex = preg_match ($regex, $url, $matches);
|
|
if ($rcRegex !== FALSE && $rcRegex !== 0)
|
|
{
|
|
if ($this->debug > 1)
|
|
{
|
|
echo "MATCHES=";print_r ($matches);
|
|
}
|
|
if (isset ($matches["extension"]) && $matches["extension"] !== "")
|
|
{
|
|
// Authorized extension found : save it and remove it from URL
|
|
$this->output = substr ($matches["extension"], 1);
|
|
if ($this->debug)
|
|
echo "Extension found in url : output='".$this->output."'\n";
|
|
// Remove numerical indexes and extension : new URL without output
|
|
// extension
|
|
$url = "";
|
|
foreach ($matches as $key=>$val)
|
|
{
|
|
if (is_numeric ($key)) continue;
|
|
if ($key === "extension") continue;
|
|
$url .= $val;
|
|
}
|
|
|
|
if ($this->debug > 1)
|
|
echo "After removing extension : url='$url'\n";
|
|
}
|
|
else
|
|
{
|
|
$this->output = $this->defaultOutput;
|
|
if ($this->debug)
|
|
echo "Extension empty found in url : default output '".
|
|
"$this->output'\n";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
$this->output = $this->defaultOutput;
|
|
if ($this->debug)
|
|
echo "Extension not found in url : default output '$this->output'\n";
|
|
}
|
|
if ($this->debug > 1)
|
|
echo " -- REMOVE EXTENSION TEST IN ROUTE\n";
|
|
$route = str_replace ($routeOutput, "", $route);
|
|
if ($this->debug)
|
|
{
|
|
echo "URL='";print_r ($url); echo "'\n";
|
|
echo "ROUTE='";print_r ($route); echo "'\n";
|
|
}*/
|
|
/*
|
|
|
|
// URL === ROUTE (No variables)
|
|
if ($url === $route)
|
|
{
|
|
if ($this->debug)
|
|
{
|
|
echo "FOUND : URL === ROUTE (No variables) : ";
|
|
echo "Call $class->$method()\n";
|
|
echo "==== DEBUG : ROUTING END ====</pre>\n";
|
|
}
|
|
|
|
// For loading user modules controllers
|
|
$rc = @include_once ("controllers/controller_$class.php");
|
|
// For loading framework classes
|
|
if ($rc === FALSE) @include_once ("$class.php");
|
|
|
|
try
|
|
{
|
|
$reflection = new ReflectionMethod ($class, $method);
|
|
$res = $reflection->invokeArgs(new $class, $params);
|
|
}
|
|
catch (Exception $e)
|
|
{
|
|
$res = "ERROR : ". $e->getMessage();
|
|
}
|
|
return $res;
|
|
}
|
|
|
|
// URL === REGEXP ROUTE
|
|
// Variables are exposed in url/{var1}/{var2}
|
|
$regex = "#^$route$#U";
|
|
$regex = str_replace ("{", "(?P<", $regex);
|
|
$regex = str_replace ("}", ">.+)", $regex);
|
|
unset ($matches);
|
|
$rcRegex = preg_match ($regex, $url, $matches);
|
|
if ($rcRegex !== FALSE && $rcRegex !== 0)
|
|
{
|
|
if ($this->debug)
|
|
{
|
|
echo "FOUND : URL === REGEX : ";
|
|
}
|
|
|
|
// seach->replace with the matches
|
|
$search = array_keys ($matches);
|
|
array_walk ($search, function(&$n) { $n = "{".$n."}"; });
|
|
$replace = $matches;
|
|
|
|
// For loading user modules controllers
|
|
$rc = @include_once ("controllers/controller_$class.php");
|
|
// For loading framework classes
|
|
if ($rc === FALSE) $rc = @include_once ("$class.php");
|
|
if ($rc === FALSE && $this->debug)
|
|
echo "NO MATCH (missing class file)\n";
|
|
if ($rc === FALSE)
|
|
continue;
|
|
|
|
// Check if a parameter is for the constructor of the class
|
|
$passConstruct = array ();
|
|
try
|
|
{
|
|
// If there is no constructor in the controller, an exception is
|
|
// raised
|
|
$reflectionClass = new ReflectionMethod ($class, "__construct");
|
|
foreach ($reflectionClass->getParameters() as $key=>$param)
|
|
{
|
|
if (isset ($matches[$param->getName ()]))
|
|
$passConstruct[] = $matches[$param->getName ()];
|
|
elseif ($param->isOptional ())
|
|
$passConstruct[] = $matches[$param->getDefaultValue ()];
|
|
else
|
|
{
|
|
// Missing parameters : abort this route test
|
|
if ($this->debug)
|
|
echo "NO MATCH (missing constructor parameter)\n";
|
|
continue (2);
|
|
}
|
|
}
|
|
}
|
|
catch (Exception $e)
|
|
{
|
|
}
|
|
|
|
//echo "passConstruct=";print_r ($passConstruct);
|
|
// Check if the method need parameters
|
|
$reflection = new ReflectionMethod ($class, $method);
|
|
$passMethod = array();
|
|
//echo "getParameters=";var_dump ($reflection->getParameters());
|
|
foreach($reflection->getParameters() as $key=>$param)
|
|
{
|
|
if (isset ($matches[$param->getName ()]))
|
|
$passMethod[] = $matches[$param->getName ()];
|
|
elseif (isset ($params[$key]))
|
|
$passMethod[] =str_replace ($search, $replace, $params[$key]);
|
|
elseif ($param->isOptional ())
|
|
$passMethod[] = $matches[$param->getDefaultValue ()];
|
|
else
|
|
{
|
|
if ($this->debug)
|
|
echo "NO MATCH (missing method parameter)\n";
|
|
// Missing parameters : abort this route test
|
|
continue (2);
|
|
}
|
|
}
|
|
|
|
if ($this->debug)
|
|
{
|
|
echo "Call $class(".implode (",", $passConstruct).")->$method(";
|
|
echo implode (",", $passMethod).")\n";
|
|
echo "==== DEBUG : ROUTING END ====</pre>\n";
|
|
}
|
|
|
|
//echo "matches=";print_r ($matches);
|
|
//echo "passMethod=";print_r ($passMethod);
|
|
try
|
|
{
|
|
$refclass = new ReflectionClass ($class);
|
|
$refClassInst = $refclass->newInstanceArgs ($passConstruct);
|
|
$res = $reflection->invokeArgs ($refClassInst, $passMethod);
|
|
}
|
|
catch (Exception $e)
|
|
{
|
|
$res = "ERROR : ". $e->getMessage();
|
|
}
|
|
return $res;
|
|
}
|
|
|
|
if ($this->debug)
|
|
{
|
|
echo "NO MATCH\n";
|
|
}
|
|
}
|
|
if ($this->debug)
|
|
echo "==== DEBUG : ROUTING END : No route found====</pre>\n";
|
|
// TODO : ERROR 404 !
|
|
echo "404";
|
|
return;
|
|
}*/
|
|
|
|
/** Return the HTTP method used to connect to the page
|
|
Can be override by a _METHOD parameter provided in POST
|
|
Throw an exception in case of error */
|
|
public function method ()
|
|
{
|
|
if (isset ($_POST["_METHOD"]) &&
|
|
($_POST["_METHOD"] === "GET" ||
|
|
$_POST["_METHOD"] === "POST" ||
|
|
$_POST["_METHOD"] === "PUT" ||
|
|
$_POST["_METHOD"] === "DELETE"))
|
|
{
|
|
$this->method = $_POST["_METHOD"];
|
|
return $_POST["_METHOD"];
|
|
}
|
|
|
|
if (!isset ($_SERVER["REQUEST_METHOD"]))
|
|
throw new Exception ("No REQUEST_METHOD", 415);
|
|
if ($_SERVER["REQUEST_METHOD"] === "GET" ||
|
|
$_SERVER["REQUEST_METHOD"] === "POST" ||
|
|
$_SERVER["REQUEST_METHOD"] === "PUT" ||
|
|
$_SERVER["REQUEST_METHOD"] === "DELETE")
|
|
{
|
|
$this->method = $_SERVER["REQUEST_METHOD"];
|
|
return $_SERVER["REQUEST_METHOD"];
|
|
}
|
|
|
|
throw new Exception ("Invalid REQUEST_METHOD", 406);
|
|
}
|
|
|
|
/** If the URL is corresponding with $url, and the method is GET, then the
|
|
function is called.
|
|
Ex. : $app->get('/hello/{name}', function ($name) {
|
|
echo "Hello, $name";
|
|
});
|
|
@param string $route Route to check with the URL
|
|
@param function $function Function to be executed if the route match */
|
|
public function get ($route, $function)
|
|
{
|
|
if ($this->debug)
|
|
echo "<pre>==> GET $route ?? ";
|
|
if ($this->method () !== "GET")
|
|
{
|
|
if ($this->debug)
|
|
echo "==> Not a GET Method\n";
|
|
return;
|
|
}
|
|
|
|
return $this->map ($route, $function);
|
|
}
|
|
|
|
/** If the URL is corresponding with $url, and the method is POST, then the
|
|
function is called.
|
|
Ex. : $app->get('/hello/{name}', function ($name) {
|
|
echo "Hello, $name";
|
|
});
|
|
@param string $route Route to check with the URL
|
|
@param function $function Function to be executed if the route match */
|
|
public function post ($route, $function)
|
|
{
|
|
if ($this->debug)
|
|
echo "<pre>==> POST $route ?? ";
|
|
if ($this->method () !== "POST")
|
|
{
|
|
if ($this->debug)
|
|
echo "==> Not a POST Method\n";
|
|
return;
|
|
}
|
|
|
|
return $this->map ($route, $function);
|
|
}
|
|
|
|
/** If the URL is corresponding with $url, and the method is PUT, then the
|
|
function is called.
|
|
Ex. : $app->get('/hello/{name}', function ($name) {
|
|
echo "Hello, $name";
|
|
});
|
|
@param string $route Route to check with the URL
|
|
@param function $function Function to be executed if the route match */
|
|
public function put ($route, $function)
|
|
{
|
|
if ($this->debug)
|
|
echo "<pre>==> PUT $route ?? ";
|
|
if ($this->method () !== "PUT")
|
|
{
|
|
if ($this->debug)
|
|
echo "==> Not a PUT Method\n";
|
|
return;
|
|
}
|
|
|
|
return $this->map ($route, $function);
|
|
}
|
|
|
|
/** If the URL is corresponding with $url, and the method is DELETE, then the
|
|
function is called.
|
|
Ex. : $app->get('/hello/{name}', function ($name) {
|
|
echo "Hello, $name";
|
|
});
|
|
@param string $route Route to check with the URL
|
|
@param function $function Function to be executed if the route match */
|
|
public function delete ($route, $function)
|
|
{
|
|
if ($this->debug)
|
|
echo "<pre>==> DELETE $route ?? ";
|
|
if ($this->method () !== "DELETE")
|
|
{
|
|
if ($this->debug)
|
|
echo "==> Not a DELETE Method\n";
|
|
return;
|
|
}
|
|
|
|
return $this->map ($route, $function);
|
|
}
|
|
|
|
/** Do the mapping between the url and the route : call the function if
|
|
thereis a match
|
|
@param string $route Route to check with the URL
|
|
@param function $function Function to be executed if the route match */
|
|
public function map ($route, $function)
|
|
{
|
|
$url = substr ($this->requestURL (), strlen ($this->baseURLmodule ()));
|
|
if ($this->debug)
|
|
echo "$url ";
|
|
if ($url === $route)
|
|
{
|
|
if ($this->debug)
|
|
echo "==> FOUND EQUAL !\n";
|
|
try
|
|
{
|
|
$function ();
|
|
}
|
|
catch (Exception $e)
|
|
{
|
|
@header ($_SERVER["SERVER_PROTOCOL"]." 500 Internal Server Error");
|
|
// TODO : Allow a specific page to be configured and displayed
|
|
echo "<H1>Internal server error</h1>";
|
|
echo $e->getMessage();
|
|
}
|
|
exit;
|
|
}
|
|
|
|
// URL === REGEXP ROUTE
|
|
// Variables are exposed in url/{var1}/{var2}
|
|
$regex = "#^$route$#U";
|
|
//$regex = str_replace ("?", "\?", $regex);
|
|
//$regex = str_replace (".", "\.", $regex);
|
|
$regex = str_replace ("{", "(?P<", $regex);
|
|
$regex = str_replace ("}", ">.+)", $regex);
|
|
unset ($matches);
|
|
$rcRegex = preg_match ($regex, $url, $matches);
|
|
if ($rcRegex !== FALSE && $rcRegex !== 0)
|
|
{
|
|
if ($this->debug)
|
|
echo "==> FOUND REGEX !\n";
|
|
$params = array ();
|
|
$reflect = new ReflectionFunction ($function);
|
|
foreach ($reflect->getParameters() as $key=>$val)
|
|
{
|
|
if (isset ($matches[$val->name]))
|
|
$params[] = $matches[$val->name];
|
|
else
|
|
$params[] = null;
|
|
}
|
|
|
|
try
|
|
{
|
|
call_user_func_array ($function, $params);
|
|
}
|
|
catch (Exception $e)
|
|
{
|
|
if ($e->getCode () === "")
|
|
$getCode = 500;
|
|
else
|
|
$getCode = $e->getCode ();
|
|
|
|
$http = new http ();
|
|
@header ($_SERVER["SERVER_PROTOCOL"]." $getCode ".$http->codetext ($getCode));
|
|
// TODO : Allow a specific page to be configured and displayed
|
|
echo "<html>\n";
|
|
echo " <head>\n";
|
|
echo " <title>".$http->codetext ($getCode)."</title>\n";
|
|
echo " <meta charset='utf-8'>\n";
|
|
echo " <meta name='ROBOTS' content='NOINDEX, NOFOLLOW'>\n";
|
|
echo " </head>\n";
|
|
echo" <body>\n";
|
|
echo "<h1>".$http->codetext ($getCode)."</h1>";
|
|
echo $e->getMessage();
|
|
echo" </body>\n";
|
|
echo "</html>\n";
|
|
}
|
|
exit;
|
|
}
|
|
|
|
if ($this->debug)
|
|
echo "==> NO MATCH\n";
|
|
}
|
|
}
|