Location: PHPKode > projects > Krai Framework > Krai/Router.php
<?php
/**
 * Krai router class
 * @package Krai
 * @subpackage Router
 * @author Greg McWhirter <hide@address.com>
 * @copyright Copyright (c) 2008, Greg McWhirter
 * @license http://www.opensource.org/licenses/mit-license.php MIT License
 */

Krai::Uses(
  Krai::$FRAMEWORK."/Router/Route.php",
  Krai::$FRAMEWORK."/Router/Exception.php"
);

/**
 * The framework router
 *
 * This class controls parsing request uris into the execution of module actions.
 * It also generates uris from module and action data that will get back to that
 * module and action when re-parsed. It also has functionality to load files in
 * which live certain modules and actions.
 *
 * Implements the Singleton pattern via {@link Krai_Router::Instance()}.
 *
 * @package Krai
 * @subpackage Router
 */
final class Krai_Router
{

  /**
   * Holds the singleton instance
   *
   * This holds the single instance of the routing class.
   *
   * @var Krai_Router
   */
  private static $_instance = null;

  /**
   * Holds the array of defined routes sorted by complexity
   *
   * This is an array of arrays of {@link Krai_Router_Route} instances. It is in
   * levels of complexity to make finding a matching route (perhaps) easier.
   *
   * @var array
   */
  private $_routemap = array();

  /**
   * Holds the array of defined routes in entry order
   *
   * This is an array of {@link Krai_Router_Route} instances sorted in the order
   * they were parsed out of the routes configuration file. It is used for
   * reconstructing routes from module and action data.
   *
   * @var array
   */
  private $_reconstrmap = array();

  /**
   * Flag for whether or not routing has already occurred.
   *
   * This is a flag indicating whether or not {@link Krai_Router::DoRoute()} has
   * been run yet.
   *
   * @var boolean
   */
  private $_routed = false;

  /**
   * Constructor - private to implement singleton pattern
   *
   * This function parses the routes configuration file and stores the route
   * objects.
   *
   * @param string $kvfurl
   *
   */
  private function __construct()
  {
    /* Load the route map */
    $lines = file(Krai::$INCLUDES."/configs/routes.config");
    foreach($lines as $line)
    {
      $line = trim($line);
      if(substr($line,0,1) == "#" || empty($line))
      {
        continue;
      }

      $t = explode("-->", trim($line), 2);
      $pattern = trim($t[0]);
      $actmap = trim((array_key_exists(1, $t)) ? $t[1] : "");
      $pattern = trim(preg_replace(array("#^[/]*#","#[/]*$#"),
                                   array("",""),
                                   $pattern));
      $patparts = (empty($pattern)) ? array() : explode("/", $pattern);
      $actparts = (empty($actmap)) ? array() : explode(",", $actmap);
      $forces = array();

      foreach($actparts as $act)
      {
        list($k,$v) = explode("=", $act);
        $k = trim($k);
        if(substr($k,0,1) == ":")
        {
          $forces[substr($k,1)] = trim($v);
        }
      }

      if(!array_key_exists(count($patparts), $this->_routemap))
      {
        $this->_routemap[count($patparts)] = array();
      }

      $this->_routemap[count($patparts)][] = new Krai_Router_Route($patparts,
                                                                   $forces);
      $this->_reconstrmap[] = new Krai_Router_Route($patparts, $forces);
    }

    $this->_baseuri = Krai::GetConfig("BASEURI") == "" ? "" : "/".
                                                     Krai::GetConfig("BASEURI");
    Krai::WriteLog($this->_baseuri, Krai::LOG_DEBUG);
  }

  /**
   * Singleton pattern constructor / retreiver
   *
   * This function allows the retrieval of the singleton instance of the class.
   *
   * @return Krai_Router The router instance
   */
  public static function &Instance()
  {

    if(!(self::$_instance instanceOf Krai_Router))
    {
      $c = "Krai_Router";
      self::$_instance = new $c();
    }
    return self::$_instance;
  }

  /**
   * Execute a route
   *
   * This function executes a route based on the parsing of the request
   * parameter. It can only be called once, and after that will throw a
   * Krai_Router_Exception.
   *
   * @param string $request The requested uri
   * @throws Krai_Router_Exception
   */
  public function DoRoute($request)
  {
    if(!$this->_routed)
    {
      $request = preg_replace(array("#^[/]*#","#[/]*$#"),
                              array("",""),
                              $request);
      //$request = preg_replace("#\.html$#","", $request);
      $rparts = (empty($request)) ? array() : explode("/", $request);
      if(array_key_exists(count($rparts), $this->_routemap))
      {
        $found = null;
        foreach($this->_routemap[count($rparts)] as $route)
        {
          $t = $route->Matches($rparts);
          if($t!==false)
          {
            $found = $t;
            break;
          }
        }

        if(is_null($found))
        {
          throw new Krai_Router_Exception(
                                "Unable to find a route matching the request.",
                                Krai_Router_Exception::NoRouteFound);
        }
        else
        {
          $this->_routed = true;
          $this->ExecuteRoute($found["module"],
                              $found["action"],
                              $found["params"]);
        }
      }
      else
      {
        throw new Krai_Router_Exception(
                  "Unable to find a route with the proper number of arguments.",
                  Krai_Router_Exception::NoRouteFound);
      }
    }
    else
    {
      throw new Krai_Router_Exception(
                        "Routing has already been performed for this request.",
                        Krai_Router_Exception::RoutingPerformed);
    }
  }

  /**
   * Actually execute a route
   *
   * This function implements the actual execution of a route by instantiating
   * the required module and calling the module's
   * {@link Krai_Module::DoAction()} method.
   *
   * @param string $_module The name of the module to instantiate
   * @param string $_action The name of the action to execute
   * @param array $_params The parameters of the request
   * @throws Krai_Router_Exception
   */
  public function ExecuteRoute($_module, $_action, array $_params = array())
  {
    if(!is_null($_module) && !empty($_action))
    {
      Krai::$PARAMS = array_merge(Krai::$PARAMS, $_params);
      $t = Krai::$INFLECTOR->Underscore2Camel($_module."_module");
      $inst = new $t();
      $inst->DoAction($_action, $_SERVER["REQUEST_METHOD"]);
    }
    else
    {
      throw new Krai_Router_Exception(
                    "Matching route did not yield a module to which to route.",
                    Krai_Router_Exception::NoRouteAction);
    }
  }

  /**
   * Generate the URL for a certain module and action
   *
   * This function generates a uri representing a certain combination of module,
   * action, and parameters which, when parsed, would execute the same.
   *
   * @param string $_module The name of the module
   * @param string $_action The name of the action
   * @param array $_params An array of parameters
   * @param boolean $_forlink Whether or not to encode the uri returned for use
   * in a link
   * @return string The uri (including BASEURI).
   */
  public function UrlFor($_module,
                         $_action,
                         array $_params = array(),
                         $_forlink = true)
  {
    foreach($this->_reconstrmap as $route)
    {
      if($route->MatchUrlFor($_module, $_action, $_params))
      {
        //The route is a match now
        Krai::WriteLog("Matched Route.".serialize($route), Krai::LOG_DEBUG);
        return $this->_baseuri.$route->Reconstruct($_module,
                                                   $_action,
                                                   $_params,
                                                   $_forlink);
      }
    }
  }

}
Return current item: Krai Framework