Moving matching and parsing methods into RouterRoute.

This commit is contained in:
mark_story 2009-10-02 01:09:38 -04:00
parent fec83abd90
commit 6da1ab9c30
2 changed files with 204 additions and 24 deletions

View file

@ -457,7 +457,7 @@ class Router {
if (count($route) === 3) { if (count($route) === 3) {
$route = $_this->compile($i); $route = $_this->compile($i);
} }
//switches to parse() in RouterRoute
if (($r = $_this->__matchRoute($route, $url)) !== false) { if (($r = $_this->__matchRoute($route, $url)) !== false) {
$_this->__currentRoute[] = $route; $_this->__currentRoute[] = $route;
list($route, $regexp, $names, $defaults, $params) = $route; list($route, $regexp, $names, $defaults, $params) = $route;
@ -881,6 +881,7 @@ class Router {
if (isset($route[4]['persist'], $_this->__params[0])) { if (isset($route[4]['persist'], $_this->__params[0])) {
$url = array_merge(array_intersect_key($params, Set::combine($route[4]['persist'], '/')), $url); $url = array_merge(array_intersect_key($params, Set::combine($route[4]['persist'], '/')), $url);
} }
// replace with RouterRoute::match
if ($match = $_this->mapRouteElements($route, $url)) { if ($match = $_this->mapRouteElements($route, $url)) {
$output = trim($match, '/'); $output = trim($match, '/');
$url = array(); $url = array();
@ -1447,7 +1448,7 @@ class RouterRoute {
* *
* @var array * @var array
**/ **/
var $names = array(); var $keys = array();
/** /**
* An array of additional parameters for the Route. * An array of additional parameters for the Route.
* *
@ -1461,11 +1462,18 @@ class RouterRoute {
*/ */
var $defaults = array(); var $defaults = array();
/** /**
* The routes pattern string. * The routes template string.
* *
* @var string * @var string
**/ **/
var $pattern = null; var $template = null;
/**
* Is this route a greedy route? Greedy routes have a `/*` in their
* template
*
* @var string
**/
var $_greedy = false;
/** /**
* The compiled route regular expresssion * The compiled route regular expresssion
* *
@ -1486,13 +1494,13 @@ class RouterRoute {
/** /**
* Constructor for a Route * Constructor for a Route
* *
* @param string $pattern Pattern string with parameter placeholders * @param string $template Template string with parameter placeholders
* @param array $defaults Array of defaults for the route. * @param array $defaults Array of defaults for the route.
* @param string $params Array of parameters and additional options for the Route * @param string $params Array of parameters and additional options for the Route
* @return void * @return void
*/ */
function RouterRoute($pattern, $defaults = array(), $params = array()) { function RouterRoute($template, $defaults = array(), $params = array()) {
$this->pattern = $pattern; $this->template = $template;
$this->defaults = (array)$defaults; $this->defaults = (array)$defaults;
$this->params = (array)$params; $this->params = (array)$params;
} }
@ -1512,8 +1520,8 @@ class RouterRoute {
* @access public * @access public
*/ */
function compile() { function compile() {
$this->_writeRoute($this->pattern, $this->defaults, $this->params); $this->_writeRoute($this->template, $this->defaults, $this->params);
$this->defaults += array('plugin' => null, 'controller' => null); $this->defaults += array('plugin' => null, 'controller' => null, 'action' => null);
return $this->_compiledRoute; return $this->_compiledRoute;
} }
/** /**
@ -1527,7 +1535,8 @@ class RouterRoute {
*/ */
function _writeRoute($route, $default, $params) { function _writeRoute($route, $default, $params) {
if (empty($route) || ($route === '/')) { if (empty($route) || ($route === '/')) {
return array('/^[\/]*$/', array()); $this->_compiledRoute = '/^[\/]*$/';
$this->keys = array();
} }
$names = array(); $names = array();
$elements = explode('/', $route); $elements = explode('/', $route);
@ -1585,17 +1594,18 @@ class RouterRoute {
} }
} }
$this->_compiledRoute = '#^' . join('', $parsed) . '[\/]*$#'; $this->_compiledRoute = '#^' . join('', $parsed) . '[\/]*$#';
$this->names = $names; $this->keys = $names;
} }
/** /**
* Checks to see if the given URL matches the given route * Checks to see if the given URL can be parsed by this route.
* If the route can be parsed an array of parameters will be returned if not
* false will be returned.
* *
* @param array $route * @param string $url The url to attempt to parse.
* @param string $url * @return mixed Boolean false on failure, otherwise an array or parameters
* @return mixed Boolean false on failure, otherwise array
*/ */
function match($url) { function parse($url) {
if (!$this->compiled()) { if (!$this->compiled()) {
$this->compile(); $this->compile();
} }
@ -1627,5 +1637,163 @@ class RouterRoute {
} }
return $r; return $r;
} }
/**
* Attempt to match a url array. If the url matches the routes pattern, then
* return an array of parsed params. If the url doesn't match the routes compiled pattern
* returns false.
*
* @param array $url An array of parameters to check matching with.
* @return mixed Either a string url for the parameters if they match or false.
**/
function match($url) {
if (!$this->compiled()) {
$this->compile();
}
if (isset($this->defaults['prefix'])) {
$prefix = $this->defaults['prefix'];
}
$url += array('plugin' => null, 'controller' => null);
$pass = array();
$params = Set::diff($url, $this->defaults);
$urlInv = array_combine(array_values($url), array_keys($url));
$i = 0;
while (isset($this->defaults[$i])) {
if (isset($urlInv[$this->defaults[$i]])) {
if (!in_array($this->defaults[$i], $url) && is_int($urlInv[$this->defaults[$i]])) {
return false;
}
unset($urlInv[$this->defaults[$i]], $this->defaults[$i]);
} else {
return false;
}
$i++;
}
foreach ($params as $key => $value) {
if (is_int($key)) {
$pass[] = $value;
unset($params[$key]);
}
}
list($named, $params) = Router::getNamedElements($params);
if (!strpos($this->template, '*') && (!empty($pass) || !empty($named))) {
return false;
}
$urlKeys = array_keys($url);
$paramsKeys = array_keys($params);
$defaultsKeys = array_keys($this->defaults);
if (!empty($params)) {
if (array_diff($paramsKeys, $this->keys) != array()) {
return false;
}
$required = array_values(array_diff($this->keys, $urlKeys));
$reqCount = count($required);
for ($i = 0; $i < $reqCount; $i++) {
if (array_key_exists($required[$i], $this->defaults) && $this->defaults[$required[$i]] === null) {
unset($required[$i]);
}
}
}
$isFilled = true;
if (!empty($this->keys)) {
$filled = array_intersect_key($url, array_combine($this->keys, array_keys($this->keys)));
$isFilled = (array_diff($this->keys, array_keys($filled)) === array());
if (!$isFilled && empty($params)) {
return false;
}
}
if (empty($params)) {
return $this->__mapRoute(array_merge($url, compact('pass', 'named', 'prefix')));
} elseif (!empty($this->keys) && !empty($this->defaults)) {
if (!empty($required)) {
return false;
}
foreach ($params as $key => $val) {
if ((!isset($url[$key]) || $url[$key] != $val) || (!isset($this->defaults[$key]) || $this->defaults[$key] != $val) && !in_array($key, $this->keys)) {
if (!isset($this->defaults[$key])) {
continue;
}
return false;
}
}
} else {
if (empty($required) && $this->defaults['plugin'] === $url['plugin'] && $this->defaults['controller'] === $url['controller'] && $this->defaults['action'] === $url['action']) {
return $this->__mapRoute($route, array_merge($url, compact('pass', 'named', 'prefix')));
}
return false;
}
if (!empty($this->params)) {
foreach ($this->params as $key => $reg) {
if (array_key_exists($key, $url) && !preg_match('#' . $reg . '#', $url[$key])) {
return false;
}
}
}
return $this->__mapRoute(array_merge($filled, compact('pass', 'named', 'prefix')));
}
/**
* Map route..
*
* @return void
**/
function __mapRoute($params) {
if (isset($params['plugin']) && isset($params['controller']) && $params['plugin'] === $params['controller']) {
unset($params['controller']);
}
if (isset($params['prefix']) && isset($params['action'])) {
$params['action'] = str_replace($params['prefix'] . '_', '', $params['action']);
unset($params['prefix']);
}
if (isset($params['pass']) && is_array($params['pass'])) {
$params['pass'] = implode('/', Set::filter($params['pass'], true));
} elseif (!isset($params['pass'])) {
$params['pass'] = '';
}
if (isset($params['named'])) {
if (is_array($params['named'])) {
$count = count($params['named']);
$keys = array_keys($params['named']);
$named = array();
for ($i = 0; $i < $count; $i++) {
$named[] = $keys[$i] . $this->named['separator'] . $params['named'][$keys[$i]];
}
$params['named'] = join('/', $named);
}
$params['pass'] = str_replace('//', '/', $params['pass'] . '/' . $params['named']);
}
$out = $this->template;
foreach ($this->keys as $key) {
$string = null;
if (isset($params[$key])) {
$string = $params[$key];
unset($params[$key]);
} elseif (strpos($out, $key) != strlen($out) - strlen($key)) {
$key = $key . '/';
}
$out = str_replace(':' . $key, $string, $out);
}
if (strpos($this->template, '*')) {
$out = str_replace('*', $params['pass'], $out);
}
return $out;
}
} }
?> ?>

View file

@ -1974,7 +1974,7 @@ class RouterRouteTestCase extends CakeTestCase {
function testConstruction() { function testConstruction() {
$route =& new RouterRoute('/:controller/:action/:id', array('controller' => 'posts', 'id' => null), array('id' => '[0-9]+')); $route =& new RouterRoute('/:controller/:action/:id', array('controller' => 'posts', 'id' => null), array('id' => '[0-9]+'));
$this->assertEqual($route->pattern, '/:controller/:action/:id'); $this->assertEqual($route->template, '/:controller/:action/:id');
$this->assertEqual($route->defaults, array('controller' => 'posts', 'id' => null)); $this->assertEqual($route->defaults, array('controller' => 'posts', 'id' => null));
$this->assertEqual($route->params, array('id' => '[0-9]+')); $this->assertEqual($route->params, array('id' => '[0-9]+'));
$this->assertFalse($route->compiled()); $this->assertFalse($route->compiled());
@ -1998,21 +1998,21 @@ class RouterRouteTestCase extends CakeTestCase {
$expected = '#^(?:/([^\/]+))?(?:/([^\/]+))?(?:/([0-9]+)?)?[\/]*$#'; $expected = '#^(?:/([^\/]+))?(?:/([^\/]+))?(?:/([0-9]+)?)?[\/]*$#';
$this->assertEqual($result, $expected); $this->assertEqual($result, $expected);
$this->assertEqual($route->names, array('controller', 'action', 'id')); $this->assertEqual($route->keys, array('controller', 'action', 'id'));
$route =& new RouterRoute('/:controller/:action/:id', array('controller' => 'testing4'), array('id' => $ID)); $route =& new RouterRoute('/:controller/:action/:id', array('controller' => 'testing4'), array('id' => $ID));
$result = $route->compile(); $result = $route->compile();
$expected = '#^(?:/([^\/]+))?(?:/([^\/]+))?(?:/([0-9]+))[\/]*$#'; $expected = '#^(?:/([^\/]+))?(?:/([^\/]+))?(?:/([0-9]+))[\/]*$#';
$this->assertEqual($result, $expected); $this->assertEqual($result, $expected);
$this->assertEqual($route->names, array('controller', 'action', 'id')); $this->assertEqual($route->keys, array('controller', 'action', 'id'));
$route =& new RouterRoute('/posts/foo:id'); $route =& new RouterRoute('/posts/foo:id');
$result = $route->compile(); $result = $route->compile();
$expected = '#^/posts(?:/foo([^\/]+))?[\/]*$#'; $expected = '#^/posts(?:/foo([^\/]+))?[\/]*$#';
$this->assertEqual($result, $expected); $this->assertEqual($result, $expected);
$this->assertEqual($route->names, array('id')); $this->assertEqual($route->keys, array('id'));
foreach (array(':', '@', ';', '$', '-') as $delim) { foreach (array(':', '@', ';', '$', '-') as $delim) {
$route =& new RouterRoute('/posts/:id'.$delim.':title'); $route =& new RouterRoute('/posts/:id'.$delim.':title');
@ -2020,14 +2020,26 @@ class RouterRouteTestCase extends CakeTestCase {
$expected = '#^/posts(?:/([^\/]+))?(?:'.preg_quote($delim, '#').'([^\/]+))?[\/]*$#'; $expected = '#^/posts(?:/([^\/]+))?(?:'.preg_quote($delim, '#').'([^\/]+))?[\/]*$#';
$this->assertEqual($result, $expected); $this->assertEqual($result, $expected);
$this->assertEqual($route->names, array('id', 'title')); $this->assertEqual($route->keys, array('id', 'title'));
} }
$route =& new RouterRoute('/posts/:id::title/:year'); $route =& new RouterRoute('/posts/:id::title/:year');
$result = $route->compile(); $result = $route->compile();
$this->assertEqual($result, '#^/posts(?:/([^\/]+))?(?:\\:([^\/]+))?(?:/([^\/]+))?[\/]*$#'); $this->assertEqual($result, '#^/posts(?:/([^\/]+))?(?:\\:([^\/]+))?(?:/([^\/]+))?[\/]*$#');
$this->assertEqual($route->names, array('id', 'title', 'year')); $this->assertEqual($route->keys, array('id', 'title', 'year'));
} }
/**
* test that routes match their pattern.
*
* @return void
**/
function testMatching() {
$route =& new RouterRoute('/blog/:action', array('controller' => 'posts'));
$result = $route->match(array('controller' => 'posts', 'action' => 'view'));
$this->assertEqual($result, '/blog/view');
}
} }
?> ?>