Commit 3a92facc authored by Taylor Otwell's avatar Taylor Otwell

updated routing to fix several issues.

parent 31cf44c3
<?php
/*
|--------------------------------------------------------------------------
| Bundle Configuration
|--------------------------------------------------------------------------
|
| Bundles allow you to conveniently extend and organize your application.
| Think of bundles as self-contained applications. They can have routes,
| controllers, models, views, configuration, etc. You can even create
| your own bundles to share with the Laravel community.
|
| This is a list of the bundles installed for your application and tells
| Laravel the location of the bundle's root directory, as well as the
| root URI the bundle responds to.
|
| For example, if you have an "admin" bundle located in "bundles/admin"
| that you want to handle requests with URIs that begin with "admin",
| simply add it to the array like this:
|
| 'admin' => array(
| 'location' => 'admin',
| 'handles' => 'admin',
| ),
|
| Note that the "location" is relative to the "bundles" directory.
| Now the bundle will be recognized by Laravel and will be able
| to respond to requests beginning with "admin"!
|
| Have a bundle that lives in the root of the bundle directory
| and doesn't respond to any requests? Just add the bundle
| name to the array and we'll take care of the rest.
|
*/
return array();
\ No newline at end of file
...@@ -97,35 +97,6 @@ return array( ...@@ -97,35 +97,6 @@ return array(
'timezone' => 'UTC', 'timezone' => 'UTC',
/*
|--------------------------------------------------------------------------
| Bundle Options
|--------------------------------------------------------------------------
|
| Here you may specify options related to application bundles, such as the
| amount of time the bundle manifest is cached. Each option is detailed
| below with suggestions for sensible values.
|
| Cache:
|
| All bundles have a "bundle.info" file which contains information such
| as the name of a bundle and the URIs it responds to. This value is
| the number of minutes that bundle info is cached.
|
| Auto:
|
| You may wish to auto-start some bundles instead of lazy-loading them.
| This is useful for debug bundles as well as bundles that are used
| throughout your application. You may specify which bundles should
| be auto-loaded in this array.
|
*/
'bundle' => array(
'cache' => 0,
'auto' => array(),
),
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Class Aliases | Class Aliases
...@@ -168,6 +139,7 @@ return array( ...@@ -168,6 +139,7 @@ return array(
'Redis' => 'Laravel\\Redis', 'Redis' => 'Laravel\\Redis',
'Request' => 'Laravel\\Request', 'Request' => 'Laravel\\Request',
'Response' => 'Laravel\\Response', 'Response' => 'Laravel\\Response',
'Route' => 'Laravel\\Routing\\Route',
'Router' => 'Laravel\\Routing\\Router', 'Router' => 'Laravel\\Routing\\Router',
'Schema' => 'Laravel\\Database\\Schema', 'Schema' => 'Laravel\\Database\\Schema',
'Section' => 'Laravel\\Section', 'Section' => 'Laravel\\Section',
......
...@@ -7,7 +7,7 @@ class Base_Controller extends Controller { ...@@ -7,7 +7,7 @@ class Base_Controller extends Controller {
* *
* @param string $method * @param string $method
* @param array $parameters * @param array $parameters
* @return Laravel\Response * @return Response
*/ */
public function __call($method, $parameters) public function __call($method, $parameters)
{ {
......
...@@ -12,32 +12,57 @@ ...@@ -12,32 +12,57 @@
| |
| Let's respond to a simple GET request to http://example.com/hello: | Let's respond to a simple GET request to http://example.com/hello:
| |
| Router::register('GET /hello', function() | Route::get('hello', function()
| { | {
| return 'Hello World!'; | return 'Hello World!';
| }); | });
| |
| You can even respond to more than one URI: | You can even respond to more than one URI:
| |
| Router::register('GET /hello, GET /world', function() | Route::post('hello, world', function()
| { | {
| return 'Hello World!'; | return 'Hello World!';
| }); | });
| |
| It's easy to allow URI wildcards using (:num) or (:any): | It's easy to allow URI wildcards using (:num) or (:any):
| |
| Router::register('GET /hello/(:any)', function($name) | Route::put('hello/(:any)', function($name)
| { | {
| return "Welcome, $name."; | return "Welcome, $name.";
| }); | });
| |
*/ */
Router::register(array('GET /', 'GET /home'), function() Route::get('/, home', function()
{ {
return View::make('home.index'); return View::make('home.index');
}); });
/*
|--------------------------------------------------------------------------
| Application 404 & 500 Error Handlers
|--------------------------------------------------------------------------
|
| To centralize and simplify 404 handling, Laravel uses an awesome event
| system to retrieve the response. Feel free to modify this function to
| your tastes and the needs of your application.
|
| Similarly, we use an event to handle the display of 500 level errors
| within the application. These errors are fired when there is an
| uncaught exception thrown in the application.
|
*/
Event::listen('404', function()
{
return Response::error('404');
});
Event::listen('500', function()
{
return Response::error('500');
});
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Route Filters | Route Filters
......
...@@ -2,40 +2,39 @@ ...@@ -2,40 +2,39 @@
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Auto-Loader PSR-0 Directories | Auto-Loader Mappings
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| |
| The Laravel auto-loader can search directories for files using the PSR-0 | Laravel uses a simple array of class to path mappings to drive the class
| naming convention. This convention basically organizes classes by using | auto-loader. This simple approach helps avoid the performance problems
| the class namespace to indicate the directory structure. | of searching through directories by convention.
| |
| So you don't have to manually map all of your models, we've added the | Registering a mapping couldn't be easier. Just pass an array of class
| models and libraries directories for you. So, you can model away and | to path maps into the "map" function of Autoloader. Then, when you
| the auto-loader will take care of the rest. | want to use that class, just use it. It's simple!
| |
*/ */
Autoloader::psr(array( Autoloader::map(array(
path('app').'models', 'Base_Controller' => path('app').'controllers/base.php',
path('app').'libraries',
)); ));
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Auto-Loader Mappings | Auto-Loader PSR-0 Directories
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| |
| Laravel uses a simple array of class to path mappings to drive the class | The Laravel auto-loader can search directories for files using the PSR-0
| auto-loader. This simple approach helps avoid the performance problems | naming convention. This convention basically organizes classes by using
| of searching through directories by some kind of convention. It also | the class namespace to indicate the directory structure.
| gives you the freedom to organize your application how you want.
| |
| Registering a mapping couldn't be easier. Just pass an array of class | So you don't have to manually map all of your models, we've added the
| to path maps into the "map" function of Autoloader. Then, when you | models and libraries directories for you. So, you can model away and
| want to use that class, just use it. It's a piece of cake. | the auto-loader will take care of the rest.
| |
*/ */
Autoloader::map(array( Autoloader::psr(array(
'Base_Controller' => path('app').'controllers/base.php', path('app').'models',
path('app').'libraries',
)); ));
\ No newline at end of file
...@@ -104,7 +104,7 @@ class Autoloader { ...@@ -104,7 +104,7 @@ class Autoloader {
{ {
// The PSR-0 standard indicates that class namespaces and underscores // The PSR-0 standard indicates that class namespaces and underscores
// shoould be used to indcate the directory tree in which the class // shoould be used to indcate the directory tree in which the class
// resides, so we'll convert them to directory slashes. // resides, so we'll convert them to slashes.
$file = str_replace(array('\\', '_'), '/', $class); $file = str_replace(array('\\', '_'), '/', $class);
$directories = $directory ?: static::$psr; $directories = $directory ?: static::$psr;
......
...@@ -33,103 +33,35 @@ class Bundle { ...@@ -33,103 +33,35 @@ class Bundle {
public static $routed = array(); public static $routed = array();
/** /**
* The cache key for the bundle manifest. * Register the bundle for the application.
*
* @var string
*/
const manifest = 'laravel.bundle.manifest';
/**
* Detect all of the installed bundles from disk.
*
* @param string $path
* @return array
*/
public static function detect($path)
{
return static::search($path);
}
/**
* Detect all of the installed bundles from disk.
*
* @param string $path
* @return array
*/
protected static function search($path)
{
$bundles = array();
$items = new fIterator($path);
foreach ($items as $item)
{
// If the item is a directory, we'll search for a bundle.info file.
// If one exists, we will add it to the bundle array. We will set
// the location automatically since we know it.
if ($item->isDir())
{
$path = $item->getRealPath().DS.'bundle.php';
// If we found a file, we'll require in the array it contains
// and add it to the directory. The info array will contain
// basic info like the bundle name and any URIs it may
// handle incoming requests for.
if (file_exists($path))
{
$info = require $path;
$info['location'] = dirname($path).DS;
$bundles[$info['name']] = $info;
continue;
}
// If a bundle.info file doesn't exist within a directory,
// we'll recurse into the directory to keep searching in
// the bundle directory for nested bundles.
else
{
$recurse = static::detect($item->getRealPath());
$bundles = array_merge($bundles, $recurse);
}
}
}
return $bundles;
}
/**
* Register a bundle for the application.
* *
* @param string $bundle
* @param array $config * @param array $config
* @return void * @return void
*/ */
public static function register($config) public static function register($bundle, $config = array())
{ {
$defaults = array('handles' => null, 'auto' => false); $defaults = array('handles' => null, 'auto' => false);
// If a handles clause has been specified, we will cap it with a trailing // If the given configuration is actually a string, we will assume it is a
// slash so the bundle is not extra greedy with its routes. Otherwise a // location and set the bundle name to match it. This is common for most
// bundle that handles "s" would handle all routes beginning with "s". // bundles who simply live in the root bundle directory.
if (isset($config['handles'])) if (is_string($config))
{ {
$config['handles'] = str_finish($config['handles'], '/'); $bundle = $config;
}
static::$bundles[$config['name']] = array_merge($defaults, $config); $config = array('location' => $bundle);
} }
/** // IF no location is set, we will set the location to match the name of
* Disable a bundle for the current request. // the bundle. This is for bundles who are installed to the root of
* // the bundle directory so a location was not set.
* @param string $bundle if ( ! isset($config['location']))
* @return void
*/
public static function disable($bundle)
{ {
unset(static::$bundles[$bundle]); $config['location'] = $bundle;
}
static::$bundles[$bundle] = array_merge($defaults, $config);
} }
/** /**
...@@ -151,8 +83,7 @@ class Bundle { ...@@ -151,8 +83,7 @@ class Bundle {
// Each bundle may have a "start" script which is responsible for preparing // Each bundle may have a "start" script which is responsible for preparing
// the bundle for use by the application. The start script may register any // the bundle for use by the application. The start script may register any
// classes the bundle uses with the auto-loader, or perhaps will start any // classes the bundle uses with the auto-loader, etc.
// dependent bundles so that they are available.
if (file_exists($path = static::path($bundle).'start'.EXT)) if (file_exists($path = static::path($bundle).'start'.EXT))
{ {
require $path; require $path;
...@@ -178,6 +109,11 @@ class Bundle { ...@@ -178,6 +109,11 @@ class Bundle {
{ {
$path = static::path($bundle).'routes'.EXT; $path = static::path($bundle).'routes'.EXT;
// By setting the bundle property on the router the router knows what
// value to replace the (:bundle) place-holder with when the bundle
// routes are added, keeping the routes flexible.
Routing\Router::$bundle = static::option($bundle, 'handles');
if ( ! static::routed($bundle) and file_exists($path)) if ( ! static::routed($bundle) and file_exists($path))
{ {
require $path; require $path;
...@@ -186,6 +122,17 @@ class Bundle { ...@@ -186,6 +122,17 @@ class Bundle {
static::$routed[] = $bundle; static::$routed[] = $bundle;
} }
/**
* Disable a bundle for the current request.
*
* @param string $bundle
* @return void
*/
public static function disable($bundle)
{
unset(static::$bundles[$bundle]);
}
/** /**
* Determine which bundle handles the given URI. * Determine which bundle handles the given URI.
* *
...@@ -200,7 +147,10 @@ class Bundle { ...@@ -200,7 +147,10 @@ class Bundle {
foreach (static::$bundles as $key => $value) foreach (static::$bundles as $key => $value)
{ {
if (starts_with($uri, $value['handles'])) return $key; if (isset($value['handles']) and starts_with($uri, $value['handles'].'/'))
{
return $key;
}
} }
return DEFAULT_BUNDLE; return DEFAULT_BUNDLE;
...@@ -217,6 +167,19 @@ class Bundle { ...@@ -217,6 +167,19 @@ class Bundle {
return $bundle == DEFAULT_BUNDLE or in_array(strtolower($bundle), static::names()); return $bundle == DEFAULT_BUNDLE or in_array(strtolower($bundle), static::names());
} }
/**
* Get the full path location of a given bundle.
*
* @param string $bundle
* @return string
*/
public static function location($bundle)
{
$location = array_get(static::$bundles, $bundle.'.location');
return path('bundle').str_finish($location, DS);
}
/** /**
* Determine if a given bundle has been started for the request. * Determine if a given bundle has been started for the request.
* *
...@@ -277,7 +240,7 @@ class Bundle { ...@@ -277,7 +240,7 @@ class Bundle {
*/ */
public static function path($bundle) public static function path($bundle)
{ {
return ($bundle == DEFAULT_BUNDLE) ? path('app') : static::$bundles[$bundle]['location']; return ($bundle == DEFAULT_BUNDLE) ? path('app') : static::location($bundle);
} }
/** /**
...@@ -401,7 +364,7 @@ class Bundle { ...@@ -401,7 +364,7 @@ class Bundle {
* Get the information for a given bundle. * Get the information for a given bundle.
* *
* @param string $bundle * @param string $bundle
* @return array * @return object
*/ */
public static function get($bundle) public static function get($bundle)
{ {
......
...@@ -39,7 +39,7 @@ class APC extends Driver { ...@@ -39,7 +39,7 @@ class APC extends Driver {
*/ */
protected function retrieve($key) protected function retrieve($key)
{ {
if ( ! is_null($cache = apc_fetch($this->key.$key))) if (($cache = apc_fetch($this->key.$key)) !== false)
{ {
return $cache; return $cache;
} }
......
...@@ -10,26 +10,6 @@ use Laravel\Config; ...@@ -10,26 +10,6 @@ use Laravel\Config;
*/ */
Bundle::start(DEFAULT_BUNDLE); Bundle::start(DEFAULT_BUNDLE);
/**
* Set the CLI options on the $_SERVER global array so we can easily
* retrieve them from the various parts of the CLI code. We can use
* the Request class to access them conveniently.
*/
list($arguments, $_SERVER['CLI']) = Command::options($_SERVER['argv']);
$_SERVER['CLI'] = array_change_key_case($_SERVER['CLI'], CASE_UPPER);
/**
* The Laravel environment may be specified on the CLI using the "env"
* option, allowing the developer to easily use local configuration
* files from the CLI since the environment is usually controlled
* by server environmenet variables.
*/
if (isset($_SERVER['CLI']['ENV']))
{
$_SERVER['LARAVEL_ENV'] = $_SERVER['CLI']['ENV'];
}
/** /**
* The default database connection may be set by specifying a value * The default database connection may be set by specifying a value
* for the "database" CLI option. This allows migrations to be run * for the "database" CLI option. This allows migrations to be run
......
...@@ -55,10 +55,8 @@ class Bundler extends Task { ...@@ -55,10 +55,8 @@ class Bundler extends Task {
$this->download($bundle, $path); $this->download($bundle, $path);
echo "Bundle [{$bundle['name']}] has been installed!".PHP_EOL; echo "Bundle [{$bundle['name']}] installed!".PHP_EOL;
} }
$this->refresh();
} }
/** /**
...@@ -83,7 +81,7 @@ class Bundler extends Task { ...@@ -83,7 +81,7 @@ class Bundler extends Task {
// First we want to retrieve the information for the bundle, such as // First we want to retrieve the information for the bundle, such as
// where it is currently installed. This will allow us to upgrade // where it is currently installed. This will allow us to upgrade
// the bundle into it's current installation path. // the bundle into it's current installation path.
$bundle = Bundle::get($name); $location = Bundle::location($name);
// If the bundle exists, we will grab the data about the bundle from // If the bundle exists, we will grab the data about the bundle from
// the API so we can make the right bundle provider for the bundle, // the API so we can make the right bundle provider for the bundle,
...@@ -98,89 +96,12 @@ class Bundler extends Task { ...@@ -98,89 +96,12 @@ class Bundler extends Task {
// Once we have the bundle information from the API, we'll simply // Once we have the bundle information from the API, we'll simply
// recursively delete the bundle and then re-download it using // recursively delete the bundle and then re-download it using
// the correct provider assigned to the bundle. // the correct provider assigned to the bundle.
File::rmdir($bundle['location']); File::rmdir($location);
$this->download($response['bundle'], $bundle['location']); $this->download($response['bundle'], $location);
echo "Bundle [{$name}] has been upgraded!".PHP_EOL; echo "Bundle [{$name}] has been upgraded!".PHP_EOL;
} }
$this->refresh();
}
/**
* Publish bundle assets to the public directory.
*
* @param array $bundles
* @return void
*/
public function publish($bundles)
{
if (count($bundles) == 0) $bundles = Bundle::names();
array_walk($bundles, array(IoC::resolve('bundle.publisher'), 'publish'));
}
/**
* Create a new bundle stub.
*
* @param array $arguments
* @return void
*/
public function make($arguments)
{
if ( ! isset($arguments[0]))
{
throw new \Exception("We need to know the bundle name!");
}
// First we'll grab the name from the argument list and make sure a bundle
// with that name doesn't already exist. If it does, we'll bomb out and
// notify the developer of the problem. Bundle names must be unique
// since classes are prefixed with the name.
$options['name'] = $name = $arguments[0];
if (Bundle::exists($name))
{
throw new \Exception("That bundle already exists!");
}
// The developer may specify a location to which the bundle should be
// installed. If a location is not specified, the bundle name will
// be used as the default installation location.
$location = Request::server('cli.location') ?: $name;
$location = path('bundle').$location;
$options['handles'] = Request::server('cli.handles');
// We'll create the actual PHP that should be inserted into the info
// file for the bundle. This contains the bundle's name as well as
// any URIs it is setup to handle.
$info = '<?php return '.var_export($options, true).';';
mkdir($location, 0777, true);
// Finally we can write the file to disk and clear the bundle cache.
// We clear the cache so that the new bundle will be recognized
// immediately and the developer can start using it.
File::put($location.DS.'bundle'.EXT, $info);
echo "Bundle [{$name}] has been created!".PHP_EOL;
$this->refresh();
}
/**
* Clear the bundle manifest cache.
*
* @return void
*/
public function refresh()
{
Cache::forget(Bundle::manifest);
echo 'Bundle cache cleared!'.PHP_EOL;
} }
/** /**
...@@ -197,7 +118,7 @@ class Bundler extends Task { ...@@ -197,7 +118,7 @@ class Bundler extends Task {
{ {
// First we'll call the bundle repository to gather the bundle data // First we'll call the bundle repository to gather the bundle data
// array, which contains all of the information needed to install // array, which contains all of the information needed to install
// the bundle into the application. // the bundle into the Laravel application.
$response = $this->retrieve($bundle); $response = $this->retrieve($bundle);
if ($response['status'] == 'not-found') if ($response['status'] == 'not-found')
...@@ -207,12 +128,14 @@ class Bundler extends Task { ...@@ -207,12 +128,14 @@ class Bundler extends Task {
// If the bundle was retrieved successfully, we will add it to // If the bundle was retrieved successfully, we will add it to
// our array of bundles, as well as merge all of the bundle's // our array of bundles, as well as merge all of the bundle's
// dependencies into the array of responses so that they are // dependencies into the array of responses.
// installed along with the consuming dependency.
$bundle = $response['bundle']; $bundle = $response['bundle'];
$responses[] = $bundle; $responses[] = $bundle;
// We'll also get the bundle's declared dependenceis so they
// can be installed along with the bundle, making it easy
// to install a group of bundles.
$dependencies = $this->get($bundle['dependencies']); $dependencies = $this->get($bundle['dependencies']);
$responses = array_merge($responses, $dependencies); $responses = array_merge($responses, $dependencies);
...@@ -221,6 +144,19 @@ class Bundler extends Task { ...@@ -221,6 +144,19 @@ class Bundler extends Task {
return $responses; return $responses;
} }
/**
* Publish bundle assets to the public directory.
*
* @param array $bundles
* @return void
*/
public function publish($bundles)
{
if (count($bundles) == 0) $bundles = Bundle::names();
array_walk($bundles, array(IoC::resolve('bundle.publisher'), 'publish'));
}
/** /**
* Install a bundle using a provider. * Install a bundle using a provider.
* *
......
...@@ -115,7 +115,7 @@ class Migrator extends Task { ...@@ -115,7 +115,7 @@ class Migrator extends Task {
// along with their bundles and names. We will iterate through each // along with their bundles and names. We will iterate through each
// migration and run the "down" method, removing them from the // migration and run the "down" method, removing them from the
// database as we go. // database as we go.
foreach ($migrations as $migration) foreach (array_reverse($migrations) as $migration)
{ {
$migration['migration']->down(); $migration['migration']->down();
......
...@@ -30,14 +30,6 @@ require path('sys').'autoloader'.EXT; ...@@ -30,14 +30,6 @@ require path('sys').'autoloader'.EXT;
*/ */
spl_autoload_register(array('Laravel\\Autoloader', 'load')); spl_autoload_register(array('Laravel\\Autoloader', 'load'));
/**
* Register all of the core class aliases. These aliases provide a
* convenient way of working with the Laravel core classes without
* having to worry about the namespacing. The developer is also
* free to remove aliases when they extend core classes.
*/
Autoloader::$aliases = Config::get('application.aliases');
/** /**
* Register the Laravel namespace so that the auto-loader loads it * Register the Laravel namespace so that the auto-loader loads it
* according to the PSR-0 naming conventions. This should provide * according to the PSR-0 naming conventions. This should provide
...@@ -46,27 +38,40 @@ Autoloader::$aliases = Config::get('application.aliases'); ...@@ -46,27 +38,40 @@ Autoloader::$aliases = Config::get('application.aliases');
Autoloader::namespaces(array('Laravel' => path('sys'))); Autoloader::namespaces(array('Laravel' => path('sys')));
/** /**
* Grab the bundle manifest for the application. This contains an * Set the CLI options on the $_SERVER global array so we can easily
* array of all of the installed bundles, plus information about * retrieve them from the various parts of the CLI code. We can use
* each of them. If it's not cached, we'll detect them and then * the Request class to access them conveniently.
* cache it to save time later.
*/ */
$bundles = Cache::remember(Bundle::manifest, function() if (defined('STDIN'))
{ {
return Bundle::detect(path('bundle')); $console = CLI\Command::options($_SERVER['argv']);
list($arguments, $options) = $console;
$options = array_change_key_case($options, CASE_UPPER);
}, Config::get('application.bundle.cache')); $_SERVER['CLI'] = $options;
}
/** /**
* Register all of the bundles that are defined in the main bundle * The Laravel environment may be specified on the CLI using the env
* manifest. This informs the framework where the bundle lives * option, allowing the developer to easily use local configuration
* and which URIs it can respnod to. * files from the CLI since the environment is usually controlled
* by server environmenet variables.
*/ */
foreach ($bundles as $bundle) if (isset($_SERVER['CLI']['ENV']))
{ {
Bundle::register($bundle); $_SERVER['LARAVEL_ENV'] = $_SERVER['CLI']['ENV'];
} }
/**
* Register all of the core class aliases. These aliases provide a
* convenient way of working with the Laravel core classes without
* having to worry about the namespacing. The developer is also
* free to remove aliases when they extend core classes.
*/
Autoloader::$aliases = Config::get('application.aliases');
/** /**
* Register the default timezone for the application. This will * Register the default timezone for the application. This will
* be the default timezone used by all date functions through * be the default timezone used by all date functions through
...@@ -75,3 +80,15 @@ foreach ($bundles as $bundle) ...@@ -75,3 +80,15 @@ foreach ($bundles as $bundle)
$timezone = Config::get('application.timezone'); $timezone = Config::get('application.timezone');
date_default_timezone_set($timezone); date_default_timezone_set($timezone);
/**
* Finally we'll grab all of the bundles and register them
* with the bundle class. All of the bundles are stored in
* an array within the application directory.
*/
$bundles = require path('app').'bundles'.EXT;
foreach ($bundles as $bundle => $config)
{
Bundle::register($bundle, $config);
}
\ No newline at end of file
...@@ -20,11 +20,11 @@ class MySQL extends Connector { ...@@ -20,11 +20,11 @@ class MySQL extends Connector {
// Check for any optional MySQL PDO options. These options are not required // Check for any optional MySQL PDO options. These options are not required
// to establish a PDO connection; however, may be needed in certain server // to establish a PDO connection; however, may be needed in certain server
// or hosting environments used by the developer. // or hosting environments used by the developer.
foreach (array('port', 'unix_socket') as $key => $value) foreach (array('port', 'unix_socket') as $key)
{ {
if (isset($config[$key])) if (isset($config[$key]))
{ {
$dsn .= ";{$key}={$value}"; $dsn .= ";{$key}={$config[$key]}";
} }
} }
......
...@@ -23,14 +23,14 @@ class MySQL extends Grammar { ...@@ -23,14 +23,14 @@ class MySQL extends Grammar {
{ {
$columns = implode(', ', $this->columns($table)); $columns = implode(', ', $this->columns($table));
// First we will generate the base table creation statement. Other than // First we will generate the base table creation statement. Other than incrementing
// auto-incrementing keys, no indexes will be created during the first // keys, no indexes will be created during the first creation of the table since
// creation of the table. They will be added in separate commands. // they will be added in separate commands.
$sql = 'CREATE TABLE '.$this->wrap($table).' ('.$columns.')'; $sql = 'CREATE TABLE '.$this->wrap($table).' ('.$columns.')';
// MySQL supports various "engines" for database tables. If an engine // MySQL supports various "engines" for database tables. If an engine ws specified
// was specified by the developer, we will set it after adding the // by the developer, we will set it after adding the columns the table creation
// columns the table creation statement. // statement. Some engines support extra indexes.
if ( ! is_null($table->engine)) if ( ! is_null($table->engine))
{ {
$sql .= ' ENGINE = '.$table->engine; $sql .= ' ENGINE = '.$table->engine;
...@@ -50,9 +50,9 @@ class MySQL extends Grammar { ...@@ -50,9 +50,9 @@ class MySQL extends Grammar {
{ {
$columns = $this->columns($table); $columns = $this->columns($table);
// Once we the array of column definitions, we need to add "add" // Once we the array of column definitions, we need to add "add" to the front
// to the front of each definition, then we'll concatenate the // of each definition, then we'll concatenate the definitions using commas
// definitions using commas like normal and generate the SQL. // like normal and generate the SQL.
$columns = implode(', ', array_map(function($column) $columns = implode(', ', array_map(function($column)
{ {
return 'ADD '.$column; return 'ADD '.$column;
...@@ -77,7 +77,7 @@ class MySQL extends Grammar { ...@@ -77,7 +77,7 @@ class MySQL extends Grammar {
// Each of the data type's have their own definition creation method, // Each of the data type's have their own definition creation method,
// which is responsible for creating the SQL for the type. This lets // which is responsible for creating the SQL for the type. This lets
// us to keep the syntax easy and fluent, while translating the // us to keep the syntax easy and fluent, while translating the
// types to the types used by the database. // types to the correct types.
$sql = $this->wrap($column).' '.$this->type($column); $sql = $this->wrap($column).' '.$this->type($column);
$elements = array('nullable', 'defaults', 'incrementer'); $elements = array('nullable', 'defaults', 'incrementer');
...@@ -223,9 +223,9 @@ class MySQL extends Grammar { ...@@ -223,9 +223,9 @@ class MySQL extends Grammar {
{ {
$columns = array_map(array($this, 'wrap'), $command->columns); $columns = array_map(array($this, 'wrap'), $command->columns);
// Once we the array of column names, we need to add "drop" to the // Once we the array of column names, we need to add "drop" to the front
// front of each column, then we'll concatenate the columns using // of each column, then we'll concatenate the columns using commas and
// commas and generate the alter statement SQL. // generate the alter statement SQL.
$columns = implode(', ', array_map(function($column) $columns = implode(', ', array_map(function($column)
{ {
return 'DROP '.$column; return 'DROP '.$column;
......
...@@ -16,9 +16,9 @@ class Postgres extends Grammar { ...@@ -16,9 +16,9 @@ class Postgres extends Grammar {
{ {
$columns = implode(', ', $this->columns($table)); $columns = implode(', ', $this->columns($table));
// First we will generate the base table creation statement. Other than // First we will generate the base table creation statement. Other than incrementing
// auto-incrementing keys, no indexes will be created during the first // keys, no indexes will be created during the first creation of the table since
// creation of the table. They will be added in separate commands. // they will be added in separate commands.
$sql = 'CREATE TABLE '.$this->wrap($table).' ('.$columns.')'; $sql = 'CREATE TABLE '.$this->wrap($table).' ('.$columns.')';
return $sql; return $sql;
...@@ -35,9 +35,9 @@ class Postgres extends Grammar { ...@@ -35,9 +35,9 @@ class Postgres extends Grammar {
{ {
$columns = $this->columns($table); $columns = $this->columns($table);
// Once we the array of column definitions, we'll add "add column" // Once we the array of column definitions, we need to add "add" to the front
// to the front of each definition, then we'll concatenate the // of each definition, then we'll concatenate the definitions using commas
// definitions using commas like normal and generate the SQL. // like normal and generate the SQL.
$columns = implode(', ', array_map(function($column) $columns = implode(', ', array_map(function($column)
{ {
return 'ADD COLUMN '.$column; return 'ADD COLUMN '.$column;
...@@ -114,10 +114,9 @@ class Postgres extends Grammar { ...@@ -114,10 +114,9 @@ class Postgres extends Grammar {
*/ */
protected function incrementer(Table $table, Fluent $column) protected function incrementer(Table $table, Fluent $column)
{ {
// We don't actually need to specify an "auto_increment" keyword since // We don't actually need to specify an "auto_increment" keyword since we
// we handle the auto-increment definition in the type definition for // handle the auto-increment definition in the type definition for
// integers by changing the type to "serial", which is a convenient // integers by changing the type to "serial".
// notational short-cut provided by Postgres.
if ($column->type == 'integer' and $column->increment) if ($column->type == 'integer' and $column->increment)
{ {
return ' PRIMARY KEY'; return ' PRIMARY KEY';
...@@ -218,9 +217,9 @@ class Postgres extends Grammar { ...@@ -218,9 +217,9 @@ class Postgres extends Grammar {
{ {
$columns = array_map(array($this, 'wrap'), $command->columns); $columns = array_map(array($this, 'wrap'), $command->columns);
// Once we the array of column names, we need to add "drop" to the // Once we the array of column names, we need to add "drop" to the front
// front of each column, then we'll concatenate the columns using // of each column, then we'll concatenate the columns using commas and
// commas and generate the alter statement SQL. // generate the alter statement SQL.
$columns = implode(', ', array_map(function($column) $columns = implode(', ', array_map(function($column)
{ {
return 'DROP COLUMN '.$column; return 'DROP COLUMN '.$column;
......
...@@ -16,26 +16,22 @@ class SQLite extends Grammar { ...@@ -16,26 +16,22 @@ class SQLite extends Grammar {
{ {
$columns = implode(', ', $this->columns($table)); $columns = implode(', ', $this->columns($table));
// First we will generate the base table creation statement. Other than // First we will generate the base table creation statement. Other than incrementing
// auto-incrementing keys, no indexes will be created during the first // keys, no indexes will be created during the first creation of the table since
// creation of the table. They will be added in separate commands. // they will be added in separate commands.
$sql = 'CREATE TABLE '.$this->wrap($table).' ('.$columns; $sql = 'CREATE TABLE '.$this->wrap($table).' ('.$columns;
// SQLite does not allow adding a primary key as a command apart from // SQLite does not allow adding a primary key as a command apart from the creation
// when the table is initially created, so we'll need to sniff out // of the table, so we'll need to sniff out any primary keys here and add them to
// any primary keys here and add them to the table. // the table now during this command.
//
// Because of this, this class does not have the typical "primary"
// method as it would be pointless since the primary keys can't
// be set on anything but the table creation statement.
$primary = array_first($table->commands, function($key, $value) $primary = array_first($table->commands, function($key, $value)
{ {
return $value->type == 'primary'; return $value->type == 'primary';
}); });
// If we found primary key in the array of commands, we'll create // If we found primary key in the array of commands, we'll create the SQL for
// the SQL for the key addition and append it to the SQL table // the key addition and append it to the SQL table creation statement for
// creation statement for the schema table. // the schema table so the index is properly generated.
if ( ! is_null($primary)) if ( ! is_null($primary))
{ {
$columns = $this->columnize($primary->columns); $columns = $this->columnize($primary->columns);
...@@ -57,18 +53,18 @@ class SQLite extends Grammar { ...@@ -57,18 +53,18 @@ class SQLite extends Grammar {
{ {
$columns = $this->columns($table); $columns = $this->columns($table);
// Once we have an array of all of the column definitions, we need to // Once we the array of column definitions, we need to add "add" to the front
// spin through each one and prepend "ADD COLUMN" to each of them, // of each definition, then we'll concatenate the definitions using commas
// which is the syntax used by SQLite when adding columns. // like normal and generate the SQL.
$columns = array_map(function($column) $columns = array_map(function($column)
{ {
return 'ADD COLUMN '.$column; return 'ADD COLUMN '.$column;
}, $columns); }, $columns);
// SQLite only allows one column to be added in an ALTER statement, // SQLite only allows one column to be added in an ALTER statement, so we
// so we will create an array of statements and return them all to // will create an array of statements and return them all to the schema
// the schema manager, which will execute each one. // manager, which will execute each one separately.
foreach ($columns as $column) foreach ($columns as $column)
{ {
$sql[] = 'ALTER TABLE '.$this->wrap($table).' '.$column; $sql[] = 'ALTER TABLE '.$this->wrap($table).' '.$column;
......
...@@ -23,9 +23,9 @@ class SQLServer extends Grammar { ...@@ -23,9 +23,9 @@ class SQLServer extends Grammar {
{ {
$columns = implode(', ', $this->columns($table)); $columns = implode(', ', $this->columns($table));
// First we will generate the base table creation statement. Other than // First we will generate the base table creation statement. Other than incrementing
// auto-incrementing keys, no indexes will be created during the first // keys, no indexes will be created during the first creation of the table since
// creation of the table. They will be added in separate commands. // they will be added in separate commands.
$sql = 'CREATE TABLE '.$this->wrap($table).' ('.$columns.')'; $sql = 'CREATE TABLE '.$this->wrap($table).' ('.$columns.')';
return $sql; return $sql;
...@@ -42,9 +42,9 @@ class SQLServer extends Grammar { ...@@ -42,9 +42,9 @@ class SQLServer extends Grammar {
{ {
$columns = $this->columns($table); $columns = $this->columns($table);
// Once we the array of column definitions, we need to add "add" // Once we the array of column definitions, we need to add "add" to the front
// to the front of each definition, then we'll concatenate the // of each definition, then we'll concatenate the definitions using commas
// definitions using commas like normal and generate the SQL. // like normal and generate the SQL.
$columns = implode(', ', array_map(function($column) $columns = implode(', ', array_map(function($column)
{ {
return 'ADD '.$column; return 'ADD '.$column;
...@@ -166,18 +166,18 @@ class SQLServer extends Grammar { ...@@ -166,18 +166,18 @@ class SQLServer extends Grammar {
{ {
$columns = $this->columnize($command->columns); $columns = $this->columnize($command->columns);
// SQL Server requires the creation of a full-text "catalog" before $table = $this->wrap($table);
// creating a full-text index, so we'll first create the catalog
// then add another statement for the index. The catalog will // SQL Server requires the creation of a full-text "catalog" before creating
// be updated automatically by the server. // a full-text index, so we'll first create the catalog then add another
// separate statement for the index.
$sql[] = "CREATE FULLTEXT CATALOG {$command->catalog}"; $sql[] = "CREATE FULLTEXT CATALOG {$command->catalog}";
$create = "CREATE FULLTEXT INDEX ON ".$this->wrap($table)." ({$columns}) "; $create = "CREATE FULLTEXT INDEX ON ".$table." ({$columns}) ";
// Full-text indexes must specify a unique, non-nullable column as // Full-text indexes must specify a unique, non-null column as the index
// the index "key" and this should have been created manually by // "key" and this should have been created manually by the developer in
// the developer in a separate column addition command, so we // a separate column addition command.
// can just specify it in this statement.
$sql[] = $create .= "KEY INDEX {$command->key} ON {$command->catalog}"; $sql[] = $create .= "KEY INDEX {$command->key} ON {$command->catalog}";
return $sql; return $sql;
...@@ -235,9 +235,9 @@ class SQLServer extends Grammar { ...@@ -235,9 +235,9 @@ class SQLServer extends Grammar {
{ {
$columns = array_map(array($this, 'wrap'), $command->columns); $columns = array_map(array($this, 'wrap'), $command->columns);
// Once we the array of column names, we need to add "drop" to the // Once we the array of column names, we need to add "drop" to the front
// front of each column, then we'll concatenate the columns using // of each column, then we'll concatenate the columns using commas and
// commas and generate the alter statement SQL. // generate the alter statement SQL.
$columns = implode(', ', array_map(function($column) $columns = implode(', ', array_map(function($column)
{ {
return 'DROP '.$column; return 'DROP '.$column;
......
...@@ -25,9 +25,15 @@ class Error { ...@@ -25,9 +25,15 @@ class Error {
<h3>Stack Trace:</h3> <h3>Stack Trace:</h3>
<pre>".$exception->getTraceAsString()."</pre></html>"; <pre>".$exception->getTraceAsString()."</pre></html>";
} }
// If we're not using detailed error messages, we'll use the event
// system to get the response that should be sent to the browser.
// Using events gives the developer more freedom.
else else
{ {
Response::error('500')->send(); $response = Event::first('500');
return Response::prepare($response)->send();
} }
exit(1); exit(1);
...@@ -48,8 +54,7 @@ class Error { ...@@ -48,8 +54,7 @@ class Error {
// For a PHP error, we'll create an ErrorExcepetion and then feed that // For a PHP error, we'll create an ErrorExcepetion and then feed that
// exception to the exception method, which will create a simple view // exception to the exception method, which will create a simple view
// of the exception details. The ErrorException class is built-in to // of the exception details for the developer.
// PHP for converting native errors.
$exception = new \ErrorException($error, $code, 0, $file, $line); $exception = new \ErrorException($error, $code, 0, $file, $line);
if (in_array($code, Config::get('error.ignore'))) if (in_array($code, Config::get('error.ignore')))
...@@ -71,8 +76,10 @@ class Error { ...@@ -71,8 +76,10 @@ class Error {
{ {
// If a fatal error occured that we have not handled yet, we will // If a fatal error occured that we have not handled yet, we will
// create an ErrorException and feed it to the exception handler, // create an ErrorException and feed it to the exception handler,
// as it will not have been handled by the error handler. // as it will not yet have been handled.
if ( ! is_null($error = error_get_last())) $error = error_get_last();
if ( ! is_null($error))
{ {
extract($error, EXTR_SKIP); extract($error, EXTR_SKIP);
......
...@@ -40,6 +40,26 @@ class Event { ...@@ -40,6 +40,26 @@ class Event {
static::$events[$event][] = $callback; static::$events[$event][] = $callback;
} }
/**
* Fire an event and return the first response.
*
* <code>
* // Fire the "start" event
* $response = Event::first('start');
*
* // Fire the "start" event passing an array of parameters
* $response = Event::first('start', array('Laravel', 'Framework'));
* </code>
*
* @param string $event
* @param array $parameters
* @return mixed
*/
public static function first($event, $parameters = array())
{
return head(static::fire($event, $parameters));
}
/** /**
* Fire an event so that all listeners are called. * Fire an event so that all listeners are called.
* *
......
...@@ -90,14 +90,14 @@ function array_set(&$array, $key, $value) ...@@ -90,14 +90,14 @@ function array_set(&$array, $key, $value)
// This loop allows us to dig down into the array to a dynamic depth by // This loop allows us to dig down into the array to a dynamic depth by
// setting the array value for each level that we dig into. Once there // setting the array value for each level that we dig into. Once there
// is one key left, we can fall out of the loop and set the value as // is one key left, we can fall out of the loop and set the value as
// we should be at the proper depth within the array. // we should be at the proper depth.
while (count($keys) > 1) while (count($keys) > 1)
{ {
$key = array_shift($keys); $key = array_shift($keys);
// If the key doesn't exist at this depth, we will just create an // If the key doesn't exist at this depth, we will just create an
// empty array to hold the next value, allowing us to create the // empty array to hold the next value, allowing us to create the
// arrays to hold the final value at the proper depth. // arrays to hold the final value.
if ( ! isset($array[$key]) or ! is_array($array[$key])) if ( ! isset($array[$key]) or ! is_array($array[$key]))
{ {
$array[$key] = array(); $array[$key] = array();
...@@ -131,7 +131,7 @@ function array_forget(&$array, $key) ...@@ -131,7 +131,7 @@ function array_forget(&$array, $key)
// This loop functions very similarly to the loop in the "set" method. // This loop functions very similarly to the loop in the "set" method.
// We will iterate over the keys, setting the array value to the new // We will iterate over the keys, setting the array value to the new
// depth at each iteration. Once there is only one key left, we will // depth at each iteration. Once there is only one key left, we will
// be at the proper depth in the array to "forget" the value. // be at the proper depth in the array.
while (count($keys) > 1) while (count($keys) > 1)
{ {
$key = array_shift($keys); $key = array_shift($keys);
...@@ -139,7 +139,7 @@ function array_forget(&$array, $key) ...@@ -139,7 +139,7 @@ function array_forget(&$array, $key)
// Since this method is supposed to remove a value from the array, // Since this method is supposed to remove a value from the array,
// if a value higher up in the chain doesn't exist, there is no // if a value higher up in the chain doesn't exist, there is no
// need to keep digging into the array, since it is impossible // need to keep digging into the array, since it is impossible
// for the final value to even exist in the array. // for the final value to even exist.
if ( ! isset($array[$key]) or ! is_array($array[$key])) if ( ! isset($array[$key]) or ! is_array($array[$key]))
{ {
return; return;
...@@ -339,6 +339,18 @@ function starts_with($haystack, $needle) ...@@ -339,6 +339,18 @@ function starts_with($haystack, $needle)
return strpos($haystack, $needle) === 0; return strpos($haystack, $needle) === 0;
} }
/**
* Determine if a given string ends with a given value.
*
* @param string $haystack
* @param string $needle
* @return bool
*/
function ends_with($haystack, $needle)
{
return $needle == substr($haystack, strlen($haystack) - strlen($needle));
}
/** /**
* Determine if a given string contains a given sub-string. * Determine if a given string contains a given sub-string.
* *
......
...@@ -132,15 +132,25 @@ Input::$input = $input; ...@@ -132,15 +132,25 @@ Input::$input = $input;
Bundle::start(DEFAULT_BUNDLE); Bundle::start(DEFAULT_BUNDLE);
/** /**
* Start all of the bundles that are specified in the configuration * Auto-start any bundles configured to start on every request.
* array of auto-loaded bundles. This lets the developer have an * This is especially useful for debug bundles or bundles that
* easy way to load bundles for every request. * are used throughout the application.
*/ */
foreach (Config::get('application.bundle.auto') as $bundle) foreach (Bundle::$bundles as $bundle => $config)
{ {
Bundle::start($bundle); if ($config['auto']) Bundle::start($bundle);
} }
/**
* Register the "catch-all" route that handles 404 responses for
* routes that can not be matched to any other route within the
* application. We'll just raise the 404 event.
*/
Routing\Router::register('*', '(:all)', function()
{
return Event::first('404');
});
/** /**
* If the requset URI has too many segments, we will bomb out of * If the requset URI has too many segments, we will bomb out of
* the request. This is too avoid potential DDoS attacks against * the request. This is too avoid potential DDoS attacks against
...@@ -162,16 +172,6 @@ if (count(URI::$segments) > 15) ...@@ -162,16 +172,6 @@ if (count(URI::$segments) > 15)
*/ */
Request::$route = Routing\Router::route(Request::method(), $uri); Request::$route = Routing\Router::route(Request::method(), $uri);
if (is_null(Request::$route))
{
Request::$route = new Routing\Route('GET /404', array(function()
{
return Response::error('404');
}));
$response = Response::error('404');
}
$response = Request::$route->call(); $response = Request::$route->call();
/** /**
......
...@@ -35,6 +35,11 @@ class Redirect extends Response { ...@@ -35,6 +35,11 @@ class Redirect extends Response {
return static::to($url, $status, true); return static::to($url, $status, true);
} }
public static function to_action($action, $parameters = array())
{
}
/** /**
* Create a redirect response to a named route. * Create a redirect response to a named route.
* *
...@@ -49,25 +54,11 @@ class Redirect extends Response { ...@@ -49,25 +54,11 @@ class Redirect extends Response {
* @param string $route * @param string $route
* @param array $parameters * @param array $parameters
* @param int $status * @param int $status
* @param bool $https
* @return Redirect
*/
public static function to_route($route, $parameters = array(), $status = 302, $https = false)
{
return static::to(URL::to_route($route, $parameters, $https), $status);
}
/**
* Create a redirect response to a named route using HTTPS.
*
* @param string $route
* @param array $parameters
* @param int $status
* @return Redirect * @return Redirect
*/ */
public static function to_secure_route($route, $parameters = array(), $status = 302) public static function to_route($route, $parameters = array(), $status = 302)
{ {
return static::to_route($route, $parameters, $status, true); return static::to(URL::to_route($route, $parameters), $status);
} }
/** /**
......
<?php namespace Laravel; <?php namespace Laravel; use Closure;
use Closure;
class Request { class Request {
/** /**
* The route handling the current request. * All of the route instances handling the request.
* *
* @var Routing\Route * @var array
*/ */
public static $route; public static $route;
...@@ -139,7 +137,7 @@ class Request { ...@@ -139,7 +137,7 @@ class Request {
} }
/** /**
* Get the route handling the current request. * Get the main route handling the request.
* *
* @return Route * @return Route
*/ */
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
use Laravel\IoC; use Laravel\IoC;
use Laravel\Str; use Laravel\Str;
use Laravel\View; use Laravel\View;
use Laravel\Event;
use Laravel\Bundle; use Laravel\Bundle;
use Laravel\Request; use Laravel\Request;
use Laravel\Redirect; use Laravel\Redirect;
...@@ -17,6 +18,13 @@ abstract class Controller { ...@@ -17,6 +18,13 @@ abstract class Controller {
*/ */
public $layout; public $layout;
/**
* The bundle the controller belongs to.
*
* @var string
*/
public $bundle;
/** /**
* Indicates if the controller uses RESTful routing. * Indicates if the controller uses RESTful routing.
* *
...@@ -38,7 +46,7 @@ abstract class Controller { ...@@ -38,7 +46,7 @@ abstract class Controller {
* // Call the "show" method on the "user" controller * // Call the "show" method on the "user" controller
* $response = Controller::call('user@show'); * $response = Controller::call('user@show');
* *
* // Call the "profile" method on the "user/admin" controller and pass parameters * // Call the "user/admin" controller and pass parameters
* $response = Controller::call('user.admin@profile', array($username)); * $response = Controller::call('user.admin@profile', array($username));
* </code> * </code>
* *
...@@ -48,47 +56,52 @@ abstract class Controller { ...@@ -48,47 +56,52 @@ abstract class Controller {
*/ */
public static function call($destination, $parameters = array()) public static function call($destination, $parameters = array())
{ {
static::references($destination, $parameters);
list($bundle, $destination) = Bundle::parse($destination); list($bundle, $destination) = Bundle::parse($destination);
// We will always start the bundle, just in case the developer is pointing // We will always start the bundle, just in case the developer is pointing
// a route to another bundle. This allows us to lazy load the bundle and // a route to another bundle. This allows us to lazy load the bundle and
// improve performance since the bundle is not loaded on every request. // improve speed since the bundle is not loaded on every request.
Bundle::start($bundle); Bundle::start($bundle);
list($controller, $method) = explode('@', $destination); list($controller, $method) = explode('@', $destination);
list($method, $parameters) = static::backreference($method, $parameters);
$controller = static::resolve($bundle, $controller); $controller = static::resolve($bundle, $controller);
// If the controller could not be resolved, we're out of options and // If the controller could not be resolved, we're out of options and
// will return the 404 error response. If we found the controller, // will return the 404 error response. If we found the controller,
// we can execute the requested method on the instance. // we can execute the requested method on the instance.
if (is_null($controller)) return Response::error('404'); if (is_null($controller))
{
return Event::first('404');
}
return $controller->execute($method, $parameters); return $controller->execute($method, $parameters);
} }
/** /**
* Replace all back-references on the given method. * Replace all back-references on the given destination.
* *
* @param string $method * @param string $destination
* @param array $parameters * @param array $parameters
* @return array * @return array
*/ */
protected static function backreference($method, $parameters) protected static function references(&$destination, &$parameters)
{ {
// Controller delegates may use back-references to the action parameters, // Controller delegates may use back-references to the action parameters,
// which allows the developer to setup more flexible routes to various // which allows the developer to setup more flexible routes to various
// controllers with much less code than usual. // controllers with much less code than would be usual.
foreach ($parameters as $key => $value) foreach ($parameters as $key => $value)
{ {
$method = str_replace('(:'.($key + 1).')', $value, $method, $count); $search = '(:'.($key + 1).')';
$destination = str_replace($search, $value, $destination, $count);
if ($count > 0) unset($parameters[$key]); if ($count > 0) unset($parameters[$key]);
} }
return array(str_replace('(:1)', 'index', $method), $parameters); return array($destination, $parameters);
} }
/** /**
...@@ -100,18 +113,23 @@ abstract class Controller { ...@@ -100,18 +113,23 @@ abstract class Controller {
*/ */
public static function resolve($bundle, $controller) public static function resolve($bundle, $controller)
{ {
if ( ! static::load($bundle, $controller)) return; $identifier = Bundle::identifier($bundle, $controller);
// If the controller is registered in the IoC container, we will resolve // If the controller is registered in the IoC container, we will resolve
// it out of the container. Using constructor injection on controllers // it out of the container. Using constructor injection on controllers
// via the container allows more flexible and testable applications. // via the container allows more flexible applications.
$resolver = 'controller: '.Bundle::identifier($bundle, $controller); $resolver = 'controller: '.$identifier;
if (IoC::registered($resolver)) if (IoC::registered($resolver))
{ {
return IoC::resolve($resolver); return IoC::resolve($resolver);
} }
// If we couldn't resolve the controller out of the IoC container we'll
// format the controller name into its proper class name and load it
// by convention out of the bundle's controller directory.
if ( ! static::load($bundle, $controller)) return;
$controller = static::format($bundle, $controller); $controller = static::format($bundle, $controller);
$controller = new $controller; $controller = new $controller;
...@@ -169,11 +187,12 @@ abstract class Controller { ...@@ -169,11 +187,12 @@ abstract class Controller {
*/ */
public function execute($method, $parameters = array()) public function execute($method, $parameters = array())
{ {
$filters = $this->filters('before', $method);
// Again, as was the case with route closures, if the controller "before" // Again, as was the case with route closures, if the controller "before"
// filters return a response, it will be considered the response to the // filters return a response, it will be considered the response to the
// request and the controller method will not be used to handle the // request and the controller method will not be used .
// request to the application. $response = Filter::run($filters, array(), true);
$response = Filter::run($this->filters('before', $method), array(), true);
if (is_null($response)) if (is_null($response))
{ {
...@@ -186,7 +205,7 @@ abstract class Controller { ...@@ -186,7 +205,7 @@ abstract class Controller {
// The "after" function on the controller is simply a convenient hook // The "after" function on the controller is simply a convenient hook
// so the developer can work on the response before it's returned to // so the developer can work on the response before it's returned to
// the browser. This is useful for setting partials on the layout. // the browser. This is useful for templating, etc.
$this->after($response); $this->after($response);
Filter::run($this->filters('after', $method), array($response)); Filter::run($this->filters('after', $method), array($response));
...@@ -321,7 +340,7 @@ abstract class Controller { ...@@ -321,7 +340,7 @@ abstract class Controller {
* Dynamically resolve items from the application IoC container. * Dynamically resolve items from the application IoC container.
* *
* <code> * <code>
* // Retrieve an object registered in the container as "mailer" * // Retrieve an object registered in the container
* $mailer = $this->mailer; * $mailer = $this->mailer;
* *
* // Equivalent call using the IoC container instance * // Equivalent call using the IoC container instance
...@@ -330,9 +349,10 @@ abstract class Controller { ...@@ -330,9 +349,10 @@ abstract class Controller {
*/ */
public function __get($key) public function __get($key)
{ {
if (IoC::registered($key)) return IoC::resolve($key); if (IoC::registered($key))
{
throw new \Exception("Accessing undefined property [$key] on controller."); return IoC::resolve($key);
}
} }
} }
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
...@@ -21,7 +21,20 @@ class URI { ...@@ -21,7 +21,20 @@ class URI {
* *
* @var array * @var array
*/ */
protected static $attempt = array('PATH_INFO', 'REQUEST_URI', 'PHP_SELF', 'REDIRECT_URL'); protected static $attempt = array(
'PATH_INFO', 'REQUEST_URI',
'PHP_SELF', 'REDIRECT_URL'
);
/**
* Get the full URI including the query string.
*
* @return string
*/
public static function full()
{
return static::current().static::query();
}
/** /**
* Get the URI for the current request. * Get the URI for the current request.
...@@ -39,7 +52,7 @@ class URI { ...@@ -39,7 +52,7 @@ class URI {
// If you ever encounter this error, please inform the nerdy Laravel // If you ever encounter this error, please inform the nerdy Laravel
// dev team with information about your server. We want to support // dev team with information about your server. We want to support
// Laravel an as many server environments as possible! // Laravel an as many servers as we possibly can!
if (is_null(static::$uri)) if (is_null(static::$uri))
{ {
throw new \Exception("Could not detect request URI."); throw new \Exception("Could not detect request URI.");
...@@ -81,16 +94,16 @@ class URI { ...@@ -81,16 +94,16 @@ class URI {
*/ */
protected static function format($uri) protected static function format($uri)
{ {
// First we want to remove the application's base URL from the URI // First we want to remove the application's base URL from the URI if it is
// if it is in the string. It is possible for some of the server // in the string. It is possible for some of the parsed server variables to
// variables to include the entire document root. // include the entire document root in the string.
$uri = static::remove_base($uri); $uri = static::remove_base($uri);
$index = '/'.Config::get('application.index'); $index = '/'.Config::get('application.index');
// Next we'll remove the index file from the URI if it is there // Next we'll remove the index file from the URI if it is there and then
// and then finally trim down the URI. If the URI is left with // finally trim down the URI. If the URI is left with spaces, we'll use
// nothing but spaces, we use a single slash for root. // a single slash for the root URI.
if ($index !== '/') if ($index !== '/')
{ {
$uri = static::remove($uri, $index); $uri = static::remove($uri, $index);
...@@ -99,6 +112,29 @@ class URI { ...@@ -99,6 +112,29 @@ class URI {
return trim($uri, '/') ?: '/'; return trim($uri, '/') ?: '/';
} }
/**
* Determine if the current URI matches a given pattern.
*
* @param string $pattern
* @return bool
*/
public static function is($pattern)
{
// Asterisks are translated into zero-or-more regular expression wildcards
// to make it convenient to check if the URI starts with a given pattern
// such as "library/*". This is only done when not root.
if ($pattern !== '/')
{
$pattern = str_replace('*', '(.*)', $pattern).'\z';
}
else
{
$pattern = '^/$';
}
return preg_match('#'.$pattern.'#', static::current());
}
/** /**
* Parse the PATH_INFO server variable. * Parse the PATH_INFO server variable.
* *
...@@ -201,4 +237,14 @@ class URI { ...@@ -201,4 +237,14 @@ class URI {
return (strpos($uri, $value) === 0) ? substr($uri, strlen($value)) : $uri; return (strpos($uri, $value) === 0) ? substr($uri, strlen($value)) : $uri;
} }
/**
* Get the query string for the current request.
*
* @return string
*/
protected static function query()
{
return (count((array) $_GET) > 0) ? '?'.http_build_query($_GET) : '';
}
} }
\ No newline at end of file
<?php namespace Laravel; use Laravel\Routing\Route, Laravel\Routing\Router; <?php namespace Laravel; use Laravel\Routing\Router, Laravel\Routing\Route;
class URL { class URL {
...@@ -9,6 +9,16 @@ class URL { ...@@ -9,6 +9,16 @@ class URL {
*/ */
public static $base; public static $base;
/**
* Get the full URI including the query string.
*
* @return string
*/
public static function full()
{
return static::to(URI::full());
}
/** /**
* Get the full URL for the current request. * Get the full URL for the current request.
* *
...@@ -30,9 +40,9 @@ class URL { ...@@ -30,9 +40,9 @@ class URL {
$base = 'http://localhost'; $base = 'http://localhost';
// If the application URL configuration is set, we will just use // If the application URL configuration is set, we will just use that
// that instead of trying to guess the URL based on the $_SERVER // instead of trying to guess the URL from the $_SERVER array's host
// array's host and script name. // and script variables as this is more reliable.
if (($url = Config::get('application.url')) !== '') if (($url = Config::get('application.url')) !== '')
{ {
$base = $url; $base = $url;
...@@ -43,10 +53,14 @@ class URL { ...@@ -43,10 +53,14 @@ class URL {
// Basically, by removing the basename, we are removing everything after the // Basically, by removing the basename, we are removing everything after the
// and including the front controller from the request URI. Leaving us with // and including the front controller from the request URI. Leaving us with
// the path in which the framework is installed. From that path, we can // the path in which the framework is installed.
// construct the base URL to the application. $script = $_SERVER['SCRIPT_NAME'];
$path = str_replace(basename($_SERVER['SCRIPT_NAME']), '', $_SERVER['SCRIPT_NAME']);
$path = str_replace(basename($script), '', $script);
// Now that we have the base URL, all we need to do is attach the protocol
// and the HTTP_HOST to build the full URL for the application. We also
// trim off trailing slashes to clean the URL.
$base = rtrim($protocol.$_SERVER['HTTP_HOST'].$path, '/'); $base = rtrim($protocol.$_SERVER['HTTP_HOST'].$path, '/');
} }
...@@ -109,41 +123,26 @@ class URL { ...@@ -109,41 +123,26 @@ class URL {
* *
* @param string $action * @param string $action
* @param array $parameters * @param array $parameters
* @param string $method
* @return string * @return string
*/ */
public static function to_action($action, $parameters = array(), $method = 'GET') public static function to_action($action, $parameters = array())
{ {
// This allows us to use true reverse routing to controllers, since // This allows us to use true reverse routing to controllers, since
// URIs may be setup to handle the action that do not follow the // URIs may be setup to handle the action that do not follow the
// typical Laravel controller URI convention. // typical Laravel controller URI conventions.
$route = Router::uses($action, $method); $route = Router::uses($action);
if ( ! is_null($route)) if ( ! is_null($route))
{ {
$uri = static::explicit($route, $action, $parameters); return static::explicit($route, $action, $parameters);
} }
// If no route was found that handled the given action, we'll just // If no route was found that handled the given action, we'll just
// generate the URL using the typical controller routing setup // generate the URL using the typical controller routing setup
// for URIs and turn SSL to false. // for URIs and turn SSL to false.
else else
{ {
$uri = static::convention($action, $parameters); return static::convention($action, $parameters);
}
return static::to($uri, $https);
} }
/**
* Generate a HTTPS URL to a controller action.
*
* @param string $action
* @param array $parameters
* @return string
*/
public static function to_post_action($action, $parameters = array())
{
return static::to_action($action, $parameters, 'POST');
} }
/** /**
...@@ -158,7 +157,7 @@ class URL { ...@@ -158,7 +157,7 @@ class URL {
{ {
$https = array_get(current($route), 'https', false); $https = array_get(current($route), 'https', false);
return Route::transpose(Route::destination(key($route)), $parameters); return static::to(static::transpose(key($route), $parameters), $https);
} }
/** /**
...@@ -181,12 +180,16 @@ class URL { ...@@ -181,12 +180,16 @@ class URL {
$https = false; $https = false;
$parameters = implode('/', $parameters);
// We'll replace both dots and @ signs in the URI since both are used // We'll replace both dots and @ signs in the URI since both are used
// to specify the controller and action, and by convention should be // to specify the controller and action, and by convention should be
// translated into URI slashes. // translated into URI slashes.
$uri = $root.str_replace(array('.', '@'), '/', $action); $uri = $root.'/'.str_replace(array('.', '@'), '/', $action);
$uri = static::to(str_finish($uri, '/').$parameters);
return str_finish($uri, '/').implode('/', $parameters); return trim($uri, '/');
} }
/** /**
...@@ -204,7 +207,7 @@ class URL { ...@@ -204,7 +207,7 @@ class URL {
// Since assets are not served by Laravel, we do not need to come through // Since assets are not served by Laravel, we do not need to come through
// the front controller. So, we'll remove the application index specified // the front controller. So, we'll remove the application index specified
// in the application configuration from the URL. // in the application config from the generated URL.
if (($index = Config::get('application.index')) !== '') if (($index = Config::get('application.index')) !== '')
{ {
$url = str_replace($index.'/', '', $url); $url = str_replace($index.'/', '', $url);
...@@ -236,14 +239,40 @@ class URL { ...@@ -236,14 +239,40 @@ class URL {
throw new \Exception("Error creating URL for undefined route [$name]."); throw new \Exception("Error creating URL for undefined route [$name].");
} }
$uri = Route::destination(key($route));
// To determine whether the URL should be HTTPS or not, we look for the "https" // To determine whether the URL should be HTTPS or not, we look for the "https"
// value on the route action array. The route has control over whether the // value on the route action array. The route has control over whether the URL
// URL should be generated with an HTTPS protocol. // should be generated with an HTTPS protocol string or just HTTP.
$https = array_get(current($route), 'https', false); $https = array_get(current($route), 'https', false);
return static::to(Route::transpose($uri, $parameters), $https); return static::to(static::transpose(key($route), $parameters), $https);
}
/**
* Substitute the parameters in a given URI.
*
* @param string $uri
* @param array $parameters
* @return string
*/
public static function transpose($uri, $parameters)
{
// Spin through each route parameter and replace the route wildcard segment
// with the corresponding parameter passed to the method. Afterwards, we'll
// replace all of the remaining optional URI segments.
foreach ((array) $parameters as $parameter)
{
if ( ! is_null($parameter))
{
$uri = preg_replace('/\(.+?\)/', $parameter, $uri, 1);
}
}
// If there are any remaining optional place-holders, we'll just replace
// them with empty strings since not every optional parameter has to be
// in the array of parameters that were passed.
$uri = str_replace(array_keys(Router::$optional), '', $uri);
return trim($uri, '/');
} }
} }
\ No newline at end of file
...@@ -853,11 +853,11 @@ class Validator { ...@@ -853,11 +853,11 @@ class Validator {
// More reader friendly versions of the attribute names may be stored // More reader friendly versions of the attribute names may be stored
// in the validation language file, allowing a more readable version // in the validation language file, allowing a more readable version
// of the attribute name to be used in the validation message. // of the attribute name to be used in the message.
// //
// If no language line has been specified for the attribute, all of // If no language line has been specified for the attribute, all of
// the underscores will be removed from the attribute name and that // the underscores will be removed from the attribute name and that
// will be used as the attribtue name in the message. // will be used as the attribtue name.
$line = "{$bundle}validation.attributes.{$attribute}"; $line = "{$bundle}validation.attributes.{$attribute}";
$display = Lang::line($line)->get($this->language); $display = Lang::line($line)->get($this->language);
......
...@@ -67,7 +67,7 @@ class View implements ArrayAccess { ...@@ -67,7 +67,7 @@ class View implements ArrayAccess {
// //
// This makes error display in the view extremely convenient, since the // This makes error display in the view extremely convenient, since the
// developer can always assume they have a message container instance // developer can always assume they have a message container instance
// available to them in the view. // available to them in the view's variables.
if ( ! isset($this->data['errors'])) if ( ! isset($this->data['errors']))
{ {
if (Session::started() and Session::has('errors')) if (Session::started() and Session::has('errors'))
......
laravel.bundle.manifest *
\ No newline at end of file !.gitignore
\ No newline at end of file
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