route.php 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. <?php namespace Laravel\Routing;
  2. use Closure;
  3. use Laravel\Arr;
  4. use Laravel\Response;
  5. class Route {
  6. /**
  7. * The route key, including request method and URI.
  8. *
  9. * @var string
  10. */
  11. public $key;
  12. /**
  13. * The URIs the route responds to.
  14. *
  15. * @var array
  16. */
  17. public $uris;
  18. /**
  19. * The route callback or array.
  20. *
  21. * @var mixed
  22. */
  23. public $callback;
  24. /**
  25. * The parameters that will passed to the route callback.
  26. *
  27. * @var array
  28. */
  29. public $parameters;
  30. /**
  31. * Create a new Route instance.
  32. *
  33. * @param string $key
  34. * @param mixed $callback
  35. * @param array $parameters
  36. * @return void
  37. */
  38. public function __construct($key, $callback, $parameters = array())
  39. {
  40. $this->key = $key;
  41. $this->callback = $callback;
  42. $this->parameters = $parameters;
  43. // Extract each URI from the route key. Since the route key has the
  44. // request method, we will extract that from the string. If the URI
  45. // points to the root of the application, a single forward slash
  46. // will be returned since that is used for the root route.
  47. if (strpos($key, ', ') === false)
  48. {
  49. $this->uris = array($this->extract($this->key));
  50. }
  51. else
  52. {
  53. $this->uris = array_map(array($this, 'extract'), explode(', ', $key));
  54. }
  55. if ( ! $this->callable($callback))
  56. {
  57. throw new \InvalidArgumentException('Invalid route defined for URI ['.$this->key.']');
  58. }
  59. }
  60. /**
  61. * Determine if the given route callback is callable.
  62. *
  63. * Route callbacks must be either a Closure, array, or string.
  64. *
  65. * @param mixed $callback
  66. * @return bool
  67. */
  68. protected function callable($callback)
  69. {
  70. return $callback instanceof Closure or is_array($callback) or is_string($callback);
  71. }
  72. /**
  73. * Retrieve the URI from a given route destination.
  74. *
  75. * If the request is to the root of the application, a single slash
  76. * will be returned, otherwise the leading slash will be removed.
  77. *
  78. * @param string $segment
  79. * @return string
  80. */
  81. protected function extract($segment)
  82. {
  83. $segment = substr($segment, strpos($segment, ' ') + 1);
  84. return ($segment !== '/') ? trim($segment, '/') : $segment;
  85. }
  86. /**
  87. * Call a given route and return the route's response.
  88. *
  89. * @return Response
  90. */
  91. public function call()
  92. {
  93. // Since "before" filters can halt the request cycle, we will return
  94. // any response from the before filters. Allowing filters to halt the
  95. // request cycle makes tasks like authorization convenient.
  96. //
  97. // The route is responsible for running the global filters, and any
  98. // filters defined on the route itself. Since all incoming requests
  99. // come through a route (either defined or ad-hoc), it makes sense
  100. // to let the route handle the global filters. If the route uses
  101. // a controller, the controller will only call its own filters.
  102. $before = array_merge(array('before'), $this->filters('before'));
  103. $response = Filter::run($before, array(), true);
  104. if (is_null($response) and ! is_null($response = $this->response()))
  105. {
  106. if ($response instanceof Delegate)
  107. {
  108. $response = Controller::call($response->destination, $this->parameters);
  109. }
  110. }
  111. if ( ! $response instanceof Response)
  112. {
  113. $response = new Response($response);
  114. }
  115. // Stringify the response. We need to force the response to be
  116. // stringed before closing the session, since the developer may
  117. // be using the session within their views, so we cannot age
  118. // the session data until the view is rendered.
  119. $response->content = $response->render();
  120. $filters = array_merge($this->filters('after'), array('after'));
  121. Filter::run($filters, array($response));
  122. return $response;
  123. }
  124. /**
  125. * Call the closure defined for the route, or get the route delegator.
  126. *
  127. * Note that this method differs from the "call" method in that it does
  128. * not resolve the controller or prepare the response. Delegating to
  129. * controller's is handled by the "call" method.
  130. *
  131. * @return mixed
  132. */
  133. protected function response()
  134. {
  135. // If the route callback is an instance of a Closure, we can call the
  136. // route function directly. There are no before or after filters to
  137. // parse out of the route.
  138. if ($this->callback instanceof Closure)
  139. {
  140. return call_user_func_array($this->callback, $this->parameters);
  141. }
  142. // If the route is an array, we will return the first value with a
  143. // key of "uses", or the first instance of a Closure. If the value
  144. // is a string, the route is delegating the responsibility for
  145. // for handling the request to a controller.
  146. elseif (is_array($this->callback))
  147. {
  148. $callback = Arr::first($this->callback, function($key, $value)
  149. {
  150. return $key == 'uses' or $value instanceof Closure;
  151. });
  152. if ($callback instanceof Closure)
  153. {
  154. return call_user_func_array($callback, $this->parameters);
  155. }
  156. else
  157. {
  158. return new Delegate($callback);
  159. }
  160. }
  161. elseif (is_string($this->callback))
  162. {
  163. return new Delegate($this->callback);
  164. }
  165. }
  166. /**
  167. * Get an array of filter names defined for the route.
  168. *
  169. * @param string $name
  170. * @return array
  171. */
  172. public function filters($name)
  173. {
  174. if (is_array($this->callback) and isset($this->callback[$name]))
  175. {
  176. $filters = $this->callback[$name];
  177. return (is_string($filters)) ? explode('|', $filters) : (array) $filters;
  178. }
  179. return array();
  180. }
  181. /**
  182. * Determine if the route has a given name.
  183. *
  184. * @param string $name
  185. * @return bool
  186. */
  187. public function is($name)
  188. {
  189. return is_array($this->callback) and Arr::get($this->callback, 'name') === $name;
  190. }
  191. /**
  192. * Determine if the route handles a given URI.
  193. *
  194. * @param string $uri
  195. * @return bool
  196. */
  197. public function handles($uri)
  198. {
  199. return in_array($uri, $this->uris);
  200. }
  201. /**
  202. * Magic Method to handle dynamic method calls to determine the name of the route.
  203. */
  204. public function __call($method, $parameters)
  205. {
  206. if (strpos($method, 'is_') === 0)
  207. {
  208. return $this->is(substr($method, 3));
  209. }
  210. throw new \BadMethodCallException("Call to undefined method [$method] on Route class.");
  211. }
  212. }