/** * @covers Zepto\Router::run() * @covers Zepto\Router::match() * @covers Zepto\Router::parse_parameters() * @covers Zepto\Router::current_route() * @covers Zepto\Router::current_http_status() */ public function testRunWithParameters() { $_SERVER['REQUEST_URL'] = '/zepto/index.php/get/666'; $_SERVER['REQUEST_URI'] = '/zepto/index.php/get/666'; $this->mock_request->expects($this->any())->method('getPathInfo')->will($this->returnValue('/get/666')); $this->router->get('/get/<:id|[6]{3}>', function ($id) { return 'This is ' . $id; }); $this->assertTrue($this->router->run()); $this->assertEquals('/get/<:id|[6]{3}>', $this->router->current_route()->url()); $this->assertEquals('#^/get/(?P<id>[6]{3})/$#', $this->router->current_route()->pattern()); $this->assertEquals(200, $this->router->current_http_status()); }
/** * Instantiates the specified controller, optionally replacing $_GET and $_POST contents before calling the component constructor. * \note The original $_GET and $_POST array are restored as soon as the component is instantiated. * @param $controller The name of the controller class to be instantiated, lowercase, without the "Controller_" prefix. * @param $get The array that should replace $_GET. * @param $post The array that should replace $_POST. * @return A new Component object. */ public static function factory($controller, $get = null, $post = null) { // Backup router state $old_router = array(); $old_router['current_route'] = Router::$current_route; $old_router['current_uri'] = Router::$current_uri; $old_router['query_string'] = Router::$query_string; $old_router['complete_uri'] = Router::$complete_uri; $old_router['controller'] = Router::$controller; $old_router['method'] = Router::$method; $old_router['arguments'] = Router::$arguments; // The following three variables could be determined by running Router code and passing an url, but: // 1) The performance penalty would be high // 1) It's not a good idea for a controller to alter its behaviour depending on them, anyway //Router::$current_route = ''; //Router::$current_uri = ''; //Router::$complete_uri = ''; Router::$controller = $controller; // We don't know these yet Router::$method = ''; Router::$arguments = array(); // If get or post parameters are passed, alter $_GET, $_POST and Router::$query_string accordingly // NOTE: Should we alter $_SERVER['QUERY_STRING'] too? if ($get !== null) { $old_get = $_GET; $_GET = $get; Router::$query_string = '?' . http_build_query($get); } if ($post !== null) { $old_post = $_POST; $_POST = $post; } // If class is not defined already, load controller file $controller_class = 'Controller_' . ucfirst($controller); if (!class_exists($controller_class, false)) { $controller_file = str_replace('_', '/', strtolower($controller_class)); // If the component file doesn't exist, fire exception $filepath = Kohana::find_file('classes', $controller_file, true); // Include the Controller file require_once $filepath; } // Run system.pre_controller Event::run('dispatch.pre_controller'); // Initialize the controller $controller_instance = new $controller_class(); // Run system.post_controller_constructor Event::run('dispatch.post_controller_constructor'); // Revert $_GET and $_POST changes if ($get !== null) { $_GET = $old_get; } if ($post !== null) { $_POST = $old_post; } // Revert Router state Router::$current_route = $old_router['current_route']; Router::$current_uri = $old_router['current_uri']; Router::$query_string = $old_router['query_string']; Router::$complete_uri = $old_router['complete_uri']; Router::$controller = $old_router['controller']; Router::$method = $old_router['method']; Router::$arguments = $old_router['arguments']; return new Component($controller_instance, $controller, $old_router); }
/** * Parses givven url, tries to match it against routes * * @param string $url * @return array Current route */ public function parse_url($url) { $out = []; $ext = null; if (strpos($url, '/') !== 0) { $url = '/' . $url; } foreach ($this->routes as $route => $params) { if (($matches = $this->match_route($route, $url)) !== false) { $names = $params['names']; $defaults = $params['defaults']; array_shift($matches); foreach ($matches as $key => $found) { if (empty($found) && $found !== '0' && $found !== 0) { continue; } if (isset($names[$key])) { $out[$names[$key]] = $found; } else { foreach (array_cleanup(explode('/', $found)) as $param) { array_push($out, $param); } } } $out = array_merge($defaults, $out); break; } } self::$current_route = $out; return $out; }
private static function route($priority) { $uri = implode('/', segments()); foreach (self::$slugs as $key => $routes) { $key = str_replace('%s', '([a-z0-9\\-]+)', str_replace('%d', '([0-9]+)', str_replace('/', '\\/', $key))); if (preg_match('/^' . $key . '(\\/|$)/is', $uri, $matches)) { array_pop($matches); array_shift($matches); foreach ($routes as &$route) { if ($route->get_priority() == $priority) { $arguments = preg_replace('/^' . $key . '(\\/|$)/is', '', $uri); $arguments = explode("?", $arguments); $arguments = array_filter(explode("/", array_shift($arguments))); $arguments = array_merge($matches, $arguments); // Check if the class that will handle the content actually contains the requested function. if (!method_exists($route->get_classname(), $route->get_function())) { continue; } // Check if we're not calling said function with too few parameters. $reflector = new ReflectionClass($route->get_classname()); if (count($arguments) < $reflector->getMethod($route->get_function())->getNumberOfRequiredParameters()) { continue; } // Check if this function might want variable number of parameters. $collapse_parameters = false; $parameters = $reflector->getMethod($route->get_function())->getParameters(); if (count($parameters) && end($parameters)->name == 'parameters') { $collapse_parameters = true; } if (count($arguments) > count($parameters) && $collapse_parameters && $priority == ROUTE_DEFAULT) { $route->set_priority(ROUTE_LATE); continue; } // Check if we're not calling said function with too many parameters. if (count($arguments) > count($parameters) && !$collapse_parameters) { continue; } // Check if we're not calling a static function. if ($reflector->getMethod($route->get_function())->isStatic()) { continue; } // Save old segments should we need it again later self::$urlsegments = self::$segments; // Set the segments to those that matched our content self::$segments = array(); self::$segments[0] = strtolower($route->get_contentname()); self::$segments[1] = strtolower($route->get_function()); self::$segments = array_merge(self::$segments, $arguments); // Set the current route self::$current_route = $route; // Check database if needed (only do this when there's no admin panel) if (!Config::admin_enabled()) { $site = current_site(); if (self::is_fw4() && !$site->live) { FW4_Structure::check_structure(); } } // Fire the controller View_Loader::get_instance()->set_path(CONTENTPATH . self::$content_prefix . self::$segments[0]); $page = self::$content_pages[strtolower($route->get_classname())]; if ($collapse_parameters) { $non_optional = array_splice($arguments, 0, count($parameters) - 1); $arguments = array_merge($non_optional, array(array_diff($arguments, array('index')))); } try { $result = call_user_func_array(array($page, $route->get_function()), $arguments); } catch (RowNotFoundException $e) { $result = false; } // If the controller returns false, reset the segments and continue matching if ($result === false) { self::$segments = self::$urlsegments; continue; } return true; } } } } return false; }
public static function doRouting() { // Set the locale self::setLocale(); // Check if we have a route and it's not empty if (isset($_GET[self::$trigger]) && !empty($_GET[self::$trigger])) { // Cleanup the route using our regex (only alphanumeric and url safe chars (.,-_/) are allowed) self::$current_route = preg_replace('/[^[:alnum:]\\.\\-\\_\\/]/', '', $_GET[self::$trigger]); // See if we have an filename ending (.html, etc.) if (self::$ending !== '') { // Calculate the offset for the length of the filename ending $offset = @strrpos(self::$current_route, self::$ending, -strlen(self::$ending)); // Check if offset is present if ($offset !== FALSE) { // So we remove the ending using the calculated offset to include the files we need self::$tpl = substr_replace(self::$current_route, '', $offset, strlen(self::$ending)) . '.tpl'; self::$conf = substr_replace(self::$current_route, '', $offset, strlen(self::$ending)) . '.conf'; self::$md = substr_replace(self::$current_route, '', $offset, strlen(self::$ending)) . '.md'; } else { // Show 404 self::error404(); return; } } else { // We don't have a filename ending, thus we can assign the route's name to files we need self::$tpl = self::$current_route . '.tpl'; self::$conf = self::$current_route . '.conf'; self::$md = self::$current_route . '.md'; } // Check for needed files we include later if (!file_exists('templates/' . self::$view_dir . self::$tpl) || !is_readable('templates/' . self::$view_dir . self::$tpl) || !is_file('templates/' . self::$view_dir . self::$tpl)) { // ...and if not show up the 404 self::error404(); return; } } else { // Set the current route to the default self::$current_route = self::$default_route . self::$ending; // Since we have no route, try to show up the default one self::$tpl = self::$default_route . '.tpl'; self::$conf = self::$default_route . '.conf'; self::$md = self::$default_route . '.md'; // Again, check for needed files we include later if (!file_exists('templates/' . self::$view_dir . self::$tpl) || !is_readable('templates/' . self::$view_dir . self::$tpl) || !is_file('templates/' . self::$view_dir . self::$tpl)) { // ...and if not shopw up the 404 (again) self::error404(); return; } } // Assign all needed files to our main template self::$smarty->assign('viewFile', self::$view_dir . self::$tpl); self::$smarty->assign('markdownFile', self::$md_dir . self::$md); self::$smarty->assign('currentRoute', self::$current_route); self::$smarty->assign('configFile', self::$conf); // Show the main template self::$smarty->display('main.tpl'); }