*/ 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", 508); if (substr_count ($baseURL, "../") > 1+ substr_count ($destURL,"/")) throw new Exception ("Can't redirect outside this site (Base $baseURL)", 405); if ($this->debug) { echo "
\n";
      echo "==== DEBUG : REDIRECT START ====\n";
      echo "BASEURL=$baseURL\n";
      echo "REQUURL=$requestURL\n";
      echo "destURL=$destURL\n";
      echo " --->  Redirect to $destURL\n";
      echo "==== DEBUG : REDIRECT END ====\n";
      echo "
\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 ""; echo "Redirect to $destURL"; } 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 "
==> 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 "
==> 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 "
==> 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 "
==> 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 = 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 ();

    // 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, $e->getMessage());
      exit;
    }

    $http = new http ();
    @header ($_SERVER["SERVER_PROTOCOL"]." $getCode ".
             $http->codetext ($getCode));
    // TODO : Allow a specific page to be configured and displayed
    echo "\n";
    echo "  \n";
    echo "    ".$http->codetext ($getCode)."\n";
    echo "    \n";
    echo "    \n";
    echo "  \n";
    echo"   \n";
    echo "

".$http->codetext ($getCode)."

"; echo $e->getMessage(); echo" \n"; echo "\n"; exit; } }