<?php
class Dispatcher
{
public function dispatch()
{
$log = empty(Config::$logRouting) ? false : fopen(Config::$logRouting, 'r+');
static $isLoaded, $urlParam;
if (!$isLoaded)
{
$routes = Routes::$routes;
$urlParams = isset($_GET['_url']) ? $_GET['_url'] : '';
$urlSegments = explode('/', trim($urlParams,'/'));
$urlSegCount = count($urlSegments);
}
$routed = false;
foreach ($routes as $i=>$routeParams)
{
$route = trim($routeParams[0], '/');
$params = $defaults = $routeParams[1];
$routeSegments = explode('/', $route);
// we count a lot, so we cache the size of the route segments
$routeSegCount = count($routeSegments);
// if we have more segments in the url than in the route, then we have a * situation.
if ($routeSegCount < $urlSegCount)
{
// if we don't have an even number of leftover url segments, all is lost.
if(end($routeSegments) !== '*')
{
$log && fputs($log, '<pre>Route '.$i.': We don\'t have a * for the extra url segments</pre>');
continue; // we have failed
}
// Re-init this when i care...i don't know if i should care about the end of the url
// elseif(($urlSegCount-$routeSegCount)%2 !== 0)
// {
// return false;
//}
$segmentCount = $routeSegCount;
}
else
{
$segmentCount = $urlSegCount;
}
// run through and match the route, segment by segment, in search of flaws
for ($i=0; $i<$segmentCount; $i++)
{
if ($routeSegments[$i]===$urlSegments[$i] || // if it's not a var or a glob, match the literal strings
($routeSegments[$i]==='*' && $i+1===$segmentCount)) // * only as the LAST SEGMENT
{
continue; // THIS IS A LITERAL MATCH AND SUCCESS.
}
elseif ($routeSegments[$i][0]===':') // check for :foo, if so...then it's good
{
$key = substr($routeSegments[$i], 1);
$params[$key] = empty($urlSegments[$i]) ? $defaults[$key] : $urlSegments[$i];
}
else
{
$log && fputs($log, '<pre>Route '.$i.': Literal string match for "'.$routeSegments[$i].'" against "'.$urlSegments[$i].'" failed</pre>');
continue 2; // You have failed us
}
}
// we need to run through the rest of the segments, if the url is shorter, and test for defaults
for (; $i<$routeSegCount; $i++)
{
// If we have an unfulfilled (we're in this loop, so we ran out of user params, remember?) *, just make sure we're at the end
if ($routeSegments[$i]==='*' && $i+1!==$segmentCount)
{
$log && fputs($log, '<pre>Route '.$i.': Ran out of parameters</pre>');
continue 2; // next in foreach
}
// Check if this segment starts with :, and since we're in "unset params" mode, if it isn't, we have a problem
if ($routeSegments[$i][0]!==':')
{
$log && fputs($log, '<pre>Route '.$i.': A route segment literal cannot be matched after input has run out</pre>');
continue 2; // next in foreach
}
$key = substr($routeSegments[$i], 1); // We need to check that the var has a default, and we need the array key it will exist under
if (!isset($defaults[$key]))
{
$log && fputs($log, '<pre>Route '.$i.': No default specified for extra parameter</pre>');
continue 2; // next in foreach
}
}
$routed = true;
// we ran the gauntlet!
break;
} // endforeach routes
if (!$routed)
{
throw new Exception('Route not found!');
}
if (empty($params['controller']) || !class_exists($params['controller'].'_Controller'))
{
$controller = ($params['controller'] = Config::$defaultController).'_Controller';
}
else
{
$controller = $params['controller'].'_Controller';
}
if (empty($params['action']) || !method_exists($controller, $params['action']))
{
$action = $params['action'] = Config::$defaultAction;
}
else
{
$action = $params['action'];
}
$controllerInstance = new $controller($params);
$controllerInstance->$action();
}
}
?>