From d4ddb05abbfd25f8a016e5021b6f303861686692 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Mon, 24 Oct 2011 21:00:51 -0500 Subject: [PATCH] refactoring. added default controller. added uri class. --- application/controllers/.gitignore | 0 application/controllers/home.php | 10 +++ application/routes.php | 12 ++-- laravel/bootstrap/errors.php | 1 + laravel/form.php | 2 +- laravel/laravel.php | 3 +- laravel/paginator.php | 2 +- laravel/request.php | 85 ++++++------------------ laravel/routing/router.php | 74 ++++++++++++++------- laravel/uri.php | 101 +++++++++++++++++++++++++++++ laravel/url.php | 9 ++- public/index.php | 7 -- tests/Cases/RequestTest.php | 28 +------- 13 files changed, 200 insertions(+), 134 deletions(-) delete mode 100644 application/controllers/.gitignore create mode 100644 application/controllers/home.php create mode 100644 laravel/uri.php diff --git a/application/controllers/.gitignore b/application/controllers/.gitignore deleted file mode 100644 index e69de29b..00000000 diff --git a/application/controllers/home.php b/application/controllers/home.php new file mode 100644 index 00000000..11cbc1e4 --- /dev/null +++ b/application/controllers/home.php @@ -0,0 +1,10 @@ + function() | { @@ -28,7 +28,7 @@ return array( | return 'Hello World!'; | } | - | It's easy to allow URI wildcards using the (:num) or (:any) place-holders: + | It's easy to allow URI wildcards using (:num) or (:any): | | 'GET /hello/(:any)' => function($name) | { @@ -42,4 +42,4 @@ return array( return View::make('home.index'); }, -); +); \ No newline at end of file diff --git a/laravel/bootstrap/errors.php b/laravel/bootstrap/errors.php index f61b406d..488c8e6a 100644 --- a/laravel/bootstrap/errors.php +++ b/laravel/bootstrap/errors.php @@ -101,6 +101,7 @@ register_shutdown_function(function() use ($handler) { if ( ! is_null($error = error_get_last())) { + die('here'); extract($error, EXTR_SKIP); $handler(new \ErrorException($message, $type, 0, $file, $line)); diff --git a/laravel/form.php b/laravel/form.php index 5f2b810b..bf9feea1 100644 --- a/laravel/form.php +++ b/laravel/form.php @@ -82,7 +82,7 @@ class Form { */ protected static function action($action, $https) { - return HTML::entities(URL::to(((is_null($action)) ? Request::uri() : $action), $https)); + return HTML::entities(URL::to(((is_null($action)) ? Request::uri()->get() : $action), $https)); } /** diff --git a/laravel/laravel.php b/laravel/laravel.php index 86ef75d3..7823d9be 100644 --- a/laravel/laravel.php +++ b/laravel/laravel.php @@ -37,6 +37,7 @@ if (Config::$items['session']['driver'] !== '') * Manually load some core classes that are used on every request * This allows to avoid using the loader for these classes. */ +require SYS_PATH.'uri'.EXT; require SYS_PATH.'input'.EXT; require SYS_PATH.'request'.EXT; require SYS_PATH.'response'.EXT; @@ -90,7 +91,7 @@ Input::$input = $input; */ Routing\Filter::register(require APP_PATH.'filters'.EXT); -list($uri, $method) = array(Request::uri(), Request::method()); +list($uri, $method) = array(Request::uri()->get(), Request::method()); $route = IoC::container()->core('routing.router')->route($method, $uri); diff --git a/laravel/paginator.php b/laravel/paginator.php index 4de36915..2f9669c0 100644 --- a/laravel/paginator.php +++ b/laravel/paginator.php @@ -250,7 +250,7 @@ class Paginator { // We will assume the page links should use HTTPS if the current request // is also using HTTPS. Since pagination links automatically point to // the current URI, this makes pretty good sense. - list($uri, $secure) = array(Request::uri(), Request::secure()); + list($uri, $secure) = array(Request::uri()->get(), Request::secure()); $appendage = $this->appendage($element, $page); diff --git a/laravel/request.php b/laravel/request.php index 581295d5..ea552a63 100644 --- a/laravel/request.php +++ b/laravel/request.php @@ -2,6 +2,13 @@ class Request { + /** + * The request URI for the current request. + * + * @var URI + */ + public static $uri; + /** * The route handling the current request. * @@ -9,13 +16,6 @@ class Request { */ public static $route; - /** - * The request URI for the current request. - * - * @var string - */ - public static $uri; - /** * The request data key that is used to indicate a spoofed request method. * @@ -24,79 +24,36 @@ class Request { const spoofer = '__spoofer'; /** - * Get the URI for the current request. + * Get the URI instance for the current request. * - * Note: This method is the equivalent of calling the URI::get method. - * - * @return string + * @return URI */ public static function uri() { - if ( ! is_null(static::$uri)) return static::$uri; - - // Sniff the request URI out of the $_SERVER array. The PATH_IFNO - // variable contains the URI without the base URL or index page, - // so we will use that if possible, otherwise we will parse the - // URI out of the REQUEST_URI element. - if (isset($_SERVER['PATH_INFO'])) - { - $uri = $_SERVER['PATH_INFO']; - } - elseif (isset($_SERVER['REQUEST_URI'])) - { - $uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH); - - if ($uri === false) - { - throw new \Exception("Invalid request URI. Request terminated."); - } - } - else - { - throw new \Exception("Unable to determine the request URI."); - } - - // Remove the application URL and the application index page from - // the request URI. Both of these elements will interfere with the - // routing engine and are extraneous, so they will be removed. - $base = parse_url(Config::$items['application']['url'], PHP_URL_PATH); - - if (strpos($uri, $base) === 0) - { - $uri = substr($uri, strlen($base)); - } - - $index = '/'.Config::$items['application']['index']; - - if (trim($index) !== '/' and strpos($uri, $index) === 0) - { - $uri = substr($uri, strlen($index)); - } - - // Request URIs to the root of the application will be returned - // as a single forward slash. Otherwise, the request URI will be - // returned without leading or trailing slashes. - return static::$uri = (($uri = trim($uri, '/')) == '') ? '/' : $uri; + return (is_null(static::$uri)) ? static::$uri = new URI($_SERVER) : static::$uri; } /** * Get the request format. * - * The format is determined by essentially taking the "extension" of the URI. + * The format is determined by taking the "extension" of the URI. * + * @param string $uri * @return string */ - public static function format() + public static function format($uri = null) { - return (($extension = pathinfo(static::uri(), PATHINFO_EXTENSION)) !== '') ? $extension : 'html'; + if (is_null($uri)) $uri = static::uri()->get(); + + return (($extension = pathinfo($uri, PATHINFO_EXTENSION)) !== '') ? $extension : 'html'; } /** * Get the request method. * - * Typically, this will be the value of the REQUEST_METHOD $_SERVER variable. - * However, when the request is being spoofed by a hidden form value, the request - * method will be stored in the $_POST array. + * This will usually be the value of the REQUEST_METHOD $_SERVER variable + * However, when the request method is spoofed using a hidden form value, + * the method will be stored in the $_POST array. * * @return string */ @@ -122,10 +79,6 @@ class Request { /** * Determine if the request method is being spoofed by a hidden Form element. * - * Hidden elements are used to spoof PUT and DELETE requests since they are not supported - * by HTML forms. If the request is being spoofed, Laravel considers the spoofed request - * method the actual request method throughout the framework. - * * @return bool */ public static function spoofed() diff --git a/laravel/routing/router.php b/laravel/routing/router.php index 85de9d14..8b1c92df 100644 --- a/laravel/routing/router.php +++ b/laravel/routing/router.php @@ -122,11 +122,16 @@ class Router { foreach ($routes as $keys => $callback) { + // Formats are appended to the route key as a regular expression. + // It will look something like: "(\.(json|xml|html))?" + $formats = $this->provides($callback); + // Only check routes that have multiple URIs or wildcards since other // routes would have been caught by the check for literal matches. - if (strpos($keys, '(') !== false or strpos($keys, ',') !== false) + // We also need to check routes with "provides" clauses. + if ($this->fuzzy($keys) or ! is_null($formats)) { - if ( ! is_null($route = $this->match($destination, $keys, $callback))) + if ( ! is_null($route = $this->match($destination, $keys, $callback, $formats))) { return Request::$route = $route; } @@ -136,6 +141,21 @@ class Router { return Request::$route = $this->controller($method, $uri, $destination); } + /** + * Determine if the route contains elements that forbid literal matches. + * + * Any route key containing a regular expression, wildcard, or multiple + * URIs cannot be matched using a literal string check, but must be + * checked using regular expressions. + * + * @param string $keys + * @return bool + */ + protected function fuzzy($keys) + { + return strpos($keys, '(') !== false or strpos($keys, ',') !== false; + } + /** * Attempt to match a given route destination to a given route. * @@ -146,20 +166,18 @@ class Router { * @param string $destination * @param array $keys * @param mixed $callback + * @param array $formats * @return mixed */ - protected function match($destination, $keys, $callback) + protected function match($destination, $keys, $callback, $formats) { + // Append the provided formats to the route as an optional regular expression. + // This should make the route look something like: "user(\.(json|xml|html))?" + $formats = ( ! is_null($formats)) ? '(\.('.implode('|', $formats).'))?' : ''; + foreach (explode(', ', $keys) as $key) { - // Append the provided formats to the route as an optional regular expression. - // This should make the route look something like: "user(\.(json|xml|html))?" - if ( ! is_null($formats = $this->provides($callback))) - { - $key .= '(\.('.implode('|', $formats).'))?'; - } - - if (preg_match('#^'.$this->wildcards($key).'$#', $destination)) + if (preg_match('#^'.$this->wildcards($key).$formats.'$#', $destination)) { return new Route($keys, $callback, $this->parameters($destination, $key)); } @@ -185,19 +203,13 @@ class Router { if ( ! is_null($key = $this->controller_key($segments))) { - // Create the controller name for the current request. This controller - // name will be returned by the anonymous route we will create. Instead - // of using directory slashes, dots will be used to specify the controller - // location with the controllers directory. + // Extract the controller name from the URI segments. $controller = implode('.', array_slice($segments, 0, $key)); - // Now that we have the controller path and name, we can slice the controller - // section of the URI from the array of segments. + // Remove the controller name from the URI. $segments = array_slice($segments, $key); - // Extract the controller method from the URI segments. If no more segments - // are remaining after slicing off the controller, the "index" method will - // be used as the default controller method. + // Extract the controller method from the remaining segments. $method = (count($segments) > 0) ? array_shift($segments) : 'index'; return new Route($destination, $controller.'@'.$method, $segments); @@ -206,12 +218,12 @@ class Router { /** * Search the controllers for the application and determine if an applicable - * controller exists for the current request. + * controller exists for the current request to the application. * * If a controller is found, the array key for the controller name in the URI * segments will be returned by the method, otherwise NULL will be returned. - * The deepest possible matching controller will be considered the controller - * that should handle the request. + * The deepest possible controller will be considered the controller that + * should handle the request. * * @param array $segments * @return int @@ -276,7 +288,21 @@ class Router { */ protected function parameters($uri, $route) { - return array_values(array_intersect_key(explode('/', $uri), preg_grep('/\(.+\)/', explode('/', $route)))); + list($uri, $route) = array(explode('/', $uri), explode('/', $route)); + + $count = count($route); + + $parameters = array(); + + for ($i = 0; $i < $count; $i++) + { + if (preg_match('/\(.+\)/', $route[$i])) + { + $parameters[] = $uri[$i]; + } + } + + return $parameters; } } \ No newline at end of file diff --git a/laravel/uri.php b/laravel/uri.php new file mode 100644 index 00000000..962f10a8 --- /dev/null +++ b/laravel/uri.php @@ -0,0 +1,101 @@ +server = $server; + } + + /** + * Get the request URI for the current request. + * + * @return string + */ + public function get() + { + if (is_null($this->uri)) + { + $uri = parse_url($this->server['REQUEST_URI'], PHP_URL_PATH); + + $this->uri = $this->format($this->clean($uri)); + } + + return $this->uri; + } + + /** + * Remove extraneous information from the given request URI. + * + * @param string $uri + * @return string + */ + protected function clean($uri) + { + // The base application URL is removed first. If the application is being + // served out of a sub-directory of the web document root, we need to get + // rid of the folders that are included in the URI. + $uri = $this->remove($uri, parse_url(Config::$items['application']['url'], PHP_URL_PATH)); + + // Next, the application index file is removed. The index file has nothing + // to do with how the request is routed to a closure or controller, so it + // can be safely removed from the URI. + if (($index = '/'.Config::$items['application']['index']) !== '/') + { + $uri = $this->remove($uri, $index); + } + + // We don't consider the request format to be a part of the request URI. + // The format merely determines in which format the requested resource + // should be returned to the client. + return rtrim($uri, '.'.Request::format($uri)); + } + + /** + * Remove a string from the beginning of a URI. + * + * @param string $uri + * @param string $remove + * @return string + */ + protected function remove($uri, $remove) + { + return (strpos($uri, $remove) === 0) ? substr($uri, strlen($remove)) : $uri; + } + + /** + * Format the URI for use throughout the framework. + * + * If the request is to the root of the application, a single forward slash + * will be returned. Otherwise, the URI will be returned with all leading + * and trailing slashes removed. + * + * @param string $uri + * @return string + */ + protected function format($uri) + { + return (($uri = trim($uri, '/')) !== '') ? $uri : '/'; + } + +} \ No newline at end of file diff --git a/laravel/url.php b/laravel/url.php index fc2d19dd..60e8d3a9 100644 --- a/laravel/url.php +++ b/laravel/url.php @@ -55,7 +55,14 @@ class URL { { if (is_null($https)) $https = Request::secure(); - return str_replace(Config::$items['application']['index'].'/', '', static::to($url, $https)); + $url = static::to($url, $https); + + if (($index = Config::$items['application']['index']) !== '') + { + $url = str_replace($index.'/', '', $url); + } + + return $url; } /** diff --git a/public/index.php b/public/index.php index 78aa549f..033593fc 100644 --- a/public/index.php +++ b/public/index.php @@ -20,13 +20,6 @@ define('START_TIME', microtime(true)); |-------------------------------------------------------------------------- | Laravel Installation Paths |-------------------------------------------------------------------------- -| -| Here you may specify the location of the various Laravel framework -| directories for your installation. -| -| Of course, these are already set to the proper default values, so you do -| not need to change them if you have not modified the directory structure. -| */ $application = '../application'; diff --git a/tests/Cases/RequestTest.php b/tests/Cases/RequestTest.php index fe7ab866..77f735f9 100644 --- a/tests/Cases/RequestTest.php +++ b/tests/Cases/RequestTest.php @@ -10,39 +10,13 @@ class RequestTest extends PHPUnit_Framework_TestCase { Laravel\Request::$uri = null; } - /** - * @expectedException Exception - */ - public function test_exception_thrown_if_uri_cant_be_determined() - { - Laravel\Request::uri(); - } - - public function test_uri_method_returns_path_info_if_set() - { - $_SERVER['PATH_INFO'] = 'something'; - $this->assertEquals('something', Laravel\Request::uri()); - } - /** * @dataProvider requestUriProvider */ public function test_correct_uri_is_returned_when_request_uri_is_used($uri, $expectation) { $_SERVER['REQUEST_URI'] = $uri; - $this->assertEquals($expectation, Laravel\Request::uri()); - } - - public function test_format_returns_the_extension_of_the_request_uri() - { - $_SERVER['PATH_INFO'] = 'profile.json'; - $this->assertEquals('json', Laravel\Request::format()); - } - - public function test_format_returns_html_if_no_format_is_available() - { - $_SERVER['PATH_INFO'] = 'profile'; - $this->assertEquals('html', Laravel\Request::format()); + $this->assertEquals($expectation, Laravel\Request::uri()->get()); } public function test_request_method_returns_spoofed_method_if_uri_is_spoofed()