diff --git a/application/config/application.php b/application/config/application.php index 525227c2..60735a60 100755 --- a/application/config/application.php +++ b/application/config/application.php @@ -94,6 +94,19 @@ 'language' => 'en', + /* + |-------------------------------------------------------------------------- + | Supported Languages + |-------------------------------------------------------------------------- + | + | These languages may also be supported by your application. If a request + | enters your application with a URI beginning with one of these values + | the default language will automatically be set to that language. + | + */ + + 'languages' => array(), + /* |-------------------------------------------------------------------------- | SSL Link Generation diff --git a/artisan b/artisan index 5127755f..6212745f 100644 --- a/artisan +++ b/artisan @@ -4,7 +4,7 @@ * Laravel - A PHP Framework For Web Artisans * * @package Laravel - * @version 3.2.5 + * @version 3.2.6 * @author Taylor Otwell * @link http://laravel.com */ diff --git a/laravel/blade.php b/laravel/blade.php index 15c8efde..f5bed6f4 100644 --- a/laravel/blade.php +++ b/laravel/blade.php @@ -105,7 +105,7 @@ public static function expired($view, $path) /** * Compiles the specified file containing Blade pseudo-code into valid PHP. * - * @param string $view + * @param string $path * @return string */ public static function compile($view) @@ -149,33 +149,31 @@ protected static function compile_layouts($value) } // First we'll split out the lines of the template so we can get the - // layout from the top of the template. By convention, it must be + // layout from the top of the template. By convention it must be // located on the first line of the template contents. - preg_replace_callback( - '/^@layout(\s*?\(.+?\))(\r?\n)?/', - function($matches) use (&$value) - { - $value = substr( $value, strlen( $matches[0] ) ).CRLF.'@include'.$matches[1]; - }, - $value - ); + $lines = preg_split("/(\r?\n)/", $value); - return $value; + $pattern = static::matcher('layout'); + + $lines[] = preg_replace($pattern, '$1@include$2', $lines[0]); + + // We will add a "render" statement to the end of the templates and + // then slice off the "@layout" shortcut from the start so the + // sections register before the parent template renders. + return implode(CRLF, array_slice($lines, 1)); } /** * Extract a variable value out of a Blade expression. * * @param string $value - * @param string $expression * @return string */ protected static function extract($value, $expression) { - if ( preg_match("/@layout\s*?\(\s*?'(.+?)'\s*?\)/", $value, $matches)) - { - return trim( $matches[1] ); - } + preg_match('/@layout(\s*\(.*\))(\s*)/', $value, $matches); + + return str_replace(array("('", "')"), '', $matches[1]); } /** @@ -186,9 +184,9 @@ protected static function extract($value, $expression) */ protected static function compile_comments($value) { - $value = preg_replace('/\{\{--(.*?)--\}\}/', "", $value); - - return preg_replace('/\{\{--(.*?)--\}\}/s', "$1", $value); + $value = preg_replace('/\{\{--(.+?)(--\}\})?\n/', "", $value); + + return preg_replace('/\{\{--((.|\s)*?)--\}\}/', "\n", $value); } /** @@ -199,7 +197,7 @@ protected static function compile_comments($value) */ protected static function compile_echos($value) { - return preg_replace('/\{\{(.+?)\}\}/s', '', $value); + return preg_replace('/\{\{(.+?)\}\}/', '', $value); } /** @@ -210,21 +208,27 @@ protected static function compile_echos($value) */ protected static function compile_forelse($value) { - preg_match_all('/@forelse\s*?\(\s*?\$(.+?)\s*?as\s*?\$(.+?)\s*?\)/', $value, $matches, PREG_SET_ORDER ); + preg_match_all('/(\s*)@forelse(\s*\(.*\))(\s*)/', $value, $matches); - if ( count($matches) < 1 ) return $value; - - foreach ($matches as $forelse) + foreach ($matches[0] as $forelse) { + preg_match('/\s*\(\s*(\S*)\s/', $forelse, $variable); + // Once we have extracted the variable being looped against, we can add // an if statement to the start of the loop that checks if the count // of the variable being looped against is greater than zero. - $replace = ' 0): foreach ($'.$forelse[1].' as $'.$forelse[2].'): ?>'; + $if = " 0): ?>"; + + $search = '/(\s*)@forelse(\s*\(.*\))/'; + + $replace = '$1'.$if.''; + + $blade = preg_replace($search, $replace, $forelse); // Finally, once we have the check prepended to the loop we'll replace // all instances of this forelse syntax in the view content of the // view being compiled to Blade syntax with real PHP syntax. - $value = str_replace($forelse[0], $replace, $value); + $value = str_replace($forelse, $blade, $value); } return $value; @@ -238,7 +242,7 @@ protected static function compile_forelse($value) */ protected static function compile_empty($value) { - return str_replace('@empty', '', $value); + return str_replace('@empty', '', $value); } /** @@ -260,42 +264,9 @@ protected static function compile_endforelse($value) */ protected static function compile_structure_openings($value) { - preg_replace_callback( - '/@(if|elseif|foreach|for|while)(\s*?)(\([^\n\r\t]+\))/', - function($matches) use (&$value) - { - if(count( $matches ) === 4) - { - $open = 0; - $close = 0; - $cut = 0; - $len = strlen($matches[3]); - for($i = 0; $i < $len; $i++) - { - if($matches[3][$i] === '(' ) - { - $open++; - } - if($matches[3][$i] === ')' ) - { - $close++; - } - if($open !== 0 && ($open === $close)) - { - break; - } - } - $condition = substr($matches[3], 0, ($i + 1)); - $value = str_replace( - '@'.$matches[1].$matches[2].$condition, - '', - $value - ); - } - }, - $value - ); - return $value; + $pattern = '/(\s*)@(if|elseif|foreach|for|while)(\s*\(.*\))/'; + + return preg_replace($pattern, '$1', $value); } /** @@ -306,9 +277,9 @@ function($matches) use (&$value) */ protected static function compile_structure_closings($value) { - $pattern = '/@(endif|endforeach|endfor|endwhile|break|continue)/'; + $pattern = '/(\s*)@(endif|endforeach|endfor|endwhile)(\s*)/'; - return preg_replace($pattern, '', $value); + return preg_replace($pattern, '$1$3', $value); } /** @@ -319,7 +290,7 @@ protected static function compile_structure_closings($value) */ protected static function compile_else($value) { - return str_replace( '@else', '', $value); + return preg_replace('/(\s*)@(else)(\s*)/', '$1$3', $value); } /** @@ -330,9 +301,9 @@ protected static function compile_else($value) */ protected static function compile_unless($value) { - $pattern = static::matcher('unless'); + $pattern = '/(\s*)@unless(\s*\(.*\))/'; - return preg_replace($pattern, '', $value); + return preg_replace($pattern, '$1', $value); } /** @@ -356,7 +327,7 @@ protected static function compile_includes($value) { $pattern = static::matcher('include'); - return preg_replace($pattern, 'with(get_defined_vars())->render(); ?>', $value); + return preg_replace($pattern, '$1with(get_defined_vars())->render(); ?>', $value); } /** @@ -369,7 +340,7 @@ protected static function compile_render($value) { $pattern = static::matcher('render'); - return preg_replace($pattern, '', $value); + return preg_replace($pattern, '$1', $value); } /** @@ -382,7 +353,7 @@ protected static function compile_render_each($value) { $pattern = static::matcher('render_each'); - return preg_replace($pattern, '', $value); + return preg_replace($pattern, '$1', $value); } /** @@ -397,18 +368,19 @@ protected static function compile_yields($value) { $pattern = static::matcher('yield'); - return preg_replace($pattern, '', $value); + return preg_replace($pattern, '$1', $value); } /** * Rewrites Blade yield section statements into valid PHP. * - * @param string $value * @return string */ protected static function compile_yield_sections($value) { - return str_replace('@yield_section', '', $value); + $replace = ''; + + return str_replace('@yield_section', $replace, $value); } /** @@ -423,7 +395,7 @@ protected static function compile_section_start($value) { $pattern = static::matcher('section'); - return preg_replace($pattern, '', $value); + return preg_replace($pattern, '$1', $value); } /** @@ -436,7 +408,7 @@ protected static function compile_section_start($value) */ protected static function compile_section_end($value) { - return str_replace('@endsection', '', $value); + return preg_replace('/@endsection/', '', $value); } /** @@ -453,7 +425,7 @@ protected static function compile_extensions($value) } return $value; - } + } /** * Get the regular expression for a generic Blade function. @@ -463,13 +435,13 @@ protected static function compile_extensions($value) */ public static function matcher($function) { - return '/@'.$function.'\s*?(\(.+?\))/'; + return '/(\s*)@'.$function.'(\s*\(.*\))/'; } /** * Get the fully qualified path for a compiled view. * - * @param string $path + * @param string $view * @return string */ public static function compiled($path) @@ -477,4 +449,4 @@ public static function compiled($path) return path('storage').'views/'.md5($path); } -} +} \ No newline at end of file diff --git a/laravel/documentation/changes.md b/laravel/documentation/changes.md index 5ff87c38..71d0c661 100644 --- a/laravel/documentation/changes.md +++ b/laravel/documentation/changes.md @@ -2,6 +2,8 @@ # Laravel Change Log ## Contents +- [Laravel 3.2.6](#3.2.6) +- [Upgrading From 3.2.5](#upgrade-3.2.6) - [Laravel 3.2.5](#3.2.5) - [Upgrading From 3.2.4](#upgrade-3.2.5) - [Laravel 3.2.4](#3.2.4) @@ -35,12 +37,23 @@ ## Contents - [Laravel 3.1](#3.1) - [Upgrading From 3.0](#upgrade-3.1) + +## Laravel 3.2.6 + +- Revert Blade code back to 3.2.3 tag. + + +### Upgrading From 3.2.5 + +- Replace the **laravel** folder. + +## Laravel 3.2.5 - Revert nested where code back to 3.2.3 tag. -## Upgrading From 3.2.4 +### Upgrading From 3.2.4 - Replace the **laravel** folder. @@ -52,7 +65,7 @@ ## Laravel 3.2.4 - Various bug fixes and improvements. -## Upgrading From 3.2.3 +### Upgrading From 3.2.3 - Replace the **laravel** folder. @@ -63,7 +76,7 @@ ## Laravel 3.2.3 - Added `laravel.resolving` event for all IoC resolutions. -## Upgrading From 3.2.2 +### Upgrading From 3.2.2 - Replace the **laravel** folder. @@ -79,7 +92,7 @@ ## Laravel 3.2.2 - Added `password` option to Auth configuration. -## Upgrading From 3.2.1 +### Upgrading From 3.2.1 - Replace the **laravel** folder. @@ -94,7 +107,7 @@ ## Laravel 3.2.1 - Added `format` method to message container. -## Upgrading From 3.2 +### Upgrading From 3.2 - Replace the **laravel** folder. @@ -151,7 +164,7 @@ ## Laravel 3.2 - Added `array_except` and `array_only` helpers, similar to `Input::except` and `Input::only` but for arbitrary arrays. -## Upgrading From 3.1 +### Upgrading From 3.1 - Add new `asset_url` and `profiler` options to application configuration. - Replace **auth** configuration file. @@ -176,7 +189,7 @@ ## Laravel 3.1.9 - Fixes cookie session driver bug that caused infinite loop on some occasions. -## Upgrading From 3.1.8 +### Upgrading From 3.1.8 - Replace the **laravel** folder. @@ -186,7 +199,7 @@ ## Laravel 3.1.8 - Fixes possible WSOD when using Blade's @include expression. -## Upgrading From 3.1.7 +### Upgrading From 3.1.7 - Replace the **laravel** folder. @@ -198,7 +211,7 @@ ## Laravel 3.1.7 - Classify migration names. -## Upgrading From 3.1.6 +### Upgrading From 3.1.6 - Replace the **laravel** folder. @@ -208,7 +221,7 @@ ## Laravel 3.1.6 - Fixes many-to-many eager loading in Eloquent. -## Upgrading From 3.1.5 +### Upgrading From 3.1.5 - Replace the **laravel** folder. @@ -218,7 +231,7 @@ ## Laravel 3.1.5 - Fixes bug that could allow secure cookies to be sent over HTTP. -## Upgrading From 3.1.4 +### Upgrading From 3.1.4 - Replace the **laravel** folder. @@ -229,7 +242,7 @@ ## Laravel 3.1.4 - Fixes SQL "where in" (...) short-cut bug. -## Upgrading From 3.1.3 +### Upgrading From 3.1.3 - Replace the **laravel** folder. @@ -239,7 +252,7 @@ ## Laravel 3.1.3 - Fixes **delete** method in Eloquent models. -## Upgrade From 3.1.2 +### Upgrade From 3.1.2 - Replace the **laravel** folder. @@ -249,7 +262,7 @@ ## Laravel 3.1.2 - Fixes Eloquent query method constructor conflict. -## Upgrade From 3.1.1 +### Upgrade From 3.1.1 - Replace the **laravel** folder. @@ -259,7 +272,7 @@ ## Laravel 3.1.1 - Fixes Eloquent model hydration bug involving custom setters. -## Upgrading From 3.1 +### Upgrading From 3.1 - Replace the **laravel** folder. @@ -297,48 +310,48 @@ ## Laravel 3.1 - Added "before" and "after" validation checks for dates. -## Upgrading From 3.0 +### Upgrading From 3.0 -### Replace your **application/start.php** file. +#### Replace your **application/start.php** file. The default **start.php** file has been expanded in order to give you more flexibility over the loading of your language, configuration, and view files. To upgrade your file, copy your current file and paste it at the bottom of a copy of the new Laravel 3.1 start file. Next, scroll up in the **start** file until you see the default Autoloader registrations (line 61 and line 76). Delete both of these sections since you just pasted your previous auto-loader registrations at the bottom of the file. -### Remove the **display** option from your **errors** configuration file. +#### Remove the **display** option from your **errors** configuration file. This option is now set at the beginning of your **application/start** file. -### Call the parent controller's constructor from your controller. +#### Call the parent controller's constructor from your controller. Simply add a **parent::__construct();** to to any of your controllers that have a constructor. -### Prefix Laravel migration created indexes with their table name. +#### Prefix Laravel migration created indexes with their table name. If you have created indexes on tables using the Laravel migration system and you used to the default index naming scheme provided by Laravel, prefix the index names with their table name on your database. So, if the current index name is "id_unique" on the "users" table, make the index name "users_id_unique". -### Add alias for Eloquent in your application configuration. +#### Add alias for Eloquent in your application configuration. Add the following to the **aliases** array in your **application/config/application.php** file: 'Eloquent' => 'Laravel\\Database\\Eloquent\\Model', 'Blade' => 'Laravel\\Blade', -### Update Eloquent many-to-many tables. +#### Update Eloquent many-to-many tables. Eloquent now maintains **created_at** and **updated_at** column on many-to-many intermediate tables by default. Simply add these columns to your tables. Also, many-to-many tables are now the singular model names concatenated with an underscore. For example, if the relationship is between User and Role, the intermediate table name should be **role_user**. -### Remove Eloquent bundle. +#### Remove Eloquent bundle. If you are using the Eloquent bundle with your installation, you can remove it from your bundles directory and your **application/bundles.php** file. Eloquent version 2 is included in the core in Laravel 3.1. Your models can also now extend simply **Eloquent** instead of **Eloquent\Model**. -### Update your **config/strings.php** file. +#### Update your **config/strings.php** file. English pluralization and singularization is now automatic. Just completely replace your **application/config/strings.php** file. -### Add the **fetch** option to your database configuration file. +#### Add the **fetch** option to your database configuration file. A new **fetch** option allows you to specify in which format you receive your database results. Just copy and paste the option from the new **application/config/database.php** file. -### Add **database** option to your Redis configuration. +#### Add **database** option to your Redis configuration. If you are using Redis, add the "database" option to your Redis connection configurations. The "database" value can be zero by default. diff --git a/laravel/documentation/database/schema.md b/laravel/documentation/database/schema.md index 605f53d0..904edbba 100644 --- a/laravel/documentation/database/schema.md +++ b/laravel/documentation/database/schema.md @@ -71,6 +71,7 @@ ## Adding Columns `$table->blob('data');` | BLOB equivalent to the table `->nullable()` | Designate that the column allows NULL values `->default($value)` | Declare a default value for a column +`->unsigned()` | Set INTEGER to UNSIGNED > **Note:** Laravel's "boolean" type maps to a small integer column on all database systems. @@ -144,4 +145,10 @@ ## Foreign Keys You may also easily drop a foreign key constraint. The default foreign key names follow the [same convention](#dropping-indexes) as the other indexes created by the Schema builder. Here's an example: - $table->drop_foreign('posts_user_id_foreign'); \ No newline at end of file + $table->drop_foreign('posts_user_id_foreign'); + +> **Note:** The field referenced in the foreign key is very likely an auto increment and therefore automatically an unsigned integer. Please make sure to create the foreign key field with **unsigned()** as both fields have to be the exact same type, the engine on both tables has to be set to **InnoDB**, and the referenced table must be created **before** the table with the foreign key. + + $table->engine = 'InnoDB'; + + $table->integer('user_id')->unsigned(); \ No newline at end of file diff --git a/laravel/laravel.php b/laravel/laravel.php index e1c2ba32..74c387f9 100644 --- a/laravel/laravel.php +++ b/laravel/laravel.php @@ -107,6 +107,48 @@ return Event::first('404'); }); +/* +|-------------------------------------------------------------------------- +| Gather The URI And Locales +|-------------------------------------------------------------------------- +| +| When routing, we'll need to grab the URI and the supported locales for +| the route so we can properly set the language and route the request +| to the proper end-point in the application. +| +*/ + +$uri = URI::current(); + +$languages = Config::get('application.languages', array()); + +$languages[] = Config::get('application.language'); + +/* +|-------------------------------------------------------------------------- +| Set The Locale Based On The Route +|-------------------------------------------------------------------------- +| +| If the URI starts with one of the supported languages, we will set +| the default lagnauge to match that URI segment and shorten the +| URI we'll pass to the router to not include the lang segment. +| +*/ + +foreach ($languages as $language) +{ + if (starts_with($uri, $language)) + { + Config::set('application.language', $language); + + $uri = trim(substr($uri, strlen($language)), '/'); break; + } +} + +if ($uri == '') $uri = '/'; + +URI::$uri = $uri; + /* |-------------------------------------------------------------------------- | Route The Incoming Request @@ -118,8 +160,6 @@ | */ -$uri = URI::current(); - Request::$route = Routing\Router::route(Request::method(), $uri); $response = Request::$route->call(); diff --git a/laravel/profiling/profiler.php b/laravel/profiling/profiler.php index eda884c5..01469ab8 100644 --- a/laravel/profiling/profiler.php +++ b/laravel/profiling/profiler.php @@ -14,7 +14,7 @@ class Profiler { * * @var array */ - protected static $data = array('queries' => array(), 'logs' => array()); + protected static $data = array('queries' => array(), 'logs' => array(), 'timers' => array()); /** * Get the rendered contents of the Profiler. @@ -32,10 +32,95 @@ public static function render($response) static::$data['memory'] = get_file_size(memory_get_usage(true)); static::$data['memory_peak'] = get_file_size(memory_get_peak_usage(true)); static::$data['time'] = number_format((microtime(true) - LARAVEL_START) * 1000, 2); + foreach ( static::$data['timers'] as &$timer) + { + $timer['running_time'] = number_format((microtime(true) - $timer['start'] ) * 1000, 2); + } + return render('path: '.__DIR__.'/template'.BLADE_EXT, static::$data); } } + /** + * Allow a callback to be timed. + * + * @param closure $func + * @param string $name + * @return void + */ + public static function time( $func, $name = 'default_func_timer' ) + { + // First measure the runtime of the func + $start = microtime(true); + $func(); + $end = microtime(true); + + // Check to see if a timer by that name exists + if (isset(static::$data['timers'][$name])) + { + $name = $name.uniqid(); + } + + // Push the time into the timers array for display + static::$data['timers'][$name]['start'] = $start; + static::$data['timers'][$name]['end'] = $end; + static::$data['timers'][$name]['time'] = number_format(($end - $start) * 1000, 2); + } + + /** + * Start, or add a tick to a timer. + * + * @param string $name + * @return void + */ + public static function tick($name = 'default_timer', $callback = null) + { + $name = trim($name); + if (empty($name)) $name = 'default_timer'; + + // Is this a brand new tick? + if (isset(static::$data['timers'][$name])) + { + $current_timer = static::$data['timers'][$name]; + $ticks = count($current_timer['ticks']); + + // Initialize the new time for the tick + $new_tick = array(); + $mt = microtime(true); + $new_tick['raw_time'] = $mt - $current_timer['start']; + $new_tick['time'] = number_format(($mt - $current_timer['start']) * 1000, 2); + + // Use either the start time or the last tick for the diff + if ($ticks > 0) + { + $last_tick = $current_timer['ticks'][$ticks- 1]['raw_time']; + $new_tick['diff'] = number_format(($new_tick['raw_time'] - $last_tick) * 1000, 2); + } + else + { + $new_tick['diff'] = $new_tick['time']; + } + + // Add the new tick to the stack of them + static::$data['timers'][$name]['ticks'][] = $new_tick; + } + else + { + // Initialize a start time on the first tick + static::$data['timers'][$name]['start'] = microtime(true); + static::$data['timers'][$name]['ticks'] = array(); + } + + // Run the callback for this tick if it's specified + if ( ! is_null($callback) and is_callable($callback)) + { + // After we've ticked, call the callback function + call_user_func_array($callback, array( + static::$data['timers'][$name] + )); + } + } + /** * Add a log entry to the log entries array. * diff --git a/laravel/profiling/template.blade.php b/laravel/profiling/template.blade.php index 32c430a6..9c855b43 100755 --- a/laravel/profiling/template.blade.php +++ b/laravel/profiling/template.blade.php @@ -48,6 +48,52 @@ There have been no SQL queries executed. @endif + +
+ @if (count($timers) > 0) + + + + + + + @foreach ($timers as $name => $timer) + + + + + + + @if (isset($timer['ticks'])) + @foreach( $timer['ticks'] as $tick) + + + + + + @endforeach + @else + + + + + + @endif + + @endforeach +
NameRunning Time (ms)Difference
+ {{ $name }} +
{{ $timer['running_time'] }}ms (time from start to render)
 
+
Tick
+
+
{{ $tick['time'] }}ms
+
+
+ {{ $tick['diff'] }}ms
+
Running Time
{{ $timer['time'] }}ms
 
+ @else + There have been no checkpoints set. + @endif +
@@ -61,7 +107,7 @@ @endif -
  • Time {{ $time }}ms
  • +
  • Time {{ $time }}ms
  • Memory {{ $memory }} ({{ $memory_peak }})
  • ×
  • @@ -75,4 +121,4 @@ - \ No newline at end of file + diff --git a/laravel/url.php b/laravel/url.php index 4ff0f474..b85dbc70 100644 --- a/laravel/url.php +++ b/laravel/url.php @@ -26,7 +26,7 @@ public static function full() */ public static function current() { - return static::to(URI::current()); + return static::to(URI::current(), null, false, false); } /** @@ -89,9 +89,11 @@ public static function base() * * @param string $url * @param bool $https + * @param bool $asset + * @param bool $locale * @return string */ - public static function to($url = '', $https = null) + public static function to($url = '', $https = null, $asset = false, $locale = true) { // If the given URL is already valid or begins with a hash, we'll just return // the URL unchanged since it is already well formed. Otherwise we will add @@ -105,7 +107,17 @@ public static function to($url = '', $https = null) // security for any new links generated. So https for all secure links. if (is_null($https)) $https = Request::secure(); - $root = static::base().'/'.Config::get('application.index'); + $root = static::base(); + + if ( ! $asset) + { + $root .= '/'.Config::get('application.index'); + } + + if ( ! $asset and $locale and count(Config::get('application.languages')) > 0) + { + $root .= '/'.Config::get('application.language'); + } // Since SSL is not often used while developing the application, we allow the // developer to disable SSL on all framework generated links to make it more @@ -232,7 +244,7 @@ public static function to_asset($url, $https = null) return rtrim($root, '/').'/'.ltrim($url, '/'); } - $url = static::to($url, $https); + $url = static::to($url, $https, true); // Since assets are not served by Laravel, we do not need to come through // the front controller. So, we'll remove the application index specified diff --git a/paths.php b/paths.php index 824f8ea0..9fdd9f90 100644 --- a/paths.php +++ b/paths.php @@ -3,7 +3,7 @@ * Laravel - A PHP Framework For Web Artisans * * @package Laravel - * @version 3.2.5 + * @version 3.2.6 * @author Taylor Otwell * @link http://laravel.com */ diff --git a/public/index.php b/public/index.php index 4c5604ba..ec6b0cca 100644 --- a/public/index.php +++ b/public/index.php @@ -3,7 +3,7 @@ * Laravel - A PHP Framework For Web Artisans * * @package Laravel - * @version 3.2.5 + * @version 3.2.6 * @author Taylor Otwell * @link http://laravel.com */