diff --git a/application/config/error.php b/application/config/error.php index daf084d5..1713afce 100644 --- a/application/config/error.php +++ b/application/config/error.php @@ -66,21 +66,4 @@ return array( Log::exception($exception); }, - /* - |-------------------------------------------------------------------------- - | PHP INI Display Errors Setting - |-------------------------------------------------------------------------- - | - | Here you may specify the display_errors setting of the PHP.ini file. - | Typically you may keep this "Off", as Laravel will cleanly handle - | the display of all errors. - | - | However, if you encounter an infamous white screen of death scenario, - | turning this "On" may help you solve the problem by getting the - | real error message being thrown by the application. - | - */ - - 'display' => 'Off', - ); \ No newline at end of file diff --git a/application/start.php b/application/start.php index a4725849..03854cd2 100644 --- a/application/start.php +++ b/application/start.php @@ -1,5 +1,118 @@ $method(array_slice($arguments, 1)); } @@ -114,7 +114,7 @@ class Command { // First we'll check to see if the task has been registered in the // application IoC container. This allows all dependencies to be - // injected into tasks for more testability. + // injected into tasks for more flexible testability. if (IoC::registered("task: {$identifier}")) { return IoC::resolve("task: {$identifier}"); diff --git a/laravel/config.php b/laravel/config.php index 54cc87d8..76ccb154 100644 --- a/laravel/config.php +++ b/laravel/config.php @@ -20,6 +20,13 @@ class Config { */ public static $cache = array(); + /** + * The configuration loader event name. + * + * @var string + */ + const loader = 'laravel.config.loader'; + /** * Determine if a configuration item or file exists. * @@ -106,8 +113,7 @@ class Config { // If the item is null, it means the developer wishes to set the entire // configuration array to a given value, so we will pass the entire - // array for the bundle into the array_set method, otherwise we'll - // only pass the file array for the bundle. + // array for the bundle into the array_set method. if (is_null($item)) { array_set(static::$items[$bundle], $file, $value); @@ -166,6 +172,31 @@ class Config { { if (isset(static::$items[$bundle][$file])) return true; + // We allow a "config.loader" event to be registered which is responsible for + // returning an array representing the configuration for the bundle and file + // requested. This allows many types of config "drivers". + $config = Event::first(static::loader, func_get_args()); + + // If configuration items were actually found for the bundle and file we + // will add them to the configuration array and return true, otherwise + // we will return false indicating the file was not found. + if (count($config) > 0) + { + static::$items[$bundle][$file] = $config; + } + + return isset(static::$items[$bundle][$file]); + } + + /** + * Load the configuration items from a configuration file. + * + * @param string $bundle + * @param string $file + * @return array + */ + public static function file($bundle, $file) + { $config = array(); // Configuration files cascade. Typically, the bundle configuration array is @@ -179,12 +210,7 @@ class Config { } } - if (count($config) > 0) - { - static::$items[$bundle][$file] = $config; - } - - return isset(static::$items[$bundle][$file]); + return $config; } /** diff --git a/laravel/core.php b/laravel/core.php index a03d2cc6..d2b8128e 100644 --- a/laravel/core.php +++ b/laravel/core.php @@ -1,46 +1,78 @@ path('sys'))); -/** - * 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. - */ +/* +|-------------------------------------------------------------------------- +| Set The CLI Options Array +|-------------------------------------------------------------------------- +| +| If the current request is from the Artisan command-line interface, we +| will parse the command line arguments and options and set them the +| array of options in the $_SERVER global array for convenience. +| +*/ + if (defined('STDIN')) { $console = CLI\Command::options($_SERVER['argv']); @@ -52,39 +84,34 @@ if (defined('STDIN')) $_SERVER['CLI'] = $options; } -/** - * 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. - */ +/* +|-------------------------------------------------------------------------- +| Set The CLI Laravel Environment +|-------------------------------------------------------------------------- +| +| Next we'll set the LARAVEL_ENV variable if the current request is from +| the Artisan command-line interface. Since the environment is often +| specified within an Apache .htaccess file, we need to set it here +| when the request is not coming through Apache. +| +*/ + if (isset($_SERVER['CLI']['ENV'])) { $_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 Laravel Bundles +|-------------------------------------------------------------------------- +| +| Finally we will register all of the bundles that have been defined for +| the application. None of them will be started, yet but will be setup +| so that they may be started by the develop at any time. +| +*/ -/** - * Register the default timezone for the application. This will - * be the default timezone used by all date functions through - * throughout the entire application. - */ -$timezone = Config::get('application.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) diff --git a/laravel/database/schema/grammars/grammar.php b/laravel/database/schema/grammars/grammar.php index 8212e219..512db2be 100644 --- a/laravel/database/schema/grammars/grammar.php +++ b/laravel/database/schema/grammars/grammar.php @@ -6,14 +6,60 @@ use Laravel\Database\Schema\Table; abstract class Grammar extends \Laravel\Database\Grammar { /** - * Get the appropriate data type definition for the column. + * Generate the SQL statement for creating a foreign key. * + * @param Table $table + * @param Command $command + * @return string + */ + public function foreign(Table $table, Fluent $command) + { + $name = $command->name; + + // We need to wrap both of the table names in quoted identifiers to protect + // against any possible keyword collisions, both the table on which the + // command is being executed and the referenced table are wrapped. + $table = $this->wrap($table); + + $on = $this->wrap($command->on); + + // Next we need to columnize both the command table's columns as well as + // the columns referenced by the foreign key. We'll cast the referenced + // columns to an array since they aren't by the fluent command. + $foreign = $this->columnize($command->columns); + + $referenced = $this->columnize((array) $command->references); + + $sql = "ALTER TABLE $table ADD CONSTRAINT $name "; + + return $sql .= "FOREIGN KEY ($foreign) REFERENCES $on ($referenced)"; + } + + /** + * Drop a constraint from the table. + * + * @param Table $table + * @param Fluent $fluent + * @return string + */ + protected function drop_constraint(Table $table, Fluent $command) + { + return "ALTER TABLE ".$this->wrap($table)." DROP CONSTRAINT ".$command->name; + } + + /** + * Get the SQL syntax for indicating if a column is unsigned. + * + * @param Table $table * @param Fluent $column * @return string */ - protected function type(Fluent $column) + protected function unsigned(Table $table, Fluent $column) { - return $this->{'type_'.$column->type}($column); + if ($column->type == 'integer' && $column->unsigned) + { + return ' UNSIGNED'; + } } /** @@ -40,18 +86,14 @@ abstract class Grammar extends \Laravel\Database\Grammar { } /** - * Get the SQL syntax for indicating if a column is unsigned. + * Get the appropriate data type definition for the column. * - * @param Table $table * @param Fluent $column * @return string */ - protected function unsigned(Table $table, Fluent $column) + protected function type(Fluent $column) { - if ($column->type == 'integer' && $column->unsigned) - { - return ' UNSIGNED'; - } + return $this->{'type_'.$column->type}($column); } } \ No newline at end of file diff --git a/laravel/database/schema/grammars/mysql.php b/laravel/database/schema/grammars/mysql.php index 595c1dd8..5d783cee 100644 --- a/laravel/database/schema/grammars/mysql.php +++ b/laravel/database/schema/grammars/mysql.php @@ -292,6 +292,18 @@ class MySQL extends Grammar { return 'ALTER TABLE '.$this->wrap($table)." DROP INDEX {$command->name}"; } + /** + * Drop a foreign key constraint from the table. + * + * @param Table $table + * @param Fluent $fluent + * @return string + */ + public function drop_foreign(Table $table, Fluent $command) + { + return "ALTER TABLE ".$this->wrap($table)." DROP FOREIGN KEY ".$command->name; + } + /** * Generate the data-type definition for a string. * diff --git a/laravel/database/schema/grammars/postgres.php b/laravel/database/schema/grammars/postgres.php index b974efc9..9a32edff 100644 --- a/laravel/database/schema/grammars/postgres.php +++ b/laravel/database/schema/grammars/postgres.php @@ -146,7 +146,11 @@ class Postgres extends Grammar { */ public function unique(Table $table, Fluent $command) { - return $this->key($table, $command, true); + $table = $this->wrap($table); + + $columns = $this->columnize($command->columns); + + return "ALTER TABLE $table ADD CONSTRAINT ".$command->name." UNIQUE ($columns)"; } /** @@ -250,7 +254,7 @@ class Postgres extends Grammar { */ public function drop_unique(Table $table, Fluent $command) { - return $this->drop_key($table, $command); + return $this->drop_constraint($table, $command); } /** @@ -289,6 +293,18 @@ class Postgres extends Grammar { return 'DROP INDEX '.$command->name; } + /** + * Drop a foreign key constraint from the table. + * + * @param Table $table + * @param Fluent $fluent + * @return string + */ + public function drop_foreign(Table $table, Fluent $command) + { + return $this->drop_constraint($table, $command); + } + /** * Generate the data-type definition for a string. * diff --git a/laravel/database/schema/grammars/sqlserver.php b/laravel/database/schema/grammars/sqlserver.php index 6cf1bffb..3972edd5 100644 --- a/laravel/database/schema/grammars/sqlserver.php +++ b/laravel/database/schema/grammars/sqlserver.php @@ -311,6 +311,18 @@ class SQLServer extends Grammar { return "DROP INDEX {$command->name} ON ".$this->wrap($table); } + /** + * Drop a foreign key constraint from the table. + * + * @param Table $table + * @param Fluent $fluent + * @return string + */ + public function drop_foreign(Table $table, Fluent $command) + { + return $this->drop_constraint($table, $command); + } + /** * Generate the data-type definition for a string. * diff --git a/laravel/database/schema/table.php b/laravel/database/schema/table.php index 4af83422..b40a2819 100644 --- a/laravel/database/schema/table.php +++ b/laravel/database/schema/table.php @@ -108,6 +108,17 @@ class Table { return $this->key(__FUNCTION__, $columns, $name); } + /** + * Add a foreign key constraint to the table. + * + * @param string|array $columns + * @param string $name + */ + public function foreign($columns, $name = null) + { + return $this->key(__FUNCTION__, $columns, $name); + } + /** * Create a command for creating any index. * @@ -196,6 +207,17 @@ class Table { return $this->drop_key(__FUNCTION__, $name); } + /** + * Drop a foreign key constraint from the table. + * + * @param string $name + * @return void + */ + public function drop_foreign($name) + { + return $this->drop_key(__FUNCTION__, $name); + } + /** * Create a command to drop any type of index. * @@ -205,7 +227,7 @@ class Table { */ protected function drop_key($type, $name) { - return $this->command($type, array('name' => $name)); + return $this->command($type, compact('name')); } /** diff --git a/laravel/error.php b/laravel/error.php index 23574efa..bf668d8a 100644 --- a/laravel/error.php +++ b/laravel/error.php @@ -12,7 +12,7 @@ class Error { { static::log($exception); - ob_end_clean(); + ob_get_level() and ob_end_clean(); // If detailed errors are enabled, we'll just format the exception into // a simple error message and display it on the screen. We don't use a diff --git a/laravel/event.php b/laravel/event.php index 418b82ed..02fc649c 100644 --- a/laravel/event.php +++ b/laravel/event.php @@ -40,6 +40,31 @@ class Event { static::$events[$event][] = $callback; } + /** + * Override all callbacks for a given event with a new callback. + * + * @param string $event + * @param mixed $callback + * @return void + */ + public static function override($event, $callback) + { + static::clear($event); + + static::listen($event, $callback); + } + + /** + * Clear all event listeners for a given event. + * + * @param string $event + * @return void + */ + public static function clear($event) + { + static::$events[$event] = array(); + } + /** * Fire an event and return the first response. * diff --git a/laravel/lang.php b/laravel/lang.php index 7840dab0..87be5f3d 100644 --- a/laravel/lang.php +++ b/laravel/lang.php @@ -32,6 +32,13 @@ class Lang { */ protected static $lines = array(); + /** + * The language loader event name. + * + * @var string + */ + const loader = 'laravel.language.loader'; + /** * Create a new Lang instance. * @@ -179,6 +186,26 @@ class Lang { return true; } + // We use a "loader" event to delegate the loading of the language + // array, which allows the develop to organize the language line + // arrays for their application however they wish. + $lines = Event::first(static::loader, func_get_args()); + + static::$lines[$bundle][$language][$file] = $lines; + + return count($lines) > 0; + } + + /** + * Load a language array from a language file. + * + * @param string $bundle + * @param string $language + * @param string $file + * @return array + */ + public static function file($bundle, $language, $file) + { $lines = array(); // Language files can belongs to the application or to any bundle @@ -191,12 +218,7 @@ class Lang { $lines = require $path; } - // All of the language lines are cached in an array so we can - // quickly look them up on subsequent reqwuests for the line. - // This keeps us from loading files each time. - static::$lines[$bundle][$language][$file] = $lines; - - return count($lines) > 0; + return $lines; } /** diff --git a/laravel/laravel.php b/laravel/laravel.php index 43ea0658..97e4e750 100644 --- a/laravel/laravel.php +++ b/laravel/laravel.php @@ -1,63 +1,70 @@ $config) { 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. - */ +/* +|-------------------------------------------------------------------------- +| Register The Catch-All Route +|-------------------------------------------------------------------------- +| +| This route will catch all requests that do not hit another route in +| the application, and will raise the 404 error event so the error +| can be handled by the developer in their 404 event listener. +| +*/ + Routing\Router::register('*', '(:all)', function() { return Event::first('404'); }); -/** - * If the requset URI has too many segments, we will bomb out of - * the request. This is too avoid potential DDoS attacks against - * the framework by overloading the controller lookup method - * with thousands of segments. - */ +/* +|-------------------------------------------------------------------------- +| Route The Incoming Request +|-------------------------------------------------------------------------- +| +| Phew! We can finally route the request to the appropriate route and +| execute the route to get the response. This will give an instance +| of the Response object that we can send back to the browser +| +*/ + $uri = URI::current(); -if (count(URI::$segments) > 15) -{ - throw new \Exception("Invalid request. Too many URI segments."); -} - -/** - * Route the request to the proper route in the application. If a - * route is found, the route will be called via the request class - * static property. If no route is found, the 404 response will - * be returned to the browser. - */ Request::$route = Routing\Router::route(Request::method(), $uri); $response = Request::$route->call(); -/** - * Close the session and write the active payload to persistent - * storage. The session cookie will also be written and if the - * driver is a sweeper, session garbage collection might be - * performed depending on the "sweepage" probability. - */ +/* +|-------------------------------------------------------------------------- +| Persist The Session To Storage +|-------------------------------------------------------------------------- +| +| If a session driver has been configured, we will save the session to +| storage so it is avaiable for the next request. This will also set +| the session cookie in the cookie jar to be sent to the user. +| +*/ + if (Config::get('session.driver') !== '') { Session::save(); } -/** - * Send all of the cookies to the browser. The cookies are - * stored in a "jar" until the end of a request, primarily - * to make testing the cookie functionality of the site - * much easier since the jar can be inspected. - */ +/* +|-------------------------------------------------------------------------- +| Let's Eat Cookies +|-------------------------------------------------------------------------- +| +| All cookies set during the request are actually stored in a cookie jar +| until the end of the request so they can be expected by unit tests or +| the developer. Here, we'll push them out to the browser. +| +*/ + Cookie::send(); -/** - * Send the final response to the browser and fire the - * final event indicating that the processing for the - * current request is completed. - */ +/* +|-------------------------------------------------------------------------- +| Send The Response To The Browser +|-------------------------------------------------------------------------- +| +| We'll send the response back to the browser here. This method will also +| send all of the response headers to the browser as well as the string +| content of the Response. This should make the view available to the +| browser and show something pretty to the user. +| +*/ + $response->send(); +/* +|-------------------------------------------------------------------------- +| And We're Done! +|-------------------------------------------------------------------------- +| +| Raise the "done" event so extra output can be attached to the response +| This allows the adding of debug toolbars, etc. to the view, or may be +| used to do some kind of logging by the application. +| +*/ + Event::fire('laravel.done'); \ No newline at end of file diff --git a/laravel/request.php b/laravel/request.php index 21ed5cd9..73c82ffc 100644 --- a/laravel/request.php +++ b/laravel/request.php @@ -141,6 +141,27 @@ class Request { return defined('STDIN'); } + /** + * Get the Laravel environment for the current request. + * + * @return string|null + */ + public static function env() + { + if (isset($_SERVER['LARAVEL_ENV'])) return $_SERVER['LARAVEL_ENV']; + } + + /** + * Determine the current request environment. + * + * @param string $env + * @return bool + */ + public static function is_env($env) + { + return static::env() === $env; + } + /** * Get the main route handling the request. * diff --git a/laravel/session.php b/laravel/session.php index ee997776..2e3044db 100644 --- a/laravel/session.php +++ b/laravel/session.php @@ -17,7 +17,19 @@ class Session { const csrf_token = 'csrf_token'; /** - * Create the session payload instance and load the session for the request. + * Create the session payload and the load the session. + * + * @return void + */ + public static function load() + { + static::start(Config::get('session.driver')); + + static::$instance->load(Cookie::get(Config::get('session.cookie'))); + } + + /** + * Create the session payload instance for the request. * * @param string $driver * @return void diff --git a/laravel/view.php b/laravel/view.php index 205129d9..4eddb64a 100644 --- a/laravel/view.php +++ b/laravel/view.php @@ -51,6 +51,13 @@ class View implements ArrayAccess { */ public static $paths = array(DEFAULT_BUNDLE => array('')); + /** + * The Laravel view loader event name. + * + * @var string + */ + const loader = 'laravel.view.loader'; + /** * The Laravel view engine event name. * @@ -106,28 +113,40 @@ class View implements ArrayAccess { */ protected function path($view) { + list($bundle, $view) = Bundle::parse($view); + $view = str_replace('.', '/', $view); - $root = Bundle::path(Bundle::name($view)).'views/'; + // We delegate the determination of view paths to the view loader + // event so that the developer is free to override and manage + // the loading views in any way they see fit. + $path = Event::first(static::loader, array($bundle, $view)); - // We need to make sure that the view exists. If it doesn't, we will - // throw an exception since there is not any point in going further. - // If it does, we can just return the full view path. - $paths = array_get(static::$paths, Bundle::name($view), array('')); - - foreach ($paths as $path) + if ( ! is_null($path)) { - foreach (static::$extensions as $ext) - { - $file = $root.$path.Bundle::element($view).$ext; - - if (file_exists($file)) return $file; - } + return $path; } throw new \Exception("View [$view] doesn't exist."); } + /** + * Get the path to a view using the default folder convention. + * + * @param string $bundle + * @param string $view + * @return string + */ + public static function file($bundle, $view) + { + $root = Bundle::path($bundle).'views/'; + + if (file_exists($path = $root.$view.EXT)) + { + return $path; + } + } + /** * Create a new view instance. *