Commit d4ddb05a authored by Taylor Otwell's avatar Taylor Otwell

refactoring. added default controller. added uri class.

parent 320653e8
<?php
class Home_Controller extends Controller {
public function index()
{
return View::make('home.index');
}
}
\ No newline at end of file
...@@ -10,11 +10,11 @@ return array( ...@@ -10,11 +10,11 @@ return array(
| Here is the public API of your application. To add functionality to your | Here is the public API of your application. To add functionality to your
| application, you just add to the array located in this file. | application, you just add to the array located in this file.
| |
| Simply tell Laravel the HTTP verbs and request URIs it should respond to. | Simply tell Laravel the HTTP verbs and URIs it should respond to. It is a
| You may respond to the GET, POST, PUT, or DELETE verbs. Enjoy the simplicity | breeze to create beautiful applications using the simplicity and elegance
| and elegance of RESTful routing. | of Laravel's RESTful routing.
| |
| Here is how to respond to a simple GET request to http://example.com/hello: | Let's respond to a simple GET request to http://example.com/hello:
| |
| 'GET /hello' => function() | 'GET /hello' => function()
| { | {
...@@ -28,7 +28,7 @@ return array( ...@@ -28,7 +28,7 @@ return array(
| return 'Hello World!'; | 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) | 'GET /hello/(:any)' => function($name)
| { | {
...@@ -42,4 +42,4 @@ return array( ...@@ -42,4 +42,4 @@ return array(
return View::make('home.index'); return View::make('home.index');
}, },
); );
\ No newline at end of file
...@@ -101,6 +101,7 @@ register_shutdown_function(function() use ($handler) ...@@ -101,6 +101,7 @@ register_shutdown_function(function() use ($handler)
{ {
if ( ! is_null($error = error_get_last())) if ( ! is_null($error = error_get_last()))
{ {
die('here');
extract($error, EXTR_SKIP); extract($error, EXTR_SKIP);
$handler(new \ErrorException($message, $type, 0, $file, $line)); $handler(new \ErrorException($message, $type, 0, $file, $line));
......
...@@ -82,7 +82,7 @@ class Form { ...@@ -82,7 +82,7 @@ class Form {
*/ */
protected static function action($action, $https) 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));
} }
/** /**
......
...@@ -37,6 +37,7 @@ if (Config::$items['session']['driver'] !== '') ...@@ -37,6 +37,7 @@ if (Config::$items['session']['driver'] !== '')
* Manually load some core classes that are used on every request * Manually load some core classes that are used on every request
* This allows to avoid using the loader for these classes. * This allows to avoid using the loader for these classes.
*/ */
require SYS_PATH.'uri'.EXT;
require SYS_PATH.'input'.EXT; require SYS_PATH.'input'.EXT;
require SYS_PATH.'request'.EXT; require SYS_PATH.'request'.EXT;
require SYS_PATH.'response'.EXT; require SYS_PATH.'response'.EXT;
...@@ -90,7 +91,7 @@ Input::$input = $input; ...@@ -90,7 +91,7 @@ Input::$input = $input;
*/ */
Routing\Filter::register(require APP_PATH.'filters'.EXT); 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); $route = IoC::container()->core('routing.router')->route($method, $uri);
......
...@@ -250,7 +250,7 @@ class Paginator { ...@@ -250,7 +250,7 @@ class Paginator {
// We will assume the page links should use HTTPS if the current request // We will assume the page links should use HTTPS if the current request
// is also using HTTPS. Since pagination links automatically point to // is also using HTTPS. Since pagination links automatically point to
// the current URI, this makes pretty good sense. // 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); $appendage = $this->appendage($element, $page);
......
...@@ -3,18 +3,18 @@ ...@@ -3,18 +3,18 @@
class Request { class Request {
/** /**
* The route handling the current request. * The request URI for the current request.
* *
* @var Routing\Route * @var URI
*/ */
public static $route; public static $uri;
/** /**
* The request URI for the current request. * The route handling the current request.
* *
* @var string * @var Routing\Route
*/ */
public static $uri; public static $route;
/** /**
* The request data key that is used to indicate a spoofed request method. * The request data key that is used to indicate a spoofed request method.
...@@ -24,79 +24,36 @@ class Request { ...@@ -24,79 +24,36 @@ class Request {
const spoofer = '__spoofer'; 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() public static function uri()
{ {
if ( ! is_null(static::$uri)) return static::$uri; return (is_null(static::$uri)) ? static::$uri = new URI($_SERVER) : 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;
} }
/** /**
* Get the request format. * 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 * @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. * Get the request method.
* *
* Typically, this will be the value of the REQUEST_METHOD $_SERVER variable. * This will usually be the value of the REQUEST_METHOD $_SERVER variable
* However, when the request is being spoofed by a hidden form value, the request * However, when the request method is spoofed using a hidden form value,
* method will be stored in the $_POST array. * the method will be stored in the $_POST array.
* *
* @return string * @return string
*/ */
...@@ -122,10 +79,6 @@ class Request { ...@@ -122,10 +79,6 @@ class Request {
/** /**
* Determine if the request method is being spoofed by a hidden Form element. * 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 * @return bool
*/ */
public static function spoofed() public static function spoofed()
......
...@@ -122,11 +122,16 @@ class Router { ...@@ -122,11 +122,16 @@ class Router {
foreach ($routes as $keys => $callback) 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 // Only check routes that have multiple URIs or wildcards since other
// routes would have been caught by the check for literal matches. // 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; return Request::$route = $route;
} }
...@@ -136,6 +141,21 @@ class Router { ...@@ -136,6 +141,21 @@ class Router {
return Request::$route = $this->controller($method, $uri, $destination); 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. * Attempt to match a given route destination to a given route.
* *
...@@ -146,20 +166,18 @@ class Router { ...@@ -146,20 +166,18 @@ class Router {
* @param string $destination * @param string $destination
* @param array $keys * @param array $keys
* @param mixed $callback * @param mixed $callback
* @param array $formats
* @return mixed * @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) foreach (explode(', ', $keys) as $key)
{ {
// Append the provided formats to the route as an optional regular expression. if (preg_match('#^'.$this->wildcards($key).$formats.'$#', $destination))
// 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))
{ {
return new Route($keys, $callback, $this->parameters($destination, $key)); return new Route($keys, $callback, $this->parameters($destination, $key));
} }
...@@ -185,19 +203,13 @@ class Router { ...@@ -185,19 +203,13 @@ class Router {
if ( ! is_null($key = $this->controller_key($segments))) if ( ! is_null($key = $this->controller_key($segments)))
{ {
// Create the controller name for the current request. This controller // Extract the controller name from the URI segments.
// 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.
$controller = implode('.', array_slice($segments, 0, $key)); $controller = implode('.', array_slice($segments, 0, $key));
// Now that we have the controller path and name, we can slice the controller // Remove the controller name from the URI.
// section of the URI from the array of segments.
$segments = array_slice($segments, $key); $segments = array_slice($segments, $key);
// Extract the controller method from the URI segments. If no more segments // Extract the controller method from the remaining segments.
// are remaining after slicing off the controller, the "index" method will
// be used as the default controller method.
$method = (count($segments) > 0) ? array_shift($segments) : 'index'; $method = (count($segments) > 0) ? array_shift($segments) : 'index';
return new Route($destination, $controller.'@'.$method, $segments); return new Route($destination, $controller.'@'.$method, $segments);
...@@ -206,12 +218,12 @@ class Router { ...@@ -206,12 +218,12 @@ class Router {
/** /**
* Search the controllers for the application and determine if an applicable * 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 * 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. * segments will be returned by the method, otherwise NULL will be returned.
* The deepest possible matching controller will be considered the controller * The deepest possible controller will be considered the controller that
* that should handle the request. * should handle the request.
* *
* @param array $segments * @param array $segments
* @return int * @return int
...@@ -276,7 +288,21 @@ class Router { ...@@ -276,7 +288,21 @@ class Router {
*/ */
protected function parameters($uri, $route) 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
<?php namespace Laravel;
class URI {
/**
* The request URI for the current request.
*
* @var string
*/
protected $uri;
/**
* The $_SERVER global array for the current request.
*
* @var array
*/
protected $server;
/**
* Create a new instance of the URI class.
*
* @param array $server
* @return void
*/
public function __construct($server)
{
$this->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
...@@ -55,7 +55,14 @@ class URL { ...@@ -55,7 +55,14 @@ class URL {
{ {
if (is_null($https)) $https = Request::secure(); 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;
} }
/** /**
......
...@@ -20,13 +20,6 @@ define('START_TIME', microtime(true)); ...@@ -20,13 +20,6 @@ define('START_TIME', microtime(true));
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Laravel Installation Paths | 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'; $application = '../application';
......
...@@ -10,39 +10,13 @@ class RequestTest extends PHPUnit_Framework_TestCase { ...@@ -10,39 +10,13 @@ class RequestTest extends PHPUnit_Framework_TestCase {
Laravel\Request::$uri = null; 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 * @dataProvider requestUriProvider
*/ */
public function test_correct_uri_is_returned_when_request_uri_is_used($uri, $expectation) public function test_correct_uri_is_returned_when_request_uri_is_used($uri, $expectation)
{ {
$_SERVER['REQUEST_URI'] = $uri; $_SERVER['REQUEST_URI'] = $uri;
$this->assertEquals($expectation, Laravel\Request::uri()); $this->assertEquals($expectation, Laravel\Request::uri()->get());
}
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());
} }
public function test_request_method_returns_spoofed_method_if_uri_is_spoofed() public function test_request_method_returns_spoofed_method_if_uri_is_spoofed()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment