summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--system/classes/route.php327
-rw-r--r--tests/system/routeTest.php14
2 files changed, 215 insertions, 126 deletions
diff --git a/system/classes/route.php b/system/classes/route.php
index 3eadb1f..244a0c7 100644
--- a/system/classes/route.php
+++ b/system/classes/route.php
@@ -1,127 +1,202 @@
-<?php
-
-/**
- * Routing class to extract and parse request parameters from the URL.
- * @package Core
- */
-class Route {
-
- /**
- * Name of the route.
- * @var string
- * @access public
- */
- public $name;
-
- /**
- * Extracted parameters
- * @var array
- * @access public
- */
- public $params=array();
-
- /**
- * Associative array of routes.
- * @var array
- * @access private
- * @static
- */
- private static $rules=array();
-
- /**
- * Ads a route
- *
- * @param string $name Name of the route. Routes with the same name will override one another.
- * @param mixed $rule Either an expression to match URI against or a function that will
- * be passed the URI and must return either an associative array of
- * extracted parameters (if it matches) or False.
- * @param array $defaults An associated array of default values.
- * @return void
- * @access public
- * @static
- */
- public static function add($name, $rule, $defaults = array()) {
- Route::$rules[$name]=array(
- 'rule'=>$rule,
- 'defaults'=>$defaults
- );
- }
-
- /**
- * Gets route by name
- *
- * @param string $name Route name
- * @return Route
- * @access public
- * @throws Exception If specified route doesn't exist
- * @static
- */
- public static function get($name) {
- if (!isset(Route::$rules[$name]))
- throw new Exception("Route {$name} not found.");
- $route = new Route();
- $route-> name = $name;
- return $route;
- }
-
- /**
- * Matches the URI against available routes to find the correct one.
- *
- * @param string $uri Request URI
- * @return Route
- * @access public
- * @throws Exception If no route matches the URI
- * @throws Exception If route matched but no Controller was defined for it
- * @throws Exception If route matched but no action was defined for it
- * @static
- */
- public static function match($uri) {
- $matched = false;
- foreach(Route::$rules as $name=>$rule) {
- $rule=$rule['rule'];
- if (is_callable($rule)) {
- if (($data = $rule($uri)) !== FALSE) {
- $matched = $name;
- break;
- }
- }else {
- $pattern = is_array($rule)?$rule[0]:$rule;
- $pattern = str_replace(')', ')?', $pattern);
-
- $pattern=preg_replace_callback('/<.*?>/',
- function($str) use ($rule){
- $str=$str[0];
- $regexp='[a-zA-Z0-9\-\._]+';
- if(is_array($rule))
- $regexp=Misc::arr($rule[1],str_replace(array('<','>'),'',$str),$regexp);
- return '(?P'.$str.$regexp.')';
- },$pattern);
-
- preg_match('#^'.$pattern.'/?$#',$uri,$match);
- if(!empty($match[0])){
- $matched=$name;
- $data=array();
- foreach($match as $k=>$v)
- if(!is_numeric($k))
- $data[$k]=$v;
- break;
- }
- }
- }
- if($matched==false)
- throw new Exception('No route matched your request',404);
- $rule=Route::$rules[$matched];
-
- $params=array_merge($rule['defaults'],$data);
-
- if(!isset($params['controller']))
- throw new Exception("Route {$matched} matched, but no controller was defined for this route",404);
- if(!isset($params['action']))
- throw new Exception("Route {$matched} matched with controller {$params['controller']}, but no action was defined for this route",404);
-
- $route=Route::get($matched);
- $route->params=$params;
- return $route;
- }
-
+<?php
+
+/**
+ * Routing class to extract and parse request parameters from the URL.
+ * @package Core
+ */
+class Route {
+
+ /**
+ * Name of the route.
+ * @var string
+ * @access public
+ */
+ public $name;
+
+ /**
+ * Rule for this route.
+ * @var mixed
+ * @access public
+ */
+ public $rule;
+
+ /**
+ * Default parameters for this route.
+ * @var mixed
+ * @access public
+ */
+ public $defaults;
+
+ /**
+ * Extracted parameters
+ * @var array
+ * @access public
+ */
+ public $params=array();
+
+ /**
+ * Associative array of route rules.
+ * @var array
+ * @access private
+ * @static
+ */
+ private static $rules=array();
+
+ /**
+ * Associative array of route instances.
+ * @var array
+ * @access private
+ * @static
+ */
+ private static $routes = array();
+
+ /**
+ * Constructs a route.
+ *
+ * @param string $name Name of the route
+ * @param mixed $rule Rule for this route
+ * @param array $defaults Default parameters for the route
+ * @return Route Initialized Route
+ * @access protected
+ */
+ protected function __construct($name, $rule, $defaults) {
+ $this->name = $name;
+ $this->rule = $rule;
+ $this->defaults = $defaults;
+ }
+
+ /**
+ * Generates a url for a route
+ *
+ * @param array $params Parameters to substitute in the route
+ * @param bool $absolute Whether to return an absolute url
+ * @param string $protocol Protocol to use for absolute url
+ * @return string Generated url
+ * @access public
+ */
+ public function url($params = array(), $absolute = false, $protocol = 'http') {
+ if (is_callable($this->rule))
+ throw new Exception("The rule for '{$this->name}' route is a function and cannot be reversed");
+
+ $url = is_array($this->rule)?$this->rule[0]:$this->rule;
+
+ $replace = array();
+ $params = array_merge($this->defaults, $params);
+ foreach($params as $key => $value)
+ $replace["<{$key}>"] = $value;
+ $url = str_replace(array_keys($replace), array_values($replace), $url);
+
+ $count = 1;
+ $chars='[^\(\)]*?';
+ while($count>0)
+ $url = preg_replace("#\({$chars}<{$chars}>{$chars}\)#", '', $url, -1, $count);
+
+ $url = str_replace(array('(', ')'), '', $url);
+
+ if ($absolute)
+ $url = $protocol.'://'.$_SERVER['HTTP_HOST'].$url;
+
+ return $url;
+ }
+
+ /**
+ * Ads a route
+ *
+ * @param string $name Name of the route. Routes with the same name will override one another.
+ * @param mixed $rule Either an expression to match URI against or a function that will
+ * be passed the URI and must return either an associative array of
+ * extracted parameters (if it matches) or False.
+ * @param array $defaults An associated array of default values.
+ * @return void
+ * @access public
+ * @static
+ */
+ public static function add($name, $rule, $defaults = array()) {
+ Route::$rules[$name]=array(
+ 'rule'=>$rule,
+ 'defaults'=>$defaults
+ );
+ }
+
+ /**
+ * Gets route by name
+ *
+ * @param string $name Route name
+ * @return Route
+ * @access public
+ * @throws Exception If specified route doesn't exist
+ * @static
+ */
+ public static function get($name) {
+ if (!isset(Route::$rules[$name]))
+ throw new Exception("Route {$name} not found.");
+
+ if (!isset(Route::$routes[$name])) {
+ $rules = Route::$rules[$name];
+ Route::$routes[$name] = new static($name, $rules['rule'], $rules['defaults']);
+ }
+
+ return Route::$routes[$name];
+ }
+
+ /**
+ * Matches the URI against available routes to find the correct one.
+ *
+ * @param string $uri Request URI
+ * @return Route
+ * @access public
+ * @throws Exception If no route matches the URI
+ * @throws Exception If route matched but no Controller was defined for it
+ * @throws Exception If route matched but no action was defined for it
+ * @static
+ */
+ public static function match($uri) {
+ $matched = false;
+ foreach(Route::$rules as $name=>$rule) {
+ $rule=$rule['rule'];
+ if (is_callable($rule)) {
+ if (($data = $rule($uri)) !== FALSE) {
+ $matched = $name;
+ break;
+ }
+ }else {
+ $pattern = is_array($rule)?$rule[0]:$rule;
+ $pattern = str_replace(')', ')?', $pattern);
+
+ $pattern=preg_replace_callback('/<.*?>/',
+ function($str) use ($rule){
+ $str=$str[0];
+ $regexp='[a-zA-Z0-9\-\._]+';
+ if(is_array($rule))
+ $regexp=Misc::arr($rule[1],str_replace(array('<','>'),'',$str),$regexp);
+ return '(?P'.$str.$regexp.')';
+ },$pattern);
+
+ preg_match('#^'.$pattern.'/?$#',$uri,$match);
+ if(!empty($match[0])){
+ $matched=$name;
+ $data=array();
+ foreach($match as $k=>$v)
+ if(!is_numeric($k))
+ $data[$k]=$v;
+ break;
+ }
+ }
+ }
+ if($matched==false)
+ throw new Exception('No route matched your request',404);
+ $rule=Route::$rules[$matched];
+
+ $params=array_merge($rule['defaults'],$data);
+
+ if(!isset($params['controller']))
+ throw new Exception("Route {$matched} matched, but no controller was defined for this route",404);
+ if(!isset($params['action']))
+ throw new Exception("Route {$matched} matched with controller {$params['controller']}, but no action was defined for this route",404);
+
+ $route=Route::get($matched);
+ $route->params=$params;
+ return $route;
+ }
+
} \ No newline at end of file
diff --git a/tests/system/routeTest.php b/tests/system/routeTest.php
index 1cda47b..dc22931 100644
--- a/tests/system/routeTest.php
+++ b/tests/system/routeTest.php
@@ -107,4 +107,18 @@ class RoteTest extends PHPUnit_Framework_TestCase
$this->assertEquals('test', $route-> params['alpha']);
$this->assertEquals(123,$route->params['num']);
}
+
+ /**
+ * @covers Route::url
+ */
+ public function testUrl()
+ {
+ Route::add('url', '(/<controller>(/<action>(/<id>)))', array(
+ 'controller' => 'home',
+ 'action' => 'index'
+ ));
+ $route = Route::get('url');
+ $this->assertEquals('/home/index/5', $route-> url(array('id' => 5)));
+ $this->assertEquals('/home/index', $route->url());
+ }
}