Routes are used to determine the controller and action for a requested URI. Every route generates a regular expression which is used to match a URI and a route. Routes may also contain keys which can be used to set the controller, action, and parameters.
Each
// This route will only match when <id> is a digit
Route::set('user', 'user/<action>/<id>', ['id' => '\d+']);
// This route will match when <path> is anything
Route::set('file', '<path>', ['path' => '.*']);
It is also possible to create optional segments by using parentheses in the URI definition:
// This is the standard default route, and no keys are required
Route::set('default', '(<controller>(/<action>(/<id>)))');
// This route only requires the <file> key
Route::set('file', '(<path>/)<file>(.<format>)', ['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.
Class declared in SYSPATH/classes/Route.php on line 3.
string(26) "\(((?:(?>[^()]+)|(?R))*)\)"
string(18) "<([a-zA-Z0-9_]++)>"
string(12) "[^/.,;?\n]++"
string(17) "[.\+*?[^\]${}=!|]"
bool
$cacheIndicates whether routes are cached
bool FALSE
string
$default_actiondefault action for all routes
string(5) "index"
string
$default_protocoldefault protocol for all routes
string(7) "http://"
array
$localhostslist of valid localhost entries
array(4) ( 0 => bool FALSE 1 => string(0) "" 2 => string(5) "local" 3 => string(9) "localhost" )
array
$_defaultsarray(2) ( "action" => string(5) "index" "host" => bool FALSE )
array
$_filtersroute filters
array(0)
array
$_regexarray(0)
string
$_route_regexNULL
array
$_routesarray(5) ( "codebench" => object Route(5){ protected _filters => array(0) protected _uri => string(19) "codebench(/<class>)" protected _regex => array(0) protected _defaults => array(3) ( "controller" => string(9) "Codebench" "action" => string(5) "index" "class" => NULL ) protected _route_regex => string(44) "#^codebench(?:/(?P<class>[^/.,;?\n]++))?$#uD" }
"docs/media" => object Route(5){ protected _filters => array(0) protected _uri => string(20) "guide-media(/<file>)" protected _regex => array(1) ( "file" => string(2) ".+" ) protected _defaults => array(3) ( "controller" => string(9) "Userguide" "action" => string(5) "media" "file" => NULL ) protected _route_regex => string(35) "#^guide-media(?:/(?P<file>.+))?$#uD" }
"docs/api" => object Route(5){ protected _filters => array(0) protected _uri => string(19) "guide-api(/<class>)" protected _regex => array(1) ( "class" => string(13) "[a-zA-Z0-9_]+" ) protected _defaults => array(3) ( "controller" => string(9) "Userguide" "action" => string(3) "api" "class" => NULL ) protected _route_regex => string(45) "#^guide-api(?:/(?P<class>[a-zA-Z0-9_]+))?$#uD" }
"docs/guide" => object Route(5){ protected _filters => array(0) protected _uri => string(25) "guide(/<module>(/<page>))" protected _regex => array(1) ( "page" => string(2) ".+" ) protected _defaults => array(3) ( "controller" => string(9) "Userguide" "action" => string(4) "docs" "module" => string(0) "" ) protected _route_regex => string(59) "#^guide(?:/(?P<module>[^/.,;?\n]++)(?:/(?P<page>.+))?)?$#uD" }
"default" => object Route(5){ protected _filters => array(0) protected _uri => string(32) "(<controller>(/<action>(/<id>)))" protected _regex => array(0) protected _defaults => array(2) ( "controller" => string(7) "welcome" "action" => string(5) "index" ) protected _route_regex => string(95) "#^(?:(?P<controller>[^/.,;?\n]++)(?:/(?P<action>[^/.,;?\n]++)(?:/(?P<id>[^/.,;?\n]++))?)?)?$#uD" }
)
string
$_uriroute URI
string(0) ""
Creates a new route. Sets the URI and regular expressions for keys. Routes should always be created with Route::set or they will not be properly stored.
$route = new Route($uri, $regex);
The $uri parameter should be a string for basic regex matching.
string
$uri
= NULL - Route URI pattern array
$regex
= NULL - Key patterns void
public function __construct($uri = null, $regex = null)
{
if ($uri === null) {
// Assume the route is from cache
return;
}
if (!empty($uri)) {
$this->_uri = $uri;
}
if (!empty($regex)) {
$this->_regex = $regex;
}
// Store the compiled regex locally
$this->_route_regex = Route::compile($uri, $regex);
}
Retrieves all named routes.
$routes = Route::all();
array
- Routes by namepublic static function all()
{
return Route::$_routes;
}
Saves or loads the route cache. If your routes will remain the same for a long period of time, use this to reload the routes from the cache rather than redefining them on every page load.
if (! Route::cache()) {
// Set routes here
Route::cache(true);
}
boolean
$save
= bool FALSE - Cache the current routes boolean
$append
= bool FALSE - Append, rather than replace, cached routes when loading void
- When saving routesboolean
- When loading routespublic static function cache($save = false, $append = false)
{
if ($save === true) {
try {
// Cache all defined routes
Kohana::cache('Route::cache()', Route::$_routes);
} catch (Exception $e) {
// We most likely have a lambda in a route, which cannot be cached
throw new Kohana_Exception('One or more routes could not be cached (:message)', [
':message' => $e->getMessage(),
], 0, $e);
}
} else {
if ($routes = Kohana::cache('Route::cache()')) {
if ($append) {
// Append cached routes
Route::$_routes += $routes;
} else {
// Replace existing routes
Route::$_routes = $routes;
}
// Routes were cached
return Route::$cache = true;
} else {
// Routes were not cached
return Route::$cache = false;
}
}
}
Returns the compiled regular expression for the route. This translates keys and optional groups to a proper PCRE regular expression.
$compiled = Route::compile('<controller>(/<action>(/<id>))', [
'controller' => '[a-z]+',
'id' => '\d+',
]
);
string
public static function compile($uri, array $regex = null)
{
// The URI should be considered literal except for keys and optional parts
// Escape everything preg_quote would escape except for : ( ) < >
$expression = preg_replace('#' . Route::REGEX_ESCAPE . '#', '\\\\$0', $uri);
if (strpos($expression, '(') !== false) {
// Make optional parts of the URI non-capturing and optional
$expression = str_replace(['(', ')'], ['(?:', ')?'], $expression);
}
// Insert default regex for keys
$expression = str_replace(['<', '>'], ['(?P<', '>' . Route::REGEX_SEGMENT . ')'], $expression);
if ($regex) {
$search = $replace = [];
foreach ($regex as $key => $value) {
$search[] = "<$key>" . Route::REGEX_SEGMENT;
$replace[] = "<$key>$value";
}
// Replace the default regex with the user-specified regex
$expression = str_replace($search, $replace, $expression);
}
return '#^' . $expression . '$#uD';
}
Provides default values for keys when they are not present. The default action will always be "index" unless it is overloaded here.
$route->defaults([
'controller' => 'welcome',
'action' => 'index'
]);
If no parameter is passed, this method will act as a getter.
array
$defaults
= NULL - Key values $this
- Or arraypublic function defaults(array $defaults = null)
{
if ($defaults === null) {
return $this->_defaults;
}
$this->_defaults = $defaults;
return $this;
}
Filters to be run before route parameters are returned:
$route->filter(function(Route $route, $params, Request $request) {
// This route only matches POST requests
if ($request->method() !== HTTP_Request::POST) {
return false;
}
if ($params AND $params['controller'] === 'welcome') {
$params['controller'] = 'home';
}
return $params;
});
To prevent a route from matching, return false
. To replace the route
parameters, return an array.
Default parameters are added before filters are called!
array
$callback
required - Callback string, array, or closure $this
public function filter($callback)
{
if (!is_callable($callback)) {
throw new Kohana_Exception('Invalid Route::callback specified');
}
$this->_filters[] = $callback;
return $this;
}
Retrieves a named route.
$route = Route::get('default');
string
$name
required - Route name Route
public static function get($name)
{
if (!isset(Route::$_routes[$name])) {
throw new Kohana_Exception('The requested route does not exist: :route', [':route' => $name]);
}
return Route::$_routes[$name];
}
Returns whether this route is an external route to a remote controller.
boolean
public function is_external()
{
return !in_array(Arr::get($this->_defaults, 'host', false), Route::$localhosts);
}
Tests if the route matches a given Request. A successful match will return all of the routed parameters as an array. A failed match will return boolean false.
// Params: controller = users, action = edit, id = 10
$params = $route->matches(Request::factory('users/edit/10'));
This method should almost always be used within an if/else block:
if ($params = $route->matches($request)) {
// Parse the parameters
}
Request
$request
required - Request object to match array
- On successfalse
- On failurepublic function matches(Request $request)
{
// Get the URI from the Request
$uri = trim($request->uri(), '/');
if (!preg_match($this->_route_regex, $uri, $matches))
return false;
$params = [];
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]) OR $params[$key] === '') {
// Set default values for any key that was not matched
$params[$key] = $value;
}
}
if (!empty($params['controller'])) {
// PSR-0: Replace underscores with spaces, run ucwords, then replace underscore
$params['controller'] = str_replace(' ', '_', ucwords(str_replace('_', ' ', $params['controller'])));
}
if (!empty($params['directory'])) {
// PSR-0: Replace underscores with spaces, run ucwords, then replace underscore
$params['directory'] = str_replace(' ', '_', ucwords(str_replace('_', ' ', $params['directory'])));
}
if ($this->_filters) {
foreach ($this->_filters as $callback) {
// Execute the filter giving it the route, params, and request
$return = call_user_func($callback, $this, $params, $request);
if ($return === false) {
// Filter has aborted the match
return false;
} elseif (is_array($return)) {
// Filter has modified the parameters
$params = $return;
}
}
}
return $params;
}
Get the name of a route.
$name = Route::name($route)
Route
$route
required - Instance string
public static function name(Route $route)
{
return array_search($route, Route::$_routes);
}
Stores a named route and returns it. The "action" will always be set to "index" if it is not defined.
Route::set('default', '(<controller>(/<action>(/<id>)))')
->defaults([
'controller' => 'welcome',
]);
string
$name
required - Route name string
$uri
= NULL - URI pattern array
$regex
= NULL - Regex patterns for route keys Route
public static function set($name, $uri = null, $regex = null)
{
return Route::$_routes[$name] = new Route($uri, $regex);
}
Generates a URI for the current route based on the parameters given.
// Using the "default" route: "users/profile/10"
$route->uri([
'controller' => 'users',
'action' => 'profile',
'id' => '10'
]);
array
$params
= NULL - URI parameters string
public function uri(array $params = null)
{
if ($params) {
// @issue #4079 rawurlencode parameters
$params = array_map('rawurlencode', $params);
// decode slashes back, see Apache docs about AllowEncodedSlashes and AcceptPathInfo
$params = str_replace(['%2F', '%5C'], ['/', '\\'], $params);
}
$defaults = $this->_defaults;
/**
* Recursively compiles a portion of a URI specification by replacing
* the specified parameters and any optional parameters that are needed.
*
* @param string $portion Part of the URI specification
* @param boolean $required Whether or not parameters are required (initially)
* @return array Tuple of the compiled portion and whether or not it contained specified parameters
*/
$compile = function ($portion, $required) use (&$compile, $defaults, $params) {
$missing = [];
$pattern = '#(?:' . Route::REGEX_KEY . '|' . Route::REGEX_GROUP . ')#';
$result = preg_replace_callback($pattern, function ($matches) use (&$compile, $defaults, &$missing, $params, &$required) {
if ($matches[0][0] === '<') {
// Parameter, unwrapped
$param = $matches[1];
if (isset($params[$param])) {
// This portion is required when a specified
// parameter does not match the default
$required = ($required OR ! isset($defaults[$param]) OR $params[$param] !== $defaults[$param]);
// Add specified parameter to this result
return $params[$param];
}
// Add default parameter to this result
if (isset($defaults[$param]))
return $defaults[$param];
// This portion is missing a parameter
$missing[] = $param;
}
else {
// Group, unwrapped
$result = $compile($matches[2], false);
if ($result[1]) {
// This portion is required when it contains a group
// that is required
$required = true;
// Add required groups to this result
return $result[0];
}
// Do not add optional groups to this result
}
}, $portion);
if ($required AND $missing) {
throw new Kohana_Exception('Required route parameter not passed: :param', [':param' => reset($missing)]);
}
return [$result, $required];
};
list($uri) = $compile($this->_uri, true);
// Trim all extra slashes from the URI
$uri = preg_replace('#//+#', '/', rtrim($uri, '/'));
if ($this->is_external()) {
// Need to add the host to the URI
$host = $this->_defaults['host'];
if (strpos($host, '://') === false) {
// Use the default defined protocol
$host = Route::$default_protocol . $host;
}
// Clean up the host and prepend it to the URI
$uri = rtrim($host, '/') . '/' . $uri;
}
return $uri;
}
Create a URL from a route name. This is a shortcut for:
echo URL::site(Route::get($name)->uri($params), $protocol);
string
$name
required - Route name array
$params
= NULL - URI parameters mixed
$protocol
= NULL - Protocol string or boolean, adds protocol and domain string
public static function url($name, array $params = null, $protocol = null)
{
$route = Route::get($name);
// Create a URI with the route and convert it to a URL
if ($route->is_external())
return $route->uri($params);
else
return URL::site($route->uri($params), $protocol);
}