From d35e2abd77c94125e078738ebed573a69d989c73 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Sun, 4 Sep 2011 22:40:33 -0500 Subject: [PATCH] refactoring the routing engine. --- application/config/aliases.php | 57 +++++----- application/filters.php | 4 +- application/routes.php | 2 +- laravel/config/container.php | 10 +- laravel/laravel.php | 4 +- laravel/routing/caller.php | 197 +++++++++++++++++++++++++++++++++ laravel/routing/route.php | 180 +----------------------------- laravel/routing/router.php | 6 +- 8 files changed, 243 insertions(+), 217 deletions(-) create mode 100644 laravel/routing/caller.php diff --git a/application/config/aliases.php b/application/config/aliases.php index 12c85a17..6722eb2d 100644 --- a/application/config/aliases.php +++ b/application/config/aliases.php @@ -18,33 +18,34 @@ return array( | */ - 'Asset' => 'Laravel\\Asset', - 'Auth' => 'Laravel\\Security\\Authenticator_Facade', - 'Benchmark' => 'Laravel\\Benchmark', - 'Cache' => 'Laravel\\Cache\\Manager_Facade', - 'Config' => 'Laravel\\Config_Facade', - 'Cookie' => 'Laravel\\Cookie_Facade', - 'Crypter' => 'Laravel\\Security\\Crypter_Facade', - 'DB' => 'Laravel\\Database\\Manager_Facade', - 'Download' => 'Laravel\\Download_Facade', - 'Eloquent' => 'Laravel\\Database\\Eloquent\\Model', - 'File' => 'Laravel\\File_Facade', - 'Form' => 'Laravel\\Form_Facade', - 'Hasher' => 'Laravel\\Security\\Hashing\\Hasher_Facade', - 'HTML' => 'Laravel\\HTML_Facade', - 'Inflector' => 'Laravel\\Inflector', - 'Input' => 'Laravel\\Input_Facade', - 'IoC' => 'Laravel\\IoC', - 'Lang' => 'Laravel\\Lang_Facade', - 'Loader' => 'Laravel\\Loader_Facade', - 'Package' => 'Laravel\\Package_Facade', - 'URL' => 'Laravel\\URL_Facade', - 'Redirect' => 'Laravel\\Redirect_Facade', - 'Request' => 'Laravel\\Request_Facade', - 'Response' => 'Laravel\\Response_Facade', - 'Session' => 'Laravel\\Session\\Manager_Facade', - 'Str' => 'Laravel\\Str', - 'Validator' => 'Laravel\\Validation\\Validator_Facade', - 'View' => 'Laravel\\View_Facade', + 'Asset' => 'Laravel\\Asset', + 'Auth' => 'Laravel\\Security\\Authenticator_Facade', + 'Benchmark' => 'Laravel\\Benchmark', + 'Cache' => 'Laravel\\Cache\\Manager_Facade', + 'Config' => 'Laravel\\Config_Facade', + 'Controller' => 'Laravel\\Controller', + 'Cookie' => 'Laravel\\Cookie_Facade', + 'Crypter' => 'Laravel\\Security\\Crypter_Facade', + 'DB' => 'Laravel\\Database\\Manager_Facade', + 'Download' => 'Laravel\\Download_Facade', + 'Eloquent' => 'Laravel\\Database\\Eloquent\\Model', + 'File' => 'Laravel\\File_Facade', + 'Form' => 'Laravel\\Form_Facade', + 'Hasher' => 'Laravel\\Security\\Hashing\\Hasher_Facade', + 'HTML' => 'Laravel\\HTML_Facade', + 'Inflector' => 'Laravel\\Inflector', + 'Input' => 'Laravel\\Input_Facade', + 'IoC' => 'Laravel\\IoC', + 'Lang' => 'Laravel\\Lang_Facade', + 'Loader' => 'Laravel\\Loader_Facade', + 'Package' => 'Laravel\\Package_Facade', + 'URL' => 'Laravel\\URL_Facade', + 'Redirect' => 'Laravel\\Redirect_Facade', + 'Request' => 'Laravel\\Request_Facade', + 'Response' => 'Laravel\\Response_Facade', + 'Session' => 'Laravel\\Session\\Manager_Facade', + 'Str' => 'Laravel\\Str', + 'Validator' => 'Laravel\\Validation\\Validator_Facade', + 'View' => 'Laravel\\View_Facade', ); \ No newline at end of file diff --git a/application/filters.php b/application/filters.php index ffaba762..8e06de17 100644 --- a/application/filters.php +++ b/application/filters.php @@ -45,13 +45,13 @@ return array( | */ - 'before' => function(Application $application) + 'before' => function() { // Do stuff before every request to your application. }, - 'after' => function(Application $application, Response $response) + 'after' => function(Response $response) { // Do stuff after every request to your application. }, diff --git a/application/routes.php b/application/routes.php index 3dee548d..94efd50d 100644 --- a/application/routes.php +++ b/application/routes.php @@ -37,7 +37,7 @@ return array( | */ - 'GET /' => function($laravel) + 'GET /' => function() { return View::make('home.index'); }, diff --git a/laravel/config/container.php b/laravel/config/container.php index 8de3e0f0..a1f2fc92 100644 --- a/laravel/config/container.php +++ b/laravel/config/container.php @@ -150,12 +150,18 @@ return array( }), - 'laravel.router' => array('singleton' => true, 'resolver' => function($container) + 'laravel.routing.router' => array('singleton' => true, 'resolver' => function($container) { return new Routing\Router($container->resolve('laravel.request'), require APP_PATH.'routes'.EXT, CONTROLLER_PATH); }), + 'laravel.routing.caller' => array('resolver' => function($container) + { + return new Routing\Caller($container, CONTROLLER_PATH); + }), + + 'laravel.session' => array('singleton' => true, 'resolver' => function($container) { return $container->resolve('laravel.session.manager')->driver($container->resolve('laravel.config')->get('session.driver')); @@ -176,7 +182,7 @@ return array( $container->resolve('laravel.config')->get('application.index'), ); - return new URL($container->resolve('laravel.router'), $base, $index, $request->secure()); + return new URL($container->resolve('laravel.routing.router'), $base, $index, $request->secure()); }), diff --git a/laravel/laravel.php b/laravel/laravel.php index 00fc576e..e7b2c8f2 100644 --- a/laravel/laravel.php +++ b/laravel/laravel.php @@ -67,13 +67,13 @@ unset($packages); // -------------------------------------------------------------- // Route the request and get the response from the route. // -------------------------------------------------------------- -$route = $application->router->route(); +$route = $application->container->resolve('laravel.routing.router')->route(); if ( ! is_null($route)) { $route->filters = require APP_PATH.'filters'.EXT; - $response = $route->call($application); + $response = $application->container->resolve('laravel.routing.caller')->call($route); } else { diff --git a/laravel/routing/caller.php b/laravel/routing/caller.php new file mode 100644 index 00000000..44c43cd0 --- /dev/null +++ b/laravel/routing/caller.php @@ -0,0 +1,197 @@ +container = $container; + $this->controller_path = $controller_path; + } + + /** + * Call a given route and return the route's response. + * + * @param Route $route + * @return Response + */ + public function call(Route $route) + { + if ( ! $route->callback instanceof \Closure and ! is_array($route->callback)) + { + throw new \Exception('Invalid route defined for URI ['.$route->key.']'); + } + + // Run the "before" filters for the route. If a before filter returns a value, that value + // will be considered the response to the request and the route function / controller will + // not be used to handle the request. + $before = array_merge($route->before(), array('before')); + + if ( ! is_null($response = $this->filter($route, $before, array(), true))) + { + return $this->finish($route, $response); + } + + $closure = ( ! $route->callback instanceof Closure) ? $this->find_route_closure($route) : $route->callback; + + if ( ! is_null($closure)) return $this->handle_closure($route, $closure); + + return $this->finish($route, $this->container->resolve('laravel.response')->error('404')); + } + + /** + * Extract the route closure from the route. + * + * If a "do" index is specified on the callback, that is the handler. + * Otherwise, we will return the first callable array value. + * + * @param Route $route + * @return Closure + */ + protected function find_route_closure(Route $route) + { + if (isset($route->callback['do'])) return $route->callback['do']; + + foreach ($route->callback as $value) { if ($value instanceof Closure) return $value; } + } + + /** + * Handle a route closure. + * + * @param Route $route + * @param Closure $closure + * @return mixed + */ + protected function handle_closure(Route $route, Closure $closure) + { + $response = call_user_func_array($closure, $route->parameters); + + // If the route closure returns an array, we assume that they are returning a + // reference to a controller and method and will use the given controller method + // to handle the request to the application. + if (is_array($response)) + { + $response = $this->delegate($route, $response[0], $response[1], $route->parameters); + } + + return $this->finish($route, $response); + } + + /** + * Handle the delegation of a route to a controller method. + * + * @param Route $route + * @param string $controller + * @param string $method + * @param array $parameters + * @return Response + */ + protected function delegate(Route $route, $controller, $method, $parameters) + { + if ( ! file_exists($path = $this->controller_path.strtolower(str_replace('.', '/', $controller)).EXT)) + { + throw new \Exception("Controller [$controller] does not exist."); + } + + require $path; + + $controller = $this->resolve_controller($controller); + + if ($method == 'before' or strncmp($method, '_', 1) === 0) + { + $response = $this->container->resolve('laravel.response')->error('404'); + } + else + { + $response = $controller->before(); + } + + // Again, as was the case with route closures, if the controller "before" method returns + // a response, it will be considered the response to the request and the controller method + // will not be used to handle the request to the application. + return (is_null($response)) ? call_user_func_array(array($controller, $method), $parameters) : $response; + } + + /** + * Resolve a controller name to a controller instance. + * + * @param string $controller + * @return Controller + */ + protected function resolve_controller($controller) + { + if ($this->container->registered('controllers.'.$controller)) return $this->container->resolve('controllers.'.$controller); + + $controller = str_replace(' ', '_', ucwords(str_replace('.', ' ', $controller))).'_Controller'; + + return new $controller; + } + + /** + * Finish the route handling for the request. + * + * The route response will be converted to a Response instance and the "after" filters + * defined for the route will be executed. + * + * @param Route $route + * @param mixed $response + * @return Response + */ + protected function finish(Route $route, $response) + { + if ( ! $response instanceof Response) $response = new Response($response); + + $this->filter($route, array_merge($route->after(), array('after')), array($response)); + + return $response; + } + + /** + * Call a filter or set of filters. + * + * @param Route $route + * @param array $filters + * @param array $parameters + * @param bool $override + * @return mixed + */ + protected function filter(Route $route, $filters, $parameters = array(), $override = false) + { + foreach ((array) $filters as $filter) + { + if ( ! isset($route->filters[$filter])) continue; + + $response = call_user_func_array($route->filters[$filter], $parameters); + + // "Before" filters may override the request cycle. For example, an authentication + // filter may redirect a user to a login view if they are not logged in. Because of + // this, we will return the first filter response if overriding is enabled. + if ( ! is_null($response) and $override) return $response; + } + } + +} \ No newline at end of file diff --git a/laravel/routing/route.php b/laravel/routing/route.php index 3f45fb97..5080858a 100644 --- a/laravel/routing/route.php +++ b/laravel/routing/route.php @@ -1,10 +1,5 @@ key = $key; $this->callback = $callback; $this->parameters = $parameters; - $this->controller_path = $controller_path; // Extract each URI handled by the URI. These will be used to find the route by // URI when requested. The leading slash will be removed for convenience. @@ -74,166 +61,6 @@ class Route { } } - /** - * Execute the route for a given request to the application and return the response. - * - * @param Application $application - * @return Response - */ - public function call(Application $application) - { - if ( ! $this->callback instanceof Closure and ! is_array($this->callback)) - { - throw new \Exception('Invalid route defined for URI ['.$this->key.']'); - } - - // Run the "before" filters for the route. If a before filter returns a value, that value - // will be considered the response to the request and the route function / controller will - // not be used to handle the request. - if ( ! is_null($response = $this->filter(array_merge($this->before(), array('before')), array($application), true))) - { - return $this->finish($application, $response); - } - - $closure = ( ! $this->callback instanceof Closure) ? $this->find_route_closure() : $this->callback; - - if ( ! is_null($closure)) return $this->handle_closure($application, $closure); - - return $this->finish($application, $application->responder->error('404')); - } - - /** - * Extract the route closure from the route. - * - * If a "do" index is specified on the callback, that is the handler. - * Otherwise, we will return the first callable array value. - * - * @return Closure - */ - protected function find_route_closure() - { - if (isset($this->callback['do'])) return $this->callback['do']; - - foreach ($this->callback as $value) { if ($value instanceof Closure) return $value; } - } - - /** - * Handle a route closure. - * - * @param Route $route - * @param Closure $closure - * @return mixed - */ - protected function handle_closure(Application $application, Closure $closure) - { - array_unshift($this->parameters, $application); - - $response = call_user_func_array($closure, $this->parameters); - - // If the route closure returns an array, we assume that they are returning a - // reference to a controller and method and will use the given controller method - // to handle the request to the application. - if (is_array($response)) - { - $response = $this->delegate($application, $response[0], $response[1], $this->parameters); - } - - return $this->finish($application, $response); - } - - /** - * Handle the delegation of a route to a controller method. - * - * @param Application $application - * @param string $controller - * @param string $method - * @param array $parameters - * @return Response - */ - protected function delegate(Application $application, $controller, $method, $parameters) - { - if ( ! file_exists($path = $this->controller_path.strtolower(str_replace('.', '/', $controller)).EXT)) - { - throw new \Exception("Controller [$controller] does not exist."); - } - - require $path; - - $controller = $this->resolve($application->container, $controller); - - if ($method == 'before' or strncmp($method, '_', 1) === 0) - { - $response = $application->responder->error('404'); - } - else - { - $response = $controller->before(); - } - - // Again, as was the case with route closures, if the controller "before" method returns - // a response, it will be considered the response to the request and the controller method - // will not be used to handle the request to the application. - return (is_null($response)) ? call_user_func_array(array($controller, $method), $parameters) : $response; - } - - /** - * Resolve a controller name to a controller instance. - * - * @param Container $container - * @param string $controller - * @return Controller - */ - protected function resolve(Container $container, $controller) - { - if ($container->registered('controllers.'.$controller)) return $container->resolve('controllers.'.$controller); - - $controller = str_replace(' ', '_', ucwords(str_replace('.', ' ', $controller))).'_Controller'; - - return new $controller; - } - - /** - * Finish the route handling for the request. - * - * The route response will be converted to a Response instance and the "after" filters - * defined for the route will be executed. - * - * @param Route $route - * @param mixed $response - * @return Response - */ - protected function finish(Application $application, $response) - { - if ( ! $response instanceof Response) $response = new Response($response); - - $this->filter(array_merge($this->after(), array('after')), array($application, $response)); - - return $response; - } - - /** - * Call a filter or set of filters. - * - * @param array $filters - * @param array $parameters - * @param bool $override - * @return mixed - */ - protected function filter($filters, $parameters = array(), $override = false) - { - foreach ((array) $filters as $filter) - { - if ( ! isset($this->filters[$filter])) continue; - - $response = call_user_func_array($this->filters[$filter], $parameters); - - // "Before" filters may override the request cycle. For example, an authentication - // filter may redirect a user to a login view if they are not logged in. Because of - // this, we will return the first filter response if overriding is enabled. - if ( ! is_null($response) and $override) return $response; - } - } - /** * Get all of the "before" filters defined for the route. * @@ -257,11 +84,6 @@ class Route { /** * Get an array of filters defined for the route. * - * - * // Get all of the "before" filters defined for the route. - * $filters = $route->filters('before'); - * - * * @param string $name * @return array */ diff --git a/laravel/routing/router.php b/laravel/routing/router.php index 17cf117b..f8a66ab8 100644 --- a/laravel/routing/router.php +++ b/laravel/routing/router.php @@ -90,7 +90,7 @@ class Router { // no need to spin through all of the routes. if (isset($this->routes[$destination])) { - return $this->request->route = new Route($destination, $this->routes[$destination], array(), $this->controller_path); + return $this->request->route = new Route($destination, $this->routes[$destination], array()); } foreach ($this->routes as $keys => $callback) @@ -105,7 +105,7 @@ class Router { if (preg_match('#^'.$this->translate_wildcards($key).'$#', $destination)) { - return $this->request->route = new Route($keys, $callback, $this->parameters($destination, $key), $this->controller_path); + return $this->request->route = new Route($keys, $callback, $this->parameters($destination, $key)); } } } @@ -147,7 +147,7 @@ class Router { // were they to code the controller delegation manually. $callback = function() use ($controller, $method) { return array($controller, $method); }; - return new Route($controller, $callback, $segments, $this->controller_path); + return new Route($controller, $callback, $segments); } }