/Slim/Route.php
PHP | 349 lines | 121 code | 33 blank | 195 comment | 14 complexity | 725eb3f070ee1613dbc24b0cb5d4007f MD5 | raw file
- <?php
- /**
- * Slim - a micro PHP 5 framework
- *
- * @author Josh Lockhart <info@joshlockhart.com>
- * @copyright 2011 Josh Lockhart
- * @link http://www.slimframework.com
- * @license http://www.slimframework.com/license
- * @version 1.5.0
- *
- * MIT LICENSE
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files (the
- * "Software"), to deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, sublicense, and/or sell copies of the Software, and to
- * permit persons to whom the Software is furnished to do so, subject to
- * the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
- * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
- * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
- /**
- * Route
- *
- * @package Slim
- * @author Josh Lockhart <info@joshlockhart.com>
- * @since Version 1.0
- */
- class Slim_Route {
- /**
- * @var string The route pattern (ie. "/books/:id")
- */
- protected $pattern;
- /**
- * @var mixed The callable associated with this route
- */
- protected $callable;
- /**
- * @var array Conditions for this route's URL parameters
- */
- protected $conditions = array();
- /**
- * @var array Default conditions applied to all Route instances
- */
- protected static $defaultConditions = array();
- /**
- * @var string The name of this route (optional)
- */
- protected $name;
- /**
- * @var array Key-value array of URL parameters
- */
- protected $params = array();
- /**
- * @var Slim_Router The Router to which this Route belongs
- */
- protected $router;
- /**
- * @var array[Callable] Middleware
- */
- protected $middleware = array();
- /**
- * Constructor
- * @param string $pattern The URL pattern (ie. "/books/:id")
- * @param mixed $callable Anything that returns TRUE for is_callable()
- */
- public function __construct( $pattern, $callable ) {
- $this->setPattern($pattern);
- $this->setCallable($callable);
- $this->setConditions(self::getDefaultConditions());
- }
- /**
- * Set default route conditions for all instances
- * @param array $defaultConditions
- * @return void
- */
- public static function setDefaultConditions( array $defaultConditions ) {
- self::$defaultConditions = $defaultConditions;
- }
- /**
- * Get default route conditions for all instances
- * @return array
- */
- public static function getDefaultConditions() {
- return self::$defaultConditions;
- }
- /**
- * Get route pattern
- * @return string
- */
- public function getPattern() {
- return $this->pattern;
- }
- /**
- * Set route pattern
- * @param string $pattern
- * @return void
- */
- public function setPattern( $pattern ) {
- $this->pattern = str_replace(')', ')?', (string)$pattern);
- }
- /**
- * Get route callable
- * @return mixed
- */
- public function getCallable() {
- return $this->callable;
- }
- /**
- * Set route callable
- * @param mixed $callable
- * @return void
- */
- public function setCallable($callable) {
- $this->callable = $callable;
- }
- /**
- * Get route conditions
- * @return array
- */
- public function getConditions() {
- return $this->conditions;
- }
- /**
- * Set route conditions
- * @param array $conditions
- * @return void
- */
- public function setConditions( array $conditions ) {
- $this->conditions = $conditions;
- }
- /**
- * Get route name
- * @return string|null
- */
- public function getName() {
- return $this->name;
- }
- /**
- * Set route name
- * @param string $name
- * @return void
- */
- public function setName( $name ) {
- $this->name = (string)$name;
- $this->router->cacheNamedRoute($this->name, $this);
- }
- /**
- * Get route parameters
- * @return array
- */
- public function getParams() {
- return $this->params;
- }
- /**
- * Get router
- * @return Slim_Router
- */
- public function getRouter() {
- return $this->router;
- }
- /**
- * Set router
- * @param Slim_Router $router
- * @return void
- */
- public function setRouter( Slim_Router $router ) {
- $this->router = $router;
- }
- /**
- * Get middleware
- * @return array[Callable]
- */
- public function getMiddleware() {
- return $this->middleware;
- }
- /**
- * Set middleware
- *
- * This method allows middleware to be assigned to a specific Route.
- * If the method argument `is_callable` (including callable arrays!),
- * we directly append the argument to `$this->middleware`. Else, we
- * assume the argument is an array of callables and merge the array
- * with `$this->middleware`. Even if non-callables are included in the
- * argument array, we still merge them; we lazily check each item
- * against `is_callable` during Route::dispatch().
- *
- * @param Callable|array[Callable]
- * @return Slim_Route
- * @throws InvalidArgumentException If argument is not callable or not an array
- */
- public function setMiddleware( $middleware ) {
- if ( is_callable($middleware) ) {
- $this->middleware[] = $middleware;
- } else if ( is_array($middleware) ) {
- $this->middleware = array_merge($this->middleware, $middleware);
- } else {
- throw new InvalidArgumentException('Route middleware must be callable or an array of callables');
- }
- return $this;
- }
- /**
- * Matches URI?
- *
- * Parse this route's pattern, and then compare it to an HTTP resource URI
- * This method was modeled after the techniques demonstrated by Dan Sosedoff at:
- *
- * http://blog.sosedoff.com/2009/09/20/rails-like-php-url-router/
- *
- * @param string $resourceUri A Request URI
- * @return bool
- */
- public function matches( $resourceUri ) {
- //Extract URL params
- preg_match_all('@:([\w]+)@', $this->pattern, $paramNames, PREG_PATTERN_ORDER);
- $paramNames = $paramNames[0];
- //Convert URL params into regex patterns, construct a regex for this route
- $patternAsRegex = preg_replace_callback('@:[\w]+@', array($this, 'convertPatternToRegex'), $this->pattern);
- if ( substr($this->pattern, -1) === '/' ) {
- $patternAsRegex = $patternAsRegex . '?';
- }
- $patternAsRegex = '@^' . $patternAsRegex . '$@';
- //Cache URL params' names and values if this route matches the current HTTP request
- if ( preg_match($patternAsRegex, $resourceUri, $paramValues) ) {
- array_shift($paramValues);
- foreach ( $paramNames as $index => $value ) {
- $val = substr($value, 1);
- if ( isset($paramValues[$val]) ) {
- $this->params[$val] = urldecode($paramValues[$val]);
- }
- }
- return true;
- } else {
- return false;
- }
- }
- /**
- * Convert a URL parameter (ie. ":id") into a regular expression
- * @param array URL parameters
- * @return string Regular expression for URL parameter
- */
- protected function convertPatternToRegex( $matches ) {
- $key = str_replace(':', '', $matches[0]);
- if ( array_key_exists($key, $this->conditions) ) {
- return '(?P<' . $key . '>' . $this->conditions[$key] . ')';
- } else {
- return '(?P<' . $key . '>[a-zA-Z0-9_\-\.\!\~\*\\\'\(\)\:\@\&\=\$\+,%]+)';
- }
- }
- /**
- * Set route name
- * @param string $name The name of the route
- * @return Slim_Route
- */
- public function name( $name ) {
- $this->setName($name);
- return $this;
- }
- /**
- * Merge route conditions
- * @param array $conditions Key-value array of URL parameter conditions
- * @return Slim_Route
- */
- public function conditions( array $conditions ) {
- $this->conditions = array_merge($this->conditions, $conditions);
- return $this;
- }
- /**
- * Dispatch route
- *
- * This method invokes this route's callable. If middleware is
- * registered for this route, each callable middleware is invoked in
- * the order specified.
- *
- * This method is smart about trailing slashes on the route pattern.
- * If this route's pattern is defined with a trailing slash, and if the
- * current request URI does not have a trailing slash but otherwise
- * matches this route's pattern, a Slim_Exception_RequestSlash
- * will be thrown triggering an HTTP 301 Permanent Redirect to the same
- * URI _with_ a trailing slash. This Exception is caught in the
- * `Slim::run` loop. If this route's pattern is defined without a
- * trailing slash, and if the current request URI does have a trailing
- * slash, this route will not be matched and a 404 Not Found
- * response will be sent if no subsequent matching routes are found.
- *
- * @return bool Was route callable invoked successfully?
- * @throws Slim_Exception_RequestSlash
- */
- public function dispatch() {
- if ( substr($this->pattern, -1) === '/' && substr($this->router->getRequest()->getResourceUri(), -1) !== '/' ) {
- throw new Slim_Exception_RequestSlash();
- }
- //Invoke middleware
- foreach ( $this->middleware as $mw ) {
- if ( is_callable($mw) ) {
- call_user_func($mw);
- }
- }
- //Invoke callable
- if ( is_callable($this->getCallable()) ) {
- call_user_func_array($this->callable, array_values($this->params));
- return true;
- }
- return false;
- }
- }