Files
DomFramework/route.php
2015-08-10 14:11:53 +00:00

475 lines
15 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
/** Allow slashes in the url when matching the regex */
public $allowSlashes = true;
// Provide the the class catch errors in routing.
// Must be provided in an array(class,method);
public $errors = null;
/** Preroute used in modules */
public $preroute = "";
/** 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, $absolute=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 ($absolute === true)
{
$this->baseURL = "";
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 .= "/";
}
elseif (isset ($_SERVER["REQUEST_URI"]) &&
strpos ($_SERVER["REQUEST_URI"], "index.php?url=") !== false)
{
$this->baseURL = "";
}
else
{
// Calculate the root in relative
$request = $this->requestURL ();
$this->baseURL = str_repeat ("../",
substr_count ($request, "/")).
$this->baseURL;
if ($this->baseURL === "")
$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 for a resource (add a index.php?url= if there is no
mod_rewrite support. Used to link to modules in the HTML page.
The baseURL is the real base of the project, which is different when not
using the mod_rewrite. They are equal when using the mod_rewrite */
function baseURLresource ()
{
if ($this->baseURL === "")
$this->baseURL ();
if (isset ($_SERVER["REQUEST_URI"]) &&
strpos ($_SERVER["REQUEST_URI"], "index.php?url=") !== false)
return "index.php?url=".$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 ($absolute = false)
{
$url = "";
if ($absolute === true)
{
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"]))
{
// If there is a directory before the index.php file, must remove the
// directory structure
if (dirname ($_SERVER["SCRIPT_NAME"]) !== "/")
$url .= substr ($_SERVER["REQUEST_URI"],
1+strlen (dirname ($_SERVER["SCRIPT_NAME"])));
else
// If there is no directory before the index.php (root of the Web server),
// just remove the /
$url = substr ($_SERVER["REQUEST_URI"], 1);
}
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 ();
if ($module !== "")
$baseURL .= $module."/";
$requestURL = $this->requestURL ();
if (substr ($requestURL, -1) !== "/")
$requestURL .= "/";
if ($destURL[0] === "/")
{
// Absolute : return to project base
$destURL = $baseURL.substr ($destURL, 1);
}
if (strpos ($requestURL, "index.php?url=") !== false)
{
// If not using the mod_rewrite, force the index.php?url=
$destURL = substr ($destURL, strlen ($baseURL));
$destURL = "index.php?url=".$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", 400);
if (substr_count ($baseURL, "../") > 1+ substr_count ($destURL,"/"))
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;
}
/** 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)
{
$route = $this->preroute.$route;
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)
{
$route = $this->preroute.$route;
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)
{
$route = $this->preroute.$route;
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)
{
$route = $this->preroute.$route;
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);
}
/** Allow multiple methods to execute the function if the route is
corresponding to the URL.
Ex. : $app->multi("get,post,put,delete", '/hello/{name}', function ($name) {
echo "Hello, $name";
});
@param string $methods The allowed methods, separated by commas (,)
@param string $route Route to check with the URL
@param function $function Function to be executed if the route match */
public function multi ($methods, $route, $function)
{
foreach (explode (",", $methods) as $method)
{
$this->$method ($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 = str_replace ("index.php?url=", "", $this->requestURL ());
if ($this->debug)
echo "$url ";
if ($url === $route)
{
if ($this->debug)
echo "==> FOUND EQUAL !\n";
try
{
$function ();
}
catch (Exception $e)
{
$this->error ($e);
}
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);
if ($this->allowSlashes)
$regex = str_replace ("}", ">.+)", $regex);
else
$regex = str_replace ("}", ">[^/]+)", $regex);
unset ($matches);
//echo "REGEX=".htmlentities ($regex)." ? URL=".htmlentities ($url);
$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[] = urldecode ($matches[$val->name]);
else
$params[] = null;
}
try
{
call_user_func_array ($function, $params);
}
catch (Exception $e)
{
$this->error ($e);
}
exit;
}
if ($this->debug)
echo "==> NO MATCH\n";
}
/** Print an error page. If a custom page exists, use it
@param $e Exception to print */
public function error ($e)
{
if ($e->getCode () === "" || $e->getCode () === 0)
$getCode = 500;
else
$getCode = $e->getCode ();
$message = $e->getMessage ();
if ($e->getCode () === 0)
$message .= "<p>The Exception code was 0 and converted to 500</p>\n";
// If an error class/method is defined, use it in place of the default
// one.
// The errors must be defined in an array("class", "method");
if ($this->errors !== null)
{
if (! is_array ($this->errors))
die ("The route::\$errors is not an array\n");
if (! isset ($this->errors[0]) || ! isset ($this->errors[1]))
die ("The route::\$errors is not correct : must be the class and ".
"the method in an array\n");
$a = new $this->errors[0];
$method = $this->errors[1];
$a->$method ($getCode, $message);
exit;
}
$http = new http ();
@header ($_SERVER["SERVER_PROTOCOL"]." $getCode ".
$http->codetext ($getCode));
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 $message;
echo" </body>\n";
echo "</html>\n";
exit;
}
}