name = $name; $this->rule = $rule; $this->defaults = $defaults; } /** * Generates a url for a route * * @param array $params Parameters to substitute in the route * @param bool $absolute Whether to return an absolute url * @param string $protocol Protocol to use for absolute url * @return string Generated url * @access public */ public function url($params = array(), $absolute = false, $protocol = 'http') { if (is_callable($this->rule)) throw new Exception("The rule for '{$this->name}' route is a function and cannot be reversed"); $url = is_array($this->rule) ? $this->rule[0] : $this->rule; $replace = array(); $params = array_merge($this->defaults, $params); foreach ($params as $key => $value) $replace["<{$key}>"] = $value; $url = str_replace(array_keys($replace), array_values($replace), $url); $count = 1; $chars = '[^\(\)]*?'; while ($count > 0) $url = preg_replace("#\({$chars}<{$chars}>{$chars}\)#", '', $url, -1, $count); $url = Config::get('core.basepath','/').str_replace(array('(', ')'), '', $url); if ($absolute) $url = $protocol.'://'.$_SERVER['HTTP_HOST'].$url; return $url; } /** * Ads a route * * @param string $name Name of the route. Routes with the same name will override one another. * @param mixed $rule Either an expression to match URI against or a function that will * be passed the URI and must return either an associative array of * extracted parameters (if it matches) or False. * @param array $defaults An associated array of default values. * @return void * @access public * @static */ public static function add($name, $rule, $defaults = array()) { Route::$rules[$name] = array( 'rule' => $rule, 'defaults' => $defaults ); } /** * Gets route by name * * @param string $name Route name * @return Route * @access public * @throws Exception If specified route doesn't exist * @static */ public static function get($name) { if (!isset(Route::$rules[$name])) throw new Exception("Route {$name} not found."); if (!isset(Route::$routes[$name])) { $rules = Route::$rules[$name]; Route::$routes[$name] = new static($name, $rules['rule'], $rules['defaults']); } return Route::$routes[$name]; } /** * Matches the URI against available routes to find the correct one. * * @param string $uri Request URI * @return Route * @access public * @throws Exception If no route matches the URI * @throws Exception If route matched but no Controller was defined for it * @throws Exception If route matched but no action was defined for it * @static */ public static function match($uri) { $matched = false; foreach (Route::$rules as $name => $rule) { $rule = $rule['rule']; if (is_callable($rule)) { if (($data = $rule($uri)) !== FALSE) { $matched = $name; break; } } else { $pattern = is_array($rule) ? $rule[0] : $rule; $pattern = str_replace(')', ')?', $pattern); $pattern = preg_replace_callback('/<.*?>/', function($str) use ($rule) { $str = $str[0]; $regexp = '[a-zA-Z0-9\-\._]+'; if (is_array($rule)) $regexp = Misc::arr($rule[1], str_replace(array('<', '>'), '', $str), $regexp); return '(?P'.$str.$regexp.')'; }, $pattern); preg_match('#^'.$pattern.'/?$#', $uri, $match); if (!empty($match[0])) { $matched = $name; $data = array(); foreach ($match as $k => $v) if (!is_numeric($k)) $data[$k] = $v; break; } } } if ($matched == false) throw new Exception('No route matched your request', 404); $rule = Route::$rules[$matched]; $params = array_merge($rule['defaults'], $data); if (!isset($params['controller'])) throw new Exception("Route {$matched} matched, but no controller was defined for this route", 404); if (!isset($params['action'])) throw new Exception("Route {$matched} matched with controller {$params['controller']}, but no action was defined for this route", 404); $route = Route::get($matched); $route->params = $params; return $route; } }