*/ error_reporting (E_ALL); /** 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["SCRIPT_NAME"])) $this->baseURL = dirname ($_SERVER["SCRIPT_NAME"]); if (isset ($_SERVER["SERVER_NAME"])) $this->baseURL = "//".$_SERVER["SERVER_NAME"].":".$_SERVER["SERVER_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_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 @param string $destURL Do a redirection of the HTTP page @param string $module The 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 "
\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"); header ("Location: $destURL"); } else { echo ""; echo "Redirect to $destURL"; } 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 "
==== 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";
        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 ====
\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 ====\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====\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 "
==> 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 "
==> 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 "
==> 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 "
==> 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 "

Internal server error

"; 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) { @header ($_SERVER["SERVER_PROTOCOL"]." 500 Internal Server Error"); // TODO : Allow a specific page to be configured and displayed echo "

Internal server error

"; echo $e->getMessage(); } exit; } if ($this->debug) echo "==> NO MATCH\n"; } }