will be translated to a regular expression using a default * regular expression pattern. You can override the default pattern by providing * a pattern for the key: * * // This route will only match when is a digit * Route::factory('user/edit/', array('id' => '\d+')); * * // This route will match when is anything * Route::factory('', array('path' => '.*')); * * It is also possible to create optional segments by using parenthesis in * the URI definition: * * // This is the standard default route, and no keys are required * Route::default('((/(/)))'); * * // This route only requires the :file key * Route::factory('(/)()', array('path' => '.*', 'format' => '\.\w+')); * * Routes also provide a way to generate URIs (called "reverse routing"), which * makes them an extremely powerful and flexible way to generate internal links. * * @package Kohana * @author Kohana Team * @copyright (c) 2008-2009 Kohana Team * @license http://kohanaphp.com/license.html */ class Route_Core { const REGEX_KEY = '<([a-zA-Z0-9_]++)>'; const REGEX_SEGMENT = '[^/.,;?]++'; const REGEX_ESCAPE = '[.\\+*?[^\\]${}=!|]'; protected static $_routes = array(); /** * Called when the object is re-constructed from the cache. * * @param array cached values * @return Route */ public static function __set_state(array $values) { // Reconstruct the route $route = new Route($values['uri'], $values['regex']); // Set defaults $route->defaults = $values['defaults']; return $route; } /** * Stores a named route and returns it. * * @param string route name * @param string URI pattern * @param array regex patterns for route keys * @return Route */ public static function set($name, $uri, array $regex = NULL) { return Route::$_routes[$name] = new Route($uri, $regex); } /** * Retrieves a named route. * * @param string route name * @return Route * @return FALSE when no route is found */ public static function get($name) { return isset(Route::$_routes[$name]) ? Route::$_routes[$name] : FALSE; } /** * Retrieves all named routes, with the default route last. * * @return array named routes */ public static function all() { return Route::$_routes; } // Route URI string protected $_uri = ''; // Regular expressions for route keys protected $_regex = array(); // Default values for route keys protected $_defaults = array('action' => 'index'); // Compiled regex cache protected $_route_regex; /** * Creates a new route. Sets the URI and regular expressions for keys. * * @param string route URI pattern * @param array key patterns */ public function __construct($uri, array $regex = NULL) { if ( ! empty($regex)) $this->_regex = $regex; // Store the URI that this route will match $this->_uri = $uri; // Set the cache key $cache_key = 'route::compile("'.$uri.'")'; if (($regex = Kohana::cache($cache_key)) === NULL) { // Compile the complete regex for this uri $regex = $this->_compile(); // Cache the compiled regex Kohana::cache($cache_key, $regex); } // Store the compiled regex locally $this->_route_regex = $regex; } /** * Provides default values for keys when they are not present. The default * action will always be "index" unless it is overloaded here. * * $route->defaults(array('controller' => 'welcome', 'action' => 'index')); * * @chainable * @param array key values * @return Route */ public function defaults(array $defaults = NULL) { if ( ! isset($defaults['action'])) { $defaults['action'] = 'index'; } $this->_defaults = $defaults; return $this; } /** * Tests if the route matches a given URI. A successful match will return * all of the routed parameters as an array. A failed match will return * boolean FALSE. * * // This route will only match if the , , and exist * $params = Route::factory('//', array('id' => '\d+')) * ->match('users/edit/10'); * // The parameters are now: controller = users, action = edit, id = 10 * * This method should almost always be used within an if/else block: * * if ($params = $route->match($uri)) * { * // Parse the parameters * } * * @param string URI to match * @return array on success * @return FALSE on failure */ public function matches($uri) { if ( ! preg_match($this->_route_regex, $uri, $matches)) return FALSE; $params = array(); foreach ($matches as $key => $value) { if (is_int($key)) { // Skip all unnamed keys continue; } // Set the value for all matched keys $params[$key] = $value; } foreach ($this->_defaults as $key => $value) { if ( ! isset($params[$key])) { // Set default values for any key that was not matched $params[$key] = $value; } } return $params; } /** * Generates a URI for the current route based on the parameters given. * * @param array URI parameters * @return string */ public function uri(array $params = NULL) { if ($params === NULL) { // Use the default parameters $params = $this->_defaults; } // Start with the routed URI $uri = $this->_uri; if (strpos($uri, '<') === FALSE AND strpos('(', $this->uri) === FALSE) { // This is a static route, no need to replace anything return $uri; } if (preg_match_all('#'.Route::REGEX_KEY.'#', $uri, $keys)) { foreach ($keys[1] as $key) { $search[] = "<$key>"; $replace[] = isset($params[$key]) ? $params[$key] : ''; } // Replace all the variable keys in the URI $uri = str_replace($search, $replace, $uri); } if (strpos($uri, '(') !== FALSE) { // Remove all groupings from the URI $uri = str_replace(array('(', ')'), '', $uri); } // Trim off extra slashes $uri = rtrim($uri, '/'); return $uri; } /** * Returns the compiled regular expression for the route. This translates * keys and optional groups to a proper PCRE regular expression. * * @access protected * @return string */ protected function _compile() { // The URI should be considered literal except for keys and optional parts // Escape everything preg_quote would escape except for : ( ) < > $regex = preg_replace('#'.Route::REGEX_ESCAPE.'#', '\\\\$0', $this->_uri); if (strpos($regex, '(') !== FALSE) { // Make optional parts of the URI non-capturing and optional $regex = str_replace(array('(', ')'), array('(?:', ')?'), $regex); } // Insert default regex for keys $regex = str_replace(array('<', '>'), array('(?P<', '>'.Route::REGEX_SEGMENT.')'), $regex); if ( ! empty($this->_regex)) { $search = $replace = array(); foreach ($this->_regex as $key => $value) { $search[] = "<$key>".Route::REGEX_SEGMENT; $replace[] = "<$key>$value"; } // Replace the default regex with the user-specified regex $regex = str_replace($search, $replace, $regex); } return '#^'.$regex.'$#'; } } // End Route