Merge branch 'maint/7.0'
[ninja.git] / system / libraries / Router.php
blob0aa4a377b8526c4d97eb4bed9b1fd0c07185078f
1 <?php defined('SYSPATH') OR die('No direct access allowed.');
2 /**
3 * Router
5 * $Id: Router.php 3917 2009-01-21 03:06:22Z zombor $
7 * @package Core
8 * @author Kohana Team
9 * @copyright (c) 2007-2008 Kohana Team
10 * @license http://kohanaphp.com/license.html
12 class Router {
14 protected static $routes;
16 public static $current_uri = '';
17 public static $query_string = '';
18 public static $complete_uri = '';
19 public static $routed_uri = '';
20 public static $url_suffix = '';
22 public static $segments;
23 public static $rsegments;
25 public static $controller;
26 public static $controller_path;
28 public static $method = 'index';
29 public static $arguments = array();
31 /**
32 * Router setup routine. Automatically called during Kohana setup process.
34 * @return void
36 public static function setup()
38 if ( ! empty($_SERVER['QUERY_STRING']))
40 // Set the query string to the current query string
41 self::$query_string = '?'.trim($_SERVER['QUERY_STRING'], '&/');
44 if (self::$routes === NULL)
46 // Load routes
47 self::$routes = Kohana::config('routes');
50 // Default route status
51 $default_route = FALSE;
53 if (self::$current_uri === '')
55 // Make sure the default route is set
56 if ( ! isset(self::$routes['_default']))
57 throw new Kohana_Exception('core.no_default_route');
59 // Use the default route when no segments exist
60 self::$current_uri = self::$routes['_default'];
62 // Default route is in use
63 $default_route = TRUE;
66 // Make sure the URL is not tainted with HTML characters
67 self::$current_uri = html::specialchars(self::$current_uri, FALSE);
69 // Remove all dot-paths from the URI, they are not valid
70 self::$current_uri = preg_replace('#\.[\s./]*/#', '', self::$current_uri);
72 // At this point segments, rsegments, and current URI are all the same
73 self::$segments = self::$rsegments = self::$current_uri = trim(self::$current_uri, '/');
75 // Set the complete URI
76 self::$complete_uri = self::$current_uri.self::$query_string;
78 // Explode the segments by slashes
79 self::$segments = ($default_route === TRUE OR self::$segments === '') ? array() : explode('/', self::$segments);
81 if ($default_route === FALSE AND count(self::$routes) > 1)
83 // Custom routing
84 self::$rsegments = self::routed_uri(self::$current_uri);
87 // The routed URI is now complete
88 self::$routed_uri = self::$rsegments;
90 // Routed segments will never be empty
91 self::$rsegments = explode('/', self::$rsegments);
93 // Prepare to find the controller
94 $controller_path = '';
95 $method_segment = NULL;
97 // Paths to search
98 $paths = Kohana::include_paths();
100 foreach (self::$rsegments as $key => $segment)
102 // Add the segment to the search path
103 $controller_path .= $segment;
105 $found = FALSE;
106 foreach ($paths as $dir)
108 // Search within controllers only
109 $dir .= 'controllers/';
111 if (is_dir($dir.$controller_path) OR is_file($dir.$controller_path.EXT))
113 // Valid path
114 $found = TRUE;
116 // The controller must be a file that exists with the search path
117 // Allow break the $dir-jail with symlinks outside production.
118 // (not in production due to possible security issues)
119 if ($c = str_replace('\\', '/', realpath($dir.$controller_path.EXT))
120 AND is_file($c))
122 // Set controller name
123 self::$controller = $segment;
125 // Change controller path
126 self::$controller_path = $c;
128 // Set the method segment
129 $method_segment = $key + 1;
131 // Stop searching
132 break;
137 if ($found === FALSE)
139 // Maximum depth has been reached, stop searching
140 break;
143 // Add another slash
144 $controller_path .= '/';
147 if ($method_segment !== NULL AND isset(self::$rsegments[$method_segment]))
149 // Set method
150 self::$method = self::$rsegments[$method_segment];
152 if (isset(self::$rsegments[$method_segment + 1]))
154 // Set arguments
155 self::$arguments = array_slice(self::$rsegments, $method_segment + 1);
159 // Last chance to set routing before a 404 is triggered
160 Event::run('system.post_routing');
162 if (self::$controller === NULL)
164 // No controller was found, so no page can be rendered
165 Event::run('system.404');
170 * Attempts to determine the current URI using CLI, GET, PATH_INFO, ORIG_PATH_INFO, or PHP_SELF.
172 * @return void
174 public static function find_uri()
176 if (PHP_SAPI === 'cli')
178 // Command line requires a bit of hacking
179 if (isset($_SERVER['argv'][1]))
181 self::$current_uri = $_SERVER['argv'][1];
183 // Remove GET string from segments
184 if (($query = strpos(self::$current_uri, '?')) !== FALSE)
186 list (self::$current_uri, $query) = explode('?', self::$current_uri, 2);
188 // Parse the query string into $_GET
189 parse_str($query, $_GET);
191 // Convert $_GET to UTF-8
192 $_GET = utf8::clean($_GET);
196 elseif (isset($_GET['kohana_uri']))
198 // Use the URI defined in the query string
199 self::$current_uri = $_GET['kohana_uri'];
201 // Remove the URI from $_GET
202 unset($_GET['kohana_uri']);
204 // Remove the URI from $_SERVER['QUERY_STRING']
205 $_SERVER['QUERY_STRING'] = preg_replace('~\bkohana_uri\b[^&]*+&?~', '', $_SERVER['QUERY_STRING']);
207 elseif (isset($_SERVER['PATH_INFO']) AND $_SERVER['PATH_INFO'])
209 self::$current_uri = $_SERVER['PATH_INFO'];
211 elseif (isset($_SERVER['ORIG_PATH_INFO']) AND $_SERVER['ORIG_PATH_INFO'])
213 self::$current_uri = $_SERVER['ORIG_PATH_INFO'];
215 elseif (isset($_SERVER['PHP_SELF']) AND $_SERVER['PHP_SELF'])
217 self::$current_uri = $_SERVER['PHP_SELF'];
220 // The front controller directory and filename
221 $fc = substr(realpath($_SERVER['SCRIPT_FILENAME']), strlen(DOCROOT));
223 if (($strpos_fc = strpos(self::$current_uri, $fc)) !== FALSE)
225 // Remove the front controller from the current uri
226 self::$current_uri = substr(self::$current_uri, $strpos_fc + strlen($fc));
229 // Remove slashes from the start and end of the URI
230 self::$current_uri = trim(self::$current_uri, '/');
232 if (self::$current_uri !== '')
234 if ($suffix = Kohana::config('core.url_suffix') AND strpos(self::$current_uri, $suffix) !== FALSE)
236 // Remove the URL suffix
237 self::$current_uri = preg_replace('#'.preg_quote($suffix).'$#u', '', self::$current_uri);
239 // Set the URL suffix
240 self::$url_suffix = $suffix;
243 // Reduce multiple slashes into single slashes
244 self::$current_uri = preg_replace('#//+#', '/', self::$current_uri);
249 * Generates routed URI from given URI.
251 * @param string URI to convert
252 * @return string Routed uri
254 public static function routed_uri($uri)
256 if (self::$routes === NULL)
258 // Load routes
259 self::$routes = Kohana::config('routes');
262 // Prepare variables
263 $routed_uri = $uri = trim($uri, '/');
265 if (isset(self::$routes[$uri]))
267 // Literal match, no need for regex
268 $routed_uri = self::$routes[$uri];
270 else
272 // Loop through the routes and see if anything matches
273 foreach (self::$routes as $key => $val)
275 if ($key === '_default') continue;
277 // Trim slashes
278 $key = trim($key, '/');
279 $val = trim($val, '/');
281 if (preg_match('#^'.$key.'$#u', $uri))
283 if (strpos($val, '$') !== FALSE)
285 // Use regex routing
286 $routed_uri = preg_replace('#^'.$key.'$#u', $val, $uri);
288 else
290 // Standard routing
291 $routed_uri = $val;
294 // A valid route has been found
295 break;
300 if (isset(self::$routes[$routed_uri]))
302 // Check for double routing (without regex)
303 $routed_uri = self::$routes[$routed_uri];
306 return trim($routed_uri, '/');
309 } // End Router