caller.php 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. <?php namespace Laravel\Routing;
  2. use Closure;
  3. use Laravel\Response;
  4. use Laravel\Container;
  5. use Laravel\Controller;
  6. class Caller {
  7. /**
  8. * The IoC container instance.
  9. *
  10. * @var Container
  11. */
  12. protected $container;
  13. /**
  14. * The route filters defined for the application.
  15. *
  16. * @var array
  17. */
  18. protected $filters;
  19. /**
  20. * The path to the application's controllers.
  21. *
  22. * @var string
  23. */
  24. protected $path;
  25. /**
  26. * Create a new route caller instance.
  27. *
  28. * @param Container $container
  29. * @param Delegator $delegator
  30. * @param array $filters
  31. * @return void
  32. */
  33. public function __construct(Container $container, $filters, $path)
  34. {
  35. $this->path = $path;
  36. $this->filters = $filters;
  37. $this->container = $container;
  38. }
  39. /**
  40. * Call a given route and return the route's response.
  41. *
  42. * @param Route $route
  43. * @return Response
  44. */
  45. public function call(Route $route)
  46. {
  47. // Since "before" filters can halt the request cycle, we will return any response
  48. // from the before filters. Allowing the filters to halt the request cycle makes
  49. // common tasks like authorization convenient to implement.
  50. $before = array_merge(array('before'), $route->filters('before'));
  51. if ( ! is_null($response = $this->filter($before, array(), true)))
  52. {
  53. return $this->finish($route, $response);
  54. }
  55. // If a route returns a Delegate, it means the route is delegating the handling
  56. // of the request to a controller method. We will pass the Delegate instance
  57. // to the "delegate" method which will call the controller.
  58. if ($route->delegates())
  59. {
  60. return $this->delegate($route, $route->call());
  61. }
  62. // If no before filters returned a response and the route is not delegating
  63. // execution to a controller, we will call the route like normal and return
  64. // the response. If the no response is given by the route, we will return
  65. // the 404 error view.
  66. elseif ( ! is_null($response = $route->call()))
  67. {
  68. return $this->finish($route, $response);
  69. }
  70. else
  71. {
  72. return $this->finish($route, Response::error('404'));
  73. }
  74. }
  75. /**
  76. * Handle the delegation of a route to a controller method.
  77. *
  78. * @param Route $route
  79. * @param Delegate $delegate
  80. * @return mixed
  81. */
  82. protected function delegate(Route $route, Delegate $delegate)
  83. {
  84. // Route delegates follow a {controller}@{method} naming convention. For example,
  85. // to delegate to the "home" controller's "index" method, the delegate should be
  86. // formatted like "home@index". Nested controllers may be delegated to using dot
  87. // syntax, like so: "user.profile@show".
  88. if (strpos($delegate->destination, '@') === false)
  89. {
  90. throw new \Exception("Route delegate [{$delegate->destination}] has an invalid format.");
  91. }
  92. list($controller, $method) = explode('@', $delegate->destination);
  93. $controller = Controller::resolve($this->container, $controller, $this->path);
  94. // If the controller doesn't exist or the request is to an invalid method, we will
  95. // return the 404 error response. The "before" method and any method beginning with
  96. // an underscore are not publicly available.
  97. if (is_null($controller) or ! $this->callable($method))
  98. {
  99. return Response::error('404');
  100. }
  101. $controller->container = $this->container;
  102. // Again, as was the case with route closures, if the controller "before" filters
  103. // return a response, it will be considered the response to the request and the
  104. // controller method will not be used to handle the request to the application.
  105. $response = $this->filter($controller->filters('before'), array(), true);
  106. if (is_null($response))
  107. {
  108. $response = call_user_func_array(array($controller, $method), $route->parameters);
  109. }
  110. return $this->finish($controller, $response);
  111. }
  112. /**
  113. * Determine if a given controller method is callable.
  114. *
  115. * @param string $method
  116. * @return bool
  117. */
  118. protected function callable($method)
  119. {
  120. return $method !== 'before' and $method !== 'after' and strncmp($method, '_', 1) !== 0;
  121. }
  122. /**
  123. * Finish the route handling for the request.
  124. *
  125. * The route response will be converted to a Response instance and the "after" filters will be run.
  126. *
  127. * @param Route|Controller $destination
  128. * @param mixed $response
  129. * @return Response
  130. */
  131. protected function finish($destination, $response)
  132. {
  133. if ( ! $response instanceof Response) $response = new Response($response);
  134. $this->filter(array_merge($destination->filters('after'), array('after')), array($response));
  135. return $response;
  136. }
  137. /**
  138. * Call a filter or set of filters.
  139. *
  140. * @param array|string $filters
  141. * @param array $parameters
  142. * @param bool $override
  143. * @return mixed
  144. */
  145. protected function filter($filters, $parameters = array(), $override = false)
  146. {
  147. if (is_string($filters)) $filters = explode('|', $filters);
  148. foreach ((array) $filters as $filter)
  149. {
  150. // Parameters may be passed into routes by specifying the list of parameters after
  151. // a colon. If parameters are present, we will merge them into the parameter array
  152. // that was passed to the method and slice the parameters off of the filter string.
  153. if (($colon = strpos($filter, ':')) !== false)
  154. {
  155. $parameters = array_merge($parameters, explode(',', substr($filter, $colon + 1)));
  156. $filter = substr($filter, 0, $colon);
  157. }
  158. if ( ! isset($this->filters[$filter])) continue;
  159. $response = call_user_func_array($this->filters[$filter], $parameters);
  160. // "Before" filters may override the request cycle. For example, an authentication
  161. // filter may redirect a user to a login view if they are not logged in. Because of
  162. // this, we will return the first filter response if overriding is enabled.
  163. if ( ! is_null($response) and $override) return $response;
  164. }
  165. }
  166. }