Updated Route, adding get/set/all methods to act as a singleton location for Route storage. Created initial Request class, the replacement for Router/Input.

This commit is contained in:
Woody Gilk 2009-02-24 01:17:28 -06:00
parent 020a5dde44
commit 12271c68dc
2 changed files with 245 additions and 73 deletions

107
system/classes/request.php Normal file
View file

@ -0,0 +1,107 @@
<?php defined('SYSPATH') or die('No direct script access.');
class Request_Core {
public static $routes = array();
public static function factory($uri)
{
return new Requst($uri);
}
protected static $_instance;
public static function instance($uri = NULL)
{
if (Request::$_instance === NULL)
{
// Create the instance singleton
Request::$_instance = new Request($uri);
}
return Request::$_instance;
}
public $route;
public $status = 200;
public $response = '';
public $abort = FALSE;
protected $_uri;
protected $_params;
protected $_get;
protected $_post;
public function __construct($uri, array $get = NULL, array $post = NULL)
{
// Remove trailing slashes from the URI
$uri = trim($uri, '/');
// Load routes
$routes = Route::all();
foreach ($routes as $name => $route)
{
if ($params = $route->matches($uri))
{
$this->route = $route;
$this->_uri = $uri;
$this->_params = $params;
$this->_get = ($get === NULL) ? $_GET : $get;
$this->_post = ($post === NULL) ? $_POST : $get;
return;
}
}
throw new Exception('Unable to find a route to handle '.$uri);
}
public function get($key, $default = NULL)
{
return isset($this->_get[$key]) ? $this->_get[$key] : $default;
}
public function post($key, $default = NULL)
{
return isset($this->_post[$key]) ? $this->_post[$key] : $default;
}
public function param($key, $default = NULL)
{
return isset($this->_params[$key]) ? $this->_params[$key] : $default;
}
public function process()
{
$params = $this->_params;
$controller = 'controller_'.$params['controller'];
$action = 'action_'.$params['action'];
// Remove the controller and action from the params
unset($params['controller'], $params['action']);
// Load the controller
$controller = new $controller($this);
// A new action is about to be run
$controller->before();
if ($this->status === 200)
{
// Execute the action
$controller->$action();
// The action has been run
$controller->after();
}
echo $this->response;
}
} // End Request

View file

@ -36,26 +36,15 @@
*/
class Route_Core {
const REGEX_KEY = ':[a-zA-Z0-9_]++';
const REGEX_KEY = '<([a-zA-Z0-9_]++)>';
const REGEX_SEGMENT = '[^/.,;?]++';
const REGEX_ESCAPE = '[.\\+*?[^\\]${}=!<>|]';
const REGEX_ESCAPE = '[.\\+*?[^\\]${}=!|]';
/**
* Returns a new Route object.
*
* @chainable
* @param string route URI
* @param array regular expressions for keys
* @return Route
*/
public static function factory($uri, array $regex = array())
{
return new Route($uri, $regex);
}
protected static $_routes = array();
/**
* Called when the object is re-constructed from the cache.
*
*
* @param array cached values
* @return Route
*/
@ -70,17 +59,52 @@ class Route_Core {
return $route;
}
/**
* Stores a named route and returns it.
*
* @param string route name
* @param string URI pattern
* @param array regex patterns for route keys
* @return Route
*/
public static function set($name, $uri, array $regex = NULL)
{
return Route::$_routes[$name] = new Route($uri, $regex);
}
/**
* Retrieves a named route.
*
* @param string route name
* @return Route
* @return FALSE when no route is found
*/
public static function get($name)
{
return isset(Route::$_routes[$name]) ? Route::$_routes[$name] : FALSE;
}
/**
* Retrieves all named routes, with the default route last.
*
* @return array named routes
*/
public function all()
{
return Route::$_routes;
}
// Route URI string
protected $uri = '';
protected $_uri = '';
// Regular expressions for route keys
protected $regex = array();
protected $_regex = array();
// Default values for route keys
protected $defaults = array('method' => 'index');
protected $_defaults = array('method' => 'index');
// Compiled regex cache
protected $compiled;
protected $_route_regex;
/**
* Creates a new route. Sets the URI and regular expressions for keys.
@ -88,27 +112,25 @@ class Route_Core {
* @param string route URI pattern
* @param array key patterns
*/
public function __construct($uri, array $regex = array())
public function __construct($uri, array $regex = NULL)
{
if ( ! empty($regex))
{
$this->regex = $regex;
}
$this->_regex = $regex;
// Store the routed URI
$this->uri = $uri;
// Store the URI that this route will match
$this->_uri = $uri;
if (($regex = Kohana::cache('kohana_route_regex_'.$uri)) === NULL)
if (($regex = Kohana::cache('kohana_route:'.$uri)) === NULL)
{
// Compile the complete regex for this uri
$regex = $this->compile();
$regex = $this->_compile();
// Cache the compiled regex
Kohana::cache('kohana_route_regex_'.$uri, $regex);
Kohana::cache('kohana_route:'.$uri, $regex);
}
// Store the compiled regex locally
$this->compiled = $regex;
$this->_route_regex = $regex;
}
/**
@ -121,14 +143,14 @@ class Route_Core {
* @param array key values
* @return Route
*/
public function defaults(array $defaults)
public function defaults(array $defaults = NULL)
{
if (empty($defaults['method']))
if (empty($defaults['action']))
{
$defaults['method'] = 'index';
$defaults['action'] = 'index';
}
$this->defaults = $defaults;
$this->_defaults = $defaults;
return $this;
}
@ -139,7 +161,7 @@ class Route_Core {
* boolean FALSE.
*
* // This route will only match if the :controller, :method, and :id exist
* $params = Route::factory(':controller/:method/:id', array('id' => '\d+'))
* $params = Route::factory('<controller>/<method>/<id>', array('id' => '\d+'))
* ->match('users/edit/10');
* // The parameters are now:
* // controller = users
@ -159,36 +181,83 @@ class Route_Core {
*/
public function matches($uri)
{
if (preg_match('#'.$this->compiled.'#', $uri, $matches))
{
$params = array();
foreach ($matches as $key => $value)
{
if (is_int($key))
{
// Skip all unnamed keys
continue;
}
if ( ! preg_match($this->_route_regex, $uri, $matches))
return FALSE;
// Set the value for all matched keys
$params = array();
foreach ($matches as $key => $value)
{
if (is_int($key))
{
// Skip all unnamed keys
continue;
}
// Set the value for all matched keys
$params[$key] = $value;
}
foreach ($this->_defaults as $key => $value)
{
if ( ! isset($params[$key]))
{
// Set default values for any key that was not matched
$params[$key] = $value;
}
}
foreach ($this->defaults as $key => $value)
return $params;
}
/**
* Generates a URI for the current route based on the parameters given.
*
* @param array URI parameters
* @return string
* @throws Kohana_Exception when the URI will not match the current route
*/
public function uri(array $params = NULL)
{
if ($params === NULL)
$params = $this->_defaults;
// Start with the routed URI
$uri = $this->_uri;
if (strpos($uri, '<') === FALSE AND strpos('(', $this->uri) === FALSE)
{
// This is a static route, no need to replace anything
return $uri;
}
if (preg_match_all('#'.Route::REGEX_KEY.'#', $uri, $keys))
{
foreach ($keys[1] as $key)
{
if ( ! isset($params[$key]))
{
// Set default values for any key that was not matched
$params[$key] = $value;
}
$search[] = "<$key>";
$replace[] = isset($params[$key]) ? $params[$key] : '';
}
return $params;
// Replace all the variable keys in the URI
$uri = str_replace($search, $replace, $uri);
}
else
if (strpos($uri, '(') !== FALSE)
{
return FALSE;
// Remove all groupings from the URI
$uri = str_replace(array('(', ')'), '', $uri);
}
// Trim off extra slashes
$uri = rtrim($uri, '/');
if ( ! preg_match($this->_route_regex, $uri))
{
// This will generally happen with the user supplies invalid parameters
throw new Exception('The generated URI "'.$uri.'" will not be matched by "'.$this->_uri.'"');
}
return $uri;
}
/**
@ -197,39 +266,35 @@ class Route_Core {
*
* @return string
*/
protected function compile()
protected function _compile()
{
// The URI should be considered literal except for keys and optional parts
// Escape everything preg_quote would escape except for : ( )
$this->uri = preg_replace('#'.Route::REGEX_ESCAPE.'#', '\\\\$0', $this->uri);
// Escape everything preg_quote would escape except for : ( ) < >
$regex = preg_replace('#'.Route::REGEX_ESCAPE.'#', '\\\\$0', $this->_uri);
if (strpos($this->uri, '(') === FALSE)
{
// No optional parts of the URI
$regex = $this->uri;
}
else
if (strpos($regex, '(') !== FALSE)
{
// Make optional parts of the URI non-capturing and optional
$regex = str_replace(array('(', ')'), array('(?:', ')?'), $this->uri);
$regex = str_replace(array('(', ')'), array('(?:', ')?'), $regex);
}
// Insert default regex for keys
$regex = str_replace(array('<', '>'), array('(?P<', '>'.self::REGEX_SEGMENT.')'), $regex);
$regex = str_replace(array('<', '>'), array('(?P<', '>'.Route::REGEX_SEGMENT.')'), $regex);
// Replace default regex patterns with user-specified patterns
if (count($this->regex))
if ( ! empty($this->_regex))
{
$replace = array();
foreach ($this->regex as $key => $value)
$search = $replace = array();
foreach ($this->_regex as $key => $value)
{
$search = "<$key>".self::REGEX_SEGMENT;
$replace[$search] = "<$key>$value";
$search[] = "<$key>".Route::REGEX_SEGMENT;
$replace[] = "<$key>$value";
}
$regex = strtr($regex, $replace);
// Replace the default regex with the user-specified regex
$regex = str_replace($search, $replace, $regex);
}
return '^'.$regex.'$';
return '#^'.$regex.'$#';
}
} // End Kohana_Route