All pastes #1364968 Raw Edit

Unnamed

public php v1 · immutable
#1364968 ·published 2009-03-19 05:24 UTC
rendered paste body
<?php/** * @package lib374c *//** * Lib374C_Exception */require_once 'Lib374C/Exception.php';/** * Lib374C_MVC_Exception */require_once 'Lib374C/MVC/Exception.php';/** * URL routing class * * @package lib374c */class Lib374C_MVC_URLRouter{    protected static $_instance;  protected $_routes = array();  protected $_defaultRoute = null;  protected $_defaultRouteParsed = null;  protected $_baseUrl = '';    /**   * Connect a url to some parameters   *    * @param string $url   * @param array $defaults   */  public function connect($url, $defaults = array(), $requirements = array())  {    $url = preg_replace('|^/|', '', $url);        // parse route    extract($this->_parseRoute($url, $defaults, $requirements)); // $regex, $begin_regex, $param_names, $offsets        // store route    $route = new StdClass;    $route->url = $url;    $route->regex = $regex;    $route->begin_regex = $begin_regex;    $route->defaults = $defaults;    $route->param_names = $param_names;    $route->requirements = $requirements;    $route->offsets = $offsets;    array_unshift($this->_routes, $route);  }    /**   * Parse a route.   *    * returns: array(   *   'regex'        => '`^...$`i',   *   'begin_regex'  => '`^...(?:/\|$)`i',   *   'param_names'  => array('a', 'b', 'c'),   *   'offsets'      => array(   *     array(0, 10), // $offset, $length   *     array(16, 2),   *   ),   * )   *    * @param string $route   * @param array $defaults   * @param array $requirements   * @return array   */  protected function _parseRoute($route, $defaults = array(), $requirements = array())  {    $regex = str_replace("\\:", ":", preg_quote($route, '`'));    preg_match_all('|:([0-9A-Za-z_]+)|', $regex, $url_param_matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE);    $offset_adjust = 0;    $param_names = array();    $offsets = array();        foreach($url_param_matches as $match)    {      $param = new StdClass;      $param->name = $match[1][0];      $param->offset = $match[0][1];      $param->length = strlen($match[0][0]);            // check duplicate      if(in_array($param->name, $param_names))        throw new Lib374C_MVC_Exception('Duplicate param in url: [' . $param->name . ']');      if(isset($defaults[$param->name]))        throw new Lib374C_MVC_Exception('Cannot set default value for parameter defined in URL');            // generate regex      $old_length = strlen($regex);      $regex = substr_replace($regex, '(?<' . $param->name . '>[^/]*?)', $param->offset + $offset_adjust, $param->length);      $new_length = strlen($regex);      $offset_adjust += $new_length - $old_length;            // store offset      $offsets[$param->name] = array($param->offset, $param->length);            $param_names[] = $param->name;    }        $begin_regex = '`^' . $regex . '(?<__REST__>/.*|)$`i';    $regex = '`^' . $regex . '$`i';       foreach(array_keys($requirements) as $key)      if(!in_array($key, $param_names))        throw new Lib374C_MVC_Exception('Can\'t set requirement for non-existent param');        return array(      'regex'         => $regex,      'begin_regex'   => $begin_regex,      'param_names'   => $param_names,      'offsets'       => $offsets,    );  }    /**   * Route a url   *   * @param string $url    * @return array   */  public function route($url)  {    $url = preg_replace('|^/|', '', $url);        foreach($this->_routes as $route)    {      if(preg_match($route->regex, $url, $matches))      {        $params = $route->defaults;                // get parameters        foreach($route->param_names as $name)        {          $params[$name] = urldecode($matches[$name]);        }                // check requirements        foreach($route->requirements as $name => $req)        {          if(!preg_match($req, $params[$name]))          {            continue 2;          }        }        return $params;      }    }        if($this->_defaultRoute !== null && preg_match($this->_defaultRouteParsed['begin_regex'], $url, $matches))    {      $params = array();      foreach($this->_defaultRouteParsed['param_names'] as $name) $params[$name] = $matches[$name];      $rest = explode("/", $matches['__REST__']);            foreach($rest as $rest_val)      {        if(strpos($rest_val, '=') === false) continue;        list($key, $val) = array_map('urldecode', explode('=', $rest_val, 2));        $params[$key] = $val;      }            return $params;    }        throw new Lib374C_MVC_Route_Not_Found_Exception('Couldn\'t route that URL');  }    /**   * Generate a url   *    * @param array $params   * @return string   */  public function assemble($params)  {    $keys = array_keys($params);        foreach($this->_routes as $route)    {            foreach($keys as $key)        if(!in_array($key, $route->param_names) && !(isset($route->defaults[$key]) && $route->defaults[$key] == $params[$key]))          continue 2; // next route            foreach($route->requirements as $key => $req)        if(!preg_match($req, $params[$key]))          continue 2; // next route            $output = $route->url;      $offset_adjust = 0;      foreach($params as $key => $value)      {        if(!isset($route->defaults[$key]))        {          $old_length = strlen($output);          $output = substr_replace($output, urlencode($value), $route->offsets[$key][0] + $offset_adjust, $route->offsets[$key][1]);          $offset_adjust += strlen($output) - $old_length;        }      }            return $this->_baseUrl . $output;    }        if($this->_defaultRoute !== null)    {      // all required parameters given?      foreach($this->_defaultRouteParsed['param_names'] as $param_name)        if(!isset($params[$param_name]))          throw new Lib374C_MVC_Exception('Specify param [' . $param_name . ']');           // generate url      $url = $this->_defaultRoute;      $offset_adjust = 0;            foreach($this->_defaultRouteParsed['offsets'] as $param_name => $value)      {        list($offset, $length) = $value;        $old_length = strlen($url);        $url = substr_replace($url, urlencode($params[$param_name]), $offset + $offset_adjust, $length);        $offset_adjust += strlen($url) - $old_length;        unset($params[$param_name]);      }            foreach($params as $key => $value) $url .= "/" . urlencode($key) . "=" . urlencode($value);      return $this->_baseUrl . $url;    }    else    {      throw new Lib374C_MVC_Exception('No such route, and no default route set');    }  }    /**   * Set or remove default route   *    * @param string|null $route   */  public function setDefaultRoute($route)  {    if($route !== null) $route = preg_replace('|^/*(.*?)/*$|', '$1', $route);    $this->_defaultRoute = $route;    if($route !== null) $this->_defaultRouteParsed = $this->_parseRoute($route);  }  /**   * Set the instance retrieved by getInstance()   *    * @param Lib374C_MVC_URLRouter $router   */  public static function setInstance(Lib374C_MVC_URLRouter $router)  {    self::$_instance = $router;  }    /**   * Get instance set by setInstance()   *    * @return Lib374C_MVC_URLRouter   */  public static function getInstance()  {    if(!isset(self::$_instance)) throw new Lib374C_MVC_Exception('Use setInstance($router) before calling getInstance()');    return self::$_instance;  }    /**   * Set base url   *    * @param string $url   */  public function setBaseUrl($url)  {    $this->_baseUrl = $url;  }  /**   * Connect routes from array.   *    * array(   *   'route1' => array(   *     'url'          => 'test/:param',   *     'params' => array( // optional   *       'controller' => 'testController',   *       'action'     => 'testAction',   *     ),   *     'requirements' => array( // optional   *       'param'      => '/^\d+$/',   *     ),   *   ),   * )   *    * @param array $routes   */  public function routesFromArray($routes)  {    foreach(array_reverse($routes) as $key => $route)    {      if(!isset($route['url'])) throw new Lib374C_MVC_Exception("routes[$key]: [url] not set");      if(!isset($route['params'])) $route['params'] = array();      if(!isset($route['requirements'])) $route['requirements'] = array();            $this->connect($route['url'], $route['params'], $route['requirements']);    }  }}