Commit b5442c67 authored by Taylor Otwell's avatar Taylor Otwell

merged skunkworks into develop.

parent 610d8827
<?php
/*
|--------------------------------------------------------------------------
| Auto-Loader PSR-0 Directories
|--------------------------------------------------------------------------
|
| The Laravel auto-loader can search directories for files using the PSR-0
| naming convention. This convention basically organizes classes by using
| the class namespace to indicate the directory structure.
|
| So you don't have to manually map all of your models, we've added the
| models and libraries directories for you. So, you can model away and
| the auto-loader will take care of the rest.
|
*/
Autoloader::psr(array(
APP_PATH.'models',
APP_PATH.'libraries',
));
/*
|--------------------------------------------------------------------------
| Auto-Loader Mappings
|--------------------------------------------------------------------------
|
| Laravel uses a simple array of class to path mappings to drive the class
| auto-loader. This simple approach helps avoid the performance problems
| of searching through directories by some kind of convention. It also
| gives you the freedom to organize your application how you want.
|
| Registering a mapping couldn't be easier. Just pass an array of class
| to path maps into the "map" function of Autoloader. Then, when you
| want to use that class, just use it. It's a piece of cake.
|
*/
Autoloader::map(array(
//'User' => APP_PATH.'models/user.php',
//'Role' => APP_PATH.'models/role.php',
));
\ No newline at end of file
<?php
return array(
/*
|--------------------------------------------------------------------------
| View Names & Composers
|--------------------------------------------------------------------------
|
| Named views give you beautiful syntax when working with your views.
|
| Here's how to define a named view:
|
| 'home.index' => array('name' => 'home')
|
| Now, you can create an instance of that view using the very expressive
| View::of dynamic method. Take a look at this example:
|
| return View::of_home();
|
| View composers provide a convenient way to add common elements to a view
| each time it is created. For example, you may wish to bind a header and
| footer partial each time the view is created.
|
| The composer will receive an instance of the view being created, and is
| free to modify the view however you wish. Here is how to define one:
|
| 'home.index' => function($view)
| {
| //
| }
|
| Of course, you may define a view name and a composer for a single view:
|
| 'home.index' => array('name' => 'home', function($view)
| {
| //
| })
|
*/
'home.index' => array('name' => 'home', function($view)
{
// This composer is called for the "home.index" view.
}),
);
\ No newline at end of file
...@@ -7,7 +7,9 @@ return array( ...@@ -7,7 +7,9 @@ return array(
| Application URL | Application URL
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| |
| The URL used to access your application. No trailing slash. | The URL used to access your application without a trailing slash. The URL
| does nto have to be set. If it isn't we'll try our best to guess the URL
| of your application.
| |
*/ */
...@@ -20,8 +22,8 @@ return array( ...@@ -20,8 +22,8 @@ return array(
| |
| If you are including the "index.php" in your URLs, you can ignore this. | If you are including the "index.php" in your URLs, you can ignore this.
| |
| However, if you are using mod_rewrite or something similar to get | However, if you are using mod_rewrite to get cleaner URLs, just set
| cleaner URLs, set this option to an empty string. | this option to an empty string and we'll take care of the rest.
| |
*/ */
...@@ -32,11 +34,10 @@ return array( ...@@ -32,11 +34,10 @@ return array(
| Application Key | Application Key
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| |
| The application key should be a random, 32 character string.
|
| This key is used by the encryption and cookie classes to generate secure | This key is used by the encryption and cookie classes to generate secure
| encrypted strings and hashes. It is extremely important that this key | encrypted strings and hashes. It is extremely important that this key
| remain secret and should not be shared with anyone. | remain secret and should not be shared with anyone. Make it about 32
| characters of random gibberish.
| |
*/ */
...@@ -48,7 +49,8 @@ return array( ...@@ -48,7 +49,8 @@ return array(
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| |
| The default character encoding used by your application. This encoding | The default character encoding used by your application. This encoding
| will be used by the Str, Text, and Form classes. | will be used by the Str, Text, Form, and any other classes that need
| to know what type of encoding to use for your awesome application.
| |
*/ */
...@@ -89,12 +91,28 @@ return array( ...@@ -89,12 +91,28 @@ return array(
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| |
| The default timezone of your application. This timezone will be used when | The default timezone of your application. This timezone will be used when
| Laravel needs a date, such as when writing to a log file. | Laravel needs a date, such as when writing to a log file or travelling
| to a distant star at warp speed.
| |
*/ */
'timezone' => 'UTC', 'timezone' => 'UTC',
/*
|--------------------------------------------------------------------------
| Autoloaded Bundles
|--------------------------------------------------------------------------
|
| Bundles can provide a ton of awesome drop-in functionality for your web
| application. Everything from Twitter integration to an admin backend.
|
| Here you may specify the bundles that should be automatically started
| on every request to your application.
|
*/
'bundles' => array(),
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Class Aliases | Class Aliases
...@@ -112,26 +130,26 @@ return array( ...@@ -112,26 +130,26 @@ return array(
*/ */
'aliases' => array( 'aliases' => array(
'Arr' => 'Laravel\\Arr',
'Asset' => 'Laravel\\Asset',
'Auth' => 'Laravel\\Auth', 'Auth' => 'Laravel\\Auth',
'Asset' => 'Laravel\\Asset',
'Autoloader' => 'Laravel\\Autoloader', 'Autoloader' => 'Laravel\\Autoloader',
'Benchmark' => 'Laravel\\Benchmark', 'Bundle' => 'Laravel\\Bundle',
'Cache' => 'Laravel\\Cache\\Manager', 'Cache' => 'Laravel\\Cache',
'Config' => 'Laravel\\Config', 'Config' => 'Laravel\\Config',
'Controller' => 'Laravel\\Routing\\Controller', 'Controller' => 'Laravel\\Routing\\Controller',
'Cookie' => 'Laravel\\Cookie', 'Cookie' => 'Laravel\\Cookie',
'Crypter' => 'Laravel\\Crypter', 'Crypter' => 'Laravel\\Crypter',
'DB' => 'Laravel\\Database\\Manager', 'DB' => 'Laravel\\Database',
'Eloquent' => 'Laravel\\Database\\Eloquent\\Model', 'Event' => 'Laravel\\Event',
'File' => 'Laravel\\File', 'File' => 'Laravel\\File',
'Filter' => 'Laravel\\Routing\\Filter',
'Form' => 'Laravel\\Form', 'Form' => 'Laravel\\Form',
'Hash' => 'Laravel\\Hash', 'Hash' => 'Laravel\\Hash',
'HTML' => 'Laravel\\HTML', 'HTML' => 'Laravel\\HTML',
'Inflector' => 'Laravel\\Inflector',
'Input' => 'Laravel\\Input', 'Input' => 'Laravel\\Input',
'IoC' => 'Laravel\\IoC', 'IoC' => 'Laravel\\IoC',
'Lang' => 'Laravel\\Lang', 'Lang' => 'Laravel\\Lang',
'Log' => 'Laravel\\Log',
'Memcached' => 'Laravel\\Memcached', 'Memcached' => 'Laravel\\Memcached',
'Paginator' => 'Laravel\\Paginator', 'Paginator' => 'Laravel\\Paginator',
'URL' => 'Laravel\\URL', 'URL' => 'Laravel\\URL',
...@@ -139,9 +157,13 @@ return array( ...@@ -139,9 +157,13 @@ return array(
'Redis' => 'Laravel\\Redis', 'Redis' => 'Laravel\\Redis',
'Request' => 'Laravel\\Request', 'Request' => 'Laravel\\Request',
'Response' => 'Laravel\\Response', 'Response' => 'Laravel\\Response',
'Router' => 'Laravel\\Routing\\Router',
'Schema' => 'Laravel\\Database\\Schema',
'Section' => 'Laravel\\Section', 'Section' => 'Laravel\\Section',
'Session' => 'Laravel\\Facades\\Session', 'Session' => 'Laravel\\Session',
'Str' => 'Laravel\\Str', 'Str' => 'Laravel\\Str',
'Task' => 'Laravel\\CLI\\Tasks\\Task',
'URI' => 'Laravel\\URI',
'Validator' => 'Laravel\\Validator', 'Validator' => 'Laravel\\Validator',
'View' => 'Laravel\\View', 'View' => 'Laravel\\View',
), ),
......
...@@ -2,45 +2,27 @@ ...@@ -2,45 +2,27 @@
return array( return array(
/*
|--------------------------------------------------------------------------
| Authentication Username
|--------------------------------------------------------------------------
|
} This option should be set to the "username" property of your users.
| Typically, this will be set to "email" or "username".
|
| The value of this property will be used by the "attempt" closure when
| searching for users by their username. It will also be used when the
| user is set to be "remembered", as the username is embedded into the
| encrypted cookie and is used to verify the user's identity.
|
*/
'username' => 'email',
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Retrieve The Current User | Retrieve The Current User
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| |
| This closure is called by the Auth::user() method when attempting to | This closure is called by the Auth class' "user" method when trying to
| retrieve a user by their ID stored in the session. | retrieve a user by the ID that is stored in their session. If you find
| the user, just return the user object, but make sure it has an "id"
| property. If you can't find the user, just return null.
| |
| Simply return an object representing the user with the given ID. Or, if | Of course, a simple and elegant authentication solution has already
| no user with the given ID is registered to use your application, you do | been provided for you using the query builder and hashing engine.
| not need to return anything. | We love making your life as easy as possible.
|
| Of course, a simple, elegant authentication solution is already provided
| for you using Eloquent and the default Laravel hashing engine.
| |
*/ */
'user' => function($id) 'user' => function($id)
{ {
if ( ! is_null($id) and filter_var($id, FILTER_VALIDATE_INT) !== false) if (filter_var($id, FILTER_VALIDATE_INT) !== false)
{ {
return User::find($id); return DB::table('users')->find($id);
} }
}, },
...@@ -50,19 +32,19 @@ return array( ...@@ -50,19 +32,19 @@ return array(
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| |
| This closure is called by the Auth::attempt() method when attempting to | This closure is called by the Auth::attempt() method when attempting to
| authenticate a user that is logging into your application. | authenticate a user that is logging into your application. It's like a
| super buff bouncer to your application.
| |
| If the provided credentials are correct, simply return an object that | If the provided credentials are correct, simply return an object that
| represents the user being authenticated. If the credentials are not | represents the user being authenticated. As long as it has a property
| valid, don't return anything. | for the "id", any object will work. If the credentials are not valid,
| | you don't meed to return anything.
| Note: If a user object is returned, it must have an "id" property.
| |
*/ */
'attempt' => function($username, $password, $config) 'attempt' => function($username, $password)
{ {
$user = User::where($config['username'], '=', $username)->first(); $user = DB::table('users')->where_username($username)->first();
if ( ! is_null($user) and Hash::check($password, $user->password)) if ( ! is_null($user) and Hash::check($password, $user->password))
{ {
...@@ -72,12 +54,12 @@ return array( ...@@ -72,12 +54,12 @@ return array(
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Logout | Logout The Current User
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| |
| Here you may do anything that needs to be done when a user logs out of | Here you may do anything that needs to be done when a user logs out of
| your application, such as call the logout method on a third-party API | your application, such as call the logout method on a third-party API
| you are using for authentication, or anything else you desire. | you are using for authentication or anything else you desire.
| |
*/ */
......
...@@ -7,12 +7,15 @@ return array( ...@@ -7,12 +7,15 @@ return array(
| Cache Driver | Cache Driver
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| |
| The name of the default cache driver for your application. | The name of the default cache driver for your application. Caching can
| be used to increase the performance of your application by storing any
| commonly accessed data in memory, a file, or some other storage.
| |
| Caching can be used to increase the performance of your application | A variety of awesome drivers are available for you to use with Laravel.
| by storing commonly accessed data in memory or in a file. | Some, like APC, are extremely fast. However, if that isn't an option
| in your environment, try file or database caching.
| |
| Supported Drivers: 'file', 'memcached', 'apc', 'redis'. | Drivers: 'file', 'memcached', 'apc', 'redis', 'database'.
| |
*/ */
...@@ -23,8 +26,10 @@ return array( ...@@ -23,8 +26,10 @@ return array(
| Cache Key | Cache Key
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| |
| This key will be prepended to item keys stored using Memcached and APC to | This key will be prepended to item keys stored using Memcached and APC
| prevent collisions with other applications on the server. | to prevent collisions with other applications on the server. Since the
| memory based stores could be shared by other applications, we need to
| be polite and use a prefix to uniquely identifier our items.
| |
*/ */
...@@ -32,16 +37,28 @@ return array( ...@@ -32,16 +37,28 @@ return array(
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Memcached Servers | Cache Database
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| |
| The Memcached servers used by your application. | When using the database cache driver, this database table will be used
| to store the cached item. You may also add a "connection" option to
| the array to specify which database connection should be used.
|
*/
'database' => array('table' => 'laravel_cache'),
/*
|--------------------------------------------------------------------------
| Memcached Servers
|--------------------------------------------------------------------------
| |
| Memcached is a free and open source, high-performance, distributed memory | The Memcached servers used by your application. Memcached is a free and
| object caching system, generic in nature, but intended for use in speeding | open source, high-performance, distributed memory caching system. It is
| up dynamic web applications by alleviating database load. | generic in nature but intended for use in speeding up web applications
| by alleviating database load.
| |
| For more information about Memcached, check out: http://memcached.org | For more information, check out: http://memcached.org
| |
*/ */
......
<?php
return array(
/*
|--------------------------------------------------------------------------
| Inversion of Control Container
|--------------------------------------------------------------------------
|
| Here you may define resolvers for the Laravel inversion of control (IoC)
| container. An IoC container provides the ability to create more flexible
| and testable applications, as well as a convenient method of managing
| the instantiation of complex objects.
|
| To register a resolver in the container, simple create add an item to
| the array for the object with a closure that returns an instance of
| the object.
|
| For example, here's how to register a resolver for a Mailer class:
|
| 'mailer' => function($c)
| {
| return new Mailer($sender, $key);
| }
|
| Note that the container instance itself is passed into the resolver,
| allowing you to continue to resolve dependencies within the resolver
| itself. This allows you to easily resolve nested dependencies.
|
| When creating controller instances, Laravel will check to see if a
| resolver has been registered for the controller. If it has, it will
| be used to create the controller instance. All controller resolvers
| should be registered beginning using a {controllers}.{name} naming
| convention. For example:
|
| 'controllers.user' => function($c)
| {
| return new User_Controller($c->resolve('repository'));
| }
|
| Of course, sometimes you may wish to register an object as a singleton
| Singletons are resolved by the controller the first time they are
| resolved; however, that same resolved instance will continue to be
| returned by the container each time it is requested. Registering an
| object as a singleton couldn't be simpler:
|
| 'mailer' => array('singleton' => true, 'resolver' => function($c)
| {
| return new Mailer($sender, $key);
| })
|
*/
);
\ No newline at end of file
...@@ -7,39 +7,29 @@ return array( ...@@ -7,39 +7,29 @@ return array(
| Default Database Connection | Default Database Connection
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| |
| The name of your default database connection. | The name of your default database connection. This connection will used
| | as the default for all database operations unless a different name is
| This connection will be the default for all database operations unless a | given when performing said operation. This connection name should be
| different connection is specified when performing the operation. | listed in the array of connections below.
| |
*/ */
'default' => 'sqlite', 'default' => 'mysql',
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Database Connections | Database Connections
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| |
| All of the database connections used by your application. | All of the database connections used by your application. Many of your
| | applications will no doubt only use one connection; however, you have
| Supported Drivers: 'mysql', 'pgsql', 'sqlite'. | the freedom to specify as many connections as you can handle.
|
| Note: When using the SQLite driver, the path and "sqlite" extention will
| be added automatically. You only need to specify the database name.
| |
| Using a driver that isn't supported? You can still establish a PDO | All database work in Laravel is done through the PHP's PDO facilities,
| connection. Simply specify a driver and DSN option: | so make sure you have the PDO drivers for your particlar database of
| choice installed on your machine.
| |
| 'odbc' => array( | Drivers: 'mysql', 'pgsql', 'sqlsrv', 'sqlite'.
| 'driver' => 'odbc',
| 'dsn' => 'your-dsn',
| 'username' => 'username',
| 'password' => 'password',
| )
|
| Note: When using an unsupported driver, Eloquent and the fluent query
| builder may not work as expected.
| |
*/ */
...@@ -68,6 +58,14 @@ return array( ...@@ -68,6 +58,14 @@ return array(
'charset' => 'utf8', 'charset' => 'utf8',
), ),
'sqlsrv' => array(
'driver' => 'sqlsrv',
'host' => 'localhost',
'database' => 'database',
'username' => 'root',
'password' => 'password',
),
), ),
/* /*
...@@ -77,11 +75,9 @@ return array( ...@@ -77,11 +75,9 @@ return array(
| |
| Redis is an open source, fast, and advanced key-value store. However, it | Redis is an open source, fast, and advanced key-value store. However, it
| provides a richer set of commands than a typical key-value store such as | provides a richer set of commands than a typical key-value store such as
| APC or memcached. | APC or memcached. All the cool kids are using it.
|
| Here you may specify the hosts and ports for your Redis databases.
| |
| For more information regarding Redis, check out: http://redis.io | To get the scoop on Redis, check out: http://redis.io
| |
*/ */
......
...@@ -7,7 +7,7 @@ return array( ...@@ -7,7 +7,7 @@ return array(
| Ignored Error Levels | Ignored Error Levels
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| |
| Here you may specify the error levels that should be ignored by the | Here you simply specify the error levels that should be ignored by the
| Laravel error handler. These levels will still be logged; however, no | Laravel error handler. These levels will still be logged; however, no
| information about about them will be displayed. | information about about them will be displayed.
| |
...@@ -22,10 +22,11 @@ return array( ...@@ -22,10 +22,11 @@ return array(
| |
| Detailed error messages contain information about the file in which an | Detailed error messages contain information about the file in which an
| error occurs, as well as a PHP stack trace containing the call stack. | error occurs, as well as a PHP stack trace containing the call stack.
| You'll want them when you're trying to debug your application.
| |
| If your application is in production, consider turning off error details | If your application is in production, you'll want to turn off the error
| for enhanced security and user experience. The error stack trace could | details for enhanced security and user experience since the exception
| contain sensitive information that should not be publicly visible. | stack trace could contain sensitive information.
| |
*/ */
...@@ -56,18 +57,13 @@ return array( ...@@ -56,18 +57,13 @@ return array(
| |
| You may log the error message however you like; however, a simple log | You may log the error message however you like; however, a simple log
| solution has been setup for you which will log all error messages to | solution has been setup for you which will log all error messages to
| a single text file within the application storage directory. | text files within the application storage directory.
|
| Of course, you are free to implement more complex solutions including
| emailing the exceptions details to your team, etc.
| |
*/ */
'logger' => function($exception) 'logger' => function($exception)
{ {
$message = (string) $exception; Log::exception($exception);
File::append(STORAGE_PATH.'log.txt', date('Y-m-d H:i:s').' - '.$message.PHP_EOL);
}, },
); );
\ No newline at end of file
...@@ -7,12 +7,12 @@ return array( ...@@ -7,12 +7,12 @@ return array(
| Session Driver | Session Driver
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| |
| The name of the session driver for your application. | The name of the session driver used by your application. Since HTTP is
| stateless, sessions are used to simulate "state" across requests made
| by the same user of your application. In other words, it's how an
| application knows who the heck you are.
| |
| Since HTTP is stateless, sessions are used to maintain "state" across | Drivers: 'cookie', 'file', 'database', 'memcached', 'apc', 'redis'.
| multiple requests from the same user of your application.
|
| Supported Drivers: 'cookie', 'file', 'database', 'memcached', 'apc', 'redis'.
| |
*/ */
...@@ -23,9 +23,9 @@ return array( ...@@ -23,9 +23,9 @@ return array(
| Session Database | Session Database
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| |
| The database table on which the session should be stored. | The database table on which the session should be stored. It probably
| | goes without saying that this option only matters if you are using
| This option is only relevant when using the "database" session driver. | the super slick database session driver.
| |
*/ */
...@@ -40,8 +40,9 @@ return array( ...@@ -40,8 +40,9 @@ return array(
| This option specifies the probability of session garbage collection | This option specifies the probability of session garbage collection
| occuring for any given request. | occuring for any given request.
| |
| For example, the default value states that garbage collection has about | For example, the default value states that garbage collection has a
| a 2% (2 / 100) chance of occuring for any given request. | 2% chance of occuring for any given request to the application.
| Feel free to tune this to your application's size and speed.
| |
*/ */
......
<?php
return array(
/*
|--------------------------------------------------------------------------
| String Inflection
|--------------------------------------------------------------------------
|
| This array contains the singular and plural forms of words. It's used by
| the "singular" and "plural" methods on the Str class to convert a given
| word from singular to plural and vice versa.
|
| This simple array is in constrast to the complicated regular expression
| patterns used by other frameworks. We think you'll enjoy the speed and
| simplicity of this solution.
|
| When adding a word to the array, the key should be the singular form,
| while the array value should be the plural form. We've included an
| example to get you started!
|
*/
'inflection' => array(
'user' => 'users',
'person' => 'people',
'comment' => 'comments',
),
/*
|--------------------------------------------------------------------------
| ASCII Characters
|--------------------------------------------------------------------------
|
| This array contains foreign characters and their 7-bit ASCII equivalents.
| The array is used by the "ascii" method on the Str class to get strings
| ready for inclusion in a URL slug.
|
| Of course, the "ascii" method may also be used by you for whatever your
| application requires. Feel free to add any characters we missed, and be
| sure to let us know about them!
|
*/
'ascii' => array(
'/æ|ǽ/' => 'ae',
'/œ/' => 'oe',
'/À|Á|Â|Ã|Ä|Å|Ǻ|Ā|Ă|Ą|Ǎ|А/' => 'A',
'/à|á|â|ã|ä|å|ǻ|ā|ă|ą|ǎ|ª|а/' => 'a',
'/Б/' => 'B',
'/б/' => 'b',
'/Ç|Ć|Ĉ|Ċ|Č|Ц/' => 'C',
'/ç|ć|ĉ|ċ|č|ц/' => 'c',
'/Ð|Ď|Đ|Д/' => 'Dj',
'/ð|ď|đ|д/' => 'dj',
'/È|É|Ê|Ë|Ē|Ĕ|Ė|Ę|Ě|Е|Ё|Э/' => 'E',
'/è|é|ê|ë|ē|ĕ|ė|ę|ě|е|ё|э/' => 'e',
'/Ф/' => 'F',
'/ƒ|ф/' => 'f',
'/Ĝ|Ğ|Ġ|Ģ|Г/' => 'G',
'/ĝ|ğ|ġ|ģ|г/' => 'g',
'/Ĥ|Ħ|Х/' => 'H',
'/ĥ|ħ|х/' => 'h',
'/Ì|Í|Î|Ï|Ĩ|Ī|Ĭ|Ǐ|Į|İ|И/' => 'I',
'/ì|í|î|ï|ĩ|ī|ĭ|ǐ|į|ı|и/' => 'i',
'/Ĵ|Й/' => 'J',
'/ĵ|й/' => 'j',
'/Ķ|К/' => 'K',
'/ķ|к/' => 'k',
'/Ĺ|Ļ|Ľ|Ŀ|Ł|Л/' => 'L',
'/ĺ|ļ|ľ|ŀ|ł|л/' => 'l',
'/М/' => 'M',
'/м/' => 'm',
'/Ñ|Ń|Ņ|Ň|Н/' => 'N',
'/ñ|ń|ņ|ň|ʼn|н/' => 'n',
'/Ö|Ò|Ó|Ô|Õ|Ō|Ŏ|Ǒ|Ő|Ơ|Ø|Ǿ|О/' => 'O',
'/ö|ò|ó|ô|õ|ō|ŏ|ǒ|ő|ơ|ø|ǿ|º|о/' => 'o',
'/П/' => 'P',
'/п/' => 'p',
'/Ŕ|Ŗ|Ř|Р/' => 'R',
'/ŕ|ŗ|ř|р/' => 'r',
'/Ś|Ŝ|Ş|Š|С/' => 'S',
'/ś|ŝ|ş|š|ſ|с/' => 's',
'/Ţ|Ť|Ŧ|Т/' => 'T',
'/ţ|ť|ŧ|т/' => 't',
'/Ù|Ú|Û|Ũ|Ū|Ŭ|Ů|Ű|Ų|Ư|Ǔ|Ǖ|Ǘ|Ǚ|Ǜ|У/' => 'U',
'/ù|ú|û|ũ|ū|ŭ|ů|ű|ų|ư|ǔ|ǖ|ǘ|ǚ|ǜ|у/' => 'u',
'/В/' => 'V',
'/в/' => 'v',
'/Ý|Ÿ|Ŷ|Ы/' => 'Y',
'/ý|ÿ|ŷ|ы/' => 'y',
'/Ŵ/' => 'W',
'/ŵ/' => 'w',
'/Ź|Ż|Ž|З/' => 'Z',
'/ź|ż|ž|з/' => 'z',
'/Æ|Ǽ/' => 'AE',
'/ß/'=> 'ss',
'/IJ/' => 'IJ',
'/ij/' => 'ij',
'/Œ/' => 'OE',
'/Ч/' => 'Ch',
'/ч/' => 'ch',
'/Ю/' => 'Ju',
'/ю/' => 'ju',
'/Я/' => 'Ja',
'/я/' => 'ja',
'/Ш/' => 'Sh',
'/ш/' => 'sh',
'/Щ/' => 'Shch',
'/щ/' => 'shch',
'/Ж/' => 'Zh',
'/ж/' => 'zh',
),
);
\ No newline at end of file
...@@ -7,10 +7,10 @@ class Home_Controller extends Controller { ...@@ -7,10 +7,10 @@ class Home_Controller extends Controller {
| The Default Controller | The Default Controller
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| |
| Instead of using RESTful routes and anonymous functions, you may wish to | Instead of using RESTful routes and anonymous functions, you might wish
| use controllers to organize your application API. You'll love them. | to use controllers to organize your application API. You'll love them.
| |
| To start using this controller, simply remove the default route from the | To start using this controller simply remove the default route from the
| application "routes.php" file. Laravel is smart enough to find this | application "routes.php" file. Laravel is smart enough to find this
| controller and call the default method, which is "action_index". | controller and call the default method, which is "action_index".
| |
......
<?php
return array(
/*
|--------------------------------------------------------------------------
| Filters
|--------------------------------------------------------------------------
|
| Filters provide a convenient method for attaching functionality to your
| routes. Filters can run either before or after a route is exectued.
|
| The built-in "before" and "after" filters are called before and after
| every request to your application; however, you may create other filters
| that can be attached to individual routes.
|
| Filters also make common tasks such as authentication and CSRF protection
| a breeze. If a filter that runs before a route returns a response, that
| response will override the route action.
|
| Let's walk through an example...
|
| First, define a filter:
|
| 'simple_filter' => function()
| {
| return 'Filtered!';
| }
|
| Next, attach the filter to a route:
|
| 'GET /' => array('before' => 'simple_filter', function()
| {
| return 'Hello World!';
| })
|
| Now every requests to http://example.com will return "Filtered!", since
| the filter is overriding the route action by returning a value.
|
| To make your life easier, we have built authentication and CSRF filters
| that are ready to attach to your routes. Enjoy.
|
*/
'before' => function()
{
// Do stuff before every request to your application.
},
'after' => function($response)
{
// Do stuff after every request to your application.
},
'auth' => function()
{
if (Auth::guest()) return Redirect::to_login();
},
'csrf' => function()
{
if (Request::forged()) return Response::error('500');
},
);
\ No newline at end of file
...@@ -8,8 +8,8 @@ return array( ...@@ -8,8 +8,8 @@ return array(
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| |
| The following language lines are used by the paginator library to build | The following language lines are used by the paginator library to build
| the pagination links. They may be easily changed by the developer to | the pagination links. You're free to change them to anything you want.
| anything they wish. | If you come up with something more exciting, let us know.
| |
*/ */
......
...@@ -8,11 +8,12 @@ return array( ...@@ -8,11 +8,12 @@ return array(
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| |
| The following language lines are used to swap attribute place-holders | The following language lines are used to swap attribute place-holders
| with something more reader friendly, such as "E-Mail Address" instead | with something more reader friendly such as "E-Mail Address" instead
| of "email". | of "email". Your users will thank you.
| |
| The Validator class will automatically search this array of lines when | The Validator class will automatically search this array of lines it
| attempting to replace the :attribute place-holder in error messages. | is attempting to replace the :attribute place-holder in messages.
| It's pretty slick. We think you'll like it.
| |
*/ */
...@@ -28,9 +29,9 @@ return array( ...@@ -28,9 +29,9 @@ return array(
| such as the size (max, min, between) rules. These versions are used | such as the size (max, min, between) rules. These versions are used
| for different input types such as strings and files. | for different input types such as strings and files.
| |
| These language lines may be easily changed by the developer to provide | These language lines may be easily changed to provide custom error
| custom error messages in their application. Error messages for custom | messages in your application. Error messages for custom validation
| validation rules may also be added to this file. | rules may also be added to this file.
| |
*/ */
......
<?php <?php
return array( /*
|--------------------------------------------------------------------------
| Application Routes
|--------------------------------------------------------------------------
|
| Simply tell Laravel the HTTP verbs and URIs it should respond to. It is a
| breeze to setup your applications using Laravel's RESTful routing, and it
| is perfectly suited for building both large applications and simple APIs.
| Enjoy the fresh air and simplicity of the framework.
|
| Let's respond to a simple GET request to http://example.com/hello:
|
| Router::register('GET /hello', function()
| {
| return 'Hello World!';
| });
|
| You can even respond to more than one URI:
|
| Router::register('GET /hello, GET /world', function()
| {
| return 'Hello World!';
| });
|
| It's easy to allow URI wildcards using (:num) or (:any):
|
| Router::register('GET /hello/(:any)', function($name)
| {
| return "Welcome, $name.";
| });
|
*/
/* Router::register(array('GET /', 'GET /home'), function()
|-------------------------------------------------------------------------- {
| Application Routes
|--------------------------------------------------------------------------
|
| Simply tell Laravel the HTTP verbs and URIs it should respond to. It's a
| piece of cake to create beautiful applications using the elegant RESTful
| routing available in Laravel.
|
| Let's respond to a simple GET request to http://example.com/hello:
|
| 'GET /hello' => function()
| {
| return 'Hello World!';
| }
|
| You can even respond to more than one URI:
|
| 'GET /hello, GET /world' => function()
| {
| return 'Hello World!';
| }
|
| It's easy to allow URI wildcards using (:num) or (:any):
|
| 'GET /hello/(:any)' => function($name)
| {
| return "Welcome, $name.";
| }
|
*/
'GET /' => function()
{
return View::make('home.index'); return View::make('home.index');
}, });
/*
|--------------------------------------------------------------------------
| Route Filters
|--------------------------------------------------------------------------
|
| Filters provide a convenient method for attaching functionality to your
| routes. The built-in "before" and "after" filters are called before and
| after every request to your application, and you may even create other
| filters that can be attached to individual routes.
|
| Let's walk through an example...
|
| First, define a filter:
|
| Filter::register('filter', function()
| {
| return 'Filtered!';
| });
|
| Next, attach the filter to a route:
|
| Router::register('GET /', array('before' => 'filter', function()
| {
| return 'Hello World!';
| }));
|
*/
Filter::register('before', function()
{
// Do stuff before every request to your application...
});
Filter::register('after', function()
{
// Do stuff after every request to your application...
});
Filter::register('csrf', function()
{
if (Request::forged()) return Response::error('500');
});
); Filter::register('auth', function()
\ No newline at end of file {
if (Auth::guest()) return Redirect::to('login');
});
\ No newline at end of file
<?php
/**
* Laravel (CLI) - A Command Line For Web Artisans
*
* @package Laravel
* @version 2.0.7
* @author Taylor Otwell <taylorotwell@gmail.com>
* @link http://laravel.com
*/
// --------------------------------------------------------------
// Define the directory separator for the environment.
// --------------------------------------------------------------
define('DS', DIRECTORY_SEPARATOR);
// --------------------------------------------------------------
// The path to the application directory.
// --------------------------------------------------------------
define('APP_PATH', realpath('application').'/');
// --------------------------------------------------------------
// The path to the bundles directory.
// --------------------------------------------------------------
define('BUNDLE_PATH', realpath('bundles').'/');
// --------------------------------------------------------------
// The path to the storage directory.
// --------------------------------------------------------------
define('STORAGE_PATH', realpath('storage').'/');
// --------------------------------------------------------------
// The path to the Laravel directory.
// --------------------------------------------------------------
define('SYS_PATH', realpath('laravel').'/');
// --------------------------------------------------------------
// The path to the public directory.
// --------------------------------------------------------------
define('PUBLIC_PATH', realpath('public').'/');
// --------------------------------------------------------------
// Bootstrap the Laravel core.
// --------------------------------------------------------------
require SYS_PATH.'core.php';
// --------------------------------------------------------------
// Launch the Laravel "Artisan" CLI.
// --------------------------------------------------------------
require SYS_PATH.'cli/artisan'.EXT;
\ No newline at end of file
# Laravel Change Log # Laravel Change Log
## Version 2.1.0
- Fix: Multiple wildcards / regular expressions per segment are now supported.
### Upgrading from 2.0.9
- Replace **laravel** directory.
## Version 2.0.9
- Minor: Made "timestamps" method in Eloquent model protected instead of private.
- Fix: Authentication cookies are not deleted properly when custom domains or paths are used.
### Upgrading from 2.0.8
- Replace **laravel** directory.
## Version 2.0.8
- Fix: Limited URI segments to 20 to protect against DDoS.
### Upgrading from 2.0.7
- Replace **laravel** directory.
## Version 2.0.7 ## Version 2.0.7
- Fix: Fixed raw_where in query builder. - Fix: Fixed raw_where in query builder.
......
<?php namespace Laravel; use Closure;
class Arr {
/**
* Get an item from an array.
*
* "Dot" notation may be used to dig deep into the array.
*
* <code>
* // Get the $array['user']['name'] value from the array
* $name = Arr::get($array, 'user.name');
*
* // Return a default from if the specified item doesn't exist
* $name = Arr::get($array, 'user.name', 'Taylor');
* </code>
*
* @param array $array
* @param string $key
* @param mixed $default
* @return mixed
*/
public static function get($array, $key, $default = null)
{
if (is_null($key)) return $array;
foreach (explode('.', $key) as $segment)
{
if ( ! is_array($array) or ! array_key_exists($segment, $array))
{
return ($default instanceof Closure) ? call_user_func($default) : $default;
}
$array = $array[$segment];
}
return $array;
}
/**
* Set an array item to a given value.
*
* The same "dot" syntax used by the "get" method may be used here.
*
* If no key is given to the method, the entire array will be replaced.
*
* <code>
* // Set the $array['user']['name'] value on the array
* Arr::set($array, 'user.name', 'Taylor');
* </code>
*
* @param array $array
* @param string $key
* @param mixed $value
* @return void
*/
public static function set(&$array, $key, $value)
{
if (is_null($key)) return $array = $value;
$keys = explode('.', $key);
while (count($keys) > 1)
{
$key = array_shift($keys);
if ( ! isset($array[$key]) or ! is_array($array[$key]))
{
$array[$key] = array();
}
$array =& $array[$key];
}
$array[array_shift($keys)] = $value;
}
/**
* Remove an array item from a given array.
*
* The same "dot" syntax used by the "get" method may be used here.
*
* <code>
* // Remove the $array['user']['name'] item from the array
* Arr::forget($array, 'user.name');
* </code>
*
* @param array $array
* @param string $key
* @return void
*/
public static function forget(&$array, $key)
{
if (is_null($key)) return;
$keys = explode('.', $key);
while (count($keys) > 1)
{
$key = array_shift($keys);
if ( ! isset($array[$key]) or ! is_array($array[$key]))
{
return;
}
$array =& $array[$key];
}
unset($array[array_shift($keys)]);
}
/**
* Return the first element in an array which passes a given truth test.
*
* <code>
* // Return the first array element that equals "Taylor"
* $value = Arr::first($array, function($k, $v) {return $v === 'Taylor';});
*
* // Return a default value if no matching element is found
* $value = Arr::first($array, function($k, $v) {return $v === 'Taylor'}, 'Default');
* </code>
*
* @param array $array
* @param Closure $callback
* @param mixed $default
* @return mixed
*/
public static function first($array, $callback, $default = null)
{
foreach ($array as $key => $value)
{
if (call_user_func($callback, $key, $value)) return $value;
}
return ($default instanceof Closure) ? call_user_func($default) : $default;
}
/**
* Remove all array values that are contained within a given array of values.
*
* <code>
* // Remove all array values that are empty strings
* $array = Arr::without($array, '');
*
* // Remove all array values that are "One", "Two", or "Three"
* $array = Arr::without($array, array('One', 'Two', 'Three'));
* </code>
*
* @param array $array
* @param array $without
* @return array
*/
public static function without($array, $without = array())
{
$without = (array) $without;
foreach ((array) $array as $key => $value)
{
if (in_array($value, $without)) unset($array[$key]);
}
return $array;
}
}
\ No newline at end of file
<?php namespace Laravel; <?php namespace Laravel; defined('APP_PATH') or die('No direct script access.');
class Asset { class Asset {
...@@ -34,7 +34,7 @@ class Asset { ...@@ -34,7 +34,7 @@ class Asset {
} }
/** /**
* Magic Method for calling methods on the default Asset container. * Magic Method for calling methods on the default container.
* *
* <code> * <code>
* // Call the "styles" method on the default container * // Call the "styles" method on the default container
...@@ -60,6 +60,13 @@ class Asset_Container { ...@@ -60,6 +60,13 @@ class Asset_Container {
*/ */
public $name; public $name;
/**
* The bundle that the assets belong to.
*
* @var string
*/
public $bundle = DEFAULT_BUNDLE;
/** /**
* All of the registered assets. * All of the registered assets.
* *
...@@ -83,8 +90,8 @@ class Asset_Container { ...@@ -83,8 +90,8 @@ class Asset_Container {
* Add an asset to the container. * Add an asset to the container.
* *
* The extension of the asset source will be used to determine the type of * The extension of the asset source will be used to determine the type of
* asset being registered (CSS or JavaScript). If you are using a non-standard * asset being registered (CSS or JavaScript). When using a non-standard
* extension, you may use the style or script methods to register assets. * extension, the style/script methods may be used to register assets.
* *
* <code> * <code>
* // Add an asset to the container * // Add an asset to the container
...@@ -107,7 +114,7 @@ class Asset_Container { ...@@ -107,7 +114,7 @@ class Asset_Container {
{ {
$type = (pathinfo($source, PATHINFO_EXTENSION) == 'css') ? 'style' : 'script'; $type = (pathinfo($source, PATHINFO_EXTENSION) == 'css') ? 'style' : 'script';
return call_user_func(array($this, $type), $name, $source, $dependencies, $attributes); return $this->$type($name, $source, $dependencies, $attributes);
} }
/** /**
...@@ -147,6 +154,29 @@ class Asset_Container { ...@@ -147,6 +154,29 @@ class Asset_Container {
return $this; return $this;
} }
/**
* Returns the full-path for an asset.
*
* @param string $source
* @return string
*/
public function path($source)
{
return Bundle::assets($this->bundle).$source;
}
/**
* Set the bundle that the container's assets belong to.
*
* @param string $bundle
* @return Asset_Container
*/
public function bundle($bundle)
{
$this->bundle = $bundle;
return $this;
}
/** /**
* Add an asset to the array of registered assets. * Add an asset to the array of registered assets.
* *
...@@ -219,6 +249,14 @@ class Asset_Container { ...@@ -219,6 +249,14 @@ class Asset_Container {
$asset = $this->assets[$group][$name]; $asset = $this->assets[$group][$name];
// If the bundle source is not a complete URL, we will go ahead and prepend
// the bundle's asset path to the source provided with the asset. This will
// ensure that we attach the correct path to the asset.
if (filter_var($asset['source'], FILTER_VALIDATE_URL) === false)
{
$asset['source'] = Bundle::assets($this->bundle).$asset['source'];
}
return HTML::$group($asset['source'], $asset['attributes']); return HTML::$group($asset['source'], $asset['attributes']);
} }
...@@ -257,7 +295,7 @@ class Asset_Container { ...@@ -257,7 +295,7 @@ class Asset_Container {
{ {
// If the asset has no more dependencies, we can add it to the sorted list // If the asset has no more dependencies, we can add it to the sorted list
// and remove it from the array of assets. Otherwise, we will not verify // and remove it from the array of assets. Otherwise, we will not verify
// the asset's dependencies and determine if they have already been sorted. // the asset's dependencies and determine if they've been sorted.
if (count($assets[$asset]['dependencies']) == 0) if (count($assets[$asset]['dependencies']) == 0)
{ {
$sorted[$asset] = $value; $sorted[$asset] = $value;
...@@ -289,7 +327,8 @@ class Asset_Container { ...@@ -289,7 +327,8 @@ class Asset_Container {
* Verify that an asset's dependency is valid. * Verify that an asset's dependency is valid.
* *
* A dependency is considered valid if it exists, is not a circular reference, and is * A dependency is considered valid if it exists, is not a circular reference, and is
* not a reference to the owning asset itself. * not a reference to the owning asset itself. If the dependency doesn't exist, no
* error or warning will be given. For the other cases, an exception is thrown.
* *
* @param string $asset * @param string $asset
* @param string $dependency * @param string $dependency
...@@ -299,16 +338,20 @@ class Asset_Container { ...@@ -299,16 +338,20 @@ class Asset_Container {
*/ */
protected function dependency_is_valid($asset, $dependency, $original, $assets) protected function dependency_is_valid($asset, $dependency, $original, $assets)
{ {
if ( ! isset($original[$dependency])) return false; if ( ! isset($original[$dependency]))
if ($dependency === $asset)
{ {
throw new \LogicException("Asset [$asset] is dependent on itself."); return false;
}
elseif ($dependency === $asset)
{
throw new \Exception("Asset [$asset] is dependent on itself.");
} }
elseif (isset($assets[$dependency]) and in_array($asset, $assets[$dependency]['dependencies'])) elseif (isset($assets[$dependency]) and in_array($asset, $assets[$dependency]['dependencies']))
{ {
throw new \LogicException("Assets [$asset] and [$dependency] have a circular dependency."); throw new \Exception("Assets [$asset] and [$dependency] have a circular dependency.");
} }
return true;
} }
} }
...@@ -7,7 +7,7 @@ class Auth { ...@@ -7,7 +7,7 @@ class Auth {
* *
* @var object * @var object
*/ */
protected static $user; public static $user;
/** /**
* The key used when storing the user ID in the session. * The key used when storing the user ID in the session.
...@@ -48,12 +48,6 @@ class Auth { ...@@ -48,12 +48,6 @@ class Auth {
/** /**
* Get the current user of the application. * Get the current user of the application.
* *
* This method will call the "user" closure in the auth configuration file.
* If the user is not authenticated, null will be returned by the methd.
*
* If no user exists in the session, the method will check for a "remember me"
* cookie and attempt to login the user based on the value of that cookie.
*
* <code> * <code>
* // Get the current user of the application * // Get the current user of the application
* $user = Auth::user(); * $user = Auth::user();
...@@ -62,20 +56,26 @@ class Auth { ...@@ -62,20 +56,26 @@ class Auth {
* $email = Auth::user()->email; * $email = Auth::user()->email;
* </code> * </code>
* *
* @return object * @return object|null
*/ */
public static function user() public static function user()
{ {
if ( ! is_null(static::$user)) return static::$user; if ( ! is_null(static::$user)) return static::$user;
$id = IoC::core('session')->get(Auth::user_key); $id = Session::get(Auth::user_key);
// To retrieve the user, we'll first attempt to use the "user" Closure
// defined in the auth configuration file, passing in the ID. The user
// Closure gives the developer a ton of freedom surrounding how the
// user is actually retrieved.
$config = Config::get('auth');
static::$user = call_user_func(Config::get('auth.user'), $id); static::$user = call_user_func($config['user'], $id);
// If the user was not found in the database, but a "remember me" cookie // If the user wasn't found in the database but a "remember me" cookie
// exists, we will attempt to recall the user based on the cookie value. // exists, we'll attempt to recall the user based on the cookie value.
// Since all cookies contain a fingerprint hash verifying that the have // Since all cookies contain a fingerprint hash verifying that they
// not been modified on the client, we should be able to trust it. // haven't changed, we can trust it.
$recaller = Cookie::get(Auth::remember_key); $recaller = Cookie::get(Auth::remember_key);
if (is_null(static::$user) and ! is_null($recaller)) if (is_null(static::$user) and ! is_null($recaller))
...@@ -94,13 +94,17 @@ class Auth { ...@@ -94,13 +94,17 @@ class Auth {
*/ */
protected static function recall($recaller) protected static function recall($recaller)
{ {
// When the "remember me" cookie is stored, it is encrypted and contains the // When the remember me cookie is stored, it is encrypted and contains
// user's ID and a long, random string. The ID and string are separated by // the user's ID and a long, random string. The segments are separated
// a pipe character. Since we exploded the decrypted string, we can just // by a pipe character so we'll explode on that.
// pass the first item in the array to the user Closure.
$recaller = explode('|', Crypter::decrypt($recaller)); $recaller = explode('|', Crypter::decrypt($recaller));
if ( ! is_null($user = call_user_func(Config::get('auth.user'), $recaller[0]))) // We'll pass the ID that was stored in the cookie into the same user
// Closure that is used by the "user" method. If the method returns
// a user, we will log them into the application.
$user = call_user_func(Config::get('auth.user'), $recaller[0]);
if ( ! is_null($user))
{ {
static::login($user); static::login($user);
...@@ -111,12 +115,13 @@ class Auth { ...@@ -111,12 +115,13 @@ class Auth {
/** /**
* Attempt to log a user into the application. * Attempt to log a user into the application.
* *
* If the credentials are valid, the user will be logged into the application * <code>
* and their user ID will be stored in the session via the "login" method. * // Attempt to log a user into the application
* $success = Auth::attempt('username', 'password');
* *
* The user may also be "remembered", which will keep the user logged into the * // Attempt to login a user and set the "remember me" cookie
* application for one year or until they logout. The user is remembered via * Auth::attempt('username', 'password', true);
* an encrypted cookie. * </code>
* *
* @param string $username * @param string $username
* @param string $password * @param string $password
...@@ -127,32 +132,35 @@ class Auth { ...@@ -127,32 +132,35 @@ class Auth {
{ {
$config = Config::get('auth'); $config = Config::get('auth');
$user = call_user_func($config['attempt'], $username, $password, $config); // When attempting to login the user, we will call the "attempt" closure
// from the configuration file. This gives the developer the freedom to
// authenticate based on the needs of their application.
//
// All of the password hashing and checking and left totally up to the
// developer, as this gives them the freedom to use any hashing scheme
// or authentication provider they wish.
$user = call_user_func($config['attempt'], $username, $password);
// If the user credentials were authenticated by the closure, we will
// log the user into the application, which will store their user ID
// in the session for subsequent requests.
if (is_null($user)) return false;
if ( ! is_null($user))
{
static::login($user, $remember); static::login($user, $remember);
return true; return true;
} }
return false;
}
/** /**
* Log a user into the application. * Log a user into the application.
* *
* An object representing the user or an integer user ID may be given to the method.
* If an object is given, the object must have an "id" property containing the user
* ID as it is stored in the database.
*
* <code> * <code>
* // Login a user by passing a user object
* Auth::login($user);
*
* // Login the user with an ID of 15 * // Login the user with an ID of 15
* Auth::login(15); * Auth::login(15);
* *
* // Login a user by passing a user object
* Auth::login($user);
*
* // Login a user and set a "remember me" cookie * // Login a user and set a "remember me" cookie
* Auth::login($user, true); * Auth::login($user, true);
* </code> * </code>
...@@ -167,11 +175,11 @@ class Auth { ...@@ -167,11 +175,11 @@ class Auth {
if ($remember) static::remember($id); if ($remember) static::remember($id);
IoC::core('session')->put(Auth::user_key, $id); Session::put(Auth::user_key, $id);
} }
/** /**
* Set a cookie so that users are "remembered" and don't need to login. * Set a cookie so that the user is "remembered".
* *
* @param string $id * @param string $id
* @return void * @return void
...@@ -183,7 +191,7 @@ class Auth { ...@@ -183,7 +191,7 @@ class Auth {
// This method assumes the "remember me" cookie should have the same // This method assumes the "remember me" cookie should have the same
// configuration as the session cookie. Since this cookie, like the // configuration as the session cookie. Since this cookie, like the
// session cookie, should be kept very secure, it's probably safe // session cookie, should be kept very secure, it's probably safe
// to assume the settings are the same. // to assume the settings are the same for this cookie.
$config = Config::get('session'); $config = Config::get('session');
extract($config, EXTR_SKIP); extract($config, EXTR_SKIP);
...@@ -194,14 +202,13 @@ class Auth { ...@@ -194,14 +202,13 @@ class Auth {
/** /**
* Log the current user out of the application. * Log the current user out of the application.
* *
* The "logout" closure in the authenciation configuration file will be
* called. All authentication cookies will be deleted and the user ID
* will be removed from the session.
*
* @return void * @return void
*/ */
public static function logout() public static function logout()
{ {
// We will call the "logout" closure first, which gives the developer
// the chance to do any clean-up or before the user is logged out of
// the application. No action is taken by default.
call_user_func(Config::get('auth.logout'), static::user()); call_user_func(Config::get('auth.logout'), static::user());
static::$user = null; static::$user = null;
...@@ -213,11 +220,9 @@ class Auth { ...@@ -213,11 +220,9 @@ class Auth {
// When forgetting the cookie, we need to also pass in the path and // When forgetting the cookie, we need to also pass in the path and
// domain that would have been used when the cookie was originally // domain that would have been used when the cookie was originally
// set by the framework, otherwise it will not be deleted. // set by the framework, otherwise it will not be deleted.
Cookie::forget(Auth::user_key, $path, $domain, $secure);
Cookie::forget(Auth::remember_key, $path, $domain, $secure); Cookie::forget(Auth::remember_key, $path, $domain, $secure);
IoC::core('session')->forget(Auth::user_key); Session::forget(Auth::user_key);
} }
} }
\ No newline at end of file
<?php namespace Laravel; <?php namespace Laravel; defined('APP_PATH') or die('No direct script access.');
class Autoloader { class Autoloader {
...@@ -10,176 +10,143 @@ class Autoloader { ...@@ -10,176 +10,143 @@ class Autoloader {
public static $mappings = array(); public static $mappings = array();
/** /**
* The PSR-0 compliant libraries registered with the loader. * All of the class aliases registered with the auto-loader.
* *
* @var array * @var array
*/ */
public static $libraries = array(); public static $aliases = array();
/** /**
* The paths to be searched by the loader. * The directories that use the PSR-0 naming convention.
* *
* @var array * @var array
*/ */
protected static $paths = array(MODEL_PATH, LIBRARY_PATH); public static $psr = array();
/** /**
* Load the file corresponding to a given class. * Load the file corresponding to a given class.
* *
* This method is registerd in the core bootstrap file as an SPL Autoloader. * This method is registerd in the bootstrap file as an SPL auto-loader.
* *
* @param string $class * @param string $class
* @return void * @return void
*/ */
public static function load($class) public static function load($class)
{ {
if (isset(Config::$items['application']['aliases'][$class])) // First, we will check to see if the class has been aliased. If it has,
// we will register the alias, which may cause the auto-loader to be
// called again for the "real" class name.
if (isset(static::$aliases[$class]))
{ {
return class_alias(Config::$items['application']['aliases'][$class], $class); class_alias(static::$aliases[$class], $class);
} }
if ( ! is_null($path = static::find($class))) // All classes in Laravel are staticly mapped. There is no crazy search
// routine that digs through directories. It's just a simple array of
// class to file path maps for ultra-fast file loading.
elseif (isset(static::$mappings[$class]))
{ {
require $path; require static::$mappings[$class];
}
} }
/** // If the class is namespaced to an existing bundle and the bundle has
* Determine the file path associated with a given class name. // not been started, we will start the bundle and attempt to load the
* // class file again. If that fails, an error will be thrown by PHP.
* @param string $class //
* @return string // This allows bundle classes to be loaded by the auto-loader before
*/ // their class mappings have actually been registered; however, it
protected static function find($class) // is up to the bundle developer to namespace their classes to
{ // match the name of their bundle.
// First we will look for the class in the hard-coded class mappings, since elseif (($slash = strpos($class, '\\')) !== false)
// this is the fastest way to resolve a class name to its associated file.
// This saves us from having to search through the file system manually.
if (isset(static::$mappings[$class]))
{ {
return static::$mappings[$class]; $bundle = substr($class, 0, $slash);
}
// If the library has been registered as a PSR-0 compliant library, we will // It's very important that we make sure the bundle has not been
// load the library according to the PSR-0 naming standards, which state that // started here. If we don't, we'll end up in an infinite loop
// namespaces and underscores indicate the directory hierarchy of the class. // attempting to load a bundle's class.
if (in_array(static::library($class), static::$libraries)) if (Bundle::exists($bundle) and ! Bundle::started($bundle))
{ {
return LIBRARY_PATH.str_replace(array('\\', '_'), '/', $class).EXT; Bundle::start($bundle);
}
// Next we will search through the common Laravel paths for the class file.
// The Laravel libraries and models directories will be searched according
// to the Laravel class naming standards.
$file = strtolower(str_replace('\\', '/', $class));
foreach (static::$paths as $path) static::load($class);
{
if (file_exists($path = $path.$file.EXT))
{
return $path;
} }
} }
// Since not all controllers will be resolved by the controller resolver, static::load_psr($class);
// we will do a quick check in the controller directory for the class.
// For instance, since base controllers would not be resolved by the
// controller class, we will need to resolve them here.
if (file_exists($path = static::controller($class)))
{
return $path;
}
} }
/** /**
* Extract the "library" name from the given class. * Attempt to resolve a class using the PSR-0 standard.
*
* The library name is essentially the namespace, or the string that preceeds
* the first PSR-0 separator. PSR-0 states that namespaces or undescores may
* be used to indicate the directory structure in which the file resides.
* *
* @param string $class * @param string $class
* @return string * @return void
*/ */
protected static function library($class) protected static function load_psr($class)
{
// The PSR-0 standard indicates that class namespace slashes or
// underscores should be used to indicate the directory tree in
// which the class resides.
$file = str_replace(array('\\', '_'), '/', $class);
// Once we have formatted the class name, we will simply spin
// through the registered PSR-0 directories and attempt to
// locate and load the class into the script.
foreach (static::$psr as $directory)
{ {
if (($separator = strpos($class, '\\')) !== false) if (file_exists($path = $directory.strtolower($file).EXT))
{ {
return substr($class, 0, $separator); return require $path;
} }
elseif (($separator = strpos($class, '_')) !== false) elseif (file_exists($path = $directory.$file.EXT))
{ {
return substr($class, 0, $separator); return require $path;
}
} }
} }
/** /**
* Translate a given controller class name into the corresponding file name. * Register an array of class to path mappings.
*
* The controller suffix will be removed, and the underscores will be translated
* into directory slashes. Of course, the entire class name will be converted to
* lower case as well.
* *
* <code> * <code>
* // Returns "user/profile"... * // Register a class mapping with the Autoloader
* $file = static::controller('User_Profile_Controller'); * Autoloader::map(array('User' => APP_PATH.'models/user.php'));
* </code> * </code>
* *
* @param string $class * @param array $mappings
* @return string * @return void
*/ */
protected static function controller($class) public static function map($mappings)
{ {
$controller = str_replace(array('_', '_Controller'), array('/', ''), $class); static::$mappings = array_merge(static::$mappings, $mappings);
return CONTROLLER_PATH.strtolower($controller).EXT;
} }
/** /**
* Register an array of class to path mappings. * Register a class alias with the auto-loader.
*
* The mappings will be used to resolve file paths from class names when
* a class is lazy loaded through the Autoloader, providing a faster way
* of resolving file paths than the typical file_exists method.
* *
* <code> * @param string $class
* // Register a class mapping with the Autoloader * @param string $alias
* Autoloader::maps(array('User' => MODEL_PATH.'user'.EXT));
* </code>
*
* @param array $mappings
* @return void * @return void
*/ */
public static function maps($mappings) public static function alias($class, $alias)
{ {
foreach ($mappings as $class => $path) static::$aliases[$alias] = $class;
{
static::$mappings[$class] = $path;
}
} }
/** /**
* Register PSR-0 libraries with the Autoloader. * Register directories to be searched as a PSR-0 library.
*
* The library names given to this method should match directories within
* the application libraries directory. This method provides an easy way
* to indicate that some libraries should be loaded using the PSR-0
* naming conventions instead of the Laravel conventions.
*
* <code>
* // Register the "Assetic" library with the Autoloader
* Autoloader::libraries('Assetic');
* *
* // Register several libraries with the Autoloader * @param string|array $directory
* Autoloader::libraries(array('Assetic', 'Twig'));
* </code>
*
* @param array $libraries
* @return void * @return void
*/ */
public static function libraries($libraries) public static function psr($directory)
{
$directories = array_map(function($directory)
{ {
static::$libraries = array_merge(static::$libraries, (array) $libraries); return rtrim($directory, '/').'/';
}, (array) $directory);
static::$psr = array_unique(array_merge(static::$psr, $directories));
} }
} }
\ No newline at end of file
<?php namespace Laravel;
class Benchmark {
/**
* All of the benchmark starting times.
*
* @var array
*/
protected static $marks = array();
/**
* Start a benchmark starting time.
*
* @param string $name
* @return void
*/
public static function start($name)
{
static::$marks[$name] = microtime(true);
}
/**
* Get the elapsed time in milliseconds since starting a benchmark.
*
* @param string $name
* @return float
*/
public static function check($name)
{
if (array_key_exists($name, static::$marks))
{
return (float) number_format((microtime(true) - static::$marks[$name]) * 1000, 2);
}
return (float) 0.0;
}
/**
* Get the total memory usage in megabytes.
*
* @return float
*/
public static function memory()
{
return (float) number_format(memory_get_usage() / 1024 / 1024, 2);
}
}
\ No newline at end of file
<?php namespace Laravel; <?php namespace Laravel; defined('APP_PATH') or die('No direct script access.');
class Blade { class Blade {
...@@ -49,10 +49,6 @@ class Blade { ...@@ -49,10 +49,6 @@ class Blade {
/** /**
* Rewrites Blade echo statements into PHP echo statements. * Rewrites Blade echo statements into PHP echo statements.
* *
* Blade echo statements are simply PHP statement enclosed within double curly
* braces. For example, {{$content}} will simply echo out the content variable
* to the output buffer.
*
* @param string $value * @param string $value
* @return string * @return string
*/ */
...@@ -64,10 +60,6 @@ class Blade { ...@@ -64,10 +60,6 @@ class Blade {
/** /**
* Rewrites Blade structure openings into PHP structure openings. * Rewrites Blade structure openings into PHP structure openings.
* *
* By "structures", we mean the if, elseif, foreach, for, and while statements.
* All of these structures essentially have the same format, and can be lumped
* into a single regular expression.
*
* @param string $value * @param string $value
* @return string * @return string
*/ */
......
<?php namespace Laravel; defined('APP_PATH') or die('No direct script access.');
class Bundle {
/**
* All of the application's bundles.
*
* @var array
*/
protected static $bundles;
/**
* A cache of the parsed bundle elements.
*
* @var array
*/
protected static $elements = array();
/**
* All of the bundles that have been started.
*
* @var array
*/
protected static $started = array();
/**
* Load a bundle by running it's start-up script.
*
* If the bundle has already been started, no action will be taken.
*
* @param string $bundle
* @return void
*/
public static function start($bundle)
{
if (static::started($bundle)) return;
if ($bundle !== DEFAULT_BUNDLE and ! static::exists($bundle))
{
throw new \Exception("Bundle [$bundle] has not been installed.");
}
// 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
// classes the bundle uses with the auto-loader, or perhaps will start any
// dependent bundles so that they are available.
if (file_exists($path = static::path($bundle).'bundle'.EXT))
{
require $path;
}
// Each bundle may also have a "routes" file which is responsible for
// registering the bundle's routes. This is kept separate from the
// start script for reverse routing efficiency purposes.
static::routes($bundle);
static::$started[] = strtolower($bundle);
}
/**
* Load the "routes" file for a given bundle.
*
* @param string $bundle
* @return void
*/
public static function routes($bundle)
{
if (static::started($bundle)) return;
if (file_exists($path = static::path($bundle).'routes'.EXT))
{
require $path;
}
}
/**
* Determine if the given bundle is "routable".
*
* A bundle is considered routable if it has a controller directory or a routes file.
*
* @param string $bundle
* @return bool
*/
public static function routable($bundle)
{
$path = static::path($bundle);
return is_dir($path.'controllers/') or file_exists($path.'routes'.EXT);
}
/**
* Deteremine if a bundle exists within the bundles directory.
*
* @param string $bundle
* @return bool
*/
public static function exists($bundle)
{
return in_array(strtolower($bundle), static::all());
}
/**
* Determine if a given bundle has been started for the request.
*
* @param string $bundle
* @return void
*/
public static function started($bundle)
{
return in_array(strtolower($bundle), static::$started);
}
/**
* Get the identifier prefix for the bundle.
*
* @param string $bundle
* @return string
*/
public static function prefix($bundle)
{
return ($bundle !== DEFAULT_BUNDLE) ? "{$bundle}::" : '';
}
/**
* Get the class prefix for a given bundle.
*
* @param string $bundle
* @return string
*/
public static function class_prefix($bundle)
{
return ($bundle !== DEFAULT_BUNDLE) ? Str::classify($bundle).'_' : '';
}
/**
* Return the root bundle path for a given bundle.
*
* <code>
* // Returns the bundle path for the "admin" bundle
* $path = Bundle::path('admin');
*
* // Returns the APP_PATH constant as the default bundle
* $path = Bundle::path('application');
* </code>
*
* @param string $bundle
* @return string
*/
public static function path($bundle)
{
return ($bundle != DEFAULT_BUNDLE) ? BUNDLE_PATH.strtolower($bundle).DS : APP_PATH;
}
/**
* Return the root asset path for the given bundle.
*
* @param string $bundle
* @return string
*/
public static function assets($bundle)
{
return ($bundle != DEFAULT_BUNDLE) ? PUBLIC_PATH."bundles/{$bundle}/" : PUBLIC_PATH;
}
/**
* Get the bundle name from a given identifier.
*
* <code>
* // Returns "admin" as the bundle name for the identifier
* $bundle = Bundle::name('admin::home.index');
* </code>
*
* @param string $identifier
* @return string
*/
public static function name($identifier)
{
list($bundle, $element) = static::parse($identifier);
return $bundle;
}
/**
* Get the element name from a given identifier.
*
* <code>
* // Returns "home.index" as the element name for the identifier
* $bundle = Bundle::bundle('admin::home.index');
* </code>
*
* @param string $identifier
* @return string
*/
public static function element($identifier)
{
list($bundle, $element) = static::parse($identifier);
return $element;
}
/**
* Reconstruct an identifier from a given bundle and element.
*
* <code>
* // Returns "admin::home.index"
* $identifier = Bundle::identifier('admin', 'home.index');
*
* // Returns "home.index"
* $identifier = Bundle::identifier('application', 'home.index');
* </code>
*
* @param string $bundle
* @param string $element
* @return string
*/
public static function identifier($bundle, $element)
{
return (is_null($bundle) or $bundle == DEFAULT_BUNDLE) ? $element : $bundle.'::'.$element;
}
/**
* Return the bundle name if it exists, else return the default bundle.
*
* @param string $bundle
* @return string
*/
public static function resolve($bundle)
{
return (static::exists($bundle)) ? $bundle : DEFAULT_BUNDLE;
}
/**
* Parse a element identifier and return the bundle name and element.
*
* <code>
* // Returns array(null, 'admin.user')
* $element = Bundle::parse('admin.user');
*
* // Parses "admin::user" and returns array('admin', 'user')
* $element = Bundle::parse('admin::user');
* </code>
*
* @param string $identifier
* @return array
*/
public static function parse($identifier)
{
// The parsed elements are cached so we don't have to reparse them on each
// subsequent request for the parsed element. So, if we've already parsed
// the given element, we'll just return the cached copy.
if (isset(static::$elements[$identifier]))
{
return static::$elements[$identifier];
}
if (strpos($identifier, '::') !== false)
{
$element = explode('::', strtolower($identifier));
}
// If no bundle is in the identifier, we will insert the default bundle
// since classes like Config and Lang organize their items by bundle.
// The "application" folder essentially behaves as a bundle.
else
{
$element = array(DEFAULT_BUNDLE, strtolower($identifier));
}
return static::$elements[$identifier] = $element;
}
/**
* Detect all of the existing bundles in the application.
*
* The names of the bundles are cached so this operation will be only be
* performed once and then the same array will be returned on each later
* request for the bundle names.
*
* @return array
*/
public static function all()
{
if (is_array(static::$bundles)) return static::$bundles;
$bundles = array();
foreach (array_filter(glob(BUNDLE_PATH.'*'), 'is_dir') as $bundle)
{
$bundles[] = basename($bundle);
}
return static::$bundles = $bundles;
}
}
\ No newline at end of file
<?php namespace Laravel\Cache; <?php namespace Laravel; defined('APP_PATH') or die('No direct script access.');
use Laravel\Redis; class Cache {
use Laravel\Config;
use Laravel\Memcached;
class Manager {
/** /**
* All of the active cache drivers. * All of the active cache drivers.
* *
* @var array * @var array
*/ */
protected static $drivers = array(); public static $drivers = array();
/** /**
* Get a cache driver instance. * Get a cache driver instance.
* *
* If no driver name is specified, the default cache driver will * If no driver name is specified, the default will be returned.
* be returned as defined in the cache configuration file.
* *
* <code> * <code>
* // Get the default cache driver instance * // Get the default cache driver instance
...@@ -34,9 +29,9 @@ class Manager { ...@@ -34,9 +29,9 @@ class Manager {
{ {
if (is_null($driver)) $driver = Config::get('cache.driver'); if (is_null($driver)) $driver = Config::get('cache.driver');
if ( ! array_key_exists($driver, static::$drivers)) if ( ! isset(static::$drivers[$driver]))
{ {
return static::$drivers[$driver] = static::factory($driver); static::$drivers[$driver] = static::factory($driver);
} }
return static::$drivers[$driver]; return static::$drivers[$driver];
...@@ -53,33 +48,33 @@ class Manager { ...@@ -53,33 +48,33 @@ class Manager {
switch ($driver) switch ($driver)
{ {
case 'apc': case 'apc':
return new Drivers\APC(Config::get('cache.key')); return new Cache\Drivers\APC(Config::get('cache.key'));
case 'file': case 'file':
return new Drivers\File(CACHE_PATH); return new Cache\Drivers\File(CACHE_PATH);
case 'memcached': case 'memcached':
return new Drivers\Memcached(Memcached::instance(), Config::get('cache.key')); return new Cache\Drivers\Memcached(Memcached::connection(), Config::get('cache.key'));
case 'redis': case 'redis':
return new Drivers\Redis(Redis::db()); return new Cache\Drivers\Redis(Redis::db());
case 'database':
return new Cache\Drivers\Database(Config::get('cache.key'));
default: default:
throw new \DomainException("Cache driver {$driver} is not supported."); throw new \Exception("Cache driver {$driver} is not supported.");
} }
} }
/** /**
* Pass all other methods to the default cache driver. * Magic Method for calling the methods on the default cache driver.
*
* Passing method calls to the driver instance provides a convenient API
* for the developer when always using the default cache driver.
* *
* <code> * <code>
* // Call the "get" method on the default driver * // Call the "get" method on the default cache driver
* $name = Cache::get('name'); * $name = Cache::get('name');
* *
* // Call the "put" method on the default driver * // Call the "put" method on the default cache driver
* Cache::put('name', 'Taylor', 15); * Cache::put('name', 'Taylor', 15);
* </code> * </code>
*/ */
......
<?php namespace Laravel\Cache\Drivers;
use Laravel\Config;
use Laravel\Database as DB;
use Laravel\Database\Connection;
class Database extends Driver {
/**
* The cache key from the cache configuration file.
*
* @var string
*/
protected $key;
/**
* Create a new database cache driver instance.
*
* @param string $key
* @return void
*/
public function __construct($key)
{
$this->key = $key;
}
/**
* Determine if an item exists in the cache.
*
* @param string $key
* @return bool
*/
public function has($key)
{
return ( ! is_null($this->get($key)));
}
/**
* Retrieve an item from the cache driver.
*
* @param string $key
* @return mixed
*/
protected function retrieve($key)
{
$cache = $this->table()->where('key', '=', $this->key.$key)->first();
if ( ! is_null($cache))
{
if (time() >= $cache->expiration) return $this->forget($key);
return unserialize($cache->value);
}
}
/**
* Write an item to the cache for a given number of minutes.
*
* <code>
* // Put an item in the cache for 15 minutes
* Cache::put('name', 'Taylor', 15);
* </code>
*
* @param string $key
* @param mixed $value
* @param int $minutes
* @return void
*/
public function put($key, $value, $minutes)
{
$key = $this->key.$key;
$value = serialize($value);
$expiration = $this->expiration($minutes);
// To update the value, we'll first attempt an insert against the
// database and if we catch an exception, we'll assume that the
// primary key already exists in the table and update.
try
{
$this->table()->insert(compact('key', 'value', 'expiration'));
}
catch (\Exception $e)
{
$this->table()->where('key', '=', $key)->update(compact('value', 'expiration'));
}
}
/**
* Delete an item from the cache.
*
* @param string $key
* @return void
*/
public function forget($key)
{
$this->table()->where('key', '=', $this->key.$key)->delete();
}
/**
* Get a query builder for the database table.
*
* @return Query
*/
protected function table()
{
$connection = DB::connection(Config::get('cache.database.connection'));
return $connection->table(Config::get('cache.database.table'));
}
}
\ No newline at end of file
...@@ -28,9 +28,7 @@ abstract class Driver { ...@@ -28,9 +28,7 @@ abstract class Driver {
*/ */
public function get($key, $default = null) public function get($key, $default = null)
{ {
if ( ! is_null($item = $this->retrieve($key))) return $item; return ( ! is_null($item = $this->retrieve($key))) ? $item : value($default);
return ($default instanceof Closure) ? call_user_func($default) : $default;
} }
/** /**
...@@ -57,8 +55,7 @@ abstract class Driver { ...@@ -57,8 +55,7 @@ abstract class Driver {
abstract public function put($key, $value, $minutes); abstract public function put($key, $value, $minutes);
/** /**
* Get an item from the cache. If the item doesn't exist in the * Get an item from the cache, or cache and return the default value.
* cache, store the default value in the cache and return it.
* *
* <code> * <code>
* // Get an item from the cache, or cache a value for 15 minutes * // Get an item from the cache, or cache a value for 15 minutes
...@@ -77,9 +74,7 @@ abstract class Driver { ...@@ -77,9 +74,7 @@ abstract class Driver {
{ {
if ( ! is_null($item = $this->get($key, null))) return $item; if ( ! is_null($item = $this->get($key, null))) return $item;
$default = ($default instanceof Closure) ? call_user_func($default) : $default; $this->put($key, value($default), $minutes);
$this->put($key, $default, $minutes);
return $default; return $default;
} }
...@@ -92,4 +87,15 @@ abstract class Driver { ...@@ -92,4 +87,15 @@ abstract class Driver {
*/ */
abstract public function forget($key); abstract public function forget($key);
/**
* Get the expiration time as a UNIX timestamp.
*
* @param int $minutes
* @return int
*/
protected function expiration($minutes)
{
return time() + ($minutes * 60);
}
} }
...@@ -68,7 +68,7 @@ class File extends Driver { ...@@ -68,7 +68,7 @@ class File extends Driver {
*/ */
public function put($key, $value, $minutes) public function put($key, $value, $minutes)
{ {
$value = (time() + ($minutes * 60)).serialize($value); $value = $this->expiration($minutes).serialize($value);
file_put_contents($this->path.$key, $value, LOCK_EX); file_put_contents($this->path.$key, $value, LOCK_EX);
} }
...@@ -81,10 +81,7 @@ class File extends Driver { ...@@ -81,10 +81,7 @@ class File extends Driver {
*/ */
public function forget($key) public function forget($key)
{ {
if (file_exists($this->path.$key)) if (file_exists($this->path.$key)) @unlink($this->path.$key);
{
@unlink($this->path.$key);
}
} }
} }
\ No newline at end of file
<?php namespace Laravel\CLI; defined('APP_PATH') or die('No direct script access.');
use Laravel\IoC;
use Laravel\Bundle;
use Laravel\Database as DB;
/**
* Fire up the default bundle. This will ensure any dependencies that
* need to be registered in the IoC container are registered and that
* the auto-loader mappings are registered.
*/
Bundle::start(DEFAULT_BUNDLE);
/**
* We will register all of the Laravel provided tasks inside the IoC
* container so they can be resolved by the task class. This allows
* us to seamlessly add tasks to the CLI so that the Task class
* doesn't have to worry about how to resolve core tasks.
*/
/**
* The bundle task is responsible for the installation of bundles
* and their dependencies. It utilizes the bundles API to get the
* meta-data for the available bundles.
*/
IoC::register('task: bundle', function()
{
return new Tasks\Bundle\Bundler;
});
/**
* The migrate task is responsible for running database migrations
* as well as migration rollbacks. We will also create an instance
* of the migration resolver and database classes, which are used
* to perform various support functions for the migrator.
*/
IoC::register('task: migrate', function()
{
$database = new Tasks\Migrate\Database;
$resolver = new Tasks\Migrate\Resolver($database);
return new Tasks\Migrate\Migrator($resolver, $database);
});
/**
* We will wrap the command execution in a try / catch block and
* simply write out any exception messages we receive to the CLI
* for the developer. Note that this only writes out messages
* for the CLI exceptions. All others will be not be caught
* and will be totally dumped out to the CLI.
*/
try
{
Command::run(array_slice($_SERVER['argv'], 1));
}
catch (\Exception $e)
{
echo $e->getMessage();
}
echo PHP_EOL;
\ No newline at end of file
<?php namespace Laravel\CLI;
use Laravel\IoC;
use Laravel\Str;
use Laravel\Bundle;
class Command {
/**
* Run a CLI task with the given arguments.
*
* @param array $arguments
* @return void
*/
public static function run($arguments = array())
{
if ( ! isset($arguments[0]))
{
throw new \Exception("Whoops! You forgot to provide the task name.");
}
list($bundle, $task, $method) = static::parse($arguments[0]);
// If the task exists within a bundle, we will start the bundle so that
// any dependencies can be registered in the application IoC container.
// If the task is registered in the container, it will be resolved
// via the container instead of by this class.
if (Bundle::exists($bundle)) Bundle::start($bundle);
if (is_null($task = static::resolve($bundle, $task)))
{
throw new \Exception("Sorry, I can't find that task.");
}
$task->$method(array_slice($arguments, 1));
}
/**
* Parse the task name to extract the bundle, task, and method.
*
* @param string $task
* @return array
*/
protected static function parse($task)
{
list($bundle, $task) = Bundle::parse($task);
// Extract the task method from the task string. Methods are called
// on tasks by separating the task and method with a single colon.
// If no task is specified, "run" is used as the default method.
if (str_contains($task, ':'))
{
list($task, $method) = explode(':', $task);
}
else
{
$method = 'run';
}
return array($bundle, $task, $method);
}
/**
* Resolve an instance of the given task name.
*
* @param string $bundle
* @param string $task
* @return object
*/
public static function resolve($bundle, $task)
{
$identifier = Bundle::identifier($bundle, $task);
// First we'll check to see if the task has been registered in
// the application IoC container. This allows dependencies to
// be injected into tasks for more testability.
if (IoC::registered("task: {$identifier}"))
{
return IoC::resolve("task: {$identifier}");
}
// If the task file exists, we'll format the bundle and task
// name into a task class name and resolve an instance of
// the so that the requested method may be executed.
if (file_exists($path = Bundle::path($bundle).'tasks/'.$task.EXT))
{
require $path;
$task = static::format($bundle, $task);
return new $task;
}
}
/**
* Format a bundle and task into a task class name.
*
* @param string $bundle
* @param string $task
* @return string
*/
protected static function format($bundle, $task)
{
$prefix = Bundle::class_prefix($bundle);
return '\\'.$prefix.Str::clasify($task).'_Task';
}
}
\ No newline at end of file
<?php namespace Laravel\CLI\Tasks\Bundle; defined('APP_PATH') or die('No direct script access.');
use Laravel\IoC;
use Laravel\Bundle;
use Laravel\CLI\Tasks\Task;
IoC::singleton('bundle.repository', function()
{
return new Repository;
});
IoC::singleton('bundle.publisher', function()
{
return new Publisher;
});
IoC::singleton('bundle.provider: github', function()
{
return new Providers\Github;
});
class Bundler extends Task {
/**
* Install the given bundles into the application.
*
* @param array $bundles
* @return void
*/
public function install($bundles)
{
$publisher = IoC::resolve('bundle.publisher');
foreach ($this->get($bundles) as $bundle)
{
if (is_dir(BUNDLE_PATH.$bundle['name']))
{
echo "Bundle {$bundle['name']} is already installed.";
continue;
}
// Once we have the bundle information, we can resolve an instance
// of a provider and install the bundle into the application and
// all of its registered dependencies as well.
//
// Each bundle provider implements the Provider interface and
// is repsonsible for retrieving the bundle source from its
// hosting party and installing it into the application.
$provider = "bundle.provider: {$bundle['provider']}";
IoC::resolve($provider)->install($bundle);
$publisher->publish($bundle);
}
}
/**
* Publish bundle assets to the public directory.
*
* @param array $bundles
* @return void
*/
public function publish($bundles)
{
// If no bundles are passed to the command, we'll just gather all
// of the installed bundle names and publish the assets for each
// for each one of the bundles to the public directory.
if (count($bundles) == 0) $bundles = Bundle::all();
$publisher = IoC::resolve('bundle.publisher');
foreach ($bundles as $bundle)
{
$publisher->publish($bundle);
}
}
/**
* Gather all of the bundles from the bundle repository.
*
* @param array $bundles
* @return array
*/
protected function get($bundles)
{
$responses = array();
$repository = IoC::resolve('bundle.repository');
// This method is primarily responsible for gathering the data
// for all bundles that need to be installed. This allows us
// to verify the existence of the bundle before even getting
// started on the actual installation process.
foreach ($bundles as $bundle)
{
// First we'll call the bundle repository to gather the bundle data
// array, which contains all of the information needed to install
// the bundle into the application. We'll verify that the bundle
// exists and the API is responding for each bundle.
$response = $repository->get($bundle);
if ( ! $response)
{
throw new \Exception("The bundle API is not responding.");
}
if ($response['status'] == 'not-found')
{
throw new \Exception("There is not a bundle named [$bundle].");
}
// If the bundle was retrieved successfully, we will add it to
// our array of bundles, as well as merge all of the bundle's
// dependencies into the array of responses so that they are
// installed along with the consuming dependency.
$bundle = $response['bundle'];
$responses[] = $bundle;
$responses = array_merge($responses, $this->get($bundle['dependencies']));
}
return $responses;
}
}
\ No newline at end of file
<?php namespace Laravel\CLI\Tasks\Bundle\Providers;
class Github implements Provider {
/**
* Install the given bundle into the application.
*
* @param string $bundle
* @return void
*/
public function install($bundle)
{
$repository = "git://github.com/{$bundle['location']}.git";
// We need to just extract the basename of the bundle path when
// adding the submodule. Of course, we can't add a submodule to
// a location outside of the Git repository, so we don't need
// the full bundle path. We'll just take the basename in case
// the bundle directory has been renamed.
$path = basename(BUNDLE_PATH).'/';
passthru('git submodule add '.$repository.' '.$path.$bundle['name']);
passthru('git submodule update');
}
}
\ No newline at end of file
<?php namespace Laravel\CLI\Tasks\Bundle\Providers;
interface Provider {
/**
* Install the given bundle into the application.
*
* @param string $bundle
* @return void
*/
public function install($bundle);
}
\ No newline at end of file
<?php namespace Laravel\CLI\Tasks\Bundle;
use Laravel\Bundle;
use FilesystemIterator;
class Publisher {
/**
* Publish a bundle's assets to the public directory.
*
* @param string $bundle
* @return void
*/
public function publish($bundle)
{
$this->move($bundle, $this->from($bundle), $this->to($bundle));
echo "Assets published for bundle [$bundle].".PHP_EOL;
}
/**
* Copy the contents of a bundle's assets to the public folder.
*
* @param string $bundle
* @param string $source
* @param string $destination
* @return void
*/
protected function move($bundle, $source, $destination)
{
if ( ! is_dir($source)) return;
// First we need to create the destination directory if it doesn't
// already exists. This directory hosts all of the assets we copy
// from the installed bundle's source directory.
if ( ! is_dir($destination))
{
mkdir($destination);
}
$items = new FilesystemIterator($source, FilesystemIterator::SKIP_DOTS);
foreach ($items as $item)
{
// If the file system item is a directory, we will recurse the
// function, passing in the item directory. To get the proper
// destination path, we'll replace the root bundle asset
// directory with the root public asset directory.
if ($item->isDir())
{
$path = $item->getRealPath();
$recurse = str_replace($this->from($bundle), $this->to($bundle), $path);
$this->move($bundle, $path, $recurse);
}
// If the file system item is an actual file, we can copy the
// file from the bundle asset directory to the public asset
// directory. The "copy" method will overwrite any existing
// files with the same name.
else
{
copy($item->getRealPath(), $destination.DS.$item->getBasename());
}
}
}
/**
* Get the "to" location of the bundle's assets.
*
* @param string $bundle
* @return string
*/
protected function to($bundle)
{
return PUBLIC_PATH.'bundles'.DS.$bundle.DS;
}
/**
* Get the "from" location of the bundle's assets.
*
* @param string $bundle
* @return string
*/
protected function from($bundle)
{
return Bundle::path($bundle).'public';
}
}
\ No newline at end of file
<?php namespace Laravel\CLI\Tasks\Bundle;
class Repository {
/**
* The root of the Laravel bundle API.
*
* @var string
*/
protected $api = 'http://bundles.laravel.com/api/';
/**
* Get the decoded JSON information for a bundle.
*
* @param string|int $bundle
* @return array
*/
public function get($bundle)
{
// The Bundle API will return a JSON string that we can decode and
// pass back to the consumer. The decoded array will contain info
// regarding the bundle's provider and location, as well as all
// of the bundle's dependencies.
$bundle = @file_get_contents($this->api.$bundle);
return json_decode($bundle, true);
}
}
\ No newline at end of file
<?php namespace Laravel\CLI\Tasks\Migrate;
use Laravel\Database as DB;
class Database {
/**
* Log a migration in the migration table.
*
* @param string $bundle
* @param string $name
* @param int $batch
* @return void
*/
public function log($bundle, $name, $batch)
{
$this->table()->insert(compact('bundle', 'name', 'batch'));
}
/**
* Delete a row from the migration table.
*
* @param string $bundle
* @param string $name
* @return void
*/
public function delete($bundle, $name)
{
$this->table()->where_bundle_and_name($bundle, $name)->delete();
}
/**
* Return an array of the last batch of migrations.
*
* @return array
*/
public function last()
{
$table = $this->table();
// First we need to grab the last batch ID from the migration table,
// as this will allow us to grab the lastest batch of migrations
// that need to be run for a rollback command.
$id = $this->batch();
// Once we have the batch ID, we will pull all of the rows for that
// batch. Then we can feed the results into the resolve method to
// get the migration instances for the command.
return $table->where_batch($id)->order_by('name', 'desc')->get();
}
/**
* Get all of the migrations that have run for a bundle.
*
* @param string $bundle
* @return array
*/
public function ran($bundle)
{
return $this->table()->where_bundle($bundle)->lists('name');
}
/**
* Get the maximum batch ID from the migration table.
*
* @return int
*/
public function batch()
{
return $this->table()->max('batch');
}
/**
* Get a database query instance for the migration table.
*
* @return Query
*/
protected function table()
{
return DB::connection()->table('laravel_migrations');
}
}
\ No newline at end of file
<?php namespace Laravel\CLI\Tasks\Migrate;
use Laravel\Str;
use Laravel\File;
use Laravel\Bundle;
use Laravel\CLI\Tasks\Task;
use Laravel\Database\Schema;
class Migrator extends Task {
/**
* The migration resolver instance.
*
* @var Resolver
*/
protected $resolver;
/**
* The migration database instance.
*
* @var Database
*/
protected $database;
/**
* Create a new instance of the Migrator CLI task.
*
* @param Resolver $resolver
* @param Database $database
* @return void
*/
public function __construct(Resolver $resolver, Database $database)
{
$this->resolver = $resolver;
$this->database = $database;
}
/**
* Run a database migration command.
*
* @param array $arguments
* @return void
*/
public function run($arguments = array())
{
// If no arguments were passed to the task, we will just migrate
// to the latest version across all bundles. Otherwise, we will
// parse the arguments to determine the bundle for which the
// database migrations should be run.
if (count($arguments) == 0)
{
$this->migrate();
}
else
{
$this->migrate(array_get($arguments, 0));
}
}
/**
* Run the outstanding migrations for a given bundle.
*
* @param string $bundle
* @param int $version
* @return void
*/
public function migrate($bundle = null, $version = null)
{
$migrations = $this->resolver->outstanding($bundle);
if (count($migrations) == 0)
{
echo "No outstanding migrations.";
return;
}
// We need to grab the latest batch ID and increment it
// by one. This allows us to group the migrations such
// that we can easily determine which migrations need
// to be rolled back for a given command.
$batch = $this->database->batch() + 1;
foreach ($migrations as $migration)
{
$migration['migration']->up();
echo 'Migrated: '.$this->display($migration).PHP_EOL;
// After running a migration, we log its execution in the
// migration table so that we can easily determine which
// migrations we will need to reverse on a rollback.
$this->database->log($migration['bundle'], $migration['name'], $batch);
}
}
/**
* Rollback the latest migration command.
*
* @param array $arguments
* @return bool
*/
public function rollback($arguments = array())
{
$migrations = $this->resolver->last();
if (count($migrations) == 0)
{
echo "Nothing to rollback.";
return false;
}
// The "last" method on the resolver returns an array of migrations,
// along with their bundles and names. We will iterate through each
// migration and run the "down" method, removing them from the
// database as we go.
foreach ($migrations as $migration)
{
$migration['migration']->down();
echo 'Rolled back: '.$this->display($migration).PHP_EOL;
// By only removing the migration after it has successfully rolled back,
// we can re-run the rollback command in the event of any errors with
// the migration. When we re-run, only the migrations that have not
// been rolled-back for the batch will still be in the database.
$this->database->delete($migration['bundle'], $migration['name']);
}
return true;
}
/**
* Rollback all of the executed migrations.
*
* @param array $arguments
* @return void
*/
public function reset($arguments = array())
{
while ($this->rollback()) {};
}
/**
* Install the database tables used by the migration system.
*
* @return void
*/
public function install()
{
Schema::table('laravel_migrations', function($table)
{
$table->create();
// Migrations can be run for a specific bundle, so we'll use
// the bundle name and string migration name as an unique ID
// for the migrations, allowing us to easily identify which
// migrations have been run for each bundle.
$table->string('bundle');
$table->string('name');
// When running a migration command, we will store a batch
// ID with each of the rows on the table. This will allow
// us to grab all of the migrations that were run for the
// last command when performing rollbacks.
$table->integer('batch');
$table->primary(array('bundle', 'name'));
});
echo "Migration table created successfully.";
}
/**
* Generate a new migration file.
*
* @param array $arguments
* @return void
*/
public function make($arguments = array())
{
if (count($arguments) == 0)
{
throw new \Exception("I need to know what to name the migration.");
}
list($bundle, $migration) = Bundle::parse($arguments[0]);
// The migration path is prefixed with the UNIX timestamp, which
// is a better way of ordering migrations than a simple integer
// incrementation, since developers may start working on the
// next migration at the same time unknowingly.
$date = date('Y_m_d').'_'.time();
$path = Bundle::path($bundle).'migrations/'.$date.'_'.$migration.EXT;
File::put($path, $this->stub($bundle, $migration));
echo "Great! New migration created!";
}
/**
* Get the stub migration with the proper class name.
*
* @param string $bundle
* @param string $migration
* @return string
*/
protected function stub($bundle, $migration)
{
$stub = File::get(SYS_PATH.'cli/tasks/migrate/stub'.EXT);
// The class name is formatted simialrly to tasks and controllers,
// where the bundle name is prefixed to the class if it is not in
// the default bundle. However, unlike tasks, there is nothing
// appended to the class name since they're already unique.
$class = Bundle::class_prefix($bundle).Str::classify($migration);
return str_replace('{{class}}', $class, $stub);
}
/**
* Get the migration bundle and name for display.
*
* @param array $migration
* @return string
*/
protected function display($migration)
{
return $migration['bundle'].'/'.$migration['name'];
}
}
\ No newline at end of file
<?php namespace Laravel\CLI\Tasks\Migrate;
use Laravel\Bundle;
class Resolver {
/**
* The migration database instance.
*
* @var Database
*/
protected $database;
/**
* Create a new instance of the migration resolver.
*
* @param Database $datbase
* @return void
*/
public function __construct(Database $database)
{
$this->database = $database;
}
/**
* Resolve all of the outstanding migrations for a bundle.
*
* @param string $bundle
* @return array
*/
public function outstanding($bundle = null)
{
$migrations = array();
// If no bundle was given to the command, we'll grab every bundle for
// the application, including the "application" bundle, which is not
// returned by "all" method on the Bundle class.
if (is_null($bundle))
{
$bundles = array_merge(Bundle::all(), array('application'));
}
else
{
$bundles = array($bundle);
}
foreach ($bundles as $bundle)
{
// First we need to grab all of the migrations that have already
// run for this bundle, as well as all of the migration files
// for the bundle. Once we have these, we can determine which
// migrations are still outstanding.
$ran = $this->database->ran($bundle);
$files = $this->migrations($bundle);
// To find outstanding migrations, we will simply iterate over
// the migration files and add the files that do not exist in
// the array of ran migrations to the outstanding array.
foreach ($files as $key => $name)
{
if ( ! in_array($name, $ran))
{
$migrations[] = compact('bundle', 'name');
}
}
}
return $this->resolve($migrations);
}
/**
* Resolve an array of the last batch of migrations.
*
* @return array
*/
public function last()
{
return $this->resolve($this->database->last());
}
/**
* Resolve an array of migration instances.
*
* @param array $migrations
* @return array
*/
protected function resolve($migrations)
{
$instances = array();
foreach ($migrations as $migration)
{
$migration = (array) $migration;
// The migration array contains the bundle name, so we will get the
// path to the bundle's migrations and resolve an instance of the
// migration using the name.
$bundle = $migration['bundle'];
$path = Bundle::path($bundle).'migrations/';
// Migrations are not resolved through the auto-loader, so we will
// manually instantiate the migration class instances for each of
// the migration names we're given.
$name = $migration['name'];
require_once $path.$name.EXT;
// Since the migration name will begin with the numeric ID, we'll
// slice off the ID so we are left with the migration class name.
// The IDs are for sorting when resolving outstanding migrations.
//
// Migrations that exist within bundles other than the default
// will be prefixed with the bundle name to avoid any possible
// naming collisions with other bundle's migrations.
$prefix = Bundle::class_prefix($bundle);
$class = $prefix.substr($name, 22);
$migration = new $class;
// When adding to the array of instances, we will actually
// add the migration instance, the bundle, and the name.
// This allows the migrator to log the bundle and name
// when the migration is executed.
$instances[] = compact('bundle', 'name', 'migration');
}
return $instances;
}
/**
* Grab all of the migration filenames for a bundle.
*
* @param string $bundle
* @return array
*/
protected function migrations($bundle)
{
$files = glob(Bundle::path($bundle).'migrations/*_*'.EXT);
// Once we have the array of files in the migration directory,
// we'll take the basename of the file and remove the PHP file
// extension, which isn't needed.
foreach ($files as &$file)
{
$file = str_replace(EXT, '', basename($file));
}
// We'll also sort the files so that the earlier migrations
// will be at the front of the array and will be resolved
// first by this class' resolve method.
sort($files);
return $files;
}
}
\ No newline at end of file
<?php
class {{class}} {
/**
* Make changes to the database.
*
* @return void
*/
public function up()
{
//
}
/**
* Revert the changes to the database.
*
* @return void
*/
public function down()
{
//
}
}
\ No newline at end of file
<?php namespace Laravel\CLI\Tasks;
abstract class Task {}
\ No newline at end of file
<?php namespace Laravel; use Closure; <?php namespace Laravel; defined('APP_PATH') or die('No direct script access.');
use Closure;
class Config { class Config {
/** /**
* All of the loaded configuration items. * All of the loaded configuration items.
* *
* The configuration arrays are keyed by their owning file name. * The configuration arrays are keyed by their owning bundle and file.
* *
* @var array * @var array
*/ */
public static $items = array(); public static $items = array();
/** /**
* The paths to the configuration files. * A cache of the loaded configuration items.
* *
* @var array * @var array
*/ */
public static $paths = array(SYS_CONFIG_PATH, CONFIG_PATH, ENV_CONFIG_PATH); protected static $cache = array();
/** /**
* Determine if a configuration item or file exists. * Determine if a configuration item or file exists.
...@@ -25,7 +27,7 @@ class Config { ...@@ -25,7 +27,7 @@ class Config {
* // Determine if the "session" configuration file exists * // Determine if the "session" configuration file exists
* $exists = Config::has('session'); * $exists = Config::has('session');
* *
* // Determine if the "timezone" option exists in the "application" configuration * // Determine if the "timezone" option exists in the configuration
* $exists = Config::has('application.timezone'); * $exists = Config::has('application.timezone');
* </code> * </code>
* *
...@@ -46,29 +48,38 @@ class Config { ...@@ -46,29 +48,38 @@ class Config {
* // Get the "session" configuration array * // Get the "session" configuration array
* $session = Config::get('session'); * $session = Config::get('session');
* *
* // Get a configuration item from a bundle's configuration file
* $name = Config::get('admin::names.first');
*
* // Get the "timezone" option from the "application" configuration file * // Get the "timezone" option from the "application" configuration file
* $timezone = Config::get('application.timezone'); * $timezone = Config::get('application.timezone');
* </code> * </code>
* *
* @param string $key * @param string $key
* @param string $default
* @return array * @return array
*/ */
public static function get($key, $default = null) public static function get($key)
{ {
list($file, $key) = static::parse($key); // First, we'll check the keyed cache of configuration items, as this will
// be the fastest method of retrieving the configuration option. After an
if ( ! static::load($file)) // item is retrieved, it is always stored in the cache by its key.
if (array_key_exists($key, static::$cache))
{ {
return ($default instanceof Closure) ? call_user_func($default) : $default; return static::$cache[$key];
} }
$items = static::$items[$file]; list($bundle, $file, $item) = static::parse($key);
if ( ! static::load($bundle, $file)) return;
$items = static::$items[$bundle][$file];
// If a specific configuration item was not requested, the key will be null, // If a specific configuration item was not requested, the key will be null,
// meaning we need to return the entire array of configuration item from the // meaning we need to return the entire array of configuration item from the
// requested configuration file. Otherwise we can return the item. // requested configuration file. Otherwise we can return the item.
return (is_null($key)) ? $items : Arr::get($items, $key, $default); $value = (is_null($item)) ? $items : array_get($items, $item);
return static::$cache[$key] = $value;
} }
/** /**
...@@ -78,6 +89,9 @@ class Config { ...@@ -78,6 +89,9 @@ class Config {
* // Set the "session" configuration array * // Set the "session" configuration array
* Config::set('session', $array); * Config::set('session', $array);
* *
* // Set a configuration option that belongs by a bundle
* Config::set('admin::names.first', 'Taylor');
*
* // Set the "timezone" option in the "application" configuration file * // Set the "timezone" option in the "application" configuration file
* Config::set('application.timezone', 'UTC'); * Config::set('application.timezone', 'UTC');
* </code> * </code>
...@@ -88,61 +102,53 @@ class Config { ...@@ -88,61 +102,53 @@ class Config {
*/ */
public static function set($key, $value) public static function set($key, $value)
{ {
list($file, $key) = static::parse($key); static::$cache[$key] = $value;
static::load($file);
if (is_null($key))
{
Arr::set(static::$items, $file, $value);
}
else
{
Arr::set(static::$items[$file], $key, $value);
}
} }
/** /**
* Parse a configuration key and return its file and key segments. * Parse a key and return its bundle, file, and key segments.
* *
* The first segment of a configuration key represents the configuration * Configuration items are named using the {bundle}::{file}.{item} convention.
* file, while the remaining segments represent an item within that file.
* If no item segment is present, null will be returned for the item value
* indicating that the entire configuration array should be returned.
* *
* @param string $key * @param string $key
* @return array * @return array
*/ */
protected static function parse($key) protected static function parse($key)
{ {
$segments = explode('.', $key); $bundle = Bundle::name($key);
$segments = explode('.', Bundle::element($key));
// If there are not at least two segments in the array, it means that the
// developer is requesting the entire configuration array to be returned.
// If that is the case, we'll make the item field of the array "null".
if (count($segments) >= 2) if (count($segments) >= 2)
{ {
return array($segments[0], implode('.', array_slice($segments, 1))); return array($bundle, $segments[0], implode('.', array_slice($segments, 1)));
} }
else else
{ {
return array($segments[0], null); return array($bundle, $segments[0], null);
} }
} }
/** /**
* Load all of the configuration items from a configuration file. * Load all of the configuration items from a configuration file.
* *
* @param string $bundle
* @param string $file * @param string $file
* @return bool * @return bool
*/ */
public static function load($file) public static function load($bundle, $file)
{ {
if (isset(static::$items[$file])) return true; if (isset(static::$items[$bundle][$file])) return true;
$config = array(); $config = array();
// Configuration files cascade. Typically, the system configuration array is // Configuration files cascade. Typically, the bundle configuration array is
// loaded first, followed by the application array, providing the convenient // loaded first, followed by the environment array, providing the convenient
// cascading of configuration options from system to application. // cascading of configuration options across environments.
foreach (static::$paths as $directory) foreach (static::paths($bundle) as $directory)
{ {
if ($directory !== '' and file_exists($path = $directory.$file.EXT)) if ($directory !== '' and file_exists($path = $directory.$file.EXT))
{ {
...@@ -150,9 +156,37 @@ class Config { ...@@ -150,9 +156,37 @@ class Config {
} }
} }
if (count($config) > 0) static::$items[$file] = $config; if (count($config) > 0)
{
static::$items[$bundle][$file] = $config;
}
return isset(static::$items[$bundle][$file]);
}
/**
* Get the array of configuration paths that should be searched for a bundle.
*
* @param string $bundle
* @return array
*/
protected static function paths($bundle)
{
$paths[] = Bundle::path($bundle).'config/';
// Configuration files can be made specific for a given environment. If an
// environment has been set, we will merge the environment configuration
// in last, so that it overrides all other options.
//
// This allows the developer to quickly and easily create configurations
// for various scenarios, such as local development and production,
// without constantly changing configuration files.
if (isset($_SERVER['LARAVEL_ENV']))
{
$paths[] = $paths[count($paths) - 1].$_SERVER['LARAVEL_ENV'].'/';
}
return isset(static::$items[$file]); return $paths;
} }
} }
\ No newline at end of file
<?php
return array(
'/æ|ǽ/' => 'ae',
'/œ/' => 'oe',
'/À|Á|Â|Ã|Ä|Å|Ǻ|Ā|Ă|Ą|Ǎ|А/' => 'A',
'/à|á|â|ã|ä|å|ǻ|ā|ă|ą|ǎ|ª|а/' => 'a',
'/Б/' => 'B',
'/б/' => 'b',
'/Ç|Ć|Ĉ|Ċ|Č|Ц/' => 'C',
'/ç|ć|ĉ|ċ|č|ц/' => 'c',
'/Ð|Ď|Đ|Д/' => 'Dj',
'/ð|ď|đ|д/' => 'dj',
'/È|É|Ê|Ë|Ē|Ĕ|Ė|Ę|Ě|Е|Ё|Э/' => 'E',
'/è|é|ê|ë|ē|ĕ|ė|ę|ě|е|ё|э/' => 'e',
'/Ф/' => 'F',
'/ƒ|ф/' => 'f',
'/Ĝ|Ğ|Ġ|Ģ|Г/' => 'G',
'/ĝ|ğ|ġ|ģ|г/' => 'g',
'/Ĥ|Ħ|Х/' => 'H',
'/ĥ|ħ|х/' => 'h',
'/Ì|Í|Î|Ï|Ĩ|Ī|Ĭ|Ǐ|Į|İ|И/' => 'I',
'/ì|í|î|ï|ĩ|ī|ĭ|ǐ|į|ı|и/' => 'i',
'/Ĵ|Й/' => 'J',
'/ĵ|й/' => 'j',
'/Ķ|К/' => 'K',
'/ķ|к/' => 'k',
'/Ĺ|Ļ|Ľ|Ŀ|Ł|Л/' => 'L',
'/ĺ|ļ|ľ|ŀ|ł|л/' => 'l',
'/М/' => 'M',
'/м/' => 'm',
'/Ñ|Ń|Ņ|Ň|Н/' => 'N',
'/ñ|ń|ņ|ň|ʼn|н/' => 'n',
'/Ö|Ò|Ó|Ô|Õ|Ō|Ŏ|Ǒ|Ő|Ơ|Ø|Ǿ|О/' => 'O',
'/ö|ò|ó|ô|õ|ō|ŏ|ǒ|ő|ơ|ø|ǿ|º|о/' => 'o',
'/П/' => 'P',
'/п/' => 'p',
'/Ŕ|Ŗ|Ř|Р/' => 'R',
'/ŕ|ŗ|ř|р/' => 'r',
'/Ś|Ŝ|Ş|Š|С/' => 'S',
'/ś|ŝ|ş|š|ſ|с/' => 's',
'/Ţ|Ť|Ŧ|Т/' => 'T',
'/ţ|ť|ŧ|т/' => 't',
'/Ù|Ú|Û|Ũ|Ū|Ŭ|Ů|Ű|Ų|Ư|Ǔ|Ǖ|Ǘ|Ǚ|Ǜ|У/' => 'U',
'/ù|ú|û|ũ|ū|ŭ|ů|ű|ų|ư|ǔ|ǖ|ǘ|ǚ|ǜ|у/' => 'u',
'/В/' => 'V',
'/в/' => 'v',
'/Ý|Ÿ|Ŷ|Ы/' => 'Y',
'/ý|ÿ|ŷ|ы/' => 'y',
'/Ŵ/' => 'W',
'/ŵ/' => 'w',
'/Ź|Ż|Ž|З/' => 'Z',
'/ź|ż|ž|з/' => 'z',
'/Æ|Ǽ/' => 'AE',
'/ß/'=> 'ss',
'/IJ/' => 'IJ',
'/ij/' => 'ij',
'/Œ/' => 'OE',
'/Ч/' => 'Ch',
'/ч/' => 'ch',
'/Ю/' => 'Ju',
'/ю/' => 'ju',
'/Я/' => 'Ja',
'/я/' => 'ja',
'/Ш/' => 'Sh',
'/ш/' => 'sh',
'/Щ/' => 'Shch',
'/щ/' => 'shch',
'/Ж/' => 'Zh',
'/ж/' => 'zh',
);
\ No newline at end of file
<?php namespace Laravel; use Closure; <?php namespace Laravel; defined('APP_PATH') or die('No direct script access.');
if (trim(Config::$items['application']['key']) === '') use Closure;
if (trim(Config::get('application.key')) === '')
{ {
throw new \LogicException('The cookie class may not be used without an application key.'); throw new \Exception('The cookie class may not be used without an application key.');
} }
class Cookie { class Cookie {
...@@ -21,23 +23,31 @@ class Cookie { ...@@ -21,23 +23,31 @@ class Cookie {
/** /**
* Get the value of a cookie. * Get the value of a cookie.
* *
* <code>
* // Get the value of the "favorite" cookie
* $favorite = Cookie::get('favorite');
*
* // Get the value of a cookie or return a default value if it doesn't exist
* $favorite = Cookie::get('framework', 'Laravel');
* </code>
*
* @param string $name * @param string $name
* @param mixed $default * @param mixed $default
* @return string * @return string
*/ */
public static function get($name, $default = null) public static function get($name, $default = null)
{ {
$value = Arr::get($_COOKIE, $name); $value = array_get($_COOKIE, $name);
if ( ! is_null($value)) if ( ! is_null($value) and isset($value[40]) and $value[40] == '~')
{
// All Laravel managed cookies are "signed" with a fingerprint hash.
// The hash serves to verify that the contents of the cookie have not
// been modified by the user. We can verify the integrity of the cookie
// by extracting the value and re-hashing it, then comparing that hash
// against the hash stored in the cookie.
if (isset($value[40]) and $value[40] === '~')
{ {
// The hash signature and the cookie value are separated by a tilde
// character for convenience. To separate the hash and the contents
// we can simply expode on that character.
//
// By re-feeding the cookie value into the "sign" method, we should
// be able to generate a hash that matches the one taken out of the
// cookie. If they don't match, the cookie value has been changed.
list($hash, $value) = explode('~', $value, 2); list($hash, $value) = explode('~', $value, 2);
if (static::hash($name, $value) === $hash) if (static::hash($name, $value) === $hash)
...@@ -45,75 +55,74 @@ class Cookie { ...@@ -45,75 +55,74 @@ class Cookie {
return $value; return $value;
} }
} }
}
return ($default instanceof Closure) ? call_user_func($default) : $default; return value($default);
} }
/** /**
* Set a "permanent" cookie. The cookie will last for one year. * Set the value of a cookie.
*
* If the response headers have already been sent, the cookie will not be set.
*
* <code>
* // Set the value of the "favorite" cookie
* Cookie::put('favorite', 'Laravel');
*
* // Set the value of the "favorite" cookie for twenty minutes
* Cookie::put('favorite', 'Laravel', 20);
* </code>
* *
* @param string $name * @param string $name
* @param string $value * @param string $value
* @param int $minutes
* @param string $path * @param string $path
* @param string $domain * @param string $domain
* @param bool $secure * @param bool $secure
* @param bool $http_only
* @return bool * @return bool
*/ */
public static function forever($name, $value, $path = '/', $domain = null, $secure = false, $http_only = false) public static function put($name, $value, $minutes = 0, $path = '/', $domain = null, $secure = false)
{ {
return static::put($name, $value, 525600, $path, $domain, $secure, $http_only); if (headers_sent()) return false;
$time = ($minutes !== 0) ? time() + ($minutes * 60) : 0;
return setcookie($name, static::sign($name, $value), $time, $path, $domain, $secure);
} }
/** /**
* Set the value of a cookie. * Set a "permanent" cookie. The cookie will last for one year.
*
* If a negative number of minutes is specified, the cookie will be deleted.
* *
* This method's signature is very similar to the PHP setcookie method. * <code>
* However, you simply need to pass the number of minutes for which you * // Set a cookie that should last one year
* wish the cookie to be valid. No funky time calculation is required. * Cookie::forever('favorite', 'Blue');
* </code>
* *
* @param string $name * @param string $name
* @param string $value * @param string $value
* @param int $minutes
* @param string $path * @param string $path
* @param string $domain * @param string $domain
* @param bool $secure * @param bool $secure
* @param bool $http_only
* @return bool * @return bool
*/ */
public static function put($name, $value, $minutes = 0, $path = '/', $domain = null, $secure = false, $http_only = false) public static function forever($name, $value, $path = '/', $domain = null, $secure = false)
{
if (headers_sent()) return false;
$time = ($minutes !== 0) ? time() + ($minutes * 60) : 0;
$value = static::hash($name, $value).'~'.$value;
if ($minutes < 0)
{
unset($_COOKIE[$name]);
}
else
{ {
$_COOKIE[$name] = $value; return static::put($name, $value, 525600, $path, $domain, $secure);
} }
return setcookie($name, $value, $time, $path, $domain, $secure, $http_only); /**
* Generate a cookie signature based on the contents.
*
* @param string $name
* @param string $value
* @return string
*/
protected static function sign($name, $value)
{
return static::hash($name, $value).'~'.$value;
} }
/** /**
* Generate a cookie hash. * Generate a cookie hash based on the contents.
*
* Cookie salts are used to verify that the contents of the cookie have not
* been modified by the user, since they serve as a fingerprint of the cookie
* contents. The application key is used to salt the salts.
*
* When the cookie is read using the "get" method, the value will be extracted
* from the cookie and hashed, if the hash in the cookie and the hashed value
* do not match, we know the cookie has been changed on the client.
* *
* @param string $name * @param string $name
* @param string $value * @param string $value
...@@ -121,7 +130,7 @@ class Cookie { ...@@ -121,7 +130,7 @@ class Cookie {
*/ */
protected static function hash($name, $value) protected static function hash($name, $value)
{ {
return sha1($name.$value.Config::$items['application']['key']); return sha1($name.$value.Config::get('application.key'));
} }
/** /**
...@@ -131,12 +140,11 @@ class Cookie { ...@@ -131,12 +140,11 @@ class Cookie {
* @param string $path * @param string $path
* @param string $domain * @param string $domain
* @param bool $secure * @param bool $secure
* @param bool $http_only
* @return bool * @return bool
*/ */
public static function forget($name, $path = '/', $domain = null, $secure = false, $http_only = false) public static function forget($name, $path = '/', $domain = null, $secure = false)
{ {
return static::put($name, null, -2000, $path, $domain, $secure, $http_only); return static::put($name, null, -2000, $path, $domain, $secure);
} }
} }
\ No newline at end of file
...@@ -9,59 +9,22 @@ ...@@ -9,59 +9,22 @@
define('EXT', '.php'); define('EXT', '.php');
define('CRLF', "\r\n"); define('CRLF', "\r\n");
define('BLADE_EXT', '.blade.php'); define('BLADE_EXT', '.blade.php');
define('APP_PATH', realpath($application).'/'); define('CACHE_PATH', STORAGE_PATH.'cache'.DS);
define('PUBLIC_PATH', realpath($public).'/'); define('DATABASE_PATH', STORAGE_PATH.'database'.DS);
define('SYS_PATH', realpath($laravel).'/'); define('SESSION_PATH', STORAGE_PATH.'sessions'.DS);
define('STORAGE_PATH', APP_PATH.'storage/'); define('DEFAULT_BUNDLE', 'application');
define('CACHE_PATH', STORAGE_PATH.'cache/'); define('MB_STRING', (int) function_exists('mb_get_info'));
define('CONFIG_PATH', APP_PATH.'config/');
define('CONTROLLER_PATH', APP_PATH.'controllers/');
define('DATABASE_PATH', STORAGE_PATH.'database/');
define('LANG_PATH', APP_PATH.'language/');
define('LIBRARY_PATH', APP_PATH.'libraries/');
define('MODEL_PATH', APP_PATH.'models/');
define('ROUTE_PATH', APP_PATH.'routes/');
define('SESSION_PATH', STORAGE_PATH.'sessions/');
define('SYS_CONFIG_PATH', SYS_PATH.'config/');
define('VIEW_PATH', APP_PATH.'views/');
/**
* Define the Laravel environment configuration path. This path is used
* by the configuration class to load configuration options specific for
* the server environment, allowing the developer to conveniently change
* configuration options based on the application environment.
*
*/
$environment = '';
if (isset($_SERVER['LARAVEL_ENV']))
{
$environment = CONFIG_PATH.$_SERVER['LARAVEL_ENV'].'/';
}
define('ENV_CONFIG_PATH', $environment);
unset($application, $public, $laravel, $environment);
/** /**
* Require all of the classes that can't be loaded by the auto-loader. * Require all of the classes that can't be loaded by the auto-loader.
* These are typically classes that the auto-loader itself relies upon * These are typically classes that the auto-loader itself relies upon
* to load classes, such as the array and configuration classes. * to load classes, such as the array and configuration classes.
*/ */
require SYS_PATH.'arr'.EXT; require SYS_PATH.'bundle'.EXT;
require SYS_PATH.'config'.EXT; require SYS_PATH.'config'.EXT;
require SYS_PATH.'facades'.EXT; require SYS_PATH.'helpers'.EXT;
require SYS_PATH.'autoloader'.EXT; require SYS_PATH.'autoloader'.EXT;
/**
* Load a few of the core configuration files that are loaded for every
* request to the application. It is quicker to load them manually each
* request rather than parse the keys for every request.
*/
Config::load('application');
Config::load('session');
Config::load('error');
/** /**
* Register the Autoloader's "load" method on the auto-loader stack. * Register the Autoloader's "load" method on the auto-loader stack.
* This method provides the lazy-loading of all class files, as well * This method provides the lazy-loading of all class files, as well
...@@ -75,22 +38,27 @@ spl_autoload_register(array('Laravel\\Autoloader', 'load')); ...@@ -75,22 +38,27 @@ spl_autoload_register(array('Laravel\\Autoloader', 'load'));
* More mappings can also be registered by the developer as needed. * More mappings can also be registered by the developer as needed.
*/ */
Autoloader::$mappings = array( Autoloader::$mappings = array(
'Laravel\\Arr' => SYS_PATH.'arr'.EXT,
'Laravel\\Asset' => SYS_PATH.'asset'.EXT,
'Laravel\\Auth' => SYS_PATH.'auth'.EXT, 'Laravel\\Auth' => SYS_PATH.'auth'.EXT,
'Laravel\\Asset' => SYS_PATH.'asset'.EXT,
'Laravel\\Benchmark' => SYS_PATH.'benchmark'.EXT, 'Laravel\\Benchmark' => SYS_PATH.'benchmark'.EXT,
'Laravel\\Blade' => SYS_PATH.'blade'.EXT, 'Laravel\\Blade' => SYS_PATH.'blade'.EXT,
'Laravel\\Bundle' => SYS_PATH.'bundle'.EXT,
'Laravel\\Cache' => SYS_PATH.'cache'.EXT,
'Laravel\\Config' => SYS_PATH.'config'.EXT, 'Laravel\\Config' => SYS_PATH.'config'.EXT,
'Laravel\\Cookie' => SYS_PATH.'cookie'.EXT, 'Laravel\\Cookie' => SYS_PATH.'cookie'.EXT,
'Laravel\\Crypter' => SYS_PATH.'crypter'.EXT, 'Laravel\\Crypter' => SYS_PATH.'crypter'.EXT,
'Laravel\\Database' => SYS_PATH.'database'.EXT,
'Laravel\\Error' => SYS_PATH.'error'.EXT,
'Laravel\\Event' => SYS_PATH.'event'.EXT,
'Laravel\\File' => SYS_PATH.'file'.EXT, 'Laravel\\File' => SYS_PATH.'file'.EXT,
'Laravel\\Fluent' => SYS_PATH.'fluent'.EXT,
'Laravel\\Form' => SYS_PATH.'form'.EXT, 'Laravel\\Form' => SYS_PATH.'form'.EXT,
'Laravel\\Hash' => SYS_PATH.'hash'.EXT, 'Laravel\\Hash' => SYS_PATH.'hash'.EXT,
'Laravel\\HTML' => SYS_PATH.'html'.EXT, 'Laravel\\HTML' => SYS_PATH.'html'.EXT,
'Laravel\\Inflector' => SYS_PATH.'inflector'.EXT,
'Laravel\\Input' => SYS_PATH.'input'.EXT, 'Laravel\\Input' => SYS_PATH.'input'.EXT,
'Laravel\\IoC' => SYS_PATH.'ioc'.EXT, 'Laravel\\IoC' => SYS_PATH.'ioc'.EXT,
'Laravel\\Lang' => SYS_PATH.'lang'.EXT, 'Laravel\\Lang' => SYS_PATH.'lang'.EXT,
'Laravel\\Log' => SYS_PATH.'log'.EXT,
'Laravel\\Memcached' => SYS_PATH.'memcached'.EXT, 'Laravel\\Memcached' => SYS_PATH.'memcached'.EXT,
'Laravel\\Messages' => SYS_PATH.'messages'.EXT, 'Laravel\\Messages' => SYS_PATH.'messages'.EXT,
'Laravel\\Paginator' => SYS_PATH.'paginator'.EXT, 'Laravel\\Paginator' => SYS_PATH.'paginator'.EXT,
...@@ -99,40 +67,62 @@ Autoloader::$mappings = array( ...@@ -99,40 +67,62 @@ Autoloader::$mappings = array(
'Laravel\\Request' => SYS_PATH.'request'.EXT, 'Laravel\\Request' => SYS_PATH.'request'.EXT,
'Laravel\\Response' => SYS_PATH.'response'.EXT, 'Laravel\\Response' => SYS_PATH.'response'.EXT,
'Laravel\\Section' => SYS_PATH.'section'.EXT, 'Laravel\\Section' => SYS_PATH.'section'.EXT,
'Laravel\\Session' => SYS_PATH.'session'.EXT,
'Laravel\\Str' => SYS_PATH.'str'.EXT, 'Laravel\\Str' => SYS_PATH.'str'.EXT,
'Laravel\\URI' => SYS_PATH.'uri'.EXT, 'Laravel\\URI' => SYS_PATH.'uri'.EXT,
'Laravel\\URL' => SYS_PATH.'url'.EXT, 'Laravel\\URL' => SYS_PATH.'url'.EXT,
'Laravel\\Validator' => SYS_PATH.'validator'.EXT, 'Laravel\\Validator' => SYS_PATH.'validator'.EXT,
'Laravel\\View' => SYS_PATH.'view'.EXT, 'Laravel\\View' => SYS_PATH.'view'.EXT,
'Laravel\\Cache\\Manager' => SYS_PATH.'cache/manager'.EXT,
'Laravel\\Cache\\Drivers\\APC' => SYS_PATH.'cache/drivers/apc'.EXT, 'Laravel\\Cache\\Drivers\\APC' => SYS_PATH.'cache/drivers/apc'.EXT,
'Laravel\\Cache\\Drivers\\Driver' => SYS_PATH.'cache/drivers/driver'.EXT, 'Laravel\\Cache\\Drivers\\Driver' => SYS_PATH.'cache/drivers/driver'.EXT,
'Laravel\\Cache\\Drivers\\File' => SYS_PATH.'cache/drivers/file'.EXT, 'Laravel\\Cache\\Drivers\\File' => SYS_PATH.'cache/drivers/file'.EXT,
'Laravel\\Cache\\Drivers\\Memcached' => SYS_PATH.'cache/drivers/memcached'.EXT, 'Laravel\\Cache\\Drivers\\Memcached' => SYS_PATH.'cache/drivers/memcached'.EXT,
'Laravel\\Cache\\Drivers\\Redis' => SYS_PATH.'cache/drivers/redis'.EXT, 'Laravel\\Cache\\Drivers\\Redis' => SYS_PATH.'cache/drivers/redis'.EXT,
'Laravel\\Cache\\Drivers\\Database' => SYS_PATH.'cache/drivers/database'.EXT,
'Laravel\\CLI\\Command' => SYS_PATH.'cli/command'.EXT,
'Laravel\\CLI\\Tasks\\Task' => SYS_PATH.'cli/tasks/task'.EXT,
'Laravel\\CLI\\Tasks\\Bundle\\Bundler' => SYS_PATH.'cli/tasks/bundle/bundler'.EXT,
'Laravel\\CLI\\Tasks\\Bundle\\Repository' => SYS_PATH.'cli/tasks/bundle/repository'.EXT,
'Laravel\\CLI\\Tasks\\Bundle\\Publisher' => SYS_PATH.'cli/tasks/bundle/publisher'.EXT,
'Laravel\\CLI\\Tasks\\Bundle\\Providers\\Provider' => SYS_PATH.'cli/tasks/bundle/providers/provider'.EXT,
'Laravel\\CLI\\Tasks\\Bundle\\Providers\\Github' => SYS_PATH.'cli/tasks/bundle/providers/github'.EXT,
'Laravel\\CLI\\Tasks\\Migrate\\Migrator' => SYS_PATH.'cli/tasks/migrate/migrator'.EXT,
'Laravel\\CLI\\Tasks\\Migrate\\Resolver' => SYS_PATH.'cli/tasks/migrate/resolver'.EXT,
'Laravel\\CLI\\Tasks\\Migrate\\Database' => SYS_PATH.'cli/tasks/migrate/database'.EXT,
'Laravel\\Database\\Connection' => SYS_PATH.'database/connection'.EXT, 'Laravel\\Database\\Connection' => SYS_PATH.'database/connection'.EXT,
'Laravel\\Database\\Expression' => SYS_PATH.'database/expression'.EXT, 'Laravel\\Database\\Expression' => SYS_PATH.'database/expression'.EXT,
'Laravel\\Database\\Manager' => SYS_PATH.'database/manager'.EXT,
'Laravel\\Database\\Query' => SYS_PATH.'database/query'.EXT, 'Laravel\\Database\\Query' => SYS_PATH.'database/query'.EXT,
'Laravel\\Database\\Schema' => SYS_PATH.'database/schema'.EXT,
'Laravel\\Database\\Grammar' => SYS_PATH.'database/grammar'.EXT,
'Laravel\\Database\\Connectors\\Connector' => SYS_PATH.'database/connectors/connector'.EXT, 'Laravel\\Database\\Connectors\\Connector' => SYS_PATH.'database/connectors/connector'.EXT,
'Laravel\\Database\\Connectors\\MySQL' => SYS_PATH.'database/connectors/mysql'.EXT, 'Laravel\\Database\\Connectors\\MySQL' => SYS_PATH.'database/connectors/mysql'.EXT,
'Laravel\\Database\\Connectors\\Postgres' => SYS_PATH.'database/connectors/postgres'.EXT, 'Laravel\\Database\\Connectors\\Postgres' => SYS_PATH.'database/connectors/postgres'.EXT,
'Laravel\\Database\\Connectors\\SQLite' => SYS_PATH.'database/connectors/sqlite'.EXT, 'Laravel\\Database\\Connectors\\SQLite' => SYS_PATH.'database/connectors/sqlite'.EXT,
'Laravel\\Database\\Eloquent\\Hydrator' => SYS_PATH.'database/eloquent/hydrator'.EXT, 'Laravel\\Database\\Connectors\\SQLServer' => SYS_PATH.'database/connectors/sqlserver'.EXT,
'Laravel\\Database\\Eloquent\\Model' => SYS_PATH.'database/eloquent/model'.EXT, 'Laravel\\Database\\Query\\Grammars\\Grammar' => SYS_PATH.'database/query/grammars/grammar'.EXT,
'Laravel\\Database\\Grammars\\Grammar' => SYS_PATH.'database/grammars/grammar'.EXT, 'Laravel\\Database\\Query\\Grammars\\MySQL' => SYS_PATH.'database/query/grammars/mysql'.EXT,
'Laravel\\Database\\Grammars\\MySQL' => SYS_PATH.'database/grammars/mysql'.EXT, 'Laravel\\Database\\Query\\Grammars\\SQLServer' => SYS_PATH.'database/query/grammars/sqlserver'.EXT,
'Laravel\\Database\\Schema\\Table' => SYS_PATH.'database/schema/table'.EXT,
'Laravel\\Database\\Schema\\Grammars\\Grammar' => SYS_PATH.'database/schema/grammars/grammar'.EXT,
'Laravel\\Database\\Schema\\Grammars\\MySQL' => SYS_PATH.'database/schema/grammars/mysql'.EXT,
'Laravel\\Database\\Schema\\Grammars\\Postgres' => SYS_PATH.'database/schema/grammars/postgres'.EXT,
'Laravel\\Database\\Schema\\Grammars\\SQLServer' => SYS_PATH.'database/schema/grammars/sqlserver'.EXT,
'Laravel\\Database\\Schema\\Grammars\\SQLite' => SYS_PATH.'database/schema/grammars/sqlite'.EXT,
'Laravel\\Routing\\Controller' => SYS_PATH.'routing/controller'.EXT, 'Laravel\\Routing\\Controller' => SYS_PATH.'routing/controller'.EXT,
'Laravel\\Routing\\Filter' => SYS_PATH.'routing/filter'.EXT, 'Laravel\\Routing\\Filter' => SYS_PATH.'routing/filter'.EXT,
'Laravel\\Routing\\Loader' => SYS_PATH.'routing/loader'.EXT, 'Laravel\\Routing\\Filter_Collection' => SYS_PATH.'routing/filter'.EXT,
'Laravel\\Routing\\Route' => SYS_PATH.'routing/route'.EXT, 'Laravel\\Routing\\Route' => SYS_PATH.'routing/route'.EXT,
'Laravel\\Routing\\Router' => SYS_PATH.'routing/router'.EXT, 'Laravel\\Routing\\Router' => SYS_PATH.'routing/router'.EXT,
'Laravel\\Session\\Payload' => SYS_PATH.'session/payload'.EXT, 'Laravel\\Session\\Payload' => SYS_PATH.'session/payload'.EXT,
'Laravel\\Session\\Drivers\\APC' => SYS_PATH.'session/drivers/apc'.EXT, 'Laravel\\Session\\Drivers\\APC' => SYS_PATH.'session/drivers/apc'.EXT,
'Laravel\\Session\\Drivers\\Cookie' => SYS_PATH.'session/drivers/cookie'.EXT, 'Laravel\\Session\\Drivers\\Cookie' => SYS_PATH.'session/drivers/cookie'.EXT,
'Laravel\\Session\\Drivers\\Database' => SYS_PATH.'session/drivers/database'.EXT, 'Laravel\\Session\\Drivers\\Database' => SYS_PATH.'session/drivers/database'.EXT,
'Laravel\\Session\\Drivers\\Driver' => SYS_PATH.'session/drivers/driver'.EXT, 'Laravel\\Session\\Drivers\\Driver' => SYS_PATH.'session/drivers/driver'.EXT,
'Laravel\\Session\\Drivers\\Factory' => SYS_PATH.'session/drivers/factory'.EXT,
'Laravel\\Session\\Drivers\\File' => SYS_PATH.'session/drivers/file'.EXT, 'Laravel\\Session\\Drivers\\File' => SYS_PATH.'session/drivers/file'.EXT,
'Laravel\\Session\\Drivers\\Memcached' => SYS_PATH.'session/drivers/memcached'.EXT, 'Laravel\\Session\\Drivers\\Memcached' => SYS_PATH.'session/drivers/memcached'.EXT,
'Laravel\\Session\\Drivers\\Redis' => SYS_PATH.'session/drivers/redis'.EXT, 'Laravel\\Session\\Drivers\\Redis' => SYS_PATH.'session/drivers/redis'.EXT,
...@@ -140,16 +130,9 @@ Autoloader::$mappings = array( ...@@ -140,16 +130,9 @@ Autoloader::$mappings = array(
); );
/** /**
* Register the default timezone for the application. This will be * Register all of the core class aliases. These aliases provide a
* the default timezone used by all date / timezone functions in * convenient way of working with the Laravel core classes without
* the entire application. * having to worry about the namespacing. The developer is also
* free to remove aliases when they extend core classes.
*/ */
date_default_timezone_set(Config::$items['application']['timezone']); Autoloader::$aliases = Config::get('application.aliases');
\ No newline at end of file
/**
* Define a few global, convenient functions. These functions
* provide short-cuts for things like the retrieval of language
* lines and HTML::entities. They just make our lives as devs a
* little sweeter and more enjoyable.
*/
require SYS_PATH.'helpers'.EXT;
\ No newline at end of file
<?php namespace Laravel; <?php namespace Laravel; defined('APP_PATH') or die('No direct script access.');
if (trim(Config::$items['application']['key']) === '') if (trim(Config::get('application.key')) === '')
{ {
throw new \LogicException('The encryption class may not be used without an application key.'); throw new \Exception('The Crypter class may not be used without an application key.');
} }
class Crypter { class Crypter {
...@@ -12,28 +12,19 @@ class Crypter { ...@@ -12,28 +12,19 @@ class Crypter {
* *
* @var string * @var string
*/ */
protected static $cipher = MCRYPT_RIJNDAEL_256; public static $cipher = MCRYPT_RIJNDAEL_256;
/** /**
* The encryption mode. * The encryption mode.
* *
* @var string * @var string
*/ */
protected static $mode = MCRYPT_MODE_CBC; public static $mode = MCRYPT_MODE_CBC;
/** /**
* Encrypt a string using Mcrypt. * Encrypt a string using Mcrypt.
* *
* The given string will be encrypted using AES-256 encryption for a high * The string will be encrypted using the AES-256 scheme and will be base64 encoded.
* degree of security. The returned string will also be base64 encoded.
*
* Mcrypt must be installed on your machine before using this method, and
* an application key must be specified in the application configuration.
*
* <code>
* // Encrypt a string using the Mcrypt PHP extension
* $encrypted = Crypter::encrpt('secret');
* </code>
* *
* @param string $value * @param string $value
* @return string * @return string
...@@ -50,26 +41,27 @@ class Crypter { ...@@ -50,26 +41,27 @@ class Crypter {
/** /**
* Decrypt a string using Mcrypt. * Decrypt a string using Mcrypt.
* *
* The given encrypted value must have been encrypted using Laravel and
* the application key specified in the application configuration file.
*
* Mcrypt must be installed on your machine before using this method.
*
* @param string $value * @param string $value
* @return string * @return string
*/ */
public static function decrypt($value) public static function decrypt($value)
{ {
if (($value = base64_decode($value)) === false) $value = base64_decode($value);
{
throw new \InvalidArgumentException('Input value is not valid base64 data.');
}
// To decrypt the value, we first need to extract the input vector and
// the encrypted value. The input vector size varies across different
// encryption ciphers and modes, so we will get the correct size for
// the cipher and mode being used by the class.
$iv = substr($value, 0, static::iv_size()); $iv = substr($value, 0, static::iv_size());
$value = substr($value, static::iv_size()); $value = substr($value, static::iv_size());
return rtrim(mcrypt_decrypt(static::$cipher, static::key(), $value, static::$mode, $iv), "\0"); // Once we have the input vector and the value, we can give them both
// to Mcrypt for decryption. The value is sometimes padded with \0,
// so we will trim all of the padding characters from the string.
$key = static::key();
return rtrim(mcrypt_decrypt(static::$cipher, $key, $value, static::$mode, $iv), "\0");
} }
/** /**
...@@ -89,7 +81,7 @@ class Crypter { ...@@ -89,7 +81,7 @@ class Crypter {
*/ */
protected static function key() protected static function key()
{ {
return Config::$items['application']['key']; return Config::get('application.key');
} }
} }
\ No newline at end of file
<?php namespace Laravel\Database; <?php namespace Laravel;
use Laravel\IoC; use Laravel\Database\Expression;
use Laravel\Config; use Laravel\Database\Connection;
class Manager { class Database {
/** /**
* The established database connections. * The established database connections.
* *
* @var array * @var array
*/ */
protected static $connections = array(); public static $connections = array();
/** /**
* Get a database connection. * Get a database connection.
...@@ -38,7 +38,7 @@ class Manager { ...@@ -38,7 +38,7 @@ class Manager {
if (is_null($config)) if (is_null($config))
{ {
throw new \OutOfBoundsException("Connection is not defined for [$connection]."); throw new \Exception("Database connection is not defined for [$connection].");
} }
static::$connections[$connection] = new Connection(static::connect($config), $config); static::$connections[$connection] = new Connection(static::connect($config), $config);
...@@ -55,26 +55,12 @@ class Manager { ...@@ -55,26 +55,12 @@ class Manager {
*/ */
protected static function connect($config) protected static function connect($config)
{ {
// We allow the developer to place a "connector" option in the database
// configuration, which should have a Closure value. If the connector
// is present, we will use the Closure to retrieve the PDO connection
// to the database. This allows the flexiblity to connect to database
// systems that are not officially supported by the the framework.
if (isset($config['connector']))
{
return call_user_func($config['connector'], $config);
}
return static::connector($config['driver'])->connect($config); return static::connector($config['driver'])->connect($config);
} }
/** /**
* Create a new database connector instance. * Create a new database connector instance.
* *
* The database connectors are responsible for simply establishing a PDO
* database connection given a configuration. This allows us to easily
* drop in support for new database systems by writing a connector.
*
* @param string $driver * @param string $driver
* @return Connector * @return Connector
*/ */
...@@ -83,16 +69,19 @@ class Manager { ...@@ -83,16 +69,19 @@ class Manager {
switch ($driver) switch ($driver)
{ {
case 'sqlite': case 'sqlite':
return new Connectors\SQLite(DATABASE_PATH); return new Database\Connectors\SQLite;
case 'mysql': case 'mysql':
return new Connectors\MySQL; return new Database\Connectors\MySQL;
case 'pgsql': case 'pgsql':
return new Connectors\Postgres; return new Database\Connectors\Postgres;
case 'sqlsrv':
return new Database\Connectors\SQLServer;
default: default:
throw new \DomainException("Database driver [$driver] is not supported."); throw new \Exception("Database driver [$driver] is not supported.");
} }
} }
...@@ -124,7 +113,13 @@ class Manager { ...@@ -124,7 +113,13 @@ class Manager {
/** /**
* Magic Method for calling methods on the default database connection. * Magic Method for calling methods on the default database connection.
* *
* This provides a convenient API for querying or examining the default database connection. * <code>
* // Get the driver name for the default database connection
* $driver = DB::driver();
*
* // Execute a fluent query on the default database connection
* $users = DB::table('users')->get();
* </code>
*/ */
public static function __callStatic($method, $parameters) public static function __callStatic($method, $parameters)
{ {
......
...@@ -9,13 +9,6 @@ class Connection { ...@@ -9,13 +9,6 @@ class Connection {
*/ */
public $pdo; public $pdo;
/**
* All of the queries that have been executed on the connection.
*
* @var array
*/
public $queries = array();
/** /**
* The connection configuration array. * The connection configuration array.
* *
...@@ -30,6 +23,13 @@ class Connection { ...@@ -30,6 +23,13 @@ class Connection {
*/ */
protected $grammar; protected $grammar;
/**
* All of the queries that have been executed on all connections.
*
* @var array
*/
public static $queries = array();
/** /**
* Create a new database connection instance. * Create a new database connection instance.
* *
...@@ -46,6 +46,14 @@ class Connection { ...@@ -46,6 +46,14 @@ class Connection {
/** /**
* Begin a fluent query against a table. * Begin a fluent query against a table.
* *
* <code>
* // Start a fluent query against the "users" table
* $query = DB::connection()->table('users');
*
* // Start a fluent query against the "users" table and get all the users
* $users = DB::connection()->table('users')->get();
* </code>
*
* @param string $table * @param string $table
* @return Query * @return Query
*/ */
...@@ -57,27 +65,22 @@ class Connection { ...@@ -57,27 +65,22 @@ class Connection {
/** /**
* Create a new query grammar for the connection. * Create a new query grammar for the connection.
* *
* Query grammars allow support for new database systems to be added quickly * @return Query\Grammars\Grammar
* and easily. Since the responsibility of the query generation is delegated
* to the grammar classes, it is simple to override only the methods with
* SQL syntax that differs from the default implementation.
*
* @return Grammars\Grammar
*/ */
protected function grammar() protected function grammar()
{ {
if (isset($this->grammar)) return $this->grammar; if (isset($this->grammar)) return $this->grammar;
// We allow the developer to hard-code a grammar for the connection. This really
// has no use yet; however, if database systems that can use multiple grammars
// like ODBC are added in the future, this will be needed.
switch (isset($this->config['grammar']) ? $this->config['grammar'] : $this->driver()) switch (isset($this->config['grammar']) ? $this->config['grammar'] : $this->driver())
{ {
case 'mysql': case 'mysql':
return $this->grammar = new Grammars\MySQL; return $this->grammar = new Query\Grammars\MySQL;
case 'sqlsrv':
return $this->grammar = new Query\Grammars\SQLServer;
default: default:
return $this->grammar = new Grammars\Grammar; return $this->grammar = new Query\Grammars\Grammar;
} }
} }
...@@ -98,9 +101,9 @@ class Connection { ...@@ -98,9 +101,9 @@ class Connection {
*/ */
public function only($sql, $bindings = array()) public function only($sql, $bindings = array())
{ {
$result = (array) $this->first($sql, $bindings); $results = (array) $this->first($sql, $bindings);
return reset($result); return reset($results);
} }
/** /**
...@@ -127,67 +130,124 @@ class Connection { ...@@ -127,67 +130,124 @@ class Connection {
} }
/** /**
* Execute a SQL query against the connection. * Execute a SQL query and return an array of StdClass objects.
*
* The method returns the following based on query type:
* *
* SELECT -> Array of stdClasses * @param string $sql
* UPDATE -> Number of rows affected. * @param array $bindings
* DELETE -> Number of Rows affected. * @return array
* ELSE -> Boolean true / false depending on success. */
* public function query($sql, $bindings = array())
* <code> {
* // Execute a query against the database connection list($statement, $result) = $this->execute($sql, $bindings);
* $users = DB::connection()->query('select * from users');
return $statement->fetchAll(PDO::FETCH_CLASS, 'stdClass');
}
/**
* Execute a SQL UPDATE query and return the affected row count.
* *
* // Execute a query with bound parameters * @param string $sql
* $user = DB::connection()->query('select * from users where id = ?', array($id)); * @param array $bindings
* </code> * @return int
*/
public function update($sql, $bindings = array())
{
list($statement, $result) = $this->execute($sql, $bindings);
return $statement->rowCount();
}
/**
* Execute a SQL DELETE query and return the affected row count.
* *
* @param string $sql * @param string $sql
* @param array $bindings * @param array $bindings
* @return mixed * @return int
*/ */
public function query($sql, $bindings = array()) public function delete($sql, $bindings = array())
{ {
// Since expressions are injected into the query as raw strings, we need list($statement, $result) = $this->execute($sql, $bindings);
// to remove them from the array of bindings. They are not truly bound
// to the PDO statement as named parameters. return $statement->rowCount();
foreach ($bindings as $key => $value) }
/**
* Execute an SQL query and return the boolean result of the PDO statement.
*
* @param string $sql
* @param array $bindings
* @return bool
*/
public function statement($sql, $bindings = array())
{ {
if ($value instanceof Expression) unset($bindings[$key]); list($statement, $result) = $this->execute($sql, $bindings);
return $result;
} }
$bindings = array_values($bindings); /**
* Execute a SQL query against the connection.
*
* The PDO statement and boolean result will be return in an array.
*
* @param string $sql
* @param array $bindings
* @return array
*/
protected function execute($sql, $bindings = array())
{
// Since expressions are injected into the query as strings, we need to
// remove them from the array of bindings. After we have removed them,
// we'll reset the array so there aren't gaps in the keys.
$bindings = array_values(array_filter($bindings, function($binding)
{
return ! $binding instanceof Expression;
}));
$sql = $this->transform($sql, $bindings); $sql = $this->transform($sql, $bindings);
$this->queries[] = compact('sql', 'bindings'); $statement = $this->pdo->prepare($sql);
// Every query is timed so that we can log the executinon time along
// with the query SQL and array of bindings. This should be make it
// convenient for the developer to profile the application's query
// performance to diagnose bottlenecks.
$time = microtime(true);
$result = $statement->execute($bindings);
$time = number_format((microtime(true) - $time) * 1000, 2);
return $this->execute($this->pdo->prepare($sql), $bindings); // Once we have execute the query, we log the SQL, bindings, and
// execution time in a static array that is accessed by all of
// the connections used by the application. This allows us to
// review all of the executed SQL.
static::$queries[] = compact('sql', 'bindings', 'time');
return array($statement, $result);
} }
/** /**
* Transform an SQL query into an executable query. * Transform an SQL query into an executable query.
* *
* Laravel provides a convenient short-cut when writing raw queries for
* handling cumbersome "where in" statements. This method will transform
* those segments into their full SQL counterparts.
*
* @param string $sql * @param string $sql
* @param array $bindings * @param array $bindings
* @return string * @return string
*/ */
protected function transform($sql, $bindings) protected function transform($sql, $bindings)
{ {
// Laravel provides an easy short-cut notation for writing raw
// WHERE IN statements. If (...) is in the query, it will be
// replaced with the correct number of parameters based on
// the bindings for the query.
if (strpos($sql, '(...)') !== false) if (strpos($sql, '(...)') !== false)
{ {
for ($i = 0; $i < count($bindings); $i++) for ($i = 0; $i < count($bindings); $i++)
{ {
// If the binding is an array, we can assume it is being used to fill // If the binding is an array, we can assume it is being used
// a "where in" condition, so we will replace the next place-holder // to fill a "where in" condition, so we'll replace the next
// in the query with the correct number of parameters based on the // place-holder in the SQL query with the correct number of
// number of elements in this binding. // parameters based on the elements in the binding.
if (is_array($bindings[$i])) if (is_array($bindings[$i]))
{ {
$parameters = implode(', ', array_fill(0, count($bindings[$i]), '?')); $parameters = implode(', ', array_fill(0, count($bindings[$i]), '?'));
...@@ -200,33 +260,6 @@ class Connection { ...@@ -200,33 +260,6 @@ class Connection {
return trim($sql); return trim($sql);
} }
/**
* Execute a prepared PDO statement and return the appropriate results.
*
* @param PDOStatement $statement
* @param array $bindings
* @return mixed
*/
protected function execute(PDOStatement $statement, $bindings)
{
$result = $statement->execute($bindings);
$sql = strtoupper($statement->queryString);
if (strpos($sql, 'SELECT') === 0)
{
return $statement->fetchAll(PDO::FETCH_CLASS, 'stdClass');
}
elseif (strpos($sql, 'UPDATE') === 0 or strpos($sql, 'DELETE') === 0)
{
return $statement->rowCount();
}
else
{
return $result;
}
}
/** /**
* Get the driver name for the database connection. * Get the driver name for the database connection.
* *
......
...@@ -16,7 +16,7 @@ abstract class Connector { ...@@ -16,7 +16,7 @@ abstract class Connector {
); );
/** /**
* Establish a PDO database connection for a given database configuration. * Establish a PDO database connection.
* *
* @param array $config * @param array $config
* @return PDO * @return PDO
...@@ -24,7 +24,7 @@ abstract class Connector { ...@@ -24,7 +24,7 @@ abstract class Connector {
abstract public function connect($config); abstract public function connect($config);
/** /**
* Get the PDO connection options for a given database configuration. * Get the PDO connection options for the configuration.
* *
* Developer specified options will override the default connection options. * Developer specified options will override the default connection options.
* *
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
class MySQL extends Connector { class MySQL extends Connector {
/** /**
* Establish a PDO database connection for a given database configuration. * Establish a PDO database connection.
* *
* @param array $config * @param array $config
* @return PDO * @return PDO
...@@ -15,7 +15,7 @@ class MySQL extends Connector { ...@@ -15,7 +15,7 @@ class MySQL extends Connector {
// Format the initial MySQL PDO connection string. These options are required // Format the initial MySQL PDO connection string. These options are required
// for every MySQL connection that is established. The connection strings // for every MySQL connection that is established. The connection strings
// have the following convention: "mysql:host=hostname;dbname=database" // have the following convention: "mysql:host=hostname;dbname=database"
$dsn = sprintf('%s:host=%s;dbname=%s', $driver, $host, $database); $dsn = "mysql:host={$host};dbname={$database}";
// 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
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
class Postgres extends Connector { class Postgres extends Connector {
/** /**
* Establish a PDO database connection for a given database configuration. * Establish a PDO database connection.
* *
* @param array $config * @param array $config
* @return PDO * @return PDO
...@@ -15,7 +15,7 @@ class Postgres extends Connector { ...@@ -15,7 +15,7 @@ class Postgres extends Connector {
// Format the initial Postgres PDO connection string. These options are required // Format the initial Postgres PDO connection string. These options are required
// for every Postgres connection that is established. The connection strings // for every Postgres connection that is established. The connection strings
// have the following convention: "pgsql:host=hostname;dbname=database" // have the following convention: "pgsql:host=hostname;dbname=database"
$dsn = sprintf('%s:host=%s;dbname=%s', $driver, $host, $database); $dsn = "pgsql:host={$host};dbname={$database}";
// Check for any optional Postgres PDO options. These options are not required // Check for any optional Postgres 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
......
...@@ -3,25 +3,7 @@ ...@@ -3,25 +3,7 @@
class SQLite extends Connector { class SQLite extends Connector {
/** /**
* The path to the SQLite databases for the application. * Establish a PDO database connection.
*
* @var string
*/
protected $path;
/**
* Create a new SQLite database connector instance.
*
* @param string $path
* @return void
*/
public function __construct($path)
{
$this->path = $path;
}
/**
* Establish a PDO database connection for a given database configuration.
* *
* @param array $config * @param array $config
* @return PDO * @return PDO
...@@ -32,27 +14,19 @@ class SQLite extends Connector { ...@@ -32,27 +14,19 @@ class SQLite extends Connector {
// SQLite provides supported for "in-memory" databases, which exist only for the // SQLite provides supported for "in-memory" databases, which exist only for the
// lifetime of the request. Any given in-memory database may only have one PDO // lifetime of the request. Any given in-memory database may only have one PDO
// connection open to it at a time. Generally, these databases are use for // connection open to it at a time. Generally, these databases are used for
// testing and development purposes, not in production scenarios. // testing and development purposes, not in production scenarios.
if ($config['database'] == ':memory:') if ($config['database'] == ':memory:')
{ {
return new PDO('sqlite::memory:', null, null, $options); return new PDO('sqlite::memory:', null, null, $options);
} }
// First, we will check for the database in the default storage directory for the if (file_exists($path = DATABASE_PATH.$config['database'].'.sqlite'))
// application. If we don't find the database there, we will assume the database
// name is actually a full qualified path to the database on disk and attempt
// to load it. If we still can't find it, we'll bail out.
elseif (file_exists($path = $this->path.$config['database'].'.sqlite'))
{ {
return new PDO('sqlite:'.$path, null, null, $options); return new PDO('sqlite:'.$path, null, null, $options);
} }
elseif (file_exists($config['database']))
{
return new PDO('sqlite:'.$config['database'], null, null, $options);
}
throw new \OutOfBoundsException("SQLite database [{$config['database']}] could not be found."); throw new \Exception("SQLite database [{$config['database']}] could not be found.");
} }
} }
<?php namespace Laravel\Database\Connectors; use PDO;
class SQLServer extends Connector {
/**
* The PDO connection options.
*
* @var array
*/
protected $options = array(
PDO::ATTR_CASE => PDO::CASE_LOWER,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_ORACLE_NULLS => PDO::NULL_NATURAL,
PDO::ATTR_STRINGIFY_FETCHES => false,
);
/**
* Establish a PDO database connection.
*
* @param array $config
* @return PDO
*/
public function connect($config)
{
extract($config);
// Format the SQL Server connection string. This connection string format can
// also be used to connect to Azure SQL Server databases. The port is defined
// directly after the server name, so we'll create that and then create the
// final DSN string to pass to PDO.
$port = (isset($port)) ? ','.$port : '';
$dsn = "sqlsrv:Server={$host}{$port};Database={$database}";
return new PDO($dsn, $username, $password, $this->options($config));
}
}
\ No newline at end of file
<?php namespace Laravel\Database\Eloquent;
class Hydrator {
/**
* Load the array of hydrated models and their eager relationships.
*
* @param Model $eloquent
* @return array
*/
public static function hydrate($eloquent)
{
$results = static::base(get_class($eloquent), $eloquent->query->get());
if (count($results) > 0)
{
foreach ($eloquent->includes as $include)
{
if ( ! method_exists($eloquent, $include))
{
throw new \LogicException("Attempting to eager load [$include], but the relationship is not defined.");
}
static::eagerly($eloquent, $results, $include);
}
}
return $results;
}
/**
* Hydrate the base models for a query.
*
* The resulting model array is keyed by the primary keys of the models.
* This allows the models to easily be matched to their children.
*
* @param string $class
* @param array $results
* @return array
*/
private static function base($class, $results)
{
$models = array();
foreach ($results as $result)
{
$model = new $class;
$model->attributes = (array) $result;
$model->exists = true;
if (isset($model->attributes['id']))
{
$models[$model->id] = $model;
}
else
{
$models[] = $model;
}
}
return $models;
}
/**
* Eagerly load a relationship.
*
* @param object $eloquent
* @param array $parents
* @param string $include
* @return void
*/
private static function eagerly($eloquent, &$parents, $include)
{
// We temporarily spoof the query attributes to allow the query to be fetched without
// any problems, since the belongs_to method actually gets the related attribute.
$first = reset($parents);
$eloquent->attributes = $first->attributes;
$relationship = $eloquent->$include();
$eloquent->attributes = array();
// Reset the WHERE clause and bindings on the query. We'll add our own WHERE clause soon.
// This will allow us to load a range of related models instead of only one.
$relationship->query->reset_where();
// Initialize the relationship attribute on the parents. As expected, "many" relationships
// are initialized to an array and "one" relationships are initialized to null.
foreach ($parents as &$parent)
{
$parent->ignore[$include] = (in_array($eloquent->relating, array('has_many', 'has_and_belongs_to_many'))) ? array() : null;
}
if (in_array($relating = $eloquent->relating, array('has_one', 'has_many', 'belongs_to')))
{
return static::$relating($relationship, $parents, $eloquent->relating_key, $include);
}
else
{
static::has_and_belongs_to_many($relationship, $parents, $eloquent->relating_key, $eloquent->relating_table, $include);
}
}
/**
* Eagerly load a 1:1 relationship.
*
* @param object $relationship
* @param array $parents
* @param string $relating_key
* @param string $relating
* @param string $include
* @return void
*/
private static function has_one($relationship, &$parents, $relating_key, $include)
{
foreach ($relationship->where_in($relating_key, array_keys($parents))->get() as $key => $child)
{
$parents[$child->$relating_key]->ignore[$include] = $child;
}
}
/**
* Eagerly load a 1:* relationship.
*
* @param object $relationship
* @param array $parents
* @param string $relating_key
* @param string $relating
* @param string $include
* @return void
*/
private static function has_many($relationship, &$parents, $relating_key, $include)
{
foreach ($relationship->where_in($relating_key, array_keys($parents))->get() as $key => $child)
{
$parents[$child->$relating_key]->ignore[$include][$child->id] = $child;
}
}
/**
* Eagerly load a 1:1 belonging relationship.
*
* @param object $relationship
* @param array $parents
* @param string $relating_key
* @param string $include
* @return void
*/
private static function belongs_to($relationship, &$parents, $relating_key, $include)
{
$keys = array();
foreach ($parents as &$parent)
{
$keys[] = $parent->$relating_key;
}
$children = $relationship->where_in('id', array_unique($keys))->get();
foreach ($parents as &$parent)
{
if (array_key_exists($parent->$relating_key, $children))
{
$parent->ignore[$include] = $children[$parent->$relating_key];
}
}
}
/**
* Eagerly load a many-to-many relationship.
*
* @param object $relationship
* @param array $parents
* @param string $relating_key
* @param string $relating_table
* @param string $include
*
* @return void
*/
private static function has_and_belongs_to_many($relationship, &$parents, $relating_key, $relating_table, $include)
{
// The model "has and belongs to many" method sets the SELECT clause; however, we need
// to clear it here since we will be adding the foreign key to the select.
$relationship->query->select = null;
$relationship->query->where_in($relating_table.'.'.$relating_key, array_keys($parents));
// The foreign key is added to the select to allow us to easily match the models back to their parents.
// Otherwise, there would be no apparent connection between the models to allow us to match them.
$children = $relationship->query->get(array(Model::table(get_class($relationship)).'.*', $relating_table.'.'.$relating_key));
$class = get_class($relationship);
foreach ($children as $child)
{
$related = new $class;
$related->attributes = (array) $child;
$related->exists = true;
// Remove the foreign key since it was only added to the query to help match the models.
unset($related->attributes[$relating_key]);
$parents[$child->$relating_key]->ignore[$include][$child->id] = $related;
}
}
}
This diff is collapsed.
<?php namespace Laravel\Database;
abstract class Grammar {
/**
* The keyword identifier for the database system.
*
* @var string
*/
protected $wrapper = '"%s"';
/**
* Wrap a value in keyword identifiers.
*
* @param string $value
* @return string
*/
public function wrap($value)
{
// If the value being wrapped contains a column alias, we need to
// wrap it a little differently as each segment must be wrapped
// and not the entire string. We'll split the value on the "as"
// joiner to extract the column and the alias.
if (strpos(strtolower($value), ' as ') !== false)
{
$segments = explode(' ', $value);
return $this->wrap($segments[0]).' AS '.$this->wrap($segments[2]);
}
// Expressions should be injected into the query as raw strings,
// so we do not want to wrap them in any way. We'll just return
// the string value from the expression to be included.
if ($value instanceof Expression) return $value->get();
// Since columns may be prefixed with their corresponding table
// name so as to not make them ambiguous, we will need to wrap
// the table and the column in keyword identifiers.
foreach (explode('.', $value) as $segment)
{
if ($segment == '*')
{
$wrapped[] = $segment;
}
else
{
$wrapped[] = sprintf($this->wrapper, $segment);
}
}
return implode('.', $wrapped);
}
/**
* Create query parameters from an array of values.
*
* <code>
* Returns "?, ?, ?", which may be used as PDO place-holders
* $parameters = $grammar->parameterize(array(1, 2, 3));
*
* // Returns "?, "Taylor"" since an expression is used
* $parameters = $grammar->parameterize(array(1, DB::raw('Taylor')));
* </code>
*
* @param array $values
* @return string
*/
final public function parameterize($values)
{
return implode(', ', array_map(array($this, 'parameter'), $values));
}
/**
* Get the appropriate query parameter string for a value.
*
* <code>
* // Returns a "?" PDO place-holder
* $value = $grammar->parameter('Taylor Otwell');
*
* // Returns "Taylor Otwell" as the raw value of the expression
* $value = $grammar->parameter(DB::raw('Taylor Otwell'));
* </code>
*
* @param mixed $value
* @return string
*/
final public function parameter($value)
{
return ($value instanceof Expression) ? $value->get() : '?';
}
/**
* Create a comma-delimited list of wrapped column names.
*
* <code>
* // Returns ""Taylor", "Otwell"" when the identifier is quotes
* $columns = $grammar->columnize(array('Taylor', 'Otwell'));
* </code>
*
* @param array $columns
* @return string
*/
final public function columnize($columns)
{
return implode(', ', array_map(array($this, 'wrap'), $columns));
}
}
\ No newline at end of file
This diff is collapsed.
<?php namespace Laravel\Database\Grammars; <?php namespace Laravel\Database\Query\Grammars;
class MySQL extends Grammar { class MySQL extends Grammar {
...@@ -7,6 +7,6 @@ class MySQL extends Grammar { ...@@ -7,6 +7,6 @@ class MySQL extends Grammar {
* *
* @var string * @var string
*/ */
protected $wrapper = '`'; protected $wrapper = '`%s`';
} }
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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