Skip to content

Commit

Permalink
Refactored CakeRoute::match() to not use Set::diff(). This was the sl…
Browse files Browse the repository at this point in the history
…owest part of reverse routing and this change should make things faster.

Added additional tests for the 0 edge case.
  • Loading branch information
markstory committed Dec 18, 2010
1 parent c5bab54 commit 456a14c
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 29 deletions.
55 changes: 26 additions & 29 deletions cake/libs/route/cake_route.php
Expand Up @@ -263,27 +263,40 @@ public function match($url) {
if (array_intersect_key($keyNames, $url) != $keyNames) {
return false;
}

//pull out named params so comparisons later on are faster.
$named = array();

$named = $pass = $diff = array();
foreach ($url as $key => $value) {
// pull out named params so comparisons later on are faster.
if ($key[0] === ':') {
$named[$key] = $value;
$named[substr($key, 1)] = $value;
unset($url[$key]);
continue;
}
}

$diffUnfiltered = Set::diff($url, $defaults);
$diff = array();

foreach ($diffUnfiltered as $key => $var) {
if ($var === 0 || $var === '0' || !empty($var)) {
$diff[$key] = $var;
// keys that exist in the defaults and have different values cause match failures.
$keyExists = array_key_exists($key, $defaults);
if ($keyExists && $defaults[$key] != $value) {
$diff[$key] = $value;
continue;
}
// keys that don't exist are different.
if (!$keyExists) {
$diff[$key] = $value;
}

// pull out passed args
$numeric = is_numeric($key);
if ($numeric && isset($defaults[$key]) && $defaults[$key] == $value) {
continue;
} elseif ($numeric) {
$pass[] = $value;
unset($url[$key]);
continue;
}
}

//if a not a greedy route, no extra params are allowed.
if (!$this->_greedy && array_diff_key($diff, $keyNames) != array()) {
if (!$this->_greedy && (!empty($pass) || array_diff_key($diff, $keyNames) != array())) {
return false;
}

Expand All @@ -294,26 +307,10 @@ public function match($url) {
$filteredDefaults = array_filter($defaults);

//if the difference between the url diff and defaults contains keys from defaults its not a match
if (array_intersect_key($filteredDefaults, $diffUnfiltered) !== array()) {
if (array_intersect_key($filteredDefaults, $diff) !== array()) {
return false;
}

$passedArgsAndParams = array_diff_key($diff, $filteredDefaults, $keyNames) + $named;
list($named, $params) = Router::getNamedElements($passedArgsAndParams, $url['controller'], $url['action']);

//remove any pass params, they have numeric indexes, skip any params that are in the defaults
$pass = array();
$i = 0;
while (isset($url[$i])) {
if (!isset($diff[$i])) {
$i++;
continue;
}
$pass[] = $url[$i];
unset($url[$i], $params[$i]);
$i++;
}

//still some left over parameters that weren't named or passed args, bail.
if (!empty($params)) {
return false;
Expand Down
6 changes: 6 additions & 0 deletions cake/tests/cases/libs/route/cake_route.test.php
Expand Up @@ -304,6 +304,12 @@ function testMatchWithNamedParametersAndPassedArgs() {
$result = $route->match(array('controller' => 'posts', 'action' => 'view', 'plugin' => null, 5));
$this->assertEqual($result, '/posts/view/5');

$result = $route->match(array('controller' => 'posts', 'action' => 'view', 'plugin' => null, 0));
$this->assertEqual($result, '/posts/view/0');

$result = $route->match(array('controller' => 'posts', 'action' => 'view', 'plugin' => null, '0'));
$this->assertEqual($result, '/posts/view/0');

$result = $route->match(array('controller' => 'posts', 'action' => 'view', 'plugin' => null, 5, ':page' => 1, ':limit' => 20, ':order' => 'title'));
$this->assertEqual($result, '/posts/view/5/page:1/limit:20/order:title');

Expand Down

0 comments on commit 456a14c

Please sign in to comment.