1 <?php
defined('SYSPATH') or die('No direct script access.');
3 * Routes are used to determine the controller and action for a requested URI.
4 * Every route generates a regular expression which is used to match a URI
5 * and a route. Routes may also contain keys which can be used to set the
6 * controller, action, and parameters.
8 * Each <key> will be translated to a regular expression using a default
9 * regular expression pattern. You can override the default pattern by providing
10 * a pattern for the key:
12 * // This route will only match when <id> is a digit
13 * Route::set('user', 'user/<action>/<id>', array('id' => '\d+'));
15 * // This route will match when <path> is anything
16 * Route::set('file', '<path>', array('path' => '.*'));
18 * It is also possible to create optional segments by using parentheses in
21 * // This is the standard default route, and no keys are required
22 * Route::set('default', '(<controller>(/<action>(/<id>)))');
24 * // This route only requires the <file> key
25 * Route::set('file', '(<path>/)<file>(.<format>)', array('path' => '.*', 'format' => '\w+'));
27 * Routes also provide a way to generate URIs (called "reverse routing"), which
28 * makes them an extremely powerful and flexible way to generate internal links.
33 * @copyright (c) 2008-2010 Kohana Team
34 * @license http://kohanaframework.org/license
38 // Defines the pattern of a <segment>
39 const REGEX_KEY
= '<([a-zA-Z0-9_]++)>';
41 // What can be part of a <segment> value
42 const REGEX_SEGMENT
= '[^/.,;?\n]++';
44 // What must be escaped in the route regex
45 const REGEX_ESCAPE
= '[.\\+*?[^\\]${}=!|]';
48 * @var string default protocol for all routes
52 public static $default_protocol = 'http://';
55 * @var array list of valid localhost entries
57 public static $localhosts = array(FALSE, '', 'local', 'localhost');
60 * @var string default action for all routes
62 public static $default_action = 'index';
65 * @var bool Indicates whether routes are cached
67 public static $cache = FALSE;
69 // List of route objects
70 protected static $_routes = array();
73 * Stores a named route and returns it. The "action" will always be set to
74 * "index" if it is not defined.
76 * Route::set('default', '(<controller>(/<action>(/<id>)))')
78 * 'controller' => 'welcome',
81 * @param string route name
82 * @param string URI pattern
83 * @param array regex patterns for route keys
86 public static function set($name, $uri, array $regex = NULL)
88 return Route
::$_routes[$name] = new Route($uri, $regex);
92 * Retrieves a named route.
94 * $route = Route::get('default');
96 * @param string route name
98 * @throws Kohana_Exception
100 public static function get($name)
102 if ( ! isset(Route
::$_routes[$name]))
104 throw new Kohana_Exception('The requested route does not exist: :route',
105 array(':route' => $name));
108 return Route
::$_routes[$name];
112 * Retrieves all named routes.
114 * $routes = Route::all();
116 * @return array routes by name
118 public static function all()
120 return Route
::$_routes;
124 * Get the name of a route.
126 * $name = Route::name($route)
128 * @param object Route instance
131 public static function name(Route
$route)
133 return array_search($route, Route
::$_routes);
137 * Saves or loads the route cache. If your routes will remain the same for
138 * a long period of time, use this to reload the routes from the cache
139 * rather than redefining them on every page load.
141 * if ( ! Route::cache())
144 * Route::cache(TRUE);
147 * @param boolean cache the current routes
148 * @return void when saving routes
149 * @return boolean when loading routes
150 * @uses Kohana::cache
152 public static function cache($save = FALSE)
156 // Cache all defined routes
157 Kohana
::cache('Route::cache()', Route
::$_routes);
161 if ($routes = Kohana
::cache('Route::cache()'))
163 Route
::$_routes = $routes;
165 // Routes were cached
166 return Route
::$cache = TRUE;
170 // Routes were not cached
171 return Route
::$cache = FALSE;
177 * Create a URL from a route name. This is a shortcut for:
179 * echo URL::site(Route::get($name)->uri($params), $protocol);
181 * @param string route name
182 * @param array URI parameters
183 * @param mixed protocol string or boolean, adds protocol and domain
188 public static function url($name, array $params = NULL, $protocol = NULL)
190 // Create a URI with the route and convert it to a URL
191 return URL
::site(Route
::get($name)->uri($params), $protocol);
195 * Returns the compiled regular expression for the route. This translates
196 * keys and optional groups to a proper PCRE regular expression.
198 * $compiled = Route::compile(
199 * '<controller>(/<action>(/<id>))',
201 * 'controller' => '[a-z]+',
207 * @uses Route::REGEX_ESCAPE
208 * @uses Route::REGEX_SEGMENT
210 public static function compile($uri, array $regex = NULL)
212 // The URI should be considered literal except for keys and optional parts
213 // Escape everything preg_quote would escape except for : ( ) < >
214 $expression = preg_replace('#'.Route
::REGEX_ESCAPE
.'#', '\\\\$0', $uri);
216 if (strpos($expression, '(') !== FALSE)
218 // Make optional parts of the URI non-capturing and optional
219 $expression = str_replace(array('(', ')'), array('(?:', ')?'), $expression);
222 // Insert default regex for keys
223 $expression = str_replace(array('<', '>'), array('(?P<', '>'.Route
::REGEX_SEGMENT
.')'), $expression);
227 $search = $replace = array();
228 foreach ($regex as $key => $value)
230 $search[] = "<$key>".Route
::REGEX_SEGMENT
;
231 $replace[] = "<$key>$value";
234 // Replace the default regex with the user-specified regex
235 $expression = str_replace($search, $replace, $expression);
238 return '#^'.$expression.'$#uD';
242 * @var string route URI
244 protected $_uri = '';
246 // Regular expressions for route keys
247 protected $_regex = array();
249 // Default values for route keys
250 protected $_defaults = array('action' => 'index');
252 // Compiled regex cache
253 protected $_route_regex;
256 * Creates a new route. Sets the URI and regular expressions for keys.
257 * Routes should always be created with [Route::set] or they will not
258 * be properly stored.
260 * $route = new Route($uri, $regex);
262 * @param string route URI pattern
263 * @param array key patterns
265 * @uses Route::_compile
267 public function __construct($uri = NULL, array $regex = NULL)
271 // Assume the route is from cache
275 if ( ! empty($regex))
277 $this->_regex
= $regex;
280 // Store the URI that this route will match
283 // Store the compiled regex locally
284 $this->_route_regex
= Route
::compile($uri, $regex);
288 * Provides default values for keys when they are not present. The default
289 * action will always be "index" unless it is overloaded here.
291 * $route->defaults(array(
292 * 'controller' => 'welcome',
293 * 'action' => 'index'
296 * @param array key values
299 public function defaults(array $defaults = NULL)
301 $this->_defaults
= $defaults;
307 * Tests if the route matches a given URI. A successful match will return
308 * all of the routed parameters as an array. A failed match will return
311 * // Params: controller = users, action = edit, id = 10
312 * $params = $route->matches('users/edit/10');
314 * This method should almost always be used within an if/else block:
316 * if ($params = $route->matches($uri))
318 * // Parse the parameters
321 * @param string URI to match
322 * @return array on success
323 * @return FALSE on failure
325 public function matches($uri)
327 if ( ! preg_match($this->_route_regex
, $uri, $matches))
331 foreach ($matches as $key => $value)
335 // Skip all unnamed keys
339 // Set the value for all matched keys
340 $params[$key] = $value;
343 foreach ($this->_defaults
as $key => $value)
345 if ( ! isset($params[$key]) OR $params[$key] === '')
347 // Set default values for any key that was not matched
348 $params[$key] = $value;
356 * Generates a URI for the current route based on the parameters given.
358 * // Using the "default" route: "users/profile/10"
360 * 'controller' => 'users',
361 * 'action' => 'profile',
365 * @param array URI parameters
367 * @throws Kohana_Exception
368 * @uses Route::REGEX_Key
370 public function uri(array $params = NULL)
372 if ($params === NULL)
374 // Use the default parameters
375 $params = $this->_defaults
;
379 // Add the default parameters
380 $params +
= $this->_defaults
;
383 // Start with the routed URI
386 if (strpos($uri, '<') === FALSE AND strpos($uri, '(') === FALSE)
388 // This is a static route, no need to replace anything
392 while (preg_match('#\([^()]++\)#', $uri, $match))
394 // Search for the matched value
397 // Remove the parenthesis from the match as the replace
398 $replace = substr($match[0], 1, -1);
400 while (preg_match('#'.Route
::REGEX_KEY
.'#', $replace, $match))
402 list($key, $param) = $match;
404 if ($param === 'host')
407 if (isset($params[$param]))
409 // Replace the key with the parameter value
410 $replace = str_replace($key, $params[$param], $replace);
414 // This group has missing parameters
420 // Replace the group in the URI
421 $uri = str_replace($search, $replace, $uri);
424 while (preg_match('#'.Route
::REGEX_KEY
.'#', $uri, $match))
426 list($key, $param) = $match;
428 if ( ! isset($params[$param]))
430 // Ungrouped parameters are required
431 throw new Kohana_Exception('Required route parameter not passed: :param', array(
436 $uri = str_replace($key, $params[$param], $uri);
439 // Trim all extra slashes from the URI
440 $uri = preg_replace('#//+#', '/', rtrim($uri, '/'));
442 // If the localhost setting matches a local route, return the uri as is
443 if ( ! isset($params['host']) OR in_array($params['host'], Route
::$localhosts))
446 // If the localhost setting does not have a protocol
447 if (strpos($params['host'], '://') === FALSE)
449 // Use the default defined protocol
450 $params['host'] = Route
::$default_protocol.$params['host'];
453 // Compile the final uri and return it
454 return rtrim($params['host'], '/').'/'.$uri;