Files
DomFramework/route.php
2014-02-27 08:47:54 +00:00

392 lines
12 KiB
PHP

<?php
error_reporting (E_ALL);
class route
{
private $baseURL = "";
private $baseURLmodule = "";
public $module = NULL;
public $debug=0; // 0:NoDebug, 1:routing, 2:more debug (developpement)
//public $defaultOutput = "html"; // Default renderer : html
function __construct ()
{
}
/** Return the baseURL of the site
Always finish with a slash */
function baseURL ($module = FALSE)
{
if ($this->module === NULL)
$this->module = $module;
if ($this->baseURL !== "")
return $this->baseURL;
if (isset ($_SERVER["SCRIPT_NAME"]))
$this->baseURL = dirname ($_SERVER["SCRIPT_NAME"]);
if (isset ($_SERVER["SERVER_NAME"]))
$this->baseURL = "//".$_SERVER["SERVER_NAME"].$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;
}
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_NAME"]))
$url .= "//".$_SERVER["SERVER_NAME"];
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 */
function redirect ($destURL, $module)
{
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);
if ($destURL === $requestURL)
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");
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) */
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;
}
}