diff --git a/app/application/config/application.php b/app/application/config/application.php index 5c4ae6595..522dd20eb 100644 --- a/app/application/config/application.php +++ b/app/application/config/application.php @@ -30,6 +30,19 @@ 'url' => '', + /* + |-------------------------------------------------------------------------- + | Asset URL + |-------------------------------------------------------------------------- + | + | The base URL used for your application's asset files. This is useful if + | you are serving your assets through a different server or a CDN. If it + | is not set, we'll default to the application URL above. + | + */ + + 'asset_url' => '', + /* |-------------------------------------------------------------------------- | Application Index @@ -61,6 +74,20 @@ 'key' => 'YourSecretKeyGoesHere!', + /* + |-------------------------------------------------------------------------- + | Profiler Toolbar + |-------------------------------------------------------------------------- + | + | Laravel includes a beautiful profiler toolbar that gives you a heads + | up display of the queries and logs performed by your application. + | This is wonderful for development, but, of course, you should + | disable the toolbar for production applications.. + | + */ + + 'profiler' => false, + /* |-------------------------------------------------------------------------- | Application Character Encoding @@ -154,6 +181,7 @@ 'Log' => 'Laravel\\Log', 'Memcached' => 'Laravel\\Memcached', 'Paginator' => 'Laravel\\Paginator', + 'Profiler' => 'Laravel\\Profiling\\Profiler', 'URL' => 'Laravel\\URL', 'Redirect' => 'Laravel\\Redirect', 'Redis' => 'Laravel\\Redis', @@ -171,4 +199,4 @@ 'View' => 'Laravel\\View', ), -); \ No newline at end of file +); diff --git a/app/application/config/auth.php b/app/application/config/auth.php index c3ee3e4f2..0c07c47d3 100644 --- a/app/application/config/auth.php +++ b/app/application/config/auth.php @@ -4,78 +4,57 @@ /* |-------------------------------------------------------------------------- - | Retrieve The Current User + | Default Authentication Driver |-------------------------------------------------------------------------- | - | This closure is called by the Auth class' "user" method when trying to - | 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. + | Laravel uses a flexible driver-based system to handle authentication. + | You are free to register your own drivers using the Auth::extend + | method. Of course, a few great drivers are provided out of + | box to handle basic authentication simply and easily. | - | Of course, a simple and elegant authentication solution has already - | been provided for you using the query builder and hashing engine. - | We love making your life as easy as possible. + | Drivers: 'fluent', 'eloquent'. | */ - 'user' => function($id) - { - if (filter_var($id, FILTER_VALIDATE_INT) !== false) - { - return User::find($id); - } - }, + 'driver' => 'eloquent', /* |-------------------------------------------------------------------------- - | Authenticate User Credentials + | Authentication Username |-------------------------------------------------------------------------- | - | This closure is called by the Auth::attempt() method when attempting to - | 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 - | represents the user being authenticated. As long as it has a property - | for the "id", any object will work. If the credentials are not valid, - | you don't meed to return anything. + | Here you may specify the database column that should be considered the + | "username" for your users. Typically, this will either be "username" + | or "email". Of course, you're free to change the value to anything. | */ - 'attempt' => function($username, $password) - { - $user = User::where_email($username)->first(); - - if ( ! is_null($user) and Hash::check($password, $user->password)) - { - return $user; - } - }, + 'username' => 'email', /* |-------------------------------------------------------------------------- - | Logout The Current User + | Authentication Model |-------------------------------------------------------------------------- | - | 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 - | you are using for authentication or anything else you desire. + | When using the "eloquent" authentication driver, you may specify the + | model that should be considered the "User" model. This model will + | be used to authenticate and load the users of your application. | */ - 'logout' => function($user) {}, + 'model' => 'User', /* |-------------------------------------------------------------------------- - | "Remember Me" Cookie Name + | Authentication Table |-------------------------------------------------------------------------- | - | Here you may specify the cookie name that will be used for the cookie - | that serves as the "remember me" token. Of course, a sensible default - | has been set for you, so you probably don't need to change it. + | When using the "fluent" authentication driver, the database table used + | to load users may be specified here. This table will be used in by + | the fluent query builder to authenticate and load your users. | */ - 'cookie' => 'tinyissue_remember', + 'table' => 'users', ); \ No newline at end of file diff --git a/app/application/controllers/login.php b/app/application/controllers/login.php index 130bedaf4..b1d75d923 100644 --- a/app/application/controllers/login.php +++ b/app/application/controllers/login.php @@ -11,7 +11,13 @@ public function get_index() public function post_index() { - if(Auth::attempt(Input::get('email'), Input::get('password'), (bool) Input::get('remember'))) + $userdata = array( + 'username' => Input::get('email'), + 'password' => Input::get('password'), + 'remember' => (bool) Input::get('remember') + ) ; + + if(Auth::attempt($userdata)) { return Redirect::to(Input::get('return', '/')); } @@ -20,4 +26,4 @@ public function post_index() ->with('error', __('tinyissue.password_incorrect')); } -} \ No newline at end of file +} diff --git a/app/application/start.php b/app/application/start.php index 372161857..27f7b4aa6 100644 --- a/app/application/start.php +++ b/app/application/start.php @@ -144,6 +144,22 @@ return Lang::file($bundle, $language, $file); }); +/* +|-------------------------------------------------------------------------- +| Attach The Laravel Profiler +|-------------------------------------------------------------------------- +| +| If the profiler is enabled, we will attach it to the Laravel events +| for both queries and logs. This allows the profiler to intercept +| any of the queries or logs performed by the application. +| +*/ + +if (Config::get('application.profiler')) +{ + Profiler::attach(); +} + /* |-------------------------------------------------------------------------- | Enable The Blade View Engine @@ -186,4 +202,4 @@ if ( ! Request::cli() and Config::get('session.driver') !== '') { Session::load(); -} \ No newline at end of file +} diff --git a/app/laravel/asset.php b/app/laravel/asset.php index 8b6f9308e..57b45465c 100644 --- a/app/laravel/asset.php +++ b/app/laravel/asset.php @@ -1,4 +1,4 @@ - - * // Get the current user of the application - * $user = Auth::user(); - * - * // Access a property on the current user of the application - * $email = Auth::user()->email; - * - * - * @return object|null + * @param string $driver + * @return Driver */ - public static function user() + public static function driver($driver = null) { - if ( ! is_null(static::$user)) return static::$user; - - $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'); + if (is_null($driver)) $driver = Config::get('auth.driver'); - static::$user = call_user_func($config['user'], $id); - - // If the user wasn't found in the database but a "remember me" cookie - // exists, we'll attempt to recall the user based on the cookie value. - // Since all cookies contain a fingerprint hash verifying that they - // haven't changed, we can trust it. - $recaller = Cookie::get($config['cookie']); - - if (is_null(static::$user) and ! is_null($recaller)) + if ( ! isset(static::$drivers[$driver])) { - static::$user = static::recall($recaller); + static::$drivers[$driver] = static::factory($driver); } - return static::$user; + return static::$drivers[$driver]; } /** - * Attempt to login a user based on a long-lived "remember me" cookie. + * Create a new authentication driver instance. * - * @param string $recaller - * @return mixed + * @param string $driver + * @return Driver */ - protected static function recall($recaller) + protected static function factory($driver) { - $recaller = explode('|', Crypter::decrypt($recaller)); - - // 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)) + if (isset(static::$registrar[$driver])) { - static::login($user); + $resolver = static::$registrar[$driver]; - return $user; + return $resolver(); } - } - - /** - * Attempt to log a user into the application. - * - * - * // Attempt to log a user into the application - * $success = Auth::attempt('username', 'password'); - * - * // Attempt to login a user and set the "remember me" cookie - * Auth::attempt('username', 'password', true); - * - * - * @param string $username - * @param string $password - * @param bool $remember - * @return bool - */ - public static function attempt($username, $password = null, $remember = false) - { - $config = Config::get('auth'); - // 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, even allowing - // the user of third-party providers. - $user = call_user_func($config['attempt'], $username, $password); - - if (is_null($user)) return false; + switch ($driver) + { + case 'fluent': + return new Auth\Drivers\Fluent(Config::get('auth.table')); - static::login($user, $remember); + case 'eloquent': + return new Auth\Drivers\Eloquent(Config::get('auth.model')); - return true; + default: + throw new \Exception("Auth driver {$driver} is not supported."); + } } /** - * Log a user into the application. - * - * - * // Login the user with an ID of 15 - * Auth::login(15); - * - * // Login a user by passing a user object - * Auth::login($user); + * Register a third-party authentication driver. * - * // Login a user and set a "remember me" cookie - * Auth::login($user, true); - * - * - * @param object|int $user - * @param bool $remember + * @param string $driver + * @param Closure $resolver * @return void */ - public static function login($user, $remember = false) + public static function extend($driver, Closure $resolver) { - $id = (is_object($user)) ? $user->id : (int) $user; - - if ($remember) static::remember($id); - - Session::put(Auth::user_key, $id); + static::$registrar[$driver] = $resolver; } /** - * Set a cookie so that the user is "remembered". + * Magic Method for calling the methods on the default cache driver. * - * @param string $id - * @return void - */ - protected static function remember($id) - { - $recaller = Crypter::encrypt($id.'|'.Str::random(40)); - - // This method assumes the "remember me" cookie should have the same - // configuration as the session cookie. Since this cookie, like the - // session cookie, should be kept very secure, it's probably safe. - // to assume the cookie settings are the same. - $config = Config::get('session'); - - extract($config, EXTR_SKIP); - - $cookie = Config::get('auth.cookie'); - - Cookie::forever($cookie, $recaller, $path, $domain, $secure); - } - - /** - * Log the current user out of the application. + * + * // Call the "user" method on the default auth driver + * $user = Auth::user(); * - * @return void + * // Call the "check" method on the default auth driver + * Auth::check(); + * */ - public static function logout() + public static function __callStatic($method, $parameters) { - // 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()); - - static::$user = null; - - $config = Config::get('session'); - - extract($config, EXTR_SKIP); - - // When forgetting the cookie, we need to also pass in the path and - // domain that would have been used when the cookie was originally - // set by the framework, otherwise it will not be deleted. - $cookie = Config::get('auth.cookie'); - - Cookie::forget($cookie, $path, $domain, $secure); - - Session::forget(Auth::user_key); + return call_user_func_array(array(static::driver(), $method), $parameters); } } \ No newline at end of file diff --git a/app/laravel/auth/drivers/driver.php b/app/laravel/auth/drivers/driver.php new file mode 100644 index 000000000..9cd9b376e --- /dev/null +++ b/app/laravel/auth/drivers/driver.php @@ -0,0 +1,226 @@ +token = Session::get($this->token()); + } + + // If a token did not exist in the session for the user, we will attempt + // to load the value of a "remember me" cookie for the driver, which + // serves as a long-lived client side authenticator for the user. + if (is_null($this->token)) + { + $this->token = $this->recall(); + } + } + + /** + * Determine if the user of the application is not logged in. + * + * This method is the inverse of the "check" method. + * + * @return bool + */ + public function guest() + { + return ! $this->check(); + } + + /** + * Determine if the user is logged in. + * + * @return bool + */ + public function check() + { + return ! is_null($this->user()); + } + + /** + * Get the current user of the application. + * + * If the user is a guest, null should be returned. + * + * @return mixed|null + */ + public function user() + { + if ( ! is_null($this->user)) return $this->user; + + return $this->user = $this->retrieve($this->token); + } + + /** + * Get the given application user by ID. + * + * @param int $id + * @return mixed + */ + abstract public function retrieve($id); + + /** + * Attempt to log a user into the application. + * + * @param array $arguments + * @return void + */ + abstract public function attempt($arguments = array()); + + /** + * Login the user assigned to the given token. + * + * The token is typically a numeric ID for the user. + * + * @param string $token + * @param bool $remember + * @return bool + */ + public function login($token, $remember = false) + { + $this->token = $token; + + $this->store($token); + + if ($remember) $this->remember($token); + + return true; + } + + /** + * Log the user out of the driver's auth context. + * + * @return void + */ + public function logout() + { + $this->user = null; + + $this->cookie($this->recaller(), null, -2000); + + Session::forget($this->token()); + + $this->token = null; + } + + /** + * Store a user's token in the session. + * + * @param string $token + * @return void + */ + protected function store($token) + { + Session::put($this->token(), $token); + } + + /** + * Store a user's token in a long-lived cookie. + * + * @param string $token + * @return void + */ + protected function remember($token) + { + $token = Crypter::encrypt($token.'|'.Str::random(40)); + + $this->cookie($this->recaller(), $token, Cookie::forever); + } + + /** + * Attempt to find a "remember me" cookie for the user. + * + * @return string|null + */ + protected function recall() + { + $cookie = Cookie::get($this->recaller()); + + // By default, "remember me" cookies are encrypted and contain the user + // token as well as a random string. If it exists, we'll decrypt it + // and return the first segment, which is the user's ID token. + if ( ! is_null($cookie)) + { + return head(explode('|', Crypter::decrypt($cookie))); + } + } + + /** + * Store an authentication cookie. + * + * @param string $name + * @param string $value + * @param int $minutes + * @return void + */ + protected function cookie($name, $value, $minutes) + { + // When setting the default implementation of an authentication + // cookie we'll use the same settings as the session cookie. + // This typically makes sense as they both are sensitive. + $config = Config::get('session'); + + extract($config); + + Cookie::put($name, $value, $minutes, $path, $domain, $secure); + } + + /** + * Get the session key name used to store the token. + * + * @return string + */ + protected function token() + { + return $this->name().'_login'; + } + + /** + * Get the name used for the "remember me" cookie. + * + * @return string + */ + protected function recaller() + { + return $this->name().'_remember'; + } + + /** + * Get the name of the driver in a storage friendly format. + * + * @return string + */ + protected function name() + { + return strtolower(str_replace('\\', '_', get_class($this))); + } + +} \ No newline at end of file diff --git a/app/laravel/auth/drivers/eloquent.php b/app/laravel/auth/drivers/eloquent.php new file mode 100644 index 000000000..fb401d684 --- /dev/null +++ b/app/laravel/auth/drivers/eloquent.php @@ -0,0 +1,73 @@ +model()->find($token); + } + else if (get_class($token) == Config::get('auth.model')) + { + return $token; + } + } + + /** + * Attempt to log a user into the application. + * + * @param array $arguments + * @return void + */ + public function attempt($arguments = array()) + { + $user = $this->model()->where(function($query) use($arguments) + { + $username = Config::get('auth.username'); + + $query->where($username, '=', $arguments['username']); + + foreach(array_except($arguments, array('username', 'password', 'remember')) as $column => $val) + { + $query->where($column, '=', $val); + } + })->first(); + + // If the credentials match what is in the database we will just + // log the user into the application and remember them if asked. + $password = $arguments['password']; + + $password_field = Config::get('auth.password', 'password'); + + if ( ! is_null($user) and Hash::check($password, $user->{$password_field})) + { + return $this->login($user->get_key(), array_get($arguments, 'remember')); + } + + return false; + } + + /** + * Get a fresh model instance. + * + * @return Eloquent + */ + protected function model() + { + $model = Config::get('auth.model'); + + return new $model; + } + +} diff --git a/app/laravel/auth/drivers/fluent.php b/app/laravel/auth/drivers/fluent.php new file mode 100644 index 000000000..b91b93eaf --- /dev/null +++ b/app/laravel/auth/drivers/fluent.php @@ -0,0 +1,72 @@ +find($id); + } + } + + /** + * Attempt to log a user into the application. + * + * @param array $arguments + * @return void + */ + public function attempt($arguments = array()) + { + $user = $this->get_user($arguments); + + // If the credentials match what is in the database we will just + // log the user into the application and remember them if asked. + $password = $arguments['password']; + + $password_field = Config::get('auth.password', 'password'); + + if ( ! is_null($user) and Hash::check($password, $user->{$password_field})) + { + return $this->login($user->id, array_get($arguments, 'remember')); + } + + return false; + } + + /** + * Get the user from the database table. + * + * @param array $arguments + * @return mixed + */ + protected function get_user($arguments) + { + $table = Config::get('auth.table'); + + return DB::table($table)->where(function($query) use($arguments) + { + $username = Config::get('auth.username'); + + $query->where($username, '=', $arguments['username']); + + foreach(array_except($arguments, array('username', 'password', 'remember')) as $column => $val) + { + $query->where($column, '=', $val); + } + })->first(); + } + +} diff --git a/app/laravel/autoloader.php b/app/laravel/autoloader.php index f2d400722..4168e305c 100644 --- a/app/laravel/autoloader.php +++ b/app/laravel/autoloader.php @@ -1,4 +1,4 @@ - $directory) - { - if (starts_with($class, $prefix)) - { - return static::load_namespaced($class, $prefix, $directory); - } - } - - // If all else fails we will just iterator through the mapped - // PSR-0 directories looking for the class. This is the last - // resort and slowest loading option for the class. static::load_psr($class); } @@ -116,7 +102,7 @@ protected static function load_namespaced($class, $namespace, $directory) protected static function load_psr($class, $directory = null) { // The PSR-0 standard indicates that class namespaces and underscores - // shoould be used to indcate the directory tree in which the class + // should be used to indicate the directory tree in which the class // resides, so we'll convert them to slashes. $file = str_replace(array('\\', '_'), '/', $class); @@ -177,29 +163,28 @@ public static function directories($directory) } /** - * Register underscored "namespaces" to directory mappings. + * Map namespaces to directories. * - * @param array $mappings + * @param array $mappings + * @param string $append * @return void */ - public static function underscored($mappings) + public static function namespaces($mappings, $append = '\\') { - $mappings = static::format_mappings($mappings, '_'); + $mappings = static::format_mappings($mappings, $append); - static::$underscored = array_merge($mappings, static::$underscored); + static::$namespaces = array_merge($mappings, static::$namespaces); } /** - * Map namespaces to directories. + * Register underscored "namespaces" to directory mappings. * * @param array $mappings * @return void */ - public static function namespaces($mappings) + public static function underscored($mappings) { - $mappings = static::format_mappings($mappings, '\\'); - - static::$namespaces = array_merge($mappings, static::$namespaces); + static::namespaces($mappings, '_'); } /** diff --git a/app/laravel/blade.php b/app/laravel/blade.php index e3111d09c..e9d91c214 100644 --- a/app/laravel/blade.php +++ b/app/laravel/blade.php @@ -1,4 +1,4 @@ -path, BLADE_EXT)) { - return false; + return; } - $compiled = path('storage').'views/'.md5($view->path); + $compiled = Blade::compiled($view->path); // If the view doesn't exist or has been modified since the last time it // was compiled, we will recompile the view into pure PHP from it's @@ -57,22 +68,37 @@ public static function sharpen() // Once the view has been compiled, we can simply set the path to the // compiled view on the view instance and call the typical "get" // method on the view to evaluate the compiled PHP view. - return $view->get(); + return ltrim($view->get()); }); } + /** + * Register a custom Blade compiler. + * + * + * Blade::extend(function($view) + * { + * return str_replace('foo', 'bar', $view); + * }); + * + * + * @param Closure $compiler + * @return void + */ + public static function extend(Closure $compiler) + { + static::$extensions[] = $compiler; + } + /** * Determine if a view is "expired" and needs to be re-compiled. * * @param string $view * @param string $path - * @param string $compiled * @return bool */ public static function expired($view, $path) { - $compiled = static::compiled($path); - return filemtime($path) > filemtime(static::compiled($path)); } @@ -115,7 +141,7 @@ public static function compile_string($value, $view = null) protected static function compile_layouts($value) { // If the Blade template is not using "layouts", we'll just return it - // it unchanged since there is nothing to do with layouts and we'll + // unchanged since there is nothing to do with layouts and we will // just let the other Blade compilers handle the rest. if ( ! starts_with($value, '@layout')) { @@ -123,8 +149,8 @@ protected static function compile_layouts($value) } // First we'll split out the lines of the template so we can get the - // the layout from the top of the template. By convention it must - // be located on the first line of the template contents. + // layout from the top of the template. By convention it must be + // located on the first line of the template contents. $lines = preg_split("/(\r?\n)/", $value); $pattern = static::matcher('layout'); @@ -132,7 +158,7 @@ protected static function compile_layouts($value) $lines[] = preg_replace($pattern, '$1@include$2', $lines[0]); // We will add a "render" statement to the end of the templates and - // and then slice off the @layout shortcut from the start so the + // 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)); } @@ -150,6 +176,19 @@ protected static function extract($value, $expression) return str_replace(array("('", "')"), '', $matches[1]); } + /** + * Rewrites Blade comments into PHP comments. + * + * @param string $value + * @return string + */ + protected static function compile_comments($value) + { + $value = preg_replace('/\{\{--(.+?)(--\}\})?\n/', "", $value); + + return preg_replace('/\{\{--((.|\s)*?)--\}\}/', "\n", $value); + } + /** * Rewrites Blade echo statements into PHP echo statements. * @@ -173,12 +212,12 @@ protected static function compile_forelse($value) foreach ($matches[0] as $forelse) { - preg_match('/\$[^\s]*/', $forelse, $variable); + preg_match('/\s*\(\s*(\S*)\s/', $forelse, $variable); // Once we have extracted the variable being looped against, we can add - // an if statmeent to the start of the loop that checks if the count + // an if statement to the start of the loop that checks if the count // of the variable being looped against is greater than zero. - $if = " 0): ?>"; + $if = " 0): ?>"; $search = '/(\s*)@forelse(\s*\(.*\))/'; @@ -187,8 +226,8 @@ protected static function compile_forelse($value) $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 syntax. + // 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, $blade, $value); } @@ -254,6 +293,30 @@ protected static function compile_else($value) return preg_replace('/(\s*)@(else)(\s*)/', '$1$3', $value); } + /** + * Rewrites Blade "unless" statements into valid PHP. + * + * @param string $value + * @return string + */ + protected static function compile_unless($value) + { + $pattern = '/(\s*)@unless(\s*\(.*\))/'; + + return preg_replace($pattern, '$1', $value); + } + + /** + * Rewrites Blade "unless" endings into valid PHP. + * + * @param string $value + * @return string + */ + protected static function compile_endunless($value) + { + return str_replace('@endunless', '', $value); + } + /** * Rewrites Blade @include statements into valid PHP. * @@ -348,13 +411,29 @@ protected static function compile_section_end($value) return preg_replace('/@endsection/', '', $value); } + /** + * Execute user defined compilers. + * + * @param string $value + * @return string + */ + protected static function compile_extensions($value) + { + foreach (static::$extensions as $compiler) + { + $value = $compiler($value); + } + + return $value; + } + /** * Get the regular expression for a generic Blade function. * * @param string $function * @return string */ - protected static function matcher($function) + public static function matcher($function) { return '/(\s*)@'.$function.'(\s*\(.*\))/'; } diff --git a/app/laravel/bundle.php b/app/laravel/bundle.php index dd5821388..382051eae 100644 --- a/app/laravel/bundle.php +++ b/app/laravel/bundle.php @@ -46,7 +46,7 @@ public static function register($bundle, $config = array()) // If the given configuration is actually a string, we will assume it is a // location and set the bundle name to match it. This is common for most - // bundles who simply live in the root bundle directory. + // bundles that simply live in the root bundle directory. if (is_string($config)) { $bundle = $config; @@ -55,7 +55,7 @@ public static function register($bundle, $config = array()) } // If no location is set, we will set the location to match the name of - // the bundle. This is for bundles who are installed to the root of + // the bundle. This is for bundles that are installed on the root of // the bundle directory so a location was not set. if ( ! isset($config['location'])) { @@ -64,9 +64,9 @@ public static function register($bundle, $config = array()) static::$bundles[$bundle] = array_merge($defaults, $config); - // It is possible for the develoepr to specify auto-loader mappings + // It is possible for the developer to specify auto-loader mappings // directly on the bundle registration. This provides a convenient - // way to register mappings withuot a bootstrap. + // way to register mappings without a bootstrap. if (isset($config['autoloads'])) { static::autoloads($bundle, $config); @@ -74,7 +74,7 @@ public static function register($bundle, $config = array()) } /** - * Load a bundle by running it's start-up script. + * Load a bundle by running its start-up script. * * If the bundle has already been started, no action will be taken. * @@ -92,8 +92,12 @@ public static function start($bundle) // 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, etc. - if (file_exists($path = static::path($bundle).'start'.EXT)) + // any classes the bundle uses with the auto-loader class, etc. + if ( ! is_null($starter = static::option($bundle, 'starter'))) + { + $starter(); + } + elseif (file_exists($path = static::path($bundle).'start'.EXT)) { require $path; } @@ -120,7 +124,7 @@ public static function routes($bundle) $path = static::path($bundle).'routes'.EXT; - // By setting the bundle property on the router the router knows what + // By setting the bundle property on the router, the router knows what // value to replace the (:bundle) place-holder with when the bundle // routes are added, keeping the routes flexible. Router::$bundle = static::option($bundle, 'handles'); @@ -187,7 +191,7 @@ public static function handles($uri) foreach (static::$bundles as $key => $value) { - if (isset($value['handles']) and starts_with($uri, $value['handles'].'/')) + if (isset($value['handles']) and starts_with($uri, $value['handles'].'/') or $value['handles'] == '/') { return $key; } @@ -197,7 +201,7 @@ public static function handles($uri) } /** - * Deteremine if a bundle exists within the bundles directory. + * Determine if a bundle exists within the bundles directory. * * @param string $bundle * @return bool @@ -271,9 +275,19 @@ public static function path($bundle) { return path('app'); } - else if ($location = array_get(static::$bundles, $bundle.'.location')) + elseif ($location = array_get(static::$bundles, $bundle.'.location')) { - return str_finish(path('bundle').$location, DS); + // If the bundle location starts with "path: ", we will assume that a raw + // path has been specified and will simply return it. Otherwise, we'll + // prepend the bundle directory path onto the location and return. + if (starts_with($location, 'path: ')) + { + return str_finish(substr($location, 6), DS); + } + else + { + return str_finish(path('bundle').$location, DS); + } } } @@ -287,7 +301,7 @@ public static function assets($bundle) { if (is_null($bundle)) return static::assets(DEFAULT_BUNDLE); - return ($bundle != DEFAULT_BUNDLE) ? URL::base()."/bundles/{$bundle}/" : URL::base().'/'; + return ($bundle != DEFAULT_BUNDLE) ? "/bundles/{$bundle}/" : '/'; } /** @@ -358,7 +372,7 @@ public static function resolve($bundle) } /** - * Parse a element identifier and return the bundle name and element. + * Parse an element identifier and return the bundle name and element. * * * // Returns array(null, 'admin.user') @@ -374,8 +388,8 @@ public static function resolve($bundle) 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. + // subsequent request for the parsed element. So if we've already parsed + // the given element, we'll just return the cached copy as the value. if (isset(static::$elements[$identifier])) { return static::$elements[$identifier]; @@ -387,7 +401,7 @@ public static function parse($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. + // The application folder essentially behaves as a default bundle. else { $element = array(DEFAULT_BUNDLE, strtolower($identifier)); @@ -412,13 +426,19 @@ public static function get($bundle) * * @param string $bundle * @param string $option + * @param mixed $default * @return mixed */ - public static function option($bundle, $option) + public static function option($bundle, $option, $default = null) { $bundle = static::get($bundle); - if ( ! is_null($bundle)) return array_get($bundle, $option); + if (is_null($bundle)) + { + return value($default); + } + + return array_get($bundle, $option, $default); } /** @@ -441,4 +461,16 @@ public static function names() return array_keys(static::$bundles); } + /** + * Expand given bundle path of form "[bundle::]path/...". + * + * @param string $path + * @return string + */ + public static function expand($path) + { + list($bundle, $element) = static::parse($path); + return static::path($bundle).$element; + } + } \ No newline at end of file diff --git a/app/laravel/cache.php b/app/laravel/cache.php index 723aa6eb7..02b86e4e9 100644 --- a/app/laravel/cache.php +++ b/app/laravel/cache.php @@ -1,4 +1,4 @@ -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 + // database and if we catch an exception we'll assume that the // primary key already exists in the table and update. try { diff --git a/app/laravel/cache/drivers/driver.php b/app/laravel/cache/drivers/driver.php index b74ebe0a7..5df317be2 100644 --- a/app/laravel/cache/drivers/driver.php +++ b/app/laravel/cache/drivers/driver.php @@ -1,4 +1,4 @@ -get($key, null))) return $item; - $this->put($key, $default = value($default), $minutes); + $this->$function($key, $default = value($default), $minutes); return $default; } + /** + * Get an item from the cache, or cache the default value forever. + * + * @param string $key + * @param mixed $default + * @return mixed + */ + public function sear($key, $default) + { + return $this->remember($key, $default, null, 'forever'); + } + /** * Delete an item from the cache. * diff --git a/app/laravel/cache/drivers/file.php b/app/laravel/cache/drivers/file.php index ee54aa3de..c37520eae 100644 --- a/app/laravel/cache/drivers/file.php +++ b/app/laravel/cache/drivers/file.php @@ -42,9 +42,8 @@ protected function retrieve($key) if ( ! file_exists($this->path.$key)) return null; // File based caches store have the expiration timestamp stored in - // UNIX format prepended to their contents. This timestamp is then - // extracted and removed when the cache is read to determine if - // the file is still valid. + // UNIX format prepended to their contents. We'll compare the + // timestamp to the current time when we read the file. if (time() >= substr($cache = file_get_contents($this->path.$key), 0, 10)) { return $this->forget($key); @@ -68,6 +67,8 @@ protected function retrieve($key) */ public function put($key, $value, $minutes) { + if ($minutes <= 0) return; + $value = $this->expiration($minutes).serialize($value); file_put_contents($this->path.$key, $value, LOCK_EX); diff --git a/app/laravel/cache/drivers/memcached.php b/app/laravel/cache/drivers/memcached.php index 4601e3851..5a324bf53 100644 --- a/app/laravel/cache/drivers/memcached.php +++ b/app/laravel/cache/drivers/memcached.php @@ -1,13 +1,13 @@ -key = $key; $this->memcache = $memcache; @@ -47,7 +48,13 @@ public function has($key) */ protected function retrieve($key) { - if (($cache = $this->memcache->get($this->key.$key)) !== false) + if ($this->sectionable($key)) + { + list($section, $key) = $this->parse($key); + + return $this->get_from_section($section, $key); + } + elseif (($cache = $this->memcache->get($this->key.$key)) !== false) { return $cache; } @@ -68,7 +75,16 @@ protected function retrieve($key) */ public function put($key, $value, $minutes) { - $this->memcache->set($this->key.$key, $value, 0, $minutes * 60); + if ($this->sectionable($key)) + { + list($section, $key) = $this->parse($key); + + return $this->put_in_section($section, $key, $value, $minutes); + } + else + { + $this->memcache->set($this->key.$key, $value, $minutes * 60); + } } /** @@ -80,7 +96,16 @@ public function put($key, $value, $minutes) */ public function forever($key, $value) { - return $this->put($key, $value, 0); + if ($this->sectionable($key)) + { + list($section, $key) = $this->parse($key); + + return $this->forever_in_section($section, $key, $value); + } + else + { + return $this->put($key, $value, 0); + } } /** @@ -91,7 +116,71 @@ public function forever($key, $value) */ public function forget($key) { - $this->memcache->delete($this->key.$key); + if ($this->sectionable($key)) + { + list($section, $key) = $this->parse($key); + + if ($key == '*') + { + $this->forget_section($section); + } + else + { + $this->forget_in_section($section, $key); + } + } + else + { + $this->memcache->delete($this->key.$key); + } + } + + /** + * Delete an entire section from the cache. + * + * @param string $section + * @return int|bool + */ + public function forget_section($section) + { + return $this->memcache->increment($this->key.$this->section_key($section)); + } + + /** + * Get the current section ID for a given section. + * + * @param string $section + * @return int + */ + protected function section_id($section) + { + return $this->sear($this->section_key($section), function() + { + return rand(1, 10000); + }); + } + + /** + * Get a section key name for a given section. + * + * @param string $section + * @return string + */ + protected function section_key($section) + { + return $section.'_section_key'; + } + + /** + * Get a section item key for a given section and key. + * + * @param string $section + * @param string $key + * @return string + */ + protected function section_item_key($section, $key) + { + return $section.'#'.$this->section_id($section).'#'.$key; } } \ No newline at end of file diff --git a/app/laravel/cache/drivers/memory.php b/app/laravel/cache/drivers/memory.php index 3e2333488..9f57592ce 100644 --- a/app/laravel/cache/drivers/memory.php +++ b/app/laravel/cache/drivers/memory.php @@ -1,13 +1,13 @@ storage)) + if ($this->sectionable($key)) { - return $this->storage[$key]; + list($section, $key) = $this->parse($key); + + return $this->get_from_section($section, $key); + } + else + { + return array_get($this->storage, $key); } } @@ -49,7 +55,16 @@ protected function retrieve($key) */ public function put($key, $value, $minutes) { - $this->storage[$key] = $value; + if ($this->sectionable($key)) + { + list($section, $key) = $this->parse($key); + + return $this->put_in_section($section, $key, $value, $minutes); + } + else + { + array_set($this->storage, $key, $value); + } } /** @@ -61,7 +76,16 @@ public function put($key, $value, $minutes) */ public function forever($key, $value) { - $this->put($key, $value, 0); + if ($this->sectionable($key)) + { + list($section, $key) = $this->parse($key); + + return $this->forever_in_section($section, $key, $value); + } + else + { + $this->put($key, $value, 0); + } } /** @@ -72,7 +96,34 @@ public function forever($key, $value) */ public function forget($key) { - unset($this->storage[$key]); + if ($this->sectionable($key)) + { + list($section, $key) = $this->parse($key); + + if ($key == '*') + { + $this->forget_section($section); + } + else + { + $this->forget_in_section($section, $key); + } + } + else + { + array_forget($this->storage, $key); + } + } + + /** + * Delete an entire section from the cache. + * + * @param string $section + * @return int|bool + */ + public function forget_section($section) + { + array_forget($this->storage, 'section#'.$section); } /** @@ -85,4 +136,16 @@ public function flush() $this->storage = array(); } + /** + * Get a section item key for a given section and key. + * + * @param string $section + * @param string $key + * @return string + */ + protected function section_item_key($section, $key) + { + return "section#{$section}.{$key}"; + } + } \ No newline at end of file diff --git a/app/laravel/cache/drivers/sectionable.php b/app/laravel/cache/drivers/sectionable.php new file mode 100644 index 000000000..93566989e --- /dev/null +++ b/app/laravel/cache/drivers/sectionable.php @@ -0,0 +1,142 @@ +get($this->section_item_key($section, $key), $default); + } + + /** + * Write a sectioned item to the cache. + * + * @param string $section + * @param string $key + * @param mixed $value + * @param int $minutes + * @return void + */ + public function put_in_section($section, $key, $value, $minutes) + { + $this->put($this->section_item_key($section, $key), $value, $minutes); + } + + /** + * Write a sectioned item to the cache that lasts forever. + * + * @param string $section + * @param string $key + * @param mixed $value + * @return void + */ + public function forever_in_section($section, $key, $value) + { + return $this->forever($this->section_item_key($section, $key), $value); + } + + /** + * Get a sectioned item from the cache, or cache and return the default value. + * + * @param string $section + * @param string $key + * @param mixed $default + * @param int $minutes + * @param string $function + * @return mixed + */ + public function remember_in_section($section, $key, $default, $minutes, $function = 'put') + { + $key = $this->section_item_key($section, $key); + + return $this->remember($key, $default, $minutes, $function); + } + + /** + * Get a sectioned item from the cache, or cache the default value forever. + * + * @param string $section + * @param string $key + * @param mixed $default + * @return mixed + */ + public function sear_in_section($section, $key, $default) + { + return $this->sear($this->section_item_key($section, $key), $default); + } + + /** + * Delete a sectioned item from the cache. + * + * @param string $section + * @param string $key + * @return void + */ + public function forget_in_section($section, $key) + { + return $this->forget($this->section_item_key($section, $key)); + } + + /** + * Delete an entire section from the cache. + * + * @param string $section + * @return int|bool + */ + abstract public function forget_section($section); + + /** + * Indicates if a key is sectionable. + * + * @param string $key + * @return bool + */ + protected function sectionable($key) + { + return $this->implicit and $this->sectioned($key); + } + + /** + * Determine if a key is sectioned. + * + * @param string $key + * @return bool + */ + protected function sectioned($key) + { + return str_contains($key, '::'); + } + + /** + * Get the section and key from a sectioned key. + * + * @param string $key + * @return array + */ + protected function parse($key) + { + return explode('::', $key, 2); + } + +} \ No newline at end of file diff --git a/app/laravel/cli/artisan.php b/app/laravel/cli/artisan.php index a838f8e0f..e7bd130e6 100644 --- a/app/laravel/cli/artisan.php +++ b/app/laravel/cli/artisan.php @@ -2,6 +2,7 @@ use Laravel\Bundle; use Laravel\Config; +use Laravel\Request; /** * Fire up the default bundle. This will ensure any dependencies that @@ -15,9 +16,10 @@ * for the "database" CLI option. This allows migrations to be run * conveniently for a test or staging database. */ -if (isset($_SERVER['CLI']['DB'])) + +if ( ! is_null($database = get_cli_option('db'))) { - Config::set('database.default', $_SERVER['CLI']['DB']); + Config::set('database.default', $database); } /** @@ -32,7 +34,7 @@ * 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 + * for the CLI exceptions. All others will not be caught * and will be totally dumped out to the CLI. */ try diff --git a/app/laravel/cli/command.php b/app/laravel/cli/command.php index 7b4ae3af2..c6a25ee4e 100644 --- a/app/laravel/cli/command.php +++ b/app/laravel/cli/command.php @@ -100,7 +100,7 @@ protected static function parse($task) * // Resolve an instance of a task * $task = Command::resolve('application', 'migrate'); * - * // Resolve an instance of a task wtihin a bundle + * // Resolve an instance of a task within a bundle * $task = Command::resolve('bundle', 'foo'); * * @@ -121,11 +121,11 @@ public static function resolve($bundle, $task) } // 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 + // into a task class name and resolve an instance of the class so that // the requested method may be executed. if (file_exists($path = Bundle::path($bundle).'tasks/'.$task.EXT)) { - require $path; + require_once $path; $task = static::format($bundle, $task); diff --git a/app/laravel/cli/dependencies.php b/app/laravel/cli/dependencies.php index 3c6acc492..2e7f23b65 100644 --- a/app/laravel/cli/dependencies.php +++ b/app/laravel/cli/dependencies.php @@ -6,36 +6,46 @@ * of the migration resolver and database classes, which are used * to perform various support functions for the migrator. */ -IoC::register('task: migrate', function() +if(! IoC::registered('task: migrate')) { - $database = new Tasks\Migrate\Database; + IoC::register('task: migrate', function() + { + $database = new Tasks\Migrate\Database; - $resolver = new Tasks\Migrate\Resolver($database); + $resolver = new Tasks\Migrate\Resolver($database); + + return new Tasks\Migrate\Migrator($resolver, $database); + }); +} - return new Tasks\Migrate\Migrator($resolver, $database); -}); /** * 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() +if(! IoC::registered('task: bundle')) { - $repository = IoC::resolve('bundle.repository'); + IoC::register('task: bundle', function() + { + $repository = IoC::resolve('bundle.repository'); - return new Tasks\Bundle\Bundler($repository); -}); + return new Tasks\Bundle\Bundler($repository); + }); +} /** * The key task is responsible for generating a secure, random * key for use by the application when encrypting strings or * setting the hash values on cookie signatures. */ -IoC::singleton('task: key', function() +if(! IoC::registered('task: key')) { - return new Tasks\Key; -}); + IoC::singleton('task: key', function() + { + return new Tasks\Key; + }); +} /** * The session task is responsible for performing tasks related @@ -43,50 +53,65 @@ * such as generating the session table or clearing expired * sessions from storage. */ -IoC::singleton('task: session', function() +if(! IoC::registered('task: session')) { - return new Tasks\Session\Manager; -}); + IoC::singleton('task: session', function() + { + return new Tasks\Session\Manager; + }); +} /** * The route task is responsible for calling routes within the * application and dumping the result. This allows for simple * testing of APIs and JSON based applications. */ -IoC::singleton('task: route', function() +if(! IoC::registered('task: route')) { - return new Tasks\Route; -}); + IoC::singleton('task: route', function() + { + return new Tasks\Route; + }); +} /** * The "test" task is responsible for running the unit tests for * the application, bundles, and the core framework itself. * It provides a nice wrapper around PHPUnit. */ -IoC::singleton('task: test', function() +if(! IoC::registered('task: test')) { - return new Tasks\Test\Runner; -}); + IoC::singleton('task: test', function() + { + return new Tasks\Test\Runner; + }); +} /** * The bundle repository is responsible for communicating with * the Laravel bundle sources to get information regarding any * bundles that are requested for installation. */ -IoC::singleton('bundle.repository', function() +if(! IoC::registered('bundle.repository')) { - return new Tasks\Bundle\Repository; -}); + IoC::singleton('bundle.repository', function() + { + return new Tasks\Bundle\Repository; + }); +} /** * The bundle publisher is responsible for publishing bundle * assets to their correct directories within the install, * such as the web accessible directory. */ -IoC::singleton('bundle.publisher', function() +if(! IoC::registered('bundle.publisher')) { - return new Tasks\Bundle\Publisher; -}); + IoC::singleton('bundle.publisher', function() + { + return new Tasks\Bundle\Publisher; + }); +} /** * The Github bundle provider installs bundles that live on @@ -94,7 +119,22 @@ * and will update the submodule so that the bundle is * installed into the bundle directory. */ -IoC::singleton('bundle.provider: github', function() +if(! IoC::registered('bundle.provider: github')) +{ + IoC::singleton('bundle.provider: github', function() + { + return new Tasks\Bundle\Providers\Github; + }); +} + +/** + * The "help" task provides information about + * artisan usage. + */ +if(! IoC::registered('task: help')) { - return new Tasks\Bundle\Providers\Github; -}); \ No newline at end of file + IoC::singleton('task: help', function() + { + return new Tasks\Help; + }); +} \ No newline at end of file diff --git a/app/laravel/cli/tasks/bundle/bundler.php b/app/laravel/cli/tasks/bundle/bundler.php index 9d369c214..7af60e07e 100644 --- a/app/laravel/cli/tasks/bundle/bundler.php +++ b/app/laravel/cli/tasks/bundle/bundler.php @@ -49,7 +49,7 @@ public function install($bundles) // all of its registered dependencies as well. // // Each bundle provider implements the Provider interface and - // is repsonsible for retrieving the bundle source from its + // is responsible for retrieving the bundle source from its // hosting party and installing it into the application. $path = path('bundle').$this->path($bundle); @@ -135,7 +135,7 @@ protected function get($bundles) $responses[] = $bundle; - // We'll also get the bundle's declared dependenceis so they + // We'll also get the bundle's declared dependencies so they // can be installed along with the bundle, making it easy // to install a group of bundles. $dependencies = $this->get($bundle['dependencies']); diff --git a/app/laravel/cli/tasks/bundle/providers/github.php b/app/laravel/cli/tasks/bundle/providers/github.php index 7aa346236..cc0346ec5 100644 --- a/app/laravel/cli/tasks/bundle/providers/github.php +++ b/app/laravel/cli/tasks/bundle/providers/github.php @@ -11,7 +11,7 @@ class Github extends Provider { */ public function install($bundle, $path) { - $url = "http://nodeload.github.com/{$bundle['location']}/zipball/master"; + $url = "http://github.com/{$bundle['location']}/zipball/master"; parent::zipball($url, $bundle, $path); } diff --git a/app/laravel/cli/tasks/bundle/providers/provider.php b/app/laravel/cli/tasks/bundle/providers/provider.php index bdf3b8ced..8ac853ffe 100644 --- a/app/laravel/cli/tasks/bundle/providers/provider.php +++ b/app/laravel/cli/tasks/bundle/providers/provider.php @@ -27,7 +27,7 @@ protected function zipball($url, $bundle, $path) // When installing a bundle from a Zip archive, we'll first clone // down the bundle zip into the bundles "working" directory so - // we have a spot to do all of our bundle extration work. + // we have a spot to do all of our bundle extraction work. $target = $work.'laravel-bundle.zip'; File::put($target, $this->download($url)); @@ -49,11 +49,12 @@ protected function zipball($url, $bundle, $path) // Once we have the latest modified directory, we should be // able to move its contents over into the bundles folder - // so the bundle will be usable by the develoepr. + // so the bundle will be usable by the developer. File::mvdir($latest, $path); File::rmdir($work.'zip'); + $zip->close(); @unlink($target); } @@ -72,7 +73,7 @@ protected function download($url) // zip that was put in the storage directory. if ($remote === false) { - throw new \Exception("Error downloading bundle [{$bundle}]."); + throw new \Exception("Error downloading the requested bundle."); } return $remote; diff --git a/app/laravel/cli/tasks/help.json b/app/laravel/cli/tasks/help.json new file mode 100644 index 000000000..3f614ad1f --- /dev/null +++ b/app/laravel/cli/tasks/help.json @@ -0,0 +1,78 @@ +{ + "Application Configuration": { + "key:generate": { + "description": "Generate a secure application key.", + "command": "php artisan key:generate" + } + }, + "Database Tables": { + "session:table": { + "description": "Generate a migration for the sessions database table.", + "command": "php artisan session:table" + } + }, + "Migrations": { + "migrate:install": { + "description": "Create the Laravel migration table.", + "command": "php artisan migrate:install" + }, + "migrate:make": { + "description": "Create a migration.", + "command": "php artisan migrate:make create_users_table" + }, + "migrate": { + "description": "Run outstanding migrations.", + "command": "php artisan migrate" + }, + "migrate:rollback": { + "description": "Roll back the most recent migration.", + "command": "php artisan migrate:rollback" + }, + "migrate:reset": { + "description": "Roll back all migrations.", + "command": "php artisan migrate:reset" + } + }, + "Bundles": { + "bundle:install": { + "description": "Install a bundle.", + "command": "php artisan bundle:install swiftmailer" + }, + "bundle:upgrade": { + "description": "Upgrade a bundle.", + "command": "php artisan bundle:upgrade swiftmailer" + }, + "bundle:publish": { + "description": "Publish all bundles' assets.", + "command": "php artisan bundle:publish" + } + }, + "Unit Testing": { + "test": { + "description": "Run the application's tests.", + "command": "php artisan test" + } + }, + "Routing": { + "route:call": { + "description": "Call a route.", + "command": "php artisan route:call get api/user/1" + } + }, + "Application Keys": { + "key:generate": { + "description": "Generate an application key.", + "command": "php artisan key:generade" + } + }, + "CLI Options": { + "--env=": { + "description": "Set the Laravel environment.", + "command": "php artisan task --env=local" + }, + "--database=": { + "description": "Set the default database connection.", + "command": "php artisan task --database=mysql" + } + } +} \ No newline at end of file diff --git a/app/laravel/cli/tasks/help.php b/app/laravel/cli/tasks/help.php new file mode 100644 index 000000000..929c4ff89 --- /dev/null +++ b/app/laravel/cli/tasks/help.php @@ -0,0 +1,34 @@ + $commands) + { + if($i++ != 0) echo PHP_EOL; + + echo PHP_EOL . "# $category" . PHP_EOL; + + foreach($commands as $command => $details) + { + echo PHP_EOL . str_pad($command, 20) . str_pad($details->description, 30); + } + } + } +} \ No newline at end of file diff --git a/app/laravel/cli/tasks/migrate/database.php b/app/laravel/cli/tasks/migrate/database.php index a6aae9d5e..31fcf1105 100644 --- a/app/laravel/cli/tasks/migrate/database.php +++ b/app/laravel/cli/tasks/migrate/database.php @@ -40,7 +40,7 @@ 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 + // as this will allow us to grab the latest batch of migrations // that need to be run for a rollback command. $id = $this->batch(); diff --git a/app/laravel/cli/tasks/migrate/migrator.php b/app/laravel/cli/tasks/migrate/migrator.php index 5bf7617bf..7e99f7135 100644 --- a/app/laravel/cli/tasks/migrate/migrator.php +++ b/app/laravel/cli/tasks/migrate/migrator.php @@ -139,6 +139,25 @@ public function reset($arguments = array()) while ($this->rollback()) {}; } + /** + * Reset the database to pristine state and run all migrations + * + * @param array $arguments + * @return void + */ + public function rebuild() + { + // Clean the database + $this->reset(); + + echo PHP_EOL; + + // Re-run all migrations + $this->migrate(); + + echo 'The database was successfully rebuilt'.PHP_EOL; + } + /** * Install the database tables used by the migration system. * @@ -151,7 +170,7 @@ public function install() $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 + // the bundle name and string migration name as a unique ID // for the migrations, allowing us to easily identify which // migrations have been run for each bundle. $table->string('bundle', 50); @@ -206,7 +225,7 @@ public function make($arguments = array()) // Once the migration has been created, we'll return the // migration file name so it can be used by the task - // consumer if necessary for futher work. + // consumer if necessary for further work. return $file; } @@ -223,7 +242,7 @@ protected function stub($bundle, $migration) $prefix = Bundle::class_prefix($bundle); - // The class name is formatted simialrly to tasks and controllers, + // The class name is formatted similarly to tasks and controllers, // where the bundle name is prefixed to the class if it is not in // the default "application" bundle. $class = $prefix.Str::classify($migration); diff --git a/app/laravel/cli/tasks/route.php b/app/laravel/cli/tasks/route.php index 494d5ba02..b30d467c6 100644 --- a/app/laravel/cli/tasks/route.php +++ b/app/laravel/cli/tasks/route.php @@ -14,7 +14,7 @@ class Route extends Task { */ public function call($arguments = array()) { - if ( ! count($arguments) == 2) + if ( count($arguments) != 2) { throw new \Exception("Please specify a request method and URI."); } @@ -41,7 +41,7 @@ protected function route() // We'll call the router using the method and URI specified by // the developer on the CLI. If a route is found, we will not // run the filters, but simply dump the result. - $route = Router::route(Request::method(), URI::current()); + $route = Router::route(Request::method(), $_SERVER['REQUEST_URI']); if ( ! is_null($route)) { @@ -53,4 +53,4 @@ protected function route() } } -} \ No newline at end of file +} diff --git a/app/laravel/cli/tasks/test/phpunit.php b/app/laravel/cli/tasks/test/phpunit.php index 676448381..32eadd583 100644 --- a/app/laravel/cli/tasks/test/phpunit.php +++ b/app/laravel/cli/tasks/test/phpunit.php @@ -1,12 +1,4 @@ - * @link http://laravel.com - */ // -------------------------------------------------------------- // Define the directory separator for the environment. @@ -18,22 +10,6 @@ // -------------------------------------------------------------- require 'paths.php'; -// -------------------------------------------------------------- -// Override the application paths when testing the core. -// -------------------------------------------------------------- -$config = file_get_contents('phpunit.xml'); - -if (strpos($config, 'laravel-tests') !== false) -{ - $path = path('bundle').'laravel-tests'.DS; - - set_path('app', $path.'application'.DS); - - set_path('bundle', $path.'bundles'.DS); - - set_path('storage', $path.'storage'.DS); -} - // -------------------------------------------------------------- // Bootstrap the Laravel core. // -------------------------------------------------------------- diff --git a/app/laravel/cli/tasks/test/runner.php b/app/laravel/cli/tasks/test/runner.php index 85787896e..0d9fb4e6d 100644 --- a/app/laravel/cli/tasks/test/runner.php +++ b/app/laravel/cli/tasks/test/runner.php @@ -7,9 +7,19 @@ class Runner extends Task { + /** + * The base directory where the tests will be executed. + * + * A phpunit.xml should also be stored in that directory. + * + * @var string + */ + protected $base_path; + /** * Run all of the unit tests for the application. * + * @param array $bundles * @return void */ public function run($bundles = array()) @@ -26,17 +36,8 @@ public function run($bundles = array()) */ public function core() { - if ( ! is_dir(path('bundle').'laravel-tests')) - { - throw new \Exception("The bundle [laravel-tests] has not been installed!"); - } - - // When testing the Laravel core, we will just stub the path directly - // so the test bundle is not required to be registered in the bundle - // configuration, as it is kind of a unique bundle. - $this->stub(path('bundle').'laravel-tests/cases'); - - $path = path('bundle').'laravel-tests/'; + $this->base_path = path('sys').'tests'.DS; + $this->stub(path('sys').'tests'.DS.'cases'); $this->test(); } @@ -54,6 +55,8 @@ public function bundle($bundles = array()) $bundles = Bundle::names(); } + $this->base_path = path('sys').'cli'.DS.'tasks'.DS.'test'.DS; + foreach ($bundles as $bundle) { // To run PHPUnit for the application, bundles, and the framework @@ -77,13 +80,20 @@ public function bundle($bundles = array()) protected function test() { // We'll simply fire off PHPUnit with the configuration switch - // pointing to our temporary configuration file. This allows + // pointing to our requested configuration file. This allows // us to flexibly run tests for any setup. - $path = path('base').'phpunit.xml'; + $path = 'phpunit.xml'; + + // fix the spaced directories problem when using the command line + // strings with spaces inside should be wrapped in quotes. + $esc_path = escapeshellarg($path); - passthru('phpunit --configuration '.$path); + passthru('phpunit --configuration '.$esc_path, $status); @unlink($path); + + // Pass through the exit status + exit($status); } /** @@ -103,7 +113,7 @@ protected function stub($directory) // locations depending on what the developer wants to test. foreach (array('bootstrap', 'directory') as $item) { - $stub = $this->{"swap_{$item}"}($stub, $path, $directory); + $stub = $this->{"swap_{$item}"}($stub, $directory); } File::put(path('base').'phpunit.xml', $stub); @@ -113,24 +123,22 @@ protected function stub($directory) * Swap the bootstrap file in the stub. * * @param string $stub - * @param string $path * @param string $directory * @return string */ - protected function swap_bootstrap($stub, $path, $directory) + protected function swap_bootstrap($stub, $directory) { - return str_replace('{{bootstrap}}', $path.'phpunit.php', $stub); + return str_replace('{{bootstrap}}', $this->base_path.'phpunit.php', $stub); } /** * Swap the directory in the stub. * * @param string $stub - * @param string $path * @param string $directory * @return string */ - protected function swap_directory($stub, $path, $directory) + protected function swap_directory($stub, $directory) { return str_replace('{{directory}}', $directory, $stub); } diff --git a/app/laravel/config.php b/app/laravel/config.php index 9ea689462..ac46693bf 100644 --- a/app/laravel/config.php +++ b/app/laravel/config.php @@ -1,7 +1,5 @@ 0) @@ -226,9 +224,9 @@ protected static function paths($bundle) // 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. - if (isset($_SERVER['LARAVEL_ENV'])) + if ( ! is_null(Request::env())) { - $paths[] = $paths[count($paths) - 1].$_SERVER['LARAVEL_ENV'].'/'; + $paths[] = $paths[count($paths) - 1].Request::env().'/'; } return $paths; diff --git a/app/laravel/cookie.php b/app/laravel/cookie.php index 0f92818f5..56077ff7f 100644 --- a/app/laravel/cookie.php +++ b/app/laravel/cookie.php @@ -1,9 +1,14 @@ - 4000) - { - throw new \Exception("Payload too large for cookie."); - } - else - { - // We don't want to send secure cookies over HTTP unless the developer has - // turned off the "SSL" application configuration option, which is used - // while developing the application but should be true in production. - if ($secure and ! Request::secure() and Config::get('application.ssl')) - { - return; - } - - setcookie($name, $value, $time, $path, $domain, $secure); - } - } - - /** * Get the value of a cookie. * @@ -83,7 +34,7 @@ protected static function set($cookie) * // Get the value of the "favorite" cookie * $favorite = Cookie::get('favorite'); * - * // Get the value of a cookie or return a default value + * // Get the value of a cookie or return a default value * $favorite = Cookie::get('framework', 'Laravel'); * * @@ -93,24 +44,11 @@ protected static function set($cookie) */ public static function get($name, $default = null) { - if (isset(static::$jar[$name])) return static::$jar[$name]['value']; - - $value = array_get($_COOKIE, $name); + if (isset(static::$jar[$name])) return static::parse(static::$jar[$name]['value']); - if ( ! is_null($value) and isset($value[40]) and $value[40] == '~') + if ( ! is_null($value = Request::foundation()->cookies->get($name))) { - // The hash signature and the cookie value are separated by a tilde - // character for convenience. To separate the hash and the payload - // we can simply expode on that character. - list($hash, $value) = explode('~', $value, 2); - - // By re-feeding the cookie value into the "hash" method we should - // be able to generate a hash that matches the one taken from the - // cookie. If they don't, we return null. - if (static::hash($name, $value) === $hash) - { - return $value; - } + return static::parse($value); } return value($default); @@ -129,15 +67,30 @@ public static function get($name, $default = null) * * @param string $name * @param string $value - * @param int $minutes + * @param int $expiration * @param string $path * @param string $domain * @param bool $secure * @return void */ - public static function put($name, $value, $minutes = 0, $path = '/', $domain = null, $secure = false) + public static function put($name, $value, $expiration = 0, $path = '/', $domain = null, $secure = false) { - static::$jar[$name] = compact('name', 'value', 'minutes', 'path', 'domain', 'secure'); + if ($expiration !== 0) + { + $expiration = time() + ($expiration * 60); + } + + $value = static::hash($value).'+'.$value; + + // If the secure option is set to true, yet the request is not over HTTPS + // we'll throw an exception to let the developer know that they are + // attempting to send a secure cookie over the insecure HTTP. + if ($secure and ! Request::secure()) + { + throw new \Exception("Attempting to set secure cookie over HTTP."); + } + + static::$jar[$name] = compact('name', 'value', 'expiration', 'path', 'domain', 'secure'); } /** @@ -157,45 +110,63 @@ public static function put($name, $value, $minutes = 0, $path = '/', $domain = n */ public static function forever($name, $value, $path = '/', $domain = null, $secure = false) { - return static::put($name, $value, 525600, $path, $domain, $secure); + return static::put($name, $value, static::forever, $path, $domain, $secure); } /** - * Generate a cookie signature based on the contents. + * Delete a cookie. * * @param string $name - * @param string $value - * @return string + * @param string $path + * @param string $domain + * @param bool $secure + * @return bool */ - public static function sign($name, $value) + public static function forget($name, $path = '/', $domain = null, $secure = false) { - return static::hash($name, $value).'~'.$value; + return static::put($name, null, -2000, $path, $domain, $secure); } /** - * Generate a cookie hash based on the contents. + * Hash the given cookie value. * - * @param string $name * @param string $value * @return string */ - protected static function hash($name, $value) + public static function hash($value) { - return sha1($name.$value.Config::get('application.key')); + return hash_hmac('sha1', $value, Config::get('application.key')); } /** - * Delete a cookie. + * Parse a hash fingerprinted cookie value. * - * @param string $name - * @param string $path - * @param string $domain - * @param bool $secure - * @return bool + * @param string $value + * @return string */ - public static function forget($name, $path = '/', $domain = null, $secure = false) + protected static function parse($value) { - return static::put($name, null, -2000, $path, $domain, $secure); + $segments = explode('+', $value); + + // First we will make sure the cookie actually has enough segments to even + // be valid as being set by the application. If it does not we will go + // ahead and throw exceptions now since there the cookie is invalid. + if ( ! (count($segments) >= 2)) + { + return null; + } + + $value = implode('+', array_slice($segments, 1)); + + // Now we will check if the SHA-1 hash present in the first segment matches + // the ShA-1 hash of the rest of the cookie value, since the hash should + // have been set when the cookie was first created by the application. + if ($segments[0] == static::hash($value)) + { + return $value; + } + + return null; } -} \ No newline at end of file +} diff --git a/app/laravel/core.php b/app/laravel/core.php index aeec70190..0f33ccfdf 100644 --- a/app/laravel/core.php +++ b/app/laravel/core.php @@ -17,6 +17,20 @@ define('DEFAULT_BUNDLE', 'application'); define('MB_STRING', (int) function_exists('mb_get_info')); +/* +|-------------------------------------------------------------------------- +| Start Output Buffering +|-------------------------------------------------------------------------- +| +| Output buffering allows us to capture all output at any time, so that we +| can discard it or treat it accordingly. An example of this is if you have +| echoed a string, but want to return a Redirect object. Because Symfony +| only checks if headers have been sent, your redirect just silently fails. +| +*/ + +ob_start('mb_output_handler'); + /* |-------------------------------------------------------------------------- | Require Core Classes @@ -92,53 +106,122 @@ | Register The Symfony Components |-------------------------------------------------------------------------- | -| Laravel's "Artisan" CLI makes use of the Symfony Console component to -| build a wonderful CLI environment that is both robust and testable. -| We'll register the component's namespace here. +| Laravel makes use of the Symfony components where the situation is +| applicable and it is possible to do so. This allows us to focus +| on the parts of the framework that are unique and not re-do +| plumbing code that others have written. | */ Autoloader::namespaces(array( - 'Symfony\Component\Console' => path('base').'vendor/Symfony/Component/Console', + 'Symfony\Component\Console' + => path('sys').'vendor/Symfony/Component/Console', + 'Symfony\Component\HttpFoundation' + => path('sys').'vendor/Symfony/Component/HttpFoundation', )); /* |-------------------------------------------------------------------------- -| Set The CLI Options Array +| Magic Quotes Strip Slashes |-------------------------------------------------------------------------- | -| If the current request is from the Artisan command-line interface, we -| will parse the command line arguments and options and set them the -| array of options in the $_SERVER global array for convenience. +| Even though "Magic Quotes" are deprecated in PHP 5.3.x, they may still +| be enabled on the server. To account for this, we will strip slashes +| on all input arrays if magic quotes are enabled for the server. | */ -if (defined('STDIN')) +if (magic_quotes()) { - $console = CLI\Command::options($_SERVER['argv']); + $magics = array(&$_GET, &$_POST, &$_COOKIE, &$_REQUEST); - list($arguments, $options) = $console; + foreach ($magics as &$magic) + { + $magic = array_strip_slashes($magic); + } +} - $options = array_change_key_case($options, CASE_UPPER); +/* +|-------------------------------------------------------------------------- +| Create The HttpFoundation Request +|-------------------------------------------------------------------------- +| +| Laravel uses the HttpFoundation Symfony component to handle the request +| and response functionality for the framework. This allows us to not +| worry about that boilerplate code and focus on what matters. +| +*/ - $_SERVER['CLI'] = $options; +use Symfony\Component\HttpFoundation\LaravelRequest as RequestFoundation; + +Request::$foundation = RequestFoundation::createFromGlobals(); + +/* +|-------------------------------------------------------------------------- +| Determine The Application Environment +|-------------------------------------------------------------------------- +| +| Next, we're ready to determine the application environment. This may be +| set either via the command line options or via the mapping of URIs to +| environments that lives in the "paths.php" file for the application +| and is parsed. When determining the CLI environment, the "--env" +| CLI option overrides the mapping in "paths.php". +| +*/ + +if (Request::cli()) +{ + $environment = get_cli_option('env'); + + if ( ! isset($environment)) + { + $environment = Request::detect_env($environments, gethostname()); + } +} +else +{ + $root = Request::foundation()->getRootUrl(); + + $environment = Request::detect_env($environments, $root); +} + +/* +|-------------------------------------------------------------------------- +| Set The Application Environment +|-------------------------------------------------------------------------- +| +| Once we have determined the application environment, we will set it on +| the global server array of the HttpFoundation request. This makes it +| available throughout the application, though it is mainly only +| used to determine which configuration files to merge in. +| +*/ + +if (isset($environment)) +{ + Request::set_env($environment); } /* |-------------------------------------------------------------------------- -| Set The CLI Laravel Environment +| Set The CLI Options Array |-------------------------------------------------------------------------- | -| Next we'll set the LARAVEL_ENV variable if the current request is from -| the Artisan command-line interface. Since the environment is often -| specified within an Apache .htaccess file, we need to set it here -| when the request is not coming through Apache. +| If the current request is from the Artisan command-line interface, we +| will parse the command line arguments and options and set them the +| array of options in the $_SERVER global array for convenience. | */ -if (isset($_SERVER['CLI']['ENV'])) +if (defined('STDIN')) { - $_SERVER['LARAVEL_ENV'] = $_SERVER['CLI']['ENV']; + $console = CLI\Command::options($_SERVER['argv']); + + list($arguments, $options) = $console; + + $options = array_change_key_case($options, CASE_UPPER); + + $_SERVER['CLI'] = $options; } /* @@ -147,7 +230,7 @@ |-------------------------------------------------------------------------- | | Finally we will register all of the bundles that have been defined for -| the application. None of them will be started, yet but will be setup +| the application. None of them will be started yet, but will be set up | so that they may be started by the developer at any time. | */ diff --git a/app/laravel/crypter.php b/app/laravel/crypter.php index f5113f340..18bac81b2 100644 --- a/app/laravel/crypter.php +++ b/app/laravel/crypter.php @@ -74,7 +74,7 @@ public static function decrypt($value) * * @return int */ - protected static function randomizer() + public static function randomizer() { // There are various sources from which we can get random numbers // but some are more random than others. We'll choose the most diff --git a/app/laravel/database.php b/app/laravel/database.php index 447fa76bb..c5e28ac2a 100644 --- a/app/laravel/database.php +++ b/app/laravel/database.php @@ -1,5 +1,6 @@ grammar)) return $this->grammar; - switch (isset($this->config['grammar']) ? $this->config['grammar'] : $this->driver()) + if (isset(\Laravel\Database::$registrar[$this->driver()])) + { + \Laravel\Database::$registrar[$this->driver()]['query'](); + } + + switch ($this->driver()) { case 'mysql': return $this->grammar = new Query\Grammars\MySQL($this); + case 'sqlite': + return $this->grammar = new Query\Grammars\SQLite($this); + case 'sqlsrv': return $this->grammar = new Query\Grammars\SQLServer($this); + case 'pgsql': + return $this->grammar = new Query\Grammars\Postgres($this); + default: return $this->grammar = new Query\Grammars\Grammar($this); } @@ -87,14 +100,14 @@ protected function grammar() /** * Execute a callback wrapped in a database transaction. * - * @param Closure $callback + * @param callback $callback * @return void */ public function transaction($callback) { $this->pdo->beginTransaction(); - // After beginning the database transaction, we will call the Closure + // After beginning the database transaction, we will call the callback // so that it can do its database work. If an exception occurs we'll // rollback the transaction and re-throw back to the developer. try @@ -165,12 +178,14 @@ public function first($sql, $bindings = array()) */ public function query($sql, $bindings = array()) { + $sql = trim($sql); + list($statement, $result) = $this->execute($sql, $bindings); // The result we return depends on the type of query executed against the // database. On SELECT clauses, we will return the result set, for update // and deletes we will return the affected row count. - if (stripos($sql, 'select') === 0) + if (stripos($sql, 'select') === 0 || stripos($sql, 'show') === 0) { return $this->fetch($statement, Config::get('database.fetch')); } @@ -178,6 +193,13 @@ public function query($sql, $bindings = array()) { return $statement->rowCount(); } + // For insert statements that use the "returning" clause, which is allowed + // by database systems such as Postgres, we need to actually return the + // real query result so the consumer can get the ID. + elseif (stripos($sql, 'insert') === 0 and stripos($sql, 'returning') !== false) + { + return $this->fetch($statement, Config::get('database.fetch')); + } else { return $result; @@ -187,7 +209,7 @@ public function query($sql, $bindings = array()) /** * Execute a SQL query against the connection. * - * The PDO statement and boolean result will be return in an array. + * The PDO statement and boolean result will be returned in an array. * * @param string $sql * @param array $bindings @@ -209,6 +231,19 @@ protected function execute($sql, $bindings = array()) $sql = $this->grammar()->shortcut($sql, $bindings); + // Next we need to translate all DateTime bindings to their date-time + // strings that are compatible with the database. Each grammar may + // define it's own date-time format according to its needs. + $datetime = $this->grammar()->datetime; + + for ($i = 0; $i < count($bindings); $i++) + { + if ($bindings[$i] instanceof \DateTime) + { + $bindings[$i] = $bindings[$i]->format($datetime); + } + } + // Each database operation is wrapped in a try / catch so we can wrap // any database exceptions in our custom exception class, which will // set the message to include the SQL and query bindings. @@ -230,7 +265,7 @@ protected function execute($sql, $bindings = array()) throw $exception; } - // Once we have execute the query, we log the SQL, bindings, and + // Once we have executed the query, we log the SQL, bindings, and // execution time in a static array that is accessed by all of // the connections actively being used by the application. if (Config::get('database.profile')) @@ -252,7 +287,7 @@ protected function fetch($statement, $style) { // If the fetch style is "class", we'll hydrate an array of PHP // stdClass objects as generic containers for the query rows, - // otherwise we'll just use the fetch styel value. + // otherwise we'll just use the fetch style value. if ($style === PDO::FETCH_CLASS) { return $statement->fetchAll(PDO::FETCH_CLASS, 'stdClass'); @@ -287,7 +322,7 @@ protected function log($sql, $bindings, $start) */ public function driver() { - return $this->pdo->getAttribute(PDO::ATTR_DRIVER_NAME); + return $this->config['driver']; } /** @@ -298,4 +333,4 @@ public function __call($method, $parameters) return $this->table($method); } -} \ No newline at end of file +} diff --git a/app/laravel/database/connectors/postgres.php b/app/laravel/database/connectors/postgres.php index 233914fd1..d4a54344a 100644 --- a/app/laravel/database/connectors/postgres.php +++ b/app/laravel/database/connectors/postgres.php @@ -2,6 +2,18 @@ class Postgres 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. * @@ -32,6 +44,13 @@ public function connect($config) $connection->prepare("SET NAMES '{$config['charset']}'")->execute(); } + // If a schema has been specified, we'll execute a query against + // the database to set the search path. + if (isset($config['schema'])) + { + $connection->prepare("SET search_path TO '{$config['schema']}'")->execute(); + } + return $connection; } diff --git a/app/laravel/database/connectors/sqlserver.php b/app/laravel/database/connectors/sqlserver.php index 29c6929ba..04d9d7700 100644 --- a/app/laravel/database/connectors/sqlserver.php +++ b/app/laravel/database/connectors/sqlserver.php @@ -28,8 +28,16 @@ public function connect($config) // also be used to connect to Azure SQL Server databases. The port is defined // directly after the server name, so we'll create that first. $port = (isset($port)) ? ','.$port : ''; - - $dsn = "sqlsrv:Server={$host}{$port};Database={$database}"; + + //check for dblib for mac users connecting to mssql (utilizes freetds) + if (!empty($dsn_type) and $dsn_type == 'dblib') + { + $dsn = "dblib:host={$host}{$port};dbname={$database}"; + } + else + { + $dsn = "sqlsrv:Server={$host}{$port};Database={$database}"; + } return new PDO($dsn, $username, $password, $this->options($config)); } diff --git a/app/laravel/database/eloquent/model.php b/app/laravel/database/eloquent/model.php index 966a00c41..d3654a866 100644 --- a/app/laravel/database/eloquent/model.php +++ b/app/laravel/database/eloquent/model.php @@ -1,6 +1,7 @@ $value) { + // If the "raw" flag is set, it means that we'll just load every value from + // the array directly into the attributes, without any accessibility or + // mutators being accounted for. What you pass in is what you get. + if ($raw) + { + $this->set_attribute($key, $value); + + continue; + } + // If the "accessible" property is an array, the developer is limiting the // attributes that may be mass assigned, and we need to verify that the // current attribute is included in that list of allowed attributes. @@ -147,14 +164,29 @@ public function fill($attributes) return $this; } + /** + * Fill the model with the contents of the array. + * + * No mutators or accessibility checks will be accounted for. + * + * @param array $attributes + * @return Model + */ + public function fill_raw(array $attributes) + { + return $this->fill($attributes, true); + } + /** * Set the accessible attributes for the given model. * * @param array $attributes * @return void */ - public static function accessible($attributes) + public static function accessible($attributes = null) { + if (is_null($attributes)) return static::$accessible; + static::$accessible = $attributes; } @@ -186,9 +218,11 @@ public static function update($id, $attributes) { $model = new static(array(), true); - if (static::$timestamps) $attributes['updated_at'] = $model->get_timestamp(); + $model->fill($attributes); - return $model->query()->where($model->key(), '=', $id)->update($attributes); + if (static::$timestamps) $model->timestamp(); + + return $model->query()->where($model->key(), '=', $id)->update($model->attributes); } /** @@ -198,11 +232,9 @@ public static function update($id, $attributes) * @param array $columns * @return Model */ - public static function find($id, $columns = array('*')) + public function _find($id, $columns = array('*')) { - $model = new static; - - return $model->query()->where(static::$key, '=', $id)->first($columns); + return $this->query()->where(static::$key, '=', $id)->first($columns); } /** @@ -301,7 +333,7 @@ public function belongs_to($model, $foreign = null) * @param string $table * @param string $foreign * @param string $other - * @return Relationship + * @return Has_Many_And_Belongs_To */ public function has_many_and_belongs_to($model, $table = null, $foreign = null, $other = null) { @@ -348,6 +380,8 @@ public function save() $this->timestamp(); } + $this->fire_event('saving'); + // If the model exists, we only need to update it in the database, and the update // will be considered successful if there is one affected row returned from the // fluent query instance. We'll set the where condition automatically. @@ -356,6 +390,8 @@ public function save() $query = $this->query()->where(static::$key, '=', $this->get_key()); $result = $query->update($this->get_dirty()) === 1; + + if ($result) $this->fire_event('updated'); } // If the model does not exist, we will insert the record and retrieve the last @@ -363,11 +399,13 @@ public function save() // then we can consider the insert successful. else { - $id = $this->query()->insert_get_id($this->attributes, $this->sequence()); + $id = $this->query()->insert_get_id($this->attributes, $this->key()); $this->set_key($id); $this->exists = $result = is_numeric($this->get_key()); + + if ($result) $this->fire_event('created'); } // After the model has been "saved", we will set the original attributes to @@ -375,6 +413,11 @@ public function save() // dirty and subsequent calls won't hit the database. $this->original = $this->attributes; + if ($result) + { + $this->fire_event('saved'); + } + return $result; } @@ -387,7 +430,13 @@ public function delete() { if ($this->exists) { - return $this->query()->where(static::$key, '=', $this->get_key())->delete(); + $this->fire_event('deleting'); + + $result = $this->query()->where(static::$key, '=', $this->get_key())->delete(); + + $this->fire_event('deleted'); + + return $result; } } @@ -396,21 +445,22 @@ public function delete() * * @return void */ - protected function timestamp() + public function timestamp() { - $this->updated_at = $this->get_timestamp(); + $this->updated_at = new \DateTime; if ( ! $this->exists) $this->created_at = $this->updated_at; } /** - * Get the current timestamp in its storable form. + *Updates the timestamp on the model and immediately saves it. * - * @return mixed + * @return void */ - public function get_timestamp() + public function touch() { - return date('Y-m-d H:i:s'); + $this->timestamp(); + $this->save(); } /** @@ -443,7 +493,7 @@ final public function sync() */ public function changed($attribute) { - return array_get($this->attributes, $attribute) !== array_get($this->original, $attribute); + return array_get($this->attributes, $attribute) != array_get($this->original, $attribute); } /** @@ -475,7 +525,17 @@ public function table() */ public function get_dirty() { - return array_diff_assoc($this->attributes, $this->original); + $dirty = array(); + + foreach ($this->attributes as $key => $value) + { + if ( ! array_key_exists($key, $this->original) or $value != $this->original[$key]) + { + $dirty[$key] = $value; + } + } + + return $dirty; } /** @@ -485,7 +545,7 @@ public function get_dirty() */ public function get_key() { - return $this->get_attribute(static::$key); + return array_get($this->attributes, static::$key); } /** @@ -533,6 +593,70 @@ final public function purge($key) unset($this->attributes[$key]); } + /** + * Get the model attributes and relationships in array form. + * + * @return array + */ + public function to_array() + { + $attributes = array(); + + // First we need to gather all of the regular attributes. If the attribute + // exists in the array of "hidden" attributes, it will not be added to + // the array so we can easily exclude things like passwords, etc. + foreach (array_keys($this->attributes) as $attribute) + { + if ( ! in_array($attribute, static::$hidden)) + { + $attributes[$attribute] = $this->$attribute; + } + } + + foreach ($this->relationships as $name => $models) + { + // If the relationship is not a "to-many" relationship, we can just + // to_array the related model and add it as an attribute to the + // array of existing regular attributes we gathered. + if ($models instanceof Model) + { + $attributes[$name] = $models->to_array(); + } + + // If the relationship is a "to-many" relationship we need to spin + // through each of the related models and add each one with the + // to_array method, keying them both by name and ID. + elseif (is_array($models)) + { + $attributes[$name] = array(); + + foreach ($models as $id => $model) + { + $attributes[$name][$id] = $model->to_array(); + } + } + elseif (is_null($models)) + { + $attributes[$name] = $models; + } + } + + return $attributes; + } + + /** + * Fire a given event for the model. + * + * @param string $event + * @return array + */ + protected function fire_event($event) + { + $events = array("eloquent.{$event}", "eloquent.{$event}: ".get_class($this)); + + Event::fire($events, array($this)); + } + /** * Handle the dynamic retrieval of attributes and associations. * @@ -598,6 +722,8 @@ public function __isset($key) { if (array_key_exists($key, $this->$source)) return true; } + + if (method_exists($this, $key)) return true; } /** @@ -623,18 +749,22 @@ public function __unset($key) */ public function __call($method, $parameters) { + $meta = array('key', 'table', 'connection', 'sequence', 'per_page', 'timestamps'); + // If the method is actually the name of a static property on the model we'll // return the value of the static property. This makes it convenient for // relationships to access these values off of the instances. - if (in_array($method, array('key', 'table', 'connection', 'sequence', 'per_page'))) + if (in_array($method, $meta)) { return static::$$method; } + $underscored = array('with', 'find'); + // Some methods need to be accessed both staticly and non-staticly so we'll // keep underscored methods of those methods and intercept calls to them // here so they can be called either way on the model instance. - if (in_array($method, array('with'))) + if (in_array($method, $underscored)) { return call_user_func_array(array($this, '_'.$method), $parameters); } @@ -644,11 +774,11 @@ public function __call($method, $parameters) // to perform the appropriate action based on the method. if (starts_with($method, 'get_')) { - return $this->attributes[substr($method, 4)]; + return $this->get_attribute(substr($method, 4)); } elseif (starts_with($method, 'set_')) { - $this->attributes[substr($method, 4)] = $parameters[0]; + $this->set_attribute(substr($method, 4), $parameters[0]); } // Finally we will assume that the method is actually the beginning of a diff --git a/app/laravel/database/eloquent/pivot.php b/app/laravel/database/eloquent/pivot.php index b98fc2264..a90d7e7d6 100644 --- a/app/laravel/database/eloquent/pivot.php +++ b/app/laravel/database/eloquent/pivot.php @@ -20,11 +20,13 @@ class Pivot extends Model { * Create a new pivot table instance. * * @param string $table + * @param string $connection * @return void */ - public function __construct($table) + public function __construct($table, $connection = null) { $this->pivot_table = $table; + static::$connection = $connection; parent::__construct(array(), true); } diff --git a/app/laravel/database/eloquent/query.php b/app/laravel/database/eloquent/query.php index e55e6a408..3aee79c95 100644 --- a/app/laravel/database/eloquent/query.php +++ b/app/laravel/database/eloquent/query.php @@ -1,5 +1,6 @@ hydrate($this->model, $this->table->get($columns), $keyed); + return $this->hydrate($this->model, $this->table->get($columns)); } /** @@ -100,10 +100,9 @@ public function paginate($per_page = null, $columns = array('*')) * * @param Model $model * @param array $results - * @param bool $keyed * @return array */ - public function hydrate($model, $results, $keyed = true) + public function hydrate($model, $results) { $class = get_class($model); @@ -121,33 +120,18 @@ public function hydrate($model, $results, $keyed = true) // We need to set the attributes manually in case the accessible property is // set on the array which will prevent the mass assignemnt of attributes if // we were to pass them in using the constructor or fill methods. - foreach ($result as $key => $value) - { - $new->set_attribute($key, $value); - } - - $new->original = $new->attributes; + $new->fill_raw($result); - // Typically, the resulting models are keyed by their primary key, but it - // may be useful to not do this in some circumstances such as when we - // are eager loading a *-to-* relationships which has duplicates. - if ($keyed) - { - $models[$result[$this->model->key()]] = $new; - } - else - { - $models[] = $new; - } + $models[] = $new; } if (count($results) > 0) { foreach ($this->model_includes() as $relationship => $constraints) { - // If the relationship is nested, we will skip laoding it here and let + // If the relationship is nested, we will skip loading it here and let // the load method parse and set the nested eager loads on the right - // relationship when it is getting ready to eager laod. + // relationship when it is getting ready to eager load. if (str_contains($relationship, '.')) { continue; @@ -199,17 +183,7 @@ protected function load(&$results, $relationship, $constraints) $query->initialize($results, $relationship); - // If we're eager loading a many-to-many relationship we will disable - // the primary key indexing on the hydration since there could be - // roles shared across users and we don't want to overwrite. - if ( ! $query instanceof Has_Many_And_Belongs_To) - { - $query->match($relationship, $results, $query->get()); - } - else - { - $query->match($relationship, $results, $query->get(array('*'), false)); - } + $query->match($relationship, $results, $query->get()); } /** @@ -293,8 +267,8 @@ public function __call($method, $parameters) $result = call_user_func_array(array($this->table, $method), $parameters); // Some methods may get their results straight from the fluent query - // builder, such as the aggregate methods. If the called method is - // one of these, we will return the result straight away. + // builder such as the aggregate methods. If the called method is + // one of these, we will just return the result straight away. if (in_array($method, $this->passthru)) { return $result; diff --git a/app/laravel/database/eloquent/relationships/belongs_to.php b/app/laravel/database/eloquent/relationships/belongs_to.php index b73c57bf3..79802ee97 100644 --- a/app/laravel/database/eloquent/relationships/belongs_to.php +++ b/app/laravel/database/eloquent/relationships/belongs_to.php @@ -32,7 +32,7 @@ public function update($attributes) */ protected function constrain() { - $this->table->where($this->base->key(), '=', $this->foreign_value()); + $this->table->where($this->model->key(), '=', $this->foreign_value()); } /** @@ -65,9 +65,14 @@ public function eagerly_constrain($results) // are looking for the parent of a child model in this relationship. foreach ($results as $result) { - $keys[] = $result->{$this->foreign_key()}; + if ( ! is_null($key = $result->{$this->foreign_key()})) + { + $keys[] = $key; + } } + if (count($keys) == 0) $keys = array(0); + $this->table->where_in($this->model->key(), array_unique($keys)); } @@ -82,11 +87,18 @@ public function match($relationship, &$children, $parents) { $foreign = $this->foreign_key(); - foreach ($children as &$child) + $dictionary = array(); + + foreach ($parents as $parent) + { + $dictionary[$parent->get_key()] = $parent; + } + + foreach ($children as $child) { - if (array_key_exists($child->$foreign, $parents)) + if (array_key_exists($child->$foreign, $dictionary)) { - $child->relationships[$relationship] = $parents[$child->$foreign]; + $child->relationships[$relationship] = $dictionary[$child->$foreign]; } } } diff --git a/app/laravel/database/eloquent/relationships/has_many.php b/app/laravel/database/eloquent/relationships/has_many.php index b80d3844c..b791a542a 100644 --- a/app/laravel/database/eloquent/relationships/has_many.php +++ b/app/laravel/database/eloquent/relationships/has_many.php @@ -12,6 +12,59 @@ public function results() return parent::get(); } + /** + * Sync the association table with an array of models. + * + * @param mixed $models + * @return bool + */ + public function save($models) + { + // If the given "models" are not an array, we'll force them into an array so + // we can conveniently loop through them and insert all of them into the + // related database table assigned to the associated model instance. + if ( ! is_array($models)) $models = array($models); + + $current = $this->table->lists($this->model->key()); + + foreach ($models as $attributes) + { + $class = get_class($this->model); + + // If the "attributes" are actually an array of the related model we'll + // just use the existing instance instead of creating a fresh model + // instance for the attributes. This allows for validation. + if ($attributes instanceof $class) + { + $model = $attributes; + } + else + { + $model = $this->fresh_model($attributes); + } + + // We'll need to associate the model with its parent, so we'll set the + // foreign key on the model to the key of the parent model, making + // sure that the two models are associated in the database. + $foreign = $this->foreign_key(); + + $model->$foreign = $this->base->get_key(); + + $id = $model->get_key(); + + $model->exists = ( ! is_null($id) and in_array($id, $current)); + + // Before saving we'll force the entire model to be "dirty" so all of + // the attributes are saved. It shouldn't affect the updates as + // saving all the attributes shouldn't hurt anything. + $model->original = array(); + + $model->save(); + } + + return true; + } + /** * Initialize a relationship on an array of parent models. * @@ -38,9 +91,19 @@ public function match($relationship, &$parents, $children) { $foreign = $this->foreign_key(); - foreach ($children as $key => $child) + $dictionary = array(); + + foreach ($children as $child) + { + $dictionary[$child->$foreign][] = $child; + } + + foreach ($parents as $parent) { - $parents[$child->$foreign]->relationships[$relationship][$child->get_key()] = $child; + if (array_key_exists($key = $parent->get_key(), $dictionary)) + { + $parent->relationships[$relationship] = $dictionary[$key]; + } } } diff --git a/app/laravel/database/eloquent/relationships/has_many_and_belongs_to.php b/app/laravel/database/eloquent/relationships/has_many_and_belongs_to.php index 9fecb66d5..7dc8b0a34 100644 --- a/app/laravel/database/eloquent/relationships/has_many_and_belongs_to.php +++ b/app/laravel/database/eloquent/relationships/has_many_and_belongs_to.php @@ -25,7 +25,7 @@ class Has_Many_And_Belongs_To extends Relationship { * * @var array */ - protected $with = array('id', 'created_at', 'updated_at'); + protected $with = array('id'); /** * Create a new many to many relationship instance. @@ -43,6 +43,16 @@ public function __construct($model, $associated, $table, $foreign, $other) $this->joining = $table ?: $this->joining($model, $associated); + // If the Pivot table is timestamped, we'll set the timestamp columns to be + // fetched when the pivot table models are fetched by the developer else + // the ID will be the only "extra" column fetched in by default. + if (Pivot::$timestamps) + { + $this->with[] = 'created_at'; + + $this->with[] = 'updated_at'; + } + parent::__construct($model, $associated, $foreign); } @@ -75,17 +85,66 @@ public function results() /** * Insert a new record into the joining table of the association. * - * @param int $id - * @param array $joining + * @param Model|int $id + * @param array $attributes * @return bool */ public function attach($id, $attributes = array()) { + if ($id instanceof Model) $id = $id->get_key(); + $joining = array_merge($this->join_record($id), $attributes); return $this->insert_joining($joining); } + /** + * Detach a record from the joining table of the association. + * + * @param array|Model|int $ids + * @return bool + */ + public function detach($ids) + { + if ($ids instanceof Model) $ids = array($ids->get_key()); + elseif ( ! is_array($ids)) $ids = array($ids); + + return $this->pivot()->where_in($this->other_key(), $ids)->delete(); + } + + /** + * Sync the joining table with the array of given IDs. + * + * @param array $ids + * @return bool + */ + public function sync($ids) + { + $current = $this->pivot()->lists($this->other_key()); + $ids = (array) $ids; + + // First we need to attach any of the associated models that are not currently + // in the joining table. We'll spin through the given IDs, checking to see + // if they exist in the array of current ones, and if not we insert. + foreach ($ids as $id) + { + if ( ! in_array($id, $current)) + { + $this->attach($id); + } + } + + // Next we will take the difference of the current and given IDs and detach + // all of the entities that exists in the current array but are not in + // the array of IDs given to the method, finishing the sync. + $detach = array_diff($current, $ids); + + if (count($detach) > 0) + { + $this->detach($detach); + } + } + /** * Insert a new record for the association. * @@ -147,9 +206,12 @@ protected function join_record($id) */ protected function insert_joining($attributes) { - $attributes['created_at'] = $this->model->get_timestamp(); + if (Pivot::$timestamps) + { + $attributes['created_at'] = new \DateTime; - $attributes['updated_at'] = $attributes['created_at']; + $attributes['updated_at'] = $attributes['created_at']; + } return $this->joining_table()->insert($attributes); } @@ -192,7 +254,7 @@ protected function set_select($foreign, $other) $this->with = array_merge($this->with, array($foreign, $other)); // Since pivot tables may have extra information on them that the developer - // needs, we allow an extra array of columns to be specified that will be + // needs we allow an extra array of columns to be specified that will be // fetched from the pivot table and hydrate into the pivot model. foreach ($this->with as $column) { @@ -253,7 +315,7 @@ public function initialize(&$parents, $relationship) */ public function eagerly_constrain($results) { - $this->table->where_in($this->joining.'.'.$this->foreign_key(), array_keys($results)); + $this->table->where_in($this->joining.'.'.$this->foreign_key(), $this->keys($results)); } /** @@ -267,9 +329,19 @@ public function match($relationship, &$parents, $children) { $foreign = $this->foreign_key(); - foreach ($children as $key => $child) + $dictionary = array(); + + foreach ($children as $child) + { + $dictionary[$child->pivot->$foreign][] = $child; + } + + foreach ($parents as $parent) { - $parents[$child->pivot->$foreign]->relationships[$relationship][$child->{$child->key()}] = $child; + if (array_key_exists($key = $parent->get_key(), $dictionary)) + { + $parent->relationships[$relationship] = $dictionary[$key]; + } } } @@ -286,11 +358,11 @@ protected function hydrate_pivot(&$results) // Every model result for a many-to-many relationship needs a Pivot instance // to represent the pivot table's columns. Sometimes extra columns are on // the pivot table that may need to be accessed by the developer. - $pivot = new Pivot($this->joining); + $pivot = new Pivot($this->joining, $this->model->connection()); // If the attribute key starts with "pivot_", we know this is a column on // the pivot table, so we will move it to the Pivot model and purge it - // from the model since it actually belongs to the pivot. + // from the model since it actually belongs to the pivot model. foreach ($result->attributes as $key => $value) { if (starts_with($key, 'pivot_')) @@ -320,9 +392,9 @@ public function with($columns) { $columns = (is_array($columns)) ? $columns : func_get_args(); - // The "with" array contains a couple of columns by default, so we will - // just merge in the developer specified columns here, and we'll make - // sure the values of the array are unique. + // The "with" array contains a couple of columns by default, so we will just + // merge in the developer specified columns here, and we will make sure + // the values of the array are unique to avoid duplicates. $this->with = array_unique(array_merge($this->with, $columns)); $this->set_select($this->foreign_key(), $this->other_key()); @@ -331,17 +403,15 @@ public function with($columns) } /** - * Get a model instance of the pivot table for the relationship. + * Get a relationship instance of the pivot table. * - * @return Pivot + * @return Has_Many */ public function pivot() { - $key = $this->base->get_key(); - - $foreign = $this->foreign_key(); + $pivot = new Pivot($this->joining, $this->model->connection()); - return with(new Pivot($this->joining))->where($foreign, '=', $key); + return new Has_Many($this->base, $pivot, $this->foreign_key()); } /** diff --git a/app/laravel/database/eloquent/relationships/has_one.php b/app/laravel/database/eloquent/relationships/has_one.php index 077c3ada1..5a9ea7608 100644 --- a/app/laravel/database/eloquent/relationships/has_one.php +++ b/app/laravel/database/eloquent/relationships/has_one.php @@ -38,9 +38,19 @@ public function match($relationship, &$parents, $children) { $foreign = $this->foreign_key(); - foreach ($children as $key => $child) + $dictionary = array(); + + foreach ($children as $child) + { + $dictionary[$child->$foreign] = $child; + } + + foreach ($parents as $parent) { - $parents[$child->$foreign]->relationships[$relationship] = $child; + if (array_key_exists($key = $parent->get_key(), $dictionary)) + { + $parent->relationships[$relationship] = $dictionary[$key]; + } } } diff --git a/app/laravel/database/eloquent/relationships/has_one_or_many.php b/app/laravel/database/eloquent/relationships/has_one_or_many.php index 2cdac83ec..e7bfc0b05 100644 --- a/app/laravel/database/eloquent/relationships/has_one_or_many.php +++ b/app/laravel/database/eloquent/relationships/has_one_or_many.php @@ -7,16 +7,41 @@ class Has_One_Or_Many extends Relationship { /** * Insert a new record for the association. * + * If save is successful, the model will be returned, otherwise false. + * * @param Model|array $attributes - * @return bool + * @return Model|false */ public function insert($attributes) { - $attributes = ($attributes instanceof Model) ? $attributes->attributes : $attributes; + if ($attributes instanceof Model) + { + $attributes->set_attribute($this->foreign_key(), $this->base->get_key()); + + return $attributes->save() ? $attributes : false; + } + else + { + $attributes[$this->foreign_key()] = $this->base->get_key(); - $attributes[$this->foreign_key()] = $this->base->get_key(); + return $this->model->create($attributes); + } + } + + /** + * Update a record for the association. + * + * @param array $attributes + * @return bool + */ + public function update(array $attributes) + { + if ($this->model->timestamps()) + { + $attributes['updated_at'] = new \DateTime; + } - return $this->model->create($attributes); + return $this->table->update($attributes); } /** @@ -37,7 +62,7 @@ protected function constrain() */ public function eagerly_constrain($results) { - $this->table->where_in($this->foreign_key(), array_keys($results)); + $this->table->where_in($this->foreign_key(), $this->keys($results)); } } \ No newline at end of file diff --git a/app/laravel/database/eloquent/relationships/relationship.php b/app/laravel/database/eloquent/relationships/relationship.php index 2ec10aa45..34c03f6b0 100644 --- a/app/laravel/database/eloquent/relationships/relationship.php +++ b/app/laravel/database/eloquent/relationships/relationship.php @@ -101,4 +101,22 @@ public function foreign_key() return static::foreign($this->base, $this->foreign); } + /** + * Gather all the primary keys from a result set. + * + * @param array $results + * @return array + */ + public function keys($results) + { + $keys = array(); + + foreach ($results as $result) + { + $keys[] = $result->get_key(); + } + + return array_unique($keys); + } + } \ No newline at end of file diff --git a/app/laravel/database/exception.php b/app/laravel/database/exception.php index a116ad70c..d8f425a54 100644 --- a/app/laravel/database/exception.php +++ b/app/laravel/database/exception.php @@ -24,6 +24,16 @@ public function __construct($sql, $bindings, \Exception $inner) $this->setMessage($sql, $bindings); } + /** + * Get the inner exception. + * + * @return Exception + */ + public function getInner() + { + return $this->inner; + } + /** * Set the exception message to include the SQL and bindings. * diff --git a/app/laravel/database/grammar.php b/app/laravel/database/grammar.php index 13d92d8ea..0ff5bd14a 100644 --- a/app/laravel/database/grammar.php +++ b/app/laravel/database/grammar.php @@ -35,7 +35,7 @@ public function __construct(Connection $connection) */ public function wrap_table($table) { - // Expressions should be injected into the query as raw strings so + // Expressions should be injected into the query as raw strings // so we do not want to wrap them in any way. We will just return // the string value from the expression to be included. if ($table instanceof Expression) @@ -64,7 +64,7 @@ public function wrap_table($table) */ public function wrap($value) { - // Expressions should be injected into the query as raw strings so + // Expressions should be injected into the query as raw strings // so we do not want to wrap them in any way. We will just return // the string value from the expression to be included. if ($value instanceof Expression) diff --git a/app/laravel/database/query.php b/app/laravel/database/query.php old mode 100644 new mode 100755 index 9d221176c..0b2029c14 --- a/app/laravel/database/query.php +++ b/app/laravel/database/query.php @@ -3,6 +3,7 @@ use Closure; use Laravel\Database; use Laravel\Paginator; +use Laravel\Database\Query\Grammars\Postgres; use Laravel\Database\Query\Grammars\SQLServer; class Query { @@ -70,6 +71,13 @@ class Query { */ public $groupings; + /** + * The HAVING clauses. + * + * @var array + */ + public $havings; + /** * The ORDER BY clauses. * @@ -334,6 +342,67 @@ public function or_where_not_in($column, $values) { return $this->where_not_in($column, $values, 'OR'); } + + /** + * Add a BETWEEN condition to the query + * + * @param string $column + * @param mixed $min + * @param mixed $max + * @param string $connector + * @param boolean $not + * @return Query + */ + public function where_between($column, $min, $max, $connector = 'AND', $not = false) + { + $type = ($not) ? 'where_not_between' : 'where_between'; + + $this->wheres[] = compact('type', 'column', 'min', 'max', 'connector'); + + $this->bindings[] = $min; + $this->bindings[] = $max; + + return $this; + } + + /** + * Add a OR BETWEEN condition to the query + * + * @param string $column + * @param mixed $min + * @param mixed $max + * @return Query + */ + public function or_where_between($column, $min, $max) + { + return $this->where_between($column, $min, $max, 'OR'); + } + + /** + * Add a NOT BETWEEN condition to the query + * + * @param string $column + * @param mixed $min + * @param mixed $max + * @return Query + */ + public function where_not_between($column, $min, $max, $connector = 'AND') + { + return $this->where_between($column, $min, $max, $connector, true); + } + + /** + * Add a OR NOT BETWEEN condition to the query + * + * @param string $column + * @param mixed $min + * @param mixed $max + * @return Query + */ + public function or_where_not_between($column, $min, $max) + { + return $this->where_not_between($column, $min, $max, 'OR'); + } /** * Add a where null condition to the query. @@ -407,7 +476,10 @@ public function where_nested($callback, $connector = 'AND') // Once the callback has been run on the query, we will store the nested // query instance on the where clause array so that it's passed to the // query's query grammar instance when building. - $this->wheres[] = compact('type', 'query', 'connector'); + if ($query->wheres !== null) + { + $this->wheres[] = compact('type', 'query', 'connector'); + } $this->bindings = array_merge($this->bindings, $query->bindings); @@ -475,6 +547,22 @@ public function group_by($column) return $this; } + /** + * Add a having to the query. + * + * @param string $column + * @param string $operator + * @param mixed $value + */ + public function having($column, $operator, $value) + { + $this->havings[] = compact('column', 'operator', 'value'); + + $this->bindings[] = $value; + + return $this; + } + /** * Add an ordering to the query. * @@ -594,7 +682,7 @@ public function lists($column, $key = null) // set the keys on the array of values using the array_combine // function provided by PHP, which should give us the proper // array form to return from the method. - if ( ! is_null($key)) + if ( ! is_null($key) && count($results)) { return array_combine(array_map(function($row) use ($key) { @@ -725,19 +813,23 @@ public function insert($values) * Insert an array of values into the database table and return the ID. * * @param array $values - * @param string $sequence + * @param string $column * @return int */ - public function insert_get_id($values, $sequence = null) + public function insert_get_id($values, $column = 'id') { - $sql = $this->grammar->insert($this, $values); + $sql = $this->grammar->insert_get_id($this, $values, $column); - $this->connection->query($sql, array_values($values)); + $result = $this->connection->query($sql, array_values($values)); - // Some database systems (Postgres) require a sequence name to be - // given when retrieving the auto-incrementing ID, so we'll pass - // the given sequence into the method just in case. - return (int) $this->connection->pdo->lastInsertId($sequence); + if ($this->grammar instanceof Postgres) + { + return (int) $result[0]->$column; + } + else + { + return (int) $this->connection->pdo->lastInsertId(); + } } /** diff --git a/app/laravel/database/query/grammars/grammar.php b/app/laravel/database/query/grammars/grammar.php old mode 100644 new mode 100755 index 50d2b2072..d5d8a2e7d --- a/app/laravel/database/query/grammars/grammar.php +++ b/app/laravel/database/query/grammars/grammar.php @@ -6,13 +6,20 @@ class Grammar extends \Laravel\Database\Grammar { /** - * All of the query componenets in the order they should be built. + * The format for properly saving a DateTime. + * + * @var string + */ + public $datetime = 'Y-m-d H:i:s'; + + /** + * All of the query components in the order they should be built. * * @var array */ protected $components = array( 'aggregate', 'selects', 'from', 'joins', 'wheres', - 'groupings', 'orderings', 'limit', 'offset', + 'groupings', 'havings', 'orderings', 'limit', 'offset', ); /** @@ -118,7 +125,7 @@ protected function from(Query $query) protected function joins(Query $query) { // We need to iterate through each JOIN clause that is attached to the - // query an translate it into SQL. The table and the columns will be + // query and translate it into SQL. The table and the columns will be // wrapped in identifiers to avoid naming collisions. foreach ($query->joins as $join) { @@ -128,7 +135,7 @@ protected function joins(Query $query) // Each JOIN statement may have multiple clauses, so we will iterate // through each clause creating the conditions then we'll join all - // of the together at the end to build the clause. + // of them together at the end to build the clause. foreach ($join->clauses as $clause) { extract($clause); @@ -142,7 +149,7 @@ protected function joins(Query $query) // The first clause will have a connector on the front, but it is // not needed on the first condition, so we will strip it off of - // the condition before adding it to the arrya of joins. + // the condition before adding it to the array of joins. $search = array('AND ', 'OR '); $clauses[0] = str_replace($search, '', $clauses[0]); @@ -235,6 +242,33 @@ protected function where_not_in($where) return $this->wrap($where['column']).' NOT IN ('.$parameters.')'; } + /** + * Compile a WHERE BETWEEN clause + * + * @param array $where + * @return string + */ + protected function where_between($where) + { + $min = $this->parameter($where['min']); + $max = $this->parameter($where['max']); + + return $this->wrap($where['column']).' BETWEEN '.$min.' AND '.$max; + } + + /** + * Compile a WHERE NOT BETWEEN clause + * @param array $where + * @return string + */ + protected function where_not_between($where) + { + $min = $this->parameter($where['min']); + $max = $this->parameter($where['max']); + + return $this->wrap($where['column']).' NOT BETWEEN '.$min.' AND '.$max; + } + /** * Compile a WHERE NULL clause. * @@ -279,6 +313,24 @@ protected function groupings(Query $query) return 'GROUP BY '.$this->columnize($query->groupings); } + /** + * Compile the HAVING clause for a query. + * + * @param Query $query + * @return string + */ + protected function havings(Query $query) + { + if (is_null($query->havings)) return ''; + + foreach ($query->havings as $having) + { + $sql[] = 'AND '.$this->wrap($having['column']).' '.$having['operator'].' '.$this->parameter($having['value']); + } + + return 'HAVING '.preg_replace('/AND /', '', implode(' ', $sql), 1); + } + /** * Compile the ORDER BY clause for a query. * @@ -318,7 +370,7 @@ protected function offset(Query $query) } /** - * Compile a SQL INSERT statment from a Query instance. + * Compile a SQL INSERT statement from a Query instance. * * This method handles the compilation of single row inserts and batch inserts. * @@ -341,7 +393,7 @@ public function insert(Query $query, $values) $columns = $this->columnize(array_keys(reset($values))); // Build the list of parameter place-holders of values bound to the query. - // Each insert should have the same number of bound paramters, so we can + // Each insert should have the same number of bound parameters, so we can // just use the first array of values. $parameters = $this->parameterize(reset($values)); @@ -351,7 +403,20 @@ public function insert(Query $query, $values) } /** - * Compile a SQL UPDATE statment from a Query instance. + * Compile a SQL INSERT and get ID statement from a Query instance. + * + * @param Query $query + * @param array $values + * @param string $column + * @return string + */ + public function insert_get_id(Query $query, $values, $column) + { + return $this->insert($query, $values); + } + + /** + * Compile a SQL UPDATE statement from a Query instance. * * @param Query $query * @param array $values @@ -372,13 +437,13 @@ public function update(Query $query, $values) $columns = implode(', ', $columns); // UPDATE statements may be constrained by a WHERE clause, so we'll run - // the entire where compilation process for those contraints. This is + // the entire where compilation process for those constraints. This is // easily achieved by passing it to the "wheres" method. return trim("UPDATE {$table} SET {$columns} ".$this->wheres($query)); } /** - * Compile a SQL DELETE statment from a Query instance. + * Compile a SQL DELETE statement from a Query instance. * * @param Query $query * @return string diff --git a/app/laravel/database/query/grammars/postgres.php b/app/laravel/database/query/grammars/postgres.php new file mode 100644 index 000000000..6041ae82a --- /dev/null +++ b/app/laravel/database/query/grammars/postgres.php @@ -0,0 +1,20 @@ +insert($query, $values)." RETURNING $column"; + } + +} \ No newline at end of file diff --git a/app/laravel/database/query/grammars/sqlite.php b/app/laravel/database/query/grammars/sqlite.php new file mode 100644 index 000000000..cacc936e5 --- /dev/null +++ b/app/laravel/database/query/grammars/sqlite.php @@ -0,0 +1,24 @@ +orderings as $ordering) + { + $sql[] = $this->wrap($ordering['column']).' COLLATE NOCASE '.strtoupper($ordering['direction']); + } + + return 'ORDER BY '.implode(', ', $sql); + } + +} \ No newline at end of file diff --git a/app/laravel/database/query/grammars/sqlserver.php b/app/laravel/database/query/grammars/sqlserver.php index 8edb30c0e..f912f562f 100644 --- a/app/laravel/database/query/grammars/sqlserver.php +++ b/app/laravel/database/query/grammars/sqlserver.php @@ -11,6 +11,13 @@ class SQLServer extends Grammar { */ protected $wrapper = '[%s]'; + /** + * The format for properly saving a DateTime. + * + * @var string + */ + public $datetime = 'Y-m-d H:i:s.000'; + /** * Compile a SQL SELECT statement from a Query instance. * @@ -88,7 +95,7 @@ protected function ansi_offset(Query $query, $components) // Next we need to calculate the constraint that should be placed on // the row number to get the correct offset and limit on the query. - // If there is not limit, we'll just handle the offset. + // If there is not a limit, we'll just handle the offset. if ($query->limit > 0) { $finish = $query->offset + $query->limit; diff --git a/app/laravel/database/schema.php b/app/laravel/database/schema.php index 3787e33bc..e09b8b162 100644 --- a/app/laravel/database/schema.php +++ b/app/laravel/database/schema.php @@ -40,16 +40,38 @@ public static function create($table, $callback) return static::execute($table); } + /** + * Rename a database table in the schema. + * + * @param string $table + * @param string $new_name + * @return void + */ + public static function rename($table, $new_name) + { + $table = new Schema\Table($table); + + // To indicate that the table needs to be renamed, we will run the + // "rename" command on the table instance and pass the instance to + // the execute method as calling a Closure isn't needed. + $table->rename($new_name); + + return static::execute($table); + } + /** * Drop a database table from the schema. * * @param string $table + * @param string $connection * @return void */ - public static function drop($table) + public static function drop($table, $connection = null) { $table = new Schema\Table($table); + $table->on($connection); + // To indicate that the table needs to be dropped, we will run the // "drop" command on the table instance and pass the instance to // the execute method as calling a Closure isn't needed. @@ -68,7 +90,7 @@ public static function execute($table) { // The implications method is responsible for finding any fluently // defined indexes on the schema table and adding the explicit - // commands that are needed to tbe schema instance. + // commands that are needed for the schema instance. static::implications($table); foreach ($table->commands as $command) @@ -145,6 +167,11 @@ public static function grammar(Connection $connection) { $driver = $connection->driver(); + if (isset(\Laravel\Database::$registrar[$driver])) + { + return \Laravel\Database::$registrar[$driver]['schema'](); + } + switch ($driver) { case 'mysql': @@ -163,4 +190,4 @@ public static function grammar(Connection $connection) throw new \Exception("Schema operations not supported for [$driver]."); } -} \ No newline at end of file +} diff --git a/app/laravel/database/schema/grammars/grammar.php b/app/laravel/database/schema/grammars/grammar.php index 1d3390a1c..c33d8dd72 100644 --- a/app/laravel/database/schema/grammars/grammar.php +++ b/app/laravel/database/schema/grammars/grammar.php @@ -9,7 +9,7 @@ abstract class Grammar extends \Laravel\Database\Grammar { * Generate the SQL statement for creating a foreign key. * * @param Table $table - * @param Command $command + * @param Fluent $command * @return string */ public function foreign(Table $table, Fluent $command) @@ -21,7 +21,7 @@ public function foreign(Table $table, Fluent $command) // command is being executed and the referenced table are wrapped. $table = $this->wrap($table); - $on = $this->wrap($command->on); + $on = $this->wrap_table($command->on); // Next we need to columnize both the command table's columns as well as // the columns referenced by the foreign key. We'll cast the referenced @@ -50,11 +50,23 @@ public function foreign(Table $table, Fluent $command) return $sql; } + /** + * Generate the SQL statement for a drop table command. + * + * @param Table $table + * @param Fluent $command + * @return string + */ + public function drop(Table $table, Fluent $command) + { + return 'DROP TABLE '.$this->wrap($table); + } + /** * Drop a constraint from the table. * * @param Table $table - * @param Fluent $fluent + * @param Fluent $command * @return string */ protected function drop_constraint(Table $table, Fluent $command) @@ -96,4 +108,19 @@ protected function type(Fluent $column) return $this->{'type_'.$column->type}($column); } + /** + * Format a value so that it can be used in SQL DEFAULT clauses. + * @param mixed $value + * @return string + */ + protected function default_value($value) + { + if (is_bool($value)) + { + return intval($value); + } + + return strval($value); + } + } \ No newline at end of file diff --git a/app/laravel/database/schema/grammars/mysql.php b/app/laravel/database/schema/grammars/mysql.php index c2ae7455e..93b22a4e3 100644 --- a/app/laravel/database/schema/grammars/mysql.php +++ b/app/laravel/database/schema/grammars/mysql.php @@ -37,7 +37,7 @@ public function create(Table $table, Fluent $command) } /** - * Geenrate the SQL statements for a table modification command. + * Generate the SQL statements for a table modification command. * * @param Table $table * @param Fluent $command @@ -47,7 +47,7 @@ public function add(Table $table, Fluent $command) { $columns = $this->columns($table); - // Once we the array of column definitions, we need to add "add" to the + // Once we have the array of column definitions, we need to add "add" to the // front of each definition, then we'll concatenate the definitions // using commas like normal and generate the SQL. $columns = implode(', ', array_map(function($column) @@ -99,7 +99,7 @@ protected function columns(Table $table) */ protected function unsigned(Table $table, Fluent $column) { - if ($column->type == 'integer' && $column->unsigned) + if ($column->type == 'integer' && ($column->unsigned || $column->increment)) { return ' UNSIGNED'; } @@ -128,7 +128,7 @@ protected function defaults(Table $table, Fluent $column) { if ( ! is_null($column->default)) { - return " DEFAULT '".$column->default."'"; + return " DEFAULT '".$this->default_value($column->default)."'"; } } @@ -213,15 +213,15 @@ protected function key(Table $table, Fluent $command, $type) } /** - * Generate the SQL statement for a drop table command. + * Generate the SQL statement for a rename table command. * * @param Table $table * @param Fluent $command * @return string */ - public function drop(Table $table, Fluent $command) + public function rename(Table $table, Fluent $command) { - return 'DROP TABLE '.$this->wrap($table); + return 'RENAME TABLE '.$this->wrap($table).' TO '.$this->wrap($command->name); } /** @@ -260,7 +260,7 @@ public function drop_primary(Table $table, Fluent $command) } /** - * Generate the SQL statement for a drop unqique key command. + * Generate the SQL statement for a drop unique key command. * * @param Table $table * @param Fluent $command @@ -284,7 +284,7 @@ public function drop_fulltext(Table $table, Fluent $command) } /** - * Generate the SQL statement for a drop unqique key command. + * Generate the SQL statement for a drop unique key command. * * @param Table $table * @param Fluent $command @@ -311,7 +311,7 @@ protected function drop_key(Table $table, Fluent $command) * Drop a foreign key constraint from the table. * * @param Table $table - * @param Fluent $fluent + * @param Fluent $command * @return string */ public function drop_foreign(Table $table, Fluent $command) @@ -353,7 +353,7 @@ protected function type_float(Fluent $column) } /** - * Generate the data-type definintion for a decimal. + * Generate the data-type definition for a decimal. * * @param Fluent $column * @return string @@ -371,7 +371,7 @@ protected function type_decimal(Fluent $column) */ protected function type_boolean(Fluent $column) { - return 'TINYINT'; + return 'TINYINT(1)'; } /** @@ -418,4 +418,4 @@ protected function type_blob(Fluent $column) return 'BLOB'; } -} \ No newline at end of file +} diff --git a/app/laravel/database/schema/grammars/postgres.php b/app/laravel/database/schema/grammars/postgres.php index 760af1a8a..1382acc54 100644 --- a/app/laravel/database/schema/grammars/postgres.php +++ b/app/laravel/database/schema/grammars/postgres.php @@ -25,7 +25,7 @@ public function create(Table $table, Fluent $command) } /** - * Geenrate the SQL statements for a table modification command. + * Generate the SQL statements for a table modification command. * * @param Table $table * @param Fluent $command @@ -35,7 +35,7 @@ public function add(Table $table, Fluent $command) { $columns = $this->columns($table); - // Once we the array of column definitions, we need to add "add" to the + // Once we have the array of column definitions, we need to add "add" to the // front of each definition, then we'll concatenate the definitions // using commas like normal and generate the SQL. $columns = implode(', ', array_map(function($column) @@ -101,7 +101,7 @@ protected function defaults(Table $table, Fluent $column) { if ( ! is_null($column->default)) { - return " DEFAULT '".$column->default."'"; + return " DEFAULT '".$this->default_value($column->default)."'"; } } @@ -199,15 +199,15 @@ protected function key(Table $table, Fluent $command, $unique = false) } /** - * Generate the SQL statement for a drop table command. + * Generate the SQL statement for a rename table command. * * @param Table $table * @param Fluent $command * @return string */ - public function drop(Table $table, Fluent $command) + public function rename(Table $table, Fluent $command) { - return 'DROP TABLE '.$this->wrap($table); + return 'ALTER TABLE '.$this->wrap($table).' RENAME TO '.$this->wrap($command->name); } /** @@ -246,7 +246,7 @@ public function drop_primary(Table $table, Fluent $command) } /** - * Generate the SQL statement for a drop unqique key command. + * Generate the SQL statement for a drop unique key command. * * @param Table $table * @param Fluent $command @@ -297,12 +297,12 @@ protected function drop_key(Table $table, Fluent $command) * Drop a foreign key constraint from the table. * * @param Table $table - * @param Fluent $fluent + * @param Fluent $command * @return string */ public function drop_foreign(Table $table, Fluent $command) { - return $this->drop_constraint($table, $command); + return $this->drop_constraint($table, $command); } /** @@ -324,7 +324,7 @@ protected function type_string(Fluent $column) */ protected function type_integer(Fluent $column) { - return ($column->increment) ? 'SERIAL' : 'INTEGER'; + return ($column->increment) ? 'SERIAL' : 'BIGINT'; } /** @@ -339,7 +339,7 @@ protected function type_float(Fluent $column) } /** - * Generate the data-type definintion for a decimal. + * Generate the data-type definition for a decimal. * * @param Fluent $column * @return string @@ -368,7 +368,7 @@ protected function type_boolean(Fluent $column) */ protected function type_date(Fluent $column) { - return 'TIMESTAMP'; + return 'TIMESTAMP(0) WITHOUT TIME ZONE'; } /** diff --git a/app/laravel/database/schema/grammars/sqlite.php b/app/laravel/database/schema/grammars/sqlite.php index 09102f52c..975c41373 100644 --- a/app/laravel/database/schema/grammars/sqlite.php +++ b/app/laravel/database/schema/grammars/sqlite.php @@ -29,7 +29,7 @@ public function create(Table $table, Fluent $command) return $value->type == 'primary'; }); - // If we found primary key in the array of commands, we'll create the SQL for + // If we found primary keys in the array of commands, we'll create the SQL for // the key addition and append it to the SQL table creation statement for // the schema table so the index is properly generated. if ( ! is_null($primary)) @@ -43,7 +43,7 @@ public function create(Table $table, Fluent $command) } /** - * Geenrate the SQL statements for a table modification command. + * Generate the SQL statements for a table modification command. * * @param Table $table * @param Fluent $command @@ -53,7 +53,7 @@ public function add(Table $table, Fluent $command) { $columns = $this->columns($table); - // Once we the array of column definitions, we need to add "add" to the + // Once we have the array of column definitions, we need to add "add" to the // front of each definition, then we'll concatenate the definitions // using commas like normal and generate the SQL. $columns = array_map(function($column) @@ -87,7 +87,7 @@ protected function columns(Table $table) { // Each of the data type's have their own definition creation method // which is responsible for creating the SQL for the type. This lets - // us to keep the syntax easy and fluent, while translating the + // us keep the syntax easy and fluent, while translating the // types to the types used by the database. $sql = $this->wrap($column).' '.$this->type($column); @@ -127,7 +127,7 @@ protected function defaults(Table $table, Fluent $column) { if ( ! is_null($column->default)) { - return ' DEFAULT '.$this->wrap($column->default); + return ' DEFAULT '.$this->wrap($this->default_value($column->default)); } } @@ -202,19 +202,19 @@ protected function key(Table $table, Fluent $command, $unique = false) } /** - * Generate the SQL statement for a drop table command. + * Generate the SQL statement for a rename table command. * - * @param Table $table - * @param Fluent $command + * @param Table $table + * @param Fluent $command * @return string */ - public function drop(Table $table, Fluent $command) + public function rename(Table $table, Fluent $command) { - return 'DROP TABLE '.$this->wrap($table); + return 'ALTER TABLE '.$this->wrap($table).' RENAME TO '.$this->wrap($command->name); } /** - * Generate the SQL statement for a drop unqique key command. + * Generate the SQL statement for a drop unique key command. * * @param Table $table * @param Fluent $command @@ -226,7 +226,7 @@ public function drop_unique(Table $table, Fluent $command) } /** - * Generate the SQL statement for a drop unqique key command. + * Generate the SQL statement for a drop unique key command. * * @param Table $table * @param Fluent $command @@ -283,7 +283,7 @@ protected function type_float(Fluent $column) } /** - * Generate the data-type definintion for a decimal. + * Generate the data-type definition for a decimal. * * @param Fluent $column * @return string diff --git a/app/laravel/database/schema/grammars/sqlserver.php b/app/laravel/database/schema/grammars/sqlserver.php index 0fb80f6a4..a9164fd6d 100644 --- a/app/laravel/database/schema/grammars/sqlserver.php +++ b/app/laravel/database/schema/grammars/sqlserver.php @@ -32,7 +32,7 @@ public function create(Table $table, Fluent $command) } /** - * Geenrate the SQL statements for a table modification command. + * Generate the SQL statements for a table modification command. * * @param Table $table * @param Fluent $command @@ -42,7 +42,7 @@ public function add(Table $table, Fluent $command) { $columns = $this->columns($table); - // Once we the array of column definitions, we need to add "add" to the + // Once we have the array of column definitions, we need to add "add" to the // front of each definition, then we'll concatenate the definitions // using commas like normal and generate the SQL. $columns = implode(', ', array_map(function($column) @@ -108,7 +108,7 @@ protected function defaults(Table $table, Fluent $column) { if ( ! is_null($column->default)) { - return " DEFAULT '".$column->default."'"; + return " DEFAULT '".$this->default_value($column->default)."'"; } } @@ -138,7 +138,7 @@ public function primary(Table $table, Fluent $command) { $name = $command->name; - $columns = $this->columnize($columns); + $columns = $this->columnize($command->columns); return 'ALTER TABLE '.$this->wrap($table)." ADD CONSTRAINT {$name} PRIMARY KEY ({$columns})"; } @@ -213,15 +213,15 @@ protected function key(Table $table, Fluent $command, $unique = false) } /** - * Generate the SQL statement for a drop table command. + * Generate the SQL statement for a rename table command. * - * @param Table $table - * @param Fluent $command + * @param Table $table + * @param Fluent $command * @return string */ - public function drop(Table $table, Fluent $command) + public function rename(Table $table, Fluent $command) { - return 'DROP TABLE '.$this->wrap($table); + return 'ALTER TABLE '.$this->wrap($table).' RENAME TO '.$this->wrap($command->name); } /** @@ -235,7 +235,7 @@ public function drop_column(Table $table, Fluent $command) { $columns = array_map(array($this, 'wrap'), $command->columns); - // Once we the array of column names, we need to add "drop" to the front + // Once we have the array of column names, we need to add "drop" to the front // of each column, then we'll concatenate the columns using commas and // generate the alter statement SQL. $columns = implode(', ', array_map(function($column) @@ -260,7 +260,7 @@ public function drop_primary(Table $table, Fluent $command) } /** - * Generate the SQL statement for a drop unqiue key command. + * Generate the SQL statement for a drop unique key command. * * @param Table $table * @param Fluent $command @@ -315,12 +315,12 @@ protected function drop_key(Table $table, Fluent $command) * Drop a foreign key constraint from the table. * * @param Table $table - * @param Fluent $fluent + * @param Fluent $command * @return string */ public function drop_foreign(Table $table, Fluent $command) { - return $this->drop_constraint($table, $command); + return $this->drop_constraint($table, $command); } /** @@ -357,7 +357,7 @@ protected function type_float(Fluent $column) } /** - * Generate the data-type definintion for a decimal. + * Generate the data-type definition for a decimal. * * @param Fluent $column * @return string diff --git a/app/laravel/database/schema/table.php b/app/laravel/database/schema/table.php index 9518d6e77..c728260cc 100644 --- a/app/laravel/database/schema/table.php +++ b/app/laravel/database/schema/table.php @@ -113,6 +113,7 @@ public function index($columns, $name = null) * * @param string|array $columns * @param string $name + * @return Fluent */ public function foreign($columns, $name = null) { @@ -136,12 +137,25 @@ public function key($type, $columns, $name) // the index that can be used when dropping indexes. if (is_null($name)) { - $name = $this->name.'_'.implode('_', $columns).'_'.$type; + $name = str_replace(array('-', '.'), '_', $this->name); + + $name = $name.'_'.implode('_', $columns).'_'.$type; } return $this->command($type, compact('name', 'columns')); } + /** + * Rename the database table. + * + * @param string $name + * @return Fluent + */ + public function rename($name) + { + return $this->command(__FUNCTION__, compact('name')); + } + /** * Drop the database table. * @@ -391,9 +405,7 @@ protected function command($type, $parameters = array()) { $parameters = array_merge(compact('type'), $parameters); - $this->commands[] = new Fluent($parameters); - - return end($this->commands); + return $this->commands[] = new Fluent($parameters); } /** @@ -407,9 +419,7 @@ protected function column($type, $parameters = array()) { $parameters = array_merge(compact('type'), $parameters); - $this->columns[] = new Fluent($parameters); - - return end($this->columns); + return $this->columns[] = new Fluent($parameters); } } \ No newline at end of file diff --git a/app/laravel/documentation/artisan/commands.md b/app/laravel/documentation/artisan/commands.md new file mode 100644 index 000000000..0a2dd5ffc --- /dev/null +++ b/app/laravel/documentation/artisan/commands.md @@ -0,0 +1,110 @@ +# Artisan Commands + +## Contents + +- [Help](#help) +- [Application Configuration](#application-configuration) +- [Sessions](#sessions) +- [Migrations](#migrations) +- [Bundles](#bundles) +- [Tasks](#tasks) +- [Unit Tests](#unit-tests) +- [Routing](#routing) +- [Application Keys](#keys) +- [CLI Options](#cli-options) + + +## Help + +Description | Command +------------- | ------------- +View a list of available artisan commands. | `php artisan help:commands` + + +## Application Configuration [(More Information)](/docs/install#basic-configuration) + +Description | Command +------------- | ------------- +Generate a secure application key. An application key will not be generated unless the field in **config/application.php** is empty. | `php artisan key:generate` + + +## Database Sessions [(More Information)](/docs/session/config#database) + +Description | Command +------------- | ------------- +Create a session table | `php artisan session:table` + + +## Migrations [(More Information)](/docs/database/migrations) + +Description | Command +------------- | ------------- +Create the Laravel migration table | `php artisan migrate:install` +Creating a migration | `php artisan migrate:make create_users_table` +Creating a migration for a bundle | `php artisan migrate:make bundle::tablename` +Running outstanding migrations | `php artisan migrate` +Running outstanding migrations in the application | `php artisan migrate application` +Running all outstanding migrations in a bundle | `php artisan migrate bundle` +Rolling back the last migration operation | `php artisan migrate:rollback` +Roll back all migrations that have ever run | `php artisan migrate:reset` + + +## Bundles [(More Information)](/docs/bundles) + +Description | Command +------------- | ------------- +Install a bundle | `php artisan bundle:install eloquent` +Upgrade a bundle | `php artisan bundle:upgrade eloquent` +Upgrade all bundles | `php artisan bundle:upgrade` +Publish a bundle assets | `php artisan bundle:publish bundle_name` +Publish all bundles assets | `php artisan bundle:publish` + +
+> **Note:** After installing you need to [register the bundle](../bundles/#registering-bundles) + + +## Tasks [(More Information)](/docs/artisan/tasks) + +Description | Command +------------- | ------------- +Calling a task | `php artisan notify` +Calling a task and passing arguments | `php artisan notify taylor` +Calling a specific method on a task | `php artisan notify:urgent` +Running a task on a bundle | `php artisan admin::generate` +Running a specific method on a bundle | `php artisan admin::generate:list` + + +## Unit Tests [(More Information)](/docs/testing) + +Description | Command +------------- | ------------- +Running the application tests | `php artisan test` +Running the bundle tests | `php artisan test bundle-name` + + +## Routing [(More Information)](/docs/routing) + +Description | Command +------------- | ------------- +Calling a route | `php artisan route:call get api/user/1` + +
+> **Note:** You can replace get with post, put, delete, etc. + + +## Application Keys + +Description | Command +------------- | ------------- +Generate an application key | `php artisan key:generate` + +
+> **Note:** You can specify an alternate key length by adding an extra argument to the command. + + +## CLI Options + +Description | Command +------------- | ------------- +Setting the Laravel environment | `php artisan foo --env=local` +Setting the default database connection | `php artisan foo --database=sqlitename` diff --git a/app/laravel/documentation/artisan/tasks.md b/app/laravel/documentation/artisan/tasks.md new file mode 100644 index 000000000..a5ab78b69 --- /dev/null +++ b/app/laravel/documentation/artisan/tasks.md @@ -0,0 +1,108 @@ +# Tasks + +## Contents + +- [The Basics](#the-basics) +- [Creating & Running Tasks](#creating-tasks) +- [Bundle Tasks](#bundle-tasks) +- [CLI Options](#cli-options) + + +## The Basics + +Laravel's command-line tool is called Artisan. Artisan can be used to run "tasks" such as migrations, cronjobs, unit-tests, or anything that want. + + +## Creating & Running Tasks + +To create a task create a new class in your **application/tasks** directory. The class name should be suffixed with "_Task", and should at least have a "run" method, like this: + +#### Creating a task class: + + class Notify_Task { + + public function run($arguments) + { + // Do awesome notifying... + } + + } + +Now you can call the "run" method of your task via the command-line. You can even pass arguments: + +#### Calling a task from the command line: + + php artisan notify + +#### Calling a task and passing arguments: + + php artisan notify taylor + +#### Calling a task from your application: + + Command::run(array('notify')); + +#### Calling a task from your application with arguements: + + Command::run(array('notify', 'taylor')); + +Remember, you can call specific methods on your task, so, let's add an "urgent" method to the notify task: + +#### Adding a method to the task: + + class Notify_Task { + + public function run($arguments) + { + // Do awesome notifying... + } + + public function urgent($arguments) + { + // This is urgent! + } + + } + +Now we can call our "urgent" method: + +#### Calling a specific method on a task: + + php artisan notify:urgent + + +## Bundle Tasks + +To create a task for your bundle just prefix the bundle name to the class name of your task. So, if your bundle was named "admin", a task might look like this: + +#### Creating a task class that belongs to a bundle: + + class Admin_Generate_Task { + + public function run($arguments) + { + // Generate the admin! + } + + } + +To run your task just use the usual Laravel double-colon syntax to indicate the bundle: + +#### Running a task belonging to a bundle: + + php artisan admin::generate + +#### Running a specific method on a task belonging to a bundle: + + php artisan admin::generate:list + + +## CLI Options + +#### Setting the Laravel environment: + + php artisan foo --env=local + +#### Setting the default database connection: + + php artisan foo --database=sqlite diff --git a/app/laravel/documentation/auth/config.md b/app/laravel/documentation/auth/config.md new file mode 100644 index 000000000..a16093208 --- /dev/null +++ b/app/laravel/documentation/auth/config.md @@ -0,0 +1,38 @@ +# Auth Configuration + +## Contents + +- [The Basics](#the-basics) +- [The Authentication Driver](#driver) +- [The Default "Username"](#username) +- [Authentication Model](#model) +- [Authentication Table](#table) + + +## The Basics + +Most interactive applications have the ability for users to login and logout. Laravel provides a simple class to help you validate user credentials and retrieve information about the current user of your application. + +To get started, let's look over the **application/config/auth.php** file. The authentication configuration contains some basic options to help you get started with authentication. + + +## The Authentication Driver + +Laravel's authentication is driver based, meaning the responsibility for retrieving users during authentication is delegated to various "drivers". Two are included out of the box: Eloquent and Fluent, but you are free to write your own drivers if needed! + +The **Eloquent** driver uses the Eloquent ORM to load the users of your application, and is the default authentication driver. The **Fluent** driver uses the fluent query builder to load your users. + + +## The Default "Username" + +The second option in the configuration file determines the default "username" of your users. This will typically correspond to a database column in your "users" table, and will usually be "email" or "username". + + +## Authentication Model + +When using the **Eloquent** authentication driver, this option determines the Eloquent model that should be used when loading users. + + +## Authentication Table + +When using the **Fluent** authentication drivers, this option determines the database table containing the users of your application. \ No newline at end of file diff --git a/app/laravel/documentation/auth/usage.md b/app/laravel/documentation/auth/usage.md new file mode 100644 index 000000000..4e7ba5fe2 --- /dev/null +++ b/app/laravel/documentation/auth/usage.md @@ -0,0 +1,86 @@ +# Authentication Usage + +## Contents + +- [Salting & Hashing](#hash) +- [Logging In](#login) +- [Protecting Routes](#filter) +- [Retrieving The Logged In User](#user) +- [Logging Out](#logout) +- [Writing Custom Drivers](#drivers) + +> **Note:** Before using the Auth class, you must [specify a session driver](/docs/session/config). + + +## Salting & Hashing + +If you are using the Auth class, you are strongly encouraged to hash and salt all passwords. Web development must be done responsibly. Salted, hashed passwords make a rainbow table attack against your user's passwords impractical. + +Salting and hashing passwords is done using the **Hash** class. The Hash class is uses the **bcrypt** hashing algorithm. Check out this example: + + $password = Hash::make('secret'); + +The **make** method of the Hash class will return a 60 character hashed string. + +You can compare an unhashed value against a hashed one using the **check** method on the **Hash** class: + + if (Hash::check('secret', $hashed_value)) + { + return 'The password is valid!'; + } + + +## Logging In + +Logging a user into your application is simple using the **attempt** method on the Auth class. Simply pass the username and password of the user to the method. The credentials should be contained in an array, which allows for maximum flexibility across drivers, as some drivers may require a different number of arguments. The login method will return **true** if the credentials are valid. Otherwise, **false** will be returned: + + $credentials = array('username' => 'example@gmail.com', 'password' => 'secret'); + + if (Auth::attempt($credentials)) + { + return Redirect::to('user/profile'); + } + +If the user's credentials are valid, the user ID will be stored in the session and the user will be considered "logged in" on subsequent requests to your application. + +To determine if the user of your application is logged in, call the **check** method: + + if (Auth::check()) + { + return "You're logged in!"; + } + +Use the **login** method to login a user without checking their credentials, such as after a user first registers to use your application. Just pass the user's ID: + + Auth::login($user->id); + + Auth::login(15); + + +## Protecting Routes + +It is common to limit access to certain routes only to logged in users. In Laravel this is accomplished using the [auth filter](/docs/routing#filters). If the user is logged in, the request will proceed as normal; however, if the user is not logged in, they will be redirected to the "login" [named route](/docs/routing#named-routes). + +To protect a route, simply attach the **auth** filter: + + Route::get('admin', array('before' => 'auth', function() {})); + +> **Note:** You are free to edit the **auth** filter however you like. A default implementation is located in **application/routes.php**. + + +## Retrieving The Logged In User + +Once a user has logged in to your application, you can access the user model via the **user** method on the Auth class: + + return Auth::user()->email; + +> **Note:** If the user is not logged in, the **user** method will return NULL. + + +## Logging Out + +Ready to log the user out of your application? + + Auth::logout(); + +This method will remove the user ID from the session, and the user will no longer be considered logged in on subsequent requests to your application. \ No newline at end of file diff --git a/app/laravel/documentation/bundles.md b/app/laravel/documentation/bundles.md new file mode 100644 index 000000000..674deb254 --- /dev/null +++ b/app/laravel/documentation/bundles.md @@ -0,0 +1,214 @@ +# Bundles + +## Contents + +- [The Basics](#the-basics) +- [Creating Bundles](#creating-bundles) +- [Registering Bundles](#registering-bundles) +- [Bundles & Class Loading](#bundles-and-class-loading) +- [Starting Bundles](#starting-bundles) +- [Routing To Bundles](#routing-to-bundles) +- [Using Bundles](#using-bundles) +- [Bundle Assets](#bundle-assets) +- [Installing Bundles](#installing-bundles) +- [Upgrading Bundles](#upgrading-bundles) + + +## The Basics + +Bundles are the heart of the improvements that were made in Laravel 3.0. They are a simple way to group code into convenient "bundles". A bundle can have it's own views, configuration, routes, migrations, tasks, and more. A bundle could be everything from a database ORM to a robust authentication system. Modularity of this scope is an important aspect that has driven virtually all design decisions within Laravel. In many ways you can actually think of the application folder as the special default bundle with which Laravel is pre-programmed to load and use. + + +## Creating Bundles + +The first step in creating a bundle is to create a folder for the bundle within your **bundles** directory. For this example, let's create an "admin" bundle, which could house the administrator back-end to our application. The **application/start.php** file provides some basic configuration that helps to define how our application will run. Likewise we'll create a **start.php** file within our new bundle folder for the same purpose. It is run every time the bundle is loaded. Let's create it: + +#### Creating a bundle start.php file: + + Bundle::path('admin').'models', + )); + +In this start file we've told the auto-loader that classes that are namespaced to "Admin" should be loaded out of our bundle's models directory. You can do anything you want in your start file, but typically it is used for registering classes with the auto-loader. **In fact, you aren't required to create a start file for your bundle.** + +Next, we'll look at how to register this bundle with our application! + + +## Registering Bundles + +Now that we have our admin bundle, we need to register it with Laravel. Pull open your **application/bundles.php** file. This is where you register all bundles used by your application. Let's add ours: + +#### Registering a simple bundle: + + return array('admin'), + +By convention, Laravel will assume that the Admin bundle is located at the root level of the bundle directory, but we can specify another location if we wish: + +#### Registering a bundle with a custom location: + + return array( + + 'admin' => array('location' => 'userscape/admin'), + + ); + +Now Laravel will look for our bundle in **bundles/userscape/admin**. + + +## Bundles & Class Loading + +Typically, a bundle's **start.php** file only contains auto-loader registrations. So, you may want to just skip **start.php** and declare your bundle's mappings right in its registration array. Here's how: + +#### Defining auto-loader mappings in a bundle registration: + + return array( + + 'admin' => array( + 'autoloads' => array( + 'map' => array( + 'Admin' => '(:bundle)/admin.php', + ), + 'namespaces' => array( + 'Admin' => '(:bundle)/lib', + ), + 'directories' => array( + '(:bundle)/models', + ), + ), + ), + + ); + +Notice that each of these options corresponds to a function on the Laravel [auto-loader](/docs/loading). In fact, the value of the option will automatically be passed to the corresponding function on the auto-loader. + +You may have also noticed the **(:bundle)** place-holder. For convenience, this will automatically be replaced with the path to the bundle. It's a piece of cake. + + +## Starting Bundles + +So our bundle is created and registered, but we can't use it yet. First, we need to start it: + +#### Starting a bundle: + + Bundle::start('admin'); + +This tells Laravel to run the **start.php** file for the bundle, which will register its classes in the auto-loader. The start method will also load the **routes.php** file for the bundle if it is present. + +> **Note:** The bundle will only be started once. Subsequent calls to the start method will be ignored. + +If you use a bundle throughout your application, you may want it to start on every request. If this is the case, you can configure the bundle to auto-start in your **application/bundles.php** file: + +#### Configuration a bundle to auto-start: + + return array( + + 'admin' => array('auto' => true), + + ); + +You do not always need to explicitly start a bundle. In fact, you can usually code as if the bundle was auto-started and Laravel will take care of the rest. For example, if you attempt to use a bundle views, configurations, languages, routes or filters, the bundle will automatically be started! + +Each time a bundle is started, it fires an event. You can listen for the starting of bundles like so: + +#### Listen for a bundle's start event: + + Event::listen('laravel.started: admin', function() + { + // The "admin" bundle has started... + }); + +It is also possible to "disable" a bundle so that it will never be started. + +#### Disabling a bundle so it can't be started: + + Bundle::disable('admin'); + + +## Routing To Bundles + +Refer to the documentation on [bundle routing](/docs/routing#bundle-routes) and [bundle controllers](/docs/controllers#bundle-controllers) for more information on routing and bundles. + + +## Using Bundles + +As mentioned previously, bundles can have views, configuration, language files and more. Laravel uses a double-colon syntax for loading these items. So, let's look at some examples: + +#### Loading a bundle view: + + return View::make('bundle::view'); + +#### Loading a bundle configuration item: + + return Config::get('bundle::file.option'); + +#### Loading a bundle language line: + + return Lang::line('bundle::file.line'); + +Sometimes you may need to gather more "meta" information about a bundle, such as whether it exists, its location, or perhaps its entire configuration array. Here's how: + +#### Determine whether a bundle exists: + + Bundle::exists('admin'); + +#### Retrieving the installation location of a bundle: + + $location = Bundle::path('admin'); + +#### Retrieving the configuration array for a bundle: + + $config = Bundle::get('admin'); + +#### Retrieving the names of all installed bundles: + + $names = Bundle::names(); + + +## Bundle Assets + +If your bundle contains views, it is likely you have assets such as JavaScript and images that need to be available in the **public** directory of the application. No problem. Just create **public** folder within your bundle and place all of your assets in this folder. + +Great! But, how do they get into the application's **public** folder. The Laravel "Artisan" command-line provides a simple command to copy all of your bundle's assets to the public directory. Here it is: + +#### Publish bundle assets into the public directory: + + php artisan bundle:publish + +This command will create a folder for the bundle's assets within the application's **public/bundles** directory. For example, if your bundle is named "admin", a **public/bundles/admin** folder will be created, which will contain all of the files in your bundle's public folder. + +For more information on conveniently getting the path to your bundle assets once they are in the public directory, refer to the documentation on [asset management](/docs/views/assets#bundle-assets). + + +## Installing Bundles + +Of course, you may always install bundles manually; however, the "Artisan" CLI provides an awesome method of installing and upgrading your bundle. The framework uses simple Zip extraction to install the bundle. Here's how it works. + +#### Installing a bundle via Artisan: + + php artisan bundle:install eloquent + +Great! Now that you're bundle is installed, you're ready to [register it](#registering-bundles) and [publish its assets](#bundle-assets). + +Need a list of available bundles? Check out the Laravel [bundle directory](http://bundles.laravel.com) + + +## Upgrading Bundles + +When you upgrade a bundle, Laravel will automatically remove the old bundle and install a fresh copy. + +#### Upgrading a bundle via Artisan: + + php artisan bundle:upgrade eloquent + +> **Note:** After upgrading the bundle, you may need to [re-publish its assets](#bundle-assets). + +**Important:** Since the bundle is totally removed on an upgrade, you must be aware of any changes you have made to the bundle code before upgrading. You may need to change some configuration options in a bundle. Instead of modifying the bundle code directly, use the bundle start events to set them. Place something like this in your **application/start.php** file. + +#### Listening for a bundle's start event: + + Event::listen('laravel.started: admin', function() + { + Config::set('admin::file.option', true); + }); \ No newline at end of file diff --git a/app/laravel/documentation/cache/config.md b/app/laravel/documentation/cache/config.md new file mode 100644 index 000000000..39f80c4cd --- /dev/null +++ b/app/laravel/documentation/cache/config.md @@ -0,0 +1,79 @@ +# Cache Configuration + +## Contents + +- [The Basics](#the-basics) +- [Database](#database) +- [Memcached](#memcached) +- [Redis](#redis) +- [Cache Keys](#keys) +- [In-Memory Cache](#memory) + + +## The Basics + +Imagine your application displays the ten most popular songs as voted on by your users. Do you really need to look up these ten songs every time someone visits your site? What if you could store them for 10 minutes, or even an hour, allowing you to dramatically speed up your application? Laravel's caching makes it simple. + +Laravel provides five cache drivers out of the box: + +- File System +- Database +- Memcached +- APC +- Redis +- Memory (Arrays) + +By default, Laravel is configured to use the **file** system cache driver. It's ready to go out of the box with no configuration. The file system driver stores cached items as files in the **cache** directory. If you're satisfied with this driver, no other configuration is required. You're ready to start using it. + +> **Note:** Before using the file system cache driver, make sure your **storage/cache** directory is writeable. + + +## Database + +The database cache driver uses a given database table as a simple key-value store. To get started, first set the name of the database table in **application/config/cache.php**: + + 'database' => array('table' => 'laravel_cache'), + +Next, create the table on your database. The table should have three columns: + +- key (varchar) +- value (text) +- expiration (integer) + +That's it. Once your configuration and table is setup, you're ready to start caching! + + +## Memcached + +[Memcached](http://memcached.org) is an ultra-fast, open-source distributed memory object caching system used by sites such as Wikipedia and Facebook. Before using Laravel's Memcached driver, you will need to install and configure Memcached and the PHP Memcache extension on your server. + +Once Memcached is installed on your server you must set the **driver** in the **application/config/cache.php** file: + + 'driver' => 'memcached' + +Then, add your Memcached servers to the **servers** array: + + 'servers' => array( + array('host' => '127.0.0.1', 'port' => 11211, 'weight' => 100), + ) + + +## Redis + +[Redis](http://redis.io) is an open source, advanced key-value store. It is often referred to as a data structure server since keys can contain [strings](http://redis.io/topics/data-types#strings), [hashes](http://redis.io/topics/data-types#hashes), [lists](http://redis.io/topics/data-types#lists), [sets](http://redis.io/topics/data-types#sets), and [sorted sets](http://redis.io/topics/data-types#sorted-sets). + +Before using the Redis cache driver, you must [configure your Redis servers](/docs/database/redis#config). Now you can just set the **driver** in the **application/config/cache.php** file: + + 'driver' => 'redis' + + +### Cache Keys + +To avoid naming collisions with other applications using APC, Redis, or a Memcached server, Laravel prepends a **key** to each item stored in the cache using these drivers. Feel free to change this value: + + 'key' => 'laravel' + + +### In-Memory Cache + +The "memory" cache driver does not actually cache anything to disk. It simply maintains an internal array of the cache data for the current request. This makes it perfect for unit testing your application in isolation from any storage mechanism. It should never be used as a "real" cache driver. \ No newline at end of file diff --git a/app/laravel/documentation/cache/usage.md b/app/laravel/documentation/cache/usage.md new file mode 100644 index 000000000..adc2f2e8b --- /dev/null +++ b/app/laravel/documentation/cache/usage.md @@ -0,0 +1,59 @@ +# Cache Usage + +## Contents + +- [Storing Items](#put) +- [Retrieving Items](#get) +- [Removing Items](#forget) + + +## Storing Items + +Storing items in the cache is simple. Simply call the **put** method on the Cache class: + + Cache::put('name', 'Taylor', 10); + +The first parameter is the **key** to the cache item. You will use this key to retrieve the item from the cache. The second parameter is the **value** of the item. The third parameter is the number of **minutes** you want the item to be cached. + +You may also cache something "forever" if you do not want the cache to expire: + + Cache::forever('name', 'Taylor'); + +> **Note:** It is not necessary to serialize objects when storing them in the cache. + + +## Retrieving Items + +Retrieving items from the cache is even more simple than storing them. It is done using the **get** method. Just mention the key of the item you wish to retrieve: + + $name = Cache::get('name'); + +By default, NULL will be returned if the cached item has expired or does not exist. However, you may pass a different default value as a second parameter to the method: + + $name = Cache::get('name', 'Fred'); + +Now, "Fred" will be returned if the "name" cache item has expired or does not exist. + +What if you need a value from your database if a cache item doesn't exist? The solution is simple. You can pass a closure into the **get** method as a default value. The closure will only be executed if the cached item doesn't exist: + + $users = Cache::get('count', function() {return DB::table('users')->count();}); + +Let's take this example a step further. Imagine you want to retrieve the number of registered users for your application; however, if the value is not cached, you want to store the default value in the cache using the **remember** method: + + $users = Cache::remember('count', function() {return DB::table('users')->count();}, 5); + +Let's talk through that example. If the **count** item exists in the cache, it will be returned. If it doesn't exist, the result of the closure will be stored in the cache for five minutes **and** be returned by the method. Slick, huh? + +Laravel even gives you a simple way to determine if a cached item exists using the **has** method: + + if (Cache::has('name')) + { + $name = Cache::get('name'); + } + + +## Removing Items + +Need to get rid of a cached item? No problem. Just mention the name of the item to the **forget** method: + + Cache::forget('name'); \ No newline at end of file diff --git a/app/laravel/documentation/changes.md b/app/laravel/documentation/changes.md new file mode 100644 index 000000000..7cc76b9bb --- /dev/null +++ b/app/laravel/documentation/changes.md @@ -0,0 +1,440 @@ +# Laravel Change Log + +## Contents + +- [Laravel 3.2.12](#3.2.12) +- [Upgrading From 3.2.11](#upgrade-3.2.12) +- [Laravel 3.2.11](#3.2.11) +- [Upgrading From 3.2.10](#upgrade-3.2.11) +- [Laravel 3.2.10](#3.2.10) +- [Upgrading From 3.2.9](#upgrade-3.2.10) +- [Laravel 3.2.9](#3.2.9) +- [Upgrading From 3.2.8](#upgrade-3.2.9) +- [Laravel 3.2.8](#3.2.8) +- [Upgrading From 3.2.7](#upgrade-3.2.8) +- [Laravel 3.2.7](#3.2.7) +- [Upgrading From 3.2.6](#upgrade-3.2.7) +- [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) +- [Upgrading From 3.2.3](#upgrade-3.2.4) +- [Laravel 3.2.3](#3.2.3) +- [Upgrading From 3.2.2](#upgrade-3.2.3) +- [Laravel 3.2.2](#3.2.2) +- [Upgrading From 3.2.1](#upgrade-3.2.2) +- [Laravel 3.2.1](#3.2.1) +- [Upgrading From 3.2](#upgrade-3.2.1) +- [Laravel 3.2](#3.2) +- [Upgrading From 3.1](#upgrade-3.2) +- [Laravel 3.1.9](#3.1.9) +- [Upgrading From 3.1.8](#upgrade-3.1.9) +- [Laravel 3.1.8](#3.1.8) +- [Upgrading From 3.1.7](#upgrade-3.1.8) +- [Laravel 3.1.7](#3.1.7) +- [Upgrading From 3.1.6](#upgrade-3.1.7) +- [Laravel 3.1.6](#3.1.6) +- [Upgrading From 3.1.5](#upgrade-3.1.6) +- [Laravel 3.1.5](#3.1.5) +- [Upgrading From 3.1.4](#upgrade-3.1.5) +- [Laravel 3.1.4](#3.1.4) +- [Upgrading From 3.1.3](#upgrade-3.1.4) +- [Laravel 3.1.3](#3.1.3) +- [Upgrading From 3.1.2](#uprade-3.1.3) +- [Laravel 3.1.2](#3.1.2) +- [Upgrading From 3.1.1](#upgrade-3.1.2) +- [Laravel 3.1.1](#3.1.1) +- [Upgrading From 3.1](#upgrade-3.1.1) +- [Laravel 3.1](#3.1) +- [Upgrading From 3.0](#upgrade-3.1) + + +## Laravel 3.2.12 + +- Clear sections on a complete render operation. + + +### Upgrading From 3.2.11 + +- Replace the **laravel** folder. + + +## Laravel 3.2.11 + +- Improve performance of Eloquent eager load matching. +- Check `gethostname` on environment detection. + + +### Upgrading From 3.2.10 + +- Replace the **laravel** folder. + + +## Laravel 3.2.10 + +- Fix bug in Eloquent model. + + +### Upgrading From 3.2.9 + +- Replace the **laravel** folder. + + +## Laravel 3.2.9 + +- Always log exceptions even when there are "logger" event listeners. +- Fix nasty view exception messages. + + +### Upgrading From 3.2.8 + +- Replace the **laravel** folder. + + +## Laravel 3.2.8 + +- Fix double slash bug in URLs when using languages and no "index.php". +- Fix possible security issue in Auth "remember me" cookies. + + +### Upgrading From 3.2.7 + +- Replace the **laravel** folder. + + +## Laravel 3.2.7 + +- Fix bug in Eloquent `to_array` method. +- Fix bug in displaying of generic error page. + + +### Upgrading From 3.2.6 + +- Replace the **laravel** folder. + + +## 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 + +- Replace the **laravel** folder. + + +## Laravel 3.2.4 + +- Speed up many to many eager loading mapping. +- Tweak the Eloquent::changed() method. +- Various bug fixes and improvements. + + +### Upgrading From 3.2.3 + +- Replace the **laravel** folder. + + +## Laravel 3.2.3 + +- Fixed eager loading bug in Eloquent. +- Added `laravel.resolving` event for all IoC resolutions. + + +### Upgrading From 3.2.2 + +- Replace the **laravel** folder. + + +## Laravel 3.2.2 + +- Overall improvement of Postgres support. +- Fix issue in SQL Server Schema grammar. +- Fix issue with eager loading and `first` or `find`. +- Fix bug causing parameters to not be passed to `IoC::resolve`. +- Allow the specification of hostnames in environment setup. +- Added `DB::last_query` method. +- Added `password` option to Auth configuration. + + +### Upgrading From 3.2.1 + +- Replace the **laravel** folder. + + +## Laravel 3.2.1 + +- Fixed bug in cookie retrieval when cookie is set on same request. +- Fixed bug in SQL Server grammar for primary keys. +- Fixed bug in Validator on PHP 5.4. +- If HTTP / HTTPS is not specified for generated links, current protocol is used. +- Fix bug in Eloquent auth driver. +- Added `format` method to message container. + + +### Upgrading From 3.2 + +- Replace the **laravel** folder. + + +## Laravel 3.2 + +- [Added `to_array` method to the base Eloquent model](/docs/database/eloquent#to-array). +- [Added `$hidden` static variable to the base Eloquent model](/docs/database/eloquent#to-array). +- [Added `sync` method to has\_many\_and\_belongs\_to Eloquent relationship](/docs/database/eloquent#sync-method). +- [Added `save` method to has\_many Eloquent relationship](/docs/database/eloquent#has-many-save). +- [Added `unless` structure to Blade template engine](/docs/views/templating#blade-unless). +- [Added Blade comments](/docs/views/templating#blade-comments). +- [Added simpler environment management](/docs/install#environments). +- Added `Blade::extend()` method to define custom blade compilers. +- Added `View::exists` method. +- Use [Memcached](http://php.net/manual/en/book.memcached.php) API instead of older [Memcache](http://php.net/manual/en/book.memcache.php) API. +- Added support for bundles outside of the bundle directory. +- Added support for DateTime database query bindings. +- Migrated to the Symfony HttpFoundation component for core request / response handling. +- Fixed the passing of strings into the `Input::except` method. +- Fixed replacement of optional parameters in `URL::transpose` method. +- Improved `update` handling on `Has_Many` and `Has_One` relationships. +- Improved View performance by only loading contents from file once. +- Fix handling of URLs beginning with hashes in `URL::to`. +- Fix the resolution of unset Eloquent attributes. +- Allows pivot table timestamps to be disabled. +- Made the `get_timestamp` Eloquent method static. +- `Request::secure` now takes `application.ssl` configuration option into consideration. +- Simplified the `paths.php` file. +- Only write file caches if number of minutes is greater than zero. +- Added `$default` parameter to Bundle::option method. +- Fixed bug present when using Eloquent models with Twig. +- Allow multiple views to be registered for a single composer. +- Added `Request::set_env` method. +- `Schema::drop` now accepts `$connection` as second parameter. +- Added `Input::merge` method. +- Added `Input::replace` method. +- Added saving, saved, updating, creating, deleting, and deleted events to Eloquent. +- Added new `Sectionable` interface to allow cache drivers to simulate namespacing. +- Added support for `HAVING` SQL clauses. +- Added `array_pluck` helper, similar to pluck method in Underscore.js. +- Allow the registration of custom cache and session drivers. +- Allow the specification of a separate asset base URL for using CDNs. +- Allow a `starter` Closure to be defined in `bundles.php` to be run on Bundle::start. +- Allow the registration of custom database drivers. +- New, driver based authentication system. +- Added Input::json() method for working with applications using Backbone.js or similar. +- Added Response::json method for creating JSON responses. +- Added Response::eloquent method for creating Eloquent responses. +- Fixed bug when using many-to-many relationships on non-default database connection. +- Added true reflection based IoC to container. +- Added `Request::route()->controller` and `Request::route()->controller_action`. +- Added `Event::queue`, `Event::flusher`, and `Event::flush` methods to Event class. +- Added `array_except` and `array_only` helpers, similar to `Input::except` and `Input::only` but for arbitrary arrays. + + +### Upgrading From 3.1 + +- Add new `asset_url` and `profiler` options to application configuration. +- Replace **auth** configuration file. + +Add the following entry to the `aliases` array in `config/application.php`.. + + 'Profiler' => 'Laravel\\Profiling\\Profiler', + +Add the following code above `Blade::sharpen()` in `application/start.php`.. + + if (Config::get('application.profiler')) + { + Profiler::attach(); + } + +- Upgrade the **paths.php** file. +- Replace the **laravel** folder. + + +## Laravel 3.1.9 + +- Fixes cookie session driver bug that caused infinite loop on some occasions. + + +### Upgrading From 3.1.8 + +- Replace the **laravel** folder. + + +## Laravel 3.1.8 + +- Fixes possible WSOD when using Blade's @include expression. + + +### Upgrading From 3.1.7 + +- Replace the **laravel** folder. + + +## Laravel 3.1.7 + +- Fixes custom validation language line loading from bundles. +- Fixes double-loading of classes when overriding the core. +- Classify migration names. + + +### Upgrading From 3.1.6 + +- Replace the **laravel** folder. + + +## Laravel 3.1.6 + +- Fixes many-to-many eager loading in Eloquent. + + +### Upgrading From 3.1.5 + +- Replace the **laravel** folder. + + +## Laravel 3.1.5 + +- Fixes bug that could allow secure cookies to be sent over HTTP. + + +### Upgrading From 3.1.4 + +- Replace the **laravel** folder. + + +## Laravel 3.1.4 + +- Fixes Response header casing bug. +- Fixes SQL "where in" (...) short-cut bug. + + +### Upgrading From 3.1.3 + +- Replace the **laravel** folder. + + +## Laravel 3.1.3 + +- Fixes **delete** method in Eloquent models. + + +### Upgrade From 3.1.2 + +- Replace the **laravel** folder. + + +## Laravel 3.1.2 + +- Fixes Eloquent query method constructor conflict. + + +### Upgrade From 3.1.1 + +- Replace the **laravel** folder. + + +## Laravel 3.1.1 + +- Fixes Eloquent model hydration bug involving custom setters. + + +### Upgrading From 3.1 + +- Replace the **laravel** folder. + + +## Laravel 3.1 + +- Added events to logger for more flexibility. +- Added **database.fetch** configuration option. +- Added controller factories for injecting any IoC. +- Added **link_to_action** HTML helpers. +- Added ability to set default value on Config::get. +- Added the ability to add pattern based filters. +- Improved session ID assignment. +- Added support for "unsigned" integers in schema builder. +- Added config, view, and lang loaders. +- Added more logic to **application/start.php** for more flexibility. +- Added foreign key support to schema builder. +- Postgres "unique" indexes are now added with ADD CONSTRAINT. +- Added "Event::until" method. +- Added "memory" cache and session drivers. +- Added Controller::detect method. +- Added Cache::forever method. +- Controller layouts now resolved in Laravel\Controller __construct. +- Rewrote Eloquent and included in core. +- Added "match" validation rule. +- Fixed table prefix bug. +- Added Form::macro method. +- Added HTML::macro method. +- Added Route::forward method. +- Prepend table name to default index names in schema. +- Added "forelse" to Blade. +- Added View::render_each. +- Able to specify full path to view (path: ). +- Added support for Blade template inheritance. +- Added "before" and "after" validation checks for dates. + + +### Upgrading From 3.0 + +#### 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. + +This option is now set at the beginning of your **application/start** file. + +#### 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. + +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 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. + +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. + +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. + +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. + +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. + +If you are using Redis, add the "database" option to your Redis connection configurations. The "database" value can be zero by default. + + 'redis' => array( + 'default' => array( + 'host' => '127.0.0.1', + 'port' => 6379, + 'database' => 0 + ), + ), diff --git a/app/laravel/documentation/config.md b/app/laravel/documentation/config.md new file mode 100644 index 000000000..ae7413863 --- /dev/null +++ b/app/laravel/documentation/config.md @@ -0,0 +1,34 @@ +# Runtime Configuration + +## Contents + +- [The Basics](#the-basics) +- [Retrieving Options](#retrieving-options) +- [Setting Options](#setting-options) + + +## The Basics + +Sometimes you may need to get and set configuration options at runtime. For this you'll use the **Config** class, which utilizes Laravel's "dot" syntax for accessing configuration files and items. + + +## Retrieving Options + +#### Retrieve a configuration option: + + $value = Config::get('application.url'); + +#### Return a default value if the option doesn't exist: + + $value = Config::get('application.timezone', 'UTC'); + +#### Retrieve an entire configuration array: + + $options = Config::get('database'); + + +## Setting Options + +#### Set a configuration option: + + Config::set('cache.driver', 'apc'); \ No newline at end of file diff --git a/app/laravel/documentation/contents.md b/app/laravel/documentation/contents.md new file mode 100644 index 000000000..d29b39083 --- /dev/null +++ b/app/laravel/documentation/contents.md @@ -0,0 +1,118 @@ +### General +- [Laravel Overview](/docs/home) +- [Change Log](/docs/changes) +- [Installation & Setup](/docs/install) + - [Requirements](/docs/install#requirements) + - [Installation](/docs/install#installation) + - [Server Configuration](/docs/install#server-configuration) + - [Basic Configuration](/docs/install#basic-configuration) + - [Environments](/docs/install#environments) + - [Cleaner URLs](/docs/install#cleaner-urls) +- [Routing](/docs/routing) + - [The Basics](/docs/routing#the-basics) + - [Wildcards](/docs/routing#wildcards) + - [The 404 Event](/docs/routing#the-404-event) + - [Filters](/docs/routing#filters) + - [Pattern Filters](/docs/routing#pattern-filters) + - [Global Filters](/docs/routing#global-filters) + - [Route Groups](/docs/routing#groups) + - [Named Routes](/docs/routing#named-routes) + - [HTTPS Routes](/docs/routing#https-routes) + - [Bundle Routes](/docs/routing#bundle-routes) + - [Controller Routing](/docs/routing#controller-routing) + - [CLI Route Testing](/docs/routing#cli-route-testing) +- [Controllers](/docs/controllers) + - [The Basics](/docs/controllers#the-basics) + - [Controller Routing](/docs/controllers#controller-routing) + - [Bundle Controllers](/docs/controllers#bundle-controllers) + - [Action Filters](/docs/controllers#action-filters) + - [Nested Controllers](/docs/controllers#nested-controllers) + - [RESTful Controllers](/docs/controllers#restful-controllers) + - [Dependency Injection](/docs/controllers#dependency-injection) + - [Controller Factory](/docs/controllers#controller-factory) +- [Models & Libraries](/docs/models) +- [Views & Responses](/docs/views) + - [The Basics](/docs/views#basics) + - [Binding Data To Views](/docs/views#binding-data-to-views) + - [Nesting Views](/docs/views#nesting-views) + - [Named Views](/docs/views#named-views) + - [View Composers](/docs/views#view-composers) + - [Redirects](/docs/views#redirects) + - [Redirecting With Data](/docs/views#redirecting-with-flash-data) + - [Downloads](/docs/views#downloads) + - [Errors](/docs/views#errors) + - [Managing Assets](/docs/views/assets) + - [Templating](/docs/views/templating) + - [Pagination](/docs/views/pagination) + - [Building HTML](/docs/views/html) + - [Building Forms](/docs/views/forms) +- [Input & Cookies](/docs/input) + - [Input](/docs/input#input) + - [Files](/docs/input#files) + - [Old Input](/docs/input#old-input) + - [Redirecting With Old Input](/docs/input#redirecting-with-old-input) + - [Cookies](/docs/input#cookies) +- [Bundles](/docs/bundles) + - [The Basics](/docs/bundles#the-basics) + - [Creating Bundles](/docs/bundles#creating-bundles) + - [Registering Bundles](/docs/bundles#registering-bundles) + - [Bundles & Class Loading](/docs/bundles#bundles-and-class-loading) + - [Starting Bundles](/docs/bundles#starting-bundles) + - [Routing To Bundles](/docs/bundles#routing-to-bundles) + - [Using Bundles](/docs/bundles#using-bundles) + - [Bundle Assets](/docs/bundles#bundle-assets) + - [Installing Bundles](/docs/bundles#installing-bundles) + - [Upgrading Bundles](/docs/bundles#upgrading-bundles) +- [Class Auto Loading](/docs/loading) +- [Errors & Logging](/docs/logging) +- [Runtime Configuration](/docs/config) +- [Examining Requests](/docs/requests) +- [Generating URLs](/docs/urls) +- [Events](/docs/events) +- [Validation](/docs/validation) +- [Working With Files](/docs/files) +- [Working With Strings](/docs/strings) +- [Localization](/docs/localization) +- [Encryption](/docs/encryption) +- [IoC Container](/docs/ioc) +- [Unit Testing](/docs/testing) + +### Database + +- [Configuration](/docs/database/config) +- [Raw Queries](/docs/database/raw) +- [Fluent Query Builder](/docs/database/fluent) +- [Eloquent ORM](/docs/database/eloquent) +- [Schema Builder](/docs/database/schema) +- [Migrations](/docs/database/migrations) +- [Redis](/docs/database/redis) + +### Caching + +- [Configuration](/docs/cache/config) +- [Usage](/docs/cache/usage) + +### Sessions + +- [Configuration](/docs/session/config) +- [Usage](/docs/session/usage) + +### Authentication + +- [Configuration](/docs/auth/config) +- [Usage](/docs/auth/usage) + +### Artisan CLI + +- [Tasks](/docs/artisan/tasks) + - [The Basics](/docs/artisan/tasks#the-basics) + - [Creating & Running Tasks](/docs/artisan/tasks#creating-tasks) + - [Bundle Tasks](/docs/artisan/tasks#bundle-tasks) + - [CLI Options](/docs/artisan/tasks#cli-options) +- [Commands](/docs/artisan/commands) + +### Contributing + +- [Laravel on GitHub](/docs/contrib/github) +- [Command Line](/docs/contrib/command-line) +- [TortoiseGit](/docs/contrib/tortoisegit) diff --git a/app/laravel/documentation/contrib/command-line.md b/app/laravel/documentation/contrib/command-line.md new file mode 100644 index 000000000..5dea4cb6d --- /dev/null +++ b/app/laravel/documentation/contrib/command-line.md @@ -0,0 +1,128 @@ +# Contributing to Laravel via Command-Line + +## Contents + +- [Getting Started](#getting-started) +- [Forking Laravel](#forking-laravel) +- [Cloning Laravel](#cloning-laravel) +- [Adding your Fork](#adding-your-fork) +- [Creating Branches](#creating-branches) +- [Committing](#committing) +- [Submitting a Pull Request](#submitting-a-pull-request) +- [What's Next?](#whats-next) + + +## Getting Started + +This tutorial explains the basics of contributing to a project on [GitHub](https://github.com/) via the command-line. The workflow can apply to most projects on GitHub, but in this case, we will be focused on the [Laravel](https://github.com/laravel/laravel) project. This tutorial is applicable to OSX, Linux and Windows. + +This tutorial assumes you have installed [Git](http://git-scm.com/) and you have created a [GitHub account](https://github.com/signup/free). If you haven't already, look at the [Laravel on GitHub](/docs/contrib/github) documentation in order to familiarize yourself with Laravel's repositories and branches. + + +## Forking Laravel + +Login to GitHub and visit the [Laravel Repository](https://github.com/laravel/laravel). Click on the **Fork** button. This will create your own fork of Laravel in your own GitHub account. Your Laravel fork will be located at **https://github.com/username/laravel** (your GitHub username will be used in place of *username*). + + +## Cloning Laravel + +Open up the command-line or terminal and make a new directory where you can make development changes to Laravel: + + # mkdir laravel-develop + # cd laravel-develop + +Next, clone the Laravel repository (not your fork you made): + + # git clone https://github.com/laravel/laravel.git . + +> **Note**: The reason you are cloning the original Laravel repository (and not the fork you made) is so you can always pull down the most recent changes from the Laravel repository to your local repository. + + +## Adding your Fork + +Next, it's time to add the fork you made as a **remote repository**: + + # git remote add fork git@github.com:username/laravel.git + +Remember to replace *username** with your GitHub username. *This is case-sensitive*. You can verify that your fork was added by typing: + + # git remote + +Now you have a pristine clone of the Laravel repository along with your fork as a remote repository. You are ready to begin branching for new features or fixing bugs. + + +## Creating Branches + +First, make sure you are working in the **develop** branch. If you submit changes to the **master** branch, it is unlikely they will be pulled in anytime in the near future. For more information on this, read the documentation for [Laravel on GitHub](/docs/contrib/github). To switch to the develop branch: + + # git checkout develop + +Next, you want to make sure you are up-to-date with the latest Laravel repository. If any new features or bug fixes have been added to the Laravel project since you cloned it, this will ensure that your local repository has all of those changes. This important step is the reason we originally cloned the Laravel repository instead of your own fork. + + # git pull origin develop + +Now you are ready to create a new branch for your new feature or bug-fix. When you create a new branch, use a self-descriptive naming convention. For example, if you are going to fix a bug in Eloquent, name your branch *bug/eloquent*: + + # git branch bug/eloquent + # git checkout bug/eloquent + Switched to branch 'bug/eloquent' + +Or if there is a new feature to add or change to the documentation that you want to make, for example, the localization documentation: + + # git branch feature/localization-docs + # git checkout feature/localization-docs + Switched to branch 'feature/localization-docs' + +> **Note:** Create one new branch for every new feature or bug-fix. This will encourage organization, limit interdependency between new features/fixes and will make it easy for the Laravel team to merge your changes into the Laravel core. + +Now that you have created your own branch and have switched to it, it's time to make your changes to the code. Add your new feature or fix that bug. + + +## Committing + +Now that you have finished coding and testing your changes, it's time to commit them to your local repository. First, add the files that you changed/added: + + # git add laravel/documentation/localization.md + +Next, commit the changes to the repository: + + # git commit -s -m "I added some more stuff to the Localization documentation." + +"- **-s** means that you are signing-off on your commit with your name. This tells the Laravel team know that you personally agree to your code being added to the Laravel core. +"- **-m** is the message that goes with your commit. Provide a brief explanation of what you added or changed. + + +## Pushing to your Fork + +Now that your local repository has your committed changes, it's time to push (or sync) your new branch to your fork that is hosted in GitHub: + + # git push fork feature/localization-docs + +Your branch has been successfully pushed to your fork on GitHub. + + +## Submitting a Pull Request + +The final step is to submit a pull request to the Laravel repository. This means that you are requesting that the Laravel team pull and merge your changes to the Laravel core. In your browser, visit your Laravel fork at [https://github.com/username/laravel](https://github.com/username/laravel). Click on **Pull Request**. Next, make sure you choose the proper base and head repositories and branches: + +- **base repo:** laravel/laravel +- **base branch:** develop +- **head repo:** username/laravel +- **head branch:** feature/localization-docs + +Use the form to write a more detailed description of the changes you made and why you made them. Finally, click **Send pull request**. That's it! The changes you made have been submitted to the Laravel team. + + +## What's Next? + +Do you have another feature you want to add or another bug you need to fix? First, make sure you always base your new branch off of the develop branch: + + # git checkout develop + +Then, pull down the latest changes from Laravel's repository: + + # git pull origin develop + +Now you are ready to create a new branch and start coding again! + +> [Jason Lewis](http://jasonlewis.me/)'s blog post [Contributing to a GitHub Project](http://jasonlewis.me/blog/2012/06/how-to-contributing-to-a-github-project) was the primary inspiration for this tutorial. diff --git a/app/laravel/documentation/contrib/github.md b/app/laravel/documentation/contrib/github.md new file mode 100644 index 000000000..7d3687ec8 --- /dev/null +++ b/app/laravel/documentation/contrib/github.md @@ -0,0 +1,46 @@ +# Laravel on GitHub + +## Contents + +- [The Basics](#the-basics) +- [Repositories](#repositories) +- [Branches](#branches) +- [Pull Requests](#pull-requests) + + +## The Basics + +Because Laravel's development and source control is done through GitHub, anyone is able to make contributions to it. Anyone can fix bugs, add features or improve the documentation. + +After submitting proposed changes to the project, the Laravel team will review the changes and make the decision to commit them to Laravel's core. + + +## Repositories + +Laravel's home on GitHub is at [github.com/laravel](https://github.com/laravel). Laravel has several repositories. For basic contributions, the only repository you need to pay attention to is the **laravel** repository, located at [github.com/laravel/laravel](https://github.com/laravel/laravel). + + +## Branches + +The **laravel** repository has multiple branches, each serving a specific purpose: + +- **master** - This is the Laravel release branch. Active development does not happen on this branch. This branch is only for the most recent, stable Laravel core code. When you download Laravel from [laravel.com](http://laravel.com/), you are downloading directly from this master branch. *Do not make pull requests to this branch.* +- **develop** - This is the working development branch. All proposed code changes and contributions by the community are pulled into this branch. *When you make a pull request to the Laravel project, this is the branch you want to pull-request into.* + +Once certain milestones have been reached and/or Taylor Otwell and the Laravel team is happy with the stability and additional features of the current development branch, the changes in the **develop** branch are pulled into the **master** branch, thus creating and releasing the newest stable version of Laravel for the world to use. + + +## Pull Requests + +[GitHub pull requests](https://help.github.com/articles/using-pull-requests) are a great way for everyone in the community to contribute to the Laravel codebase. Found a bug? Just fix it in your fork and submit a pull request. This will then be reviewed, and, if found as good, merged into the main repository. + +In order to keep the codebase clean, stable and at high quality, even with so many people contributing, some guidelines are necessary for high-quality pull requests: + +- **Branch:** Unless they are immediate documentation fixes relevant for old versions, pull requests should be sent to the `develop` branch only. Make sure to select that branch as target when creating the pull request (GitHub will not automatically select it.) +- **Documentation:** If you are adding a new feature or changing the API in any relevant way, this should be documented. The documentation files can be found directly in the core repository. +- **Unit tests:** To keep old bugs from re-appearing and generally hold quality at a high level, the Laravel core is thoroughly unit-tested. Thus, when you create a pull request, it is expected that you unit test any new code you add. For any bug you fix, you should also add regression tests to make sure the bug will never appear again. If you are unsure about how to write tests, the core team or other contributors will gladly help. + +*Further Reading* + + - [Contributing to Laravel via Command-Line](/docs/contrib/command-line) + - [Contributing to Laravel using TortoiseGit](/docs/contrib/tortoisegit) diff --git a/app/laravel/documentation/contrib/tortoisegit.md b/app/laravel/documentation/contrib/tortoisegit.md new file mode 100644 index 000000000..74049ead8 --- /dev/null +++ b/app/laravel/documentation/contrib/tortoisegit.md @@ -0,0 +1,114 @@ +# Contributing to Laravel using TortoiseGit + +## Contents + +- [Getting Started](#getting-started) +- [Forking Laravel](#forking-laravel) +- [Cloning Laravel](#cloning-laravel) +- [Adding your Fork](#adding-your-fork) +- [Creating Branches](#creating-branches) +- [Committing](#committing) +- [Submitting a Pull Request](#submitting-a-pull-request) +- [What's Next?](#whats-next) + + +## Getting Started + +This tutorial explains the basics of contributing to a project on [GitHub](https://github.com/) using [TortoiseGit](http://code.google.com/p/tortoisegit/) for Windows. The workflow can apply to most projects on GitHub, but in this case, we will be focused on the [Laravel](https://github.com/laravel/laravel) project. + +This tutorial assumes you have installed TortoiseGit for Windows and you have created a GitHub account. If you haven't already, look at the [Laravel on GitHub](/docs/contrib/github) documentation in order to familiarize yourself with Laravel's repositories and branches. + + +## Forking Laravel + +Login to GitHub and visit the [Laravel Repository](https://github.com/laravel/laravel). Click on the **Fork** button. This will create your own fork of Laravel in your own GitHub account. Your Laravel fork will be located at **https://github.com/username/laravel** (your GitHub username will be used in place of *username*). + + +## Cloning Laravel + +Open up Windows Explorer and create a new directory where you can make development changes to Laravel. + +- Right-click the Laravel directory to bring up the context menu. Click on **Git Clone...** +- Git clone + - **Url:** https://github.com/laravel/laravel.git + - **Directory:** the directory that you just created in the previous step + - Click **OK** + +> **Note**: The reason you are cloning the original Laravel repository (and not the fork you made) is so you can always pull down the most recent changes from the Laravel repository to your local repository. + + +## Adding your Fork + +After the cloning process is complete, it's time to add the fork you made as a **remote repository**. + +- Right-click the Laravel directory and goto **TortoiseGit > Settings** +- Goto the **Git/Remote** section. Add a new remote: + - **Remote**: fork + - **URL**: https://github.com/username/laravel.git + - Click **Add New/Save** + - Click **OK** + +Remember to replace *username* with your GitHub username. *This is case-sensitive*. + + +## Creating Branches + +Now you are ready to create a new branch for your new feature or bug-fix. When you create a new branch, use a self-descriptive naming convention. For example, if you are going to fix a bug in Eloquent, name your branch *bug/eloquent*. Or if you were going to make changes to the localization documentation, name your branch *feature/localization-docs*. A good naming convention will encourage organization and help others understand the purpose of your branch. + +- Right-click the Laravel directory and goto **TortoiseGit > Create Branch** + - **Branch:** feature/localization-docs + - **Base On Branch:** remotes/origin/develop + - **Check** *Track* + - **Check** *Switch to new branch* + - Click **OK** + +This will create your new *feature/localization-docs* branch and switch you to it. + +> **Note:** Create one new branch for every new feature or bug-fix. This will encourage organization, limit interdependency between new features/fixes and will make it easy for the Laravel team to merge your changes into the Laravel core. + +Now that you have created your own branch and have switched to it, it's time to make your changes to the code. Add your new feature or fix that bug. + + +##Committing + +Now that you have finished coding and testing your changes, it's time to commit them to your local repository: + +- Right-click the Laravel directory and goto **Git Commit -> "feature/localization-docs"...** +- Commit + - **Message:** Provide a brief explaination of what you added or changed + - Click **Sign** - This tells the Laravel team know that you personally agree to your code being added to the Laravel core + - **Changes made:** Check all changed/added files + - Click **OK** + + +## Pushing to your Fork + +Now that your local repository has your committed changes, it's time to push (or sync) your new branch to your fork that is hosted in GitHub: + +- Right-click the Laravel directory and goto **Git Sync...** +- Git Syncronization + - **Local Branch:** feature/localization-docs + - **Remote Branch:** leave this blank + - **Remote URL:** fork + - Click **Push** + - When asked for "username:" enter your GitHub *case-sensitive* username + - When asked for "password:" enter your GitHub *case-sensitive* account + +Your branch has been successfully pushed to your fork on GitHub. + + +## Submitting a Pull Request + +The final step is to submit a pull request to the Laravel repository. This means that you are requesting that the Laravel team pull and merge your changes to the Laravel core. In your browser, visit your Laravel fork at [https://github.com/username/laravel](https://github.com/username/laravel). Click on **Pull Request**. Next, make sure you choose the proper base and head repositories and branches: + +- **base repo:** laravel/laravel +- **base branch:** develop +- **head repo:** username/laravel +- **head branch:** feature/localization-docs + +Use the form to write a more detailed description of the changes you made and why you made them. Finally, click **Send pull request**. That's it! The changes you made have been submitted to the Laravel team. + + +## What's Next? + +Do you have another feature you want to add or another bug you need to fix? Just follow the same instructions as before in the [Creating Branches](#creating-branches) section. Just remember to always create a new branch for every new feature/fix and don't forget to always base your new branches off of the *remotes/origin/develop* branch. diff --git a/app/laravel/documentation/controllers.md b/app/laravel/documentation/controllers.md new file mode 100644 index 000000000..eb3e490a2 --- /dev/null +++ b/app/laravel/documentation/controllers.md @@ -0,0 +1,206 @@ +# Controllers + +## Contents + +- [The Basics](#the-basics) +- [Controller Routing](#controller-routing) +- [Bundle Controllers](#bundle-controllers) +- [Action Filters](#action-filters) +- [Nested Controllers](#nested-controllers) +- [Controller Layouts](#controller-layouts) +- [RESTful Controllers](#restful-controllers) +- [Dependency Injection](#dependency-injection) +- [Controller Factory](#controller-factory) + + +## The Basics + +Controllers are classes that are responsible for accepting user input and managing interactions between models, libraries, and views. Typically, they will ask a model for data, and then return a view that presents that data to the user. + +The usage of controllers is the most common method of implementing application logic in modern web-development. However, Laravel also empowers developers to implement their application logic within routing declarations. This is explored in detail in the [routing document](/docs/routing). New users are encouraged to start with controllers. There is nothing that route-based application logic can do that controllers can't. + +Controller classes should be stored in **application/controllers** and should extend the Base\_Controller class. A Home\_Controller class is included with Laravel. + +#### Creating a simple controller: + + class Admin_Controller extends Base_Controller + { + + public function action_index() + { + // + } + + } + +**Actions** are the name of controller methods that are intended to be web-accessible. Actions should be prefixed with "action\_". All other methods, regardless of scope, will not be web-accessible. + +> **Note:** The Base\_Controller class extends the main Laravel Controller class, and gives you a convenient place to put methods that are common to many controllers. + + +## Controller Routing + +It is important to be aware that all routes in Laravel must be explicitly defined, including routes to controllers. + +This means that controller methods that have not been exposed through route registration **cannot** be accessed. It's possible to automatically expose all methods within a controller using controller route registration. Controller route registrations are typically defined in **application/routes.php**. + +Check [the routing page](/docs/routing#controller-routing) for more information on routing to controllers. + + +## Bundle Controllers + +Bundles are Laravel's modular package system. Bundles can be easily configured to handle requests to your application. We'll be going over [bundles in more detail](/docs/bundles) in another document. + +Creating controllers that belong to bundles is almost identical to creating your application controllers. Just prefix the controller class name with the name of the bundle, so if your bundle is named "admin", your controller classes would look like this: + +#### Creating a bundle controller class: + + class Admin_Home_Controller extends Base_Controller + { + + public function action_index() + { + return "Hello Admin!"; + } + + } + +But, how do you register a bundle controller with the router? It's simple. Here's what it looks like: + +#### Registering a bundle's controller with the router: + + Route::controller('admin::home'); + +Great! Now we can access our "admin" bundle's home controller from the web! + +> **Note:** Throughout Laravel the double-colon syntax is used to denote bundles. More information on bundles can be found in the [bundle documentation](/docs/bundles). + + +## Action Filters + +Action filters are methods that can be run before or after a controller action. With Laravel you don't only have control over which filters are assigned to which actions. But, you can also choose which http verbs (post, get, put, and delete) will activate a filter. + +You can assign "before" and "after" filters to controller actions within the controller's constructor. + +#### Attaching a filter to all actions: + + $this->filter('before', 'auth'); + +In this example the 'auth' filter will be run before every action within this controller. The auth action comes out-of-the-box with Laravel and can be found in **application/routes.php**. The auth filter verifies that a user is logged in and redirects them to 'login' if they are not. + +#### Attaching a filter to only some actions: + + $this->filter('before', 'auth')->only(array('index', 'list')); + +In this example the auth filter will be run before the action_index() or action_list() methods are run. Users must be logged in before having access to these pages. However, no other actions within this controller require an authenticated session. + +#### Attaching a filter to all except a few actions: + + $this->filter('before', 'auth')->except(array('add', 'posts')); + +Much like the previous example, this declaration ensures that the auth filter is run on only some of this controller's actions. Instead of declaring to which actions the filter applies we are instead declaring the actions that will not require authenticated sessions. It can sometimes be safer to use the 'except' method as it's possible to add new actions to this controller and to forget to add them to only(). This could potentially lead your controller's action being unintentionally accessible by users who haven't been authenticated. + +#### Attaching a filter to run on POST: + + $this->filter('before', 'csrf')->on('post'); + +This example shows how a filter can be run only on a specific http verb. In this case we're running the csrf filter only when a form post is made. The csrf filter is designed to prevent form posts from other systems (spam bots for example) and comes by default with Laravel. You can find the csrf filter in **application/routes.php**. + +*Further Reading:* + +- *[Route Filters](/docs/routing#filters)* + + +## Nested Controllers + +Controllers may be located within any number of sub-directories within the main **application/controllers** folder. + +Define the controller class and store it in **controllers/admin/panel.php**. + + class Admin_Panel_Controller extends Base_Controller + { + + public function action_index() + { + // + } + + } + +#### Register the nested controller with the router using "dot" syntax: + + Route::controller('admin.panel'); + +> **Note:** When using nested controllers, always register your controllers from most nested to least nested in order to avoid shadowing controller routes. + +#### Access the "index" action of the controller: + + http://localhost/admin/panel + + +## Controller Layouts + +Full documentation on using layouts with Controllers [can be found on the Templating page](http://laravel.com/docs/views/templating). + + +## RESTful Controllers + +Instead of prefixing controller actions with "action_", you may prefix them with the HTTP verb they should respond to. + +#### Adding the RESTful property to the controller: + + class Home_Controller extends Base_Controller + { + + public $restful = true; + + } + +#### Building RESTful controller actions: + + class Home_Controller extends Base_Controller + { + + public $restful = true; + + public function get_index() + { + // + } + + public function post_index() + { + // + } + + } + +This is particularly useful when building CRUD methods as you can separate the logic which populates and renders a form from the logic that validates and stores the results. + + +## Dependency Injection + +If you are focusing on writing testable code, you will probably want to inject dependencies into the constructor of your controller. No problem. Just register your controller in the [IoC container](/docs/ioc). When registering the controller with the container, prefix the key with **controller**. So, in our **application/start.php** file, we could register our user controller like so: + + IoC::register('controller: user', function() + { + return new User_Controller; + }); + +When a request to a controller enters your application, Laravel will automatically determine if the controller is registered in the container, and if it is, will use the container to resolve an instance of the controller. + +> **Note:** Before diving into controller dependency injection, you may wish to read the documentation on Laravel's beautiful [IoC container](/docs/ioc). + + +## Controller Factory + +If you want even more control over the instantiation of your controllers, such as using a third-party IoC container, you'll need to use the Laravel controller factory. + +**Register an event to handle controller instantiation:** + + Event::listen(Controller::factory, function($controller) + { + return new $controller; + }); + +The event will receive the class name of the controller that needs to be resolved. All you need to do is return an instance of the controller. diff --git a/app/laravel/documentation/database/config.md b/app/laravel/documentation/database/config.md new file mode 100644 index 000000000..b593dbc60 --- /dev/null +++ b/app/laravel/documentation/database/config.md @@ -0,0 +1,70 @@ +# Database Configuration + +## Contents + +- [Quick Start Using SQLite](#quick) +- [Configuring Other Databases](#server) +- [Setting The Default Connection Name](#default) +- [Overwriting The Default PDO Options](#options) + +Laravel supports the following databases out of the box: + +- MySQL +- PostgreSQL +- SQLite +- SQL Server + +All of the database configuration options live in the **application/config/database.php** file. + + +## Quick Start Using SQLite + +[SQLite](http://sqlite.org) is an awesome, zero-configuration database system. By default, Laravel is configured to use a SQLite database. Really, you don't have to change anything. Just drop a SQLite database named **application.sqlite** into the **application/storage/database** directory. You're done. + +Of course, if you want to name your database something besides "application", you can modify the database option in the SQLite section of the **application/config/database.php** file: + + 'sqlite' => array( + 'driver' => 'sqlite', + 'database' => 'your_database_name', + ) + +If your application receives less than 100,000 hits per day, SQLite should be suitable for production use in your application. Otherwise, consider using MySQL or PostgreSQL. + +> **Note:** Need a good SQLite manager? Check out this [Firefox extension](https://addons.mozilla.org/en-US/firefox/addon/sqlite-manager/). + + +## Configuring Other Databases + +If you are using MySQL, SQL Server, or PostgreSQL, you will need to edit the configuration options in **application/config/database.php**. In the configuration file you can find sample configurations for each of these systems. Just change the options as necessary for your server and set the default connection name. + + +## Setting The Default Connection Name + +As you have probably noticed, each database connection defined in the **application/config/database.php** file has a name. By default, there are three connections defined: **sqlite**, **mysql**, **sqlsrv**, and **pgsql**. You are free to change these connection names. The default connection can be specified via the **default** option: + + 'default' => 'sqlite'; + +The default connection will always be used by the [fluent query builder](/docs/database/fluent). If you need to change the default connection during a request, use the **Config::set** method. + + +##Overwriting The Default PDO Options + +The PDO connector class (**laravel/database/connectors/connector.php**) has a set of default PDO attributes defined which can be overwritten in the options array for each system. For example, one of the default attributes is to force column names to lowercase (**PDO::CASE_LOWER**) even if they are defined in UPPERCASE or CamelCase in the table. Therefore, under the default attributes, query result object variables would only be accessible in lowercase. +An example of the MySQL system settings with added default PDO attributes: + + 'mysql' => array( + 'driver' => 'mysql', + 'host' => 'localhost', + 'database' => 'database', + 'username' => 'root', + 'password' => '', + 'charset' => 'utf8', + 'prefix' => '', + PDO::ATTR_CASE => PDO::CASE_LOWER, + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, + PDO::ATTR_ORACLE_NULLS => PDO::NULL_NATURAL, + PDO::ATTR_STRINGIFY_FETCHES => false, + PDO::ATTR_EMULATE_PREPARES => false, + ), + +More about the PDO connection attributes can be found [in the PHP manual](http://php.net/manual/en/pdo.setattribute.php). \ No newline at end of file diff --git a/app/laravel/documentation/database/eloquent.md b/app/laravel/documentation/database/eloquent.md new file mode 100644 index 000000000..f16437177 --- /dev/null +++ b/app/laravel/documentation/database/eloquent.md @@ -0,0 +1,563 @@ +# Eloquent ORM + +## Contents + +- [The Basics](#the-basics) +- [Conventions](#conventions) +- [Retrieving Models](#get) +- [Aggregates](#aggregates) +- [Inserting & Updating Models](#save) +- [Relationships](#relationships) +- [Inserting Related Models](#inserting-related-models) +- [Working With Intermediate Tables](#intermediate-tables) +- [Eager Loading](#eager) +- [Constraining Eager Loads](#constraining-eager-loads) +- [Setter & Getter Methods](#getter-and-setter-methods) +- [Mass-Assignment](#mass-assignment) +- [Converting Models To Arrays](#to-array) +- [Deleting Models](#delete) + + +## The Basics + +An ORM is an [object-relational mapper](http://en.wikipedia.org/wiki/Object-relational_mapping), and Laravel has one that you will absolutely love to use. It is named "Eloquent" because it allows you to work with your database objects and relationships using an eloquent and expressive syntax. In general, you will define one Eloquent model for each table in your database. To get started, let's define a simple model: + + class User extends Eloquent {} + +Nice! Notice that our model extends the **Eloquent** class. This class will provide all of the functionality you need to start working eloquently with your database. + +> **Note:** Typically, Eloquent models live in the **application/models** directory. + + +## Conventions + +Eloquent makes a few basic assumptions about your database structure: + +- Each table should have a primary key named **id**. +- Each table name should be the plural form of its corresponding model name. + +Sometimes you may wish to use a table name other than the plural form of your model. No problem. Just add a static **table** property your model: + + class User extends Eloquent { + + public static $table = 'my_users'; + + } + + +## Retrieving Models + +Retrieving models using Eloquent is refreshingly simple. The most basic way to retrieve an Eloquent model is the static **find** method. This method will return a single model by primary key with properties corresponding to each column on the table: + + $user = User::find(1); + + echo $user->email; + +The find method will execute a query that looks something like this: + + SELECT * FROM "users" WHERE "id" = 1 + +Need to retrieve an entire table? Just use the static **all** method: + + $users = User::all(); + + foreach ($users as $user) + { + echo $user->email; + } + +Of course, retrieving an entire table isn't very helpful. Thankfully, **every method that is available through the fluent query builder is available in Eloquent**. Just begin querying your model with a static call to one of the [query builder](/docs/database/fluent) methods, and execute the query using the **get** or **first** method. The get method will return an array of models, while the first method will return a single model: + + $user = User::where('email', '=', $email)->first(); + + $user = User::where_email($email)->first(); + + $users = User::where_in('id', array(1, 2, 3))->or_where('email', '=', $email)->get(); + + $users = User::order_by('votes', 'desc')->take(10)->get(); + +> **Note:** If no results are found, the **first** method will return NULL. The **all** and **get** methods return an empty array. + + +## Aggregates + +Need to get a **MIN**, **MAX**, **AVG**, **SUM**, or **COUNT** value? Just pass the column to the appropriate method: + + $min = User::min('id'); + + $max = User::max('id'); + + $avg = User::avg('id'); + + $sum = User::sum('id'); + + $count = User::count(); + +Of course, you may wish to limit the query using a WHERE clause first: + + $count = User::where('id', '>', 10)->count(); + + +## Inserting & Updating Models + +Inserting Eloquent models into your tables couldn't be easier. First, instantiate a new model. Second, set its properties. Third, call the **save** method: + + $user = new User; + + $user->email = 'example@gmail.com'; + $user->password = 'secret'; + + $user->save(); + +Alternatively, you may use the **create** method, which will insert a new record into the database and return the model instance for the newly inserted record, or **false** if the insert failed. + + $user = User::create(array('email' => 'example@gmail.com')); + +Updating models is just as simple. Instead of instantiating a new model, retrieve one from your database. Then, set its properties and save: + + $user = User::find(1); + + $user->email = 'new_email@gmail.com'; + $user->password = 'new_secret'; + + $user->save(); + +Need to maintain creation and update timestamps on your database records? With Eloquent, you don't have to worry about it. Just add a static **timestamps** property to your model: + + class User extends Eloquent { + + public static $timestamps = true; + + } + +Next, add **created_at** and **updated_at** date columns to your table. Now, whenever you save the model, the creation and update timestamps will be set automatically. You're welcome. + +In some cases it may be useful to update the **updated_at** date column without actually modifying any data within the model. Simply use the **touch** method, which will also automatically save the changes immediately: + + $comment = Comment::find(1); + $comment->touch(); + +You can also use the **timestamp** function to update the **updated_at** date column without saving the model immediately. Note that if you are actually modifying the model's data this is handled behind the scenes: + + $comment = Comment::find(1); + $comment->timestamp(); + //do something else here, but not modifying the $comment model data + $comment->save(); + +> **Note:** You can change the default timezone of your application in the **application/config/application.php** file. + + +## Relationships + +Unless you're doing it wrong, your database tables are probably related to one another. For instance, an order may belong to a user. Or, a post may have many comments. Eloquent makes defining relationships and retrieving related models simple and intuitive. Laravel supports three types of relationships: + +- [One-To-One](#one-to-one) +- [One-To-Many](#one-to-many) +- [Many-To-Many](#many-to-many) + +To define a relationship on an Eloquent model, you simply create a method that returns the result of either the **has\_one**, **has\_many**, **belongs\_to**, or **has\_many\_and\_belongs\_to** method. Let's examine each one in detail. + + +### One-To-One + +A one-to-one relationship is the most basic form of relationship. For example, let's pretend a user has one phone. Simply describe this relationship to Eloquent: + + class User extends Eloquent { + + public function phone() + { + return $this->has_one('Phone'); + } + + } + +Notice that the name of the related model is passed to the **has_one** method. You can now retrieve the phone of a user through the **phone** method: + + $phone = User::find(1)->phone()->first(); + +Let's examine the SQL performed by this statement. Two queries will be performed: one to retrieve the user and one to retrieve the user's phone: + + SELECT * FROM "users" WHERE "id" = 1 + + SELECT * FROM "phones" WHERE "user_id" = 1 + +Note that Eloquent assumes the foreign key of the relationship will be **user\_id**. Most foreign keys will follow this **model\_id** convention; however, if you want to use a different column name as the foreign key, just pass it in the second parameter to the method: + + return $this->has_one('Phone', 'my_foreign_key'); + +Want to just retrieve the user's phone without calling the first method? No problem. Just use the **dynamic phone property**. Eloquent will automatically load the relationship for you, and is even smart enough to know whether to call the get (for one-to-many relationships) or first (for one-to-one relationships) method: + + $phone = User::find(1)->phone; + +What if you need to retrieve a phone's user? Since the foreign key (**user\_id**) is on the phones table, we should describe this relationship using the **belongs\_to** method. It makes sense, right? Phones belong to users. When using the **belongs\_to** method, the name of the relationship method should correspond to the foreign key (sans the **\_id**). Since the foreign key is **user\_id**, your relationship method should be named **user**: + + class Phone extends Eloquent { + + public function user() + { + return $this->belongs_to('User'); + } + + } + +Great! You can now access a User model through a Phone model using either your relationship method or dynamic property: + + echo Phone::find(1)->user()->first()->email; + + echo Phone::find(1)->user->email; + + +### One-To-Many + +Assume a blog post has many comments. It's easy to define this relationship using the **has_many** method: + + class Post extends Eloquent { + + public function comments() + { + return $this->has_many('Comment'); + } + + } + +Now, simply access the post comments through the relationship method or dynamic property: + + $comments = Post::find(1)->comments()->get(); + + $comments = Post::find(1)->comments; + +Both of these statements will execute the following SQL: + + SELECT * FROM "posts" WHERE "id" = 1 + + SELECT * FROM "comments" WHERE "post_id" = 1 + +Want to join on a different foreign key? No problem. Just pass it in the second parameter to the method: + + return $this->has_many('Comment', 'my_foreign_key'); + +You may be wondering: _If the dynamic properties return the relationship and require less keystrokes, why would I ever use the relationship methods?_ Actually, relationship methods are very powerful. They allow you to continue to chain query methods before retrieving the relationship. Check this out: + + echo Post::find(1)->comments()->order_by('votes', 'desc')->take(10)->get(); + + +### Many-To-Many + +Many-to-many relationships are the most complicated of the three relationships. But don't worry, you can do this. For example, assume a User has many Roles, but a Role can also belong to many Users. Three database tables must be created to accomplish this relationship: a **users** table, a **roles** table, and a **role_user** table. The structure for each table looks like this: + +**users:** + + id - INTEGER + email - VARCHAR + +**roles:** + + id - INTEGER + name - VARCHAR + +**role_user:** + + id - INTEGER + user_id - INTEGER + role_id - INTEGER + +Tables contain many records and are consequently plural. Pivot tables used in **has\_many\_and\_belongs\_to** relationships are named by combining the singular names of the two related models arranged alphabetically and concatenating them with an underscore. + +Now you're ready to define the relationship on your models using the **has\_many\_and\_belongs\_to** method: + + class User extends Eloquent { + + public function roles() + { + return $this->has_many_and_belongs_to('Role'); + } + + } + +Great! Now it's time to retrieve a user's roles: + + $roles = User::find(1)->roles()->get(); + +Or, as usual, you may retrieve the relationship through the dynamic roles property: + + $roles = User::find(1)->roles; + +If your table names don't follow conventions, simply pass the table name in the second parameter to the **has\_and\_belongs\_to\_many** method: + + class User extends Eloquent { + + public function roles() + { + return $this->has_many_and_belongs_to('Role', 'user_roles'); + } + + } + +By default only certain fields from the pivot table will be returned (the two **id** fields, and the timestamps). If your pivot table contains additional columns, you can fetch them too by using the **with()** method : + + class User extends Eloquent { + + public function roles() + { + return $this->has_many_and_belongs_to('Role', 'user_roles')->with('column'); + } + + } + + +## Inserting Related Models + +Let's assume you have a **Post** model that has many comments. Often you may want to insert a new comment for a given post. Instead of manually setting the **post_id** foreign key on your model, you may insert the new comment from it's owning Post model. Here's what it looks like: + + $comment = new Comment(array('message' => 'A new comment.')); + + $post = Post::find(1); + + $comment = $post->comments()->insert($comment); + +When inserting related models through their parent model, the foreign key will automatically be set. So, in this case, the "post_id" was automatically set to "1" on the newly inserted comment. + + +When working with `has_many` relationships, you may use the `save` method to insert / update related models: + + $comments = array( + array('message' => 'A new comment.'), + array('message' => 'A second comment.'), + ); + + $post = Post::find(1); + + $post->comments()->save($comments); + +### Inserting Related Models (Many-To-Many) + +This is even more helpful when working with many-to-many relationships. For example, consider a **User** model that has many roles. Likewise, the **Role** model may have many users. So, the intermediate table for this relationship has "user_id" and "role_id" columns. Now, let's insert a new Role for a User: + + $role = new Role(array('title' => 'Admin')); + + $user = User::find(1); + + $role = $user->roles()->insert($role); + +Now, when the Role is inserted, not only is the Role inserted into the "roles" table, but a record in the intermediate table is also inserted for you. It couldn't be easier! + +However, you may often only want to insert a new record into the intermediate table. For example, perhaps the role you wish to attach to the user already exists. Just use the attach method: + + $user->roles()->attach($role_id); + +It's also possible to attach data for fields in the intermediate table (pivot table), to do this add a second array variable to the attach command containing the data you want to attach: + + $user->roles()->attach($role_id, array('expires' => $expires)); + + +Alternatively, you can use the `sync` method, which accepts an array of IDs to "sync" with the intermediate table. After this operation is complete, only the IDs in the array will be on the intermediate table. + + $user->roles()->sync(array(1, 2, 3)); + + +## Working With Intermediate Tables + +As your probably know, many-to-many relationships require the presence of an intermediate table. Eloquent makes it a breeze to maintain this table. For example, let's assume we have a **User** model that has many roles. And, likewise, a **Role** model that has many users. So the intermediate table has "user_id" and "role_id" columns. We can access the pivot table for the relationship like so: + + $user = User::find(1); + + $pivot = $user->roles()->pivot(); + +Once we have an instance of the pivot table, we can use it just like any other Eloquent model: + + foreach ($user->roles()->pivot()->get() as $row) + { + // + } + +You may also access the specific intermediate table row associated with a given record. For example: + + $user = User::find(1); + + foreach ($user->roles as $role) + { + echo $role->pivot->created_at; + } + +Notice that each related **Role** model we retrieved is automatically assigned a **pivot** attribute. This attribute contains a model representing the intermediate table record associated with that related model. + +Sometimes you may wish to remove all of the record from the intermediate table for a given model relationship. For instance, perhaps you want to remove all of the assigned roles from a user. Here's how to do it: + + $user = User::find(1); + + $user->roles()->delete(); + +Note that this does not delete the roles from the "roles" table, but only removes the records from the intermediate table which associated the roles with the given user. + + +## Eager Loading + +Eager loading exists to alleviate the N + 1 query problem. Exactly what is this problem? Well, pretend each Book belongs to an Author. We would describe this relationship like so: + + class Book extends Eloquent { + + public function author() + { + return $this->belongs_to('Author'); + } + + } + +Now, examine the following code: + + foreach (Book::all() as $book) + { + echo $book->author->name; + } + +How many queries will be executed? Well, one query will be executed to retrieve all of the books from the table. However, another query will be required for each book to retrieve the author. To display the author name for 25 books would require **26 queries**. See how the queries can add up fast? + +Thankfully, you can eager load the author models using the **with** method. Simply mention the **function name** of the relationship you wish to eager load: + + foreach (Book::with('author')->get() as $book) + { + echo $book->author->name; + } + +In this example, **only two queries will be executed**! + + SELECT * FROM "books" + + SELECT * FROM "authors" WHERE "id" IN (1, 2, 3, 4, 5, ...) + +Obviously, wise use of eager loading can dramatically increase the performance of your application. In the example above, eager loading cut the execution time in half. + +Need to eager load more than one relationship? It's easy: + + $books = Book::with(array('author', 'publisher'))->get(); + +> **Note:** When eager loading, the call to the static **with** method must always be at the beginning of the query. + +You may even eager load nested relationships. For example, let's assume our **Author** model has a "contacts" relationship. We can eager load both of the relationships from our Book model like so: + + $books = Book::with(array('author', 'author.contacts'))->get(); + +If you find yourself eager loading the same models often, you may want to use **$includes** in the model. + + class Book extends Eloquent { + + public $includes = array('author'); + + public function author() + { + return $this->belongs_to('Author'); + } + + } + +**$includes** takes the same arguments that **with** takes. The following is now eagerly loaded. + + foreach (Book::all() as $book) + { + echo $book->author->name; + } + +> **Note:** Using **with** will override a models **$includes**. + + +## Constraining Eager Loads + +Sometimes you may wish to eager load a relationship, but also specify a condition for the eager load. It's simple. Here's what it looks like: + + $users = User::with(array('posts' => function($query) + { + $query->where('title', 'like', '%first%'); + + }))->get(); + +In this example, we're eager loading the posts for the users, but only if the post's "title" column contains the word "first". + + +## Getter & Setter Methods + +Setters allow you to handle attribute assignment with custom methods. Define a setter by appending "set_" to the intended attribute's name. + + public function set_password($password) + { + $this->set_attribute('hashed_password', Hash::make($password)); + } + +Call a setter method as a variable (without parenthesis) using the name of the method without the "set_" prefix. + + $this->password = "my new password"; + +Getters are very similar. They can be used to modify attributes before they're returned. Define a getter by appending "get_" to the intended attribute's name. + + public function get_published_date() + { + return date('M j, Y', $this->get_attribute('published_at')); + } + +Call the getter method as a variable (without parenthesis) using the name of the method without the "get_" prefix. + + echo $this->published_date; + + +## Mass-Assignment + +Mass-assignment is the practice of passing an associative array to a model method which then fills the model's attributes with the values from the array. Mass-assignment can be done by passing an array to the model's constructor: + + $user = new User(array( + 'username' => 'first last', + 'password' => 'disgaea' + )); + + $user->save(); + +Or, mass-assignment may be accomplished using the **fill** method. + + $user = new User; + + $user->fill(array( + 'username' => 'first last', + 'password' => 'disgaea' + )); + + $user->save(); + +By default, all attribute key/value pairs will be store during mass-assignment. However, it is possible to create a white-list of attributes that will be set. If the accessible attribute white-list is set then no attributes other than those specified will be set during mass-assignment. + +You can specify accessible attributes by assigning the **$accessible** static array. Each element contains the name of a white-listed attribute. + + public static $accessible = array('email', 'password', 'name'); + +Alternatively, you may use the **accessible** method from your model: + + User::accessible(array('email', 'password', 'name')); + +> **Note:** Utmost caution should be taken when mass-assigning using user-input. Technical oversights could cause serious security vulnerabilities. + + +## Converting Models To Arrays + +When building JSON APIs, you will often need to convert your models to array so they can be easily serialized. It's really simple. + +#### Convert a model to an array: + + return json_encode($user->to_array()); + +The `to_array` method will automatically grab all of the attributes on your model, as well as any loaded relationships. + +Sometimes you may wish to limit the attributes that are included in your model's array, such as passwords. To do this, add a `hidden` attribute definition to your model: + +#### Excluding attributes from the array: + + class User extends Eloquent { + + public static $hidden = array('password'); + + } + + +## Deleting Models + +Because Eloquent inherits all the features and methods of Fluent queries, deleting models is a snap: + + $author->delete(); + +Note, however, than this won't delete any related models (e.g. all the author's Book models will still exist), unless you have set up [foreign keys](/docs/database/schema#foreign-keys) and cascading deletes. diff --git a/app/laravel/documentation/database/fluent.md b/app/laravel/documentation/database/fluent.md new file mode 100644 index 000000000..ceeb809ba --- /dev/null +++ b/app/laravel/documentation/database/fluent.md @@ -0,0 +1,270 @@ +# Fluent Query Builder + +## Contents + +- [The Basics](#the-basics) +- [Retrieving Records](#get) +- [Building Where Clauses](#where) +- [Nested Where Clauses](#nested-where) +- [Dynamic Where Clauses](#dynamic) +- [Table Joins](#joins) +- [Ordering Results](#ordering) +- [Skip & Take](#limit) +- [Aggregates](#aggregates) +- [Expressions](#expressions) +- [Inserting Records](#insert) +- [Updating Records](#update) +- [Deleting Records](#delete) + +## The Basics + +The Fluent Query Builder is Laravel's powerful fluent interface for building SQL queries and working with your database. All queries use prepared statements and are protected against SQL injection. + +You can begin a fluent query using the **table** method on the DB class. Just mention the table you wish to query: + + $query = DB::table('users'); + +You now have a fluent query builder for the "users" table. Using this query builder, you can retrieve, insert, update, or delete records from the table. + + +## Retrieving Records + +#### Retrieving an array of records from the database: + + $users = DB::table('users')->get(); + +> **Note:** The **get** method returns an array of objects with properties corresponding to the column on the table. + +#### Retrieving a single record from the database: + + $user = DB::table('users')->first(); + +#### Retrieving a single record by its primary key: + + $user = DB::table('users')->find($id); + +> **Note:** If no results are found, the **first** method will return NULL. The **get** method will return an empty array. + +#### Retrieving the value of a single column from the database: + + $email = DB::table('users')->where('id', '=', 1)->only('email'); + +#### Only selecting certain columns from the database: + + $user = DB::table('users')->get(array('id', 'email as user_email')); + +#### Selecting distinct results from the database: + + $user = DB::table('users')->distinct()->get(); + + +## Building Where Clauses + +### where and or\_where + +There are a variety of methods to assist you in building where clauses. The most basic of these methods are the **where** and **or_where** methods. Here is how to use them: + + return DB::table('users') + ->where('id', '=', 1) + ->or_where('email', '=', 'example@gmail.com') + ->first(); + +Of course, you are not limited to simply checking equality. You may also use **greater-than**, **less-than**, **not-equal**, and **like**: + + return DB::table('users') + ->where('id', '>', 1) + ->or_where('name', 'LIKE', '%Taylor%') + ->first(); + +As you may have assumed, the **where** method will add to the query using an AND condition, while the **or_where** method will use an OR condition. + +### where\_in, where\_not\_in, or\_where\_in, and or\_where\_not\_in + +The suite of **where_in** methods allows you to easily construct queries that search an array of values: + + DB::table('users')->where_in('id', array(1, 2, 3))->get(); + + DB::table('users')->where_not_in('id', array(1, 2, 3))->get(); + + DB::table('users') + ->where('email', '=', 'example@gmail.com') + ->or_where_in('id', array(1, 2, 3)) + ->get(); + + DB::table('users') + ->where('email', '=', 'example@gmail.com') + ->or_where_not_in('id', array(1, 2, 3)) + ->get(); + +### where\_null, where\_not\_null, or\_where\_null, and or\_where\_not\_null + +The suite of **where_null** methods makes checking for NULL values a piece of cake: + + return DB::table('users')->where_null('updated_at')->get(); + + return DB::table('users')->where_not_null('updated_at')->get(); + + return DB::table('users') + ->where('email', '=', 'example@gmail.com') + ->or_where_null('updated_at') + ->get(); + + return DB::table('users') + ->where('email', '=', 'example@gmail.com') + ->or_where_not_null('updated_at') + ->get(); + + +## Nested Where Clauses + +You may discover the need to group portions of a WHERE clause within parentheses. Just pass a Closure as parameter to the **where** or **or_where** methods: + + $users = DB::table('users') + ->where('id', '=', 1) + ->or_where(function($query) + { + $query->where('age', '>', 25); + $query->where('votes', '>', 100); + }) + ->get(); + +The example above would generate a query that looks like: + + SELECT * FROM "users" WHERE "id" = ? OR ("age" > ? AND "votes" > ?) + + +## Dynamic Where Clauses + +Dynamic where methods are great way to increase the readability of your code. Here are some examples: + + $user = DB::table('users')->where_email('example@gmail.com')->first(); + + $user = DB::table('users')->where_email_and_password('example@gmail.com', 'secret'); + + $user = DB::table('users')->where_id_or_name(1, 'Fred'); + + + +## Table Joins + +Need to join to another table? Try the **join** and **left\_join** methods: + + DB::table('users') + ->join('phone', 'users.id', '=', 'phone.user_id') + ->get(array('users.email', 'phone.number')); + +The **table** you wish to join is passed as the first parameter. The remaining three parameters are used to construct the **ON** clause of the join. + +Once you know how to use the join method, you know how to **left_join**. The method signatures are the same: + + DB::table('users') + ->left_join('phone', 'users.id', '=', 'phone.user_id') + ->get(array('users.email', 'phone.number')); + +You may also specify multiple conditions for an **ON** clause by passing a Closure as the second parameter of the join: + + DB::table('users') + ->join('phone', function($join) + { + $join->on('users.id', '=', 'phone.user_id'); + $join->or_on('users.id', '=', 'phone.contact_id'); + }) + ->get(array('users.email', 'phone.number')); + + +## Ordering Results + +You can easily order the results of your query using the **order_by** method. Simply mention the column and direction (desc or asc) of the sort: + + return DB::table('users')->order_by('email', 'desc')->get(); + +Of course, you may sort on as many columns as you wish: + + return DB::table('users') + ->order_by('email', 'desc') + ->order_by('name', 'asc') + ->get(); + + +## Skip & Take + +If you would like to **LIMIT** the number of results returned by your query, you can use the **take** method: + + return DB::table('users')->take(10)->get(); + +To set the **OFFSET** of your query, use the **skip** method: + + return DB::table('users')->skip(10)->get(); + + +## Aggregates + +Need to get a **MIN**, **MAX**, **AVG**, **SUM**, or **COUNT** value? Just pass the column to the query: + + $min = DB::table('users')->min('age'); + + $max = DB::table('users')->max('weight'); + + $avg = DB::table('users')->avg('salary'); + + $sum = DB::table('users')->sum('votes'); + + $count = DB::table('users')->count(); + +Of course, you may wish to limit the query using a WHERE clause first: + + $count = DB::table('users')->where('id', '>', 10)->count(); + + +## Expressions + +Sometimes you may need to set the value of a column to a SQL function such as **NOW()**. Usually a reference to now() would automatically be quoted and escaped. To prevent this use the **raw** method on the **DB** class. Here's what it looks like: + + DB::table('users')->update(array('updated_at' => DB::raw('NOW()'))); + +The **raw** method tells the query to inject the contents of the expression into the query as a string rather than a bound parameter. For example, you can also use expressions to increment column values: + + DB::table('users')->update(array('votes' => DB::raw('votes + 1'))); + +Of course, convenient methods are provided for **increment** and **decrement**: + + DB::table('users')->increment('votes'); + + DB::table('users')->decrement('votes'); + + +## Inserting Records + +The insert method expects an array of values to insert. The insert method will return true or false, indicating whether the query was successful: + + DB::table('users')->insert(array('email' => 'example@gmail.com')); + +Inserting a record that has an auto-incrementing ID? You can use the **insert\_get\_id** method to insert a record and retrieve the ID: + + $id = DB::table('users')->insert_get_id(array('email' => 'example@gmail.com')); + +> **Note:** The **insert\_get\_id** method expects the name of the auto-incrementing column to be "id". + + +## Updating Records + +To update records simply pass an array of values to the **update** method: + + $affected = DB::table('users')->update(array('email' => 'new_email@gmail.com')); + +Of course, when you only want to update a few records, you should add a WHERE clause before calling the update method: + + $affected = DB::table('users') + ->where('id', '=', 1) + ->update(array('email' => 'new_email@gmail.com')); + + +## Deleting Records + +When you want to delete records from your database, simply call the **delete** method: + + $affected = DB::table('users')->where('id', '=', 1)->delete(); + +Want to quickly delete a record by its ID? No problem. Just pass the ID into the delete method: + + $affected = DB::table('users')->delete(1); \ No newline at end of file diff --git a/app/laravel/documentation/database/migrations.md b/app/laravel/documentation/database/migrations.md new file mode 100644 index 000000000..8ebf7082a --- /dev/null +++ b/app/laravel/documentation/database/migrations.md @@ -0,0 +1,76 @@ +# Migrations + +## Contents + +- [The Basics](#the-basics) +- [Prepping Your Database](#prepping-your-database) +- [Creating Migrations](#creating-migrations) +- [Running Migrations](#running-migrations) +- [Rolling Back](#rolling-back) + + +## The Basics + +Think of migrations as a type of version control for your database. Let's say your working on a team, and you all have local databases for development. Good ole' Eric makes a change to the database and checks in his code that uses the new column. You pull in the code, and your application breaks because you don't have the new column. What do you do? Migrations are the answer. Let's dig in deeper to find out how to use them! + + +## Prepping Your Database + +Before you can run migrations, we need to do some work on your database. Laravel uses a special table to keep track of which migrations have already run. To create this table, just use the Artisan command-line: + +**Creating the Laravel migrations table:** + + php artisan migrate:install + + +## Creating Migrations + +You can easily create migrations through Laravel's "Artisan" CLI. It looks like this: + +**Creating a migration** + + php artisan migrate:make create_users_table + +Now, check your **application/migrations** folder. You should see your brand new migration! Notice that it also contains a timestamp. This allows Laravel to run your migrations in the correct order. + +You may also create migrations for a bundle. + +**Creating a migration for a bundle:** + + php artisan migrate:make bundle::create_users_table + +*Further Reading:* + +- [Schema Builder](/docs/database/schema) + + +## Running Migrations + +**Running all outstanding migrations in application and bundles:** + + php artisan migrate + +**Running all outstanding migrations in the application:** + + php artisan migrate application + +**Running all outstanding migrations in a bundle:** + + php artisan migrate bundle + + +## Rolling Back + +When you roll back a migration, Laravel rolls back the entire migration "operation". So, if the last migration command ran 122 migrations, all 122 migrations would be rolled back. + +**Rolling back the last migration operation:** + + php artisan migrate:rollback + +**Roll back all migrations that have ever run:** + + php artisan migrate:reset + +**Roll back everything and run all migrations again:** + + php artisan migrate:rebuild diff --git a/app/laravel/documentation/database/raw.md b/app/laravel/documentation/database/raw.md new file mode 100644 index 000000000..424ba27d7 --- /dev/null +++ b/app/laravel/documentation/database/raw.md @@ -0,0 +1,56 @@ +# Raw Queries + +## Contents + +- [The Basics](#the-basics) +- [Other Query Methods](#other-query-methods) +- [PDO Connections](#pdo-connections) + + +## The Basics + +The **query** method is used to execute arbitrary, raw SQL against your database connection. + +#### Selecting records from the database: + + $users = DB::query('select * from users'); + +#### Selecting records from the database using bindings: + + $users = DB::query('select * from users where name = ?', array('test')); + +#### Inserting a record into the database + + $success = DB::query('insert into users values (?, ?)', $bindings); + +#### Updating table records and getting the number of affected rows: + + $affected = DB::query('update users set name = ?', $bindings); + +#### Deleting from a table and getting the number of affected rows: + + $affected = DB::query('delete from users where id = ?', array(1)); + + +## Other Query Methods + +Laravel provides a few other methods to make querying your database simple. Here's an overview: + +#### Running a SELECT query and returning the first result: + + $user = DB::first('select * from users where id = 1'); + +#### Running a SELECT query and getting the value of a single column: + + $email = DB::only('select email from users where id = 1'); + + +## PDO Connections + +Sometimes you may wish to access the raw PDO connection behind the Laravel Connection object. + +#### Get the raw PDO connection for a database: + + $pdo = DB::connection('sqlite')->pdo; + +> **Note:** If no connection name is specified, the **default** connection will be returned. \ No newline at end of file diff --git a/app/laravel/documentation/database/redis.md b/app/laravel/documentation/database/redis.md new file mode 100644 index 000000000..42c6d90eb --- /dev/null +++ b/app/laravel/documentation/database/redis.md @@ -0,0 +1,58 @@ +# Redis + +## Contents + +- [The Basics](#the-basics) +- [Configuration](#config) +- [Usage](#usage) + + +## The Basics + +[Redis](http://redis.io) is an open source, advanced key-value store. It is often referred to as a data structure server since keys can contain [strings](http://redis.io/topics/data-types#strings), [hashes](http://redis.io/topics/data-types#hashes), [lists](http://redis.io/topics/data-types#lists), [sets](http://redis.io/topics/data-types#sets), and [sorted sets](http://redis.io/topics/data-types#sorted-sets). + + +## Configuration + +The Redis configuration for your application lives in the **application/config/database.php** file. Within this file, you will see a **redis** array containing the Redis servers used by your application: + + 'redis' => array( + + 'default' => array('host' => '127.0.0.1', 'port' => 6379), + + ), + +The default server configuration should suffice for development. However, you are free to modify this array based on your environment. Simply give each Redis server a name, and specify the host and port used by the server. + + +## Usage + +You may get a Redis instance by calling the **db** method on the **Redis** class: + + $redis = Redis::db(); + +This will give you an instance of the **default** Redis server. You may pass the server name to the **db** method to get a specific server as defined in your Redis configuration: + + $redis = Redis::db('redis_2'); + +Great! Now that we have an instance of the Redis client, we may issue any of the [Redis commands](http://redis.io/commands) to the instance. Laravel uses magic methods to pass the commands to the Redis server: + + $redis->set('name', 'Taylor'); + + $name = $redis->get('name'); + + $values = $redis->lrange('names', 5, 10); + +Notice the arguments to the command are simply passed into the magic method. Of course, you are not required to use the magic methods, you may also pass commands to the server using the **run** method: + + $values = $redis->run('lrange', array(5, 10)); + +Just want to execute commands on the default Redis server? You can just use static magic methods on the Redis class: + + Redis::set('name', 'Taylor'); + + $name = Redis::get('name'); + + $values = Redis::lrange('names', 5, 10); + +> **Note:** Redis [cache](/docs/cache/config#redis) and [session](/docs/session/config#redis) drivers are included with Laravel. \ No newline at end of file diff --git a/app/laravel/documentation/database/schema.md b/app/laravel/documentation/database/schema.md new file mode 100644 index 000000000..904edbbab --- /dev/null +++ b/app/laravel/documentation/database/schema.md @@ -0,0 +1,154 @@ +# Schema Builder + +## Contents + +- [The Basics](#the-basics) +- [Creating & Dropping Tables](#creating-dropping-tables) +- [Adding Columns](#adding-columns) +- [Dropping Columns](#dropping-columns) +- [Adding Indexes](#adding-indexes) +- [Dropping Indexes](#dropping-indexes) +- [Foreign Keys](#foreign-keys) + + +## The Basics + +The Schema Builder provides methods for creating and modifying your database tables. Using a fluent syntax, you can work with your tables without using any vendor specific SQL. + +*Further Reading:* + +- [Migrations](/docs/database/migrations) + + +## Creating & Dropping Tables + +The **Schema** class is used to create and modify tables. Let's jump right into an example: + +#### Creating a simple database table: + + Schema::create('users', function($table) + { + $table->increments('id'); + }); + +Let's go over this example. The **create** method tells the Schema builder that this is a new table, so it should be created. In the second argument, we passed a Closure which receives a Table instance. Using this Table object, we can fluently add and drop columns and indexes on the table. + +#### Dropping a table from the database: + + Schema::drop('users'); + +#### Dropping a table from a given database connection: + + Schema::drop('users', 'connection_name'); + +Sometimes you may need to specify the database connection on which the schema operation should be performed. + +#### Specifying the connection to run the operation on: + + Schema::create('users', function($table) + { + $table->on('connection'); + }); + + +## Adding Columns + +The fluent table builder's methods allow you to add columns without using vendor specific SQL. Let's go over it's methods: + +Command | Description +------------- | ------------- +`$table->increments('id');` | Incrementing ID to the table +`$table->string('email');` | VARCHAR equivalent column +`$table->string('name', 100);` | VARCHAR equivalent with a length +`$table->integer('votes');` | INTEGER equivalent to the table +`$table->float('amount');` | FLOAT equivalent to the table +`$table->decimal('amount', 5, 2);` | DECIMAL equivalent with a precision and scale +`$table->boolean('confirmed');` | BOOLEAN equivalent to the table +`$table->date('created_at');` | DATE equivalent to the table +`$table->timestamp('added_on');` | TIMESTAMP equivalent to the table +`$table->timestamps();` | Adds **created\_at** and **updated\_at** columns +`$table->text('description');` | TEXT equivalent to the table +`$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. + +#### Example of creating a table and adding columns + + Schema::table('users', function($table) + { + $table->create(); + $table->increments('id'); + $table->string('username'); + $table->string('email'); + $table->string('phone')->nullable(); + $table->text('about'); + $table->timestamps(); + }); + + +## Dropping Columns + +#### Dropping a column from a database table: + + $table->drop_column('name'); + +#### Dropping several columns from a database table: + + $table->drop_column(array('name', 'email')); + + +## Adding Indexes + +The Schema builder supports several types of indexes. There are two ways to add the indexes. Each type of index has its method; however, you can also fluently define an index on the same line as a column addition. Let's take a look: + +#### Fluently creating a string column with an index: + + $table->string('email')->unique(); + +If defining the indexes on a separate line is more your style, here are example of using each of the index methods: + +Command | Description +------------- | ------------- +`$table->primary('id');` | Adding a primary key +`$table->primary(array('fname', 'lname'));` | Adding composite keys +`$table->unique('email');` | Adding a unique index +`$table->fulltext('description');` | Adding a full-text index +`$table->index('state');` | Adding a basic index + + +## Dropping Indexes + +To drop indexes you must specify the index's name. Laravel assigns a reasonable name to all indexes. Simply concatenate the table name and the names of the columns in the index, then append the type of the index. Let's take a look at some examples: + +Command | Description +------------- | ------------- +`$table->drop_primary('users_id_primary');` | Dropping a primary key from the "users" table +`$table->drop_unique('users_email_unique');` | Dropping a unique index from the "users" table +`$table->drop_fulltext('profile_description_fulltext');` | Dropping a full-text index from the "profile" table +`$table->drop_index('geo_state_index');` | Dropping a basic index from the "geo" table + + +## Foreign Keys + +You may easily add foreign key constraints to your table using Schema's fluent interface. For example, let's assume you have a **user_id** on a **posts** table, which references the **id** column of the **users** table. Here's how to add a foreign key constraint for the column: + + $table->foreign('user_id')->references('id')->on('users'); + +You may also specify options for the "on delete" and "on update" actions of the foreign key: + + $table->foreign('user_id')->references('id')->on('users')->on_delete('restrict'); + + $table->foreign('user_id')->references('id')->on('users')->on_update('cascade'); + +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'); + +> **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/app/laravel/documentation/encryption.md b/app/laravel/documentation/encryption.md new file mode 100644 index 000000000..9d7e6cee3 --- /dev/null +++ b/app/laravel/documentation/encryption.md @@ -0,0 +1,30 @@ +# Encryption + +## Contents + +- [The Basics](#the-basics) +- [Encrypting A String](#encrypt) +- [Decrypting A String](#decrypt) + + +## The Basics + +Laravel's **Crypter** class provides a simple interface for handling secure, two-way encryption. By default, the Crypter class provides strong AES-256 encryption and decryption out of the box via the Mcrypt PHP extension. + +> **Note:** Don't forget to install the Mcrypt PHP extension on your server. + + +## Encrypting A String + +#### Encrypting a given string: + + $encrypted = Crypter::encrypt($value); + + +## Decrypting A String + +#### Decrypting a string: + + $decrypted = Crypter::decrypt($encrypted); + +> **Note:** It's incredibly important to point out that the decrypt method will only decrypt strings that were encrypted using **your** application key. \ No newline at end of file diff --git a/app/laravel/documentation/events.md b/app/laravel/documentation/events.md new file mode 100644 index 000000000..e678bb5e1 --- /dev/null +++ b/app/laravel/documentation/events.md @@ -0,0 +1,100 @@ +# Events + +## Contents + +- [The Basics](#the-basics) +- [Firing Events](#firing-events) +- [Listening To Events](#listening-to-events) +- [Queued Events](#queued-events) +- [Laravel Events](#laravel-events) + + +## The Basics + +Events can provide a great away to build de-coupled applications, and allow plug-ins to tap into the core of your application without modifying its code. + + +## Firing Events + +To fire an event, just tell the **Event** class the name of the event you want to fire: + +#### Firing an event: + + $responses = Event::fire('loaded'); + +Notice that we assigned the result of the **fire** method to a variable. This method will return an array containing the responses of all the event's listeners. + +Sometimes you may want to fire an event, but just get the first response. Here's how: + +#### Firing an event and retrieving the first response: + + $response = Event::first('loaded'); + +> **Note:** The **first** method will still fire all of the handlers listening to the event, but will only return the first response. + +The **Event::until** method will execute the event handlers until the first non-null response is returned. + +#### Firing an event until the first non-null response: + + $response = Event::until('loaded'); + + +## Listening To Events + +So, what good are events if nobody is listening? Register an event handler that will be called when an event fires: + +#### Registering an event handler: + + Event::listen('loaded', function() + { + // I'm executed on the "loaded" event! + }); + +The Closure we provided to the method will be executed each time the "loaded" event is fired. + + +## Queued Events + +Sometimes you may wish to "queue" an event for firing, but not fire it immediately. This is possible using the `queue` and `flush` methods. First, throw an event on a given queue with a unique identifier: + +#### Registering a queued event: + + Event::queue('foo', $user->id, array($user)); + +This method accepts three parameters. The first is the name of the queue, the second is a unique identifier for this item on the queue, and the third is an array of data to pass to the queue flusher. + +Next, we'll register a flusher for the `foo` queue: + +#### Registering an event flusher: + + Event::flusher('foo', function($key, $user) + { + // + }); + +Note that the event flusher receives two arguments. The first, is the unique identifier for the queued event, which in this case would be the user's ID. The second (and any remaining) parameters would be the payload items for the queued event. + +Finally, we can run our flusher and flush all queued events using the `flush` method: + + Event::flush('foo'); + + +## Laravel Events + +There are several events that are fired by the Laravel core. Here they are: + +#### Event fired when a bundle is started: + + Event::listen('laravel.started: bundle', function() {}); + +#### Event fired when a database query is executed: + + Event::listen('laravel.query', function($sql, $bindings, $time) {}); + +#### Event fired right before response is sent to browser: + + Event::listen('laravel.done', function($response) {}); + +#### Event fired when a messaged is logged using the Log class: + + Event::listen('laravel.log', function($type, $message) {}); \ No newline at end of file diff --git a/app/laravel/documentation/files.md b/app/laravel/documentation/files.md new file mode 100644 index 000000000..ebff9e814 --- /dev/null +++ b/app/laravel/documentation/files.md @@ -0,0 +1,92 @@ +# Working With Files + +## Contents + +- [Reading Files](#get) +- [Writing Files](#put) +- [Removing files](#delete) +- [File Uploads](#upload) +- [File Extensions](#ext) +- [Checking File Types](#is) +- [Getting MIME Types](#mime) +- [Copying Directories](#cpdir) +- [Removing Directories](#rmdir) + + +## Reading Files + +#### Getting the contents of a file: + + $contents = File::get('path/to/file'); + + +## Writing Files + +#### Writing to a file: + + File::put('path/to/file', 'file contents'); + +#### Appending to a file: + + File::append('path/to/file', 'appended file content'); + + +## Removing Files + +#### Deleting a single file: + + File::delete('path/to/file'); + + +## File Uploads + +#### Moving a $_FILE to a permanent location: + + Input::upload('picture', 'path/to/pictures', 'filename.ext'); + +> **Note:** You can easily validate file uploads using the [Validator class](/docs/validation). + + +## File Extensions + +#### Getting the extension from a filename: + + File::extension('picture.png'); + + +## Checking File Types + +#### Determining if a file is given type: + + if (File::is('jpg', 'path/to/file.jpg')) + { + // + } + +The **is** method does not simply check the file extension. The Fileinfo PHP extension will be used to read the content of the file and determine the actual MIME type. + +> **Note:** You may pass any of the extensions defined in the **application/config/mimes.php** file to the **is** method. +> **Note:** The Fileinfo PHP extension is required for this functionality. More information can be found on the [PHP Fileinfo page](http://php.net/manual/en/book.fileinfo.php). + + +## Getting MIME Types + +#### Getting the MIME type associated with an extension: + + echo File::mime('gif'); // outputs 'image/gif' + +> **Note:** This method simply returns the MIME type defined for the extension in the **application/config/mimes.php** file. + + +## Copying Directories + +#### Recursively copy a directory to a given location: + + File::cpdir($directory, $destination); + + +## Removing Directories + +#### Recursively delete a directory: + + File::rmdir($directory); \ No newline at end of file diff --git a/app/laravel/documentation/home.md b/app/laravel/documentation/home.md new file mode 100644 index 000000000..695526cc5 --- /dev/null +++ b/app/laravel/documentation/home.md @@ -0,0 +1,59 @@ +# Laravel Documentation + +- [The Basics](#the-basics) +- [Who Will Enjoy Laravel?](#who-will-enjoy-laravel) +- [What Makes Laravel Different?](#laravel-is-different) +- [Application Structure](#application-structure) +- [Laravel's Community](#laravel-community) +- [License Information](#laravel-license) + + +## The Basics + +Welcome to the Laravel documentation. These documents were designed to function both as a getting-started guide and as a feature reference. Even though you may jump into any section and start learning, we recommend reading the documentation in order as it allows us to progressively establish concepts that will be used in later documents. + + +## Who Will Enjoy Laravel? + +Laravel is a powerful framework that emphasizes flexibility and expressiveness. Users new to Laravel will enjoy the same ease of development that is found in the most popular and lightweight PHP frameworks. More experienced users will appreciate the opportunity to modularize their code in ways that are not possible with other frameworks. Laravel's flexibility will allow your organization to update and mold the application over time as is needed and its expressiveness will allow you and your team to develop code that is both concise and easily read. + + + +## What Makes Laravel Different? + +There are many ways in which Laravel differentiates itself from other frameworks. Here are a few examples that we think make good bullet points: + +- **Bundles** are Laravel's modular packaging system. [The Laravel Bundle Repository](http://bundles.laravel.com/) is already populated with quite a few features that can be easily added to your application. You can either download a bundle repository to your bundles directory or use the "Artisan" command-line tool to automatically install them. +- **The Eloquent ORM** is the most advanced PHP ActiveRecord implementation available. With the capacity to easily apply constraints to both relationships and nested eager-loading you'll have complete control over your data with all of the conveniences of ActiveRecord. Eloquent natively supports all of the methods from Laravel's Fluent query-builder. +- **Application Logic** can be implemented within your application either using controllers (which many web-developers are already familiar with) or directly into route declarations using syntax similar to the Sinatra framework. Laravel is designed with the philosophy of giving a developer the flexibility that they need to create everything from very small sites to massive enterprise applications. +- **Reverse Routing** allows you to create links to named routes. When creating links just use the route's name and Laravel will automatically insert the correct URI. This allows you to change your routes at a later time and Laravel will update all of the relevant links site-wide. +- **Restful Controllers** are an optional way to separate your GET and POST request logic. In a login example your controller's get_login() action would serve up the form and your controller's post_login() action would accept the posted form, validate, and either redirect to the login form with an error message or redirect your user to their dashboard. +- **Class Auto Loading** keeps you from having to maintain an autoloader configuration and from loading unnecessary components when they won't be used. Want to use a library or model? Don't bother loading it, just use it. Laravel will handle the rest. +- **View Composers** are blocks of code that can be run when a view is loaded. A good example of this would be a blog side-navigation view that contains a list of random blog posts. Your composer would contain the logic to load the blog posts so that all you have to do is load the view and it's all ready for you. This keeps you from having to make sure that your controllers load the a bunch of data from your models for views that are unrelated to that method's page content. +- **The IoC container** (Inversion of Control) gives you a method for generating new objects and optionally instantiating and referencing singletons. IoC means that you'll rarely ever need to bootstrap any external libraries. It also means that you can access these objects from anywhere in your code without needing to deal with an inflexible monolithic structure. +- **Migrations** are version control for your database schemas and they are directly integrated into Laravel. You can both generate and run migrations using the "Artisan" command-line utility. Once another member makes schema changes you can update your local copy from the repository and run migrations. Now you're up to date, too! +- **Unit-Testing** is an important part of Laravel. Laravel itself sports hundreds of tests to help ensure that new changes don't unexpectedly break anything. This is one of the reasons why Laravel is widely considered to have some of the most stable releases in the industry. Laravel also makes it easy for you to write unit-tests for your own code. You can then run tests with the "Artisan" command-line utility. +- **Automatic Pagination** prevents your application logic from being cluttered up with a bunch of pagination configuration. Instead of pulling in the current page, getting a count of db records, and selected your data using a limit/offset just call 'paginate' and tell Laravel where to output the paging links in your view. Laravel automatically does the rest. Laravel's pagination system was designed to be easy to implement and easy to change. It's also important to note that just because Laravel can handle these things automatically doesn't mean that you can't call and configure these systems manually if you prefer. + +These are just a few ways in which Laravel differentiates itself from other PHP frameworks. All of these features and many more are discussed thoroughly in this documentation. + + +## Application Structure + +Laravel's directory structure is designed to be familiar to users of other popular PHP frameworks. Web applications of any shape or size can easily be created using this structure similarly to the way that they would be created in other frameworks. + +However due to Laravel's unique architecture, it is possible for developers to create their own infrastructure that is specifically designed for their application. This may be most beneficial to large projects such as content-management-systems. This kind of architectural flexibility is unique to Laravel. + +Throughout the documentation we'll specify the default locations for declarations where appropriate. + + +## Laravel's Community + +Laravel is lucky to be supported by rapidly growing, friendly and enthusiastic community. The [Laravel Forums](http://forums.laravel.com) are a great place to find help, make a suggestion, or just see what other people are saying. + +Many of us hang out every day in the #laravel IRC channel on FreeNode. [Here's a forum post explaining how you can join us.](http://forums.laravel.com/viewtopic.php?id=671) Hanging out in the IRC channel is a really great way to learn more about web-development using Laravel. You're welcome to ask questions, answer other people's questions, or just hang out and learn from other people's questions being answered. We love Laravel and would love to talk to you about it, so don't be a stranger! + + +## License Information + +Laravel is open-sourced software licensed under the [MIT License](http://www.opensource.org/licenses/mit-license.php). \ No newline at end of file diff --git a/app/laravel/documentation/input.md b/app/laravel/documentation/input.md new file mode 100644 index 000000000..f1429ac37 --- /dev/null +++ b/app/laravel/documentation/input.md @@ -0,0 +1,160 @@ +# Input & Cookies + +## Contents + +- [Input](#input) +- [JSON Input](#json) +- [Files](#files) +- [Old Input](#old-input) +- [Redirecting With Old Input](#redirecting-with-old-input) +- [Cookies](#cookies) +- [Merging & Replacing](#merge) + + +## Input + +The **Input** class handles input that comes into your application via GET, POST, PUT, or DELETE requests. Here are some examples of how to access input data using the Input class: + +#### Retrieve a value from the input array: + + $email = Input::get('email'); + +> **Note:** The "get" method is used for all request types (GET, POST, PUT, and DELETE), not just GET requests. + +#### Retrieve all input from the input array: + + $input = Input::get(); + +#### Retrieve all input including the $_FILES array: + + $input = Input::all(); + +By default, *null* will be returned if the input item does not exist. However, you may pass a different default value as a second parameter to the method: + +#### Returning a default value if the requested input item doesn't exist: + + $name = Input::get('name', 'Fred'); + +#### Using a Closure to return a default value: + + $name = Input::get('name', function() {return 'Fred';}); + +#### Determining if the input contains a given item: + + if (Input::has('name')) ... + +> **Note:** The "has" method will return *false* if the input item is an empty string. + + +## JSON Input + +When working with JavaScript MVC frameworks like Backbone.js, you will need to get the JSON posted by the application. To make your life easier, we've included the `Input::json` method: + +#### Get JSON input to the application: + + $data = Input::json(); + + +## Files + +#### Retrieving all items from the $_FILES array: + + $files = Input::file(); + +#### Retrieving an item from the $_FILES array: + + $picture = Input::file('picture'); + +#### Retrieving a specific item from a $_FILES array: + + $size = Input::file('picture.size'); + +> **Note:** In order to use file uploads, you must use `Form::open_for_files()` or manually enable `multipart/form-data`. + +*Further Reading:* + +- *[Opening Forms](/docs/views/forms#opening-a-form)* + + +## Old Input + +You'll commonly need to re-populate forms after invalid form submissions. Laravel's Input class was designed with this problem in mind. Here's an example of how you can easily retrieve the input from the previous request. First, you need to flash the input data to the session: + +#### Flashing input to the session: + + Input::flash(); + +#### Flashing selected input to the session: + + Input::flash('only', array('username', 'email')); + + Input::flash('except', array('password', 'credit_card')); + +#### Retrieving a flashed input item from the previous request: + + $name = Input::old('name'); + +> **Note:** You must specify a session driver before using the "old" method. + +*Further Reading:* + +- *[Sessions](/docs/session/config)* + + +## Redirecting With Old Input + +Now that you know how to flash input to the session. Here's a shortcut that you can use when redirecting that prevents you from having to micro-manage your old input in that way: + +#### Flashing input from a Redirect instance: + + return Redirect::to('login')->with_input(); + +#### Flashing selected input from a Redirect instance: + + return Redirect::to('login')->with_input('only', array('username')); + + return Redirect::to('login')->with_input('except', array('password')); + + +## Cookies + +Laravel provides a nice wrapper around the $_COOKIE array. However, there are a few things you should be aware of before using it. First, all Laravel cookies contain a "signature hash". This allows the framework to verify that the cookie has not been modified on the client. Secondly, when setting cookies, the cookies are not immediately sent to the browser, but are pooled until the end of the request and then sent together. This means that you will not be able to both set a cookie and retrieve the value that you set in the same request. + +#### Retrieving a cookie value: + + $name = Cookie::get('name'); + +#### Returning a default value if the requested cookie doesn't exist: + + $name = Cookie::get('name', 'Fred'); + +#### Setting a cookie that lasts for 60 minutes: + + Cookie::put('name', 'Fred', 60); + +#### Creating a "permanent" cookie that lasts five years: + + Cookie::forever('name', 'Fred'); + +#### Deleting a cookie: + + Cookie::forget('name'); + + +## Merging & Replacing + +Sometimes you may wish to merge or replace the current input. Here's how: + +#### Merging new data into the current input: + + Input::merge(array('name' => 'Spock')); + +#### Replacing the entire input array with new data: + + Input::replace(array('doctor' => 'Bones', 'captain' => 'Kirk')); + +## Clearing Input + +To clear all input data for the current request, you may use the `clear` method: + + Input::clear(); \ No newline at end of file diff --git a/app/laravel/documentation/install.md b/app/laravel/documentation/install.md new file mode 100644 index 000000000..1e051e069 --- /dev/null +++ b/app/laravel/documentation/install.md @@ -0,0 +1,124 @@ +# Installation & Setup + +## Contents + +- [Requirements](#requirements) +- [Installation](#installation) +- [Server Configuration](#server-configuration) +- [Basic Configuration](#basic-configuration) +- [Environments](#environments) +- [Cleaner URLs](#cleaner-urls) + + +## Requirements + +- Apache, nginx, or another compatible web server. +- Laravel takes advantage of the powerful features that have become available in PHP 5.3. Consequently, PHP 5.3 is a requirement. +- Laravel uses the [FileInfo library](http://php.net/manual/en/book.fileinfo.php) to detect files' mime-types. This is included by default with PHP 5.3. However, Windows users may need to add a line to their php.ini file before the Fileinfo module is enabled. For more information check out the [installation / configuration details on PHP.net](http://php.net/manual/en/fileinfo.installation.php). +- Laravel uses the [Mcrypt library](http://php.net/manual/en/book.mcrypt.php) for encryption and hash generation. Mcrypt typically comes pre-installed. If you can't find Mcrypt in the output of phpinfo() then check the vendor site of your LAMP installation or check out the [installation / configuration details on PHP.net](http://php.net/manual/en/book.mcrypt.php). + + +## Installation + +1. [Download Laravel](http://laravel.com/download) +2. Extract the Laravel archive and upload the contents to your web server. +3. Set the value of the **key** option in the **config/application.php** file to a random, 32 character string. +4. Verify that the `storage/views` directory is writable. +5. Navigate to your application in a web browser. + +If all is well, you should see a pretty Laravel splash page. Get ready, there is lots more to learn! + +### Extra Goodies + +Installing the following goodies will help you take full advantage of Laravel, but they are not required: + +- SQLite, MySQL, PostgreSQL, or SQL Server PDO drivers. +- Memcached or APC. + +### Problems? + +If you are having problems installing, try the following: + +- Make sure the **public** directory is the document root of your web server. (see: Server Configuration below) +- If you are using mod_rewrite, set the **index** option in **application/config/application.php** to an empty string. +- Verify that your storage folder and the folders within are writable by your web server. + + +## Server Configuration + +Like most web-development frameworks, Laravel is designed to protect your application code, bundles, and local storage by placing only files that are necessarily public in the web server's DocumentRoot. This prevents some types of server misconfiguration from making your code (including database passwords and other configuration data) accessible through the web server. It's best to be safe. + +In this example let's imagine that we installed Laravel to the directory **/Users/JonSnow/Sites/MySite**. + +A very basic example of an Apache VirtualHost configuration for MySite might look like this. + + + DocumentRoot /Users/JonSnow/Sites/MySite/public + ServerName mysite.dev + + +Notice that while we installed to **/Users/JonSnow/Sites/MySite** our DocumentRoot points to **/Users/JonSnow/Sites/MySite/public**. + +While pointing the DocumentRoot to the public folder is a commonly used best-practice, it's possible that you may need to use Laravel on a host that does not allow you to update your DocumentRoot. A collection of algorithms to circumvent this need can be found [on the Laravel forums.](http://forums.laravel.com/viewtopic.php?id=1258) + + +## Basic Configuration + +All of the configuration provided are located in your applications config/ directory. We recommend that you read through these files just to get a basic understanding of the options available to you. Pay special attention to the **application/config/application.php** file as it contains the basic configuration options for your application. + +It's **extremely** important that you change the **application key** option before working on your site. This key is used throughout the framework for encryption, hashing, etc. It lives in the **config/application.php** file and should be set to a random, 32 character string. A standards-compliant application key can be automatically generated using the Artisan command-line utility. More information can be found in the [Artisan command index](/docs/artisan/commands). + +> **Note:** If you are using mod_rewrite, you should set the index option to an empty string. + + +## Environments + +Most likely, the configuration options you need for local development are not the same as the options you need on your production server. Laravel's default environment handling mechanism is URL based, which will make setting up environments a breeze. Pop open the `paths.php` file in the root of your Laravel installation. You should see an array like this: + + $environments = array( + + 'local' => array('http://localhost*', '*.dev'), + + ); + +This tells Laravel that any URLs beginning with "localhost" or ending with ".dev" should be considered part of the "local" environment. + +Next, create an **application/config/local** directory. Any files and options you place in this directory will override the options in the base **application/config** directory. For example, you may wish to create an **application.php** file within your new **local** configuration directory: + + return array( + + 'url' => 'http://localhost/laravel/public', + + ); + +In this example, the local **URL** option will override the **URL** option in **application/config/application.php**. Notice that you only need to specify the options you wish to override. + +Isn't it easy? Of course, you are free to create as many environments as you wish! + + +## Cleaner URLs + +Most likely, you do not want your application URLs to contain "index.php". You can remove it using HTTP rewrite rules. If you are using Apache to serve your application, make sure to enable mod_rewrite and create a **.htaccess** file like this one in your **public** directory: + + + RewriteEngine on + + RewriteCond %{REQUEST_FILENAME} !-f + RewriteCond %{REQUEST_FILENAME} !-d + + RewriteRule ^(.*)$ index.php/$1 [L] + + +Is the .htaccess file above not working for you? Try this one: + + Options +FollowSymLinks + RewriteEngine on + + RewriteCond %{REQUEST_FILENAME} !-f + RewriteCond %{REQUEST_FILENAME} !-d + + RewriteRule . index.php [L] + +After setting up HTTP rewriting, you should set the **index** configuration option in **application/config/application.php** to an empty string. + +> **Note:** Each web server has a different method of doing HTTP rewrites, and may require a slightly different .htaccess file. \ No newline at end of file diff --git a/app/laravel/documentation/ioc.md b/app/laravel/documentation/ioc.md new file mode 100644 index 000000000..7df9572b8 --- /dev/null +++ b/app/laravel/documentation/ioc.md @@ -0,0 +1,49 @@ +# IoC Container + +- [Definition](/docs/ioc#definition) +- [Registering Objects](/docs/ioc#register) +- [Resolving Objects](/docs/ioc#resolve) + + +## Definition + +An IoC container is simply a way of managing the creation of objects. You can use it to define the creation of complex objects, allowing you to resolve them throughout your application using a single line of code. You may also use it to "inject" dependencies into your classes and controllers. + +IoC containers help make your application more flexible and testable. Since you may register alternate implementations of an interface with the container, you may isolate the code you are testing from external dependencies using [stubs and mocks](http://martinfowler.com/articles/mocksArentStubs.html). + + +## Registering Objects + +#### Registering a resolver in the IoC container: + + IoC::register('mailer', function() + { + $transport = Swift_MailTransport::newInstance(); + + return Swift_Mailer::newInstance($transport); + }); + + +Great! Now we have registered a resolver for SwiftMailer in our container. But, what if we don't want the container to create a new mailer instance every time we need one? Maybe we just want the container to return the same instance after the initial instance is created. Just tell the container the object should be a singleton: + +#### Registering a singleton in the container: + + IoC::singleton('mailer', function() + { + // + }); + +You may also register an existing object instance as a singleton in the container. + +#### Registering an existing instance in the container: + + IoC::instance('mailer', $instance); + + +## Resolving Objects + +Now that we have SwiftMailer registered in the container, we can resolve it using the **resolve** method on the **IoC** class: + + $mailer = IoC::resolve('mailer'); + +> **Note:** You may also [register controllers in the container](/docs/controllers#dependency-injection). \ No newline at end of file diff --git a/app/laravel/documentation/loading.md b/app/laravel/documentation/loading.md new file mode 100644 index 000000000..72919d467 --- /dev/null +++ b/app/laravel/documentation/loading.md @@ -0,0 +1,58 @@ +# Class Auto Loading + +## Contents + +- [The Basics](#the-basics) +- [Registering Directories](#directories) +- [Registering Mappings](#mappings) +- [Registering Namespaces](#namespaces) + + +## The Basics + +Auto-loading allows you to lazily load class files when they are needed without explicitly *requiring* or *including* them. So, only the classes you actually need are loaded for any given request to your application, and you can just jump right in and start using any class without loading it's related file. + +By default, the **models** and **libraries** directories are registered with the auto-loader in the **application/start.php** file. The loader uses a class to file name loading convention, where all file names are lower-cased. So for instance, a "User" class within the models directory should have a file name of "user.php". You may also nest classes within sub-directories. Just namespace the classes to match the directory structure. So, a "Entities\User" class would have a file name of "entities/user.php" within the models directory. + + +## Registering Directories + +As noted above, the models and libraries directories are registered with the auto-loader by default; however, you may register any directories you like to use the same class to file name loading conventions: + +#### Registering directories with the auto-loader: + + Autoloader::directories(array( + path('app').'entities', + path('app').'repositories', + )); + + +## Registering Mappings + +Sometimes you may wish to manually map a class to its related file. This is the most performant way of loading classes: + +#### Registering a class to file mapping with the auto-loader: + + Autoloader::map(array( + 'User' => path('app').'models/user.php', + 'Contact' => path('app').'models/contact.php', + )); + + +## Registering Namespaces + +Many third-party libraries use the PSR-0 standard for their structure. PSR-0 states that class names should match their file names, and directory structure is indicated by namespaces. If you are using a PSR-0 library, just register it's root namespace and directory with the auto-loader: + +#### Registering a namespace with the auto-loader: + + Autoloader::namespaces(array( + 'Doctrine' => path('libraries').'Doctrine', + )); + +Before namespaces were available in PHP, many projects used underscores to indicate directory structure. If you are using one of these legacy libraries, you can still easily register it with the auto-loader. For example, if you are using SwiftMailer, you may have noticed all classes begin with "Swift_". So, we'll register "Swift" with the auto-loader as the root of an underscored project. + +#### Registering an "underscored" library with the auto-loader: + + Autoloader::underscored(array( + 'Swift' => path('libraries').'SwiftMailer', + )); \ No newline at end of file diff --git a/app/laravel/documentation/localization.md b/app/laravel/documentation/localization.md new file mode 100644 index 000000000..cbf4a463f --- /dev/null +++ b/app/laravel/documentation/localization.md @@ -0,0 +1,70 @@ +# Localization + +## Contents + +- [The Basics](#the-basics) +- [Retrieving A Language Line](#get) +- [Place Holders & Replacements](#replace) + + +## The Basics + +Localization is the process of translating your application into different languages. The **Lang** class provides a simple mechanism to help you organize and retrieve the text of your multilingual application. + +All of the language files for your application live under the **application/language** directory. Within the **application/language** directory, you should create a directory for each language your application speaks. So, for example, if your application speaks English and Spanish, you might create **en** and **sp** directories under the **language** directory. + +Each language directory may contain many different language files. Each language file is simply an array of string values in that language. In fact, language files are structured identically to configuration files. For example, within the **application/language/en** directory, you could create a **marketing.php** file that looks like this: + +#### Creating a language file: + + return array( + + 'welcome' => 'Welcome to our website!', + + ); + +Next, you should create a corresponding **marketing.php** file within the **application/language/sp** directory. The file would look something like this: + + return array( + + 'welcome' => 'Bienvenido a nuestro sitio web!', + + ); + +Nice! Now you know how to get started setting up your language files and directories. Let's keep localizing! + + +## Retrieving A Language Line + +#### Retrieving a language line: + + echo Lang::line('marketing.welcome')->get(); + +#### Retrieving a language line using the "__" helper: + + echo __('marketing.welcome'); + +Notice how a dot was used to separate "marketing" and "welcome"? The text before the dot corresponds to the language file, while the text after the dot corresponds to a specific string within that file. + +Need to retrieve the line in a language other than your default? Not a problem. Just mention the language to the **get** method: + +#### Getting a language line in a given language: + + echo Lang::line('marketing.welcome')->get('sp'); + + +## Place Holders & Replacements + +Now, let's work on our welcome message. "Welcome to our website!" is a pretty generic message. It would be helpful to be able to specify the name of the person we are welcoming. But, creating a language line for each user of our application would be time-consuming and ridiculous. Thankfully, you don't have to. You can specify "place-holders" within your language lines. Place-holders are preceded by a colon: + +#### Creating a language line with place-holders: + + 'welcome' => 'Welcome to our website, :name!' + +#### Retrieving a language line with replacements: + + echo Lang::line('marketing.welcome', array('name' => 'Taylor'))->get(); + +#### Retrieving a language line with replacements using "__": + + echo __('marketing.welcome', array('name' => 'Taylor')); \ No newline at end of file diff --git a/app/laravel/documentation/logging.md b/app/laravel/documentation/logging.md new file mode 100644 index 000000000..74678c657 --- /dev/null +++ b/app/laravel/documentation/logging.md @@ -0,0 +1,40 @@ +# Errors & Logging + +## Contents + +- [Basic Configuration](#basic-configuration) +- [Logging](#logging) +- [The Logger Class](#the-logger-class) + + +## Basic Configuration + +All of the configuration options regarding errors and logging live in the **application/config/errors.php** file. Let's jump right in. + +### Ignored Errors + +The **ignore** option contains an array of error levels that should be ignored by Laravel. By "ignored", we mean that we won't stop execution of the script on these errors. However, they will be logged when logging is enabled. + +### Error Detail + +The **detail** option indicates if the framework should display the error message and stack trace when an error occurs. For development, you will want this to be **true**. However, in a production environment, set this to **false**. When disabled, the view located in **application/views/error/500.php** will be displayed, which contains a generic error message. + + +## Logging + +To enable logging, set the **log** option in the error configuration to "true". When enabled, the Closure defined by the **logger** configuration item will be executed when an error occurs. This gives you total flexibility in how the error should be logged. You can even e-mail the errors to your development team! + +By default, logs are stored in the **storage/logs** directory, and a new log file is created for each day. This keeps your log files from getting crowded with too many messages. + + +## The Logger Class + +Sometimes you may wish to use Laravel's **Log** class for debugging, or just to log informational messages. Here's how to use it: + +#### Writing a message to the logs: + + Log::write('info', 'This is just an informational message!'); + +#### Using magic methods to specify the log message type: + + Log::info('This is just an informational message!'); \ No newline at end of file diff --git a/app/laravel/documentation/models.md b/app/laravel/documentation/models.md new file mode 100644 index 000000000..a1f4620c8 --- /dev/null +++ b/app/laravel/documentation/models.md @@ -0,0 +1,116 @@ +# Models & Libraries + +## Contents + +- [Models](#models) +- [Libraries](#libraries) +- [Auto-Loading](#auto-loading) +- [Best Practices](#best-practices) + + +## Models + +Models are the heart of your application. Your application logic (controllers / routes) and views (html) are just the mediums with which users interact with your models. The most typical type of logic contained within a model is [Business Logic](http://en.wikipedia.org/wiki/Business_logic). + +*Some examples of functionality that would exist within a model are:* + +- Database Interactions +- File I/O +- Interactions with Web Services + +For instance, perhaps you are writing a blog. You will likely want to have a "Post" model. Users may want to comment on posts so you'd also have a "Comment" model. If users are going to be commenting then we'll also need a "User" model. Get the idea? + + +## Libraries + +Libraries are classes that perform tasks that aren't specific to your application. For instance, consider a PDF generation library that converts HTML. That task, although complicated, is not specific to your application, so it is considered a "library". + +Creating a library is as easy as creating a class and storing it in the libraries folder. In the following example, we will create a simple library with a method that echos the text that is passed to it. We create the **printer.php** file in the libraries folder with the following code. + + +## Auto Loading + +Libraries and Models are very easy to use thanks to the Laravel auto-loader. To learn more about the auto-loader check out the documentation on [Auto-Loading](/docs/loading). + + +## Best Practices + +We've all head the mantra: "controllers should be thin!" But, how do we apply that in real life? It's possible that part of the problem is the word "model". What does it even mean? Is it even a useful term? Many associate "model" with "database", which leads to having very bloated controllers, with light models that access the database. Let's explore some alternatives. + +What if we just totally scrapped the "models" directory? Let's name it something more useful. In fact, let's just give it the same as our application. Perhaps are our satellite tracking site is named "Trackler", so let's create a "trackler" directory within the application folder. + +Great! Next, let's break our classes into "entities", "services", and "repositories". So, we'll create each of those three directories within our "trackler" folder. Let's explore each one: + +### Entities + +Think of entities as the data containers of your application. They primarily just contain properties. So, in our application, we may have a "Location" entity which has "latitude" and "longitude" properties. It could look something like this: + + latitude = $latitude; + $this->longitude = $longitude; + } + + } + +Looking good. Now that we have an entity, let's explore our other two folders. + +### Services + +Services contain the *processes* of your application. So, let's keep using our Trackler example. Our application might have a form on which a user may enter their GPS location. However, we need to validate that the coordinates are correctly formatted. We need to *validate* the *location entity*. So, within our "services" directory, we could create a "validators" folder with the following class: + + +## Working With The URI + +#### Getting the current URI for the request: + + echo URI::current(); + +#### Getting a specific segment from the URI: + + echo URI::segment(1); + +#### Returning a default value if the segment doesn't exist: + + echo URI::segment(10, 'Foo'); + +#### Getting the full request URI, including query string: + + echo URI::full(); + +Sometimes you may need to determine if the current URI is a given string, or begins with a given string. Here's an example of how you can use the is() method to accomplish this: + +#### Determine if the URI is "home": + + if (URI::is('home')) + { + // The current URI is "home"! + } + +#### Determine if the current URI begins with "docs/": + + if URI::is('docs/*')) + { + // The current URI begins with "docs/"! + } + + +## Other Request Helpers + +#### Getting the current request method: + + echo Request::method(); + +#### Accessing the $_SERVER global array: + + echo Request::server('http_referer'); + +#### Retrieving the requester's IP address: + + echo Request::ip(); + +#### Determining if the current request is using HTTPS: + + if (Request::secure()) + { + // This request is over HTTPS! + } + +#### Determining if the current request is an AJAX request: + + if (Request::ajax()) + { + // This request is using AJAX! + } + +#### Determining if the current requst is via the Artisan CLI: + + if (Request::cli()) + { + // This request came from the CLI! + } \ No newline at end of file diff --git a/app/laravel/documentation/routing.md b/app/laravel/documentation/routing.md new file mode 100644 index 000000000..38ad80755 --- /dev/null +++ b/app/laravel/documentation/routing.md @@ -0,0 +1,339 @@ +# Routing + +## Contents + +- [The Basics](#the-basics) +- [Wildcards](#wildcards) +- [The 404 Event](#the-404-event) +- [Filters](#filters) +- [Pattern Filters](#pattern-filters) +- [Global Filters](#global-filters) +- [Route Groups](#route-groups) +- [Named Routes](#named-routes) +- [HTTPS Routes](#https-routes) +- [Bundle Routes](#bundle-routes) +- [Controller Routing](#controller-routing) +- [CLI Route Testing](#cli-route-testing) + + +## The Basics + +Laravel uses the latest features of PHP 5.3 to make routing simple and expressive. It's important that building everything from APIs to complex web applications is as easy as possible. Routes are typically defined in **application/routes.php**. + +Unlike many other frameworks with Laravel it's possible to embed application logic in two ways. While controllers are the most common way to implement application logic it's also possible to embed your logic directly into routes. This is **especially** nice for small sites that contain only a few pages as you don't have to create a bunch of controllers just to expose half a dozen methods or put a handful of unrelated methods into the same controller and then have to manually designate routes that point to them. + +In the following example the first parameter is the route that you're "registering" with the router. The second parameter is the function containing the logic for that route. Routes are defined without a front-slash. The only exception to this is the default route which is represented with **only** a front-slash. + +> **Note:** Routes are evaluated in the order that they are registered, so register any "catch-all" routes at the bottom of your **routes.php** file. + +#### Registering a route that responds to "GET /": + + Route::get('/', function() + { + return "Hello World!"; + }); + +#### Registering a route that is valid for any HTTP verb (GET, POST, PUT, and DELETE): + + Route::any('/', function() + { + return "Hello World!"; + }); + +#### Registering routes for other request methods: + + Route::post('user', function() + { + // + }); + + Route::put('user/(:num)', function($id) + { + // + }); + + Route::delete('user/(:num)', function($id) + { + // + }); + +**Registering a single URI for multiple HTTP verbs:** + + Router::register(array('GET', 'POST'), $uri, $callback); + + +## Wildcards + +#### Forcing a URI segment to be any digit: + + Route::get('user/(:num)', function($id) + { + // + }); + +#### Allowing a URI segment to be any alpha-numeric string: + + Route::get('post/(:any)', function($title) + { + // + }); + +#### Catching the remaining URI without limitations: + + Route::get('files/(:all)', function($path) + { + // + }); + +#### Allowing a URI segment to be optional: + + Route::get('page/(:any?)', function($page = 'index') + { + // + }); + + +## The 404 Event + +If a request enters your application but does not match any existing route, the 404 event will be raised. You can find the default event handler in your **application/routes.php** file. + +#### The default 404 event handler: + + Event::listen('404', function() + { + return Response::error('404'); + }); + +You are free to change this to fit the needs of your application! + +*Further Reading:* + +- *[Events](/docs/events)* + + +## Filters + +Route filters may be run before or after a route is executed. If a "before" filter returns a value, that value is considered the response to the request and the route is not executed, which is convenient when implementing authentication filters, etc. Filters are typically defined in **application/routes.php**. + +#### Registering a filter: + + Route::filter('filter', function() + { + return Redirect::to('home'); + }); + +#### Attaching a filter to a route: + + Route::get('blocked', array('before' => 'filter', function() + { + return View::make('blocked'); + })); + +#### Attaching an "after" filter to a route: + + Route::get('download', array('after' => 'log', function() + { + // + })); + +#### Attaching multiple filters to a route: + + Route::get('create', array('before' => 'auth|csrf', function() + { + // + })); + +#### Passing parameters to filters: + + Route::get('panel', array('before' => 'role:admin', function() + { + // + })); + + +## Pattern Filters + +Sometimes you may want to attach a filter to all requests that begin with a given URI. For example, you may want to attach the "auth" filter to all requests with URIs that begin with "admin". Here's how to do it: + +#### Defining a URI pattern based filter: + + Route::filter('pattern: admin/*', 'auth'); + +Optionally you can register filters directly when attaching filters to a given URI by supplying an array with the name of the filter and a callback. + +#### Defining a filter and URI pattern based filter in one: + + Route::filter('pattern: admin/*', array('name' => 'auth', function() + { + // + })); + + +## Global Filters + +Laravel has two "global" filters that run **before** and **after** every request to your application. You can find them both in the **application/routes.php** file. These filters make great places to start common bundles or add global assets. + +> **Note:** The **after** filter receives the **Response** object for the current request. + + +## Route Groups + +Route groups allow you to attach a set of attributes to a group of routes, allowing you to keep your code neat and tidy. + + Route::group(array('before' => 'auth'), function() + { + Route::get('panel', function() + { + // + }); + + Route::get('dashboard', function() + { + // + }); + }); + + +## Named Routes + +Constantly generating URLs or redirects using a route's URI can cause problems when routes are later changed. Assigning the route a name gives you a convenient way to refer to the route throughout your application. When a route change occurs the generated links will point to the new route with no further configuration needed. + +#### Registering a named route: + + Route::get('/', array('as' => 'home', function() + { + return "Hello World"; + })); + +#### Generating a URL to a named route: + + $url = URL::to_route('home'); + +#### Redirecting to the named route: + + return Redirect::to_route('home'); + +Once you have named a route, you may easily check if the route handling the current request has a given name. + +#### Determine if the route handling the request has a given name: + + if (Request::route()->is('home')) + { + // The "home" route is handling the request! + } + + +## HTTPS Routes + +When defining routes, you may use the "https" attribute to indicate that the HTTPS protocol should be used when generating a URL or Redirect to that route. + +#### Defining an HTTPS route: + + Route::get('login', array('https' => true, function() + { + return View::make('login'); + })); + +#### Using the "secure" short-cut method: + + Route::secure('GET', 'login', function() + { + return View::make('login'); + }); + + +## Bundle Routes + +Bundles are Laravel's modular package system. Bundles can easily be configured to handle requests to your application. We'll be going over [bundles in more detail](/docs/bundles) in another document. For now, read through this section and just be aware that not only can routes be used to expose functionality in bundles, but they can also be registered from within bundles. + +Let's open the **application/bundles.php** file and add something: + +#### Registering a bundle to handle routes: + + return array( + + 'admin' => array('handles' => 'admin'), + + ); + +Notice the new **handles** option in our bundle configuration array? This tells Laravel to load the Admin bundle on any requests where the URI begins with "admin". + +Now you're ready to register some routes for your bundle, so create a **routes.php** file within the root directory of your bundle and add the following: + +#### Registering a root route for a bundle: + + Route::get('(:bundle)', function() + { + return 'Welcome to the Admin bundle!'; + }); + +Let's explore this example. Notice the **(:bundle)** place-holder? That will be replaced with the value of the **handles** clause that you used to register your bundle. This keeps your code [D.R.Y.](http://en.wikipedia.org/wiki/Don't_repeat_yourself) and allows those who use your bundle to change it's root URI without breaking your routes! Nice, right? + +Of course, you can use the **(:bundle)** place-holder for all of your routes, not just your root route. + +#### Registering bundle routes: + + Route::get('(:bundle)/panel', function() + { + return "I handle requests to admin/panel!"; + }); + + +## Controller Routing + +Controllers provide another way to manage your application logic. If you're unfamiliar with controllers you may want to [read about controllers](/docs/controllers) and return to this section. + +It is important to be aware that all routes in Laravel must be explicitly defined, including routes to controllers. This means that controller methods that have not been exposed through route registration **cannot** be accessed. It's possible to automatically expose all methods within a controller using controller route registration. Controller route registrations are typically defined in **application/routes.php**. + +Most likely, you just want to register all of the controllers in your application's "controllers" directory. You can do it in one simple statement. Here's how: + +#### Register all controllers for the application: + + Route::controller(Controller::detect()); + +The **Controller::detect** method simply returns an array of all of the controllers defined for the application. + +If you wish to automatically detect the controllers in a bundle, just pass the bundle name to the method. If no bundle is specified, the application folder's controller directory will be searched. + +> **Note:** It is important to note that this method gives you no control over the order in which controllers are loaded. Controller::detect() should only be used to Route controllers in very small sites. "Manually" routing controllers gives you much more control, is more self-documenting, and is certainly advised. + +#### Register all controllers for the "admin" bundle: + + Route::controller(Controller::detect('admin')); + +#### Registering the "home" controller with the Router: + + Route::controller('home'); + +#### Registering several controllers with the router: + + Route::controller(array('dashboard.panel', 'admin')); + +Once a controller is registered, you may access its methods using a simple URI convention: + + http://localhost/controller/method/arguments + +This convention is similar to that employed by CodeIgniter and other popular frameworks, where the first segment is the controller name, the second is the method, and the remaining segments are passed to the method as arguments. If no method segment is present, the "index" method will be used. + +This routing convention may not be desirable for every situation, so you may also explicitly route URIs to controller actions using a simple, intuitive syntax. + +#### Registering a route that points to a controller action: + + Route::get('welcome', 'home@index'); + +#### Registering a filtered route that points to a controller action: + + Route::get('welcome', array('after' => 'log', 'uses' => 'home@index')); + +#### Registering a named route that points to a controller action: + + Route::get('welcome', array('as' => 'home.welcome', 'uses' => 'home@index')); + + +## CLI Route Testing + +You may test your routes using Laravel's "Artisan" CLI. Simple specify the request method and URI you want to use. The route response will be var_dump'd back to the CLI. + +#### Calling a route via the Artisan CLI: + + php artisan route:call get api/user/1 \ No newline at end of file diff --git a/app/laravel/documentation/session/config.md b/app/laravel/documentation/session/config.md new file mode 100644 index 000000000..14821320c --- /dev/null +++ b/app/laravel/documentation/session/config.md @@ -0,0 +1,108 @@ +# Session Configuration + +## Contents + +- [The Basics](#the-basics) +- [Cookie Sessions](#cookie) +- [File System Sessions](#file) +- [Database Sessions](#database) +- [Memcached Sessions](#memcached) +- [Redis Sessions](#redis) +- [In-Memory Sessions](#memory) + + +## The Basics + +The web is a stateless environment. This means that each request to your application is considered unrelated to any previous request. However, **sessions** allow you to store arbitrary data for each visitor to your application. The session data for each visitor is stored on your web server, while a cookie containing a **session ID** is stored on the visitor's machine. This cookie allows your application to "remember" the session for that user and retrieve their session data on subsequent requests to your application. + +> **Note:** Before using sessions, make sure an application key has been specified in the **application/config/application.php** file. + +Six session drivers are available out of the box: + +- Cookie +- File System +- Database +- Memcached +- Redis +- Memory (Arrays) + + +## Cookie Sessions + +Cookie based sessions provide a light-weight and fast mechanism for storing session information. They are also secure. Each cookie is encrypted using strong AES-256 encryption. However, cookies have a four kilobyte storage limit, so you may wish to use another driver if you are storing a lot of data in the session. + +To get started using cookie sessions, just set the driver option in the **application/config/session.php** file: + + 'driver' => 'cookie' + + +## File System Sessions + +Most likely, your application will work great using file system sessions. However, if your application receives heavy traffic or runs on a server farm, use database or Memcached sessions. + +To get started using file system sessions, just set the driver option in the **application/config/session.php** file: + + 'driver' => 'file' + +That's it. You're ready to go! + +> **Note:** File system sessions are stored in the **storage/sessions** directory, so make sure it's writeable. + + +## Database Sessions + +To start using database sessions, you will first need to [configure your database connection](/docs/database/config). + +Next, you will need to create a session table. Below are some SQL statements to help you get started. However, you may also use Laravel's "Artisan" command-line to generate the table for you! + +### Artisan + + php artisan session:table + +### SQLite + + CREATE TABLE "sessions" ( + "id" VARCHAR PRIMARY KEY NOT NULL UNIQUE, + "last_activity" INTEGER NOT NULL, + "data" TEXT NOT NULL + ); + +### MySQL + + CREATE TABLE `sessions` ( + `id` VARCHAR(40) NOT NULL, + `last_activity` INT(10) NOT NULL, + `data` TEXT NOT NULL, + PRIMARY KEY (`id`) + ); + +If you would like to use a different table name, simply change the **table** option in the **application/config/session.php** file: + + 'table' => 'sessions' + +All you need to do now is set the driver in the **application/config/session.php** file: + + 'driver' => 'database' + + +## Memcached Sessions + +Before using Memcached sessions, you must [configure your Memcached servers](/docs/database/config#memcached). + +Just set the driver in the **application/config/session.php** file: + + 'driver' => 'memcached' + + +## Redis Sessions + +Before using Redis sessions, you must [configure your Redis servers](/docs/database/redis#config). + +Just set the driver in the **application/config/session.php** file: + + 'driver' => 'redis' + + +## In-Memory Sessions + +The "memory" session driver just uses a simple array to store your session data for the current request. This driver is perfect for unit testing your application since nothing is written to disk. It shouldn't ever be used as a "real" session driver. \ No newline at end of file diff --git a/app/laravel/documentation/session/usage.md b/app/laravel/documentation/session/usage.md new file mode 100644 index 000000000..1b5c9d5c7 --- /dev/null +++ b/app/laravel/documentation/session/usage.md @@ -0,0 +1,79 @@ +# Session Usage + +## Contents + +- [Storing Items](#put) +- [Retrieving Items](#get) +- [Removing Items](#forget) +- [Flashing Items](#flash) +- [Regeneration](#regeneration) + + +## Storing Items + +To store items in the session call the put method on the Session class: + + Session::put('name', 'Taylor'); + +The first parameter is the **key** to the session item. You will use this key to retrieve the item from the session. The second parameter is the **value** of the item. + + +## Retrieving Items + +You can use the **get** method on the Session class to retrieve any item in the session, including flash data. Just pass the key of the item you wish to retrieve: + + $name = Session::get('name'); + +By default, NULL will be returned if the session item does not exist. However, you may pass a default value as a second parameter to the get method: + + $name = Session::get('name', 'Fred'); + + $name = Session::get('name', function() {return 'Fred';}); + +Now, "Fred" will be returned if the "name" item does not exist in the session. + +Laravel even provides a simple way to determine if a session item exists using the **has** method: + + if (Session::has('name')) + { + $name = Session::get('name'); + } + + +## Removing Items + +To remove an item from the session use the **forget** method on the Session class: + + Session::forget('name'); + +You can even remove all of the items from the session using the **flush** method: + + Session::flush(); + + +## Flashing Items + +The **flash** method stores an item in the session that will expire after the next request. It's useful for storing temporary data like status or error messages: + + Session::flash('status', 'Welcome Back!'); + +Flash items that are expiring in subsequent requests can be retained for another request by using one of the **reflash** or **keep** methods: + +Retain all items for another request: + + Session::reflash(); + +Retain an individual item for another request: + + Session::keep('status'); + +Retain several items for another request: + + Session::keep(array('status', 'other_item')); + + +## Regeneration + +Sometimes you may want to "regenerate" the session ID. This simply means that a new, random session ID will be assigned to the session. Here's how to do it: + + Session::regenerate(); \ No newline at end of file diff --git a/app/laravel/documentation/strings.md b/app/laravel/documentation/strings.md new file mode 100644 index 000000000..4164e5eca --- /dev/null +++ b/app/laravel/documentation/strings.md @@ -0,0 +1,72 @@ +# Working With Strings + +## Contents + +- [Capitalization, Etc.](#capitalization) +- [Word & Character Limiting](#limits) +- [Generating Random Strings](#random) +- [Singular & Plural](#singular-and-plural) +- [Slugs](#slugs) + + +## Capitalization, Etc. + +The **Str** class also provides three convenient methods for manipulating string capitalization: **upper**, **lower**, and **title**. These are more intelligent versions of the PHP [strtoupper](http://php.net/manual/en/function.strtoupper.php), [strtolower](http://php.net/manual/en/function.strtolower.php), and [ucwords](http://php.net/manual/en/function.ucwords.php) methods. More intelligent because they can handle UTF-8 input if the [multi-byte string](http://php.net/manual/en/book.mbstring.php) PHP extension is installed on your web server. To use them, just pass a string to the method: + + echo Str::lower('I am a string.'); + + echo Str::upper('I am a string.'); + + echo Str::title('I am a string.'); + + +## Word & Character Limiting + +#### Limiting the number of characters in a string: + + echo Str::limit($string, 10); + echo Str::limit_exact($string, 10); + +#### Limiting the number of words in a string: + + echo Str::words($string, 10); + + +## Generating Random Strings + +#### Generating a random string of alpha-numeric characters: + + echo Str::random(32); + +#### Generating a random string of alphabetic characters: + + echo Str::random(32, 'alpha'); + + +## Singular & Plural + +The String class is capable of transforming your strings from singular to plural, and vice versa. + +#### Getting the plural form of a word: + + echo Str::plural('user'); + +#### Getting the singular form of a word: + + echo Str::singular('users'); + +#### Getting the plural form if given value is greater than one: + + echo Str::plural('comment', count($comments)); + + +## Slugs + +#### Generating a URL friendly slug: + + return Str::slug('My First Blog Post!'); + +#### Generating a URL friendly slug using a given separator: + + return Str::slug('My First Blog Post!', '_'); + diff --git a/app/laravel/documentation/testing.md b/app/laravel/documentation/testing.md new file mode 100644 index 000000000..a7d958c04 --- /dev/null +++ b/app/laravel/documentation/testing.md @@ -0,0 +1,68 @@ +# Unit Testing + +## Contents + +- [The Basics](#the-basics) +- [Creating Test Classes](#creating-test-classes) +- [Running Tests](#running-tests) +- [Calling Controllers From Tests](#calling-controllers-from-tests) + + +## The Basics + +Unit Testing allows you to test your code and verify that it is working correctly. In fact, many advocate that you should even write your tests before you write your code! Laravel provides beautiful integration with the popular [PHPUnit](http://www.phpunit.de/manual/current/en/) testing library, making it easy to get started writing your tests. In fact, the Laravel framework itself has hundreds of unit tests! + + +## Creating Test Classes + +All of your application's tests live in the **application/tests** directory. In this directory, you will find a basic **example.test.php** file. Pop it open and look at the class it contains: + + assertTrue(true); + } + + } + +Take special note of the **.test.php** file suffix. This tells Laravel that it should include this class as a test case when running your test. Any files in the test directory that are not named with this suffix will not be considered a test case. + +If you are writing tests for a bundle, just place them in a **tests** directory within the bundle. Laravel will take care of the rest! + +For more information regarding creating test cases, check out the [PHPUnit documentation](http://www.phpunit.de/manual/current/en/). + + +## Running Tests + +To run your tests, you can use Laravel's Artisan command-line utility: + +#### Running the application's tests via the Artisan CLI: + + php artisan test + +#### Running the unit tests for a bundle: + + php artisan test bundle-name + + +## Calling Controllers From Tests + +Here's an example of how you can call your controllers from your tests: + +#### Calling a controller from a test: + + $response = Controller::call('home@index', $parameters); + +#### Resolving an instance of a controller from a test: + + $controller = Controller::resolve('application', 'home@index'); + +> **Note:** The controller's action filters will still run when using Controller::call to execute controller actions. \ No newline at end of file diff --git a/app/laravel/documentation/urls.md b/app/laravel/documentation/urls.md new file mode 100644 index 000000000..4263712f8 --- /dev/null +++ b/app/laravel/documentation/urls.md @@ -0,0 +1,98 @@ +# Generating URLs + +## Contents + +- [The Basics](#the-basics) +- [URLs To Routes](#urls-to-routes) +- [URLs To Controller Actions](#urls-to-controller-actions) +- [URLs To Assets](#urls-to-assets) +- [URL Helpers](#url-helpers) + + +## The Basics + +#### Retrieving the application's base URL: + + $url = URL::base(); + +#### Generating a URL relative to the base URL: + + $url = URL::to('user/profile'); + +#### Generating a HTTPS URL: + + $url = URL::to_secure('user/login'); + +#### Retrieving the current URL: + + $url = URL::current(); + +#### Retrieving the current URL including query string: + + $url = URL::full(); + + +## URLs To Routes + +#### Generating a URL to a named route: + + $url = URL::to_route('profile'); + +Sometimes you may need to generate a URL to a named route, but also need to specify the values that should be used instead of the route's URI wildcards. It's easy to replace the wildcards with proper values: + +#### Generating a URL to a named route with wildcard values: + + $url = URL::to_route('profile', array($username)); + +*Further Reading:* + +- [Named Routes](/docs/routing#named-routes) + + +## URLs To Controller Actions + +#### Generating a URL to a controller action: + + $url = URL::to_action('user@profile'); + +#### Generating a URL to an action with wildcard values: + + $url = URL::to_action('user@profile', array($username)); + + +## URLs To Assets + +URLs generated for assets will not contain the "application.index" configuration option. + +#### Generating a URL to an asset: + + $url = URL::to_asset('js/jquery.js'); + + +## URL Helpers + +There are several global functions for generating URLs designed to make your life easier and your code cleaner: + +#### Generating a URL relative to the base URL: + + $url = url('user/profile'); + +#### Generating a URL to an asset: + + $url = asset('js/jquery.js'); + +#### Generating a URL to a named route: + + $url = route('profile'); + +#### Generating a URL to a named route with wildcard values: + + $url = route('profile', array($username)); + +#### Generating a URL to a controller action: + + $url = action('user@profile'); + +#### Generating a URL to an action with wildcard values: + + $url = action('user@profile', array($username)); \ No newline at end of file diff --git a/app/laravel/documentation/validation.md b/app/laravel/documentation/validation.md new file mode 100644 index 000000000..eacb66251 --- /dev/null +++ b/app/laravel/documentation/validation.md @@ -0,0 +1,453 @@ +# Validation + +## Contents + +- [The Basics](#the-basics) +- [Validation Rules](#validation-rules) +- [Retrieving Error Message](#retrieving-error-messages) +- [Validation Walkthrough](#validation-walkthrough) +- [Custom Error Messages](#custom-error-messages) +- [Custom Validation Rules](#custom-validation-rules) + + +## The Basics + +Almost every interactive web application needs to validate data. For instance, a registration form probably requires the password to be confirmed. Maybe the e-mail address must be unique. Validating data can be a cumbersome process. Thankfully, it isn't in Laravel. The Validator class provides an awesome array of validation helpers to make validating your data a breeze. Let's walk through an example: + +#### Get an array of data you want to validate: + + $input = Input::all(); + +#### Define the validation rules for your data: + + $rules = array( + 'name' => 'required|max:50', + 'email' => 'required|email|unique:users', + ); + +#### Create a Validator instance and validate the data: + + $validation = Validator::make($input, $rules); + + if ($validation->fails()) + { + return $validation->errors; + } + +With the *errors* property, you can access a simple message collector class that makes working with your error messages a piece of cake. Of course, default error messages have been setup for all validation rules. The default messages live at **language/en/validation.php**. + +Now you are familiar with the basic usage of the Validator class. You're ready to dig in and learn about the rules you can use to validate your data! + + +## Validation Rules + +- [Required](#rule-required) +- [Alpha, Alpha Numeric, & Alpha Dash](#rule-alpha) +- [Size](#rule-size) +- [Numeric](#rule-numeric) +- [Inclusion & Exclusion](#rule-in) +- [Confirmation](#rule-confirmation) +- [Acceptance](#rule-acceptance) +- [Same & Different](#same-and-different) +- [Regular Expression Match](#regex-match) +- [Uniqueness & Existence](#rule-unique) +- [Dates](#dates) +- [E-Mail Addresses](#rule-email) +- [URLs](#rule-url) +- [Uploads](#rule-uploads) + + +### Required + +#### Validate that an attribute is present and is not an empty string: + + 'name' => 'required' + +#### Validate that an attribute is present, when another attribute is present: + 'last_name' => 'required_with:first_name' + + +### Alpha, Alpha Numeric, & Alpha Dash + +#### Validate that an attribute consists solely of letters: + + 'name' => 'alpha' + +#### Validate that an attribute consists of letters and numbers: + + 'username' => 'alpha_num' + +#### Validate that an attribute only contains letters, numbers, dashes, or underscores: + + 'username' => 'alpha_dash' + + +### Size + +#### Validate that an attribute is a given length, or, if an attribute is numeric, is a given value: + + 'name' => 'size:10' + +#### Validate that an attribute size is within a given range: + + 'payment' => 'between:10,50' + +> **Note:** All minimum and maximum checks are inclusive. + +#### Validate that an attribute is at least a given size: + + 'payment' => 'min:10' + +#### Validate that an attribute is no greater than a given size: + + 'payment' => 'max:50' + + +### Numeric + +#### Validate that an attribute is numeric: + + 'payment' => 'numeric' + +#### Validate that an attribute is an integer: + + 'payment' => 'integer' + + +### Inclusion & Exclusion + +#### Validate that an attribute is contained in a list of values: + + 'size' => 'in:small,medium,large' + +#### Validate that an attribute is not contained in a list of values: + + 'language' => 'not_in:cobol,assembler' + + +### Confirmation + +The *confirmed* rule validates that, for a given attribute, a matching *attribute_confirmation* attribute exists. + +#### Validate that an attribute is confirmed: + + 'password' => 'confirmed' + +Given this example, the Validator will make sure that the *password* attribute matches the *password_confirmation* attribute in the array being validated. + + +### Acceptance + +The *accepted* rule validates that an attribute is equal to *yes* or *1*. This rule is helpful for validating checkbox form fields such as "terms of service". + +#### Validate that an attribute is accepted: + + 'terms' => 'accepted' + + +## Same & Different + +#### Validate that an attribute matches another attribute: + + 'token1' => 'same:token2' + +#### Validate that two attributes have different values: + + 'password' => 'different:old_password', + + +### Regular Expression Match + +The *match* rule validates that an attribute matches a given regular expression. + +#### Validate that an attribute matches a regular expression: + + 'username' => 'match:/[a-z]+/'; + + +### Uniqueness & Existence + +#### Validate that an attribute is unique on a given database table: + + 'email' => 'unique:users' + +In the example above, the *email* attribute will be checked for uniqueness on the *users* table. Need to verify uniqueness on a column name other than the attribute name? No problem: + +#### Specify a custom column name for the unique rule: + + 'email' => 'unique:users,email_address' + +Many times, when updating a record, you want to use the unique rule, but exclude the row being updated. For example, when updating a user's profile, you may allow them to change their e-mail address. But, when the *unique* rule runs, you want it to skip the given user since they may not have changed their address, thus causing the *unique* rule to fail. It's easy: + +#### Forcing the unique rule to ignore a given ID: + + 'email' => 'unique:users,email_address,10' + +#### Validate that an attribute exists on a given database table: + + 'state' => 'exists:states' + +#### Specify a custom column name for the exists rule: + + 'state' => 'exists:states,abbreviation' + + +### Dates + +#### Validate that a date attribute is before a given date: + + 'birthdate' => 'before:1986-05-28'; + +#### Validate that a date attribute is after a given date: + + 'birthdate' => 'after:1986-05-28'; + +> **Note:** The **before** and **after** validation rules use the **strtotime** PHP function to convert your date to something the rule can understand. + + +### E-Mail Addresses + +#### Validate that an attribute is an e-mail address: + + 'address' => 'email' + +> **Note:** This rule uses the PHP built-in *filter_var* method. + + +### URLs + +#### Validate that an attribute is a URL: + + 'link' => 'url' + +#### Validate that an attribute is an active URL: + + 'link' => 'active_url' + +> **Note:** The *active_url* rule uses *checkdnsr* to verify the URL is active. + + +### Uploads + +The *mimes* rule validates that an uploaded file has a given MIME type. This rule uses the PHP Fileinfo extension to read the contents of the file and determine the actual MIME type. Any extension defined in the *config/mimes.php* file may be passed to this rule as a parameter: + +#### Validate that a file is one of the given types: + + 'picture' => 'mimes:jpg,gif' + +> **Note:** When validating files, be sure to use Input::file() or Input::all() to gather the input. + +#### Validate that a file is an image: + + 'picture' => 'image' + +#### Validate that a file is no more than a given size in kilobytes: + + 'picture' => 'image|max:100' + + +## Retrieving Error Messages + +Laravel makes working with your error messages a cinch using a simple error collector class. After calling the *passes* or *fails* method on a Validator instance, you may access the errors via the *errors* property. The error collector has several simple functions for retrieving your messages: + +#### Determine if an attribute has an error message: + + if ($validation->errors->has('email')) + { + // The e-mail attribute has errors... + } + +#### Retrieve the first error message for an attribute: + + echo $validation->errors->first('email'); + +Sometimes you may need to format the error message by wrapping it in HTML. No problem. Along with the :message place-holder, pass the format as the second parameter to the method. + +#### Format an error message: + + echo $validation->errors->first('email', '

:message

'); + +#### Get all of the error messages for a given attribute: + + $messages = $validation->errors->get('email'); + +#### Format all of the error messages for an attribute: + + $messages = $validation->errors->get('email', '

:message

'); + +#### Get all of the error messages for all attributes: + + $messages = $validation->errors->all(); + +#### Format all of the error messages for all attributes: + + $messages = $validation->errors->all('

:message

'); + + +## Validation Walkthrough + +Once you have performed your validation, you need an easy way to get the errors back to the view. Laravel makes it amazingly simple. Let's walk through a typical scenario. We'll define two routes: + + Route::get('register', function() + { + return View::make('user.register'); + }); + + Route::post('register', function() + { + $rules = array(...); + + $validation = Validator::make(Input::all(), $rules); + + if ($validation->fails()) + { + return Redirect::to('register')->with_errors($validation); + } + }); + +Great! So, we have two simple registration routes. One to handle displaying the form, and one to handle the posting of the form. In the POST route, we run some validation over the input. If the validation fails, we redirect back to the registration form and flash the validation errors to the session so they will be available for us to display. + +**But, notice we are not explicitly binding the errors to the view in our GET route**. However, an errors variable ($errors) will still be available in the view. Laravel intelligently determines if errors exist in the session, and if they do, binds them to the view for you. If no errors exist in the session, an empty message container will still be bound to the view. In your views, this allows you to always assume you have a message container available via the errors variable. We love making your life easier. + +For example, if email address validation failed, we can look for 'email' within the $errors session var. + + $errors->has('email') + +Using Blade, we can then conditionally add error messages to our view. + + {{ $errors->has('email') ? 'Invalid Email Address' : 'Condition is false. Can be left blank' }} + +This will also work great when we need to conditionally add classes when using something like Twitter Bootstrap. +For example, if the email address failed validation, we may want to add the "error" class from Bootstrap to our *div class="control-group"* statement. + +
+ +When the validation fails, our rendered view will have the appended *error* class. + +
+ + + + +## Custom Error Messages + +Want to use an error message other than the default? Maybe you even want to use a custom error message for a given attribute and rule. Either way, the Validator class makes it easy. + +#### Create an array of custom messages for the Validator: + + $messages = array( + 'required' => 'The :attribute field is required.', + ); + + $validation = Validator::make(Input::get(), $rules, $messages); + +Great! Now our custom message will be used anytime a required validation check fails. But, what is this **:attribute** stuff in our message? To make your life easier, the Validator class will replace the **:attribute** place-holder with the actual name of the attribute! It will even remove underscores from the attribute name. + +You may also use the **:other**, **:size**, **:min**, **:max**, and **:values** place-holders when constructing your error messages: + +#### Other validation message place-holders: + + $messages = array( + 'same' => 'The :attribute and :other must match.', + 'size' => 'The :attribute must be exactly :size.', + 'between' => 'The :attribute must be between :min - :max.', + 'in' => 'The :attribute must be one of the following types: :values', + ); + +So, what if you need to specify a custom required message, but only for the email attribute? No problem. Just specify the message using an **attribute_rule** naming convention: + +#### Specifying a custom error message for a given attribute: + + $messages = array( + 'email_required' => 'We need to know your e-mail address!', + ); + +In the example above, the custom required message will be used for the email attribute, while the default message will be used for all other attributes. + +However, if you are using many custom error messages, specifying inline may become cumbersome and messy. For that reason, you can specify your custom messages in the **custom** array within the validation language file: + +#### Adding custom error messages to the validation language file: + + 'custom' => array( + 'email_required' => 'We need to know your e-mail address!', + ) + + +## Custom Validation Rules + +Laravel provides a number of powerful validation rules. However, it's very likely that you'll need to eventually create some of your own. There are two simple methods for creating validation rules. Both are solid so use whichever you think best fits your project. + +#### Registering a custom validation rule: + + Validator::register('awesome', function($attribute, $value, $parameters) + { + return $value == 'awesome'; + }); + +In this example we're registering a new validation rule with the validator. The rule receives three arguments. The first is the name of the attribute being validated, the second is the value of the attribute being validated, and the third is an array of parameters that were specified for the rule. + +Here is how your custom validation rule looks when called: + + $rules = array( + 'username' => 'required|awesome', + ); + +Of course, you will need to define an error message for your new rule. You can do this either in an ad-hoc messages array: + + $messages = array( + 'awesome' => 'The attribute value must be awesome!', + ); + + $validator = Validator::make(Input::get(), $rules, $messages); + +Or by adding an entry for your rule in the **language/en/validation.php** file: + + 'awesome' => 'The attribute value must be awesome!', + +As mentioned above, you may even specify and receive a list of parameters in your custom rule: + + // When building your rules array... + + $rules = array( + 'username' => 'required|awesome:yes', + ); + + // In your custom rule... + + Validator::register('awesome', function($attribute, $value, $parameters) + { + return $value == $parameters[0]; + }); + +In this case, the parameters argument of your validation rule would receive an array containing one element: "yes". + +Another method for creating and storing custom validation rules is to extend the Validator class itself. By extending the class you create a new version of the validator that has all of the pre-existing functionality combined with your own custom additions. You can even choose to replace some of the default methods if you'd like. Let's look at an example: + +First, create a class that extends **Laravel\Validator** and place it in your **application/libraries** directory: + +#### Defining a custom validator class: + + +## Registering Assets + +The **Asset** class provides a simple way to manage the CSS and JavaScript used by your application. To register an asset just call the **add** method on the **Asset** class: + +#### Registering an asset: + + Asset::add('jquery', 'js/jquery.js'); + +The **add** method accepts three parameters. The first is the name of the asset, the second is the path to the asset relative to the **public** directory, and the third is a list of asset dependencies (more on that later). Notice that we did not tell the method if we were registering JavaScript or CSS. The **add** method will use the file extension to determine the type of file we are registering. + + +## Dumping Assets + +When you are ready to place the links to the registered assets on your view, you may use the **styles** or **scripts** methods: + +#### Dumping assets into a view: + + + + + + + +## Asset Dependencies + +Sometimes you may need to specify that an asset has dependencies. This means that the asset requires other assets to be declared in your view before it can be declared. Managing asset dependencies couldn't be easier in Laravel. Remember the "names" you gave to your assets? You can pass them as the third parameter to the **add** method to declare dependencies: + +#### Registering a bundle that has dependencies: + + Asset::add('jquery-ui', 'js/jquery-ui.js', 'jquery'); + +In this example, we are registering the **jquery-ui** asset, as well as specifying that it is dependent on the **jquery** asset. Now, when you place the asset links on your views, the jQuery asset will always be declared before the jQuery UI asset. Need to declare more than one dependency? No problem: + +#### Registering an asset that has multiple dependencies: + + Asset::add('jquery-ui', 'js/jquery-ui.js', array('first', 'second')); + + +## Asset Containers + +To increase response time, it is common to place JavaScript at the bottom of HTML documents. But, what if you also need to place some assets in the head of your document? No problem. The asset class provides a simple way to manage asset **containers**. Simply call the **container** method on the Asset class and mention the container name. Once you have a container instance, you are free to add any assets you wish to the container using the same syntax you are used to: + +#### Retrieving an instance of an asset container: + + Asset::container('footer')->add('example', 'js/example.js'); + +#### Dumping that assets from a given container: + + echo Asset::container('footer')->scripts(); + + +## Bundle Assets + +Before learning how to conveniently add and dump bundle assets, you may wish to read the documentation on [creating and publishing bundle assets](/docs/bundles#bundle-assets). + +When registering assets, the paths are typically relative to the **public** directory. However, this is inconvenient when dealing with bundle assets, since they live in the **public/bundles** directory. But, remember, Laravel is here to make your life easier. So, it is simple to specify the bundle which the Asset container is managing. + +#### Specifying the bundle the asset container is managing: + + Asset::container('foo')->bundle('admin'); + +Now, when you add an asset, you can use paths relative to the bundle's public directory. Laravel will automatically generate the correct full paths. \ No newline at end of file diff --git a/app/laravel/documentation/views/forms.md b/app/laravel/documentation/views/forms.md new file mode 100644 index 000000000..cee6287af --- /dev/null +++ b/app/laravel/documentation/views/forms.md @@ -0,0 +1,161 @@ +# Building Forms + +## Contents + +- [Opening A Form](#opening-a-form) +- [CSRF Protection](#csrf-protection) +- [Labels](#labels) +- [Text, Text Area, Password & Hidden Fields](#text) +- [File Input](#file) +- [Checkboxes and Radio Buttons](#checkboxes-and-radio-buttons) +- [Drop-Down Lists](#drop-down-lists) +- [Buttons](#buttons) +- [Custom Macros](#custom-macros) + +> **Note:** All input data displayed in form elements is filtered through the HTML::entities method. + + +## Opening A Form + +#### Opening a form to POST to the current URL: + + echo Form::open(); + +#### Opening a form using a given URI and request method: + + echo Form::open('user/profile', 'PUT'); + +#### Opening a Form that POSTS to a HTTPS URL: + + echo Form::open_secure('user/profile'); + +#### Specifying extra HTML attributes on a form open tag: + + echo Form::open('user/profile', 'POST', array('class' => 'awesome')); + +#### Opening a form that accepts file uploads: + + echo Form::open_for_files('users/profile'); + +#### Opening a form that accepts file uploads and uses HTTPS: + + echo Form::open_secure_for_files('users/profile'); + +#### Closing a form: + + echo Form::close(); + + +## CSRF Protection + +Laravel provides an easy method of protecting your application from cross-site request forgeries. First, a random token is placed in your user's session. Don't sweat it, this is done automatically. Next, use the token method to generate a hidden form input field containing the random token on your form: + +#### Generating a hidden field containing the session's CSRF token: + + echo Form::token(); + +#### Attaching the CSRF filter to a route: + + Route::post('profile', array('before' => 'csrf', function() + { + // + })); + +#### Retrieving the CSRF token string: + + $token = Session::token(); + +> **Note:** You must specify a session driver before using the Laravel CSRF protection facilities. + +*Further Reading:* + +- [Route Filters](/docs/routing#filters) +- [Cross-Site Request Forgery](http://en.wikipedia.org/wiki/Cross-site_request_forgery) + + +## Labels + +#### Generating a label element: + + echo Form::label('email', 'E-Mail Address'); + +#### Specifying extra HTML attributes for a label: + + echo Form::label('email', 'E-Mail Address', array('class' => 'awesome')); + +> **Note:** After creating a label, any form element you create with a name matching the label name will automatically receive an ID matching the label name as well. + + +## Text, Text Area, Password & Hidden Fields + +#### Generate a text input element: + + echo Form::text('username'); + +#### Specifying a default value for a text input element: + + echo Form::text('email', 'example@gmail.com'); + +> **Note:** The *hidden* and *textarea* methods have the same signature as the *text* method. You just learned three methods for the price of one! + +#### Generating a password input element: + + echo Form::password('password'); + + +## Checkboxes and Radio Buttons + +#### Generating a checkbox input element: + + echo Form::checkbox('name', 'value'); + +#### Generating a checkbox that is checked by default: + + echo Form::checkbox('name', 'value', true); + +> **Note:** The *radio* method has the same signature as the *checkbox* method. Two for one! + + +## File Input + +#### Generate a file input element: + + echo Form::file('image'); + + +## Drop-Down Lists + +#### Generating a drop-down list from an array of items: + + echo Form::select('size', array('L' => 'Large', 'S' => 'Small')); + +#### Generating a drop-down list with an item selected by default: + + echo Form::select('size', array('L' => 'Large', 'S' => 'Small'), 'S'); + + +## Buttons + +#### Generating a submit button element: + + echo Form::submit('Click Me!'); + +> **Note:** Need to create a button element? Try the *button* method. It has the same signature as *submit*. + + +## Custom Macros + +It's easy to define your own custom Form class helpers called "macros". Here's how it works. First, simply register the macro with a given name and a Closure: + +#### Registering a Form macro: + + Form::macro('my_field', function() + { + return ''; + }); + +Now you can call your macro using its name: + +#### Calling a custom Form macro: + + echo Form::my_field(); \ No newline at end of file diff --git a/app/laravel/documentation/views/home.md b/app/laravel/documentation/views/home.md new file mode 100644 index 000000000..27383562b --- /dev/null +++ b/app/laravel/documentation/views/home.md @@ -0,0 +1,260 @@ +# Views & Responses + +## Contents + +- [The Basics](#the-basics) +- [Binding Data To Views](#binding-data-to-views) +- [Nesting Views](#nesting-views) +- [Named Views](#named-views) +- [View Composers](#view-composers) +- [Redirects](#redirects) +- [Redirecting With Flash Data](#redirecting-with-flash-data) +- [Downloads](#downloads) +- [Errors](#errors) + + +## The Basics + +Views contain the HTML that is sent to the person using your application. By separating your view from the business logic of your application, your code will be cleaner and easier to maintain. + +All views are stored within the **application/views** directory and use the PHP file extension. The **View** class provides a simple way to retrieve your views and return them to the client. Let's look at an example! + +#### Creating the view: + + + I'm stored in views/home/index.php! + + +#### Returning the view from a route: + + Route::get('/', function() + { + return View::make('home.index'); + }); + +#### Returning the view from a controller: + + public function action_index() + { + return View::make('home.index'); + }); + +#### Determining if a view exists: + + $exists = View::exists('home.index'); + +Sometimes you will need a little more control over the response sent to the browser. For example, you may need to set a custom header on the response, or change the HTTP status code. Here's how: + +#### Returning a custom response: + + Route::get('/', function() + { + $headers = array('foo' => 'bar'); + + return Response::make('Hello World!', 200, $headers); + }); + +#### Returning a custom response containing a view, with binding data: + + return Response::view('home', array('foo' => 'bar')); + +#### Returning a JSON response: + + return Response::json(array('name' => 'Batman')); + +#### Returning Eloquent models as JSON: + + return Response::eloquent(User::find(1)); + + +## Binding Data To Views + +Typically, a route or controller will request data from a model that the view needs to display. So, we need a way to pass the data to the view. There are several ways to accomplish this, so just pick the way that you like best! + +#### Binding data to a view: + + Route::get('/', function() + { + return View::make('home')->with('name', 'James'); + }); + +#### Accessing the bound data within a view: + + + Hello, . + + +#### Chaining the binding of data to a view: + + View::make('home') + ->with('name', 'James') + ->with('votes', 25); + +#### Passing an array of data to bind data: + + View::make('home', array('name' => 'James')); + +#### Using magic methods to bind data: + + $view->name = 'James'; + $view->email = 'example@example.com'; + +#### Using the ArrayAccess interface methods to bind data: + + $view['name'] = 'James'; + $view['email'] = 'example@example.com'; + + +## Nesting Views + +Often you will want to nest views within views. Nested views are sometimes called "partials", and help you keep views small and modular. + +#### Binding a nested view using the "nest" method: + + View::make('home')->nest('footer', 'partials.footer'); + +#### Passing data to a nested view: + + $view = View::make('home'); + + $view->nest('content', 'orders', array('orders' => $orders)); + +Sometimes you may wish to directly include a view from within another view. You can use the **render** helper function: + +#### Using the "render" helper to display a view: + +
+ +
+ +It is also very common to have a partial view that is responsible for display an instance of data in a list. For example, you may create a partial view responsible for displaying the details about a single order. Then, for example, you may loop through an array of orders, rendering the partial view for each order. This is made simpler using the **render_each** helper: + +#### Rendering a partial view for each item in an array: + +
+ + +The first argument is the name of the partial view, the second is the array of data, and the third is the variable name that should be used when each array item is passed to the partial view. + + +## Named Views + +Named views can help to make your code more expressive and organized. Using them is simple: + +#### Registering a named view: + + View::name('layouts.default', 'layout'); + +#### Getting an instance of the named view: + + return View::of('layout'); + +#### Binding data to a named view: + + return View::of('layout', array('orders' => $orders)); + + +## View Composers + +Each time a view is created, its "composer" event will be fired. You can listen for this event and use it to bind assets and common data to the view each time it is created. A common use-case for this functionality is a side-navigation partial that shows a list of random blog posts. You can nest your partial view by loading it in your layout view. Then, define a composer for that partial. The composer can then query the posts table and gather all of the necessary data to render your view. No more random logic strewn about! Composers are typically defined in **application/routes.php**. Here's an example: + +#### Register a view composer for the "home" view: + + View::composer('home', function($view) + { + $view->nest('footer', 'partials.footer'); + }); + +Now each time the "home" view is created, an instance of the View will be passed to the registered Closure, allowing you to prepare the view however you wish. + +#### Register a composer that handles multiple views: + + View::composer(array('home', 'profile'), function($view) + { + // + }); + +> **Note:** A view can have more than one composer. Go wild! + + +## Redirects + +It's important to note that both routes and controllers require responses to be returned with the 'return' directive. Instead of calling "Redirect::to()"" where you'd like to redirect the user. You'd instead use "return Redirect::to()". This distinction is important as it's different than most other PHP frameworks and it could be easy to accidentally overlook the importance of this practice. + +#### Redirecting to another URI: + + return Redirect::to('user/profile'); + +#### Redirecting with a specific status: + + return Redirect::to('user/profile', 301); + +#### Redirecting to a secure URI: + + return Redirect::to_secure('user/profile'); + +#### Redirecting to the root of your application: + + return Redirect::home(); + +#### Redirecting back to the previous action: + + return Redirect::back(); + +#### Redirecting to a named route: + + return Redirect::to_route('profile'); + +#### Redirecting to a controller action: + + return Redirect::to_action('home@index'); + +Sometimes you may need to redirect to a named route, but also need to specify the values that should be used instead of the route's URI wildcards. It's easy to replace the wildcards with proper values: + +#### Redirecting to a named route with wildcard values: + + return Redirect::to_route('profile', array($username)); + +#### Redirecting to an action with wildcard values: + + return Redirect::to_action('user@profile', array($username)); + + +## Redirecting With Flash Data + +After a user creates an account or signs into your application, it is common to display a welcome or status message. But, how can you set the status message so it is available for the next request? Use the with() method to send flash data along with the redirect response. + + return Redirect::to('profile')->with('status', 'Welcome Back!'); + +You can access your message from the view with the Session get method: + + $status = Session::get('status'); + +*Further Reading:* + +- *[Sessions](/docs/session/config)* + + +## Downloads + +#### Sending a file download response: + + return Response::download('file/path.jpg'); + +#### Sending a file download and assigning a file name: + + return Response::download('file/path.jpg', 'photo.jpg'); + + +## Errors + +To generating proper error responses simply specify the response code that you wish to return. The corresponding view stored in **views/error** will automatically be returned. + +#### Generating a 404 error response: + + return Response::error('404'); + +#### Generating a 500 error response: + + return Response::error('500'); diff --git a/app/laravel/documentation/views/html.md b/app/laravel/documentation/views/html.md new file mode 100644 index 000000000..629387c83 --- /dev/null +++ b/app/laravel/documentation/views/html.md @@ -0,0 +1,141 @@ +# Building HTML + +## Content + +- [Entities](#entities) +- [Scripts And Style Sheets](#scripts-and-style-sheets) +- [Links](#links) +- [Links To Named Routes](#links-to-named-routes) +- [Links To Controller Actions](#links-to-controller-actions) +- [Mail-To Links](#mail-to-links) +- [Images](#images) +- [Lists](#lists) +- [Custom Macros](#custom-macros) + + +## Entities + +When displaying user input in your Views, it is important to convert all characters which have significance in HTML to their "entity" representation. + +For example, the < symbol should be converted to its entity representation. Converting HTML characters to their entity representation helps protect your application from cross-site scripting: + +#### Converting a string to its entity representation: + + echo HTML::entities(''); + +#### Using the "e" global helper: + + echo e(''); + + +## Scripts And Style Sheets + +#### Generating a reference to a JavaScript file: + + echo HTML::script('js/scrollTo.js'); + +#### Generating a reference to a CSS file: + + echo HTML::style('css/common.css'); + +#### Generating a reference to a CSS file using a given media type: + + echo HTML::style('css/common.css', 'print'); + +*Further Reading:* + +- *[Managing Assets](/docs/views/assets)* + + +## Links + +#### Generating a link from a URI: + + echo HTML::link('user/profile', 'User Profile'); + +#### Generating a link that should use HTTPS: + + echo HTML::secure_link('user/profile', 'User Profile'); + +#### Generating a link and specifying extra HTML attributes: + + echo HTML::link('user/profile', 'User Profile', array('id' => 'profile_link')); + + +## Links To Named Routes + +#### Generating a link to a named route: + + echo HTML::link_to_route('profile'); + +#### Generating a link to a named route with wildcard values: + + $url = HTML::link_to_route('profile', 'User Profile', array($username)); + +*Further Reading:* + +- *[Named Routes](/docs/routing#named-routes)* + + +## Links To Controller Actions + +#### Generating a link to a controller action: + + echo HTML::link_to_action('home@index'); + +### Generating a link to a controller action with wildcard values: + + echo HTML::link_to_action('user@profile', 'User Profile', array($username)); + + +## Mail-To Links + +The "mailto" method on the HTML class obfuscates the given e-mail address so it is not sniffed by bots. + +#### Creating a mail-to link: + + echo HTML::mailto('example@gmail.com', 'E-Mail Me!'); + +#### Creating a mail-to link using the e-mail address as the link text: + + echo HTML::mailto('example@gmail.com'); + + +## Images + +#### Generating an HTML image tag: + + echo HTML::image('img/smile.jpg', $alt_text); + +#### Generating an HTML image tag with extra HTML attributes: + + echo HTML::image('img/smile.jpg', $alt_text, array('id' => 'smile')); + + +## Lists + +#### Creating lists from an array of items: + + echo HTML::ol(array('Get Peanut Butter', 'Get Chocolate', 'Feast')); + + echo HTML::ul(array('Ubuntu', 'Snow Leopard', 'Windows')); + + echo HTML::dl(array('Ubuntu' => 'An operating system by Canonical', 'Windows' => 'An operating system by Microsoft')); + + +## Custom Macros + +It's easy to define your own custom HTML class helpers called "macros". Here's how it works. First, simply register the macro with a given name and a Closure: + +#### Registering a HTML macro: + + HTML::macro('my_element', function() + { + return '
'; + }); + +Now you can call your macro using its name: + +#### Calling a custom HTML macro: + + echo HTML::my_element(); diff --git a/app/laravel/documentation/views/pagination.md b/app/laravel/documentation/views/pagination.md new file mode 100644 index 000000000..081a6f600 --- /dev/null +++ b/app/laravel/documentation/views/pagination.md @@ -0,0 +1,110 @@ +# Pagination + +## Contents + +- [The Basics](#the-basics) +- [Using The Query Builder](#using-the-query-builder) +- [Appending To Pagination Links](#appending-to-pagination-links) +- [Creating Paginators Manually](#creating-paginators-manually) +- [Pagination Styling](#pagination-styling) + + +## The Basics + +Laravel's paginator was designed to reduce the clutter of implementing pagination. + + +## Using The Query Builder + +Let's walk through a complete example of paginating using the [Fluent Query Builder](/docs/database/fluent): + +#### Pull the paginated results from the query: + + $orders = DB::table('orders')->paginate($per_page); + +You can also pass an optional array of table columns to select in the query: + + $orders = DB::table('orders')->paginate($per_page, array('id', 'name', 'created_at')); + +#### Display the results in a view: + + results as $order): ?> + id; ?> + + +#### Generate the pagination links: + + links(); ?> + +The links method will create an intelligent, sliding list of page links that looks something like this: + + Previous 1 2 ... 24 25 26 27 28 29 30 ... 78 79 Next + +The Paginator will automatically determine which page you're on and update the results and links accordingly. + +It's also possible to generate "next" and "previous" links: + +#### Generating simple "previous" and "next" links: + + previous().' '.$orders->next(); ?> + +*Further Reading:* + +- *[Fluent Query Builder](/docs/database/fluent)* + + +## Appending To Pagination Links + +You may need to add more items to the pagination links' query strings, such as the column your are sorting by. + +#### Appending to the query string of pagination links: + + appends(array('sort' => 'votes'))->links(); + +This will generate URLs that look something like this: + + http://example.com/something?page=2&sort=votes + + +## Creating Paginators Manually + +Sometimes you may need to create a Paginator instance manually, without using the query builder. Here's how: + +#### Creating a Paginator instance manually: + + $orders = Paginator::make($orders, $total, $per_page); + + +## Pagination Styling + +All pagination link elements can be style using CSS classes. Here is an example of the HTML elements generated by the links method: + + + +When you are on the first page of results, the "Previous" link will be disabled. Likewise, the "Next" link will be disabled when you are on the last page of results. The generated HTML will look like this: + +
  • Previous
  • diff --git a/app/laravel/documentation/views/templating.md b/app/laravel/documentation/views/templating.md new file mode 100644 index 000000000..9aabac87f --- /dev/null +++ b/app/laravel/documentation/views/templating.md @@ -0,0 +1,210 @@ +# Templating + +## Contents + +- [The Basics](#the-basics) +- [Sections](#sections) +- [Blade Template Engine](#blade-template-engine) +- [Blade Control Structures](#blade-control-structures) +- [Blade Layouts](#blade-layouts) + + +## The Basics + +Your application probably uses a common layout across most of its pages. Manually creating this layout within every controller action can be a pain. Specifying a controller layout will make your development much more enjoyable. Here's how to get started: + +#### Specify a "layout" property on your controller: + + class Base_Controller extends Controller { + + public $layout = 'layouts.common'; + + } + +#### Access the layout from the controllers' action: + + public function action_profile() + { + $this->layout->nest('content', 'user.profile'); + } + +> **Note:** When using layouts, actions do not need to return anything. + + +## Sections + +View sections provide a simple way to inject content into layouts from nested views. For example, perhaps you want to inject a nested view's needed JavaScript into the header of your layout. Let's dig in: + +#### Creating a section within a view: + + + + + +#### Rendering the contents of a section: + + + + + +#### Using Blade short-cuts to work with sections: + + @section('scripts') + + @endsection + + + @yield('scripts') + + + +## Blade Template Engine + +Blade makes writing your views pure bliss. To create a blade view, simply name your view file with a ".blade.php" extension. Blade allows you to use beautiful, unobtrusive syntax for writing PHP control structures and echoing data. Here's an example: + +#### Echoing a variable using Blade: + + Hello, {{ $name }}. + +#### Echoing function results using Blade: + + {{ Asset::styles() }} + +#### Render a view: + +You can use **@include** to render a view into another view. The rendered view will automatically inherit all of the data from the current view. + +

    Profile + @include('user.profile') + +Similarly, you can use **@render**, which behaves the same as **@include** except the rendered view will **not** inherit the data from the current view. + + @render('admin.list') + +#### Blade comments: + + {{-- This is a comment --}} + + {{-- + This is a + multi-line + comment. + --}} + +> **Note:** Unlike HTML comments, Blade comments are not visible in the HTML source. + + +## Blade Control Structures + +#### For Loop: + + @for ($i = 0; $i <= count($comments); $i++) + The comment body is {{ $comments[$i] }} + @endfor + +#### Foreach Loop: + + @foreach ($comments as $comment) + The comment body is {{ $comment->body }}. + @endforeach + +#### While Loop: + + @while ($something) + I am still looping! + @endwhile + +#### If Statement: + + @if ( $message == true ) + I'm displaying the message! + @endif + +#### If Else Statement: + + @if (count($comments) > 0) + I have comments! + @else + I have no comments! + @endif + +#### Else If Statement: + + @if ( $message == 'success' ) + It was a success! + @elseif ( $message == 'error' ) + An error occurred. + @else + Did it work? + @endif + +#### For Else Statement: + + @forelse ($posts as $post) + {{ $post->body }} + @empty + There are not posts in the array! + @endforelse + +#### Unless Statement: + + @unless(Auth::check()) + Login + @endunless + + // Equivalent to... + + + Login + + + +## Blade Layouts + +Not only does Blade provide clean, elegant syntax for common PHP control structures, it also gives you a beautiful method of using layouts for your views. For example, perhaps your application uses a "master" view to provide a common look and feel for your application. It may look something like this: + + + + +
    + @yield('content') +
    + + +Notice the "content" section being yielded. We need to fill this section with some text, so let's make another view that uses this layout: + + @layout('master') + + @section('content') + Welcome to the profile page! + @endsection + +Great! Now, we can simply return the "profile" view from our route: + + return View::make('profile'); + +The profile view will automatically use the "master" template thanks to Blade's **@layout** expression. + +> **Important:** The **@layout** call must always be on the very first line of the file, with no leading whitespaces or newline breaks. + +#### Appending with @parent + +Sometimes you may want to only append to a section of a layout rather than overwrite it. For example, consider the navigation list in our "master" layout. Let's assume we just want to append a new list item. Here's how to do it: + + @layout('master') + + @section('navigation') + @parent +
  • Nav Item 3
  • + @endsection + + @section('content') + Welcome to the profile page! + @endsection + +**@parent** will be replaced with the contents of the layout's *navigation* section, providing you with a beautiful and powerful method of performing layout extension and inheritance. diff --git a/app/laravel/error.php b/app/laravel/error.php index bf668d8a4..283e043fd 100644 --- a/app/laravel/error.php +++ b/app/laravel/error.php @@ -6,26 +6,47 @@ class Error { * Handle an exception and display the exception report. * * @param Exception $exception + * @param bool $trace * @return void */ - public static function exception($exception) + public static function exception($exception, $trace = true) { static::log($exception); ob_get_level() and ob_end_clean(); + $message = $exception->getMessage(); + + // For Laravel view errors we want to show a prettier error: + $file = $exception->getFile(); + + if (str_contains($exception->getFile(), 'eval()') and str_contains($exception->getFile(), 'laravel/view.php')) + { + $message = 'Error rendering view: ['.View::$last['name'].']'.PHP_EOL.PHP_EOL.$message; + + $file = View::$last['path']; + } + // If detailed errors are enabled, we'll just format the exception into // a simple error message and display it on the screen. We don't use a // View in case the problem is in the View class. + if (Config::get('error.detail')) { - echo "

    Unhandled Exception

    -

    Message:

    -
    ".$exception->getMessage()."
    -

    Location:

    -
    ".$exception->getFile()." on line ".$exception->getLine()."
    + $response_body = "

    Unhandled Exception

    +

    Message:

    +
    ".$message."
    +

    Location:

    +
    ".$file." on line ".$exception->getLine()."
    "; + + if ($trace) + { + $response_body .= "

    Stack Trace:

    ".$exception->getTraceAsString()."
    "; + } + + $response = Response::make($response_body, 500); } // If we're not using detailed error messages, we'll use the event @@ -35,9 +56,13 @@ public static function exception($exception) { $response = Event::first('500'); - return Response::prepare($response)->send(); + $response = Response::prepare($response); } + $response->render(); + $response->send(); + $response->foundation->finish(); + exit(1); } @@ -54,7 +79,7 @@ public static function native($code, $error, $file, $line) { if (error_reporting() === 0) return; - // For a PHP error, we'll create an ErrorExcepetion and then feed that + // For a PHP error, we'll create an ErrorException and then feed that // exception to the exception method, which will create a simple view // of the exception details for the developer. $exception = new \ErrorException($error, $code, 0, $file, $line); @@ -62,8 +87,6 @@ public static function native($code, $error, $file, $line) if (in_array($code, Config::get('error.ignore'))) { return static::log($exception); - - return true; } static::exception($exception); @@ -76,7 +99,7 @@ public static function native($code, $error, $file, $line) */ public static function shutdown() { - // If a fatal error occured that we have not handled yet, we will + // If a fatal error occurred that we have not handled yet, we will // create an ErrorException and feed it to the exception handler, // as it will not yet have been handled. $error = error_get_last(); @@ -85,7 +108,7 @@ public static function shutdown() { extract($error, EXTR_SKIP); - static::exception(new \ErrorException($message, $type, 0, $file, $line)); + static::exception(new \ErrorException($message, $type, 0, $file, $line), false); } } @@ -103,4 +126,4 @@ public static function log($exception) } } -} \ No newline at end of file +} diff --git a/app/laravel/event.php b/app/laravel/event.php index 7fdc13688..ccc3bf1c0 100644 --- a/app/laravel/event.php +++ b/app/laravel/event.php @@ -9,6 +9,20 @@ class Event { */ public static $events = array(); + /** + * The queued events waiting for flushing. + * + * @var array + */ + public static $queued = array(); + + /** + * All of the registered queue flusher callbacks. + * + * @var array + */ + public static $flushers = array(); + /** * Determine if an event has any registered listeners. * @@ -54,6 +68,31 @@ public static function override($event, $callback) static::listen($event, $callback); } + /** + * Add an item to an event queue for processing. + * + * @param string $queue + * @param string $key + * @param mixed $data + * @return void + */ + public static function queue($queue, $key, $data = array()) + { + static::$queued[$queue][$key] = $data; + } + + /** + * Register a queue flusher callback. + * + * @param string $queue + * @param mixed $callback + * @return void + */ + public static function flusher($queue, $callback) + { + static::$flushers[$queue][] = $callback; + } + /** * Clear all event listeners for a given event. * @@ -86,7 +125,7 @@ public static function first($event, $parameters = array()) } /** - * Fire an event and return the the first response. + * Fire an event and return the first response. * * Execution will be halted after the first valid response is found. * @@ -99,6 +138,30 @@ public static function until($event, $parameters = array()) return static::fire($event, $parameters, true); } + /** + * Flush an event queue, firing the flusher for each payload. + * + * @param string $queue + * @return void + */ + public static function flush($queue) + { + foreach (static::$flushers[$queue] as $flusher) + { + // We will simply spin through each payload registered for the event and + // fire the flusher, passing each payloads as we go. This allows all + // the events on the queue to be processed by the flusher easily. + if ( ! isset(static::$queued[$queue])) continue; + + foreach (static::$queued[$queue] as $key => $payload) + { + array_unshift($payload, $key); + + call_user_func_array($flusher, $payload); + } + } + } + /** * Fire an event so that all listeners are called. * @@ -108,14 +171,17 @@ public static function until($event, $parameters = array()) * * // Fire the "start" event passing an array of parameters * $responses = Event::fire('start', array('Laravel', 'Framework')); + * + * // Fire multiple events with the same parameters + * $responses = Event::fire(array('start', 'loading'), $parameters); * * - * @param string $event - * @param array $parameters - * @param bool $halt + * @param string|array $events + * @param array $parameters + * @param bool $halt * @return array */ - public static function fire($event, $parameters = array(), $halt = false) + public static function fire($events, $parameters = array(), $halt = false) { $responses = array(); @@ -124,28 +190,31 @@ public static function fire($event, $parameters = array(), $halt = false) // If the event has listeners, we will simply iterate through them and call // each listener, passing in the parameters. We will add the responses to // an array of event responses and return the array. - if (static::listeners($event)) + foreach ((array) $events as $event) { - foreach (static::$events[$event] as $callback) + if (static::listeners($event)) { - $response = call_user_func_array($callback, $parameters); - - // If the event is set to halt, we will return the first response - // that is not null. This allows the developer to easily stack - // events but still get the first valid response. - if ($halt and ! is_null($response)) + foreach (static::$events[$event] as $callback) { - return $response; + $response = call_user_func_array($callback, $parameters); + + // If the event is set to halt, we will return the first response + // that is not null. This allows the developer to easily stack + // events but still get the first valid response. + if ($halt and ! is_null($response)) + { + return $response; + } + + // After the handler has been called, we'll add the response to + // an array of responses and return the array to the caller so + // all of the responses can be easily examined. + $responses[] = $response; } - - // After the handler has been called, we'll add the response to - // an array of responses and return the array to the caller so - // all of the responses can be easily examined. - $responses[] = $response; } } - return $responses; + return $halt ? null : $responses; } } \ No newline at end of file diff --git a/app/laravel/file.php b/app/laravel/file.php index 0346c83c7..5ac39cac1 100644 --- a/app/laravel/file.php +++ b/app/laravel/file.php @@ -1,4 +1,4 @@ -isDir()) { static::rmdir($item->getRealPath()); @@ -306,6 +307,7 @@ public static function rmdir($directory, $preserve = false) } } + unset($items); if ( ! $preserve) @rmdir($directory); } @@ -329,16 +331,22 @@ public static function cleandir($directory) */ public static function latest($directory, $options = fIterator::SKIP_DOTS) { + $latest = null; + $time = 0; $items = new fIterator($directory, $options); - // To get the latest created file, we'll simply spin through the + // To get the latest created file, we'll simply loop through the // directory, setting the latest file if we encounter a file // with a UNIX timestamp greater than the latest one. foreach ($items as $item) { - if ($item->getMTime() > $time) $latest = $item; + if ($item->getMTime() > $time) + { + $latest = $item; + $time = $item->getMTime(); + } } return $latest; diff --git a/app/laravel/form.php b/app/laravel/form.php index a768f503e..5a450b526 100644 --- a/app/laravel/form.php +++ b/app/laravel/form.php @@ -8,7 +8,7 @@ class Form { * @var array */ public static $labels = array(); - + /** * The registered custom macros. * @@ -16,13 +16,13 @@ class Form { */ public static $macros = array(); - /** - * Registers a custom macro. - * - * @param string $name - * @param Closure $input - * @return void - */ + /** + * Registers a custom macro. + * + * @param string $name + * @param Closure $macro + * @return void + */ public static function macro($name, $macro) { static::$macros[$name] = $macro; @@ -51,12 +51,12 @@ public static function macro($name, $macro) * @param bool $https * @return string */ - public static function open($action = null, $method = 'POST', $attributes = array(), $https = false) + public static function open($action = null, $method = 'POST', $attributes = array(), $https = null) { $method = strtoupper($method); $attributes['method'] = static::method($method); - + $attributes['action'] = static::action($action, $https); // If a character encoding has not been specified in the attributes, we will @@ -128,8 +128,8 @@ public static function open_secure($action = null, $method = 'POST', $attributes * @param array $attributes * @param bool $https * @return string - */ - public static function open_for_files($action = null, $method = 'POST', $attributes = array(), $https = false) + */ + public static function open_for_files($action = null, $method = 'POST', $attributes = array(), $https = null) { $attributes['enctype'] = 'multipart/form-data'; @@ -143,7 +143,7 @@ public static function open_for_files($action = null, $method = 'POST', $attribu * @param string $method * @param array $attributes * @return string - */ + */ public static function open_secure_for_files($action = null, $method = 'POST', $attributes = array()) { return static::open_for_files($action, $method, $attributes, true); @@ -181,7 +181,7 @@ public static function token() * @param string $value * @param array $attributes * @return string - */ + */ public static function label($name, $value, $attributes = array()) { static::$labels[] = $name; @@ -209,7 +209,7 @@ public static function label($name, $value, $attributes = array()) * @param mixed $value * @param array $attributes * @return string - */ + */ public static function input($type, $name, $value = null, $attributes = array()) { $name = (isset($attributes['name'])) ? $attributes['name'] : $name; @@ -240,7 +240,7 @@ public static function text($name, $value = null, $attributes = array()) * @param string $name * @param array $attributes * @return string - */ + */ public static function password($name, $attributes = array()) { return static::input('password', $name, null, $attributes); @@ -266,7 +266,7 @@ public static function hidden($name, $value = null, $attributes = array()) * @param string $value * @param array $attributes * @return string - */ + */ public static function search($name, $value = null, $attributes = array()) { return static::input('search', $name, $value, $attributes); @@ -279,7 +279,7 @@ public static function search($name, $value = null, $attributes = array()) * @param string $value * @param array $attributes * @return string - */ + */ public static function email($name, $value = null, $attributes = array()) { return static::input('email', $name, $value, $attributes); @@ -305,7 +305,7 @@ public static function telephone($name, $value = null, $attributes = array()) * @param string $value * @param array $attributes * @return string - */ + */ public static function url($name, $value = null, $attributes = array()) { return static::input('url', $name, $value, $attributes); @@ -318,12 +318,12 @@ public static function url($name, $value = null, $attributes = array()) * @param string $value * @param array $attributes * @return string - */ + */ public static function number($name, $value = null, $attributes = array()) { return static::input('number', $name, $value, $attributes); } - + /** * Create a HTML date input element. * @@ -331,7 +331,7 @@ public static function number($name, $value = null, $attributes = array()) * @param string $value * @param array $attributes * @return string - */ + */ public static function date($name, $value = null, $attributes = array()) { return static::input('date', $name, $value, $attributes); @@ -343,7 +343,7 @@ public static function date($name, $value = null, $attributes = array()) * @param string $name * @param array $attributes * @return string - */ + */ public static function file($name, $attributes = array()) { return static::input('file', $name, null, $attributes); @@ -386,23 +386,50 @@ public static function textarea($name, $value = '', $attributes = array()) * @param string $selected * @param array $attributes * @return string - */ + */ public static function select($name, $options = array(), $selected = null, $attributes = array()) { $attributes['id'] = static::id($name, $attributes); - + $attributes['name'] = $name; $html = array(); foreach ($options as $value => $display) { - $html[] = static::option($value, $display, $selected); + if (is_array($display)) + { + $html[] = static::optgroup($display, $value, $selected); + } + else + { + $html[] = static::option($value, $display, $selected); + } } return ''.implode('', $html).''; } + /** + * Create a HTML select element optgroup. + * + * @param array $options + * @param string $label + * @param string $selected + * @return string + */ + protected static function optgroup($options, $label, $selected) + { + $html = array(); + + foreach ($options as $value => $display) + { + $html[] = static::option($value, $display, $selected); + } + + return ''.implode('', $html).''; + } + /** * Create a HTML select element option. * @@ -419,7 +446,7 @@ protected static function option($value, $display, $selected) } else { - $selected = ($value == $selected) ? 'selected' : null; + $selected = ((string) $value == (string) $selected) ? 'selected' : null; } $attributes = array('value' => HTML::entities($value), 'selected' => $selected); @@ -499,7 +526,7 @@ protected static function checkable($type, $name, $value, $checked, $attributes) * @param array $attributes * @return string */ - public static function submit($value, $attributes = array()) + public static function submit($value = null, $attributes = array()) { return static::input('submit', null, $value, $attributes); } @@ -511,7 +538,7 @@ public static function submit($value, $attributes = array()) * @param array $attributes * @return string */ - public static function reset($value, $attributes = array()) + public static function reset($value = null, $attributes = array()) { return static::input('reset', null, $value, $attributes); } @@ -543,7 +570,7 @@ public static function image($url, $name = null, $attributes = array()) * @param array $attributes * @return string */ - public static function button($value, $attributes = array()) + public static function button($value = null, $attributes = array()) { return ''.HTML::entities($value).''; } @@ -580,12 +607,12 @@ protected static function id($name, $attributes) */ public static function __callStatic($method, $parameters) { - if (isset(static::$macros[$method])) - { - return call_user_func_array(static::$macros[$method], $parameters); - } - - throw new \Exception("Method [$method] does not exist."); + if (isset(static::$macros[$method])) + { + return call_user_func_array(static::$macros[$method], $parameters); + } + + throw new \Exception("Method [$method] does not exist."); } } diff --git a/app/laravel/helpers.php b/app/laravel/helpers.php index b04a8c490..513e3baae 100644 --- a/app/laravel/helpers.php +++ b/app/laravel/helpers.php @@ -10,7 +10,7 @@ */ function e($value) { - return Laravel\HTML::entities($value); + return HTML::entities($value); } /** @@ -23,7 +23,21 @@ function e($value) */ function __($key, $replacements = array(), $language = null) { - return Laravel\Lang::line($key, $replacements, $language); + return Lang::line($key, $replacements, $language); +} + +/** + * Dump the given value and kill the script. + * + * @param mixed $value + * @return void + */ +function dd($value) +{ + echo "
    ";
    +	var_dump($value);
    +	echo "
    "; + die; } /** @@ -218,6 +232,62 @@ function array_divide($array) return array(array_keys($array), array_values($array)); } +/** + * Pluck an array of values from an array. + * + * @param array $array + * @param string $key + * @return array + */ +function array_pluck($array, $key) +{ + return array_map(function($v) use ($key) + { + return is_object($v) ? $v->$key : $v[$key]; + + }, $array); +} + +/** + * Get a subset of the items from the given array. + * + * @param array $array + * @param array $keys + * @return array + */ +function array_only($array, $keys) +{ + return array_intersect_key( $array, array_flip((array) $keys) ); +} + +/** + * Get all of the given array except for a specified array of items. + * + * @param array $array + * @param array $keys + * @return array + */ +function array_except($array, $keys) +{ + return array_diff_key( $array, array_flip((array) $keys) ); +} + +/** + * Transform Eloquent models to a JSON object. + * + * @param Eloquent|array $models + * @return object + */ +function eloquent_to_json($models) +{ + if ($models instanceof Laravel\Database\Eloquent\Model) + { + return json_encode($models->to_array()); + } + + return json_encode(array_map(function($m) { return $m->to_array(); }, $models)); +} + /** * Determine if "Magic Quotes" are enabled on the server. * @@ -246,17 +316,17 @@ function head($array) * * * // Create a URL to a location within the application - * $url = path('user/profile'); + * $url = url('user/profile'); * * // Create a HTTPS URL to a location within the application - * $url = path('user/profile', true); + * $url = url('user/profile', true); * * * @param string $url * @param bool $https * @return string */ -function url($url = '', $https = false) +function url($url = '', $https = null) { return Laravel\URL::to($url, $https); } @@ -268,7 +338,7 @@ function url($url = '', $https = false) * @param bool $https * @return string */ -function asset($url, $https = false) +function asset($url, $https = null) { return Laravel\URL::to_asset($url, $https); } @@ -340,13 +410,18 @@ function ends_with($haystack, $needle) /** * Determine if a given string contains a given sub-string. * - * @param string $haystack - * @param string $needle + * @param string $haystack + * @param string|array $needle * @return bool */ function str_contains($haystack, $needle) { - return strpos($haystack, $needle) !== false; + foreach ((array) $needle as $n) + { + if (strpos($haystack, $n) !== false) return true; + } + + return false; } /** @@ -361,6 +436,17 @@ function str_finish($value, $cap) return rtrim($value, $cap).$cap; } +/** + * Determine if the given object has a toString method. + * + * @param object $value + * @return bool + */ +function str_object($value) +{ + return is_object($value) and method_exists($value, '__toString'); +} + /** * Get the root namespace of a given class. * @@ -379,7 +465,7 @@ function root_namespace($class, $separator = '\\') /** * Get the "class basename" of a class or object. * - * The basename is considered the name of the class minus all namespaces. + * The basename is considered to be the name of the class minus all namespaces. * * @param object|string $class * @return string @@ -401,7 +487,7 @@ function class_basename($class) */ function value($value) { - return ($value instanceof Closure) ? call_user_func($value) : $value; + return (is_callable($value) and ! is_string($value)) ? call_user_func($value) : $value; } /** @@ -457,7 +543,7 @@ function render($view, $data = array()) /** * Get the rendered contents of a partial from a loop. * - * @param string $view + * @param string $partial * @param array $data * @param string $iterator * @param string $empty @@ -477,4 +563,36 @@ function render_each($partial, array $data, $iterator, $empty = 'raw|') function yield($section) { return Laravel\Section::yield($section); +} + +/** + * Get a CLI option from the argv $_SERVER variable. + * + * @param string $option + * @param mixed $default + * @return string + */ +function get_cli_option($option, $default = null) +{ + foreach (Laravel\Request::foundation()->server->get('argv') as $argument) + { + if (starts_with($argument, "--{$option}=")) + { + return substr($argument, strlen($option) + 3); + } + } + + return value($default); +} + +/** + * Calculate the human-readable file size (with proper units). + * + * @param int $size + * @return string + */ +function get_file_size($size) +{ + $units = array('Bytes', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB'); + return @round($size / pow(1024, ($i = floor(log($size, 1024)))), 2).' '.$units[$i]; } \ No newline at end of file diff --git a/app/laravel/html.php b/app/laravel/html.php index d1f199aaf..33d91dd65 100644 --- a/app/laravel/html.php +++ b/app/laravel/html.php @@ -9,13 +9,13 @@ class HTML { */ public static $macros = array(); - /** - * Registers a custom macro. - * - * @param string $name - * @param Closure $input - * @return void - */ + /** + * Registers a custom macro. + * + * @param string $name + * @param Closure $macro + * @return void + */ public static function macro($name, $macro) { static::$macros[$name] = $macro; @@ -45,6 +45,19 @@ public static function decode($value) return html_entity_decode($value, ENT_QUOTES, Config::get('application.encoding')); } + /** + * Convert HTML special characters. + * + * The encoding specified in the application configuration file will be used. + * + * @param string $value + * @return string + */ + public static function specialchars($value) + { + return htmlspecialchars($value, ENT_QUOTES, Config::get('application.encoding'), false); + } + /** * Generate a link to a JavaScript file. * @@ -124,10 +137,12 @@ public static function span($value, $attributes = array()) * @param bool $https * @return string */ - public static function link($url, $title, $attributes = array(), $https = false) + public static function link($url, $title = null, $attributes = array(), $https = null) { $url = URL::to($url, $https); + if (is_null($title)) $title = $url; + return ''.static::entities($title).''; } @@ -139,7 +154,7 @@ public static function link($url, $title, $attributes = array(), $https = false) * @param array $attributes * @return string */ - public static function link_to_secure($url, $title, $attributes = array()) + public static function link_to_secure($url, $title = null, $attributes = array()) { return static::link($url, $title, $attributes, true); } @@ -155,9 +170,11 @@ public static function link_to_secure($url, $title, $attributes = array()) * @param bool $https * @return string */ - public static function link_to_asset($url, $title, $attributes = array(), $https = null) + public static function link_to_asset($url, $title = null, $attributes = array(), $https = null) { $url = URL::to_asset($url, $https); + + if (is_null($title)) $title = $url; return ''.static::entities($title).''; } @@ -170,7 +187,7 @@ public static function link_to_asset($url, $title, $attributes = array(), $https * @param array $attributes * @return string */ - public static function link_to_secure_asset($url, $title, $attributes = array()) + public static function link_to_secure_asset($url, $title = null, $attributes = array()) { return static::link_to_asset($url, $title, $attributes, true); } @@ -194,7 +211,7 @@ public static function link_to_secure_asset($url, $title, $attributes = array()) * @param array $attributes * @return string */ - public static function link_to_route($name, $title, $parameters = array(), $attributes = array()) + public static function link_to_route($name, $title = null, $parameters = array(), $attributes = array()) { return static::link(URL::to_route($name, $parameters), $title, $attributes); } @@ -218,7 +235,7 @@ public static function link_to_route($name, $title, $parameters = array(), $attr * @param array $attributes * @return string */ - public static function link_to_action($action, $title, $parameters = array(), $attributes = array()) + public static function link_to_action($action, $title = null, $parameters = array(), $attributes = array()) { return static::link(URL::to_action($action, $parameters), $title, $attributes); } @@ -306,6 +323,8 @@ private static function listing($type, $list, $attributes = array()) { $html = ''; + if (count($list) == 0) return $html; + foreach ($list as $key => $value) { // If the value is an array, we will recurse the function so that we can @@ -313,7 +332,14 @@ private static function listing($type, $list, $attributes = array()) // lists may exist within nested lists, etc. if (is_array($value)) { - $html .= static::listing($type, $value); + if (is_int($key)) + { + $html .= static::listing($type, $value); + } + else + { + $html .= '
  • '.$key.static::listing($type, $value).'
  • '; + } } else { @@ -323,6 +349,28 @@ private static function listing($type, $list, $attributes = array()) return '<'.$type.static::attributes($attributes).'>'.$html.''; } + + /** + * Generate a definition list. + * + * @param array $list + * @param array $attributes + * @return string + */ + public static function dl($list, $attributes = array()) + { + $html = ''; + + if (count($list) == 0) return $html; + + foreach ($list as $term => $description) + { + $html .= '
    '.static::entities($term).'
    '; + $html .= '
    '.static::entities($description).'
    '; + } + + return ''.$html.''; + } /** * Build a list of HTML attributes from an array. @@ -337,7 +385,7 @@ public static function attributes($attributes) foreach ((array) $attributes as $key => $value) { // For numeric keys, we will assume that the key and the value are the - // same, as this will conver HTML attributes such as "required" that + // same, as this will convert HTML attributes such as "required" that // may be specified as required="required", etc. if (is_numeric($key)) $key = $value; @@ -396,7 +444,7 @@ public static function __callStatic($method, $parameters) { return call_user_func_array(static::$macros[$method], $parameters); } - + throw new \Exception("Method [$method] does not exist."); } diff --git a/app/laravel/input.php b/app/laravel/input.php index 8524957ee..b76fc7c6a 100644 --- a/app/laravel/input.php +++ b/app/laravel/input.php @@ -3,11 +3,11 @@ class Input { /** - * The applicable input for the request. + * The JSON payload for applications using Backbone.js or similar. * - * @var array + * @var object */ - public static $input; + public static $json; /** * The key used to store old input in the session. @@ -23,7 +23,11 @@ class Input { */ public static function all() { - return array_merge(static::get(), static::file()); + $input = array_merge(static::get(), static::query(), static::file()); + + unset($input[Request::spoofer]); + + return $input; } /** @@ -58,7 +62,54 @@ public static function has($key) */ public static function get($key = null, $default = null) { - return array_get(static::$input, $key, $default); + $input = Request::foundation()->request->all(); + + if (is_null($key)) + { + return array_merge($input, static::query()); + } + + $value = array_get($input, $key); + + if (is_null($value)) + { + return array_get(static::query(), $key, $default); + } + + return $value; + } + + /** + * Get an item from the query string. + * + * + * // Get the "email" item from the query string + * $email = Input::query('email'); + * + * // Return a default value if the specified item doesn't exist + * $email = Input::query('name', 'Taylor'); + * + * + * @param string $key + * @param mixed $default + * @return mixed + */ + public static function query($key = null, $default = null) + { + return array_get(Request::foundation()->query->all(), $key, $default); + } + + /** + * Get the JSON payload for the request. + * + * @param bool $as_array + * @return object + */ + public static function json($as_array = false) + { + if ( ! is_null(static::$json)) return static::$json; + + return static::$json = json_decode(Request::foundation()->getContent(), $as_array); } /** @@ -77,7 +128,7 @@ public static function get($key = null, $default = null) */ public static function only($keys) { - return array_intersect_key(static::get(), array_flip((array) $keys)); + return array_only(static::get(), $keys); } /** @@ -96,7 +147,7 @@ public static function only($keys) */ public static function except($keys) { - return array_diff_key(static::get(), array_flip($keys)); + return array_except(static::get(), $keys); } /** @@ -136,39 +187,48 @@ public static function old($key = null, $default = null) * * // Get the array of information for the "picture" upload * $picture = Input::file('picture'); - * - * // Get a specific element from within the file's data array - * $size = Input::file('picture.size'); * * - * @param string $key - * @param mixed $default - * @return array + * @param string $key + * @param mixed $default + * @return UploadedFile */ public static function file($key = null, $default = null) { return array_get($_FILES, $key, $default); } + /** + * Determine if the uploaded data contains a file. + * + * @param string $key + * @return bool + */ + public static function has_file($key) + { + return strlen(static::file("{$key}.tmp_name", "")) > 0; + } + /** * Move an uploaded file to permanent storage. * * This method is simply a convenient wrapper around move_uploaded_file. * * - * // Move the "picture" file to a permanent location on disk - * Input::upload('picture', 'path/to/photos/picture.jpg'); + * // Move the "picture" file to a new permanent location on disk + * Input::upload('picture', 'path/to/photos', 'picture.jpg'); * * * @param string $key - * @param string $path + * @param string $directory + * @param string $name * @return bool */ - public static function upload($key, $path) + public static function upload($key, $directory, $name = null) { if (is_null(static::file($key))) return false; - return move_uploaded_file(static::file("{$key}.tmp_name"), $path); + return Request::foundation()->files->get($key)->move($directory, $name); } /** @@ -206,4 +266,35 @@ public static function flush() Session::flash(Input::old_input, array()); } + /** + * Merge new input into the current request's input array. + * + * @param array $input + * @return void + */ + public static function merge(array $input) + { + Request::foundation()->request->add($input); + } + + /** + * Replace the input for the current request. + * + * @param array $input + * @return void + */ + public static function replace(array $input) + { + Request::foundation()->request->replace($input); + } + + /** + * Clear the input for the current request. + * @return void + */ + public static function clear() + { + Request::foundation()->request->replace(array()); + } + } \ No newline at end of file diff --git a/app/laravel/ioc.php b/app/laravel/ioc.php index b02280f9f..4347cbcbe 100644 --- a/app/laravel/ioc.php +++ b/app/laravel/ioc.php @@ -20,12 +20,14 @@ class IoC { * Register an object and its resolver. * * @param string $name - * @param Closure $resolver + * @param mixed $resolver * @param bool $singleton * @return void */ - public static function register($name, Closure $resolver, $singleton = false) + public static function register($name, $resolver = null, $singleton = false) { + if (is_null($resolver)) $resolver = $name; + static::$registry[$name] = compact('resolver', 'singleton'); } @@ -49,7 +51,7 @@ public static function registered($name) * @param Closure $resolver * @return void */ - public static function singleton($name, $resolver) + public static function singleton($name, $resolver = null) { static::register($name, $resolver, true); } @@ -72,70 +74,135 @@ public static function instance($name, $instance) } /** - * Register a controller with the IoC container. - * - * @param string $name - * @param Closure $resolver - * @return void - */ - public static function controller($name, $resolver) - { - static::register("controller: {$name}", $resolver); - } - - /** - * Resolve a core Laravel class from the container. + * Resolve a given type to an instance. * * - * // Resolve the "laravel.router" class from the container - * $input = IoC::core('router'); + * // Get an instance of the "mailer" object registered in the container + * $mailer = IoC::resolve('mailer'); * - * // Equivalent resolution of the router using the "resolve" method - * $input = IoC::resolve('laravel.router'); + * // Get an instance of the "mailer" object and pass parameters to the resolver + * $mailer = IoC::resolve('mailer', array('test')); * * - * @param string $name + * @param string $type * @param array $parameters * @return mixed */ - public static function core($name, $parameters = array()) + public static function resolve($type, $parameters = array()) { - return static::resolve("laravel.{$name}", $parameters); + // If an instance of the type is currently being managed as a singleton, we will + // just return the existing instance instead of instantiating a fresh instance + // so the developer can keep re-using the exact same object instance from us. + if (isset(static::$singletons[$type])) + { + return static::$singletons[$type]; + } + + // If we don't have a registered resolver or concrete for the type, we'll just + // assume the type is the concrete name and will attempt to resolve it as is + // since the container should be able to resolve concretes automatically. + if ( ! isset(static::$registry[$type])) + { + $concrete = $type; + } + else + { + $concrete = array_get(static::$registry[$type], 'resolver', $type); + } + + // We're ready to instantiate an instance of the concrete type registered for + // the binding. This will instantiate the type, as well as resolve any of + // its nested dependencies recursively until they are each resolved. + if ($concrete == $type or $concrete instanceof Closure) + { + $object = static::build($concrete, $parameters); + } + else + { + $object = static::resolve($concrete); + } + + // If the requested type is registered as a singleton, we want to cache off + // the instance in memory so we can return it later without creating an + // entirely new instances of the object on each subsequent request. + if (isset(static::$registry[$type]['singleton']) && static::$registry[$type]['singleton'] === true) + { + static::$singletons[$type] = $object; + } + + Event::fire('laravel.resolving', array($type, $object)); + + return $object; } /** - * Resolve an object instance from the container. - * - * - * // Get an instance of the "mailer" object registered in the container - * $mailer = IoC::resolve('mailer'); + * Instantiate an instance of the given type. * - * // Get an instance of the "mailer" object and pass parameters to the resolver - * $mailer = IoC::resolve('mailer', array('test')); - * - * - * @param string $name + * @param string $type * @param array $parameters * @return mixed */ - public static function resolve($name, $parameters = array()) + protected static function build($type, $parameters = array()) { - if (array_key_exists($name, static::$singletons)) + // If the concrete type is actually a Closure, we will just execute it and + // hand back the results of the function, which allows functions to be + // used as resolvers for more fine-tuned resolution of the objects. + if ($type instanceof Closure) { - return static::$singletons[$name]; + return call_user_func_array($type, $parameters); } - $object = call_user_func(static::$registry[$name]['resolver'], $parameters); + $reflector = new \ReflectionClass($type); - // If the resolver is registering as a singleton resolver, we will cache - // the instance of the object in the container so we can resolve it next - // time without having to instantiate a brand new instance. - if (static::$registry[$name]['singleton']) + // If the type is not instantiable, the developer is attempting to resolve + // an abstract type such as an Interface of an Abstract Class and there is + // no binding registered for the abstraction so we need to bail out. + if ( ! $reflector->isInstantiable()) { - return static::$singletons[$name] = $object; + throw new \Exception("Resolution target [$type] is not instantiable."); } - return $object; + $constructor = $reflector->getConstructor(); + + // If there is no constructor, that means there are no dependencies and + // we can just resolve an instance of the object right away without + // resolving any other types or dependencies from the container. + if (is_null($constructor)) + { + return new $type; + } + + $dependencies = static::dependencies($constructor->getParameters()); + + return $reflector->newInstanceArgs($dependencies); + } + + /** + * Resolve all of the dependencies from the ReflectionParameters. + * + * @param array $parameters + * @return array + */ + protected static function dependencies($parameters) + { + $dependencies = array(); + + foreach ($parameters as $parameter) + { + $dependency = $parameter->getClass(); + + // If the class is null, it means the dependency is a string or some other + // primitive type, which we can not resolve since it is not a class and + // we'll just bomb out with an error since we have nowhere to go. + if (is_null($dependency)) + { + throw new \Exception("Unresolvable dependency resolving [$parameter]."); + } + + $dependencies[] = static::resolve($dependency->name); + } + + return (array) $dependencies; } } \ No newline at end of file diff --git a/app/laravel/lang.php b/app/laravel/lang.php index 87be5f3d4..fd3bf1771 100644 --- a/app/laravel/lang.php +++ b/app/laravel/lang.php @@ -1,4 +1,4 @@ -key = $key; $this->language = $language; - $this->replacements = $replacements; + $this->replacements = (array) $replacements; } /** @@ -89,7 +89,7 @@ public static function line($key, $replacements = array(), $language = null) */ public static function has($key, $language = null) { - return ! is_null(static::line($key, array(), $language)->get()); + return static::line($key, array(), $language)->get() !== $key; } /** @@ -103,7 +103,7 @@ public static function has($key, $language = null) * $line = Lang::line('validation.required')->get('sp'); * * // Return a default value if the line doesn't exist - * $line = Lang::line('validation.required', null, 'Default'); + * $line = Lang::line('validation.required')->get(null, 'Default'); * * * @param string $language @@ -112,13 +112,18 @@ public static function has($key, $language = null) */ public function get($language = null, $default = null) { + // If no default value is specified by the developer, we'll just return the + // key of the language line. This should indicate which language line we + // were attempting to render and is better than giving nothing back. + if (is_null($default)) $default = $this->key; + if (is_null($language)) $language = $this->language; list($bundle, $file, $line) = $this->parse($this->key); - // If the file doesn't exist, we'll just return the default value that was + // If the file does not exist, we'll just return the default value that was // given to the method. The default value is also returned even when the - // file exists and the file does not actually contain any lines. + // file exists and that file does not actually contain any lines. if ( ! static::load($bundle, $language, $file)) { return value($default); @@ -129,7 +134,7 @@ public function get($language = null, $default = null) $line = array_get($lines, $line, $default); // If the line is not a string, it probably means the developer asked for - // the entire langauge file and the value of the requested value will be + // the entire language file and the value of the requested value will be // an array containing all of the lines in the file. if (is_string($line)) { @@ -244,4 +249,4 @@ public function __toString() return (string) $this->get(); } -} \ No newline at end of file +} diff --git a/app/laravel/laravel.php b/app/laravel/laravel.php index b54ca5706..0b669ae12 100644 --- a/app/laravel/laravel.php +++ b/app/laravel/laravel.php @@ -1,5 +1,7 @@ $config) { - case 'GET': - $input = $_GET; - break; - - case 'POST': - $input = $_POST; - break; - - default: - if (Request::spoofed()) - { - $input = $_POST; - } - else - { - parse_str(file_get_contents('php://input'), $input); - - if (magic_quotes()) $input = array_strip_slashes($input); - } + if ($config['auto']) Bundle::start($bundle); } /* |-------------------------------------------------------------------------- -| Remove The Spoofer Input +| Register The Catch-All Route |-------------------------------------------------------------------------- | -| The spoofed request method is removed from the input so it is not in -| the Input::all() or Input::get() results. Leaving it in the array -| could cause unexpected results since the developer won't be -| expecting it to be present. +| This route will catch all requests that do not hit another route in +| the application, and will raise the 404 error event so the error +| can be handled by the developer in their 404 event listener. | */ -unset($input[Request::spoofer]); - -Input::$input = $input; +Router::register('*', '(:all)', function() +{ + return Event::first('404'); +}); /* |-------------------------------------------------------------------------- -| Start The Application Bundle +| Gather The URI And Locales |-------------------------------------------------------------------------- | -| The application "bundle" is the default bundle for the installation and -| we'll fire it up first. In this bundle's bootstrap, more configuration -| will take place and the developer can hook into some of the core -| framework events such as the configuration loader. +| 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. | */ -Bundle::start(DEFAULT_BUNDLE); +$uri = URI::current(); + +$languages = Config::get('application.languages', array()); + +$languages[] = Config::get('application.language'); /* |-------------------------------------------------------------------------- -| Auto-Start Other Bundles +| Set The Locale Based On The Route |-------------------------------------------------------------------------- | -| Bundles that are used throughout the application may be auto-started -| so they are immediately available on every request without needing -| to explicitly start them within the application. +| 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 (Bundle::$bundles as $bundle => $config) +foreach ($languages as $language) { - if ($config['auto']) Bundle::start($bundle); + if (preg_match("#^{$language}(?:$|/)#i", $uri)) + { + Config::set('application.language', $language); + + $uri = trim(substr($uri, strlen($language)), '/'); break; + } } -/* -|-------------------------------------------------------------------------- -| Register The Catch-All Route -|-------------------------------------------------------------------------- -| -| This route will catch all requests that do not hit another route in -| the application, and will raise the 404 error event so the error -| can be handled by the developer in their 404 event listener. -| -*/ +if ($uri == '') $uri = '/'; -Routing\Router::register('*', '(:all)', function() -{ - return Event::first('404'); -}); +URI::$uri = $uri; /* |-------------------------------------------------------------------------- @@ -186,40 +162,38 @@ | */ -$uri = URI::current(); - -Request::$route = Routing\Router::route(Request::method(), $uri); +Request::$route = Router::route(Request::method(), $uri); $response = Request::$route->call(); /* |-------------------------------------------------------------------------- -| Persist The Session To Storage +| "Render" The Response |-------------------------------------------------------------------------- | -| If a session driver has been configured, we will save the session to -| storage so it is avaiable for the next request. This will also set -| the session cookie in the cookie jar to be sent to the user. +| The render method evaluates the content of the response and converts it +| to a string. This evaluates any views and sub-responses within the +| content and sets the raw string result as the new response. | */ -if (Config::get('session.driver') !== '') -{ - Session::save(); -} +$response->render(); /* |-------------------------------------------------------------------------- -| Let's Eat Cookies +| Persist The Session To Storage |-------------------------------------------------------------------------- | -| All cookies set during the request are actually stored in a cookie jar -| until the end of the request so they can be expected by unit tests or -| the developer. Here, we'll push them out to the browser. +| If a session driver has been configured, we will save the session to +| storage so it is available for the next request. This will also set +| the session cookie in the cookie jar to be sent to the user. | */ -Cookie::send(); +if (Config::get('session.driver') !== '') +{ + Session::save(); +} /* |-------------------------------------------------------------------------- @@ -240,10 +214,24 @@ | And We're Done! |-------------------------------------------------------------------------- | -| Raise the "done" event so extra output can be attached to the response +| Raise the "done" event so extra output can be attached to the response. | This allows the adding of debug toolbars, etc. to the view, or may be | used to do some kind of logging by the application. | */ -Event::fire('laravel.done', array($response)); \ No newline at end of file +Event::fire('laravel.done', array($response)); + +/* +|-------------------------------------------------------------------------- +| Finish the request for PHP-FastCGI +|-------------------------------------------------------------------------- +| +| Stopping the PHP process for PHP-FastCGI users to speed up some +| PHP queries. Acceleration is possible when there are actions in the +| process of script execution that do not affect server response. +| For example, saving the session in memcached can occur after the page +| has been formed and passed to a web server. +*/ + +$response->foundation->finish(); diff --git a/app/laravel/log.php b/app/laravel/log.php index 4cd3a30e6..f2b2b3f89 100644 --- a/app/laravel/log.php +++ b/app/laravel/log.php @@ -28,7 +28,7 @@ protected static function exception_line($e) * Write a message to the log file. * * - * // Write an "error" messge to the log file + * // Write an "error" message to the log file * Log::write('error', 'Something went horribly wrong!'); * * // Write an "error" message using the class' magic method @@ -49,22 +49,17 @@ public static function write($type, $message) Event::fire('laravel.log', array($type, $message)); } - // If there aren't listeners on the log event, we'll just write to the - // log files using the default conventions, writing one log file per - // day so they files don't get too crowded. - else - { - $message = static::format($type, $message); + $message = static::format($type, $message); - File::append(path('storage').'logs/'.date('Y-m-d').'.log', $message); - } + File::append(path('storage').'logs/'.date('Y-m-d').'.log', $message); } /** * Format a log message for logging. * * @param string $type - * @param + * @param string $message + * @return string */ protected static function format($type, $message) { diff --git a/app/laravel/memcached.php b/app/laravel/memcached.php index 548e29841..d66710bb6 100644 --- a/app/laravel/memcached.php +++ b/app/laravel/memcached.php @@ -35,16 +35,16 @@ public static function connection() /** * Create a new Memcached connection instance. * - * @param array $servers + * @param array $servers * @return Memcached */ protected static function connect($servers) { - $memcache = new \Memcache; + $memcache = new \Memcached; foreach ($servers as $server) { - $memcache->addServer($server['host'], $server['port'], true, $server['weight']); + $memcache->addServer($server['host'], $server['port'], $server['weight']); } if ($memcache->getVersion() === false) @@ -68,7 +68,7 @@ protected static function connect($servers) */ public static function __callStatic($method, $parameters) { - return call_user_func_array(array(static::instance(), $method), $parameters); + return call_user_func_array(array(static::connection(), $method), $parameters); } } \ No newline at end of file diff --git a/app/laravel/messages.php b/app/laravel/messages.php index 5ca75a6a1..bf5d61c3b 100644 --- a/app/laravel/messages.php +++ b/app/laravel/messages.php @@ -9,6 +9,13 @@ class Messages { */ public $messages; + /** + * Default format for message output. + * + * @var string + */ + public $format = ':message'; + /** * Create a new Messages instance. * @@ -68,6 +75,21 @@ public function has($key = null) return $this->first($key) !== ''; } + /** + * Set the default message format for output. + * + * + * // Apply a new default format. + * $messages->format('email', '

    this is my :message

    '); + *
    + * + * @param string $format + */ + public function format($format = ':message') + { + $this->format = $format; + } + /** * Get the first message from the container for a given key. * @@ -86,8 +108,10 @@ public function has($key = null) * @param string $format * @return string */ - public function first($key = null, $format = ':message') + public function first($key = null, $format = null) { + $format = ($format === null) ? $this->format : $format; + $messages = is_null($key) ? $this->all($format) : $this->get($key, $format); return (count($messages) > 0) ? $messages[0] : ''; @@ -108,11 +132,13 @@ public function first($key = null, $format = ':message') * @param string $format * @return array */ - public function get($key, $format = ':message') + public function get($key, $format = null) { + $format = ($format === null) ? $this->format : $format; + if (array_key_exists($key, $this->messages)) { - return $this->format($this->messages[$key], $format); + return $this->transform($this->messages[$key], $format); } return array(); @@ -132,13 +158,15 @@ public function get($key, $format = ':message') * @param string $format * @return array */ - public function all($format = ':message') + public function all($format = null) { + $format = ($format === null) ? $this->format : $format; + $all = array(); foreach ($this->messages as $messages) { - $all = array_merge($all, $this->format($messages, $format)); + $all = array_merge($all, $this->transform($messages, $format)); } return $all; @@ -151,7 +179,7 @@ public function all($format = ':message') * @param string $format * @return array */ - protected function format($messages, $format) + protected function transform($messages, $format) { $messages = (array) $messages; diff --git a/app/laravel/paginator.php b/app/laravel/paginator.php index ceffbe85b..e9b95e359 100644 --- a/app/laravel/paginator.php +++ b/app/laravel/paginator.php @@ -47,7 +47,7 @@ class Paginator { /** * The compiled appendage that will be appended to the links. * - * This consists of a sprintf format with a page place-holder and query string. + * This consists of a sprintf format with a page place-holder and query string. * * @var string */ @@ -65,7 +65,7 @@ class Paginator { * * @var string */ - protected $dots = '...'; + protected $dots = '
  • ...
  • '; /** * Create a new Paginator instance. @@ -183,7 +183,7 @@ public function links($adjacent = 3) $links = $this->slider($adjacent); } - $content = $this->previous().' '.$links.' '.$this->next(); + $content = '
      ' . $this->previous() . $links . $this->next() . '
    '; return ''; } @@ -300,7 +300,7 @@ protected function element($element, $page, $text, $disabled) // the "first" element should be a span instead of a link. if ($disabled($this->page, $this->last)) { - return HTML::span($text, array('class' => "{$class} disabled")); + return '"{$class} disabled")).'>'.$text.''; } else { @@ -349,7 +349,7 @@ protected function range($start, $end) { if ($this->page == $page) { - $pages[] = HTML::span($page, array('class' => 'current')); + $pages[] = '
  • '.$page.'
  • '; } else { @@ -372,7 +372,7 @@ protected function link($page, $text, $class) { $query = '?page='.$page.$this->appendage($this->appends); - return HTML::link(URI::current().$query, $text, compact('class'), Request::secure()); + return ' $class)).'>'. HTML::link(URI::current().$query, $text, array(), Request::secure()).''; } /** diff --git a/app/laravel/pluralizer.php b/app/laravel/pluralizer.php index 857a7ff56..2ce72f0ba 100644 --- a/app/laravel/pluralizer.php +++ b/app/laravel/pluralizer.php @@ -24,6 +24,7 @@ class Pluralizer { /** * Create a new pluralizer instance. * + * @param array $config * @return void */ public function __construct($config) @@ -48,7 +49,7 @@ public function singular($value) } // English words may be automatically inflected using regular expressions. - // If the word is english, we'll just pass off the word to the automatic + // If the word is English, we'll just pass off the word to the automatic // inflection method and return the result, which is cached. $irregular = $this->config['irregular']; @@ -77,7 +78,7 @@ public function plural($value, $count = 2) } // English words may be automatically inflected using regular expressions. - // If the word is english, we'll just pass off the word to the automatic + // If the word is English, we'll just pass off the word to the automatic // inflection method and return the result, which is cached. $irregular = array_flip($this->config['irregular']); @@ -104,7 +105,7 @@ protected function auto($value, $source, $irregular) return $value; } - // Next we will check the "irregular" patterns, which contains words + // Next, we will check the "irregular" patterns, which contain words // like "children" and "teeth" which can not be inflected using the // typically used regular expression matching approach. foreach ($irregular as $irregular => $pattern) diff --git a/app/laravel/profiling/profiler.css b/app/laravel/profiling/profiler.css new file mode 100755 index 000000000..63fc7658f --- /dev/null +++ b/app/laravel/profiling/profiler.css @@ -0,0 +1,225 @@ +.anbu +{ + font-family:Helvetica, "Helvetica Neue", Arial, sans-serif !important; + font-size:14px !important; + background-color:#222 !important; + position:fixed !important; + bottom:0 !important; + right:0 !important; + width:100%; + z-index: 9999 !important; +} + +.anbu-tabs +{ + margin:0 !important; + padding:0 !important; + overflow:hidden !important; + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEEAAAAtCAYAAADxwQZkAAAAAXNSR0IArs4c6QAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9wEBBI7MaEE338AAAsjSURBVGje1Zp/sB51dcY/z713NwGjBhIJSSDASFKhaRTEUXahpChRI/AurUkHsFScprX8tBimTQMFR0upYAajMA7WsdRqqlDdJXQ0thNEsksFLCaBisYSiIRfhiQVUpLdJE//2H1v3oT35t7k3k5vduad98fud3fPc55zznPOviqTSEIhIEMfZhxigszRxlMkvQWYZDxJaCJwlM1RkseDnrF9eZgVqziEN5VJfJbgTuAtwBEDHLfLUAlK6lcF9ACTgBU2l4RZ/tKhCkIf9tkWM0AINoKfAu0wvCLYBLxs+JXgZeNNQpua98lACrxf+Nwqib4apIUPSSZUrXgmYi02FstAfxam+YuDLaySuMdwveBG4GnsM/rOnLJRC+855EDoMX4SezkCWXOwp3QY2nVR2YoI0ny3YBmwxnA80kItvIdygDWjGgRgJ2KpEcYTJF1UtqIxBoI0704fCer9Pwd/XVBhrq6S+B1hmg8I3qgFIcwKsNYK7hfC5gpJE7WfRUGaUyURAEZ3AqvBAr7CfsAbzUwgyPIXgW8gkBhruNyzZ+93YZAWAIRpvtVwO9I24NQqiS875MKhg7qrjB8DEHxy55vL8fvLC51bmOZ/D/5J83VxmcRHDnXtqAChTd0gzZ8UfL9tF/AXQzlBOyywrjHsACbJfKodFmV7///hNtxr9NTZvu0xfdv2hubzZWUrOjIYJNEFaUHZigmy/GGZu4BexO9VSXQGjQwdUYNbURcmFp37jyiTaHrZiqIqiT9UJfEJg4slIMz62fBwmcQPAcdYvBFYBFzrwQxpWyquMySCyZg/rVrRY0FWbCtbcf81hgfA3uepkmia0TTMNOBYwbEWU4FjJKYZHyX0o7IVXRxmxX9VSdw1aasT4TArKFvR+5C+IxiH/UuLd4dp8fxghlStiCArqJL4Y8ZfEdoOXBikeTrCTJCkpcBJ4CNtJjT9zWF1tQLBL+uKxQvgjxj9h+CsIM13dgOip59SWdF+/zfZq5vyd7TQZXuhNVBYNOsNKdYqYCzmmiqJJ44kCGFWGDMXeK/h7aAdmPuAxcAHBb+BfbrtS4CP23xacDrwNQB3oXXP3gkmbvPjs2AkAvC5ZSuaGmSDiyC3IsI034y4GYPhTONW2Yp6qpFMkPJ828h6QfKVQZbPB/7WZkWQ5j8PsmJjmBVbgjTfFWbFTcB9wPyyFf1lmBWvU7U9+5S6drK7F7TONblOQvrwUESQGjYICsSyhpy3Co4M0oKqFY9AJYgJ0uLHSNdZngK6qmzFE2uDc3dzqs2HgQ2IxWUSz2nb2b6fni6NUZvWNzQ/jQHOK1vR0Z0LB84NMUGab7G5S2iTYLzRZxpRNvxwSHOq82OEbxN6EJiLWFAmcW+3Y8skJszyEvNB1Z3y7VUSn9i+n6oVvR6Efm/b/yT0jDGCsySdNSRD1O4v+D5iefP5T6okfs+ICageCNJiG7AQ2Cz4K8Fvd3NS2Ej8IMufBBbYPhFYUiXxEe0b7RmgTa4TEP50u5QaX1S24jcPdn9Bg36Q5jZeavs522B/oWpFPUGad631B7J1hOUjhpuBseDbqlZ0VLfc1Zb4tpdJusXmPGBhlcS9QZp3B2HPRXSf0AYwQucjThmijG6LmJ9I+rrEbkvvtPhkZxc6XNEUpLkxX8b+LmYWYkk7CXQTWGFW2OYGxLeAPzfM75oT+jP93Hdh/LLxHe6vv74a6K2G4MmODHwj6AXZElpQJfH0kWBDWGsSwizfarjO6CXQxVUrvirIir2kdLv8V0l0DPgC2ROMezF3lEk8a1CXVEl8Cma58dTGg7OCNF87tL6iFiZlK7qw6VKN9aUwy0ek02wLvKZ/uEboFsOvhWcHabG6bEWHS5wNmlPrCk9uhspjgd56mqafDQWEMcCtwBXNT1mQ5smB3nCVxAW1aHnW8Idhmq8c6UaqSuL7gdnUivFp8HtAQZdDt2KvNKRCqQbrzsK0oEqiua6bo4k1G3xykBY/be8fYqc3S2h1Ha3+muDyIC1eGQqTuoRaoLp0Hwa8FXgvMMfmVMS4DqN2AdttSsQz2N+VtDxI84e69g6D1P43NOLnPOrpUxpm+QVD9lBdtoT4ou3LJO0GkiDNl+9rtO1+inf83gdMAMbbTJV4N3BG83pTfzKUSmAT9uFI420eQf6S0IogzTcOBLCGGtdVEl+KWYoYZ7uSOC1IizUDeWuAJmu60QOIyYKHgA8Fab5lgOtOAabZPl7SSYZTgVNlT0FqGw2wCfyUzXpJa5qyOQtzK+LHgnlBmq/fH7P6hupN429Kuhp4u0QvaFHTJR6A5ucpweeBm8GnY11C/Z2m0ZppmCl4GzDdMB3pBGwk4focBtYirQGvAX4KejLM8l90AJgjvxN0oeFzVSu6KMiK7QMOjodK51pixtcabpLos/0cUitM80c7s/T+2uymTE0C3W04E3uzpFuBd2COd/1E62iJMW564uYGXzY8iL1K9Yj/WcTzYVps3U/lmAl8EzhZ0vVBmn9moPvUgZSiMonHCJ4FTwTZ5o4wy68YKKF2GcWd0pSrjwEzXHv4tc5ZQO11AD1qewVopeR1hleFXgnSfOe+472gc7LUce06hL0UcbjN2WFWPNAtJA5YulWt6EZLN9QxyRNCFwdpvrrT2x20nGGYI3i/zemSx9n0IPWpJrYRat42A98z3Cv4gfFWWTuRdwVDrEDdJlBVEv+D7Y9I+hUwI0jz/24z+6BBKFvRGyRtsR2otmMR8FlEH/XzyXME52DONkzcQ2m3L7cLswuxsxEtPTY/C7P8bSOsGdoJ/Y3Ao8AM4F+DNJ+zb4j2HOiJw6zYZlgq1dZJulRwj9AvZJ4WfBl7PuoEgFdBzxseB76K+H3wccb/2TRy06okvnIkQWgPiIM0fwV7AfA/ht8pW/Gi9iSsLf91kCgfY1gv3FdXqravTc0OdgqeA54H1hn+XbAqSPPV+7DqAkn/DJbR44IPBGm+sVtojQAjFtm+CelF1VXt/vb+noM5qWGj4K/duLFB4CWhH9r+O+FrgUuBuUGa/0GY5re3AejsQsOs+A5mZe0LnwT88R4vjdCDG/cz428kfUv2JNufqpJo6kHnhI7kMw6cIKZhXgTWS1pvsyHM8l37y+D7VJzfFDzeiJ+nDL8bDpBoD8xZEaC9KkHVit+FvAoUGj6HvTjMih3DbuzLVtQTZsXu14/ozGBZvYOqnwdfVUtyL5FYHKTF9uHQv+O7gHNt5gmfhnQiENTF2Odj/qXvIA3v79P3BWAoA9l+pjbDD+MlQpcYxku6EvOPwGMH6vkgLfqvXSbxKYIFhrnYEyQOA/V2XHu3JA8rHEa4nPUBnzC+pf57gO+VNC9I8/IAnTNF0keBjwLTuxyyG1gHfAFYFqT55lEDQgPEW4EUe6brEdwHgjRf0fFk63U0t32YpCMw51j8kSB2u8+o3214DfMq8nKsO8Msf7hz+iWPIhAawz5hswQsSeuxTw6yvXNDlURvsjVZ8m8ZzQOfDxqrPTwHaQf2c6AnkL+N9Y0gy3cMlDf6RonxBGmO4W6JeaDI+ARJi4Hrm3A5GTMLmC08x3Cs3LixEaO2NwitNaxCui9M88cHmVYzqsKhA5AF4NuMDhdss32zpOOaUdnMLul1O+iHxg/IPIT0SJDmr3abQw7yqOT/f9szyot7gZXGZ8pS5x26f5wggCfA9xh+IFgXpMXGznNpH41wSIDQWeaqVnya8Y8k9fSPdCyQfy10N/guW+sktgTpnlgfxgOt0bQ1fw3M8kcRC4Bdxq8B35OYJ+s42x+3eTDM8hfaAAz37zr/C3vl1F5cIAPZAAAAAElFTkSuQmCC); + background-repeat:no-repeat; + background-position:5px -8px; +} + +.anbu-tab { + cursor: pointer; +} + +.anbu-hidden .anbu-tabs +{ + background-image:none; +} + +#anbu-open-tabs li:first-child +{ + margin-left:6em; +} + +.anbu-tabs li +{ + display:inline; + line-height:1em !important; +} + +.anbu-tabs a, .anbu-tabs a:visited +{ + color:#aaa !important; + text-transform:uppercase !important; + font-weight:bold !important; + display:inline-block; + text-decoration:none !important; + font-size:0.8em !important; + padding: 0.8em 2em 0.7em 2em !important; + -webkit-transition-property:color, background-color; + -webkit-transition-duration: 0.7s, 0.2s; + -webkit-transition-timing-function: ease-in, ease-in; + -moz-transition-property:color, background-color; + -moz-transition-duration: 0.7s, 0.2s; + -moz-transition-timing-function: ease-in, ease-in; + -ms-transition-property:color, background-color; + -ms-transition-duration: 0.7s, 0.2s; + -ms-transition-timing-function: ease-in, ease-in; + -o-transition-property:color, background-color; + -o-transition-duration: 0.7s, 0.2s; + -o-transition-timing-function: ease-in, ease-in; + transition-property:color, background-color; + transition-duration: 0.7s, 0.2s; + transition-timing-function: ease-in, ease-in; +} + +#anbu-closed-tabs a, #anbu-closed-tabs a:visited +{ + padding: 0.85em 1.2em 0.85em 1.2em !important; +} + +.anbu-tabs a:hover +{ + background-color:#333 !important; + color:#fff !important; +} + +.anbu-tabs a.anbu-active-tab +{ + color:#fff !important; + background-color:#333 !important; +} + +.anbu a:focus +{ + outline:none !important; +} + +.anbu-tabs a:active +{ + background-color:#111 !important; +} + +.anbu-tabs li.anbu-tab-right +{ + float:right !important; +} + +.anbu-tabs li.anbu-tab-right a, .anbu-tabs li.anbu-tab-right a:visited +{ + padding: 0.86em 2em 0.7em 2em !important; +} + +#anbu-closed-tabs +{ + display:none; +} + + +.anbu-window +{ + display:none; +} + +.anbu-content-area +{ + background-color: #fff !important; + background-image: -webkit-gradient(linear, left top, left bottom, from(#eeeeee), to(#ffffff)); + background-image: -webkit-linear-gradient(top, #eeeeee, #ffffff); + background-image: -moz-linear-gradient(top, #eeeeee, #ffffff); + background-image: -ms-linear-gradient(top, #eeeeee, #ffffff); + background-image: -o-linear-gradient(top, #eeeeee, #ffffff); + background-image: linear-gradient(to bottom, #eeeeee, #ffffff); + height:14em; + margin-top:6px !important; + overflow-x:hidden !important; + overflow-y:auto !important; +} + +.anbu-table table +{ + margin:0 !important; + padding:0 !important; + font-size:0.9em !important; + border:0 !important; + border-collapse:collapse !important; + width:100% !important; + background-color:#fff !important; +} + +.anbu-table pre +{ + margin:0 !important; +} + +.anbu-table tr +{ + border-bottom:1px solid #ccc !important; +} + +.anbu-table tr:first-child +{ + border:0 !important; +} + +.anbu-table th +{ + background-color:#555 !important; + color:#fff !important; + text-transform:uppercase !important; +} + +.anbu-table th, .anbu-table td +{ + text-align:left !important; + padding:0.4em 1em !important; + margin:0 !important; +} + +.anbu-table td +{ + vertical-align:top !important; +} + +.anbu-table-first +{ + background-color:#eee !important; + border-right:1px solid #ccc !important; + width:10% !important; +} + +span.anbu-count +{ + text-transform: none !important; + margin-left:0.5em !important; + background-color:#555 !important; + display:inline-block !important; + padding:0.1em 0.5em 0.2em 0.5em !important; + color:#eee !important; + text-shadow:0 0 4px #000 !important; + -webkit-border-radius: 1px; + -moz-border-radius: 1px; + border-radius: 1px; + -moz-background-clip: padding; -webkit-background-clip: padding-box; background-clip: padding-box; + +} + +.anbu-empty +{ + display:block !important; + padding:1em !important; + text-align:center !important; + font-style:italic !important; + color:#ccc !important; + margin:1em !important; + text-shadow:0 1px 0px #fff !important; +} + +.anbu pre +{ + overflow-x: auto; + white-space: pre-wrap; + white-space: -moz-pre-wrap !important; + white-space: -pre-wrap; + white-space: -o-pre-wrap; + word-wrap: break-word; +} + +/* hide panel-open elements, will become visible through anbu.start() */ + +#anbu-close, #anbu-zoom, .anbu-tab-pane { + visibility: hidden; +} diff --git a/app/laravel/profiling/profiler.js b/app/laravel/profiling/profiler.js new file mode 100755 index 000000000..8fc9fe3e6 --- /dev/null +++ b/app/laravel/profiling/profiler.js @@ -0,0 +1,194 @@ +var anbu = { + + // BOUND ELEMENTS + // ------------------------------------------------------------- + // Binding these elements early, stops jQuery from "querying" + // the DOM every time they are used. + + el: { + main: jQuery('.anbu'), + close: jQuery('#anbu-close'), + zoom: jQuery('#anbu-zoom'), + hide: jQuery('#anbu-hide'), + show: jQuery('#anbu-show'), + tab_pane: jQuery('.anbu-tab-pane'), + hidden_tab_pane: jQuery('.anbu-tab-pane:visible'), + tab: jQuery('.anbu-tab'), + tabs: jQuery('.anbu-tabs'), + tab_links: jQuery('.anbu-tabs a'), + window: jQuery('.anbu-window'), + closed_tabs: jQuery('#anbu-closed-tabs'), + open_tabs: jQuery('#anbu-open-tabs'), + content_area: jQuery('.anbu-content-area') + }, + + // CLASS ATTRIBUTES + // ------------------------------------------------------------- + // Useful variable for Anbu. + + // is anbu in full screen mode + is_zoomed: false, + + // initial height of content area + small_height: jQuery('.anbu-content-area').height(), + + // the name of the active tab css + active_tab: 'anbu-active-tab', + + // the data attribute of the tab link + tab_data: 'data-anbu-tab', + + // size of anbu when compact + mini_button_width: '2.6em', + + // is the top window open? + window_open: false, + + // current active pane + active_pane: '', + + // START() + // ------------------------------------------------------------- + // Sets up all the binds for Anbu! + + start: function() { + + // hide initial elements + anbu.el.close.css('visibility', 'visible').hide(); + anbu.el.zoom.css('visibility', 'visible').hide(); + anbu.el.tab_pane.css('visibility', 'visible').hide(); + + // bind all click events + anbu.el.close.click(function(event) { + anbu.close_window(); + event.preventDefault(); + }); + anbu.el.hide.click(function(event) { + anbu.hide(); + event.preventDefault(); + }); + anbu.el.show.click(function(event) { + anbu.show(); + event.preventDefault(); + }); + anbu.el.zoom.click(function(event) { + anbu.zoom(); + event.preventDefault(); + }); + anbu.el.tab.click(function(event) { + anbu.clicked_tab(jQuery(this)); + event.preventDefault(); + }); + + }, + + // CLICKED_TAB() + // ------------------------------------------------------------- + // A tab has been clicked, decide what to do. + + clicked_tab: function(tab) { + + // if the tab is closed + if (anbu.window_open && anbu.active_pane == tab.attr(anbu.tab_data)) { + anbu.close_window(); + } else { + anbu.open_window(tab); + } + + }, + + // OPEN_WINDOW() + // ------------------------------------------------------------- + // Animate open the top window to the appropriate tab. + + open_window: function(tab) { + + // can't directly assign this line, but it works + jQuery('.anbu-tab-pane:visible').fadeOut(200); + jQuery('.' + tab.attr(anbu.tab_data)).delay(220).fadeIn(300); + anbu.el.tab_links.removeClass(anbu.active_tab); + tab.addClass(anbu.active_tab); + anbu.el.window.slideDown(300); + anbu.el.close.fadeIn(300); + anbu.el.zoom.fadeIn(300); + anbu.active_pane = tab.attr(anbu.tab_data); + anbu.window_open = true; + + }, + + // CLOSE_WINDOW() + // ------------------------------------------------------------- + // Animate closed the top window hiding all tabs. + + close_window: function() { + + anbu.el.tab_pane.fadeOut(100); + anbu.el.window.slideUp(300); + anbu.el.close.fadeOut(300); + anbu.el.zoom.fadeOut(300); + anbu.el.tab_links.removeClass(anbu.active_tab); + anbu.active_pane = ''; + anbu.window_open = false; + + }, + + // SHOW() + // ------------------------------------------------------------- + // Show the Anbu toolbar when it has been compacted. + + show: function() { + + anbu.el.closed_tabs.fadeOut(600, function () { + anbu.el.main.removeClass('anbu-hidden'); + anbu.el.open_tabs.fadeIn(200); + }); + anbu.el.main.animate({width: '100%'}, 700); + + }, + + // HIDE() + // ------------------------------------------------------------- + // Hide the anbu toolbar, show a tiny re-open button. + + hide: function() { + + anbu.close_window(); + + setTimeout(function() { + anbu.el.window.slideUp(400, function () { + anbu.close_window(); + anbu.el.main.addClass('anbu-hidden'); + anbu.el.open_tabs.fadeOut(200, function () { + anbu.el.closed_tabs.fadeIn(200); + }); + anbu.el.main.animate({width: anbu.mini_button_width}, 700); + }); + }, 100); + + }, + + // TOGGLEZOOM() + // ------------------------------------------------------------- + // Toggle the zoomed mode of the top window. + + zoom: function() { + + if (anbu.is_zoomed) { + height = anbu.small_height; + anbu.is_zoomed = false; + } else { + // the 6px is padding on the top of the window + height = (jQuery(window).height() - anbu.el.tabs.height() - 6) + 'px'; + anbu.is_zoomed = true; + } + + anbu.el.content_area.animate({height: height}, 700); + + } + +}; + +// launch anbu on jquery dom ready +jQuery(document).ready(function() { + anbu.start(); +}); \ No newline at end of file diff --git a/app/laravel/profiling/profiler.php b/app/laravel/profiling/profiler.php new file mode 100644 index 000000000..a26396eeb --- /dev/null +++ b/app/laravel/profiling/profiler.php @@ -0,0 +1,186 @@ + array(), 'logs' => array(), 'timers' => array()); + + /** + * Get the rendered contents of the Profiler. + * + * @param Response $response + * @return string + */ + public static function render($response) + { + // We only want to send the profiler toolbar if the request is not an AJAX + // request, as sending it on AJAX requests could mess up JSON driven API + // type applications, so we will not send anything in those scenarios. + if ( ! Request::ajax() and Config::get('application.profiler') ) + { + 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. + * + * @param string $type + * @param string $message + * @return void + */ + public static function log($type, $message) + { + static::$data['logs'][] = array($type, $message); + } + + /** + * Add a performed SQL query to the Profiler. + * + * @param string $sql + * @param array $bindings + * @param float $time + * @return void + */ + public static function query($sql, $bindings, $time) + { + foreach ($bindings as $binding) + { + $binding = Database::connection()->pdo->quote($binding); + + $sql = preg_replace('/\?/', $binding, $sql, 1); + $sql = htmlspecialchars($sql); + } + + static::$data['queries'][] = array($sql, $time); + } + + /** + * Attach the Profiler's event listeners. + * + * @return void + */ + public static function attach() + { + // First we'll attach to the query and log events. These allow us to catch + // all of the SQL queries and log messages that come through Laravel, + // and we will pass them onto the Profiler for simple storage. + Event::listen('laravel.log', function($type, $message) + { + Profiler::log($type, $message); + }); + + Event::listen('laravel.query', function($sql, $bindings, $time) + { + Profiler::query($sql, $bindings, $time); + }); + + // We'll attach the profiler to the "done" event so that we can easily + // attach the profiler output to the end of the output sent to the + // browser. This will display the profiler's nice toolbar. + Event::listen('laravel.done', function($response) + { + echo Profiler::render($response); + }); + } + +} diff --git a/app/laravel/profiling/template.blade.php b/app/laravel/profiling/template.blade.php new file mode 100755 index 000000000..9c855b43e --- /dev/null +++ b/app/laravel/profiling/template.blade.php @@ -0,0 +1,124 @@ + + +
    +
    +
    +
    + @if (count($logs) > 0) + + + + + + @foreach ($logs as $log) + + + + @endforeach + +
    TypeMessage
    + {{ $log[0] }} + + {{ $log[1] }} +
    + @else + There are no log entries. + @endif +
    + +
    + @if (count($queries) > 0) + + + + + + @foreach ($queries as $query) + + + + + @endforeach +
    TimeQuery
    + {{ $query[1] }}ms + +
    {{ $query[0] }}
    +
    + @else + 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 +
    +
    +
    + + + + +
    + + + + diff --git a/app/laravel/redirect.php b/app/laravel/redirect.php index 2678f4458..874cb1726 100644 --- a/app/laravel/redirect.php +++ b/app/laravel/redirect.php @@ -6,10 +6,10 @@ class Redirect extends Response { * Create a redirect response to application root. * * @param int $status - * @param bool $secure + * @param bool $https * @return Redirect */ - public static function home($status = 302, $https = false) + public static function home($status = 302, $https = null) { return static::to(URL::home($https), $status); } @@ -41,7 +41,7 @@ public static function back($status = 302) * @param bool $https * @return Redirect */ - public static function to($url, $status = 302, $https = false) + public static function to($url, $status = 302, $https = null) { return static::make('', $status)->header('Location', URL::to($url, $https)); } @@ -165,4 +165,23 @@ public function with_errors($container) return $this->with('errors', $errors); } + /** + * Send the headers and content of the response to the browser. + * + * @return void + */ + public function send() + { + // Dump all output buffering, this ensures + // that symphony will send our redirect headers + // properly if we've outputted any content from + // within Laravel. + while (ob_get_level() > 0) + { + ob_end_clean(); + } + + return parent::send(); + } + } \ No newline at end of file diff --git a/app/laravel/redis.php b/app/laravel/redis.php index d00b2f528..02267d322 100644 --- a/app/laravel/redis.php +++ b/app/laravel/redis.php @@ -17,7 +17,7 @@ class Redis { protected $port; /** - * The databse number the connection selects on load. + * The database number the connection selects on load. * * @var int */ diff --git a/app/laravel/request.php b/app/laravel/request.php index 59d0ec7e7..29340d71c 100644 --- a/app/laravel/request.php +++ b/app/laravel/request.php @@ -1,4 +1,4 @@ -getMethod(); - return (static::spoofed()) ? $_POST[Request::spoofer] : $_SERVER['REQUEST_METHOD']; + return ($method == 'HEAD') ? 'GET' : $method; + } + + /** + * Get a header from the request. + * + * + * // Get a header from the request + * $referer = Request::header('referer'); + * + * + * @param string $key + * @param mixed $default + * @return mixed + */ + public static function header($key, $default = null) + { + return array_get(static::foundation()->headers->all(), $key, $default); + } + + /** + * Get all of the HTTP request headers. + * + * @return array + */ + public static function headers() + { + return static::foundation()->headers->all(); } /** @@ -50,7 +81,7 @@ public static function method() */ public static function server($key = null, $default = null) { - return array_get($_SERVER, strtoupper($key), $default); + return array_get(static::foundation()->server->all(), strtoupper($key), $default); } /** @@ -60,7 +91,7 @@ public static function server($key = null, $default = null) */ public static function spoofed() { - return is_array($_POST) and array_key_exists(Request::spoofer, $_POST); + return ! is_null(static::foundation()->get(Request::spoofer)); } /** @@ -71,30 +102,39 @@ public static function spoofed() */ public static function ip($default = '0.0.0.0') { - if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) - { - return $_SERVER['HTTP_X_FORWARDED_FOR']; - } - elseif (isset($_SERVER['HTTP_CLIENT_IP'])) - { - return $_SERVER['HTTP_CLIENT_IP']; - } - elseif (isset($_SERVER['REMOTE_ADDR'])) - { - return $_SERVER['REMOTE_ADDR']; - } + $client_ip = static::foundation()->getClientIp(); + return $client_ip === NULL ? $default : $client_ip; + } - return value($default); + /** + * Get the list of acceptable content types for the request. + * + * @return array + */ + public static function accept() + { + return static::foundation()->getAcceptableContentTypes(); } /** - * Get the HTTP protocol for the request. + * Determine if the request accepts a given content type. * - * @return string + * @param string $type + * @return bool + */ + public static function accepts($type) + { + return in_array($type, static::accept()); + } + + /** + * Get the languages accepted by the client's browser. + * + * @return array */ - public static function protocol() + public static function languages() { - return array_get($_SERVER, 'SERVER_PROTOCOL', 'HTTP/1.1'); + return static::foundation()->getLanguages(); } /** @@ -104,7 +144,7 @@ public static function protocol() */ public static function secure() { - return isset($_SERVER['HTTPS']) and strtolower($_SERVER['HTTPS']) !== 'off'; + return static::foundation()->isSecure() and Config::get('application.ssl'); } /** @@ -126,9 +166,7 @@ public static function forged() */ public static function ajax() { - if ( ! isset($_SERVER['HTTP_X_REQUESTED_WITH'])) return false; - - return strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest'; + return static::foundation()->isXmlHttpRequest(); } /** @@ -138,7 +176,17 @@ public static function ajax() */ public static function referrer() { - return array_get($_SERVER, 'HTTP_REFERER'); + return static::foundation()->headers->get('referer'); + } + + /** + * Get the timestamp of the time when the request was started. + * + * @return int + */ + public static function time() + { + return (int) LARAVEL_START; } /** @@ -158,7 +206,18 @@ public static function cli() */ public static function env() { - if (isset($_SERVER['LARAVEL_ENV'])) return $_SERVER['LARAVEL_ENV']; + return static::foundation()->server->get('LARAVEL_ENV'); + } + + /** + * Set the Laravel environment for the current request. + * + * @param string $env + * @return void + */ + public static function set_env($env) + { + static::foundation()->server->set('LARAVEL_ENV', $env); } /** @@ -172,6 +231,30 @@ public static function is_env($env) return static::env() === $env; } + /** + * Detect the current environment from an environment configuration. + * + * @param array $environments + * @param string $uri + * @return string|null + */ + public static function detect_env(array $environments, $uri) + { + foreach ($environments as $environment => $patterns) + { + // Essentially we just want to loop through each environment pattern + // and determine if the current URI matches the pattern and if so + // we will simply return the environment for that URI pattern. + foreach ($patterns as $pattern) + { + if (Str::is($pattern, $uri) or $pattern == gethostname()) + { + return $environment; + } + } + } + } + /** * Get the main route handling the request. * @@ -182,4 +265,26 @@ public static function route() return static::$route; } + /** + * Get the Symfony HttpFoundation Request instance. + * + * @return HttpFoundation\Request + */ + public static function foundation() + { + return static::$foundation; + } + + /** + * Pass any other methods to the Symfony request. + * + * @param string $method + * @param array $parameters + * @return mixed + */ + public static function __callStatic($method, $parameters) + { + return call_user_func_array(array(static::foundation(), $method), $parameters); + } + } \ No newline at end of file diff --git a/app/laravel/response.php b/app/laravel/response.php index c98414037..ccd53e1fb 100644 --- a/app/laravel/response.php +++ b/app/laravel/response.php @@ -1,5 +1,8 @@ 'Continue', - 101 => 'Switching Protocols', - 200 => 'OK', - 201 => 'Created', - 202 => 'Accepted', - 203 => 'Non-Authoritative Information', - 204 => 'No Content', - 205 => 'Reset Content', - 206 => 'Partial Content', - 207 => 'Multi-Status', - 300 => 'Multiple Choices', - 301 => 'Moved Permanently', - 302 => 'Found', - 303 => 'See Other', - 304 => 'Not Modified', - 305 => 'Use Proxy', - 307 => 'Temporary Redirect', - 400 => 'Bad Request', - 401 => 'Unauthorized', - 402 => 'Payment Required', - 403 => 'Forbidden', - 404 => 'Not Found', - 405 => 'Method Not Allowed', - 406 => 'Not Acceptable', - 407 => 'Proxy Authentication Required', - 408 => 'Request Timeout', - 409 => 'Conflict', - 410 => 'Gone', - 411 => 'Length Required', - 412 => 'Precondition Failed', - 413 => 'Request Entity Too Large', - 414 => 'Request-URI Too Long', - 415 => 'Unsupported Media Type', - 416 => 'Requested Range Not Satisfiable', - 417 => 'Expectation Failed', - 422 => 'Unprocessable Entity', - 423 => 'Locked', - 424 => 'Failed Dependency', - 500 => 'Internal Server Error', - 501 => 'Not Implemented', - 502 => 'Bad Gateway', - 503 => 'Service Unavailable', - 504 => 'Gateway Timeout', - 505 => 'HTTP Version Not Supported', - 507 => 'Insufficient Storage', - 509 => 'Bandwidth Limit Exceeded' - ); + public $foundation; /** * Create a new response instance. @@ -87,9 +29,9 @@ class Response { */ public function __construct($content, $status = 200, $headers = array()) { - $this->status = $status; $this->content = $content; - $this->headers = array_change_key_case($headers); + + $this->foundation = new FoundationResponse('', $status, $headers); } /** @@ -136,6 +78,46 @@ public static function view($view, $data = array()) return new static(View::make($view, $data)); } + /** + * Create a new JSON response. + * + * + * // Create a response instance with JSON + * return Response::json($data, 200, array('header' => 'value')); + * + * + * @param mixed $data + * @param int $status + * @param array $headers + * @return Response + */ + public static function json($data, $status = 200, $headers = array()) + { + $headers['Content-Type'] = 'application/json; charset=utf-8'; + + return new static(json_encode($data), $status, $headers); + } + + /** + * Create a new response of JSON'd Eloquent models. + * + * + * // Create a new response instance with Eloquent models + * return Response::eloquent($data, 200, array('header' => 'value')); + * + * + * @param Eloquent|array $data + * @param int $status + * @param array $headers + * @return Response + */ + public static function eloquent($data, $status = 200, $headers = array()) + { + $headers['Content-Type'] = 'application/json; charset=utf-8'; + + return new static(eloquent_to_json($data), $status, $headers); + } + /** * Create a new error response instance. * @@ -180,48 +162,75 @@ public static function download($path, $name = null, $headers = array()) { if (is_null($name)) $name = basename($path); + // We'll set some sensible default headers, but merge the array given to + // us so that the developer has the chance to override any of these + // default headers with header values of their own liking. $headers = array_merge(array( - 'content-description' => 'File Transfer', - 'content-type' => File::mime(File::extension($path)), - 'content-disposition' => 'attachment; filename="'.$name.'"', - 'content-transfer-encoding' => 'binary', - 'expires' => 0, - 'cache-control' => 'must-revalidate, post-check=0, pre-check=0', - 'pragma' => 'public', - 'content-length' => File::size($path), + 'Content-Description' => 'File Transfer', + 'Content-Type' => File::mime(File::extension($path)), + 'Content-Transfer-Encoding' => 'binary', + 'Expires' => 0, + 'Cache-Control' => 'must-revalidate, post-check=0, pre-check=0', + 'Pragma' => 'public', + 'Content-Length' => File::size($path), ), $headers); - return new static(File::get($path), 200, $headers); + // Once we create the response, we need to set the content disposition + // header on the response based on the file's name. We'll pass this + // off to the HttpFoundation and let it create the header text. + $response = new static(File::get($path), 200, $headers); + + $d = $response->disposition($name); + + return $response->header('Content-Disposition', $d); } /** - * Prepare a response from the given value. + * Create the proper Content-Disposition header. * - * If the value is not a response, it will be converted into a response - * instance and the content will be cast to a string. + * @param string $file + * @return string + */ + public function disposition($file) + { + $type = ResponseHeaderBag::DISPOSITION_ATTACHMENT; + + return $this->foundation->headers->makeDisposition($type, $file); + } + + /** + * Prepare a response from the given value. * * @param mixed $response * @return Response */ public static function prepare($response) { + // We will need to force the response to be a string before closing + // the session since the developer may be utilizing the session + // within the view, and we can't age it until rendering. if ( ! $response instanceof Response) { $response = new static($response); } - // We'll need to force the response to be a string before closing the session, - // since the developer may be using the session within a view, and we can't - // age the flash data until the view is rendered. - // - // Since this method is used by both the Route and Controller classes, it is - // a convenient spot to cast the application response to a string before it - // is returned to the main request handler. - $response->render(); - return $response; } + /** + * Send the headers and content of the response to the browser. + * + * @return void + */ + public function send() + { + $this->cookies(); + + $this->foundation->prepare(Request::foundation()); + + $this->foundation->send(); + } + /** * Convert the content of the Response to a string and return it. * @@ -229,7 +238,10 @@ public static function prepare($response) */ public function render() { - if (is_object($this->content) and method_exists($this->content, '__toString')) + // If the content is a stringable object, we'll go ahead and call + // the toString method so that we can get the string content of + // the content object. Otherwise we'll just cast to string. + if (str_object($this->content)) { $this->content = $this->content->__toString(); } @@ -238,96 +250,98 @@ public function render() $this->content = (string) $this->content; } + // Once we obtain the string content, we can set the content on + // the HttpFoundation's Response instance in preparation for + // sending it back to client browser when all is finished. + $this->foundation->setContent($this->content); + return $this->content; } /** - * Send the headers and content of the response to the browser. + * Send all of the response headers to the browser. * * @return void */ - public function send() + public function send_headers() { - if ( ! headers_sent()) $this->send_headers(); + $this->foundation->prepare(Request::foundation()); - echo (string) $this->content; + $this->foundation->sendHeaders(); } /** - * Send all of the response headers to the browser. + * Set the cookies on the HttpFoundation Response. * * @return void */ - public function send_headers() + protected function cookies() { - // If the server is using FastCGI, we need to send a slightly different - // protocol and status header than we normally would. Otherwise it will - // not call any custom scripts setup to handle 404 responses. - // - // The status header will contain both the code and the status message, - // such as "OK" or "Not Found". For typical servers, the HTTP protocol - // will also be included with the status. - if (isset($_SERVER['FCGI_SERVER_VERSION'])) - { - header('Status: '.$this->status.' '.$this->message()); - } - else - { - header(Request::protocol().' '.$this->status.' '.$this->message()); - } + $ref = new \ReflectionClass('Symfony\Component\HttpFoundation\Cookie'); - // If the content type was not set by the developer, we will set the - // header to a default value that indicates to the browser that the - // response is HTML and that it uses the default encoding. - if ( ! isset($this->headers['content-type'])) + // All of the cookies for the response are actually stored on the + // Cookie class until we're ready to send the response back to + // the browser. This allows our cookies to be set easily. + foreach (Cookie::$jar as $name => $cookie) { - $encoding = Config::get('application.encoding'); + $config = array_values($cookie); - $this->header('content-type', 'text/html; charset='.$encoding); + $this->headers()->setCookie($ref->newInstanceArgs($config)); } + } - // Once the framework controlled headers have been sentm, we can - // simply iterate over the developer's headers and send each one - // back to the browser for the response. - foreach ($this->headers as $name => $value) - { - header("{$name}: {$value}", true); - } + /** + * Add a header to the array of response headers. + * + * @param string $name + * @param string $value + * @return Response + */ + public function header($name, $value) + { + $this->foundation->headers->set($name, $value); + + return $this; } /** - * Get the status code message for the response. + * Get the HttpFoundation Response headers. * - * @return string + * @return ResponseParameterBag */ - public function message() + public function headers() { - return static::$statuses[$this->status]; + return $this->foundation->headers; } /** - * Add a header to the array of response headers. + * Get / set the response status code. * - * @param string $name - * @param string $value - * @return Response + * @param int $status + * @return mixed */ - public function header($name, $value) + public function status($status = null) { - $this->headers[strtolower($name)] = $value; - return $this; + if (is_null($status)) + { + return $this->foundation->getStatusCode(); + } + else + { + $this->foundation->setStatusCode($status); + + return $this; + } } /** - * Set the response status code. + * Render the response when cast to string * - * @param int $status - * @return Response + * @return string */ - public function status($status) + public function __toString() { - $this->status = $status; - return $this; + return $this->render(); } } \ No newline at end of file diff --git a/app/laravel/routing/controller.php b/app/laravel/routing/controller.php index f75ef4b8a..e81d6b5f3 100644 --- a/app/laravel/routing/controller.php +++ b/app/laravel/routing/controller.php @@ -140,9 +140,19 @@ public static function call($destination, $parameters = array()) // improve speed since the bundle is not loaded on every request. Bundle::start($bundle); - list($controller, $method) = explode('@', $destination); + list($name, $method) = explode('@', $destination); - $controller = static::resolve($bundle, $controller); + $controller = static::resolve($bundle, $name); + + // For convenience we will set the current controller and action on the + // Request's route instance so they can be easily accessed from the + // application. This is sometimes useful for dynamic situations. + if ( ! is_null($route = Request::route())) + { + $route->controller = $name; + + $route->controller_action = $method; + } // If the controller could not be resolved, we're out of options and // will return the 404 error response. If we found the controller, @@ -169,6 +179,8 @@ protected static function references(&$destination, &$parameters) // controllers with much less code than would be usual. foreach ($parameters as $key => $value) { + if ( ! is_string($value)) continue; + $search = '(:'.($key + 1).')'; $destination = str_replace($search, $value, $destination, $count); diff --git a/app/laravel/routing/filter.php b/app/laravel/routing/filter.php index 14b6e2e33..80beec939 100644 --- a/app/laravel/routing/filter.php +++ b/app/laravel/routing/filter.php @@ -14,7 +14,7 @@ class Filter { public static $filters = array(); /** - * The route filters that are based on pattern. + * The route filters that are based on a pattern. * * @var array */ diff --git a/app/laravel/routing/route.php b/app/laravel/routing/route.php index 6349a0e84..090aa59ea 100644 --- a/app/laravel/routing/route.php +++ b/app/laravel/routing/route.php @@ -1,6 +1,7 @@ parameters($uri, $action, $parameters); + // are needed, we'll merge in the defaults. + $this->parameters($action, $parameters); } /** * Set the parameters array to the correct value. * - * @param string $uri * @param array $action * @param array $parameters * @return void */ - protected function parameters($uri, $action, $parameters) + protected function parameters($action, $parameters) { $defaults = (array) array_get($action, 'defaults'); // If there are less parameters than wildcards, we will figure out how // many parameters we need to inject from the array of defaults and - // merge them in into the main array for the route. + // merge them into the main array for the route. if (count($defaults) > count($parameters)) { $defaults = array_slice($defaults, count($parameters)); @@ -113,7 +126,7 @@ public function call() // We always return a Response instance from the route calls, so // we'll use the prepare method on the Response class to make - // sure we have a valid Response isntance. + // sure we have a valid Response instance. $response = Response::prepare($response); Filter::run($this->filters('after'), array($response)); @@ -198,8 +211,17 @@ protected function patterns() // if they match we'll attach the filter. foreach (Filter::$patterns as $pattern => $filter) { - if (URI::is($pattern, $this->uri)) + if (Str::is($pattern, $this->uri)) { + // If the filter provided is an array then we need to register + // the filter before we can assign it to the route. + if (is_array($filter)) + { + list($filter, $callback) = array_values($filter); + + Filter::register($filter, $callback); + } + $filters[] = $filter; } } @@ -251,7 +273,7 @@ public function is($name) /** * Register a controller with the router. * - * @param string|array $controller + * @param string|array $controllers * @param string|array $defaults * @return void */ @@ -393,4 +415,4 @@ public static function forward($method, $uri) return Router::route(strtoupper($method), $uri)->call(); } -} \ No newline at end of file +} diff --git a/app/laravel/routing/router.php b/app/laravel/routing/router.php index a6577b3f6..e9935e4c4 100644 --- a/app/laravel/routing/router.php +++ b/app/laravel/routing/router.php @@ -55,7 +55,7 @@ class Router { public static $group; /** - * The "handes" clause for the bundle currently being routed. + * The "handles" clause for the bundle currently being routed. * * @var string */ @@ -75,7 +75,7 @@ class Router { */ public static $patterns = array( '(:num)' => '([0-9]+)', - '(:any)' => '([a-zA-Z0-9\.\-_%]+)', + '(:any)' => '([a-zA-Z0-9\.\-_%=]+)', '(:all)' => '(.*)', ); @@ -86,7 +86,7 @@ class Router { */ public static $optional = array( '/(:num?)' => '(?:/([0-9]+)', - '/(:any?)' => '(?:/([a-zA-Z0-9\.\-_%]+)', + '/(:any?)' => '(?:/([a-zA-Z0-9\.\-_%=]+)', '/(:all?)' => '(?:/(.*)', ); @@ -119,7 +119,7 @@ public static function secure($method, $route, $action) * * * // Register a group of URIs for an action - * Router::share(array('GET', '/'), array('POST', '/'), 'home@index'); + * Router::share(array(array('GET', '/'), array('POST', '/')), 'home@index'); * * * @param array $routes @@ -174,6 +174,8 @@ public static function group($attributes, Closure $callback) */ public static function register($method, $route, $action) { + if (ctype_digit($route)) $route = "({$route})"; + if (is_string($route)) $route = explode(', ', $route); // If the developer is registering multiple request methods to handle @@ -204,7 +206,12 @@ public static function register($method, $route, $action) continue; } - $uri = str_replace('(:bundle)', static::$bundle, $uri); + $uri = ltrim(str_replace('(:bundle)', static::$bundle, $uri), '/'); + + if($uri == '') + { + $uri = '/'; + } // If the URI begins with a wildcard, we want to add this route to the // array of "fallback" routes. Fallback routes are always processed @@ -290,18 +297,18 @@ public static function secure_controller($controllers, $defaults = 'index') /** * Register a controller with the router. * - * @param string|array $controller + * @param string|array $controllers * @param string|array $defaults * @param bool $https * @return void */ - public static function controller($controllers, $defaults = 'index', $https = false) + public static function controller($controllers, $defaults = 'index', $https = null) { foreach ((array) $controllers as $identifier) { list($bundle, $controller) = Bundle::parse($identifier); - // First we need to replace the dots with slashes in thte controller name + // First we need to replace the dots with slashes in the controller name // so that it is in directory format. The dots allow the developer to use // a cleaner syntax when specifying the controller. We will also grab the // root URI for the controller's bundle. @@ -309,7 +316,7 @@ public static function controller($controllers, $defaults = 'index', $https = fa $root = Bundle::option($bundle, 'handles'); - // If the controller is a "home" controller, we'll need to also build a + // If the controller is a "home" controller, we'll need to also build an // index method route for the controller. We'll remove "home" from the // route root and setup a route to point to the index method. if (ends_with($controller, 'home')) @@ -426,7 +433,7 @@ public static function uses($action) // To find the route, we'll simply spin through the routes looking // for a route with a "uses" key matching the action, and if we - // find one we cache and return it. + // find one, we cache and return it. foreach (static::routes() as $method => $routes) { foreach ($routes as $key => $value) @@ -482,7 +489,7 @@ protected static function match($method, $uri) { foreach (static::method($method) as $route => $action) { - // We only need to check routes with regular expression since all other + // We only need to check routes with regular expression since all others // would have been able to be matched by the search for literal matches // we just did before we started searching. if (str_contains($route, '(')) diff --git a/app/laravel/section.php b/app/laravel/section.php index ff12889f2..9638880ce 100644 --- a/app/laravel/section.php +++ b/app/laravel/section.php @@ -39,7 +39,7 @@ public static function start($section, $content = '') } else { - static::append($section, $content); + static::extend($section, $content); } } @@ -79,19 +79,19 @@ public static function yield_section() */ public static function stop() { - static::append($last = array_pop(static::$last), ob_get_clean()); + static::extend($last = array_pop(static::$last), ob_get_clean()); return $last; } /** - * Append content to a given section. + * Extend the content in a given section. * * @param string $section * @param string $content * @return void */ - protected static function append($section, $content) + protected static function extend($section, $content) { if (isset(static::$sections[$section])) { @@ -103,6 +103,25 @@ protected static function append($section, $content) } } + /** + * Append content to a given section. + * + * @param string $section + * @param string $content + * @return void + */ + public static function append($section, $content) + { + if (isset(static::$sections[$section])) + { + static::$sections[$section] .= $content; + } + else + { + static::$sections[$section] = $content; + } + } + /** * Get the string contents of a section. * diff --git a/app/laravel/session.php b/app/laravel/session.php index bdc549a55..877b5a454 100644 --- a/app/laravel/session.php +++ b/app/laravel/session.php @@ -1,4 +1,4 @@ -cookie($config); - // Some session drivers implement the Sweeper interface, meaning that - // they must clean up expired sessions manually. If the driver is a - // sweeper, we need to determine if garbage collection should be - // run for the request. + // Some session drivers implement the Sweeper interface meaning that + // they must clean up expired sessions manually. Here we'll calculate + // if we need to run garbage collection. $sweepage = $config['sweepage']; - if ($this->driver instanceof Sweeper and (mt_rand(1, $sweepage[1]) <= $sweepage[0])) + if (mt_rand(1, $sweepage[1]) <= $sweepage[0]) { - $this->driver->sweep(time() - ($config['lifetime'] * 60)); + $this->sweep(); + } + } + + /** + * Clean up expired sessions. + * + * If the session driver is a sweeper, it must clean up expired sessions + * from time to time. This method triggers garbage collection. + * + * @return void + */ + public function sweep() + { + if ($this->driver instanceof Sweeper) + { + $this->driver->sweep(time() - (Config::get('session.lifetime') * 60)); } } diff --git a/app/laravel/str.php b/app/laravel/str.php index 78a554edf..681300420 100644 --- a/app/laravel/str.php +++ b/app/laravel/str.php @@ -9,17 +9,22 @@ class Str { */ public static $pluralizer; - /** - * Get the default string encoding for the application. - * - * This method is simply a short-cut to Config::get('application.encoding'). - * - * @return string - */ - public static function encoding() - { - return Config::get('application.encoding'); - } + /** + * Cache application encoding locally to save expensive calls to Config::get(). + * + * @var string + */ + public static $encoding = null; + + /** + * Get the appliction.encoding without needing to request it from Config::get() each time. + * + * @return string + */ + protected static function encoding() + { + return static::$encoding ?: static::$encoding = Config::get('application.encoding'); + } /** * Get the length of a string. @@ -130,6 +135,31 @@ public static function limit($value, $limit = 100, $end = '...') return substr($value, 0, $limit).$end; } + /** + * Limit the number of chracters in a string including custom ending + * + * + * // Returns "Taylor..." + * echo Str::limit_exact('Taylor Otwell', 9); + * + * // Limit the number of characters and append a custom ending + * echo Str::limit_exact('Taylor Otwell', 9, '---'); + * + * + * @param string $value + * @param int $limit + * @param string $end + * @return string + */ + public static function limit_exact($value, $limit = 100, $end = '...') + { + if (static::length($value) <= $limit) return $value; + + $limit -= static::length($end); + + return static::limit($value, $limit, $end); + } + /** * Limit the number of words in a string. * @@ -148,7 +178,9 @@ public static function limit($value, $limit = 100, $end = '...') */ public static function words($value, $words = 100, $end = '...') { - preg_match('/^\s*+(?:\S++\s*+){1,'.$words.'}/', $value, $matches); + if (trim($value) == '') return ''; + + preg_match('/^\s*+(?:\S++\s*+){1,'.$words.'}/u', $value, $matches); if (static::length($value) == static::length($matches[0])) { @@ -300,6 +332,30 @@ public static function random($length, $type = 'alnum') return substr(str_shuffle(str_repeat(static::pool($type), 5)), 0, $length); } + /** + * Determine if a given string matches a given pattern. + * + * @param string $pattern + * @param string $value + * @return bool + */ + public static function is($pattern, $value) + { + // Asterisks are translated into zero-or-more regular expression wildcards + // to make it convenient to check if the URI starts with a given pattern + // such as "library/*". This is only done when not root. + if ($pattern !== '/') + { + $pattern = str_replace('*', '(.*)', $pattern).'\z'; + } + else + { + $pattern = '^/$'; + } + + return preg_match('#'.$pattern.'#', $value); + } + /** * Get the character pool for a given type of random string. * diff --git a/app/laravel/tests/application/bundles.php b/app/laravel/tests/application/bundles.php new file mode 100644 index 000000000..1013f0cf8 --- /dev/null +++ b/app/laravel/tests/application/bundles.php @@ -0,0 +1,36 @@ + array( +| 'location' => 'admin', +| 'handles' => 'admin', +| ), +| +| Note that the "location" is relative to the "bundles" directory. +| Now the bundle will be recognized by Laravel and will be able +| to respond to requests beginning with "admin"! +| +| Have a bundle that lives in the root of the bundle directory +| and doesn't respond to any requests? Just add the bundle +| name to the array and we'll take care of the rest. +| +*/ + +return array('dashboard' => array('handles' => 'dashboard'), 'dummy'); \ No newline at end of file diff --git a/app/laravel/tests/application/config/application.php b/app/laravel/tests/application/config/application.php new file mode 100644 index 000000000..596baa245 --- /dev/null +++ b/app/laravel/tests/application/config/application.php @@ -0,0 +1,158 @@ + '', + + /* + |-------------------------------------------------------------------------- + | Application Index + |-------------------------------------------------------------------------- + | + | If you are including the "index.php" in your URLs, you can ignore this. + | + | However, if you are using mod_rewrite to get cleaner URLs, just set + | this option to an empty string and we'll take care of the rest. + | + */ + + 'index' => 'index.php', + + /* + |-------------------------------------------------------------------------- + | Application Key + |-------------------------------------------------------------------------- + | + | This key is used by the encryption and cookie classes to generate secure + | encrypted strings and hashes. It is extremely important that this key + | remain secret and should not be shared with anyone. Make it about 32 + | characters of random gibberish. + | + */ + + 'key' => '', + + /* + |-------------------------------------------------------------------------- + | Application Character Encoding + |-------------------------------------------------------------------------- + | + | The default character encoding used by your application. This encoding + | 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. + | + */ + + 'encoding' => 'UTF-8', + + /* + |-------------------------------------------------------------------------- + | Application Language + |-------------------------------------------------------------------------- + | + | The default language of your application. This language will be used by + | Lang library as the default language when doing string localization. + | + */ + + 'language' => 'en', + + /* + |-------------------------------------------------------------------------- + | SSL Link Generation + |-------------------------------------------------------------------------- + | + | Many sites use SSL to protect their users data. However, you may not + | always be able to use SSL on your development machine, meaning all HTTPS + | will be broken during development. + | + | For this reason, you may wish to disable the generation of HTTPS links + | throughout your application. This option does just that. All attempts to + | generate HTTPS links will generate regular HTTP links instead. + | + */ + + 'ssl' => true, + + /* + |-------------------------------------------------------------------------- + | Application Timezone + |-------------------------------------------------------------------------- + | + | The default timezone of your application. This timezone will be used when + | Laravel needs a date, such as when writing to a log file or travelling + | to a distant star at warp speed. + | + */ + + 'timezone' => 'UTC', + + /* + |-------------------------------------------------------------------------- + | Class Aliases + |-------------------------------------------------------------------------- + | + | Here, you can specify any class aliases that you would like registered + | when Laravel loads. Aliases are lazy-loaded, so add as many as you want. + | + | Aliases make it more convenient to use namespaced classes. Instead of + | referring to the class using its full namespace, you may simply use + | the alias defined here. + | + | We have already aliased common Laravel classes to make your life easier. + | + */ + + 'aliases' => array( + 'Auth' => 'Laravel\\Auth', + 'Asset' => 'Laravel\\Asset', + 'Autoloader' => 'Laravel\\Autoloader', + 'Blade' => 'Laravel\\Blade', + 'Bundle' => 'Laravel\\Bundle', + 'Cache' => 'Laravel\\Cache', + 'Config' => 'Laravel\\Config', + 'Controller' => 'Laravel\\Routing\\Controller', + 'Cookie' => 'Laravel\\Cookie', + 'Crypter' => 'Laravel\\Crypter', + 'DB' => 'Laravel\\Database', + 'Event' => 'Laravel\\Event', + 'File' => 'Laravel\\File', + 'Filter' => 'Laravel\\Routing\\Filter', + 'Form' => 'Laravel\\Form', + 'Hash' => 'Laravel\\Hash', + 'HTML' => 'Laravel\\HTML', + 'Input' => 'Laravel\\Input', + 'IoC' => 'Laravel\\IoC', + 'Lang' => 'Laravel\\Lang', + 'Log' => 'Laravel\\Log', + 'Memcached' => 'Laravel\\Memcached', + 'Paginator' => 'Laravel\\Paginator', + 'URL' => 'Laravel\\URL', + 'Redirect' => 'Laravel\\Redirect', + 'Redis' => 'Laravel\\Redis', + 'Request' => 'Laravel\\Request', + 'Response' => 'Laravel\\Response', + 'Route' => 'Laravel\\Routing\\Route', + 'Router' => 'Laravel\\Routing\\Router', + 'Schema' => 'Laravel\\Database\\Schema', + 'Section' => 'Laravel\\Section', + 'Session' => 'Laravel\\Session', + 'Str' => 'Laravel\\Str', + 'Task' => 'Laravel\\CLI\\Tasks\\Task', + 'URI' => 'Laravel\\URI', + 'Validator' => 'Laravel\\Validator', + 'View' => 'Laravel\\View', + ), + +); diff --git a/app/laravel/tests/application/config/auth.php b/app/laravel/tests/application/config/auth.php new file mode 100644 index 000000000..d715f063f --- /dev/null +++ b/app/laravel/tests/application/config/auth.php @@ -0,0 +1,73 @@ + 'fluent', + + /* + |-------------------------------------------------------------------------- + | Authentication Username + |-------------------------------------------------------------------------- + | + | Here you may specify the database column that should be considered the + | "username" for your users. Typically, this will either be "username" + | or "email". Of course, you're free to change the value to anything. + | + */ + + 'username' => 'username', + + /* + |-------------------------------------------------------------------------- + | Authentication Password + |-------------------------------------------------------------------------- + | + | Here you may specify the database column that should be considered the + | "password" for your users. Typically, this will be "password" but, + | again, you're free to change the value to anything you see fit. + | + */ + + 'password' => 'password', + + /* + |-------------------------------------------------------------------------- + | Authentication Model + |-------------------------------------------------------------------------- + | + | When using the "eloquent" authentication driver, you may specify the + | model that should be considered the "User" model. This model will + | be used to authenticate and load the users of your application. + | + */ + + 'model' => 'User', + + /* + |-------------------------------------------------------------------------- + | Authentication Table + |-------------------------------------------------------------------------- + | + | When using the "fluent" authentication driver, the database table used + | to load users may be specified here. This table will be used in by + | the fluent query builder to authenticate and load your users. + | + */ + + 'table' => 'users', + +); diff --git a/app/laravel/tests/application/config/cache.php b/app/laravel/tests/application/config/cache.php new file mode 100644 index 000000000..73fd4c9ca --- /dev/null +++ b/app/laravel/tests/application/config/cache.php @@ -0,0 +1,71 @@ + 'file', + + /* + |-------------------------------------------------------------------------- + | Cache Key + |-------------------------------------------------------------------------- + | + | This key will be prepended to item keys stored using Memcached and APC + | 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. + | + */ + + 'key' => 'laravel', + + /* + |-------------------------------------------------------------------------- + | Cache Database + |-------------------------------------------------------------------------- + | + | 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 + |-------------------------------------------------------------------------- + | + | The Memcached servers used by your application. Memcached is a free and + | open source, high-performance, distributed memory caching system. It is + | generic in nature but intended for use in speeding up web applications + | by alleviating database load. + | + | For more information, check out: http://memcached.org + | + */ + + 'memcached' => array( + + array('host' => '127.0.0.1', 'port' => 11211, 'weight' => 100), + + ), + +); \ No newline at end of file diff --git a/app/laravel/tests/application/config/database.php b/app/laravel/tests/application/config/database.php new file mode 100644 index 000000000..b7b9fd962 --- /dev/null +++ b/app/laravel/tests/application/config/database.php @@ -0,0 +1,108 @@ + 'sqlite', + + /* + |-------------------------------------------------------------------------- + | PDO Fetch Style + |-------------------------------------------------------------------------- + | + | By default, database results will be returned as instances of the PHP + | stdClass object; however, you may wish to retrieve records as arrays + | instead of objects. Here you can control the PDO fetch style of the + | database queries run by your application. + | + */ + + 'fetch' => PDO::FETCH_CLASS, + + /* + |-------------------------------------------------------------------------- + | Database Connections + |-------------------------------------------------------------------------- + | + | All of the database connections used by your application. Many of your + | applications will no doubt only use one connection; however, you have + | the freedom to specify as many connections as you can handle. + | + | All database work in Laravel is done through the PHP's PDO facilities, + | so make sure you have the PDO drivers for your particlar database of + | choice installed on your machine. + | + | Drivers: 'mysql', 'pgsql', 'sqlsrv', 'sqlite'. + | + */ + + 'connections' => array( + + 'sqlite' => array( + 'driver' => 'sqlite', + 'database' => 'application', + 'prefix' => '', + ), + + 'mysql' => array( + 'driver' => 'mysql', + 'host' => 'localhost', + 'database' => 'database', + 'username' => 'root', + 'password' => 'password', + 'charset' => 'utf8', + 'prefix' => '', + ), + + 'pgsql' => array( + 'driver' => 'pgsql', + 'host' => 'localhost', + 'database' => 'database', + 'username' => 'root', + 'password' => 'password', + 'charset' => 'utf8', + 'prefix' => '', + ), + + 'sqlsrv' => array( + 'driver' => 'sqlsrv', + 'host' => 'localhost', + 'database' => 'database', + 'username' => 'root', + 'password' => 'password', + 'prefix' => '', + ), + + ), + + /* + |-------------------------------------------------------------------------- + | Redis Databases + |-------------------------------------------------------------------------- + | + | 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 + | APC or memcached. All the cool kids are using it. + | + | To get the scoop on Redis, check out: http://redis.io + | + */ + + 'redis' => array( + + 'default' => array('host' => '127.0.0.1', 'port' => 6379), + + ), + +); \ No newline at end of file diff --git a/app/laravel/tests/application/config/error.php b/app/laravel/tests/application/config/error.php new file mode 100644 index 000000000..87db96653 --- /dev/null +++ b/app/laravel/tests/application/config/error.php @@ -0,0 +1,69 @@ + array(E_NOTICE, E_USER_NOTICE, E_DEPRECATED, E_USER_DEPRECATED), + + /* + |-------------------------------------------------------------------------- + | Error Detail + |-------------------------------------------------------------------------- + | + | Detailed error messages contain information about the file in which an + | 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, you'll want to turn off the error + | details for enhanced security and user experience since the exception + | stack trace could contain sensitive information. + | + */ + + 'detail' => true, + + /* + |-------------------------------------------------------------------------- + | Error Logging + |-------------------------------------------------------------------------- + | + | When error logging is enabled, the "logger" Closure defined below will + | be called for every error in your application. You are free to log the + | errors however you want. Enjoy the flexibility. + | + */ + + 'log' => false, + + /* + |-------------------------------------------------------------------------- + | Error Logger + |-------------------------------------------------------------------------- + | + | Because of the various ways of managing error logging, you get complete + | flexibility to manage error logging as you see fit. This function will + | be called anytime an error occurs within your application and error + | logging is enabled. + | + | 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 + | text files within the application storage directory. + | + */ + + 'logger' => function($exception) + { + Log::exception($exception); + }, + +); \ No newline at end of file diff --git a/app/laravel/tests/application/config/local/database.php b/app/laravel/tests/application/config/local/database.php new file mode 100644 index 000000000..bd94d7ba3 --- /dev/null +++ b/app/laravel/tests/application/config/local/database.php @@ -0,0 +1,7 @@ + 'sqlite', + +); \ No newline at end of file diff --git a/app/laravel/tests/application/config/mimes.php b/app/laravel/tests/application/config/mimes.php new file mode 100644 index 000000000..e2bd4fbb1 --- /dev/null +++ b/app/laravel/tests/application/config/mimes.php @@ -0,0 +1,97 @@ + 'application/mac-binhex40', + 'cpt' => 'application/mac-compactpro', + 'csv' => array('text/x-comma-separated-values', 'text/comma-separated-values', 'application/octet-stream'), + 'bin' => 'application/macbinary', + 'dms' => 'application/octet-stream', + 'lha' => 'application/octet-stream', + 'lzh' => 'application/octet-stream', + 'exe' => array('application/octet-stream', 'application/x-msdownload'), + 'class' => 'application/octet-stream', + 'psd' => 'application/x-photoshop', + 'so' => 'application/octet-stream', + 'sea' => 'application/octet-stream', + 'dll' => 'application/octet-stream', + 'oda' => 'application/oda', + 'pdf' => array('application/pdf', 'application/x-download'), + 'ai' => 'application/postscript', + 'eps' => 'application/postscript', + 'ps' => 'application/postscript', + 'smi' => 'application/smil', + 'smil' => 'application/smil', + 'mif' => 'application/vnd.mif', + 'xls' => array('application/excel', 'application/vnd.ms-excel', 'application/msexcel'), + 'ppt' => array('application/powerpoint', 'application/vnd.ms-powerpoint'), + 'wbxml' => 'application/wbxml', + 'wmlc' => 'application/wmlc', + 'dcr' => 'application/x-director', + 'dir' => 'application/x-director', + 'dxr' => 'application/x-director', + 'dvi' => 'application/x-dvi', + 'gtar' => 'application/x-gtar', + 'gz' => 'application/x-gzip', + 'php' => array('application/x-httpd-php', 'text/x-php'), + 'php4' => 'application/x-httpd-php', + 'php3' => 'application/x-httpd-php', + 'phtml' => 'application/x-httpd-php', + 'phps' => 'application/x-httpd-php-source', + 'js' => 'application/x-javascript', + 'swf' => 'application/x-shockwave-flash', + 'sit' => 'application/x-stuffit', + 'tar' => 'application/x-tar', + 'tgz' => array('application/x-tar', 'application/x-gzip-compressed'), + 'xhtml' => 'application/xhtml+xml', + 'xht' => 'application/xhtml+xml', + 'zip' => array('application/x-zip', 'application/zip', 'application/x-zip-compressed'), + 'mid' => 'audio/midi', + 'midi' => 'audio/midi', + 'mpga' => 'audio/mpeg', + 'mp2' => 'audio/mpeg', + 'mp3' => array('audio/mpeg', 'audio/mpg', 'audio/mpeg3', 'audio/mp3'), + 'aif' => 'audio/x-aiff', + 'aiff' => 'audio/x-aiff', + 'aifc' => 'audio/x-aiff', + 'ram' => 'audio/x-pn-realaudio', + 'rm' => 'audio/x-pn-realaudio', + 'rpm' => 'audio/x-pn-realaudio-plugin', + 'ra' => 'audio/x-realaudio', + 'rv' => 'video/vnd.rn-realvideo', + 'wav' => 'audio/x-wav', + 'bmp' => 'image/bmp', + 'gif' => 'image/gif', + 'jpeg' => array('image/jpeg', 'image/pjpeg'), + 'jpg' => array('image/jpeg', 'image/pjpeg'), + 'jpe' => array('image/jpeg', 'image/pjpeg'), + 'png' => 'image/png', + 'tiff' => 'image/tiff', + 'tif' => 'image/tiff', + 'css' => 'text/css', + 'html' => 'text/html', + 'htm' => 'text/html', + 'shtml' => 'text/html', + 'txt' => 'text/plain', + 'text' => 'text/plain', + 'log' => array('text/plain', 'text/x-log'), + 'rtx' => 'text/richtext', + 'rtf' => 'text/rtf', + 'xml' => 'text/xml', + 'xsl' => 'text/xml', + 'mpeg' => 'video/mpeg', + 'mpg' => 'video/mpeg', + 'mpe' => 'video/mpeg', + 'qt' => 'video/quicktime', + 'mov' => 'video/quicktime', + 'avi' => 'video/x-msvideo', + 'movie' => 'video/x-sgi-movie', + 'doc' => 'application/msword', + 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'word' => array('application/msword', 'application/octet-stream'), + 'xl' => 'application/excel', + 'eml' => 'message/rfc822', + 'json' => array('application/json', 'text/json'), + +); \ No newline at end of file diff --git a/app/laravel/tests/application/config/session.php b/app/laravel/tests/application/config/session.php new file mode 100644 index 000000000..3b6e6a694 --- /dev/null +++ b/app/laravel/tests/application/config/session.php @@ -0,0 +1,117 @@ + '', + + /* + |-------------------------------------------------------------------------- + | Session Database + |-------------------------------------------------------------------------- + | + | The database table on which the session should be stored. It probably + | goes without saying that this option only matters if you are using + | the super slick database session driver. + | + */ + + 'table' => 'sessions', + + /* + |-------------------------------------------------------------------------- + | Session Garbage Collection Probability + |-------------------------------------------------------------------------- + | + | Some session drivers require the manual clean-up of expired sessions. + | This option specifies the probability of session garbage collection + | occuring for any given request. + | + | For example, the default value states that garbage collection has a + | 2% chance of occuring for any given request to the application. + | Feel free to tune this to your application's size and speed. + | + */ + + 'sweepage' => array(2, 100), + + /* + |-------------------------------------------------------------------------- + | Session Lifetime + |-------------------------------------------------------------------------- + | + | The number of minutes a session can be idle before expiring. + | + */ + + 'lifetime' => 60, + + /* + |-------------------------------------------------------------------------- + | Session Expiration On Close + |-------------------------------------------------------------------------- + | + | Determines if the session should expire when the user's web browser closes. + | + */ + + 'expire_on_close' => false, + + /* + |-------------------------------------------------------------------------- + | Session Cookie Name + |-------------------------------------------------------------------------- + | + | The name that should be given to the session cookie. + | + */ + + 'cookie' => 'laravel_session', + + /* + |-------------------------------------------------------------------------- + | Session Cookie Path + |-------------------------------------------------------------------------- + | + | The path for which the session cookie is available. + | + */ + + 'path' => '/', + + /* + |-------------------------------------------------------------------------- + | Session Cookie Domain + |-------------------------------------------------------------------------- + | + | The domain for which the session cookie is available. + | + */ + + 'domain' => null, + + /* + |-------------------------------------------------------------------------- + | HTTPS Only Session Cookie + |-------------------------------------------------------------------------- + | + | Determines if the cookie should only be sent over HTTPS. + | + */ + + 'secure' => false, + +); \ No newline at end of file diff --git a/app/laravel/tests/application/config/strings.php b/app/laravel/tests/application/config/strings.php new file mode 100644 index 000000000..bbbe230c9 --- /dev/null +++ b/app/laravel/tests/application/config/strings.php @@ -0,0 +1,190 @@ + array( + '/(quiz)$/i' => "$1zes", + '/^(ox)$/i' => "$1en", + '/([m|l])ouse$/i' => "$1ice", + '/(matr|vert|ind)ix|ex$/i' => "$1ices", + '/(x|ch|ss|sh)$/i' => "$1es", + '/([^aeiouy]|qu)y$/i' => "$1ies", + '/(hive)$/i' => "$1s", + '/(?:([^f])fe|([lr])f)$/i' => "$1$2ves", + '/(shea|lea|loa|thie)f$/i' => "$1ves", + '/sis$/i' => "ses", + '/([ti])um$/i' => "$1a", + '/(tomat|potat|ech|her|vet)o$/i' => "$1oes", + '/(bu)s$/i' => "$1ses", + '/(alias)$/i' => "$1es", + '/(octop)us$/i' => "$1i", + '/(ax|test)is$/i' => "$1es", + '/(us)$/i' => "$1es", + '/s$/i' => "s", + '/$/' => "s" + ), + + 'singular' => array( + '/(quiz)zes$/i' => "$1", + '/(matr)ices$/i' => "$1ix", + '/(vert|ind)ices$/i' => "$1ex", + '/^(ox)en$/i' => "$1", + '/(alias)es$/i' => "$1", + '/(octop|vir)i$/i' => "$1us", + '/(cris|ax|test)es$/i' => "$1is", + '/(shoe)s$/i' => "$1", + '/(o)es$/i' => "$1", + '/(bus)es$/i' => "$1", + '/([m|l])ice$/i' => "$1ouse", + '/(x|ch|ss|sh)es$/i' => "$1", + '/(m)ovies$/i' => "$1ovie", + '/(s)eries$/i' => "$1eries", + '/([^aeiouy]|qu)ies$/i' => "$1y", + '/([lr])ves$/i' => "$1f", + '/(tive)s$/i' => "$1", + '/(hive)s$/i' => "$1", + '/(li|wi|kni)ves$/i' => "$1fe", + '/(shea|loa|lea|thie)ves$/i' => "$1f", + '/(^analy)ses$/i' => "$1sis", + '/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i' => "$1$2sis", + '/([ti])a$/i' => "$1um", + '/(n)ews$/i' => "$1ews", + '/(h|bl)ouses$/i' => "$1ouse", + '/(corpse)s$/i' => "$1", + '/(us)es$/i' => "$1", + '/(us|ss)$/i' => "$1", + '/s$/i' => "", + ), + + 'irregular' => array( + 'child' => 'children', + 'foot' => 'feet', + 'goose' => 'geese', + 'man' => 'men', + 'move' => 'moves', + 'person' => 'people', + 'sex' => 'sexes', + 'tooth' => 'teeth', + ), + + 'uncountable' => array( + 'audio', + 'equipment', + 'deer', + 'fish', + 'gold', + 'information', + 'money', + 'rice', + 'police', + 'series', + 'sheep', + 'species', + 'moose', + 'chassis', + 'traffic', + ), + + /* + |-------------------------------------------------------------------------- + | 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', + + ), + +); diff --git a/app/laravel/tests/application/controllers/admin/panel.php b/app/laravel/tests/application/controllers/admin/panel.php new file mode 100644 index 000000000..da3b49d7d --- /dev/null +++ b/app/laravel/tests/application/controllers/admin/panel.php @@ -0,0 +1,10 @@ +filter('before', 'test-all-before'); + $this->filter('after', 'test-all-after'); + $this->filter('before', 'test-profile-before')->only(array('profile')); + $this->filter('before', 'test-except')->except(array('index', 'profile')); + $this->filter('before', 'test-on-post')->on(array('post')); + $this->filter('before', 'test-on-get-put')->on(array('get', 'put')); + $this->filter('before', 'test-before-filter')->only('login'); + $this->filter('after', 'test-before-filter')->only('logout'); + $this->filter('before', 'test-param:1,2')->only('edit'); + $this->filter('before', 'test-multi-1|test-multi-2')->only('save'); + } + + public function action_index() + { + return __FUNCTION__; + } + + public function action_profile() + { + return __FUNCTION__; + } + + public function action_show() + { + return __FUNCTION__; + } + + public function action_edit() + { + return __FUNCTION__; + } + + public function action_save() + { + return __FUNCTION__; + } + + public function action_login() + { + return __FUNCTION__; + } + + public function action_logout() + { + return __FUNCTION__; + } + +} \ No newline at end of file diff --git a/app/laravel/tests/application/controllers/home.php b/app/laravel/tests/application/controllers/home.php new file mode 100644 index 000000000..3f442005c --- /dev/null +++ b/app/laravel/tests/application/controllers/home.php @@ -0,0 +1,42 @@ + '« Previous', + 'next' => 'Next »', + +); \ No newline at end of file diff --git a/app/laravel/tests/application/language/en/validation.php b/app/laravel/tests/application/language/en/validation.php new file mode 100644 index 000000000..5c6354ddb --- /dev/null +++ b/app/laravel/tests/application/language/en/validation.php @@ -0,0 +1,96 @@ + "The :attribute must be accepted.", + "active_url" => "The :attribute is not a valid URL.", + "alpha" => "The :attribute may only contain letters.", + "alpha_dash" => "The :attribute may only contain letters, numbers, and dashes.", + "alpha_num" => "The :attribute may only contain letters and numbers.", + "between" => array( + "numeric" => "The :attribute must be between :min - :max.", + "file" => "The :attribute must be between :min - :max kilobytes.", + "string" => "The :attribute must be between :min - :max characters.", + ), + "confirmed" => "The :attribute confirmation does not match.", + "different" => "The :attribute and :other must be different.", + "email" => "The :attribute format is invalid.", + "exists" => "The selected :attribute is invalid.", + "image" => "The :attribute must be an image.", + "in" => "The selected :attribute is invalid.", + "integer" => "The :attribute must be an integer.", + "ip" => "The :attribute must be a valid IP address.", + "max" => array( + "numeric" => "The :attribute must be less than :max.", + "file" => "The :attribute must be less than :max kilobytes.", + "string" => "The :attribute must be less than :max characters.", + ), + "mimes" => "The :attribute must be a file of type: :values.", + "min" => array( + "numeric" => "The :attribute must be at least :min.", + "file" => "The :attribute must be at least :min kilobytes.", + "string" => "The :attribute must be at least :min characters.", + ), + "not_in" => "The selected :attribute is invalid.", + "numeric" => "The :attribute must be a number.", + "required" => "The :attribute field is required.", + "same" => "The :attribute and :other must match.", + "size" => array( + "numeric" => "The :attribute must be :size.", + "file" => "The :attribute must be :size kilobyte.", + "string" => "The :attribute must be :size characters.", + ), + "unique" => "The :attribute has already been taken.", + "url" => "The :attribute format is invalid.", + + /* + |-------------------------------------------------------------------------- + | Custom Validation Language Lines + |-------------------------------------------------------------------------- + | + | Here you may specify custom validation messages for attributes using the + | convention "attribute_rule" to name the lines. This helps keep your + | custom validation clean and tidy. + | + | So, say you want to use a custom validation message when validating that + | the "email" attribute is unique. Just add "email_unique" to this array + | with your custom message. The Validator will handle the rest! + | + */ + + 'custom' => array('custom_required' => 'This field is required!'), + + /* + |-------------------------------------------------------------------------- + | Validation Attributes + |-------------------------------------------------------------------------- + | + | The following language lines are used to swap attribute place-holders + | with something more reader friendly such as "E-Mail Address" instead + | of "email". Your users will thank you. + | + | The Validator class will automatically search this array of lines it + | is attempting to replace the :attribute place-holder in messages. + | It's pretty slick. We think you'll like it. + | + */ + + 'attributes' => array('test_attribute' => 'attribute'), + +); \ No newline at end of file diff --git a/app/laravel/tests/application/language/sp/validation.php b/app/laravel/tests/application/language/sp/validation.php new file mode 100644 index 000000000..b9bcbe747 --- /dev/null +++ b/app/laravel/tests/application/language/sp/validation.php @@ -0,0 +1,7 @@ + 'El campo de atributo es necesario.', + +); \ No newline at end of file diff --git a/app/laravel/tests/application/libraries/.gitignore b/app/laravel/tests/application/libraries/.gitignore new file mode 100644 index 000000000..e69de29bb diff --git a/app/laravel/tests/application/migrations/.gitignore b/app/laravel/tests/application/migrations/.gitignore new file mode 100644 index 000000000..e69de29bb diff --git a/app/laravel/tests/application/models/.gitignore b/app/laravel/tests/application/models/.gitignore new file mode 100644 index 000000000..e69de29bb diff --git a/app/laravel/tests/application/models/autoloader.php b/app/laravel/tests/application/models/autoloader.php new file mode 100644 index 000000000..0e9027dce --- /dev/null +++ b/app/laravel/tests/application/models/autoloader.php @@ -0,0 +1,3 @@ +set_attribute('setter', 'setter: '.$setter); + } + + public function get_getter() + { + return 'getter: '.$this->get_attribute('getter'); + } + +} \ No newline at end of file diff --git a/app/laravel/tests/application/models/repositories/user.php b/app/laravel/tests/application/models/repositories/user.php new file mode 100644 index 000000000..093ec2b00 --- /dev/null +++ b/app/laravel/tests/application/models/repositories/user.php @@ -0,0 +1,3 @@ + 'home', function() +{ + return View::make('home.index'); +})); + +Route::controller(array( + 'auth', 'filter', 'home', 'restful', + 'template.basic', 'template.name', 'template.override', + 'admin.panel', +)); + +/* +|-------------------------------------------------------------------------- +| 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() +{ + $_SERVER['before'] = true; +}); + +Filter::register('after', function() +{ + $_SERVER['after'] = true; +}); + +Filter::register('csrf', function() +{ + if (Request::forged()) return Response::error('500'); +}); + +Filter::register('auth', function() +{ + if (Auth::guest()) return Redirect::to('login'); +}); \ No newline at end of file diff --git a/app/laravel/tests/application/start.php b/app/laravel/tests/application/start.php new file mode 100644 index 000000000..085dd090f --- /dev/null +++ b/app/laravel/tests/application/start.php @@ -0,0 +1,157 @@ + path('app').'controllers/base.php', +)); + +/* +|-------------------------------------------------------------------------- +| Auto-Loader 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. +| +*/ + +Autoloader::directories(array( + path('app').'models', + path('app').'libraries', +)); + +/* +|-------------------------------------------------------------------------- +| Laravel View Loader +|-------------------------------------------------------------------------- +| +| The Laravel view loader is responsible for returning the full file path +| for the given bundle and view. Of course, a default implementation is +| provided to load views according to typical Laravel conventions but +| you may change this to customize how your views are organized. +| +*/ + +Event::listen(View::loader, function($bundle, $view) +{ + return View::file($bundle, $view, Bundle::path($bundle).'views'); +}); + +/* +|-------------------------------------------------------------------------- +| Laravel Language Loader +|-------------------------------------------------------------------------- +| +| The Laravel language loader is responsible for returning the array of +| language lines for a given bundle, language, and "file". A default +| implementation has been provided which uses the default language +| directories included with Laravel. +| +*/ + +Event::listen(Lang::loader, function($bundle, $language, $file) +{ + return Lang::file($bundle, $language, $file); +}); + +/* +|-------------------------------------------------------------------------- +| Enable The Blade View Engine +|-------------------------------------------------------------------------- +| +| The Blade view engine provides a clean, beautiful templating language +| for your application, including syntax for echoing data and all of +| the typical PHP control structures. We'll simply enable it here. +| +*/ + +Blade::sharpen(); + +/* +|-------------------------------------------------------------------------- +| Set The Default Timezone +|-------------------------------------------------------------------------- +| +| We need to set the default timezone for the application. This controls +| the timezone that will be used by any of the date methods and classes +| utilized by Laravel or your application. The timezone may be set in +| your application configuration file. +| +*/ + +date_default_timezone_set(Config::get('application.timezone')); + +/* +|-------------------------------------------------------------------------- +| Start / Load The User Session +|-------------------------------------------------------------------------- +| +| Sessions allow the web, which is stateless, to simulate state. In other +| words, sessions allow you to store information about the current user +| and state of your application. Here we'll just fire up the session +| if a session driver has been configured. +| +*/ + +if ( ! Request::cli() and Config::get('session.driver') !== '') +{ + Session::load(); +} \ No newline at end of file diff --git a/app/laravel/tests/application/tasks/.gitignore b/app/laravel/tests/application/tasks/.gitignore new file mode 100644 index 000000000..e69de29bb diff --git a/app/laravel/tests/application/views/error/404.php b/app/laravel/tests/application/views/error/404.php new file mode 100644 index 000000000..9b9bf55bc --- /dev/null +++ b/app/laravel/tests/application/views/error/404.php @@ -0,0 +1,103 @@ + + + + + + Error 404 - Not Found + + + + +
    + + +

    + +

    Server Error: 404 (Not Found)

    + +

    What does this mean?

    + +

    + We couldn't find the page you requested on our servers. We're really sorry + about that. It's our fault, not yours. We'll work hard to get this page + back online as soon as possible. +

    + +

    + Perhaps you would like to go to our ? +

    +
    + + \ No newline at end of file diff --git a/app/laravel/tests/application/views/error/500.php b/app/laravel/tests/application/views/error/500.php new file mode 100644 index 000000000..4dcd92ada --- /dev/null +++ b/app/laravel/tests/application/views/error/500.php @@ -0,0 +1,103 @@ + + + + + + Error 500 - Internal Server Error + + + + +
    + + +

    + +

    Server Error: 500 (Internal Server Error)

    + +

    What does this mean?

    + +

    + Something went wrong on our servers while we were processing your request. + We're really sorry about this, and will work hard to get this resolved as + soon as possible. +

    + +

    + Perhaps you would like to go to our ? +

    +
    + + \ No newline at end of file diff --git a/app/laravel/tests/application/views/home/index.php b/app/laravel/tests/application/views/home/index.php new file mode 100644 index 000000000..394971462 --- /dev/null +++ b/app/laravel/tests/application/views/home/index.php @@ -0,0 +1,122 @@ + + + + + + Laravel - A Framework For Web Artisans + + + + +
    +

    Welcome To Laravel

    + +

    A Framework For Web Artisans

    + +

    + You have successfully installed the Laravel framework. Laravel is a simple framework + that helps web artisans create beautiful, creative applications using elegant, expressive + syntax. You'll love using it. +

    + +

    Learn the terrain.

    + +

    + You've landed yourself on our default home page. The route that + is generating this page lives at: +

    + +
    APP_PATH/routes.php
    + +

    And the view sitting before you can be found at:

    + +
    APP_PATH/views/home/index.php
    + +

    Create something beautiful.

    + +

    + Now that you're up and running, it's time to start creating! + Here are some links to help you get started: +

    + + + +
    + + \ No newline at end of file diff --git a/app/laravel/tests/application/views/tests/basic.php b/app/laravel/tests/application/views/tests/basic.php new file mode 100644 index 000000000..c961dc2fd --- /dev/null +++ b/app/laravel/tests/application/views/tests/basic.php @@ -0,0 +1 @@ + is \ No newline at end of file diff --git a/app/laravel/tests/application/views/tests/nested.php b/app/laravel/tests/application/views/tests/nested.php new file mode 100644 index 000000000..9ce498e56 --- /dev/null +++ b/app/laravel/tests/application/views/tests/nested.php @@ -0,0 +1 @@ +Taylor \ No newline at end of file diff --git a/app/laravel/tests/bundles/.gitignore b/app/laravel/tests/bundles/.gitignore new file mode 100644 index 000000000..e69de29bb diff --git a/app/laravel/tests/bundles/dashboard/config/meta.php b/app/laravel/tests/bundles/dashboard/config/meta.php new file mode 100644 index 000000000..a82d17035 --- /dev/null +++ b/app/laravel/tests/bundles/dashboard/config/meta.php @@ -0,0 +1,7 @@ + 'dashboard', + +); \ No newline at end of file diff --git a/app/laravel/tests/bundles/dashboard/controllers/panel.php b/app/laravel/tests/bundles/dashboard/controllers/panel.php new file mode 100644 index 000000000..b532296d1 --- /dev/null +++ b/app/laravel/tests/bundles/dashboard/controllers/panel.php @@ -0,0 +1,10 @@ + 'dashboard', function() +{ + // +})); + +Route::controller('dashboard::panel'); \ No newline at end of file diff --git a/app/laravel/tests/bundles/dummy/routes.php b/app/laravel/tests/bundles/dummy/routes.php new file mode 100644 index 000000000..2117e2e08 --- /dev/null +++ b/app/laravel/tests/bundles/dummy/routes.php @@ -0,0 +1,6 @@ +assertTrue($container === Asset::container('foo')); + $this->assertInstanceOf('\\Laravel\\Asset_Container', $container); + } + + /** + * Test the Asset::container method for default container creation. + * + * @group laravel + */ + public function testDefaultContainerCreatedByDefault() + { + $this->assertEquals('default', Asset::container()->name); + } + + /** + * Test the Asset::__callStatic method. + * + * @group laravel + */ + public function testContainerMethodsCanBeDynamicallyCalled() + { + Asset::style('common', 'common.css'); + + $this->assertEquals('common.css', Asset::container()->assets['style']['common']['source']); + } + + /** + * Test the Asset_Container constructor. + * + * @group laravel + */ + public function testNameIsSetOnAssetContainerConstruction() + { + $container = $this->getContainer(); + + $this->assertEquals('foo', $container->name); + } + + /** + * Test the Asset_Container::add method. + * + * @group laravel + */ + public function testAddMethodProperlySniffsAssetType() + { + $container = $this->getContainer(); + + $container->add('jquery', 'jquery.js'); + $container->add('common', 'common.css'); + + $this->assertEquals('jquery.js', $container->assets['script']['jquery']['source']); + $this->assertEquals('common.css', $container->assets['style']['common']['source']); + } + + /** + * Test the Asset_Container::style method. + * + * @group laravel + */ + public function testStyleMethodProperlyRegistersAnAsset() + { + $container = $this->getContainer(); + + $container->style('common', 'common.css'); + + $this->assertEquals('common.css', $container->assets['style']['common']['source']); + } + + /** + * Test the Asset_Container::style method sets media attribute. + * + * @group laravel + */ + public function testStyleMethodProperlySetsMediaAttributeIfNotSet() + { + $container = $this->getContainer(); + + $container->style('common', 'common.css'); + + $this->assertEquals('all', $container->assets['style']['common']['attributes']['media']); + } + + /** + * Test the Asset_Container::style method sets media attribute. + * + * @group laravel + */ + public function testStyleMethodProperlyIgnoresMediaAttributeIfSet() + { + $container = $this->getContainer(); + + $container->style('common', 'common.css', array(), array('media' => 'print')); + + $this->assertEquals('print', $container->assets['style']['common']['attributes']['media']); + } + + /** + * Test the Asset_Container::script method. + * + * @group laravel + */ + public function testScriptMethodProperlyRegistersAnAsset() + { + $container = $this->getContainer(); + + $container->script('jquery', 'jquery.js'); + + $this->assertEquals('jquery.js', $container->assets['script']['jquery']['source']); + } + + /** + * Test the Asset_Container::add method properly sets dependencies. + * + * @group laravel + */ + public function testAddMethodProperlySetsDependencies() + { + $container = $this->getContainer(); + + $container->add('common', 'common.css', 'jquery'); + $container->add('jquery', 'jquery.js', array('jquery-ui')); + + $this->assertEquals(array('jquery'), $container->assets['style']['common']['dependencies']); + $this->assertEquals(array('jquery-ui'), $container->assets['script']['jquery']['dependencies']); + } + + /** + * Test the Asset_Container::add method properly sets attributes. + * + * @group laravel + */ + public function testAddMethodProperlySetsAttributes() + { + $container = $this->getContainer(); + + $container->add('common', 'common.css', array(), array('media' => 'print')); + $container->add('jquery', 'jquery.js', array(), array('defer')); + + $this->assertEquals(array('media' => 'print'), $container->assets['style']['common']['attributes']); + $this->assertEquals(array('defer'), $container->assets['script']['jquery']['attributes']); + } + + /** + * Test the Asset_Container::bundle method. + * + * @group laravel + */ + public function testBundleMethodCorrectlySetsTheAssetBundle() + { + $container = $this->getContainer(); + + $container->bundle('eloquent'); + + $this->assertEquals('eloquent', $container->bundle); + } + + /** + * Test the Asset_Container::path method. + * + * @group laravel + */ + public function testPathMethodReturnsCorrectPathForABundleAsset() + { + $container = $this->getContainer(); + + $container->bundle('eloquent'); + + $this->assertEquals('/bundles/eloquent/foo.jpg', $container->path('foo.jpg')); + } + + /** + * Test the Asset_Container::path method. + * + * @group laravel + */ + public function testPathMethodReturnsCorrectPathForAnApplicationAsset() + { + $container = $this->getContainer(); + + $this->assertEquals('/foo.jpg', $container->path('foo.jpg')); + } + + /** + * Test the Asset_Container::scripts method. + * + * @group laravel + */ + public function testScriptsCanBeRetrieved() + { + $container = $this->getContainer(); + + $container->script('dojo', 'dojo.js', array('jquery-ui')); + $container->script('jquery', 'jquery.js', array('jquery-ui', 'dojo')); + $container->script('jquery-ui', 'jquery-ui.js'); + + $scripts = $container->scripts(); + + $this->assertTrue(strpos($scripts, 'jquery.js') > 0); + $this->assertTrue(strpos($scripts, 'jquery.js') > strpos($scripts, 'jquery-ui.js')); + $this->assertTrue(strpos($scripts, 'dojo.js') > strpos($scripts, 'jquery-ui.js')); + } + + /** + * Test the Asset_Container::styles method. + * + * @group laravel + */ + public function testStylesCanBeRetrieved() + { + $container = $this->getContainer(); + + $container->style('dojo', 'dojo.css', array('jquery-ui'), array('media' => 'print')); + $container->style('jquery', 'jquery.css', array('jquery-ui', 'dojo')); + $container->style('jquery-ui', 'jquery-ui.css'); + + $styles = $container->styles(); + + $this->assertTrue(strpos($styles, 'jquery.css') > 0); + $this->assertTrue(strpos($styles, 'media="print"') > 0); + $this->assertTrue(strpos($styles, 'jquery.css') > strpos($styles, 'jquery-ui.css')); + $this->assertTrue(strpos($styles, 'dojo.css') > strpos($styles, 'jquery-ui.css')); + } + + /** + * Get an asset container instance. + * + * @param string $name + * @return Asset_Container + */ + private function getContainer($name = 'foo') + { + return new Laravel\Asset_Container($name); + } + +} \ No newline at end of file diff --git a/app/laravel/tests/cases/auth.test.php b/app/laravel/tests/cases/auth.test.php new file mode 100644 index 000000000..3be1c1e5f --- /dev/null +++ b/app/laravel/tests/cases/auth.test.php @@ -0,0 +1,356 @@ +user = null; + Session::$instance = null; + Config::set('database.default', 'sqlite'); + } + + /** + * Tear down the test environment. + */ + public function tearDown() + { + $_SERVER['auth.login.stub'] = null; + Cookie::$jar = array(); + Config::$items = array(); + Auth::driver()->user = null; + Session::$instance = null; + Config::set('database.default', 'mysql'); + } + + /** + * Set one of the $_SERVER variables. + * + * @param string $key + * @param string $value + */ + protected function setServerVar($key, $value) + { + $_SERVER[$key] = $value; + + $this->restartRequest(); + } + + /** + * Reinitialize the global request. + * + * @return void + */ + protected function restartRequest() + { + // FIXME: Ugly hack, but old contents from previous requests seem to + // trip up the Foundation class. + $_FILES = array(); + + Request::$foundation = RequestFoundation::createFromGlobals(); + } + + /** + * Test the Auth::user method. + * + * @group laravel + */ + public function testUserMethodReturnsCurrentUser() + { + Auth::driver()->user = 'Taylor'; + + $this->assertEquals('Taylor', Auth::user()); + } + + /** + * Test the Auth::check method. + * + * @group laravel + */ + public function testCheckMethodReturnsTrueWhenUserIsSet() + { + $auth = new AuthUserReturnsDummy; + + $this->assertTrue($auth->check()); + } + + /** + * Test the Auth::check method. + * + * @group laravel + */ + public function testCheckMethodReturnsFalseWhenNoUserIsSet() + { + $auth = new AuthUserReturnsNull; + + $this->assertFalse($auth->check()); + } + + /** + * Test the Auth::guest method. + * + * @group laravel + */ + public function testGuestReturnsTrueWhenNoUserIsSet() + { + $auth = new AuthUserReturnsNull; + + $this->assertTrue($auth->guest()); + } + + /** + * Test the Auth::guest method. + * + * @group laravel + */ + public function testGuestReturnsFalseWhenUserIsSet() + { + $auth = new AuthUserReturnsDummy; + + $this->assertFalse($auth->guest()); + } + + /** + * Test the Auth::user method. + * + * @group laravel + */ + public function testUserMethodReturnsNullWhenNoUserExistsAndNoRecallerExists() + { + Session::$instance = new Payload($this->getMock('Laravel\\Session\\Drivers\\Driver')); + + $this->assertNull(Auth::user()); + } + + /** + * Test the Auth::user method. + * + * @group laravel + */ + public function testUserReturnsUserByID() + { + Session::$instance = new Payload($this->getMock('Laravel\\Session\\Drivers\\Driver')); + + Auth::login(1); + + $this->assertEquals('Taylor Otwell', Auth::user()->name); + + Auth::logout(); + } + + /** + * Test the Auth::user method. + * + * @group laravel + */ + public function testNullReturnedWhenUserIDNotValidInteger() + { + Session::$instance = new Payload($this->getMock('Laravel\\Session\\Drivers\\Driver')); + + Auth::login('asdlkasd'); + + $this->assertNull(Auth::user()); + } + + /** + * Test the Auth::recall method. + * + * @group laravel + */ + public function testUserCanBeRecalledViaCookie() + { + Session::$instance = new Payload($this->getMock('Laravel\\Session\\Drivers\\Driver')); + + $cookie = Crypter::encrypt('1|'.Str::random(40)); + Cookie::forever('authloginstub_remember', $cookie); + + $auth = new AuthLoginStub; + + $this->assertEquals('Taylor Otwell', $auth->user()->name); + + $this->assertTrue($auth->user()->id === $_SERVER['auth.login.stub']['user']); + } + + /** + * Test the Auth::attempt method. + * + * @group laravel + */ + public function testAttemptMethodReturnsFalseWhenCredentialsAreInvalid() + { + $this->assertFalse(Auth::attempt(array('username' => 'foo', 'password' => 'foo'))); + $this->assertFalse(Auth::attempt(array('username' => 'foo', 'password' => null))); + $this->assertFalse(Auth::attempt(array('username' => null, 'password' => null))); + $this->assertFalse(Auth::attempt(array('username' => 'taylor', 'password' => 'password'))); + $this->assertFalse(Auth::attempt(array('username' => 'taylor', 'password' => 232))); + } + + /** + * Test the Auth::attempt method. + * + * @group laravel + */ + public function testAttemptReturnsTrueWhenCredentialsAreCorrect() + { + Session::$instance = new Payload($this->getMock('Laravel\\Session\\Drivers\\Driver')); + + $auth = new AuthLoginStub; + + $this->assertTrue($auth->attempt(array('username' => 'taylor', 'password' => 'password1'))); + $this->assertEquals('1', $_SERVER['auth.login.stub']['user']); + $this->assertFalse($_SERVER['auth.login.stub']['remember']); + + $auth_secure = new AuthLoginStub; + + $this->assertTrue($auth_secure->attempt(array('username' => 'taylor', 'password' => 'password1', 'remember' => true))); + $this->assertEquals('1', $_SERVER['auth.login.stub']['user']); + $this->assertTrue($_SERVER['auth.login.stub']['remember']); + + $auth_secure->logout(); + $auth->logout(); + } + + /** + * Test Auth::login method. + * + * @group laravel + */ + public function testLoginMethodStoresUserKeyInSession() + { + Session::$instance = new Payload($this->getMock('Laravel\\Session\\Drivers\\Driver')); + + $user = new StdClass; + $user->id = 10; + Auth::login($user); + // FIXME: Not sure whether hard-coding the key is a good idea. + $user = Session::$instance->session['data']['laravel_auth_drivers_fluent_login']; + $this->assertEquals(10, $user->id); + + + Auth::logout(); + + Auth::login(5); + $user = Session::$instance->session['data']['laravel_auth_drivers_fluent_login']; + $this->assertEquals(5, $user); + Auth::logout(5); + } + + /** + * Test the Auth::login method. + * + * @group laravel + */ + public function testLoginStoresRememberCookieWhenNeeded() + { + Session::$instance = new Payload($this->getMock('Laravel\\Session\\Drivers\\Driver')); + + $this->setServerVar('HTTPS', 'on'); + + // Set the session vars to make sure remember cookie uses them + Config::set('session.path', 'foo'); + Config::set('session.domain', 'bar'); + Config::set('session.secure', true); + + Auth::login(1, true); + + $this->assertTrue(isset(Cookie::$jar['laravel_auth_drivers_fluent_remember'])); + + $cookie = Cookie::get('laravel_auth_drivers_fluent_remember'); + $cookie = explode('|', Crypter::decrypt($cookie)); + $this->assertEquals(1, $cookie[0]); + $this->assertEquals('foo', Cookie::$jar['laravel_auth_drivers_fluent_remember']['path']); + $this->assertEquals('bar', Cookie::$jar['laravel_auth_drivers_fluent_remember']['domain']); + $this->assertTrue(Cookie::$jar['laravel_auth_drivers_fluent_remember']['secure']); + + Auth::logout(); + + $this->setServerVar('HTTPS', 'off'); + } + + /** + * Test the Auth::logout method. + * + * @group laravel + */ + public function testLogoutMethodLogsOutUser() + { + Session::$instance = new Payload($this->getMock('Laravel\\Session\\Drivers\\Driver')); + + $data = Session::$instance->session['data']['laravel_auth_drivers_fluent_login'] = 1; + + Auth::logout(); + + $this->assertNull(Auth::user()); + + $this->assertFalse(isset(Session::$instance->session['data']['laravel_auth_drivers_fluent_login'])); + $this->assertTrue(Cookie::$jar['laravel_auth_drivers_fluent_remember']['expiration'] < time()); + } + +} + +class AuthUserReturnsNull extends Laravel\Auth\Drivers\Driver { + + public function user() { return null; } + + public function retrieve($id) { return null; } + + public function attempt($arguments = array()) { return null; } + +} + +class AuthUserReturnsDummy extends Laravel\Auth\Drivers\Driver { + + public function user() { return 'Taylor'; } + + public function retrieve($id) { return null; } + + public function attempt($arguments = array()) + { + return $this->login($arguments['username']); + } + +} + +class AuthLoginStub extends Laravel\Auth\Drivers\Fluent { + + public function login($user, $remember = false) + { + if (is_null($remember)) $remember = false; + + $_SERVER['auth.login.stub'] = compact('user', 'remember'); + + return parent::login($user, $remember); + } + + public function logout() + { + parent::logout(); + } + + public function retrieve($id) + { + $user = parent::retrieve($id); + + $_SERVER['auth.login.stub'] = array( + 'user' => $user->id, + 'remember' => false, + ); + + return $user; + } + +} \ No newline at end of file diff --git a/app/laravel/tests/cases/autoloader.test.php b/app/laravel/tests/cases/autoloader.test.php new file mode 100644 index 000000000..80605447c --- /dev/null +++ b/app/laravel/tests/cases/autoloader.test.php @@ -0,0 +1,102 @@ + path('app').'models/foo.php', + )); + + $this->assertEquals(path('app').'models/foo.php', Autoloader::$mappings['Foo']); + } + + /** + * Test the Autoloader::alias method. + * + * @group laravel + */ + public function testAliasesCanBeRegistered() + { + Autoloader::alias('Foo\\Bar', 'Foo'); + + $this->assertEquals('Foo\\Bar', Autoloader::$aliases['Foo']); + } + + /** + * Test the Autoloader::directories method. + * + * @group laravel + */ + public function testPsrDirectoriesCanBeRegistered() + { + Autoloader::directories(array( + path('app').'foo'.DS.'bar', + path('app').'foo'.DS.'baz'.DS.DS, + )); + + $this->assertTrue(in_array(path('app').'foo'.DS.'bar'.DS, Autoloader::$directories)); + $this->assertTrue(in_array(path('app').'foo'.DS.'baz'.DS, Autoloader::$directories)); + } + + /** + * Test the Autoloader::namespaces method. + * + * @group laravel + */ + public function testNamespacesCanBeRegistered() + { + Autoloader::namespaces(array( + 'Autoloader_1' => path('bundle').'autoload'.DS.'models', + 'Autoloader_2' => path('bundle').'autoload'.DS.'libraries'.DS.DS, + )); + + $this->assertEquals(path('bundle').'autoload'.DS.'models'.DS, Autoloader::$namespaces['Autoloader_1\\']); + $this->assertEquals(path('bundle').'autoload'.DS.'libraries'.DS, Autoloader::$namespaces['Autoloader_2\\']); + } + + /** + * Test the loading of PSR-0 models and libraries. + * + * @group laravel + */ + public function testPsrLibrariesAndModelsCanBeLoaded() + { + $this->assertInstanceOf('User', new User); + $this->assertInstanceOf('Repositories\\User', new Repositories\User); + } + + /** + * Test the loading of hard-coded classes. + * + * @group laravel + */ + public function testHardcodedClassesCanBeLoaded() + { + Autoloader::map(array( + 'Autoloader_HardCoded' => path('app').'models'.DS.'autoloader.php', + )); + + $this->assertInstanceOf('Autoloader_HardCoded', new Autoloader_HardCoded); + } + + /** + * Test the loading of classes mapped by namespaces. + * + * @group laravel + */ + public function testClassesMappedByNamespaceCanBeLoaded() + { + Autoloader::namespaces(array( + 'Dashboard' => path('bundle').'dashboard'.DS.'models', + )); + + $this->assertInstanceOf('Dashboard\\Repository', new Dashboard\Repository); + } + +} \ No newline at end of file diff --git a/app/laravel/tests/cases/blade.test.php b/app/laravel/tests/cases/blade.test.php new file mode 100644 index 000000000..fb60cc21a --- /dev/null +++ b/app/laravel/tests/cases/blade.test.php @@ -0,0 +1,120 @@ +assertEquals('', Blade::compile_string($blade1)); + $this->assertEquals('', Blade::compile_string($blade2)); + } + + /** + * Test the compilation of comments statements. + * + * @group laravel + */ + public function testCommentsAreConvertedProperly() + { + $blade1 = "{{-- This is a comment --}}"; + $blade2 = "{{--\nThis is a\nmulti-line\ncomment.\n--}}"; + + $this->assertEquals("\n", Blade::compile_string($blade1)); + $this->assertEquals("\n", Blade::compile_string($blade2)); + } + + /** + * Test the compilation of control structures. + * + * @group laravel + */ + public function testControlStructuresAreCreatedCorrectly() + { + $blade1 = "@if (true)\nfoo\n@endif"; + $blade2 = "@if (count(".'$something'.") > 0)\nfoo\n@endif"; + $blade3 = "@if (true)\nfoo\n@elseif (false)\nbar\n@else\nfoobar\n@endif"; + $blade4 = "@if (true)\nfoo\n@elseif (false)\nbar\n@endif"; + $blade5 = "@if (true)\nfoo\n@else\nbar\n@endif"; + $blade6 = "@unless (count(".'$something'.") > 0)\nfoobar\n@endunless"; + $blade7 = "@for (Foo::all() as ".'$foo'.")\nfoo\n@endfor"; + $blade8 = "@foreach (Foo::all() as ".'$foo'.")\nfoo\n@endforeach"; + $blade9 = "@forelse (Foo::all() as ".'$foo'.")\nfoo\n@empty\nbar\n@endforelse"; + $blade10 = "@while (true)\nfoo\n@endwhile"; + $blade11 = "@while (Foo::bar())\nfoo\n@endwhile"; + + + $this->assertEquals("\nfoo\n", Blade::compile_string($blade1)); + $this->assertEquals(" 0): ?>\nfoo\n", Blade::compile_string($blade2)); + $this->assertEquals("\nfoo\n\nbar\n\nfoobar\n", Blade::compile_string($blade3)); + $this->assertEquals("\nfoo\n\nbar\n", Blade::compile_string($blade4)); + $this->assertEquals("\nfoo\n\nbar\n", Blade::compile_string($blade5)); + $this->assertEquals(" 0))): ?>\nfoobar\n", Blade::compile_string($blade6)); + $this->assertEquals("\nfoo\n", Blade::compile_string($blade7)); + $this->assertEquals("\nfoo\n", Blade::compile_string($blade8)); + $this->assertEquals(" 0): ?>\nfoo\n\nbar\n", Blade::compile_string($blade9)); + $this->assertEquals("\nfoo\n", Blade::compile_string($blade10)); + $this->assertEquals("\nfoo\n", Blade::compile_string($blade11)); + } + + /** + * Test the compilation of yield statements. + * + * @group laravel + */ + public function testYieldsAreCompiledCorrectly() + { + $blade = "@yield('something')"; + + $this->assertEquals("", Blade::compile_string($blade)); + } + + /** + * Test the compilation of section statements. + * + * @group laravel + */ + public function testSectionsAreCompiledCorrectly() + { + $blade = "@section('something')\nfoo\n@endsection"; + + $this->assertEquals("\nfoo\n", Blade::compile_string($blade)); + } + + /** + * Test the compilation of include statements. + * + * @group laravel + */ + public function testIncludesAreCompiledCorrectly() + { + $blade1 = "@include('user.profile')"; + $blade2 = "@include(Config::get('application.default_view', 'user.profile'))"; + + $this->assertEquals("with(get_defined_vars())->render(); ?>", Blade::compile_string($blade1)); + $this->assertEquals("with(get_defined_vars())->render(); ?>", Blade::compile_string($blade2)); + } + + /** + * Test the compilation of render statements. + * + * @group laravel + */ + public function testRendersAreCompiledCorrectly() + { + $blade1 = "@render('user.profile')"; + $blade2 = "@render(Config::get('application.default_view', 'user.profile'))"; + + $this->assertEquals("", Blade::compile_string($blade1)); + $this->assertEquals("", Blade::compile_string($blade2)); + + } +} \ No newline at end of file diff --git a/app/laravel/tests/cases/bundle.test.php b/app/laravel/tests/cases/bundle.test.php new file mode 100644 index 000000000..9826a50fd --- /dev/null +++ b/app/laravel/tests/cases/bundle.test.php @@ -0,0 +1,251 @@ + 'foo-baz')); + $this->assertEquals('foo-baz', Bundle::$bundles['foo-baz']['handles']); + $this->assertFalse(Bundle::$bundles['foo-baz']['auto']); + + Bundle::register('foo-bar', array()); + $this->assertFalse(Bundle::$bundles['foo-baz']['auto']); + $this->assertNull(Bundle::$bundles['foo-bar']['handles']); + + unset(Bundle::$bundles['foo-baz']); + unset(Bundle::$bundles['foo-bar']); + } + + /** + * Test the Bundle::start method. + * + * @group laravel + */ + public function testStartMethodStartsBundle() + { + $_SERVER['bundle.dummy.start'] = 0; + $_SERVER['bundle.dummy.routes'] = 0; + + $_SERVER['started.dummy'] = false; + + Event::listen('laravel.started: dummy', function() + { + $_SERVER['started.dummy'] = true; + }); + + Bundle::register('dummy'); + Bundle::start('dummy'); + + $this->assertTrue($_SERVER['started.dummy']); + $this->assertEquals(1, $_SERVER['bundle.dummy.start']); + $this->assertEquals(1, $_SERVER['bundle.dummy.routes']); + + Bundle::start('dummy'); + + $this->assertEquals(1, $_SERVER['bundle.dummy.start']); + $this->assertEquals(1, $_SERVER['bundle.dummy.routes']); + } + + /** + * Test Bundle::handles method. + * + * @group laravel + */ + public function testHandlesMethodReturnsBundleThatHandlesURI() + { + Bundle::register('foo', array('handles' => 'foo-bar')); + $this->assertEquals('foo', Bundle::handles('foo-bar/admin')); + unset(Bundle::$bundles['foo']); + } + + /** + * Test the Bundle::exist method. + * + * @group laravel + */ + public function testExistMethodIndicatesIfBundleExist() + { + $this->assertTrue(Bundle::exists('dashboard')); + $this->assertFalse(Bundle::exists('foo')); + } + + /** + * Test the Bundle::started method. + * + * @group laravel + */ + public function testStartedMethodIndicatesIfBundleIsStarted() + { + Bundle::register('dummy'); + Bundle::start('dummy'); + $this->assertTrue(Bundle::started('dummy')); + } + + /** + * Test the Bundle::prefix method. + * + * @group laravel + */ + public function testPrefixMethodReturnsCorrectPrefix() + { + $this->assertEquals('dummy::', Bundle::prefix('dummy')); + $this->assertEquals('', Bundle::prefix(DEFAULT_BUNDLE)); + } + + /** + * Test the Bundle::class_prefix method. + * + * @group laravel + */ + public function testClassPrefixMethodReturnsProperClassPrefixForBundle() + { + $this->assertEquals('Dummy_', Bundle::class_prefix('dummy')); + $this->assertEquals('', Bundle::class_prefix(DEFAULT_BUNDLE)); + } + + /** + * Test the Bundle::path method. + * + * @group laravel + */ + public function testPathMethodReturnsCorrectPath() + { + $this->assertEquals(path('app'), Bundle::path(null)); + $this->assertEquals(path('app'), Bundle::path(DEFAULT_BUNDLE)); + $this->assertEquals(path('bundle').'dashboard'.DS, Bundle::path('dashboard')); + } + + /** + * Test the Bundle::asset method. + * + * @group laravel + */ + public function testAssetPathReturnsPathToBundlesAssets() + { + $this->assertEquals('/bundles/dashboard/', Bundle::assets('dashboard')); + $this->assertEquals('/', Bundle::assets(DEFAULT_BUNDLE)); + + Config::set('application.url', ''); + } + + /** + * Test the Bundle::name method. + * + * @group laravel + */ + public function testBundleNameCanBeRetrievedFromIdentifier() + { + $this->assertEquals(DEFAULT_BUNDLE, Bundle::name('something')); + $this->assertEquals(DEFAULT_BUNDLE, Bundle::name('something.else')); + $this->assertEquals('bundle', Bundle::name('bundle::something.else')); + } + + /** + * Test the Bundle::element method. + * + * @group laravel + */ + public function testElementCanBeRetrievedFromIdentifier() + { + $this->assertEquals('something', Bundle::element('something')); + $this->assertEquals('something.else', Bundle::element('something.else')); + $this->assertEquals('something.else', Bundle::element('bundle::something.else')); + } + + /** + * Test the Bundle::identifier method. + * + * @group laravel + */ + public function testIdentifierCanBeConstructed() + { + $this->assertEquals('something.else', Bundle::identifier(DEFAULT_BUNDLE, 'something.else')); + $this->assertEquals('dashboard::something', Bundle::identifier('dashboard', 'something')); + $this->assertEquals('dashboard::something.else', Bundle::identifier('dashboard', 'something.else')); + } + + /** + * Test the Bundle::resolve method. + * + * @group laravel + */ + public function testBundleNamesCanBeResolved() + { + $this->assertEquals(DEFAULT_BUNDLE, Bundle::resolve('foo')); + $this->assertEquals('dashboard', Bundle::resolve('dashboard')); + } + + /** + * Test the Bundle::parse method. + * + * @group laravel + */ + public function testParseMethodReturnsElementAndIdentifier() + { + $this->assertEquals(array('application', 'something'), Bundle::parse('something')); + $this->assertEquals(array('application', 'something.else'), Bundle::parse('something.else')); + $this->assertEquals(array('dashboard', 'something'), Bundle::parse('dashboard::something')); + $this->assertEquals(array('dashboard', 'something.else'), Bundle::parse('dashboard::something.else')); + } + + /** + * Test the Bundle::get method. + * + * @group laravel + */ + public function testOptionMethodReturnsBundleOption() + { + $this->assertFalse(Bundle::option('dashboard', 'auto')); + $this->assertEquals('dashboard', Bundle::option('dashboard', 'location')); + } + + /** + * Test the Bundle::all method. + * + * @group laravel + */ + public function testAllMethodReturnsBundleArray() + { + Bundle::register('foo'); + $this->assertEquals(Bundle::$bundles, Bundle::all()); + unset(Bundle::$bundles['foo']); + } + + /** + * Test the Bundle::names method. + * + * @group laravel + */ + public function testNamesMethodReturnsBundleNames() + { + Bundle::register('foo'); + $this->assertEquals(array('dashboard', 'dummy', 'foo'), Bundle::names()); + unset(Bundle::$bundles['foo']); + } + +} \ No newline at end of file diff --git a/app/laravel/tests/cases/config.test.php b/app/laravel/tests/cases/config.test.php new file mode 100644 index 000000000..573b8cff7 --- /dev/null +++ b/app/laravel/tests/cases/config.test.php @@ -0,0 +1,79 @@ +assertEquals('UTF-8', Config::get('application.encoding')); + $this->assertEquals('mysql', Config::get('database.connections.mysql.driver')); + $this->assertEquals('dashboard', Config::get('dashboard::meta.bundle')); + } + + /** + * Test the Config::has method. + * + * @group laravel + */ + public function testHasMethodIndicatesIfConfigItemExists() + { + $this->assertFalse(Config::has('application.foo')); + $this->assertTrue(Config::has('application.encoding')); + } + + /** + * Test the Config::set method. + * + * @group laravel + */ + public function testConfigItemsCanBeSet() + { + Config::set('application.encoding', 'foo'); + Config::set('dashboard::meta.bundle', 'bar'); + + $this->assertEquals('foo', Config::get('application.encoding')); + $this->assertEquals('bar', Config::get('dashboard::meta.bundle')); + } + + /** + * Test that environment configurations are loaded correctly. + * + * @group laravel + */ + public function testEnvironmentConfigsOverrideNormalConfigurations() + { + $_SERVER['LARAVEL_ENV'] = 'local'; + + $this->assertEquals('sqlite', Config::get('database.default')); + + unset($_SERVER['LARAVEL_ENV']); + } + + /** + * Test that items can be set after the entire file has already been loaded. + * + * @group laravel + */ + public function testItemsCanBeSetAfterEntireFileIsLoaded() + { + Config::get('application'); + Config::set('application.key', 'taylor'); + $application = Config::get('application'); + + $this->assertEquals('taylor', $application['key']); + } + +} \ No newline at end of file diff --git a/app/laravel/tests/cases/controller.test.php b/app/laravel/tests/cases/controller.test.php new file mode 100644 index 000000000..9eb192b91 --- /dev/null +++ b/app/laravel/tests/cases/controller.test.php @@ -0,0 +1,267 @@ +assertEquals('action_index', Controller::call('auth@index')->content); + $this->assertEquals('Admin_Panel_Index', Controller::call('admin.panel@index')->content); + $this->assertEquals('Taylor', Controller::call('auth@profile', array('Taylor'))->content); + $this->assertEquals('Dashboard_Panel_Index', Controller::call('dashboard::panel@index')->content); + } + + /** + * Test basic controller filters are called. + * + * @group laravel + */ + public function testAssignedBeforeFiltersAreRun() + { + $_SERVER['test-all-after'] = false; + $_SERVER['test-all-before'] = false; + + Controller::call('filter@index'); + + $this->assertTrue($_SERVER['test-all-after']); + $this->assertTrue($_SERVER['test-all-before']); + } + + /** + * Test that "only" filters only apply to their assigned methods. + * + * @group laravel + */ + public function testOnlyFiltersOnlyApplyToTheirAssignedMethods() + { + $_SERVER['test-profile-before'] = false; + + Controller::call('filter@index'); + + $this->assertFalse($_SERVER['test-profile-before']); + + Controller::call('filter@profile'); + + $this->assertTrue($_SERVER['test-profile-before']); + } + + /** + * Test that "except" filters only apply to the excluded methods. + * + * @group laravel + */ + public function testExceptFiltersOnlyApplyToTheExlucdedMethods() + { + $_SERVER['test-except'] = false; + + Controller::call('filter@index'); + Controller::call('filter@profile'); + + $this->assertFalse($_SERVER['test-except']); + + Controller::call('filter@show'); + + $this->assertTrue($_SERVER['test-except']); + } + + /** + * Test that filters can be constrained by the request method. + * + * @group laravel + */ + public function testFiltersCanBeConstrainedByRequestMethod() + { + $_SERVER['test-on-post'] = false; + + Request::$foundation->setMethod('GET'); + Controller::call('filter@index'); + + $this->assertFalse($_SERVER['test-on-post']); + + Request::$foundation->setMethod('POST'); + Controller::call('filter@index'); + + $this->assertTrue($_SERVER['test-on-post']); + + $_SERVER['test-on-get-put'] = false; + + Request::$foundation->setMethod('POST'); + Controller::call('filter@index'); + + $this->assertFalse($_SERVER['test-on-get-put']); + + Request::$foundation->setMethod('PUT'); + Controller::call('filter@index'); + + $this->assertTrue($_SERVER['test-on-get-put']); + } + + public function testGlobalBeforeFilterIsNotCalledByController() + { + $_SERVER['before'] = false; + $_SERVER['after'] = false; + + Controller::call('auth@index'); + + $this->assertFalse($_SERVER['before']); + $this->assertFalse($_SERVER['after']); + } + + /** + * Test that before filters can override the controller response. + * + * @group laravel + */ + public function testBeforeFiltersCanOverrideResponses() + { + $this->assertEquals('Filtered!', Controller::call('filter@login')->content); + } + + /** + * Test that after filters do not affect the response. + * + * @group laravel + */ + public function testAfterFiltersDoNotAffectControllerResponse() + { + $this->assertEquals('action_logout', Controller::call('filter@logout')->content); + } + + /** + * Test that filter parameters are passed to the filter. + * + * @group laravel + */ + public function testFilterParametersArePassedToTheFilter() + { + $this->assertEquals('12', Controller::call('filter@edit')->content); + } + + /** + * Test that multiple filters can be assigned to a single method. + * + * @group laravel + */ + public function testMultipleFiltersCanBeAssignedToAnAction() + { + $_SERVER['test-multi-1'] = false; + $_SERVER['test-multi-2'] = false; + + Controller::call('filter@save'); + + $this->assertTrue($_SERVER['test-multi-1']); + $this->assertTrue($_SERVER['test-multi-2']); + } + + /** + * Test Restful controllers respond by request method. + * + * @group laravel + */ + public function testRestfulControllersRespondWithRestfulMethods() + { + Request::$foundation->setMethod('GET'); + //$_SERVER['REQUEST_METHOD'] = 'GET'; + + $this->assertEquals('get_index', Controller::call('restful@index')->content); + + //$_SERVER['REQUEST_METHOD'] = 'PUT'; + Request::$foundation->setMethod('PUT'); + + $this->assertEquals(404, Controller::call('restful@index')->status()); + + //$_SERVER['REQUEST_METHOD'] = 'POST'; + Request::$foundation->setMethod('POST'); + + $this->assertEquals('post_index', Controller::call('restful@index')->content); + } + + /** + * Test that the template is returned by template controllers. + * + * @group laravel + */ + public function testTemplateControllersReturnTheTemplate() + { + $response = Controller::call('template.basic@index'); + + $home = file_get_contents(path('app').'views/home/index.php'); + + $this->assertEquals($home, $response->content); + } + + /** + * Test that controller templates can be named views. + * + * @group laravel + */ + public function testControllerTemplatesCanBeNamedViews() + { + View::name('home.index', 'home'); + + $response = Controller::call('template.named@index'); + + $home = file_get_contents(path('app').'views/home/index.php'); + + $this->assertEquals($home, $response->content); + + View::$names = array(); + } + + /** + * Test that the "layout" method is called on the controller. + * + * @group laravel + */ + public function testTheTemplateCanBeOverriden() + { + $this->assertEquals('Layout', Controller::call('template.override@index')->content); + } + + /** + * Test the Controller::resolve method. + * + * @group laravel + */ + public function testResolveMethodChecksTheIoCContainer() + { + IoC::register('controller: home', function() + { + require_once path('app').'controllers/home.php'; + + $controller = new Home_Controller; + + $controller->foo = 'bar'; + + return $controller; + }); + + $controller = Controller::resolve(DEFAULT_BUNDLE, 'home'); + + $this->assertEquals('bar', $controller->foo); + } + +} \ No newline at end of file diff --git a/app/laravel/tests/cases/cookie.test.php b/app/laravel/tests/cases/cookie.test.php new file mode 100644 index 000000000..37d635539 --- /dev/null +++ b/app/laravel/tests/cases/cookie.test.php @@ -0,0 +1,134 @@ +restartRequest(); + } + + /** + * Reinitialize the global request. + * + * @return void + */ + protected function restartRequest() + { + // FIXME: Ugly hack, but old contents from previous requests seem to + // trip up the Foundation class. + $_FILES = array(); + + Request::$foundation = RequestFoundation::createFromGlobals(); + } + + /** + * Test Cookie::has method. + * + * @group laravel + */ + public function testHasMethodIndicatesIfCookieInSet() + { + Cookie::$jar['foo'] = array('value' => Cookie::hash('bar').'+bar'); + $this->assertTrue(Cookie::has('foo')); + $this->assertFalse(Cookie::has('bar')); + + Cookie::put('baz', 'foo'); + $this->assertTrue(Cookie::has('baz')); + } + + /** + * Test the Cookie::get method. + * + * @group laravel + */ + public function testGetMethodCanReturnValueOfCookies() + { + Cookie::$jar['foo'] = array('value' => Cookie::hash('bar').'+bar'); + $this->assertEquals('bar', Cookie::get('foo')); + + Cookie::put('bar', 'baz'); + $this->assertEquals('baz', Cookie::get('bar')); + } + + /** + * Test Cookie::forever method. + * + * @group laravel + */ + public function testForeverShouldUseATonOfMinutes() + { + Cookie::forever('foo', 'bar'); + $this->assertEquals(Cookie::hash('bar').'+bar', Cookie::$jar['foo']['value']); + + // Shouldn't be able to test this cause while we indicate -2000 seconds + // cookie expiration store timestamp. + // $this->assertEquals(525600, Cookie::$jar['foo']['expiration']); + + $this->setServerVar('HTTPS', 'on'); + + Cookie::forever('bar', 'baz', 'path', 'domain', true); + $this->assertEquals('path', Cookie::$jar['bar']['path']); + $this->assertEquals('domain', Cookie::$jar['bar']['domain']); + $this->assertTrue(Cookie::$jar['bar']['secure']); + + $this->setServerVar('HTTPS', 'off'); + } + + /** + * Test the Cookie::forget method. + * + * @group laravel + */ + public function testForgetSetsCookieWithExpiration() + { + Cookie::forget('bar', 'path', 'domain'); + + // Shouldn't be able to test this cause while we indicate -2000 seconds + // cookie expiration store timestamp. + //$this->assertEquals(-2000, Cookie::$jar['bar']['expiration']); + + $this->assertEquals('path', Cookie::$jar['bar']['path']); + $this->assertEquals('domain', Cookie::$jar['bar']['domain']); + $this->assertFalse(Cookie::$jar['bar']['secure']); + } + +} \ No newline at end of file diff --git a/app/laravel/tests/cases/database.test.php b/app/laravel/tests/cases/database.test.php new file mode 100644 index 000000000..84b1faaf6 --- /dev/null +++ b/app/laravel/tests/cases/database.test.php @@ -0,0 +1,74 @@ +assertTrue(isset(DB::$connections[Config::get('database.default')])); + + $connection = DatabaseConnectStub::connection('mysql'); + $this->assertTrue(isset(DB::$connections['mysql'])); + $this->assertEquals(DB::$connections['mysql']->pdo->laravel_config, Config::get('database.connections.mysql')); + } + + /** + * Test the DB::profile method. + * + * @group laravel + */ + public function testProfileMethodReturnsQueries() + { + Laravel\Database\Connection::$queries = array('Taylor'); + $this->assertEquals(array('Taylor'), DB::profile()); + Laravel\Database\Connection::$queries = array(); + } + + /** + * Test the __callStatic method. + * + * @group laravel + */ + public function testConnectionMethodsCanBeCalledStaticly() + { + $this->assertEquals('sqlite', DB::driver()); + } + +} + +class DatabaseConnectStub extends Laravel\Database { + + protected static function connect($config) { return new PDOStub($config); } + +} + +class PDOStub extends PDO { + + public $laravel_config; + + public function __construct($config) { $this->laravel_config = $config; } + + public function foo() { return 'foo'; } + +} \ No newline at end of file diff --git a/app/laravel/tests/cases/eloquent.test.php b/app/laravel/tests/cases/eloquent.test.php new file mode 100644 index 000000000..ac489f396 --- /dev/null +++ b/app/laravel/tests/cases/eloquent.test.php @@ -0,0 +1,292 @@ + 'Taylor', 'age' => 25, 'setter' => 'foo'); + + $model = new Model($array); + + $this->assertEquals('Taylor', $model->name); + $this->assertEquals(25, $model->age); + $this->assertEquals('setter: foo', $model->setter); + } + + /** + * Test the Model::fill method. + * + * @group laravel + */ + public function testAttributesAreSetByFillMethod() + { + $array = array('name' => 'Taylor', 'age' => 25, 'setter' => 'foo'); + + $model = new Model(); + $model->fill($array); + + $this->assertEquals('Taylor', $model->name); + $this->assertEquals(25, $model->age); + $this->assertEquals('setter: foo', $model->setter); + } + + /** + * Test the Model::fill_raw method. + * + * @group laravel + */ + public function testAttributesAreSetByFillRawMethod() + { + $array = array('name' => 'Taylor', 'age' => 25, 'setter' => 'foo'); + + $model = new Model(); + $model->fill_raw($array); + + $this->assertEquals($array, $model->attributes); + } + + /** + * Test the Model::fill method with accessible. + * + * @group laravel + */ + public function testAttributesAreSetByFillMethodWithAccessible() + { + Model::$accessible = array('name', 'age'); + + $array = array('name' => 'Taylor', 'age' => 25, 'foo' => 'bar'); + + $model = new Model(); + $model->fill($array); + + $this->assertEquals('Taylor', $model->name); + $this->assertEquals(25, $model->age); + $this->assertNull($model->foo); + + Model::$accessible = null; + } + + /** + * Test the Model::fill method with empty accessible array. + * + * @group laravel + */ + public function testAttributesAreSetByFillMethodWithEmptyAccessible() + { + Model::$accessible = array(); + + $array = array('name' => 'Taylor', 'age' => 25, 'foo' => 'bar'); + + $model = new Model(); + $model->fill($array); + + $this->assertEquals(array(), $model->attributes); + $this->assertNull($model->name); + $this->assertNull($model->age); + $this->assertNull($model->foo); + + Model::$accessible = null; + } + + /** + * Test the Model::fill_raw method with accessible. + * + * @group laravel + */ + public function testAttributesAreSetByFillRawMethodWithAccessible() + { + Model::$accessible = array('name', 'age'); + + $array = array('name' => 'taylor', 'age' => 25, 'setter' => 'foo'); + + $model = new Model(); + $model->fill_raw($array); + + $this->assertEquals($array, $model->attributes); + + Model::$accessible = null; + } + + /** + * Test the Model::__set method. + * + * @group laravel + */ + public function testAttributeMagicSetterMethodChangesAttribute() + { + Model::$accessible = array('setter'); + + $array = array('setter' => 'foo', 'getter' => 'bar'); + + $model = new Model($array); + $model->setter = 'bar'; + $model->getter = 'foo'; + + $this->assertEquals('setter: bar', $model->get_attribute('setter')); + $this->assertEquals('foo', $model->get_attribute('getter')); + + Model::$accessible = null; + } + + /** + * Test the Model::__get method. + * + * @group laravel + */ + public function testAttributeMagicGetterMethodReturnsAttribute() + { + $array = array('setter' => 'foo', 'getter' => 'bar'); + + $model = new Model($array); + + $this->assertEquals('setter: foo', $model->setter); + $this->assertEquals('getter: bar', $model->getter); + } + + /** + * Test the Model::set_* method. + * + * @group laravel + */ + public function testAttributeSetterMethodChangesAttribute() + { + Model::$accessible = array('setter'); + + $array = array('setter' => 'foo', 'getter' => 'bar'); + + $model = new Model($array); + $model->set_setter('bar'); + $model->set_getter('foo'); + + $this->assertEquals('setter: bar', $model->get_attribute('setter')); + $this->assertEquals('foo', $model->get_attribute('getter')); + + Model::$accessible = null; + } + + /** + * Test the Model::get_* method. + * + * @group laravel + */ + public function testAttributeGetterMethodReturnsAttribute() + { + $array = array('setter' => 'foo', 'getter' => 'bar'); + + $model = new Model($array); + + $this->assertEquals('setter: foo', $model->get_setter()); + $this->assertEquals('getter: bar', $model->get_getter()); + } + + /** + * Test determination of dirty/changed attributes. + * + * @group laravel + */ + public function testDeterminationOfChangedAttributes() + { + $array = array('name' => 'Taylor', 'age' => 25, 'foo' => null); + + $model = new Model($array, true); + $model->name = 'Otwell'; + $model->new = null; + + $this->assertTrue($model->changed('name')); + $this->assertFalse($model->changed('age')); + $this->assertFalse($model->changed('foo')); + $this->assertFalse($model->changed('new')); + $this->assertTrue($model->dirty()); + $this->assertEquals(array('name' => 'Otwell', 'new' => null), $model->get_dirty()); + + $model->sync(); + + $this->assertFalse($model->changed('name')); + $this->assertFalse($model->changed('age')); + $this->assertFalse($model->changed('foo')); + $this->assertFalse($model->changed('new')); + $this->assertFalse($model->dirty()); + $this->assertEquals(array(), $model->get_dirty()); + } + + /** + * Test the Model::purge method. + * + * @group laravel + */ + public function testAttributePurge() + { + $array = array('name' => 'Taylor', 'age' => 25); + + $model = new Model($array); + $model->name = 'Otwell'; + $model->age = 26; + + $model->purge('name'); + + $this->assertFalse($model->changed('name')); + $this->assertNull($model->name); + $this->assertTrue($model->changed('age')); + $this->assertEquals(26, $model->age); + $this->assertEquals(array('age' => 26), $model->get_dirty()); + } + + /** + * Test the Model::table method. + * + * @group laravel + */ + public function testTableMethodReturnsCorrectName() + { + $model = new Model(); + $this->assertEquals('models', $model->table()); + + Model::$table = 'table'; + $this->assertEquals('table', $model->table()); + + Model::$table = null; + $this->assertEquals('models', $model->table()); + } + + /** + * Test the Model::to_array method. + * + * @group laravel + */ + public function testConvertingToArray() + { + Model::$hidden = array('password', 'hidden'); + + $array = array('name' => 'Taylor', 'age' => 25, 'password' => 'laravel', 'null' => null); + + $model = new Model($array); + + $first = new Model(array('first' => 'foo', 'password' => 'hidden')); + $second = new Model(array('second' => 'bar', 'password' => 'hidden')); + $third = new Model(array('third' => 'baz', 'password' => 'hidden')); + + $model->relationships['one'] = new Model(array('foo' => 'bar', 'password' => 'hidden')); + $model->relationships['many'] = array($first, $second, $third); + $model->relationships['hidden'] = new Model(array('should' => 'visible')); + $model->relationships['null'] = null; + + $this->assertEquals(array( + 'name' => 'Taylor', 'age' => 25, 'null' => null, + 'one' => array('foo' => 'bar'), + 'many' => array( + array('first' => 'foo'), + array('second' => 'bar'), + array('third' => 'baz'), + ), + 'hidden' => array('should' => 'visible'), + 'null' => null, + ), $model->to_array()); + + } + +} \ No newline at end of file diff --git a/app/laravel/tests/cases/event.test.php b/app/laravel/tests/cases/event.test.php new file mode 100644 index 000000000..520638330 --- /dev/null +++ b/app/laravel/tests/cases/event.test.php @@ -0,0 +1,43 @@ +assertEquals(1, $responses[0]); + $this->assertEquals(2, $responses[1]); + } + + /** + * Test parameters can be passed to event listeners. + * + * @group laravel + */ + public function testParametersCanBePassedToEvents() + { + Event::listen('test.event', function($var) { return $var; }); + + $responses = Event::fire('test.event', array('Taylor')); + + $this->assertEquals('Taylor', $responses[0]); + } + +} \ No newline at end of file diff --git a/app/laravel/tests/cases/fluent.test.php b/app/laravel/tests/cases/fluent.test.php new file mode 100644 index 000000000..87c0e0dbb --- /dev/null +++ b/app/laravel/tests/cases/fluent.test.php @@ -0,0 +1,50 @@ + 'Taylor', 'age' => 25); + + $fluent = new Fluent($array); + + $this->assertEquals($array, $fluent->attributes); + } + + /** + * Test the Fluent::get method. + * + * @group laravel + */ + public function testGetMethodReturnsAttribute() + { + $fluent = new Fluent(array('name' => 'Taylor')); + + $this->assertEquals('Taylor', $fluent->get('name')); + $this->assertEquals('Default', $fluent->get('foo', 'Default')); + $this->assertEquals('Taylor', $fluent->name); + $this->assertNull($fluent->foo); + } + + public function testMagicMethodsCanBeUsedToSetAttributes() + { + $fluent = new Fluent; + + $fluent->name = 'Taylor'; + $fluent->developer(); + $fluent->age(25); + + $this->assertEquals('Taylor', $fluent->name); + $this->assertTrue($fluent->developer); + $this->assertEquals(25, $fluent->age); + $this->assertInstanceOf('Laravel\\Fluent', $fluent->programmer()); + } + +} \ No newline at end of file diff --git a/app/laravel/tests/cases/form.test.php b/app/laravel/tests/cases/form.test.php new file mode 100644 index 000000000..5f6f7fa35 --- /dev/null +++ b/app/laravel/tests/cases/form.test.php @@ -0,0 +1,451 @@ + 'UTF-16', 'class' => 'form')); + $form4 = Form::open('foobar', 'DELETE', array('class' => 'form')); + + $this->assertEquals('
    ', $form1); + $this->assertEquals('', $form2); + $this->assertEquals('', $form3); + $this->assertEquals('', $form4); + } + + /** + * Test the compilation of opening a secure form + * + * @group laravel + */ + public function testOpeningFormSecure() + { + $form1 = Form::open_secure('foobar', 'GET'); + $form2 = Form::open_secure('foobar', 'POST'); + $form3 = Form::open_secure('foobar', 'PUT', array('accept-charset' => 'UTF-16', 'class' => 'form')); + $form4 = Form::open_secure('foobar', 'DELETE', array('class' => 'form')); + + $this->assertEquals('', $form1); + $this->assertEquals('', $form2); + $this->assertEquals('', $form3); + $this->assertEquals('', $form4); + } + + /** + * Test the compilation of opening a form for files + * + * @group laravel + */ + public function testOpeningFormForFile() + { + $form1 = Form::open_for_files('foobar', 'GET'); + $form2 = Form::open_for_files('foobar', 'POST'); + $form3 = Form::open_for_files('foobar', 'PUT', array('accept-charset' => 'UTF-16', 'class' => 'form')); + $form4 = Form::open_for_files('foobar', 'DELETE', array('class' => 'form')); + + $this->assertEquals('', $form1); + $this->assertEquals('', $form2); + $this->assertEquals('', $form3); + $this->assertEquals('', $form4); + } + + /** + * Test the compilation of opening a secure form for files + * + * @group laravel + */ + public function testOpeningFormSecureForFile() + { + $form1 = Form::open_secure_for_files('foobar', 'GET'); + $form2 = Form::open_secure_for_files('foobar', 'POST'); + $form3 = Form::open_secure_for_files('foobar', 'PUT', array('accept-charset' => 'UTF-16', 'class' => 'form')); + $form4 = Form::open_secure_for_files('foobar', 'DELETE', array('class' => 'form')); + + $this->assertEquals('', $form1); + $this->assertEquals('', $form2); + $this->assertEquals('', $form3); + $this->assertEquals('', $form4); + } + + /** + * Test the compilation of closing a form + * + * @group laravel + */ + public function testClosingForm() + { + $this->assertEquals('
    ', Form::close()); + } + + /** + * Test the compilation of form label + * + * @group laravel + */ + public function testFormLabel() + { + $form1 = Form::label('foo', 'Foobar'); + $form2 = Form::label('foo', 'Foobar', array('class' => 'control-label')); + + $this->assertEquals('', $form1); + $this->assertEquals('', $form2); + } + + /** + * Test the compilation of form input + * + * @group laravel + */ + public function testFormInput() + { + $form1 = Form::input('text', 'foo'); + $form2 = Form::input('text', 'foo', 'foobar'); + $form3 = Form::input('date', 'foobar', null, array('class' => 'span2')); + + $this->assertEquals('', $form1); + $this->assertEquals('', $form2); + $this->assertEquals('', $form3); + } + + /** + * Test the compilation of form text + * + * @group laravel + */ + public function testFormText() + { + $form1 = Form::input('text', 'foo'); + $form2 = Form::text('foo'); + $form3 = Form::text('foo', 'foobar'); + $form4 = Form::text('foo', null, array('class' => 'span2')); + + $this->assertEquals('', $form1); + $this->assertEquals($form1, $form2); + $this->assertEquals('', $form3); + $this->assertEquals('', $form4); + } + + /** + * Test the compilation of form password + * + * @group laravel + */ + public function testFormPassword() + { + $form1 = Form::input('password', 'foo'); + $form2 = Form::password('foo'); + $form3 = Form::password('foo', array('class' => 'span2')); + + $this->assertEquals('', $form1); + $this->assertEquals($form1, $form2); + $this->assertEquals('', $form3); + } + + /** + * Test the compilation of form hidden + * + * @group laravel + */ + public function testFormHidden() + { + $form1 = Form::input('hidden', 'foo'); + $form2 = Form::hidden('foo'); + $form3 = Form::hidden('foo', 'foobar'); + $form4 = Form::hidden('foo', null, array('class' => 'span2')); + + $this->assertEquals('', $form1); + $this->assertEquals($form1, $form2); + $this->assertEquals('', $form3); + $this->assertEquals('', $form4); + } + + /** + * Test the compilation of form search + * + * @group laravel + */ + public function testFormSearch() + { + $form1 = Form::input('search', 'foo'); + $form2 = Form::search('foo'); + $form3 = Form::search('foo', 'foobar'); + $form4 = Form::search('foo', null, array('class' => 'span2')); + + $this->assertEquals('', $form1); + $this->assertEquals($form1, $form2); + $this->assertEquals('', $form3); + $this->assertEquals('', $form4); + } + + /** + * Test the compilation of form email + * + * @group laravel + */ + public function testFormEmail() + { + $form1 = Form::input('email', 'foo'); + $form2 = Form::email('foo'); + $form3 = Form::email('foo', 'foobar'); + $form4 = Form::email('foo', null, array('class' => 'span2')); + + $this->assertEquals('', $form1); + $this->assertEquals($form1, $form2); + $this->assertEquals('', $form3); + $this->assertEquals('', $form4); + } + + /** + * Test the compilation of form telephone + * + * @group laravel + */ + public function testFormTelephone() + { + $form1 = Form::input('tel', 'foo'); + $form2 = Form::telephone('foo'); + $form3 = Form::telephone('foo', 'foobar'); + $form4 = Form::telephone('foo', null, array('class' => 'span2')); + + $this->assertEquals('', $form1); + $this->assertEquals($form1, $form2); + $this->assertEquals('', $form3); + $this->assertEquals('', $form4); + } + + /** + * Test the compilation of form url + * + * @group laravel + */ + public function testFormUrl() + { + $form1 = Form::input('url', 'foo'); + $form2 = Form::url('foo'); + $form3 = Form::url('foo', 'foobar'); + $form4 = Form::url('foo', null, array('class' => 'span2')); + + $this->assertEquals('', $form1); + $this->assertEquals($form1, $form2); + $this->assertEquals('', $form3); + $this->assertEquals('', $form4); + } + + /** + * Test the compilation of form number + * + * @group laravel + */ + public function testFormNumber() + { + $form1 = Form::input('number', 'foo'); + $form2 = Form::number('foo'); + $form3 = Form::number('foo', 'foobar'); + $form4 = Form::number('foo', null, array('class' => 'span2')); + + $this->assertEquals('', $form1); + $this->assertEquals($form1, $form2); + $this->assertEquals('', $form3); + $this->assertEquals('', $form4); + } + + /** + * Test the compilation of form date + * + * @group laravel + */ + public function testFormDate() + { + $form1 = Form::input('date', 'foo'); + $form2 = Form::date('foo'); + $form3 = Form::date('foo', 'foobar'); + $form4 = Form::date('foo', null, array('class' => 'span2')); + + $this->assertEquals('', $form1); + $this->assertEquals($form1, $form2); + $this->assertEquals('', $form3); + $this->assertEquals('', $form4); + } + + /** + * Test the compilation of form file + * + * @group laravel + */ + public function testFormFile() + { + $form1 = Form::input('file', 'foo'); + $form2 = Form::file('foo'); + $form3 = Form::file('foo', array('class' => 'span2')); + + $this->assertEquals('', $form1); + $this->assertEquals($form1, $form2); + $this->assertEquals('', $form3); + } + + /** + * Test the compilation of form textarea + * + * @group laravel + */ + public function testFormTextarea() + { + $form1 = Form::textarea('foo'); + $form2 = Form::textarea('foo', 'foobar'); + $form3 = Form::textarea('foo', null, array('class' => 'span2')); + + $this->assertEquals('', $form1); + $this->assertEquals('', $form2); + $this->assertEquals('', $form3); + } + + /** + * Test the compilation of form select + * + * @group laravel + */ + public function testFormSelect() + { + $select1 = array( + 'foobar' => 'Foobar', + 'hello' => 'Hello World', + ); + + $select2 = array( + 'foo' => array( + 'foobar' => 'Foobar', + ), + 'hello' => 'Hello World', + ); + + $form1 = Form::select('foo'); + $form2 = Form::select('foo', $select1, 'foobar'); + $form3 = Form::select('foo', $select1, null, array('class' => 'span2')); + $form4 = Form::select('foo', $select2, 'foobar'); + + $this->assertEquals('', $form1); + $this->assertEquals('', $form2); + $this->assertEquals('', $form3); + $this->assertEquals('', $form4); + } + + /** + * Test the compilation of form checkbox + * + * @group laravel + */ + public function testFormCheckbox() + { + $form1 = Form::input('checkbox', 'foo'); + $form2 = Form::checkbox('foo'); + $form3 = Form::checkbox('foo', 'foobar', true); + $form4 = Form::checkbox('foo', 'foobar', false, array('class' => 'span2')); + + $this->assertEquals('', $form1); + $this->assertEquals('', $form2); + $this->assertEquals('', $form3); + $this->assertEquals('', $form4); + } + + /** + * Test the compilation of form date + * + * @group laravel + */ + public function testFormRadio() + { + $form1 = Form::input('radio', 'foo'); + $form2 = Form::radio('foo'); + $form3 = Form::radio('foo', 'foobar', true); + $form4 = Form::radio('foo', 'foobar', false, array('class' => 'span2')); + + $this->assertEquals('', $form1); + $this->assertEquals('', $form2); + $this->assertEquals('', $form3); + $this->assertEquals('', $form4); + } + + /** + * Test the compilation of form submit + * + * @group laravel + */ + public function testFormSubmit() + { + $form1 = Form::submit('foo'); + $form2 = Form::submit('foo', array('class' => 'span2')); + + $this->assertEquals('', $form1); + $this->assertEquals('', $form2); + } + + /** + * Test the compilation of form reset + * + * @group laravel + */ + public function testFormReset() + { + $form1 = Form::reset('foo'); + $form2 = Form::reset('foo', array('class' => 'span2')); + + $this->assertEquals('', $form1); + $this->assertEquals('', $form2); + } + + /** + * Test the compilation of form image + * + * @group laravel + */ + public function testFormImage() + { + $form1 = Form::image('foo/bar', 'foo'); + $form2 = Form::image('foo/bar', 'foo', array('class' => 'span2')); + $form3 = Form::image('http://google.com/foobar', 'foobar'); + + $this->assertEquals('', $form1); + $this->assertEquals('', $form2); + $this->assertEquals('', $form3); + + } + + /** + * Test the compilation of form button + * + * @group laravel + */ + public function testFormButton() + { + $form1 = Form::button('foo'); + $form2 = Form::button('foo', array('class' => 'span2')); + + $this->assertEquals('', $form1); + $this->assertEquals('', $form2); + } +} \ No newline at end of file diff --git a/app/laravel/tests/cases/hash.test.php b/app/laravel/tests/cases/hash.test.php new file mode 100644 index 000000000..4dca27138 --- /dev/null +++ b/app/laravel/tests/cases/hash.test.php @@ -0,0 +1,37 @@ +assertTrue(strlen(Hash::make('taylor')) == 60); + } + + /** + * Test the Hash::check method. + * + * @group laravel + */ + public function testHashCheckFailsWhenNotMatching() + { + $hash = Hash::make('taylor'); + + $this->assertFalse(Hash::check('foo', $hash)); + } + + /** + * Test the Hash::check method. + * + * @group laravel + */ + public function testHashCheckPassesWhenMatches() + { + $this->assertTrue(Hash::check('taylor', Hash::make('taylor'))); + } + +} \ No newline at end of file diff --git a/app/laravel/tests/cases/html.test.php b/app/laravel/tests/cases/html.test.php new file mode 100644 index 000000000..3af4efde3 --- /dev/null +++ b/app/laravel/tests/cases/html.test.php @@ -0,0 +1,242 @@ + 'text/javascript')); + + $this->assertEquals(''."\n", $html1); + $this->assertEquals(''."\n", $html2); + $this->assertEquals(''."\n", $html3); + } + + /** + * Test generating a link to CSS files + * + * @group laravel + */ + public function testGeneratingStyle() + { + $html1 = HTML::style('foo.css'); + $html2 = HTML::style('http://netdna.bootstrapcdn.com/twitter-bootstrap/2.2.1/js/bootstrap.min.js'); + $html3 = HTML::style('foo.css', array('media' => 'print')); + + $this->assertEquals(''."\n", $html1); + $this->assertEquals(''."\n", $html2); + $this->assertEquals(''."\n", $html3); + } + + /** + * Test generating proper span + * + * @group laravel + */ + public function testGeneratingSpan() + { + $html1 = HTML::span('foo'); + $html2 = HTML::span('foo', array('class' => 'badge')); + + $this->assertEquals('foo', $html1); + $this->assertEquals('foo', $html2); + } + + /** + * Test generating proper link + * + * @group laravel + */ + public function testGeneratingLink() + { + $html1 = HTML::link('foo'); + $html2 = HTML::link('foo', 'Foobar'); + $html3 = HTML::link('foo', 'Foobar', array('class' => 'btn')); + $html4 = HTML::link('http://google.com', 'Google'); + + $this->assertEquals('http://localhost/index.php/foo', $html1); + $this->assertEquals('Foobar', $html2); + $this->assertEquals('Foobar', $html3); + $this->assertEquals('Google', $html4); + } + + /** + * Test generating proper link to secure + * + * @group laravel + */ + public function testGeneratingLinkToSecure() + { + $html1 = HTML::link_to_secure('foo'); + $html2 = HTML::link_to_secure('foo', 'Foobar'); + $html3 = HTML::link_to_secure('foo', 'Foobar', array('class' => 'btn')); + $html4 = HTML::link_to_secure('http://google.com', 'Google'); + + $this->assertEquals('https://localhost/index.php/foo', $html1); + $this->assertEquals('Foobar', $html2); + $this->assertEquals('Foobar', $html3); + $this->assertEquals('Google', $html4); + } + + /** + * Test generating proper link to asset + * + * @group laravel + */ + public function testGeneratingAssetLink() + { + $html1 = HTML::link_to_asset('foo.css'); + $html2 = HTML::link_to_asset('foo.css', 'Foobar'); + $html3 = HTML::link_to_asset('foo.css', 'Foobar', array('class' => 'btn')); + $html4 = HTML::link_to_asset('http://google.com/images.jpg', 'Google'); + + $this->assertEquals('http://localhost/foo.css', $html1); + $this->assertEquals('Foobar', $html2); + $this->assertEquals('Foobar', $html3); + $this->assertEquals('Google', $html4); + } + + /** + * Test generating proper link to secure asset + * + * @group laravel + */ + public function testGeneratingAssetLinkToSecure() + { + $html1 = HTML::link_to_secure_asset('foo.css'); + $html2 = HTML::link_to_secure_asset('foo.css', 'Foobar'); + $html3 = HTML::link_to_secure_asset('foo.css', 'Foobar', array('class' => 'btn')); + $html4 = HTML::link_to_secure_asset('http://google.com/images.jpg', 'Google'); + + $this->assertEquals('https://localhost/foo.css', $html1); + $this->assertEquals('Foobar', $html2); + $this->assertEquals('Foobar', $html3); + $this->assertEquals('Google', $html4); + } + + /** + * Test generating proper link to route + * + * @group laravel + */ + public function testGeneratingLinkToRoute() + { + Route::get('dashboard', array('as' => 'foo')); + + $html1 = HTML::link_to_route('foo'); + $html2 = HTML::link_to_route('foo', 'Foobar'); + $html3 = HTML::link_to_route('foo', 'Foobar', array(), array('class' => 'btn')); + + $this->assertEquals('http://localhost/index.php/dashboard', $html1); + $this->assertEquals('Foobar', $html2); + $this->assertEquals('Foobar', $html3); + } + + /** + * Test generating proper link to action + * + * @group laravel + */ + public function testGeneratingLinkToAction() + { + $html1 = HTML::link_to_action('foo@bar'); + $html2 = HTML::link_to_action('foo@bar', 'Foobar'); + $html3 = HTML::link_to_action('foo@bar', 'Foobar', array(), array('class' => 'btn')); + + $this->assertEquals('http://localhost/index.php/foo/bar', $html1); + $this->assertEquals('Foobar', $html2); + $this->assertEquals('Foobar', $html3); + } + + /** + * Test generating proper listing + * + * @group laravel + */ + public function testGeneratingListing() + { + $list = array( + 'foo', + 'foobar' => array( + 'hello', + 'hello world', + ), + ); + + $html1 = HTML::ul($list); + $html2 = HTML::ul($list, array('class' => 'nav')); + $html3 = HTML::ol($list); + $html4 = HTML::ol($list, array('class' => 'nav')); + + $this->assertEquals('
    • foo
    • foobar
      • hello
      • hello world
    ', $html1); + $this->assertEquals('', $html2); + $this->assertEquals('
    1. foo
    2. foobar
      1. hello
      2. hello world
    ', $html3); + $this->assertEquals('', $html4); + } + + /** + * Test generating proper listing + * + * @group laravel + */ + public function testGeneratingDefinition() + { + $definition = array( + 'foo' => 'foobar', + 'hello' => 'hello world', + ); + + $html1 = HTML::dl($definition); + $html2 = HTML::dl($definition, array('class' => 'nav')); + + $this->assertEquals('
    foo
    foobar
    hello
    hello world
    ', $html1); + $this->assertEquals('', $html2); + } + + /** + * Test generating proper image link + * + * @group laravel + */ + public function testGeneratingAssetLinkImage() + { + $html1 = HTML::image('foo.jpg'); + $html2 = HTML::image('foo.jpg', 'Foobar'); + $html3 = HTML::image('foo.jpg', 'Foobar', array('class' => 'btn')); + $html4 = HTML::image('http://google.com/images.jpg', 'Google'); + + $this->assertEquals('', $html1); + $this->assertEquals('Foobar', $html2); + $this->assertEquals('Foobar', $html3); + $this->assertEquals('Google', $html4); + } +} \ No newline at end of file diff --git a/app/laravel/tests/cases/input.test.php b/app/laravel/tests/cases/input.test.php new file mode 100644 index 000000000..5a1890f06 --- /dev/null +++ b/app/laravel/tests/cases/input.test.php @@ -0,0 +1,174 @@ +request->add(array('name' => 'Taylor')); + + $_FILES = array('age' => 25); + + $this->assertEquals(Input::all(), array('name' => 'Taylor', 'age' => 25)); + } + + /** + * Test the Input::has method. + * + * @group laravel + */ + public function testHasMethodIndicatesTheExistenceOfInput() + { + $this->assertFalse(Input::has('foo')); + + Request::foundation()->request->add(array('name' => 'Taylor')); + + $this->assertTrue(Input::has('name')); + } + + /** + * Test the Input::get method. + * + * @group laravel + */ + public function testGetMethodReturnsInputValue() + { + Request::foundation()->request->add(array('name' => 'Taylor')); + + $this->assertEquals('Taylor', Input::get('name')); + $this->assertEquals('Default', Input::get('foo', 'Default')); + } + + /** + * Test the Input::only method. + * + * @group laravel + */ + public function testOnlyMethodReturnsSubsetOfInput() + { + Request::foundation()->request->add(array('name' => 'Taylor', 'age' => 25)); + + $this->assertEquals(array('name' => 'Taylor'), Input::only(array('name'))); + } + + /** + * Test the Input::except method. + * + * @group laravel + */ + public function testExceptMethodReturnsSubsetOfInput() + { + Request::foundation()->request->add(array('name' => 'Taylor', 'age' => 25)); + + $this->assertEquals(array('age' => 25), Input::except(array('name'))); + } + + /** + * Test the Input::old method. + * + * @group laravel + */ + public function testOldInputCanBeRetrievedFromSession() + { + $this->setSession(); + + Session::$instance->session['data']['laravel_old_input'] = array('name' => 'Taylor'); + + $this->assertNull(Input::old('foo')); + $this->assertTrue(Input::had('name')); + $this->assertFalse(Input::had('foo')); + $this->assertEquals('Taylor', Input::old('name')); + } + + /** + * Test the Input::file method. + * + * @group laravel + */ + public function testFileMethodReturnsFromFileArray() + { + $_FILES['foo'] = array('name' => 'Taylor', 'size' => 100); + + $this->assertEquals('Taylor', Input::file('foo.name')); + $this->assertEquals(array('name' => 'Taylor', 'size' => 100), Input::file('foo')); + } + + /** + * Test the Input::flash method. + * + * @group laravel + */ + public function testFlashMethodFlashesInputToSession() + { + $this->setSession(); + + $input = array('name' => 'Taylor', 'age' => 25); + Request::foundation()->request->add($input); + + Input::flash(); + + $this->assertEquals($input, Session::$instance->session['data'][':new:']['laravel_old_input']); + + Input::flash('only', array('name')); + + $this->assertEquals(array('name' => 'Taylor'), Session::$instance->session['data'][':new:']['laravel_old_input']); + + Input::flash('except', array('name')); + + $this->assertEquals(array('age' => 25), Session::$instance->session['data'][':new:']['laravel_old_input']); + } + + /** + * Test the Input::flush method. + * + * @group laravel + */ + public function testFlushMethodClearsFlashedInput() + { + $this->setSession(); + + $input = array('name' => 'Taylor', 'age' => 30); + Request::foundation()->request->add($input); + + Input::flash(); + + $this->assertEquals($input, Session::$instance->session['data'][':new:']['laravel_old_input']); + + Input::flush(); + + $this->assertEquals(array(), Session::$instance->session['data'][':new:']['laravel_old_input']); + } + + /** + * Set the session payload instance. + */ + protected function setSession() + { + $driver = $this->getMock('Laravel\\Session\\Drivers\\Driver'); + + Session::$instance = new Laravel\Session\Payload($driver); + } + +} \ No newline at end of file diff --git a/app/laravel/tests/cases/ioc.test.php b/app/laravel/tests/cases/ioc.test.php new file mode 100644 index 000000000..56918337c --- /dev/null +++ b/app/laravel/tests/cases/ioc.test.php @@ -0,0 +1,74 @@ +assertEquals('Taylor', IoC::resolve('foo')); + } + + /** + * Test that singletons are created once. + * + * @group laravel + */ + public function testSingletonsAreCreatedOnce() + { + IoC::singleton('foo', function() + { + return new StdClass; + }); + + $object = IoC::resolve('foo'); + + $this->assertTrue($object === IoC::resolve('foo')); + } + + /** + * Test the IoC::instance method. + * + * @group laravel + */ + public function testInstancesAreReturnedBySingleton() + { + $object = new StdClass; + + IoC::instance('bar', $object); + + $this->assertTrue($object === IoC::resolve('bar')); + } + + /** + * Test the IoC::registered method. + */ + public function testRegisteredMethodIndicatesIfRegistered() + { + IoC::register('foo', function() {}); + + $this->assertTrue(IoC::registered('foo')); + $this->assertFalse(IoC::registered('baz')); + } + + /** + * Test the IoC::controller method. + * + * @group laravel + */ + public function testControllerMethodRegistersAController() + { + IoC::register('controller: ioc.test', function() {}); + + $this->assertTrue(IoC::registered('controller: ioc.test')); + } + +} \ No newline at end of file diff --git a/app/laravel/tests/cases/lang.test.php b/app/laravel/tests/cases/lang.test.php new file mode 100644 index 000000000..74640ccdc --- /dev/null +++ b/app/laravel/tests/cases/lang.test.php @@ -0,0 +1,68 @@ +assertEquals($validation['required'], Lang::line('validation.required')->get()); + $this->assertEquals('Taylor', Lang::line('validation.foo')->get(null, 'Taylor')); + } + + /** + * Test the Lang::line method. + * + * @group laravel + */ + public function testGetMethodCanGetLinesForAGivenLanguage() + { + $validation = require path('app').'language/sp/validation.php'; + + $this->assertEquals($validation['required'], Lang::line('validation.required')->get('sp')); + } + + /** + * Test the __toString method. + * + * @group laravel + */ + public function testLineCanBeCastAsString() + { + $validation = require path('app').'language/en/validation.php'; + + $this->assertEquals($validation['required'], (string) Lang::line('validation.required')); + } + + /** + * Test that string replacements are made on lines. + * + * @group laravel + */ + public function testReplacementsAreMadeOnLines() + { + $validation = require path('app').'language/en/validation.php'; + + $line = str_replace(':attribute', 'e-mail', $validation['required']); + + $this->assertEquals($line, Lang::line('validation.required', array('attribute' => 'e-mail'))->get()); + } + + /** + * Test the Lang::has method. + * + * @group laravel + */ + public function testHasMethodIndicatesIfLangaugeLineExists() + { + $this->assertTrue(Lang::has('validation')); + $this->assertTrue(Lang::has('validation.required')); + $this->assertFalse(Lang::has('validation.foo')); + } + +} \ No newline at end of file diff --git a/app/laravel/tests/cases/messages.test.php b/app/laravel/tests/cases/messages.test.php new file mode 100644 index 000000000..f007ecb75 --- /dev/null +++ b/app/laravel/tests/cases/messages.test.php @@ -0,0 +1,115 @@ +messages = new Laravel\Messages; + } + + /** + * Test the Messages::add method. + * + * @group laravel + */ + public function testAddingMessagesDoesNotCreateDuplicateMessages() + { + $this->messages->add('email', 'test'); + $this->messages->add('email', 'test'); + $this->assertCount(1, $this->messages->messages); + } + + /** + * Test the Messages::add method. + * + * @group laravel + */ + public function testAddMethodPutsMessageInMessagesArray() + { + $this->messages->add('email', 'test'); + $this->assertArrayHasKey('email', $this->messages->messages); + $this->assertEquals('test', $this->messages->messages['email'][0]); + } + + /** + * Test the Messages::has method. + * + * @group laravel + */ + public function testHasMethodReturnsTrue() + { + $this->messages->add('email', 'test'); + $this->assertTrue($this->messages->has('email')); + } + + /** + * Test the Messages::has method. + * + * @group laravel + */ + public function testHasMethodReturnsFalse() + { + $this->assertFalse($this->messages->has('something')); + } + + /** + * Test the Messages::first method. + * + * @group laravel + */ + public function testFirstMethodReturnsSingleString() + { + $this->messages->add('email', 'test'); + $this->assertEquals('test', $this->messages->first('email')); + $this->assertEquals('', $this->messages->first('something')); + } + + /** + * Test the Messages::get method. + * + * @group laravel + */ + public function testGetMethodReturnsAllMessagesForAttribute() + { + $messages = array('email' => array('something', 'else')); + $this->messages->messages = $messages; + $this->assertEquals(array('something', 'else'), $this->messages->get('email')); + } + + /** + * Test the Messages::all method. + * + * @group laravel + */ + public function testAllMethodReturnsAllErrorMessages() + { + $messages = array('email' => array('something', 'else'), 'name' => array('foo')); + $this->messages->messages = $messages; + $this->assertEquals(array('something', 'else', 'foo'), $this->messages->all()); + } + + /** + * Test the Messages::get method. + * + * @group laravel + */ + public function testMessagesRespectFormat() + { + $this->messages->add('email', 'test'); + $this->assertEquals('

    test

    ', $this->messages->first('email', '

    :message

    ')); + $this->assertEquals(array('

    test

    '), $this->messages->get('email', '

    :message

    ')); + $this->assertEquals(array('

    test

    '), $this->messages->all('

    :message

    ')); + } + + +} \ No newline at end of file diff --git a/app/laravel/tests/cases/query.test.php b/app/laravel/tests/cases/query.test.php new file mode 100644 index 000000000..17279150d --- /dev/null +++ b/app/laravel/tests/cases/query.test.php @@ -0,0 +1,48 @@ +assertEquals('taylor@example.com', $this->query()->find(1)->email); + } + + /** + * Test the select method. + * + * @group laravel + */ + public function testSelectMethodLimitsColumns() + { + $result = $this->query()->select(array('email'))->first(); + + $this->assertTrue(isset($result->email)); + $this->assertFalse(isset($result->name)); + } + + /** + * Test the raw_where method. + * + * @group laravel + */ + public function testRawWhereCanBeUsed() + { + + } + + /** + * Get the query instance for the test case. + * + * @return Query + */ + protected function query() + { + return DB::table('query_test'); + } + +} \ No newline at end of file diff --git a/app/laravel/tests/cases/redirect.test.php b/app/laravel/tests/cases/redirect.test.php new file mode 100644 index 000000000..cce63a81b --- /dev/null +++ b/app/laravel/tests/cases/redirect.test.php @@ -0,0 +1,143 @@ +assertEquals(302, $redirect->status()); + $this->assertEquals('http://localhost/user/profile', $redirect->headers()->get('location')); + + $redirect = Redirect::to('user/profile', 301, true); + + $this->assertEquals(301, $redirect->status()); + $this->assertEquals('https://localhost/user/profile', $redirect->headers()->get('location')); + + $redirect = Redirect::to_secure('user/profile', 301); + + $this->assertEquals(301, $redirect->status()); + $this->assertEquals('https://localhost/user/profile', $redirect->headers()->get('location')); + } + + /** + * Test the Redirect::to_route method. + * + * @group laravel + */ + public function testRedirectsCanBeGeneratedForNamedRoutes() + { + Route::get('redirect', array('as' => 'redirect')); + Route::get('redirect/(:any)/(:any)', array('as' => 'redirect-2')); + Route::get('secure/redirect', array('https' => true, 'as' => 'redirect-3')); + + $this->assertEquals(301, Redirect::to_route('redirect', array(), 301, true)->status()); + $this->assertEquals('http://localhost/redirect', Redirect::to_route('redirect')->headers()->get('location')); + $this->assertEquals('https://localhost/secure/redirect', Redirect::to_route('redirect-3', array(), 302)->headers()->get('location')); + $this->assertEquals('http://localhost/redirect/1/2', Redirect::to_route('redirect-2', array('1', '2'))->headers()->get('location')); + } + + /** + * Test the Redirect::with method. + * + * @group laravel + */ + public function testWithMethodFlashesItemToSession() + { + $this->setSession(); + + $redirect = Redirect::to('')->with('name', 'Taylor'); + + $this->assertEquals('Taylor', Session::$instance->session['data'][':new:']['name']); + } + + /** + * Test the Redirect::with_input function. + * + * @group laravel + */ + public function testWithInputMethodFlashesInputToTheSession() + { + $this->setSession(); + + $input = array('name' => 'Taylor', 'age' => 25); + Request::foundation()->request->add($input); + + $redirect = Redirect::to('')->with_input(); + + $this->assertEquals($input, Session::$instance->session['data'][':new:']['laravel_old_input']); + + $redirect = Redirect::to('')->with_input('only', array('name')); + + $this->assertEquals(array('name' => 'Taylor'), Session::$instance->session['data'][':new:']['laravel_old_input']); + + $redirect = Redirect::to('')->with_input('except', array('name')); + + $this->assertEquals(array('age' => 25), Session::$instance->session['data'][':new:']['laravel_old_input']); + } + + /** + * Test the Redirect::with_errors method. + * + * @group laravel + */ + public function testWithErrorsFlashesErrorsToTheSession() + { + $this->setSession(); + + Redirect::to('')->with_errors(array('name' => 'Taylor')); + + $this->assertEquals(array('name' => 'Taylor'), Session::$instance->session['data'][':new:']['errors']); + + $validator = Validator::make(array(), array()); + $validator->errors = array('name' => 'Taylor'); + + Redirect::to('')->with_errors($validator); + + $this->assertEquals(array('name' => 'Taylor'), Session::$instance->session['data'][':new:']['errors']); + } + + /** + * Set the session payload instance. + */ + protected function setSession() + { + $driver = $this->getMock('Laravel\\Session\\Drivers\\Driver'); + + Session::$instance = new Laravel\Session\Payload($driver); + } + +} \ No newline at end of file diff --git a/app/laravel/tests/cases/request.test.php b/app/laravel/tests/cases/request.test.php new file mode 100644 index 000000000..4641a532c --- /dev/null +++ b/app/laravel/tests/cases/request.test.php @@ -0,0 +1,177 @@ +restartRequest(); + } + + /** + * Set one of the $_POST variables. + * + * @param string $key + * @param string $value + */ + protected function setPostVar($key, $value) + { + $_POST[$key] = $value; + + $this->restartRequest(); + } + + /** + * Reinitialize the global request. + * + * @return void + */ + protected function restartRequest() + { + // FIXME: Ugly hack, but old contents from previous requests seem to + // trip up the Foundation class. + $_FILES = array(); + + Request::$foundation = RequestFoundation::createFromGlobals(); + } + + /** + * Test the Request::method method. + * + * @group laravel + */ + public function testMethodReturnsTheHTTPRequestMethod() + { + $this->setServerVar('REQUEST_METHOD', 'POST'); + + $this->assertEquals('POST', Request::method()); + + $this->setPostVar(Request::spoofer, 'PUT'); + + $this->assertEquals('PUT', Request::method()); + } + + /** + * Test the Request::server method. + * + * @group laravel + */ + public function testServerMethodReturnsFromServerArray() + { + $this->setServerVar('TEST', 'something'); + $this->setServerVar('USER', array('NAME' => 'taylor')); + + $this->assertEquals('something', Request::server('test')); + $this->assertEquals('taylor', Request::server('user.name')); + } + + /** + * Test the Request::ip method. + * + * @group laravel + */ + public function testIPMethodReturnsClientIPAddress() + { + $this->setServerVar('REMOTE_ADDR', 'something'); + $this->assertEquals('something', Request::ip()); + + $this->setServerVar('HTTP_CLIENT_IP', 'something'); + $this->assertEquals('something', Request::ip()); + + $this->setServerVar('HTTP_CLIENT_IP', 'something'); + $this->assertEquals('something', Request::ip()); + + $_SERVER = array(); + $this->restartRequest(); + $this->assertEquals('0.0.0.0', Request::ip()); + } + + /** + * Test the Request::secure method. + * + * @group laravel + */ + public function testSecureMethodsIndicatesIfHTTPS() + { + $this->setServerVar('HTTPS', 'on'); + + $this->assertTrue(Request::secure()); + + $this->setServerVar('HTTPS', 'off'); + + $this->assertFalse(Request::secure()); + } + + /** + * Test the Request::ajax method. + * + * @group laravel + */ + public function testAjaxMethodIndicatesWhenAjax() + { + $this->assertFalse(Request::ajax()); + + $this->setServerVar('HTTP_X_REQUESTED_WITH', 'XMLHttpRequest'); + + $this->assertTrue(Request::ajax()); + } + + /** + * Test the Request::forged method. + * + * @group laravel + */ + public function testForgedMethodIndicatesIfRequestWasForged() + { + Session::$instance = new SessionPayloadTokenStub; + + $input = array(Session::csrf_token => 'Foo'); + Request::foundation()->request->add($input); + + $this->assertTrue(Request::forged()); + + $input = array(Session::csrf_token => 'Taylor'); + Request::foundation()->request->add($input); + + $this->assertFalse(Request::forged()); + } + + /** + * Test the Request::route method. + * + * @group laravel + */ + public function testRouteMethodReturnsStaticRoute() + { + Request::$route = 'Taylor'; + + $this->assertEquals('Taylor', Request::route()); + } + +} \ No newline at end of file diff --git a/app/laravel/tests/cases/response.test.php b/app/laravel/tests/cases/response.test.php new file mode 100644 index 000000000..11e48e74f --- /dev/null +++ b/app/laravel/tests/cases/response.test.php @@ -0,0 +1,89 @@ + 'baz')); + + $this->assertEquals('foo', $response->content); + $this->assertEquals(201, $response->status()); + $this->assertArrayHasKey('bar', $response->headers()->all()); + $this->assertEquals('baz', $response->headers()->get('bar')); + } + + /** + * Test the Response::view method. + * + * @group laravel + */ + public function testViewMethodSetsContentToView() + { + $response = Response::view('home.index', array('name' => 'Taylor')); + + $this->assertEquals('home.index', $response->content->view); + $this->assertEquals('Taylor', $response->content->data['name']); + } + + /** + * Test the Response::error method. + * + * @group laravel + */ + public function testErrorMethodSetsContentToErrorView() + { + $response = Response::error('404', array('name' => 'Taylor')); + + $this->assertEquals(404, $response->status()); + $this->assertEquals('error.404', $response->content->view); + $this->assertEquals('Taylor', $response->content->data['name']); + } + + /** + * Test the Response::prepare method. + * + * @group laravel + */ + public function testPrepareMethodCreatesAResponseInstanceFromGivenValue() + { + $response = Response::prepare('Taylor'); + + $this->assertInstanceOf('Laravel\\Response', $response); + $this->assertEquals('Taylor', $response->content); + + $response = Response::prepare(new Response('Taylor')); + + $this->assertInstanceOf('Laravel\\Response', $response); + $this->assertEquals('Taylor', $response->content); + } + + /** + * Test the Response::header method. + * + * @group laravel + */ + public function testHeaderMethodSetsValueInHeaderArray() + { + $response = Response::make('')->header('foo', 'bar'); + + $this->assertEquals('bar', $response->headers()->get('foo')); + } + + /** + * Test the Response::status method. + * + * @group laravel + */ + public function testStatusMethodSetsStatusCode() + { + $response = Response::make('')->status(404); + + $this->assertEquals(404, $response->status()); + } + +} \ No newline at end of file diff --git a/app/laravel/tests/cases/route.test.php b/app/laravel/tests/cases/route.test.php new file mode 100644 index 000000000..489191359 --- /dev/null +++ b/app/laravel/tests/cases/route.test.php @@ -0,0 +1,179 @@ + 'profile')); + $this->assertTrue($route->is('profile')); + $this->assertFalse($route->is('something')); + } + + /** + * Test the basic execution of a route. + * + * @group laravel + */ + public function testBasicRoutesCanBeExecutedProperly() + { + $route = new Route('GET', '', array(function() { return 'Route!'; })); + + $this->assertEquals('Route!', $route->call()->content); + $this->assertInstanceOf('Laravel\\Response', $route->call()); + } + + /** + * Test that route parameters are passed into the handlers. + * + * @group laravel + */ + public function testRouteParametersArePassedIntoTheHandler() + { + $route = new Route('GET', '', array(function($var) { return $var; }), array('Taylor')); + + $this->assertEquals('Taylor', $route->call()->content); + $this->assertInstanceOf('Laravel\\Response', $route->call()); + } + + /** + * Test that calling a route calls the global before and after filters. + * + * @group laravel + */ + public function testCallingARouteCallsTheBeforeAndAfterFilters() + { + $route = new Route('GET', '', array(function() { return 'Hi!'; })); + + $_SERVER['before'] = false; + $_SERVER['after'] = false; + + $route->call(); + + $this->assertTrue($_SERVER['before']); + $this->assertTrue($_SERVER['after']); + } + + /** + * Test that before filters override the route response. + * + * @group laravel + */ + public function testBeforeFiltersOverrideTheRouteResponse() + { + Filter::register('test-before', function() + { + return 'Filtered!'; + }); + + $route = new Route('GET', '', array('before' => 'test-before', function() { + return 'Route!'; + })); + + $this->assertEquals('Filtered!', $route->call()->content); + } + + /** + * Test that after filters do not affect the route response. + * + * @group laravel + */ + public function testAfterFilterDoesNotAffectTheResponse() + { + $_SERVER['test-after'] = false; + + Filter::register('test-after', function() + { + $_SERVER['test-after'] = true; + return 'Filtered!'; + }); + + $route = new Route('GET', '', array('after' => 'test-after', function() + { + return 'Route!'; + })); + + $this->assertEquals('Route!', $route->call()->content); + $this->assertTrue($_SERVER['test-after']); + } + + /** + * Test that the route calls the appropriate controller method when delegating. + * + * @group laravel + */ + public function testControllerActionCalledWhenDelegating() + { + $_SERVER['REQUEST_METHOD'] = 'GET'; + + $route = new Route('GET', '', array('uses' => 'auth@index')); + + $this->assertEquals('action_index', $route->call()->content); + } + + /** + * Test that filter parameters are passed to the filter. + * + * @group laravel + */ + public function testFilterParametersArePassedToFilter() + { + Filter::register('test-params', function($var1, $var2) + { + return $var1.$var2; + }); + + $route = new Route('GET', '', array('before' => 'test-params:1,2')); + + $this->assertEquals('12', $route->call()->content); + } + + /** + * Test that multiple filters can be assigned to a route. + * + * @group laravel + */ + public function testMultipleFiltersCanBeAssignedToARoute() + { + $_SERVER['test-multi-1'] = false; + $_SERVER['test-multi-2'] = false; + + Filter::register('test-multi-1', function() { $_SERVER['test-multi-1'] = true; }); + Filter::register('test-multi-2', function() { $_SERVER['test-multi-2'] = true; }); + + $route = new Route('GET', '', array('before' => 'test-multi-1|test-multi-2')); + + $route->call(); + + $this->assertTrue($_SERVER['test-multi-1']); + $this->assertTrue($_SERVER['test-multi-2']); + } + +} \ No newline at end of file diff --git a/app/laravel/tests/cases/routing.test.php b/app/laravel/tests/cases/routing.test.php new file mode 100644 index 000000000..68a112a07 --- /dev/null +++ b/app/laravel/tests/cases/routing.test.php @@ -0,0 +1,160 @@ + 'home')); + Route::get('dashboard', array('as' => 'dashboard')); + + $home = Router::find('home'); + $dashboard = Router::find('dashboard'); + + $this->assertTrue(isset($home['/'])); + $this->assertTrue(isset($dashboard['dashboard'])); + } + + /** + * Test the basic routing mechanism. + * + * @group laravel + */ + public function testBasicRouteCanBeRouted() + { + Route::get('/', function() {}); + Route::get('home, main', function() {}); + + $this->assertEquals('/', Router::route('GET', '/')->uri); + $this->assertEquals('home', Router::route('GET', 'home')->uri); + $this->assertEquals('main', Router::route('GET', 'main')->uri); + } + + /** + * Test that the router can handle basic wildcards. + * + * @group laravel + */ + public function testWildcardRoutesCanBeRouted() + { + Route::get('user/(:num)', function() {}); + Route::get('profile/(:any)/(:num)', function() {}); + + $this->assertNull(Router::route('GET', 'user/1.5')); + $this->assertNull(Router::route('GET', 'user/taylor')); + $this->assertEquals(array(25), Router::route('GET', 'user/25')->parameters); + $this->assertEquals('user/(:num)', Router::route('GET', 'user/1')->uri); + + $this->assertNull(Router::route('GET', 'profile/1/otwell')); + $this->assertNull(Router::route('POST', 'profile/taylor/1')); + $this->assertNull(Router::route('GET', 'profile/taylor/otwell')); + $this->assertNull(Router::route('GET', 'profile/taylor/1/otwell')); + $this->assertEquals(array('taylor', 25), Router::route('GET', 'profile/taylor/25')->parameters); + $this->assertEquals('profile/(:any)/(:num)', Router::route('GET', 'profile/taylor/1')->uri); + } + + /** + * Test that optional wildcards can be routed. + * + * @group laravel + */ + public function testOptionalWildcardsCanBeRouted() + { + Route::get('user/(:num?)', function() {}); + Route::get('profile/(:any)/(:any?)', function() {}); + + $this->assertNull(Router::route('GET', 'user/taylor')); + $this->assertEquals('user/(:num?)', Router::route('GET', 'user')->uri); + $this->assertEquals(array(25), Router::route('GET', 'user/25')->parameters); + $this->assertEquals('user/(:num?)', Router::route('GET', 'user/1')->uri); + + $this->assertNull(Router::route('GET', 'profile/taylor/otwell/test')); + $this->assertEquals('profile/(:any)/(:any?)', Router::route('GET', 'profile/taylor')->uri); + $this->assertEquals('profile/(:any)/(:any?)', Router::route('GET', 'profile/taylor/25')->uri); + $this->assertEquals('profile/(:any)/(:any?)', Router::route('GET', 'profile/taylor/otwell')->uri); + $this->assertEquals(array('taylor', 'otwell'), Router::route('GET', 'profile/taylor/otwell')->parameters); + } + + /** + * Test that basic controller routing is working. + * + * @group laravel + */ + public function testBasicRouteToControllerIsRouted() + { + $this->assertEquals('auth@(:1)', Router::route('GET', 'auth')->action['uses']); + $this->assertEquals('home@(:1)', Router::route('GET', 'home/index')->action['uses']); + $this->assertEquals('home@(:1)', Router::route('GET', 'home/profile')->action['uses']); + $this->assertEquals('admin.panel@(:1)', Router::route('GET', 'admin/panel')->action['uses']); + $this->assertEquals('admin.panel@(:1)', Router::route('GET', 'admin/panel/show')->action['uses']); + } + + /** + * Test basic bundle route resolution. + * + * @group laravel + */ + public function testRoutesToBundlesCanBeResolved() + { + $this->assertNull(Router::route('GET', 'dashboard/foo')); + $this->assertEquals('dashboard', Router::route('GET', 'dashboard')->uri); + } + + /** + * Test bundle controller route resolution. + * + * @group laravel + */ + public function testBundleControllersCanBeResolved() + { + $this->assertEquals('dashboard::panel@(:1)', Router::route('GET', 'dashboard/panel')->action['uses']); + $this->assertEquals('dashboard::panel@(:1)', Router::route('GET', 'dashboard/panel/show')->action['uses']); + } + + /** + * Test foreign characters can be used in routes. + * + * @group laravel + */ + public function testForeignCharsInRoutes() + { + Route::get(urlencode('مدرس_رياضيات').'/(:any)', function() {}); + Route::get(urlencode('مدرس_رياضيات'), function() {}); + Route::get(urlencode('ÇœŪ'), function() {}); + Route::get(urlencode('私は料理が大好き'), function() {}); + + $this->assertEquals(array(urlencode('مدرس_رياضيات')), Router::route('GET', urlencode('مدرس_رياضيات').'/'.urlencode('مدرس_رياضيات'))->parameters); + $this->assertEquals(urlencode('مدرس_رياضيات'), Router::route('GET', urlencode('مدرس_رياضيات'))->uri); + $this->assertEquals(urlencode('ÇœŪ'), Router::route('GET', urlencode('ÇœŪ'))->uri); + $this->assertEquals(urlencode('私は料理が大好き'), Router::route('GET', urlencode('私は料理が大好き'))->uri); + } + +} \ No newline at end of file diff --git a/app/laravel/tests/cases/session.test.php b/app/laravel/tests/cases/session.test.php new file mode 100644 index 000000000..b310870c2 --- /dev/null +++ b/app/laravel/tests/cases/session.test.php @@ -0,0 +1,443 @@ +assertEquals('Foo', Session::test()); + } + + /** + * Test the Session::started method. + * + * @group laravel + */ + public function testStartedMethodIndicatesIfSessionIsStarted() + { + $this->assertFalse(Session::started()); + Session::$instance = 'foo'; + $this->assertTrue(Session::started()); + } + + /** + * Test the Payload::load method. + * + * @group laravel + */ + public function testLoadMethodCreatesNewSessionWithNullIDGiven() + { + $payload = $this->getPayload(); + $payload->load(null); + $this->verifyNewSession($payload); + } + + /** + * Test the Payload::load method. + * + * @group laravel + */ + public function testLoadMethodCreatesNewSessionWhenSessionIsExpired() + { + $payload = $this->getPayload(); + + $session = $this->getSession(); + $session['last_activity'] = time() - 10000; + + $payload->driver->expects($this->any()) + ->method('load') + ->will($this->returnValue($session)); + + $payload->load('foo'); + + $this->verifyNewSession($payload); + $this->assertTrue($payload->session['id'] !== $session['id']); + } + + /** + * Assert that a session is new. + * + * @param Payload $payload + * @return void + */ + protected function verifyNewSession($payload) + { + $this->assertFalse($payload->exists); + $this->assertTrue(isset($payload->session['id'])); + $this->assertEquals(array(), $payload->session['data'][':new:']); + $this->assertEquals(array(), $payload->session['data'][':old:']); + $this->assertTrue(isset($payload->session['data'][Session::csrf_token])); + } + + /** + * Test the Payload::load method. + * + * @group laravel + */ + public function testLoadMethodSetsValidSession() + { + $payload = $this->getPayload(); + + $session = $this->getSession(); + + $payload->driver->expects($this->any()) + ->method('load') + ->will($this->returnValue($session)); + + $payload->load('foo'); + + $this->assertEquals($session, $payload->session); + } + + /** + * Test the Payload::load method. + * + * @group laravel + */ + public function testLoadMethodSetsCSRFTokenIfDoesntExist() + { + $payload = $this->getPayload(); + + $session = $this->getSession(); + + unset($session['data']['csrf_token']); + + $payload->driver->expects($this->any()) + ->method('load') + ->will($this->returnValue($session)); + + $payload->load('foo'); + + $this->assertEquals('foo', $payload->session['id']); + $this->assertTrue(isset($payload->session['data']['csrf_token'])); + } + + /** + * Test the various data retrieval methods. + * + * @group laravel + */ + public function testSessionDataCanBeRetrievedProperly() + { + $payload = $this->getPayload(); + + $payload->session = $this->getSession(); + + $this->assertTrue($payload->has('name')); + $this->assertEquals('Taylor', $payload->get('name')); + $this->assertFalse($payload->has('foo')); + $this->assertEquals('Default', $payload->get('foo', 'Default')); + $this->assertTrue($payload->has('votes')); + $this->assertEquals(10, $payload->get('votes')); + $this->assertTrue($payload->has('state')); + $this->assertEquals('AR', $payload->get('state')); + } + + /** + * Test the various data manipulation methods. + * + * @group laravel + */ + public function testDataCanBeSetProperly() + { + $payload = $this->getPayload(); + + $payload->session = $this->getSession(); + + // Test the "put" and "flash" methods. + $payload->put('name', 'Weldon'); + $this->assertEquals('Weldon', $payload->session['data']['name']); + $payload->flash('language', 'php'); + $this->assertEquals('php', $payload->session['data'][':new:']['language']); + + // Test the "reflash" method. + $payload->session['data'][':new:'] = array('name' => 'Taylor'); + $payload->session['data'][':old:'] = array('age' => 25); + $payload->reflash(); + $this->assertEquals(array('name' => 'Taylor', 'age' => 25), $payload->session['data'][':new:']); + + // Test the "keep" method. + $payload->session['data'][':new:'] = array(); + $payload->keep(array('age')); + $this->assertEquals(25, $payload->session['data'][':new:']['age']); + } + + /** + * Test the Payload::forget method. + * + * @group laravel + */ + public function testSessionDataCanBeForgotten() + { + $payload = $this->getPayload(); + + $payload->session = $this->getSession(); + + $this->assertTrue(isset($payload->session['data']['name'])); + $payload->forget('name'); + $this->assertFalse(isset($payload->session['data']['name'])); + } + + /** + * Test the Payload::flush method. + * + * @group laravel + */ + public function testFlushMaintainsTokenButDeletesEverythingElse() + { + $payload = $this->getPayload(); + + $payload->session = $this->getSession(); + + $this->assertTrue(isset($payload->session['data']['name'])); + $payload->flush(); + $this->assertFalse(isset($payload->session['data']['name'])); + $this->assertEquals('bar', $payload->session['data']['csrf_token']); + $this->assertEquals(array(), $payload->session['data'][':new:']); + $this->assertEquals(array(), $payload->session['data'][':old:']); + } + + /** + * Test the Payload::regenerate method. + * + * @group laravel + */ + public function testRegenerateMethodSetsNewIDAndTurnsOffExistenceIndicator() + { + $payload = $this->getPayload(); + + $payload->sesion = $this->getSession(); + $payload->exists = true; + $payload->regenerate(); + + $this->assertFalse($payload->exists); + $this->assertTrue(strlen($payload->session['id']) == 40); + } + + /** + * Test the Payload::token method. + * + * @group laravel + */ + public function testTokenMethodReturnsCSRFToken() + { + $payload = $this->getPayload(); + $payload->session = $this->getSession(); + + $this->assertEquals('bar', $payload->token()); + } + + /** + * Test the Payload::save method. + * + * @group laravel + */ + public function testSaveMethodCorrectlyCallsDriver() + { + $payload = $this->getPayload(); + $session = $this->getSession(); + $payload->session = $session; + $payload->exists = true; + $config = Laravel\Config::get('session'); + + $expect = $session; + $expect['data'][':old:'] = $session['data'][':new:']; + $expect['data'][':new:'] = array(); + + $payload->driver->expects($this->once()) + ->method('save') + ->with($this->equalTo($expect), $this->equalTo($config), $this->equalTo(true)); + + $payload->save(); + + $this->assertEquals($session['data'][':new:'], $payload->session['data'][':old:']); + } + + /** + * Test the Payload::save method. + * + * @group laravel + */ + public function testSaveMethodSweepsIfSweeperAndOddsHitWithTimeGreaterThanThreshold() + { + Config::set('session.sweepage', array(100, 100)); + + $payload = $this->getPayload(); + $payload->driver = $this->getMock('Laravel\\Session\\Drivers\\File', array('save', 'sweep'), array(null)); + $payload->session = $this->getSession(); + + $expiration = time() - (Config::get('session.lifetime') * 60); + + // Here we set the time to the expected expiration minus 5 seconds, just to + // allow plenty of room for PHP execution. In the next test, we'll do the + // same thing except add 5 seconds to check that the time is between a + // given window. + $payload->driver->expects($this->once()) + ->method('sweep') + ->with($this->greaterThan($expiration - 5)); + + $payload->save(); + + Config::set('session.sweepage', array(2, 100)); + } + + /** + * Test the Payload::save method. + * + * @group laravel + */ + public function testSaveMethodSweepsIfSweeperAndOddsHitWithTimeLessThanThreshold() + { + Config::set('session.sweepage', array(100, 100)); + + $payload = $this->getPayload(); + $payload->driver = $this->getMock('Laravel\\Session\\Drivers\\File', array('save', 'sweep'), array(null)); + $payload->session = $this->getSession(); + + $expiration = time() - (Config::get('session.lifetime') * 60); + + $payload->driver->expects($this->once()) + ->method('sweep') + ->with($this->lessThan($expiration + 5)); + + $payload->save(); + + Config::set('session.sweepage', array(2, 100)); + } + + /** + * Test that the session sweeper is never called if not a sweeper. + * + * @group laravel + */ + public function testSweeperShouldntBeCalledIfDriverIsntSweeper() + { + Config::set('session.sweepage', array(100, 100)); + + $payload = $this->getPayload(); + $payload->driver = $this->getMock('Laravel\\Session\\Drivers\\APC', array('save', 'sweep'), array(), '', false); + $payload->session = $this->getSession(); + + $payload->driver->expects($this->never())->method('sweep'); + + $payload->save(); + + Config::set('session.sweepage', array(2, 100)); + } + + /** + * Test the Payload::save method. + * + * @group laravel + */ + public function testSaveMethodSetsCookieWithCorrectValues() + { + $payload = $this->getPayload(); + $payload->session = $this->getSession(); + $payload->save(); + + $this->assertTrue(isset(Cookie::$jar[Config::get('session.cookie')])); + + $cookie = Cookie::$jar[Config::get('session.cookie')]; + + $this->assertEquals(Cookie::hash('foo').'+foo', $cookie['value']); + // Shouldn't be able to test this cause session.lifetime store number of minutes + // while cookie expiration store timestamp when it going to expired. + // $this->assertEquals(Config::get('session.lifetime'), $cookie['expiration']); + $this->assertEquals(Config::get('session.domain'), $cookie['domain']); + $this->assertEquals(Config::get('session.path'), $cookie['path']); + $this->assertEquals(Config::get('session.secure'), $cookie['secure']); + } + + /** + * Test the Session::activity method. + * + * @group laravel + */ + public function testActivityMethodReturnsLastActivity() + { + $payload = $this->getPayload(); + $payload->session['last_activity'] = 10; + $this->assertEquals(10, $payload->activity()); + } + + /** + * Get a session payload instance. + * + * @return Payload + */ + protected function getPayload() + { + return new Payload($this->getMockDriver()); + } + + /** + * Get a mock driver instance. + * + * @return Driver + */ + protected function getMockDriver() + { + $mock = $this->getMock('Laravel\\Session\\Drivers\\Driver', array('id', 'load', 'save', 'delete')); + + $mock->expects($this->any())->method('id')->will($this->returnValue(Str::random(40))); + + return $mock; + } + + /** + * Get a dummy session. + * + * @return array + */ + protected function getSession() + { + return array( + 'id' => 'foo', + 'last_activity' => time(), + 'data' => array( + 'name' => 'Taylor', + 'age' => 25, + 'csrf_token' => 'bar', + ':new:' => array( + 'votes' => 10, + ), + ':old:' => array( + 'state' => 'AR', + ), + )); + } + +} \ No newline at end of file diff --git a/app/laravel/tests/cases/str.test.php b/app/laravel/tests/cases/str.test.php new file mode 100644 index 000000000..7c3806004 --- /dev/null +++ b/app/laravel/tests/cases/str.test.php @@ -0,0 +1,135 @@ +assertEquals('UTF-8', Config::get('application.encoding')); + Config::set('application.encoding', 'foo'); + $this->assertEquals('foo', Config::get('application.encoding')); + Config::set('application.encoding', 'UTF-8'); + } + + /** + * Test the Str::length method. + * + * @group laravel + */ + public function testStringLengthIsCorrect() + { + $this->assertEquals(6, Str::length('Taylor')); + $this->assertEquals(5, Str::length('ラドクリフ')); + } + + /** + * Test the Str::lower method. + * + * @group laravel + */ + public function testStringCanBeConvertedToLowercase() + { + $this->assertEquals('taylor', Str::lower('TAYLOR')); + $this->assertEquals('άχιστη', Str::lower('ΆΧΙΣΤΗ')); + } + + /** + * Test the Str::upper method. + * + * @group laravel + */ + public function testStringCanBeConvertedToUppercase() + { + $this->assertEquals('TAYLOR', Str::upper('taylor')); + $this->assertEquals('ΆΧΙΣΤΗ', Str::upper('άχιστη')); + } + + /** + * Test the Str::title method. + * + * @group laravel + */ + public function testStringCanBeConvertedToTitleCase() + { + $this->assertEquals('Taylor', Str::title('taylor')); + $this->assertEquals('Άχιστη', Str::title('άχιστη')); + } + + /** + * Test the Str::limit method. + * + * @group laravel + */ + public function testStringCanBeLimitedByCharacters() + { + $this->assertEquals('Tay...', Str::limit('Taylor', 3)); + $this->assertEquals('Taylor', Str::limit('Taylor', 6)); + $this->assertEquals('Tay___', Str::limit('Taylor', 3, '___')); + } + + /** + * Test the Str::words method. + * + * @group laravel + */ + public function testStringCanBeLimitedByWords() + { + $this->assertEquals('Taylor...', Str::words('Taylor Otwell', 1)); + $this->assertEquals('Taylor___', Str::words('Taylor Otwell', 1, '___')); + $this->assertEquals('Taylor Otwell', Str::words('Taylor Otwell', 3)); + } + + /** + * Test the Str::plural and Str::singular methods. + * + * @group laravel + */ + public function testStringsCanBeSingularOrPlural() + { + $this->assertEquals('user', Str::singular('users')); + $this->assertEquals('users', Str::plural('user')); + $this->assertEquals('User', Str::singular('Users')); + $this->assertEquals('Users', Str::plural('User')); + $this->assertEquals('user', Str::plural('user', 1)); + $this->assertEquals('users', Str::plural('user', 2)); + $this->assertEquals('chassis', Str::plural('chassis', 2)); + $this->assertEquals('traffic', Str::plural('traffic', 2)); + } + + /** + * Test the Str::slug method. + * + * @group laravel + */ + public function testStringsCanBeSlugged() + { + $this->assertEquals('my-new-post', Str::slug('My nEw post!!!')); + $this->assertEquals('my_new_post', Str::slug('My nEw post!!!', '_')); + } + + /** + * Test the Str::classify method. + * + * @group laravel + */ + public function testStringsCanBeClassified() + { + $this->assertEquals('Something_Else', Str::classify('something.else')); + $this->assertEquals('Something_Else', Str::classify('something_else')); + } + + /** + * Test the Str::random method. + * + * @group laravel + */ + public function testRandomStringsCanBeGenerated() + { + $this->assertEquals(40, strlen(Str::random(40))); + } + +} diff --git a/app/laravel/tests/cases/uri.test.php b/app/laravel/tests/cases/uri.test.php new file mode 100644 index 000000000..8f2b85ffe --- /dev/null +++ b/app/laravel/tests/cases/uri.test.php @@ -0,0 +1,75 @@ +setRequestUri($uri); + + $this->assertEquals($expectation, URI::current()); + } + + /** + * Test the URI::segment method. + * + * @group laravel + */ + public function testSegmentMethodReturnsAURISegment() + { + $this->setRequestUri('/user/profile'); + + $this->assertEquals('user', URI::segment(1)); + $this->assertEquals('profile', URI::segment(2)); + } + + /** + * Data provider for the URI::current test. + */ + public function requestUriProvider() + { + return array( + array('/user', 'user'), + array('/user/', 'user'), + array('', '/'), + array('/', '/'), + array('//', '/'), + array('/user', 'user'), + array('/user/', 'user'), + array('/user/profile', 'user/profile'), + ); + } + +} \ No newline at end of file diff --git a/app/laravel/tests/cases/url.test.php b/app/laravel/tests/cases/url.test.php new file mode 100644 index 000000000..3bf37b401 --- /dev/null +++ b/app/laravel/tests/cases/url.test.php @@ -0,0 +1,131 @@ +assertEquals('http://localhost/index.php/user/profile', URL::to('user/profile')); + $this->assertEquals('https://localhost/index.php/user/profile', URL::to('user/profile', true)); + + Config::set('application.index', ''); + + $this->assertEquals('http://localhost/user/profile', URL::to('user/profile')); + $this->assertEquals('https://localhost/user/profile', URL::to('user/profile', true)); + + Config::set('application.ssl', false); + + $this->assertEquals('http://localhost/user/profile', URL::to('user/profile', true)); + } + + /** + * Test the URL::to_action method. + * + * @group laravel + */ + public function testToActionMethodGeneratesURLToControllerAction() + { + Route::get('foo/bar/(:any?)', 'foo@baz'); + $this->assertEquals('http://localhost/index.php/x/y', URL::to_action('x@y')); + $this->assertEquals('http://localhost/index.php/x/y/Taylor', URL::to_action('x@y', array('Taylor'))); + $this->assertEquals('http://localhost/index.php/foo/bar', URL::to_action('foo@baz')); + $this->assertEquals('http://localhost/index.php/foo/bar/Taylor', URL::to_action('foo@baz', array('Taylor'))); + } + + /** + * Test the URL::to_asset method. + * + * @group laravel + */ + public function testToAssetGeneratesURLWithoutFrontControllerInURL() + { + $this->assertEquals('http://localhost/image.jpg', URL::to_asset('image.jpg')); + $this->assertEquals('https://localhost/image.jpg', URL::to_asset('image.jpg', true)); + + Config::set('application.index', ''); + + $this->assertEquals('http://localhost/image.jpg', URL::to_asset('image.jpg')); + $this->assertEquals('https://localhost/image.jpg', URL::to_asset('image.jpg', true)); + + Request::foundation()->server->add(array('HTTPS' => 'on')); + + $this->assertEquals('https://localhost/image.jpg', URL::to_asset('image.jpg')); + + Request::foundation()->server->add(array('HTTPS' => 'off')); + } + + /** + * Test the URL::to_route method. + * + * @group laravel + */ + public function testToRouteMethodGeneratesURLsToRoutes() + { + Route::get('url/test', array('as' => 'url-test')); + Route::get('url/test/(:any)/(:any?)', array('as' => 'url-test-2')); + Route::get('url/secure/(:any)/(:any?)', array('as' => 'url-test-3', 'https' => true)); + + $this->assertEquals('http://localhost/index.php/url/test', URL::to_route('url-test')); + $this->assertEquals('http://localhost/index.php/url/test/taylor', URL::to_route('url-test-2', array('taylor'))); + $this->assertEquals('https://localhost/index.php/url/secure/taylor', URL::to_route('url-test-3', array('taylor'))); + $this->assertEquals('http://localhost/index.php/url/test/taylor/otwell', URL::to_route('url-test-2', array('taylor', 'otwell'))); + } + + + /** + * Test language based URL generation. + * + * @group laravel + */ + public function testUrlsGeneratedWithLanguages() + { + Config::set('application.languages', array('sp', 'fr')); + Config::set('application.language', 'sp'); + $this->assertEquals('http://localhost/index.php/sp/foo', URL::to('foo')); + $this->assertEquals('http://localhost/foo.jpg', URL::to_asset('foo.jpg')); + + Config::set('application.index', ''); + $this->assertEquals('http://localhost/sp/foo', URL::to('foo')); + + Config::set('application.index', 'index.php'); + Config::set('application.language', 'en'); + $this->assertEquals('http://localhost/index.php/foo', URL::to('foo')); + Config::set('application.languages', array()); + } + +} \ No newline at end of file diff --git a/app/laravel/tests/cases/validator.test.php b/app/laravel/tests/cases/validator.test.php new file mode 100644 index 000000000..bde1cea12 --- /dev/null +++ b/app/laravel/tests/cases/validator.test.php @@ -0,0 +1,669 @@ + 'Taylor Otwell'); + $rules = array('name' => 'required'); + $this->assertTrue(Validator::make($input, $rules)->valid()); + + $input['name'] = ''; + $this->assertFalse(Validator::make($input, $rules)->valid()); + + unset($input['name']); + $this->assertFalse(Validator::make($input, $rules)->valid()); + + $_FILES['name']['tmp_name'] = 'foo'; + $this->assertTrue(Validator::make($_FILES, $rules)->valid()); + + $_FILES['name']['tmp_name'] = ''; + $this->assertFalse(Validator::make($_FILES, $rules)->valid()); + } + + /** + * Test the confirmed validation rule. + * + * @group laravel + */ + public function testTheConfirmedRule() + { + $input = array('password' => 'foo', 'password_confirmation' => 'foo'); + $rules = array('password' => 'confirmed'); + $this->assertTrue(Validator::make($input, $rules)->valid()); + + $input['password_confirmation'] = 'foo_bar'; + $this->assertFalse(Validator::make($input, $rules)->valid()); + + unset($input['password_confirmation']); + $this->assertFalse(Validator::make($input, $rules)->valid()); + } + + /** + * Test the different validation rule. + * + * @group laravel + */ + public function testTheDifferentRule() + { + $input = array('password' => 'foo', 'password_confirmation' => 'bar'); + $rules = array('password' => 'different:password_confirmation'); + $this->assertTrue(Validator::make($input, $rules)->valid()); + + $input['password_confirmation'] = 'foo'; + $this->assertFalse(Validator::make($input, $rules)->valid()); + + unset($input['password_confirmation']); + $this->assertFalse(Validator::make($input, $rules)->valid()); + } + + /** + * Test the accepted validation rule. + * + * @group laravel + */ + public function testTheAcceptedRule() + { + $input = array('terms' => '1'); + $rules = array('terms' => 'accepted'); + $this->assertTrue(Validator::make($input, $rules)->valid()); + + $input['terms'] = 'yes'; + $this->assertTrue(Validator::make($input, $rules)->valid()); + + $input['terms'] = '2'; + $this->assertFalse(Validator::make($input, $rules)->valid()); + + // The accepted rule implies required, so should fail if field not present. + unset($input['terms']); + $this->assertFalse(Validator::make($input, $rules)->valid()); + } + + /** + * Test the numeric validation rule. + * + * @group laravel + */ + public function testTheNumericRule() + { + $input = array('amount' => '1.21'); + $rules = array('amount' => 'numeric'); + $this->assertTrue(Validator::make($input, $rules)->valid()); + + $input['amount'] = '1'; + $this->assertTrue(Validator::make($input, $rules)->valid()); + + $input['amount'] = 1.2; + $this->assertTrue(Validator::make($input, $rules)->valid()); + + $input['amount'] = '1.2a'; + $this->assertFalse(Validator::make($input, $rules)->valid()); + } + + /** + * Test the integer validation rule. + * + * @group laravel + */ + public function testTheIntegerRule() + { + $input = array('amount' => '1'); + $rules = array('amount' => 'integer'); + $this->assertTrue(Validator::make($input, $rules)->valid()); + + $input['amount'] = '0'; + $this->assertTrue(Validator::make($input, $rules)->valid()); + + $input['amount'] = 1.2; + $this->assertFalse(Validator::make($input, $rules)->valid()); + + $input['amount'] = '1.2a'; + $this->assertFalse(Validator::make($input, $rules)->valid()); + } + + /** + * Test the size validation rule. + * + * @group laravel + */ + public function testTheSizeRule() + { + $input = array('amount' => '1.21'); + $rules = array('amount' => 'numeric|size:1.21'); + $this->assertTrue(Validator::make($input, $rules)->valid()); + + $rules = array('amount' => 'numeric|size:1'); + $this->assertFalse(Validator::make($input, $rules)->valid()); + + // If no numeric rule is on the field, it is treated as a string + $input = array('amount' => '111'); + $rules = array('amount' => 'size:3'); + $this->assertTrue(Validator::make($input, $rules)->valid()); + + $rules = array('amount' => 'size:4'); + $this->assertFalse(Validator::make($input, $rules)->valid()); + + // The size rules checks kilobytes on files + $_FILES['photo']['tmp_name'] = 'foo'; + $_FILES['photo']['size'] = 10240; + $rules = array('photo' => 'size:10'); + $this->assertTrue(Validator::make($_FILES, $rules)->valid()); + + $_FILES['photo']['size'] = 14000; + $this->assertFalse(Validator::make($_FILES, $rules)->valid()); + } + + /** + * Test the between validation rule. + * + * @group laravel + */ + public function testTheBetweenRule() + { + $input = array('amount' => '1.21'); + $rules = array('amount' => 'numeric|between:1,2'); + $this->assertTrue(Validator::make($input, $rules)->valid()); + + $rules = array('amount' => 'numeric|between:2,3'); + $this->assertFalse(Validator::make($input, $rules)->valid()); + + // If no numeric rule is on the field, it is treated as a string + $input = array('amount' => '111'); + $rules = array('amount' => 'between:1,3'); + $this->assertTrue(Validator::make($input, $rules)->valid()); + + $rules = array('amount' => 'between:100,111'); + $this->assertFalse(Validator::make($input, $rules)->valid()); + + // The size rules checks kilobytes on files + $_FILES['photo']['tmp_name'] = 'foo'; + $_FILES['photo']['size'] = 10240; + $rules = array('photo' => 'between:9,11'); + $this->assertTrue(Validator::make($_FILES, $rules)->valid()); + + $_FILES['photo']['size'] = 14000; + $this->assertFalse(Validator::make($_FILES, $rules)->valid()); + } + + /** + * Test the between validation rule. + * + * @group laravel + */ + public function testTheMinRule() + { + $input = array('amount' => '1.21'); + $rules = array('amount' => 'numeric|min:1'); + $this->assertTrue(Validator::make($input, $rules)->valid()); + + $rules = array('amount' => 'numeric|min:2'); + $this->assertFalse(Validator::make($input, $rules)->valid()); + + // If no numeric rule is on the field, it is treated as a string + $input = array('amount' => '01'); + $rules = array('amount' => 'min:2'); + $this->assertTrue(Validator::make($input, $rules)->valid()); + + $rules = array('amount' => 'min:3'); + $this->assertFalse(Validator::make($input, $rules)->valid()); + + // The size rules checks kilobytes on files + $_FILES['photo']['tmp_name'] = 'foo'; + $_FILES['photo']['size'] = 10240; + $rules = array('photo' => 'min:9'); + $this->assertTrue(Validator::make($_FILES, $rules)->valid()); + + $_FILES['photo']['size'] = 8000; + $this->assertFalse(Validator::make($_FILES, $rules)->valid()); + } + + /** + * Test the between validation rule. + * + * @group laravel + */ + public function testTheMaxRule() + { + $input = array('amount' => '1.21'); + $rules = array('amount' => 'numeric|max:2'); + $this->assertTrue(Validator::make($input, $rules)->valid()); + + $rules = array('amount' => 'numeric|max:1'); + $this->assertFalse(Validator::make($input, $rules)->valid()); + + // If no numeric rule is on the field, it is treated as a string + $input = array('amount' => '01'); + $rules = array('amount' => 'max:3'); + $this->assertTrue(Validator::make($input, $rules)->valid()); + + $rules = array('amount' => 'max:1'); + $this->assertFalse(Validator::make($input, $rules)->valid()); + + // The size rules checks kilobytes on files + $_FILES['photo']['tmp_name'] = 'foo'; + $_FILES['photo']['size'] = 10240; + $rules = array('photo' => 'max:11'); + $this->assertTrue(Validator::make($_FILES, $rules)->valid()); + + $_FILES['photo']['size'] = 140000; + $this->assertFalse(Validator::make($_FILES, $rules)->valid()); + } + + /** + * Test the in validation rule. + * + * @group laravel + */ + public function testTheInRule() + { + $input = array('size' => 'L'); + $rules = array('size' => 'in:S,M,L'); + $this->assertTrue(Validator::make($input, $rules)->valid()); + + $input['size'] = 'XL'; + $this->assertFalse(Validator::make($input, $rules)->valid()); + } + + /** + * Test the not-in validation rule. + * + * @group laravel + */ + public function testTheNotInRule() + { + $input = array('size' => 'L'); + $rules = array('size' => 'not_in:S,M,L'); + $this->assertFalse(Validator::make($input, $rules)->valid()); + + $input['size'] = 'XL'; + $this->assertTrue(Validator::make($input, $rules)->valid()); + } + + /** + * Test the IP validation rule. + * + * @group laravel + */ + public function testTheIPRule() + { + $input = array('ip' => '192.168.1.1'); + $rules = array('ip' => 'ip'); + $this->assertTrue(Validator::make($input, $rules)->valid()); + + $input['ip'] = '192.111'; + $this->assertFalse(Validator::make($input, $rules)->valid()); + } + + /** + * Test the e-mail validation rule. + * + * @group laravel + */ + public function testTheEmailRule() + { + $input = array('email' => 'example@gmail.com'); + $rules = array('email' => 'email'); + $this->assertTrue(Validator::make($input, $rules)->valid()); + + $input['email'] = 'blas-asok'; + $this->assertFalse(Validator::make($input, $rules)->valid()); + } + + /** + * Test the URL validation rule. + * + * @group laravel + */ + public function testTheUrlRule() + { + $input = array('url' => 'http://www.google.com'); + $rules = array('url' => 'url'); + $this->assertTrue(Validator::make($input, $rules)->valid()); + + $input['url'] = 'blas-asok'; + $this->assertFalse(Validator::make($input, $rules)->valid()); + } + + /** + * Test the active URL validation rule. + * + * @group laravel + */ + public function testTheActiveUrlRule() + { + $input = array('url' => 'http://google.com'); + $rules = array('url' => 'active_url'); + $this->assertTrue(Validator::make($input, $rules)->valid()); + + $input['url'] = 'http://asdlk-aselkaiwels.com'; + $this->assertFalse(Validator::make($input, $rules)->valid()); + } + + /** + * Test the image validation rule. + * + * @group laravel + */ + public function testTheImageRule() + { + $_FILES['photo']['tmp_name'] = path('storage').'files/desert.jpg'; + $rules = array('photo' => 'image'); + $this->assertTrue(Validator::make($_FILES, $rules)->valid()); + + $_FILES['photo']['tmp_name'] = path('app').'routes.php'; + $this->assertFalse(Validator::make($_FILES, $rules)->valid()); + } + + /** + * Test the alpha validation rule. + * + * @group laravel + */ + public function testTheAlphaRule() + { + $input = array('name' => 'TaylorOtwell'); + $rules = array('name' => 'alpha'); + $this->assertTrue(Validator::make($input, $rules)->valid()); + + $input['name'] = 'Taylor Otwell'; + $this->assertFalse(Validator::make($input, $rules)->valid()); + } + + /** + * Test the alpha_num validation rule. + * + * @group laravel + */ + public function testTheAlphaNumRule() + { + $input = array('name' => 'TaylorOtwell1'); + $rules = array('name' => 'alpha_num'); + $this->assertTrue(Validator::make($input, $rules)->valid()); + + $input['name'] = 'Taylor Otwell'; + $this->assertFalse(Validator::make($input, $rules)->valid()); + } + + /** + * Test the alpha_num validation rule. + * + * @group laravel + */ + public function testTheAlphaDashRule() + { + $input = array('name' => 'Taylor-Otwell_1'); + $rules = array('name' => 'alpha_dash'); + $this->assertTrue(Validator::make($input, $rules)->valid()); + + $input['name'] = 'Taylor Otwell'; + $this->assertFalse(Validator::make($input, $rules)->valid()); + } + + /** + * Test the mimes validation rule. + * + * @group laravel + */ + public function testTheMimesRule() + { + $_FILES['file']['tmp_name'] = path('app').'routes.php'; + $rules = array('file' => 'mimes:php,txt'); + $this->assertTrue(Validator::make($_FILES, $rules)->valid()); + + $rules = array('file' => 'mimes:jpg,bmp'); + $this->assertFalse(Validator::make($_FILES, $rules)->valid()); + + $_FILES['file']['tmp_name'] = path('storage').'files/desert.jpg'; + $rules['file'] = 'mimes:jpg,bmp'; + $this->assertTrue(Validator::make($_FILES, $rules)->valid()); + + $rules['file'] = 'mimes:txt,bmp'; + $this->assertFalse(Validator::make($_FILES, $rules)->valid()); + } + + /** + * Test the unique validation rule. + * + * @group laravel + */ + public function testUniqueRule() + { + $input = array('code' => 'ZZ'); + $rules = array('code' => 'unique:validation_unique'); + $this->assertTrue(Validator::make($input, $rules)->valid()); + + $input = array('code' => 'AR'); + $this->assertFalse(Validator::make($input, $rules)->valid()); + + $rules = array('code' => 'unique:validation_unique,code,AR,code'); + $this->assertTrue(Validator::make($input, $rules)->valid()); + } + + /** + * Tests the exists validation rule. + * + * @group laravel + */ + public function testExistsRule() + { + $input = array('code' => 'TX'); + $rules = array('code' => 'exists:validation_unique'); + $this->assertTrue(Validator::make($input, $rules)->valid()); + + $input['code'] = array('TX', 'NY'); + $rules = array('code' => 'exists:validation_unique,code'); + $this->assertTrue(Validator::make($input, $rules)->valid()); + + $input['code'] = array('TX', 'XX'); + $this->assertFalse(Validator::make($input, $rules)->valid()); + + $input['code'] = 'XX'; + $this->assertFalse(Validator::make($input, $rules)->valid()); + } + + /** + * Test that the validator sets the correct messages. + * + * @group laravel + */ + public function testCorrectMessagesAreSet() + { + $lang = require path('app').'language/en/validation.php'; + + $input = array('email' => 'example-foo'); + $rules = array('name' => 'required', 'email' => 'required|email'); + $v = Validator::make($input, $rules); + $v->valid(); + $messages = $v->errors; + $this->assertInstanceOf('Laravel\\Messages', $messages); + $this->assertEquals(str_replace(':attribute', 'name', $lang['required']), $messages->first('name')); + $this->assertEquals(str_replace(':attribute', 'email', $lang['email']), $messages->first('email')); + } + + /** + * Test that custom messages are recognized. + * + * @group laravel + */ + public function testCustomMessagesAreRecognize() + { + $messages = array('required' => 'Required!'); + $rules = array('name' => 'required'); + $v = Validator::make(array(), $rules, $messages); + $v->valid(); + $this->assertEquals('Required!', $v->errors->first('name')); + + $messages['email_required'] = 'Email Required!'; + $rules = array('name' => 'required', 'email' => 'required'); + $v = Validator::make(array(), $rules, $messages); + $v->valid(); + $this->assertEquals('Required!', $v->errors->first('name')); + $this->assertEquals('Email Required!', $v->errors->first('email')); + + $rules = array('custom' => 'required'); + $v = Validator::make(array(), $rules); + $v->valid(); + $this->assertEquals('This field is required!', $v->errors->first('custom')); + } + + /** + * Test that size replacements are made on messages. + * + * @group laravel + */ + public function testNumericSizeReplacementsAreMade() + { + $lang = require path('app').'language/en/validation.php'; + + $input = array('amount' => 100); + $rules = array('amount' => 'numeric|size:80'); + $v = Validator::make($input, $rules); + $v->valid(); + $this->assertEquals(str_replace(array(':attribute', ':size'), array('amount', '80'), $lang['size']['numeric']), $v->errors->first('amount')); + + $rules = array('amount' => 'numeric|between:70,80'); + $v = Validator::make($input, $rules); + $v->valid(); + $expect = str_replace(array(':attribute', ':min', ':max'), array('amount', '70', '80'), $lang['between']['numeric']); + $this->assertEquals($expect, $v->errors->first('amount')); + + $rules = array('amount' => 'numeric|min:120'); + $v = Validator::make($input, $rules); + $v->valid(); + $expect = str_replace(array(':attribute', ':min'), array('amount', '120'), $lang['min']['numeric']); + $this->assertEquals($expect, $v->errors->first('amount')); + + $rules = array('amount' => 'numeric|max:20'); + $v = Validator::make($input, $rules); + $v->valid(); + $expect = str_replace(array(':attribute', ':max'), array('amount', '20'), $lang['max']['numeric']); + $this->assertEquals($expect, $v->errors->first('amount')); + } + + /** + * Test that string size replacements are made on messages. + * + * @group laravel + */ + public function testStringSizeReplacementsAreMade() + { + $lang = require path('app').'language/en/validation.php'; + + $input = array('amount' => '100'); + $rules = array('amount' => 'size:80'); + $v = Validator::make($input, $rules); + $v->valid(); + $this->assertEquals(str_replace(array(':attribute', ':size'), array('amount', '80'), $lang['size']['string']), $v->errors->first('amount')); + + $rules = array('amount' => 'between:70,80'); + $v = Validator::make($input, $rules); + $v->valid(); + $expect = str_replace(array(':attribute', ':min', ':max'), array('amount', '70', '80'), $lang['between']['string']); + $this->assertEquals($expect, $v->errors->first('amount')); + + $rules = array('amount' => 'min:120'); + $v = Validator::make($input, $rules); + $v->valid(); + $expect = str_replace(array(':attribute', ':min'), array('amount', '120'), $lang['min']['string']); + $this->assertEquals($expect, $v->errors->first('amount')); + + $rules = array('amount' => 'max:2'); + $v = Validator::make($input, $rules); + $v->valid(); + $expect = str_replace(array(':attribute', ':max'), array('amount', '2'), $lang['max']['string']); + $this->assertEquals($expect, $v->errors->first('amount')); + } + + /** + * Test that string size replacements are made on messages. + * + * @group laravel + */ + public function testFileSizeReplacementsAreMade() + { + $lang = require path('app').'language/en/validation.php'; + + $_FILES['amount']['tmp_name'] = 'foo'; + $_FILES['amount']['size'] = 10000; + $rules = array('amount' => 'size:80'); + $v = Validator::make($_FILES, $rules); + $v->valid(); + $this->assertEquals(str_replace(array(':attribute', ':size'), array('amount', '80'), $lang['size']['file']), $v->errors->first('amount')); + + $rules = array('amount' => 'between:70,80'); + $v = Validator::make($_FILES, $rules); + $v->valid(); + $expect = str_replace(array(':attribute', ':min', ':max'), array('amount', '70', '80'), $lang['between']['file']); + $this->assertEquals($expect, $v->errors->first('amount')); + + $rules = array('amount' => 'min:120'); + $v = Validator::make($_FILES, $rules); + $v->valid(); + $expect = str_replace(array(':attribute', ':min'), array('amount', '120'), $lang['min']['file']); + $this->assertEquals($expect, $v->errors->first('amount')); + + $rules = array('amount' => 'max:2'); + $v = Validator::make($_FILES, $rules); + $v->valid(); + $expect = str_replace(array(':attribute', ':max'), array('amount', '2'), $lang['max']['file']); + $this->assertEquals($expect, $v->errors->first('amount')); + } + + /** + * Test that values get replaced in messages. + * + * @group laravel + */ + public function testValuesGetReplaced() + { + $lang = require path('app').'language/en/validation.php'; + + $_FILES['file']['tmp_name'] = path('storage').'files/desert.jpg'; + $rules = array('file' => 'mimes:php,txt'); + $v = Validator::make($_FILES, $rules); + $v->valid(); + + $expect = str_replace(array(':attribute', ':values'), array('file', 'php, txt'), $lang['mimes']); + $this->assertEquals($expect, $v->errors->first('file')); + } + + /** + * Test custom attribute names are replaced. + * + * @group laravel + */ + public function testCustomAttributesAreReplaced() + { + $lang = require path('app').'language/en/validation.php'; + + $rules = array('test_attribute' => 'required'); + $v = Validator::make(array(), $rules); + $v->valid(); + + $expect = str_replace(':attribute', 'attribute', $lang['required']); + $this->assertEquals($expect, $v->errors->first('test_attribute')); + } + +} \ No newline at end of file diff --git a/app/laravel/tests/cases/view.test.php b/app/laravel/tests/cases/view.test.php new file mode 100644 index 000000000..9716724a1 --- /dev/null +++ b/app/laravel/tests/cases/view.test.php @@ -0,0 +1,255 @@ +assertInstanceOf('Laravel\\View', View::make('home.index')); + } + + /** + * Test the View class constructor. + * + * @group laravel + */ + public function testViewNameIsSetByConstrutor() + { + $view = new View('home.index'); + + $this->assertEquals('home.index', $view->view); + } + + /** + * Test the View class constructor. + * + * @group laravel + */ + public function testViewIsCreatedWithCorrectPath() + { + $view = new View('home.index'); + + $this->assertEquals( + str_replace(DS, '/', path('app')).'views/home/index.php', + str_replace(DS, '/', $view->path) + ); + } + + /** + * Test the View class constructor for bundles. + * + * @group laravel + */ + public function testBundleViewIsCreatedWithCorrectPath() + { + $view = new View('home.index'); + + $this->assertEquals( + str_replace(DS, '/', Bundle::path(DEFAULT_BUNDLE)).'views/home/index.php', + str_replace(DS, '/', $view->path) + ); + } + + /** + * Test the View class constructor. + * + * @group laravel + */ + public function testDataIsSetOnViewByConstructor() + { + $view = new View('home.index', array('name' => 'Taylor')); + + $this->assertEquals('Taylor', $view->data['name']); + } + + /** + * Test the View::name method. + * + * @group laravel + */ + public function testNameMethodRegistersAViewName() + { + View::name('home.index', 'home'); + + $this->assertEquals('home.index', View::$names['home']); + } + + /** + * Test the View::shared method. + * + * @group laravel + */ + public function testSharedMethodAddsDataToSharedArray() + { + View::share('comment', 'Taylor'); + + $this->assertEquals('Taylor', View::$shared['comment']); + } + + /** + * Test the View::with method. + * + * @group laravel + */ + public function testViewDataCanBeSetUsingWithMethod() + { + $view = View::make('home.index')->with('comment', 'Taylor'); + + $this->assertEquals('Taylor', $view->data['comment']); + } + + /** + * Test the View class constructor. + * + * @group laravel + */ + public function testEmptyMessageContainerSetOnViewWhenNoErrorsInSession() + { + $view = new View('home.index'); + + $this->assertInstanceOf('Laravel\\Messages', $view->data['errors']); + } + + /** + * Test the View __set method. + * + * @group laravel + */ + public function testDataCanBeSetOnViewsThroughMagicMethods() + { + $view = new View('home.index'); + + $view->comment = 'Taylor'; + + $this->assertEquals('Taylor', $view->data['comment']); + } + + /** + * Test the View __get method. + * + * @group laravel + */ + public function testDataCanBeRetrievedFromViewsThroughMagicMethods() + { + $view = new View('home.index'); + + $view->comment = 'Taylor'; + + $this->assertEquals('Taylor', $view->comment); + } + + /** + * Test the View's ArrayAccess implementation. + * + * @group laravel + */ + public function testDataCanBeSetOnTheViewThroughArrayAccess() + { + $view = new View('home.index'); + + $view['comment'] = 'Taylor'; + + $this->assertEquals('Taylor', $view->data['comment']); + } + + /** + * Test the View's ArrayAccess implementation. + * + * @group laravel + */ + public function testDataCanBeRetrievedThroughArrayAccess() + { + $view = new View('home.index'); + + $view['comment'] = 'Taylor'; + + $this->assertEquals('Taylor', $view['comment']); + } + + /** + * Test the View::nest method. + * + * @group laravel + */ + public function testNestMethodSetsViewInstanceInData() + { + $view = View::make('home.index')->nest('partial', 'tests.basic'); + + $this->assertEquals('tests.basic', $view->data['partial']->view); + + $this->assertInstanceOf('Laravel\\View', $view->data['partial']); + } + + /** + * Test that the registered data is passed to the view correctly. + * + * @group laravel + */ + public function testDataIsPassedToViewCorrectly() + { + View::share('name', 'Taylor'); + + $view = View::make('tests.basic')->with('age', 25)->render(); + + $this->assertEquals('Taylor is 25', $view); + } + + /** + * Test that the View class renders nested views. + * + * @group laravel + */ + public function testNestedViewsAreRendered() + { + $view = View::make('tests.basic') + ->with('age', 25) + ->nest('name', 'tests.nested'); + + $this->assertEquals('Taylor is 25', $view->render()); + } + + /** + * Test that the View class renders nested responses. + * + * @group laravel + */ + public function testNestedResponsesAreRendered() + { + $view = View::make('tests.basic') + ->with('age', 25) + ->with('name', Response::view('tests.nested')); + + $this->assertEquals('Taylor is 25', $view->render()); + } + + /** + * Test the View class raises a composer event. + * + * @group laravel + */ + public function testComposerEventIsCalledWhenViewIsRendering() + { + View::composer('tests.basic', function($view) + { + $view->data = array('name' => 'Taylor', 'age' => 25); + }); + + $view = View::make('tests.basic')->render(); + + $this->assertEquals('Taylor is 25', $view); + } + +} \ No newline at end of file diff --git a/app/laravel/tests/phpunit.php b/app/laravel/tests/phpunit.php new file mode 100644 index 000000000..8e5ba2047 --- /dev/null +++ b/app/laravel/tests/phpunit.php @@ -0,0 +1,32 @@ + * // Get the first segment of the request URI @@ -228,26 +102,4 @@ protected static function segments($uri) static::$segments = array_diff($segments, array('')); } - /** - * Remove a given value from the URI. - * - * @param string $uri - * @param string $value - * @return string - */ - protected static function remove($uri, $value) - { - return (strpos($uri, $value) === 0) ? substr($uri, strlen($value)) : $uri; - } - - /** - * Get the query string for the current request. - * - * @return string - */ - protected static function query() - { - return (count((array) $_GET) > 0) ? '?'.http_build_query($_GET) : ''; - } - } \ No newline at end of file diff --git a/app/laravel/url.php b/app/laravel/url.php index d523abbee..c87139e49 100644 --- a/app/laravel/url.php +++ b/app/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); } /** @@ -35,7 +35,7 @@ public static function current() * @param bool $https * @return string */ - public static function home($https = false) + public static function home($https = null) { $route = Router::find('home'); @@ -61,45 +61,21 @@ public static function base() $base = 'http://localhost'; - // If the application URL configuration is set, we will just use that + // If the application's URL configuration is set, we will just use that // instead of trying to guess the URL from the $_SERVER array's host - // and script variables as this is more reliable. + // and script variables as this is a more reliable method. if (($url = Config::get('application.url')) !== '') { $base = $url; } - elseif (isset($_SERVER['HTTP_HOST'])) + else { - $base = static::guess(); + $base = Request::foundation()->getRootUrl(); } return static::$base = $base; } - /** - * Guess the application URL based on the $_SERVER variables. - * - * @return string - */ - protected static function guess() - { - $protocol = (Request::secure()) ? 'https://' : 'http://'; - - // Basically, by removing the basename, we are removing everything after - // the and including the front controller from the URI. Leaving us with - // the installation path for the application. - $script = $_SERVER['SCRIPT_NAME']; - - $path = str_replace(basename($script), '', $script); - - // Now that we have the URL, all we need to do is attach the protocol - // protocol and HTTP_HOST to build the URL for the application, and - // we also trim off trailing slashes for cleanliness. - $uri = $protocol.$_SERVER['HTTP_HOST'].$path; - - return rtrim($uri, '/'); - } - /** * Generate an application URL. * @@ -113,13 +89,40 @@ protected static function guess() * * @param string $url * @param bool $https + * @param bool $asset + * @param bool $locale * @return string */ - public static function to($url = '', $https = false) + public static function to($url = '', $https = null, $asset = false, $locale = true) { - if (filter_var($url, FILTER_VALIDATE_URL) !== false) return $url; + // 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 + // the base URL of the application and return the full URL. + if (static::valid($url) or starts_with($url, '#')) + { + return $url; + } + + // Unless $https is specified (true or false), we maintain the current request + // 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'); + } + + $languages = Config::get('application.languages'); + + if ( ! $asset and $locale and count($languages) > 0) + { + if (in_array($default = Config::get('application.language'), $languages)) + { + $root = rtrim($root, '/').'/'.$default; + } + } // 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 @@ -128,6 +131,10 @@ public static function to($url = '', $https = false) { $root = preg_replace('~http://~', 'https://', $root, 1); } + else + { + $root = preg_replace('~https://~', 'http://', $root, 1); + } return rtrim($root, '/').'/'.ltrim($url, '/'); } @@ -171,7 +178,7 @@ public static function to_action($action, $parameters = array()) } // If no route was found that handled the given action, we'll just // generate the URL using the typical controller routing setup - // for URIs and turn SSL to false. + // for URIs and turn SSL to false by default. else { return static::convention($action, $parameters); @@ -179,7 +186,7 @@ public static function to_action($action, $parameters = array()) } /** - * Generate a action URL from a route definition + * Generate an action URL from a route definition * * @param array $route * @param string $action @@ -188,7 +195,7 @@ public static function to_action($action, $parameters = array()) */ protected static function explicit($route, $action, $parameters) { - $https = array_get(current($route), 'https', false); + $https = array_get(current($route), 'https', null); return static::to(static::transpose(key($route), $parameters), $https); } @@ -206,13 +213,11 @@ protected static function convention($action, $parameters) $bundle = Bundle::get($bundle); - // If a bundle exists for the action, we will attempt to use it's "handles" + // If a bundle exists for the action, we will attempt to use its "handles" // clause as the root of the generated URL, as the bundle can only handle // URIs that begin with that string and no others. $root = $bundle['handles'] ?: ''; - $https = false; - $parameters = implode('/', $parameters); // We'll replace both dots and @ signs in the URI since both are used @@ -234,9 +239,17 @@ protected static function convention($action, $parameters) */ public static function to_asset($url, $https = null) { - if (is_null($https)) $https = Request::secure(); + if (static::valid($url)) return $url; + + // If a base asset URL is defined in the configuration, use that and don't + // try and change the HTTP protocol. This allows the delivery of assets + // through a different server or third-party content delivery network. + if ($root = Config::get('application.asset_url', false)) + { + 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 @@ -262,7 +275,6 @@ public static function to_asset($url, $https = null) * * @param string $name * @param array $parameters - * @param bool $https * @return string */ public static function to_route($name, $parameters = array()) @@ -275,7 +287,7 @@ public static function to_route($name, $parameters = array()) // To determine whether the URL should be HTTPS or not, we look for the "https" // value on the route action array. The route has control over whether the URL // should be generated with an HTTPS protocol string or just HTTP. - $https = array_get(current($route), 'https', false); + $https = array_get(current($route), 'https', null); $uri = trim(static::transpose(key($route), $parameters), '/'); @@ -304,10 +316,21 @@ public static function transpose($uri, $parameters) // If there are any remaining optional place-holders, we'll just replace // them with empty strings since not every optional parameter has to be - // in the array of parameters that were passed. - $uri = str_replace(array_keys(Router::$optional), '', $uri); + // in the array of parameters that were passed to us. + $uri = preg_replace('/\(.+?\)/', '', $uri); return trim($uri, '/'); } -} \ No newline at end of file + /** + * Determine if the given URL is valid. + * + * @param string $url + * @return bool + */ + public static function valid($url) + { + return filter_var($url, FILTER_VALIDATE_URL) !== false; + } + +} diff --git a/app/laravel/validator.php b/app/laravel/validator.php index b18bf512e..ab446860f 100644 --- a/app/laravel/validator.php +++ b/app/laravel/validator.php @@ -214,7 +214,7 @@ protected function validatable($rule, $attribute, $value) */ protected function implicit($rule) { - return $rule == 'required' or $rule == 'accepted'; + return $rule == 'required' or $rule == 'accepted' or $rule == 'required_with'; } /** @@ -249,7 +249,7 @@ protected function validate_required($attribute, $value) { return false; } - elseif ( ! is_null(Input::file($attribute)) and $value['tmp_name'] == '') + elseif ( ! is_null(Input::file($attribute)) and is_array($value) and $value['tmp_name'] == '') { return false; } @@ -257,6 +257,27 @@ protected function validate_required($attribute, $value) return true; } + /** + * Validate that an attribute exists in the attributes array, if another + * attribute exists in the attributes array. + * + * @param string $attribute + * @param mixed $value + * @param array $parameters + * @return bool + */ + protected function validate_required_with($attribute, $value, $parameters) + { + $other = $parameters[0]; + + if ($this->validate_required($other, $this->attributes[$other])) + { + return $this->validate_required($attribute, $value); + } + + return true; + } + /** * Validate that an attribute has a matching confirmation attribute. * @@ -401,8 +422,8 @@ protected function validate_max($attribute, $value, $parameters) protected function size($attribute, $value) { // This method will determine if the attribute is a number, string, or file and - // return the proper size accordingly. If it is a number, then number itself is - // the size; if it is a file, the size is kilobytes in the size; if it is a + // return the proper size accordingly. If it is a number, the number itself is + // the size; if it is a file, the kilobytes is the size; if it is a // string, the length is the size. if (is_numeric($value) and $this->has_rule($attribute, $this->numeric_rules)) { @@ -560,7 +581,7 @@ protected function validate_active_url($attribute, $value) { $url = str_replace(array('http://', 'https://', 'ftp://'), '', Str::lower($value)); - return checkdnsrr($url); + return (trim($url) !== '') ? checkdnsrr($url) : false; } /** @@ -616,6 +637,7 @@ protected function validate_alpha_dash($attribute, $value) * * @param string $attribute * @param mixed $value + * @param array $parameters * @return bool */ protected function validate_match($attribute, $value, $parameters) @@ -646,6 +668,70 @@ protected function validate_mimes($attribute, $value, $parameters) return false; } + /** + * Validate that an attribute is an array + * + * @param string $attribute + * @param mixed $value + * @return bool + */ + protected function validate_array($attribute, $value) + { + return is_array($value); + } + + /** + * Validate that an attribute of type array has a specific count + * + * @param string $attribute + * @param mixed $value + * @param array $parameters + * @return bool + */ + protected function validate_count($attribute, $value, $parameters) + { + return (is_array($value) && count($value) == $parameters[0]); + } + + /** + * Validate that an attribute of type array has a minimum of elements. + * + * @param string $attribute + * @param mixed $value + * @param array $parameters + * @return bool + */ + protected function validate_countmin($attribute, $value, $parameters) + { + return (is_array($value) && count($value) >= $parameters[0]); + } + + /** + * Validate that an attribute of type array has a maximum of elements. + * + * @param string $attribute + * @param mixed $value + * @param array $parameters + * @return bool + */ + protected function validate_countmax($attribute, $value, $parameters) + { + return (is_array($value) && count($value) <= $parameters[0]); + } + + /** + * Validate that an attribute of type array has elements between max and min. + * + * @param string $attribute + * @param mixed $value + * @param array $parameters + * @return bool + */ + protected function validate_countbetween($attribute, $value, $parameters) + { + return (is_array($value) && count($value) >= $parameters[0] && count($value) <= $parameters[1] ); + } + /** * Validate the date is before a given date. * @@ -743,7 +829,7 @@ protected function size_message($bundle, $attribute, $rule) } // We assume that attributes present in the $_FILES array are files, // which makes sense. If the attribute doesn't have numeric rules - // and isn't as file, it's a string. + // and isn't a file, it's a string. elseif (array_key_exists($attribute, Input::file())) { $line = 'file'; @@ -862,7 +948,7 @@ protected function replace_not_in($message, $attribute, $rule, $parameters) } /** - * Replace all place-holders for the not_in rule. + * Replace all place-holders for the mimes rule. * * @param string $message * @param string $attribute @@ -931,6 +1017,62 @@ protected function replace_after($message, $attribute, $rule, $parameters) return str_replace(':date', $parameters[0], $message); } + /** + * Replace all place-holders for the count rule. + * + * @param string $message + * @param string $attribute + * @param string $rule + * @param array $parameters + * @return string + */ + protected function replace_count($message, $attribute, $rule, $parameters) + { + return str_replace(':count', $parameters[0], $message); + } + + /** + * Replace all place-holders for the countmin rule. + * + * @param string $message + * @param string $attribute + * @param string $rule + * @param array $parameters + * @return string + */ + protected function replace_countmin($message, $attribute, $rule, $parameters) + { + return str_replace(':min', $parameters[0], $message); + } + + /** + * Replace all place-holders for the countmax rule. + * + * @param string $message + * @param string $attribute + * @param string $rule + * @param array $parameters + * @return string + */ + protected function replace_countmax($message, $attribute, $rule, $parameters) + { + return str_replace(':max', $parameters[0], $message); + } + + /** + * Replace all place-holders for the between rule. + * + * @param string $message + * @param string $attribute + * @param string $rule + * @param array $parameters + * @return string + */ + protected function replace_countbetween($message, $attribute, $rule, $parameters) + { + return str_replace(array(':min', ':max'), $parameters, $message); + } + /** * Get the displayable name for a given attribute. * @@ -946,17 +1088,18 @@ protected function attribute($attribute) // of the attribute name in the message. $line = "{$bundle}validation.attributes.{$attribute}"; - $display = Lang::line($line)->get($this->language); + if (Lang::has($line, $this->language)) + { + return Lang::line($line)->get($this->language); + } // If no language line has been specified for the attribute, all of // the underscores are removed from the attribute name and that - // will be used as the attribtue name. - if (is_null($display)) + // will be used as the attribute name. + else { return str_replace('_', ' ', $attribute); } - - return $display; } /** diff --git a/app/laravel/vendor/Symfony/Component/Console/Application.php b/app/laravel/vendor/Symfony/Component/Console/Application.php new file mode 100644 index 000000000..e04940ab1 --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/Console/Application.php @@ -0,0 +1,1007 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console; + +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\ArgvInput; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\Output; +use Symfony\Component\Console\Output\ConsoleOutput; +use Symfony\Component\Console\Output\ConsoleOutputInterface; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Command\HelpCommand; +use Symfony\Component\Console\Command\ListCommand; +use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Helper\FormatterHelper; +use Symfony\Component\Console\Helper\DialogHelper; + +/** + * An Application is the container for a collection of commands. + * + * It is the main entry point of a Console application. + * + * This class is optimized for a standard CLI environment. + * + * Usage: + * + * $app = new Application('myapp', '1.0 (stable)'); + * $app->add(new SimpleCommand()); + * $app->run(); + * + * @author Fabien Potencier + * + * @api + */ +class Application +{ + private $commands; + private $wantHelps = false; + private $runningCommand; + private $name; + private $version; + private $catchExceptions; + private $autoExit; + private $definition; + private $helperSet; + + /** + * Constructor. + * + * @param string $name The name of the application + * @param string $version The version of the application + * + * @api + */ + public function __construct($name = 'UNKNOWN', $version = 'UNKNOWN') + { + $this->name = $name; + $this->version = $version; + $this->catchExceptions = true; + $this->autoExit = true; + $this->commands = array(); + $this->helperSet = $this->getDefaultHelperSet(); + $this->definition = $this->getDefaultInputDefinition(); + + foreach ($this->getDefaultCommands() as $command) { + $this->add($command); + } + } + + /** + * Runs the current application. + * + * @param InputInterface $input An Input instance + * @param OutputInterface $output An Output instance + * + * @return integer 0 if everything went fine, or an error code + * + * @throws \Exception When doRun returns Exception + * + * @api + */ + public function run(InputInterface $input = null, OutputInterface $output = null) + { + if (null === $input) { + $input = new ArgvInput(); + } + + if (null === $output) { + $output = new ConsoleOutput(); + } + + try { + $statusCode = $this->doRun($input, $output); + } catch (\Exception $e) { + if (!$this->catchExceptions) { + throw $e; + } + + if ($output instanceof ConsoleOutputInterface) { + $this->renderException($e, $output->getErrorOutput()); + } else { + $this->renderException($e, $output); + } + $statusCode = $e->getCode(); + + $statusCode = is_numeric($statusCode) && $statusCode ? $statusCode : 1; + } + + if ($this->autoExit) { + if ($statusCode > 255) { + $statusCode = 255; + } + // @codeCoverageIgnoreStart + exit($statusCode); + // @codeCoverageIgnoreEnd + } + + return $statusCode; + } + + /** + * Runs the current application. + * + * @param InputInterface $input An Input instance + * @param OutputInterface $output An Output instance + * + * @return integer 0 if everything went fine, or an error code + */ + public function doRun(InputInterface $input, OutputInterface $output) + { + $name = $this->getCommandName($input); + + if (true === $input->hasParameterOption(array('--ansi'))) { + $output->setDecorated(true); + } elseif (true === $input->hasParameterOption(array('--no-ansi'))) { + $output->setDecorated(false); + } + + if (true === $input->hasParameterOption(array('--help', '-h'))) { + if (!$name) { + $name = 'help'; + $input = new ArrayInput(array('command' => 'help')); + } else { + $this->wantHelps = true; + } + } + + if (true === $input->hasParameterOption(array('--no-interaction', '-n'))) { + $input->setInteractive(false); + } + + if (function_exists('posix_isatty') && $this->getHelperSet()->has('dialog')) { + $inputStream = $this->getHelperSet()->get('dialog')->getInputStream(); + if (!posix_isatty($inputStream)) { + $input->setInteractive(false); + } + } + + if (true === $input->hasParameterOption(array('--quiet', '-q'))) { + $output->setVerbosity(OutputInterface::VERBOSITY_QUIET); + } elseif (true === $input->hasParameterOption(array('--verbose', '-v'))) { + $output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE); + } + + if (true === $input->hasParameterOption(array('--version', '-V'))) { + $output->writeln($this->getLongVersion()); + + return 0; + } + + if (!$name) { + $name = 'list'; + $input = new ArrayInput(array('command' => 'list')); + } + + // the command name MUST be the first element of the input + $command = $this->find($name); + + $this->runningCommand = $command; + $statusCode = $command->run($input, $output); + $this->runningCommand = null; + + return is_numeric($statusCode) ? $statusCode : 0; + } + + /** + * Set a helper set to be used with the command. + * + * @param HelperSet $helperSet The helper set + * + * @api + */ + public function setHelperSet(HelperSet $helperSet) + { + $this->helperSet = $helperSet; + } + + /** + * Get the helper set associated with the command. + * + * @return HelperSet The HelperSet instance associated with this command + * + * @api + */ + public function getHelperSet() + { + return $this->helperSet; + } + + /** + * Gets the InputDefinition related to this Application. + * + * @return InputDefinition The InputDefinition instance + */ + public function getDefinition() + { + return $this->definition; + } + + /** + * Gets the help message. + * + * @return string A help message. + */ + public function getHelp() + { + $messages = array( + $this->getLongVersion(), + '', + 'Usage:', + sprintf(" [options] command [arguments]\n"), + 'Options:', + ); + + foreach ($this->getDefinition()->getOptions() as $option) { + $messages[] = sprintf(' %-29s %s %s', + '--'.$option->getName().'', + $option->getShortcut() ? '-'.$option->getShortcut().'' : ' ', + $option->getDescription() + ); + } + + return implode(PHP_EOL, $messages); + } + + /** + * Sets whether to catch exceptions or not during commands execution. + * + * @param Boolean $boolean Whether to catch exceptions or not during commands execution + * + * @api + */ + public function setCatchExceptions($boolean) + { + $this->catchExceptions = (Boolean) $boolean; + } + + /** + * Sets whether to automatically exit after a command execution or not. + * + * @param Boolean $boolean Whether to automatically exit after a command execution or not + * + * @api + */ + public function setAutoExit($boolean) + { + $this->autoExit = (Boolean) $boolean; + } + + /** + * Gets the name of the application. + * + * @return string The application name + * + * @api + */ + public function getName() + { + return $this->name; + } + + /** + * Sets the application name. + * + * @param string $name The application name + * + * @api + */ + public function setName($name) + { + $this->name = $name; + } + + /** + * Gets the application version. + * + * @return string The application version + * + * @api + */ + public function getVersion() + { + return $this->version; + } + + /** + * Sets the application version. + * + * @param string $version The application version + * + * @api + */ + public function setVersion($version) + { + $this->version = $version; + } + + /** + * Returns the long version of the application. + * + * @return string The long application version + * + * @api + */ + public function getLongVersion() + { + if ('UNKNOWN' !== $this->getName() && 'UNKNOWN' !== $this->getVersion()) { + return sprintf('%s version %s', $this->getName(), $this->getVersion()); + } + + return 'Console Tool'; + } + + /** + * Registers a new command. + * + * @param string $name The command name + * + * @return Command The newly created command + * + * @api + */ + public function register($name) + { + return $this->add(new Command($name)); + } + + /** + * Adds an array of command objects. + * + * @param Command[] $commands An array of commands + * + * @api + */ + public function addCommands(array $commands) + { + foreach ($commands as $command) { + $this->add($command); + } + } + + /** + * Adds a command object. + * + * If a command with the same name already exists, it will be overridden. + * + * @param Command $command A Command object + * + * @return Command The registered command + * + * @api + */ + public function add(Command $command) + { + $command->setApplication($this); + + if (!$command->isEnabled()) { + $command->setApplication(null); + + return; + } + + $this->commands[$command->getName()] = $command; + + foreach ($command->getAliases() as $alias) { + $this->commands[$alias] = $command; + } + + return $command; + } + + /** + * Returns a registered command by name or alias. + * + * @param string $name The command name or alias + * + * @return Command A Command object + * + * @throws \InvalidArgumentException When command name given does not exist + * + * @api + */ + public function get($name) + { + if (!isset($this->commands[$name])) { + throw new \InvalidArgumentException(sprintf('The command "%s" does not exist.', $name)); + } + + $command = $this->commands[$name]; + + if ($this->wantHelps) { + $this->wantHelps = false; + + $helpCommand = $this->get('help'); + $helpCommand->setCommand($command); + + return $helpCommand; + } + + return $command; + } + + /** + * Returns true if the command exists, false otherwise. + * + * @param string $name The command name or alias + * + * @return Boolean true if the command exists, false otherwise + * + * @api + */ + public function has($name) + { + return isset($this->commands[$name]); + } + + /** + * Returns an array of all unique namespaces used by currently registered commands. + * + * It does not returns the global namespace which always exists. + * + * @return array An array of namespaces + */ + public function getNamespaces() + { + $namespaces = array(); + foreach ($this->commands as $command) { + $namespaces[] = $this->extractNamespace($command->getName()); + + foreach ($command->getAliases() as $alias) { + $namespaces[] = $this->extractNamespace($alias); + } + } + + return array_values(array_unique(array_filter($namespaces))); + } + + /** + * Finds a registered namespace by a name or an abbreviation. + * + * @param string $namespace A namespace or abbreviation to search for + * + * @return string A registered namespace + * + * @throws \InvalidArgumentException When namespace is incorrect or ambiguous + */ + public function findNamespace($namespace) + { + $allNamespaces = array(); + foreach ($this->getNamespaces() as $n) { + $allNamespaces[$n] = explode(':', $n); + } + + $found = array(); + foreach (explode(':', $namespace) as $i => $part) { + $abbrevs = static::getAbbreviations(array_unique(array_values(array_filter(array_map(function ($p) use ($i) { return isset($p[$i]) ? $p[$i] : ''; }, $allNamespaces))))); + + if (!isset($abbrevs[$part])) { + $message = sprintf('There are no commands defined in the "%s" namespace.', $namespace); + + if (1 <= $i) { + $part = implode(':', $found).':'.$part; + } + + if ($alternatives = $this->findAlternativeNamespace($part, $abbrevs)) { + $message .= "\n\nDid you mean one of these?\n "; + $message .= implode("\n ", $alternatives); + } + + throw new \InvalidArgumentException($message); + } + + if (count($abbrevs[$part]) > 1) { + throw new \InvalidArgumentException(sprintf('The namespace "%s" is ambiguous (%s).', $namespace, $this->getAbbreviationSuggestions($abbrevs[$part]))); + } + + $found[] = $abbrevs[$part][0]; + } + + return implode(':', $found); + } + + /** + * Finds a command by name or alias. + * + * Contrary to get, this command tries to find the best + * match if you give it an abbreviation of a name or alias. + * + * @param string $name A command name or a command alias + * + * @return Command A Command instance + * + * @throws \InvalidArgumentException When command name is incorrect or ambiguous + * + * @api + */ + public function find($name) + { + // namespace + $namespace = ''; + $searchName = $name; + if (false !== $pos = strrpos($name, ':')) { + $namespace = $this->findNamespace(substr($name, 0, $pos)); + $searchName = $namespace.substr($name, $pos); + } + + // name + $commands = array(); + foreach ($this->commands as $command) { + if ($this->extractNamespace($command->getName()) == $namespace) { + $commands[] = $command->getName(); + } + } + + $abbrevs = static::getAbbreviations(array_unique($commands)); + if (isset($abbrevs[$searchName]) && 1 == count($abbrevs[$searchName])) { + return $this->get($abbrevs[$searchName][0]); + } + + if (isset($abbrevs[$searchName]) && count($abbrevs[$searchName]) > 1) { + $suggestions = $this->getAbbreviationSuggestions($abbrevs[$searchName]); + + throw new \InvalidArgumentException(sprintf('Command "%s" is ambiguous (%s).', $name, $suggestions)); + } + + // aliases + $aliases = array(); + foreach ($this->commands as $command) { + foreach ($command->getAliases() as $alias) { + if ($this->extractNamespace($alias) == $namespace) { + $aliases[] = $alias; + } + } + } + + $aliases = static::getAbbreviations(array_unique($aliases)); + if (!isset($aliases[$searchName])) { + $message = sprintf('Command "%s" is not defined.', $name); + + if ($alternatives = $this->findAlternativeCommands($searchName, $abbrevs)) { + $message .= "\n\nDid you mean one of these?\n "; + $message .= implode("\n ", $alternatives); + } + + throw new \InvalidArgumentException($message); + } + + if (count($aliases[$searchName]) > 1) { + throw new \InvalidArgumentException(sprintf('Command "%s" is ambiguous (%s).', $name, $this->getAbbreviationSuggestions($aliases[$searchName]))); + } + + return $this->get($aliases[$searchName][0]); + } + + /** + * Gets the commands (registered in the given namespace if provided). + * + * The array keys are the full names and the values the command instances. + * + * @param string $namespace A namespace name + * + * @return array An array of Command instances + * + * @api + */ + public function all($namespace = null) + { + if (null === $namespace) { + return $this->commands; + } + + $commands = array(); + foreach ($this->commands as $name => $command) { + if ($namespace === $this->extractNamespace($name, substr_count($namespace, ':') + 1)) { + $commands[$name] = $command; + } + } + + return $commands; + } + + /** + * Returns an array of possible abbreviations given a set of names. + * + * @param array $names An array of names + * + * @return array An array of abbreviations + */ + static public function getAbbreviations($names) + { + $abbrevs = array(); + foreach ($names as $name) { + for ($len = strlen($name) - 1; $len > 0; --$len) { + $abbrev = substr($name, 0, $len); + if (!isset($abbrevs[$abbrev])) { + $abbrevs[$abbrev] = array($name); + } else { + $abbrevs[$abbrev][] = $name; + } + } + } + + // Non-abbreviations always get entered, even if they aren't unique + foreach ($names as $name) { + $abbrevs[$name] = array($name); + } + + return $abbrevs; + } + + /** + * Returns a text representation of the Application. + * + * @param string $namespace An optional namespace name + * @param boolean $raw Whether to return raw command list + * + * @return string A string representing the Application + */ + public function asText($namespace = null, $raw = false) + { + $commands = $namespace ? $this->all($this->findNamespace($namespace)) : $this->commands; + + $width = 0; + foreach ($commands as $command) { + $width = strlen($command->getName()) > $width ? strlen($command->getName()) : $width; + } + $width += 2; + + if ($raw) { + $messages = array(); + foreach ($this->sortCommands($commands) as $space => $commands) { + foreach ($commands as $name => $command) { + $messages[] = sprintf("%-${width}s %s", $name, $command->getDescription()); + } + } + + return implode(PHP_EOL, $messages); + } + + $messages = array($this->getHelp(), ''); + if ($namespace) { + $messages[] = sprintf("Available commands for the \"%s\" namespace:", $namespace); + } else { + $messages[] = 'Available commands:'; + } + + // add commands by namespace + foreach ($this->sortCommands($commands) as $space => $commands) { + if (!$namespace && '_global' !== $space) { + $messages[] = ''.$space.''; + } + + foreach ($commands as $name => $command) { + $messages[] = sprintf(" %-${width}s %s", $name, $command->getDescription()); + } + } + + return implode(PHP_EOL, $messages); + } + + /** + * Returns an XML representation of the Application. + * + * @param string $namespace An optional namespace name + * @param Boolean $asDom Whether to return a DOM or an XML string + * + * @return string|DOMDocument An XML string representing the Application + */ + public function asXml($namespace = null, $asDom = false) + { + $commands = $namespace ? $this->all($this->findNamespace($namespace)) : $this->commands; + + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->formatOutput = true; + $dom->appendChild($xml = $dom->createElement('symfony')); + + $xml->appendChild($commandsXML = $dom->createElement('commands')); + + if ($namespace) { + $commandsXML->setAttribute('namespace', $namespace); + } else { + $namespacesXML = $dom->createElement('namespaces'); + $xml->appendChild($namespacesXML); + } + + // add commands by namespace + foreach ($this->sortCommands($commands) as $space => $commands) { + if (!$namespace) { + $namespaceArrayXML = $dom->createElement('namespace'); + $namespacesXML->appendChild($namespaceArrayXML); + $namespaceArrayXML->setAttribute('id', $space); + } + + foreach ($commands as $name => $command) { + if ($name !== $command->getName()) { + continue; + } + + if (!$namespace) { + $commandXML = $dom->createElement('command'); + $namespaceArrayXML->appendChild($commandXML); + $commandXML->appendChild($dom->createTextNode($name)); + } + + $node = $command->asXml(true)->getElementsByTagName('command')->item(0); + $node = $dom->importNode($node, true); + + $commandsXML->appendChild($node); + } + } + + return $asDom ? $dom : $dom->saveXml(); + } + + /** + * Renders a catched exception. + * + * @param Exception $e An exception instance + * @param OutputInterface $output An OutputInterface instance + */ + public function renderException($e, $output) + { + $strlen = function ($string) { + if (!function_exists('mb_strlen')) { + return strlen($string); + } + + if (false === $encoding = mb_detect_encoding($string)) { + return strlen($string); + } + + return mb_strlen($string, $encoding); + }; + + do { + $title = sprintf(' [%s] ', get_class($e)); + $len = $strlen($title); + $lines = array(); + foreach (explode("\n", $e->getMessage()) as $line) { + $lines[] = sprintf(' %s ', $line); + $len = max($strlen($line) + 4, $len); + } + + $messages = array(str_repeat(' ', $len), $title.str_repeat(' ', $len - $strlen($title))); + + foreach ($lines as $line) { + $messages[] = $line.str_repeat(' ', $len - $strlen($line)); + } + + $messages[] = str_repeat(' ', $len); + + $output->writeln(""); + $output->writeln(""); + foreach ($messages as $message) { + $output->writeln(''.$message.''); + } + $output->writeln(""); + $output->writeln(""); + + if (OutputInterface::VERBOSITY_VERBOSE === $output->getVerbosity()) { + $output->writeln('Exception trace:'); + + // exception related properties + $trace = $e->getTrace(); + array_unshift($trace, array( + 'function' => '', + 'file' => $e->getFile() != null ? $e->getFile() : 'n/a', + 'line' => $e->getLine() != null ? $e->getLine() : 'n/a', + 'args' => array(), + )); + + for ($i = 0, $count = count($trace); $i < $count; $i++) { + $class = isset($trace[$i]['class']) ? $trace[$i]['class'] : ''; + $type = isset($trace[$i]['type']) ? $trace[$i]['type'] : ''; + $function = $trace[$i]['function']; + $file = isset($trace[$i]['file']) ? $trace[$i]['file'] : 'n/a'; + $line = isset($trace[$i]['line']) ? $trace[$i]['line'] : 'n/a'; + + $output->writeln(sprintf(' %s%s%s() at %s:%s', $class, $type, $function, $file, $line)); + } + + $output->writeln(""); + $output->writeln(""); + } + } while ($e = $e->getPrevious()); + + if (null !== $this->runningCommand) { + $output->writeln(sprintf('%s', sprintf($this->runningCommand->getSynopsis(), $this->getName()))); + $output->writeln(""); + $output->writeln(""); + } + } + + /** + * Gets the name of the command based on input. + * + * @param InputInterface $input The input interface + * + * @return string The command name + */ + protected function getCommandName(InputInterface $input) + { + return $input->getFirstArgument('command'); + } + + /** + * Gets the default input definition. + * + * @return InputDefinition An InputDefinition instance + */ + protected function getDefaultInputDefinition() + { + return new InputDefinition(array( + new InputArgument('command', InputArgument::REQUIRED, 'The command to execute'), + + new InputOption('--help', '-h', InputOption::VALUE_NONE, 'Display this help message.'), + new InputOption('--quiet', '-q', InputOption::VALUE_NONE, 'Do not output any message.'), + new InputOption('--verbose', '-v', InputOption::VALUE_NONE, 'Increase verbosity of messages.'), + new InputOption('--version', '-V', InputOption::VALUE_NONE, 'Display this application version.'), + new InputOption('--ansi', '', InputOption::VALUE_NONE, 'Force ANSI output.'), + new InputOption('--no-ansi', '', InputOption::VALUE_NONE, 'Disable ANSI output.'), + new InputOption('--no-interaction', '-n', InputOption::VALUE_NONE, 'Do not ask any interactive question.'), + )); + } + + /** + * Gets the default commands that should always be available. + * + * @return array An array of default Command instances + */ + protected function getDefaultCommands() + { + return array(new HelpCommand(), new ListCommand()); + } + + /** + * Gets the default helper set with the helpers that should always be available. + * + * @return HelperSet A HelperSet instance + */ + protected function getDefaultHelperSet() + { + return new HelperSet(array( + new FormatterHelper(), + new DialogHelper(), + )); + } + + /** + * Sorts commands in alphabetical order. + * + * @param array $commands An associative array of commands to sort + * + * @return array A sorted array of commands + */ + private function sortCommands($commands) + { + $namespacedCommands = array(); + foreach ($commands as $name => $command) { + $key = $this->extractNamespace($name, 1); + if (!$key) { + $key = '_global'; + } + + $namespacedCommands[$key][$name] = $command; + } + ksort($namespacedCommands); + + foreach ($namespacedCommands as &$commands) { + ksort($commands); + } + + return $namespacedCommands; + } + + /** + * Returns abbreviated suggestions in string format. + * + * @param array $abbrevs Abbreviated suggestions to convert + * + * @return string A formatted string of abbreviated suggestions + */ + private function getAbbreviationSuggestions($abbrevs) + { + return sprintf('%s, %s%s', $abbrevs[0], $abbrevs[1], count($abbrevs) > 2 ? sprintf(' and %d more', count($abbrevs) - 2) : ''); + } + + /** + * Returns the namespace part of the command name. + * + * @param string $name The full name of the command + * @param string $limit The maximum number of parts of the namespace + * + * @return string The namespace of the command + */ + private function extractNamespace($name, $limit = null) + { + $parts = explode(':', $name); + array_pop($parts); + + return implode(':', null === $limit ? $parts : array_slice($parts, 0, $limit)); + } + + /** + * Finds alternative commands of $name + * + * @param string $name The full name of the command + * @param array $abbrevs The abbreviations + * + * @return array A sorted array of similar commands + */ + private function findAlternativeCommands($name, $abbrevs) + { + $callback = function($item) { + return $item->getName(); + }; + + return $this->findAlternatives($name, $this->commands, $abbrevs, $callback); + } + + /** + * Finds alternative namespace of $name + * + * @param string $name The full name of the namespace + * @param array $abbrevs The abbreviations + * + * @return array A sorted array of similar namespace + */ + private function findAlternativeNamespace($name, $abbrevs) + { + return $this->findAlternatives($name, $this->getNamespaces(), $abbrevs); + } + + /** + * Finds alternative of $name among $collection, + * if nothing is found in $collection, try in $abbrevs + * + * @param string $name The string + * @param array|Traversable $collection The collecion + * @param array $abbrevs The abbreviations + * @param Closure|string|array $callback The callable to transform collection item before comparison + * + * @return array A sorted array of similar string + */ + private function findAlternatives($name, $collection, $abbrevs, $callback = null) { + $alternatives = array(); + + foreach ($collection as $item) { + if (null !== $callback) { + $item = call_user_func($callback, $item); + } + + $lev = levenshtein($name, $item); + if ($lev <= strlen($name) / 3 || false !== strpos($item, $name)) { + $alternatives[$item] = $lev; + } + } + + if (!$alternatives) { + foreach ($abbrevs as $key => $values) { + $lev = levenshtein($name, $key); + if ($lev <= strlen($name) / 3 || false !== strpos($key, $name)) { + foreach ($values as $value) { + $alternatives[$value] = $lev; + } + } + } + } + + asort($alternatives); + + return array_keys($alternatives); + } +} diff --git a/app/laravel/vendor/Symfony/Component/Console/Command/Command.php b/app/laravel/vendor/Symfony/Component/Console/Command/Command.php new file mode 100644 index 000000000..033a95c76 --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/Console/Command/Command.php @@ -0,0 +1,612 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Command; + +use Symfony\Component\Console\Input\InputDefinition; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Helper\HelperSet; + +/** + * Base class for all commands. + * + * @author Fabien Potencier + * + * @api + */ +class Command +{ + private $application; + private $name; + private $aliases; + private $definition; + private $help; + private $description; + private $ignoreValidationErrors; + private $applicationDefinitionMerged; + private $code; + private $synopsis; + private $helperSet; + + /** + * Constructor. + * + * @param string $name The name of the command + * + * @throws \LogicException When the command name is empty + * + * @api + */ + public function __construct($name = null) + { + $this->definition = new InputDefinition(); + $this->ignoreValidationErrors = false; + $this->applicationDefinitionMerged = false; + $this->aliases = array(); + + if (null !== $name) { + $this->setName($name); + } + + $this->configure(); + + if (!$this->name) { + throw new \LogicException('The command name cannot be empty.'); + } + } + + /** + * Ignores validation errors. + * + * This is mainly useful for the help command. + */ + public function ignoreValidationErrors() + { + $this->ignoreValidationErrors = true; + } + + /** + * Sets the application instance for this command. + * + * @param Application $application An Application instance + * + * @api + */ + public function setApplication(Application $application = null) + { + $this->application = $application; + if ($application) { + $this->setHelperSet($application->getHelperSet()); + } else { + $this->helperSet = null; + } + } + + /** + * Sets the helper set. + * + * @param HelperSet $helperSet A HelperSet instance + */ + public function setHelperSet(HelperSet $helperSet) + { + $this->helperSet = $helperSet; + } + + /** + * Gets the helper set. + * + * @return HelperSet A HelperSet instance + */ + public function getHelperSet() + { + return $this->helperSet; + } + + /** + * Gets the application instance for this command. + * + * @return Application An Application instance + * + * @api + */ + public function getApplication() + { + return $this->application; + } + + /** + * Checks whether the command is enabled or not in the current environment + * + * Override this to check for x or y and return false if the command can not + * run properly under the current conditions. + * + * @return Boolean + */ + public function isEnabled() + { + return true; + } + + /** + * Configures the current command. + */ + protected function configure() + { + } + + /** + * Executes the current command. + * + * This method is not abstract because you can use this class + * as a concrete class. In this case, instead of defining the + * execute() method, you set the code to execute by passing + * a Closure to the setCode() method. + * + * @param InputInterface $input An InputInterface instance + * @param OutputInterface $output An OutputInterface instance + * + * @return integer 0 if everything went fine, or an error code + * + * @throws \LogicException When this abstract method is not implemented + * @see setCode() + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + throw new \LogicException('You must override the execute() method in the concrete command class.'); + } + + /** + * Interacts with the user. + * + * @param InputInterface $input An InputInterface instance + * @param OutputInterface $output An OutputInterface instance + */ + protected function interact(InputInterface $input, OutputInterface $output) + { + } + + /** + * Initializes the command just after the input has been validated. + * + * This is mainly useful when a lot of commands extends one main command + * where some things need to be initialized based on the input arguments and options. + * + * @param InputInterface $input An InputInterface instance + * @param OutputInterface $output An OutputInterface instance + */ + protected function initialize(InputInterface $input, OutputInterface $output) + { + } + + /** + * Runs the command. + * + * The code to execute is either defined directly with the + * setCode() method or by overriding the execute() method + * in a sub-class. + * + * @param InputInterface $input An InputInterface instance + * @param OutputInterface $output An OutputInterface instance + * + * @see setCode() + * @see execute() + * + * @api + */ + public function run(InputInterface $input, OutputInterface $output) + { + // force the creation of the synopsis before the merge with the app definition + $this->getSynopsis(); + + // add the application arguments and options + $this->mergeApplicationDefinition(); + + // bind the input against the command specific arguments/options + try { + $input->bind($this->definition); + } catch (\Exception $e) { + if (!$this->ignoreValidationErrors) { + throw $e; + } + } + + $this->initialize($input, $output); + + if ($input->isInteractive()) { + $this->interact($input, $output); + } + + $input->validate(); + + if ($this->code) { + return call_user_func($this->code, $input, $output); + } + + return $this->execute($input, $output); + } + + /** + * Sets the code to execute when running this command. + * + * If this method is used, it overrides the code defined + * in the execute() method. + * + * @param \Closure $code A \Closure + * + * @return Command The current instance + * + * @see execute() + * + * @api + */ + public function setCode(\Closure $code) + { + $this->code = $code; + + return $this; + } + + /** + * Merges the application definition with the command definition. + */ + private function mergeApplicationDefinition() + { + if (null === $this->application || true === $this->applicationDefinitionMerged) { + return; + } + + $currentArguments = $this->definition->getArguments(); + $this->definition->setArguments($this->application->getDefinition()->getArguments()); + $this->definition->addArguments($currentArguments); + + $this->definition->addOptions($this->application->getDefinition()->getOptions()); + + $this->applicationDefinitionMerged = true; + } + + /** + * Sets an array of argument and option instances. + * + * @param array|InputDefinition $definition An array of argument and option instances or a definition instance + * + * @return Command The current instance + * + * @api + */ + public function setDefinition($definition) + { + if ($definition instanceof InputDefinition) { + $this->definition = $definition; + } else { + $this->definition->setDefinition($definition); + } + + $this->applicationDefinitionMerged = false; + + return $this; + } + + /** + * Gets the InputDefinition attached to this Command. + * + * @return InputDefinition An InputDefinition instance + * + * @api + */ + public function getDefinition() + { + return $this->definition; + } + + /** + * Gets the InputDefinition to be used to create XML and Text representations of this Command. + * + * Can be overridden to provide the original command representation when it would otherwise + * be changed by merging with the application InputDefinition. + * + * @return InputDefinition An InputDefinition instance + */ + protected function getNativeDefinition() + { + return $this->getDefinition(); + } + + /** + * Adds an argument. + * + * @param string $name The argument name + * @param integer $mode The argument mode: InputArgument::REQUIRED or InputArgument::OPTIONAL + * @param string $description A description text + * @param mixed $default The default value (for InputArgument::OPTIONAL mode only) + * + * @return Command The current instance + * + * @api + */ + public function addArgument($name, $mode = null, $description = '', $default = null) + { + $this->definition->addArgument(new InputArgument($name, $mode, $description, $default)); + + return $this; + } + + /** + * Adds an option. + * + * @param string $name The option name + * @param string $shortcut The shortcut (can be null) + * @param integer $mode The option mode: One of the InputOption::VALUE_* constants + * @param string $description A description text + * @param mixed $default The default value (must be null for InputOption::VALUE_REQUIRED or InputOption::VALUE_NONE) + * + * @return Command The current instance + * + * @api + */ + public function addOption($name, $shortcut = null, $mode = null, $description = '', $default = null) + { + $this->definition->addOption(new InputOption($name, $shortcut, $mode, $description, $default)); + + return $this; + } + + /** + * Sets the name of the command. + * + * This method can set both the namespace and the name if + * you separate them by a colon (:) + * + * $command->setName('foo:bar'); + * + * @param string $name The command name + * + * @return Command The current instance + * + * @throws \InvalidArgumentException When command name given is empty + * + * @api + */ + public function setName($name) + { + $this->validateName($name); + + $this->name = $name; + + return $this; + } + + /** + * Returns the command name. + * + * @return string The command name + * + * @api + */ + public function getName() + { + return $this->name; + } + + /** + * Sets the description for the command. + * + * @param string $description The description for the command + * + * @return Command The current instance + * + * @api + */ + public function setDescription($description) + { + $this->description = $description; + + return $this; + } + + /** + * Returns the description for the command. + * + * @return string The description for the command + * + * @api + */ + public function getDescription() + { + return $this->description; + } + + /** + * Sets the help for the command. + * + * @param string $help The help for the command + * + * @return Command The current instance + * + * @api + */ + public function setHelp($help) + { + $this->help = $help; + + return $this; + } + + /** + * Returns the help for the command. + * + * @return string The help for the command + * + * @api + */ + public function getHelp() + { + return $this->help; + } + + /** + * Returns the processed help for the command replacing the %command.name% and + * %command.full_name% patterns with the real values dynamically. + * + * @return string The processed help for the command + */ + public function getProcessedHelp() + { + $name = $this->name; + + $placeholders = array( + '%command.name%', + '%command.full_name%' + ); + $replacements = array( + $name, + $_SERVER['PHP_SELF'].' '.$name + ); + + return str_replace($placeholders, $replacements, $this->getHelp()); + } + + /** + * Sets the aliases for the command. + * + * @param array $aliases An array of aliases for the command + * + * @return Command The current instance + * + * @api + */ + public function setAliases($aliases) + { + foreach ($aliases as $alias) { + $this->validateName($alias); + } + + $this->aliases = $aliases; + + return $this; + } + + /** + * Returns the aliases for the command. + * + * @return array An array of aliases for the command + * + * @api + */ + public function getAliases() + { + return $this->aliases; + } + + /** + * Returns the synopsis for the command. + * + * @return string The synopsis + */ + public function getSynopsis() + { + if (null === $this->synopsis) { + $this->synopsis = trim(sprintf('%s %s', $this->name, $this->definition->getSynopsis())); + } + + return $this->synopsis; + } + + /** + * Gets a helper instance by name. + * + * @param string $name The helper name + * + * @return mixed The helper value + * + * @throws \InvalidArgumentException if the helper is not defined + * + * @api + */ + public function getHelper($name) + { + return $this->helperSet->get($name); + } + + /** + * Returns a text representation of the command. + * + * @return string A string representing the command + */ + public function asText() + { + $messages = array( + 'Usage:', + ' '.$this->getSynopsis(), + '', + ); + + if ($this->getAliases()) { + $messages[] = 'Aliases: '.implode(', ', $this->getAliases()).''; + } + + $messages[] = $this->getNativeDefinition()->asText(); + + if ($help = $this->getProcessedHelp()) { + $messages[] = 'Help:'; + $messages[] = ' '.str_replace("\n", "\n ", $help)."\n"; + } + + return implode("\n", $messages); + } + + /** + * Returns an XML representation of the command. + * + * @param Boolean $asDom Whether to return a DOM or an XML string + * + * @return string|DOMDocument An XML string representing the command + */ + public function asXml($asDom = false) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->formatOutput = true; + $dom->appendChild($commandXML = $dom->createElement('command')); + $commandXML->setAttribute('id', $this->name); + $commandXML->setAttribute('name', $this->name); + + $commandXML->appendChild($usageXML = $dom->createElement('usage')); + $usageXML->appendChild($dom->createTextNode(sprintf($this->getSynopsis(), ''))); + + $commandXML->appendChild($descriptionXML = $dom->createElement('description')); + $descriptionXML->appendChild($dom->createTextNode(str_replace("\n", "\n ", $this->getDescription()))); + + $commandXML->appendChild($helpXML = $dom->createElement('help')); + $helpXML->appendChild($dom->createTextNode(str_replace("\n", "\n ", $this->getProcessedHelp()))); + + $commandXML->appendChild($aliasesXML = $dom->createElement('aliases')); + foreach ($this->getAliases() as $alias) { + $aliasesXML->appendChild($aliasXML = $dom->createElement('alias')); + $aliasXML->appendChild($dom->createTextNode($alias)); + } + + $definition = $this->getNativeDefinition()->asXml(true); + $commandXML->appendChild($dom->importNode($definition->getElementsByTagName('arguments')->item(0), true)); + $commandXML->appendChild($dom->importNode($definition->getElementsByTagName('options')->item(0), true)); + + return $asDom ? $dom : $dom->saveXml(); + } + + private function validateName($name) + { + if (!preg_match('/^[^\:]+(\:[^\:]+)*$/', $name)) { + throw new \InvalidArgumentException(sprintf('Command name "%s" is invalid.', $name)); + } + } +} diff --git a/app/laravel/vendor/Symfony/Component/Console/Command/HelpCommand.php b/app/laravel/vendor/Symfony/Component/Console/Command/HelpCommand.php new file mode 100644 index 000000000..93c81045c --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/Console/Command/HelpCommand.php @@ -0,0 +1,84 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Command; + +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\Output; +use Symfony\Component\Console\Command\Command; + +/** + * HelpCommand displays the help for a given command. + * + * @author Fabien Potencier + */ +class HelpCommand extends Command +{ + private $command; + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this->ignoreValidationErrors(); + + $this + ->setName('help') + ->setDefinition(array( + new InputArgument('command_name', InputArgument::OPTIONAL, 'The command name', 'help'), + new InputOption('xml', null, InputOption::VALUE_NONE, 'To output help as XML'), + )) + ->setDescription('Displays help for a command') + ->setHelp(<<%command.name% command displays help for a given command: + + php %command.full_name% list + +You can also output the help as XML by using the --xml option: + + php %command.full_name% --xml list +EOF + ) + ; + } + + /** + * Sets the command + * + * @param Command $command The command to set + */ + public function setCommand(Command $command) + { + $this->command = $command; + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + if (null === $this->command) { + $this->command = $this->getApplication()->get($input->getArgument('command_name')); + } + + if ($input->getOption('xml')) { + $output->writeln($this->command->asXml(), OutputInterface::OUTPUT_RAW); + } else { + $output->writeln($this->command->asText()); + } + + $this->command = null; + } +} diff --git a/app/laravel/vendor/Symfony/Component/Console/Command/ListCommand.php b/app/laravel/vendor/Symfony/Component/Console/Command/ListCommand.php new file mode 100644 index 000000000..032de16c1 --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/Console/Command/ListCommand.php @@ -0,0 +1,87 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Command; + +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\Output; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputDefinition; + +/** + * ListCommand displays the list of all available commands for the application. + * + * @author Fabien Potencier + */ +class ListCommand extends Command +{ + /** + * {@inheritdoc} + */ + protected function configure() + { + $this + ->setName('list') + ->setDefinition($this->createDefinition()) + ->setDescription('Lists commands') + ->setHelp(<<%command.name% command lists all commands: + + php %command.full_name% + +You can also display the commands for a specific namespace: + + php %command.full_name% test + +You can also output the information as XML by using the --xml option: + + php %command.full_name% --xml + +It's also possible to get raw list of commands (useful for embedding command runner): + + php %command.full_name% --raw +EOF + ) + ; + } + + /** + * {@inheritdoc} + */ + protected function getNativeDefinition() + { + return $this->createDefinition(); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + if ($input->getOption('xml')) { + $output->writeln($this->getApplication()->asXml($input->getArgument('namespace')), OutputInterface::OUTPUT_RAW); + } else { + $output->writeln($this->getApplication()->asText($input->getArgument('namespace'), $input->getOption('raw'))); + } + } + + private function createDefinition() + { + return new InputDefinition(array( + new InputArgument('namespace', InputArgument::OPTIONAL, 'The namespace name'), + new InputOption('xml', null, InputOption::VALUE_NONE, 'To output help as XML'), + new InputOption('raw', null, InputOption::VALUE_NONE, 'To output raw command list'), + )); + } +} diff --git a/app/laravel/vendor/Symfony/Component/Console/Formatter/OutputFormatter.php b/app/laravel/vendor/Symfony/Component/Console/Formatter/OutputFormatter.php new file mode 100644 index 000000000..8d60c74f8 --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/Console/Formatter/OutputFormatter.php @@ -0,0 +1,192 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Formatter; + +/** + * Formatter class for console output. + * + * @author Konstantin Kudryashov + * + * @api + */ +class OutputFormatter implements OutputFormatterInterface +{ + /** + * The pattern to phrase the format. + */ + const FORMAT_PATTERN = '#<([a-z][a-z0-9_=;-]+)>(.*?)#is'; + + private $decorated; + private $styles = array(); + + /** + * Initializes console output formatter. + * + * @param Boolean $decorated Whether this formatter should actually decorate strings + * @param array $styles Array of "name => FormatterStyle" instances + * + * @api + */ + public function __construct($decorated = null, array $styles = array()) + { + $this->decorated = (Boolean) $decorated; + + $this->setStyle('error', new OutputFormatterStyle('white', 'red')); + $this->setStyle('info', new OutputFormatterStyle('green')); + $this->setStyle('comment', new OutputFormatterStyle('yellow')); + $this->setStyle('question', new OutputFormatterStyle('black', 'cyan')); + + foreach ($styles as $name => $style) { + $this->setStyle($name, $style); + } + } + + /** + * Sets the decorated flag. + * + * @param Boolean $decorated Whether to decorate the messages or not + * + * @api + */ + public function setDecorated($decorated) + { + $this->decorated = (Boolean) $decorated; + } + + /** + * Gets the decorated flag. + * + * @return Boolean true if the output will decorate messages, false otherwise + * + * @api + */ + public function isDecorated() + { + return $this->decorated; + } + + /** + * Sets a new style. + * + * @param string $name The style name + * @param OutputFormatterStyleInterface $style The style instance + * + * @api + */ + public function setStyle($name, OutputFormatterStyleInterface $style) + { + $this->styles[strtolower($name)] = $style; + } + + /** + * Checks if output formatter has style with specified name. + * + * @param string $name + * + * @return Boolean + * + * @api + */ + public function hasStyle($name) + { + return isset($this->styles[strtolower($name)]); + } + + /** + * Gets style options from style with specified name. + * + * @param string $name + * + * @return OutputFormatterStyleInterface + * + * @throws \InvalidArgumentException When style isn't defined + * + * @api + */ + public function getStyle($name) + { + if (!$this->hasStyle($name)) { + throw new \InvalidArgumentException('Undefined style: '.$name); + } + + return $this->styles[strtolower($name)]; + } + + /** + * Formats a message according to the given styles. + * + * @param string $message The message to style + * + * @return string The styled message + * + * @api + */ + public function format($message) + { + return preg_replace_callback(self::FORMAT_PATTERN, array($this, 'replaceStyle'), $message); + } + + /** + * Replaces style of the output. + * + * @param array $match + * + * @return string The replaced style + */ + private function replaceStyle($match) + { + if (!$this->isDecorated()) { + return $match[2]; + } + + if (isset($this->styles[strtolower($match[1])])) { + $style = $this->styles[strtolower($match[1])]; + } else { + $style = $this->createStyleFromString($match[1]); + + if (false === $style) { + return $match[0]; + } + } + + return $style->apply($this->format($match[2])); + } + + /** + * Tries to create new style instance from string. + * + * @param string $string + * + * @return Symfony\Component\Console\Format\FormatterStyle|Boolean false if string is not format string + */ + private function createStyleFromString($string) + { + if (!preg_match_all('/([^=]+)=([^;]+)(;|$)/', strtolower($string), $matches, PREG_SET_ORDER)) { + return false; + } + + $style = new OutputFormatterStyle(); + foreach ($matches as $match) { + array_shift($match); + + if ('fg' == $match[0]) { + $style->setForeground($match[1]); + } elseif ('bg' == $match[0]) { + $style->setBackground($match[1]); + } else { + $style->setOption($match[1]); + } + } + + return $style; + } +} diff --git a/app/laravel/vendor/Symfony/Component/Console/Formatter/OutputFormatterInterface.php b/app/laravel/vendor/Symfony/Component/Console/Formatter/OutputFormatterInterface.php new file mode 100644 index 000000000..f14657ce6 --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/Console/Formatter/OutputFormatterInterface.php @@ -0,0 +1,83 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Formatter; + +/** + * Formatter interface for console output. + * + * @author Konstantin Kudryashov + * + * @api + */ +interface OutputFormatterInterface +{ + /** + * Sets the decorated flag. + * + * @param Boolean $decorated Whether to decorate the messages or not + * + * @api + */ + function setDecorated($decorated); + + /** + * Gets the decorated flag. + * + * @return Boolean true if the output will decorate messages, false otherwise + * + * @api + */ + function isDecorated(); + + /** + * Sets a new style. + * + * @param string $name The style name + * @param OutputFormatterStyleInterface $style The style instance + * + * @api + */ + function setStyle($name, OutputFormatterStyleInterface $style); + + /** + * Checks if output formatter has style with specified name. + * + * @param string $name + * + * @return Boolean + * + * @api + */ + function hasStyle($name); + + /** + * Gets style options from style with specified name. + * + * @param string $name + * + * @return OutputFormatterStyleInterface + * + * @api + */ + function getStyle($name); + + /** + * Formats a message according to the given styles. + * + * @param string $message The message to style + * + * @return string The styled message + * + * @api + */ + function format($message); +} diff --git a/app/laravel/vendor/Symfony/Component/Console/Formatter/OutputFormatterStyle.php b/app/laravel/vendor/Symfony/Component/Console/Formatter/OutputFormatterStyle.php new file mode 100644 index 000000000..dc88f2a8c --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/Console/Formatter/OutputFormatterStyle.php @@ -0,0 +1,218 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Formatter; + +/** + * Formatter style class for defining styles. + * + * @author Konstantin Kudryashov + * + * @api + */ +class OutputFormatterStyle implements OutputFormatterStyleInterface +{ + static private $availableForegroundColors = array( + 'black' => 30, + 'red' => 31, + 'green' => 32, + 'yellow' => 33, + 'blue' => 34, + 'magenta' => 35, + 'cyan' => 36, + 'white' => 37 + ); + static private $availableBackgroundColors = array( + 'black' => 40, + 'red' => 41, + 'green' => 42, + 'yellow' => 43, + 'blue' => 44, + 'magenta' => 45, + 'cyan' => 46, + 'white' => 47 + ); + static private $availableOptions = array( + 'bold' => 1, + 'underscore' => 4, + 'blink' => 5, + 'reverse' => 7, + 'conceal' => 8 + ); + + private $foreground; + private $background; + private $options = array(); + + /** + * Initializes output formatter style. + * + * @param string $foreground style foreground color name + * @param string $background style background color name + * @param array $options style options + * + * @api + */ + public function __construct($foreground = null, $background = null, array $options = array()) + { + if (null !== $foreground) { + $this->setForeground($foreground); + } + if (null !== $background) { + $this->setBackground($background); + } + if (count($options)) { + $this->setOptions($options); + } + } + + /** + * Sets style foreground color. + * + * @param string $color color name + * + * @throws \InvalidArgumentException When the color name isn't defined + * + * @api + */ + public function setForeground($color = null) + { + if (null === $color) { + $this->foreground = null; + + return; + } + + if (!isset(static::$availableForegroundColors[$color])) { + throw new \InvalidArgumentException(sprintf( + 'Invalid foreground color specified: "%s". Expected one of (%s)', + $color, + implode(', ', array_keys(static::$availableForegroundColors)) + )); + } + + $this->foreground = static::$availableForegroundColors[$color]; + } + + /** + * Sets style background color. + * + * @param string $color color name + * + * @throws \InvalidArgumentException When the color name isn't defined + * + * @api + */ + public function setBackground($color = null) + { + if (null === $color) { + $this->background = null; + + return; + } + + if (!isset(static::$availableBackgroundColors[$color])) { + throw new \InvalidArgumentException(sprintf( + 'Invalid background color specified: "%s". Expected one of (%s)', + $color, + implode(', ', array_keys(static::$availableBackgroundColors)) + )); + } + + $this->background = static::$availableBackgroundColors[$color]; + } + + /** + * Sets some specific style option. + * + * @param string $option option name + * + * @throws \InvalidArgumentException When the option name isn't defined + * + * @api + */ + public function setOption($option) + { + if (!isset(static::$availableOptions[$option])) { + throw new \InvalidArgumentException(sprintf( + 'Invalid option specified: "%s". Expected one of (%s)', + $option, + implode(', ', array_keys(static::$availableOptions)) + )); + } + + if (false === array_search(static::$availableOptions[$option], $this->options)) { + $this->options[] = static::$availableOptions[$option]; + } + } + + /** + * Unsets some specific style option. + * + * @param string $option option name + * + * @throws \InvalidArgumentException When the option name isn't defined + * + */ + public function unsetOption($option) + { + if (!isset(static::$availableOptions[$option])) { + throw new \InvalidArgumentException(sprintf( + 'Invalid option specified: "%s". Expected one of (%s)', + $option, + implode(', ', array_keys(static::$availableOptions)) + )); + } + + $pos = array_search(static::$availableOptions[$option], $this->options); + if (false !== $pos) { + unset($this->options[$pos]); + } + } + + /** + * Sets multiple style options at once. + * + * @param array $options + */ + public function setOptions(array $options) + { + $this->options = array(); + + foreach ($options as $option) { + $this->setOption($option); + } + } + + /** + * Applies the style to a given text. + * + * @param string $text The text to style + * + * @return string + */ + public function apply($text) + { + $codes = array(); + + if (null !== $this->foreground) { + $codes[] = $this->foreground; + } + if (null !== $this->background) { + $codes[] = $this->background; + } + if (count($this->options)) { + $codes = array_merge($codes, $this->options); + } + + return sprintf("\033[%sm%s\033[0m", implode(';', $codes), $text); + } +} diff --git a/app/laravel/vendor/Symfony/Component/Console/Formatter/OutputFormatterStyleInterface.php b/app/laravel/vendor/Symfony/Component/Console/Formatter/OutputFormatterStyleInterface.php new file mode 100644 index 000000000..212cb86ff --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/Console/Formatter/OutputFormatterStyleInterface.php @@ -0,0 +1,72 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Formatter; + +/** + * Formatter style interface for defining styles. + * + * @author Konstantin Kudryashov + * + * @api + */ +interface OutputFormatterStyleInterface +{ + /** + * Sets style foreground color. + * + * @param string $color color name + * + * @api + */ + function setForeground($color = null); + + /** + * Sets style background color. + * + * @param string $color color name + * + * @api + */ + function setBackground($color = null); + + /** + * Sets some specific style option. + * + * @param string $option option name + * + * @api + */ + function setOption($option); + + /** + * Unsets some specific style option. + * + * @param string $option option name + */ + function unsetOption($option); + + /** + * Sets multiple style options at once. + * + * @param array $options + */ + function setOptions(array $options); + + /** + * Applies the style to a given text. + * + * @param string $text The text to style + * + * @return string + */ + function apply($text); +} diff --git a/app/laravel/vendor/Symfony/Component/Console/Helper/DialogHelper.php b/app/laravel/vendor/Symfony/Component/Console/Helper/DialogHelper.php new file mode 100644 index 000000000..e15fdd18b --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/Console/Helper/DialogHelper.php @@ -0,0 +1,139 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Output\OutputInterface; + +/** + * The Dialog class provides helpers to interact with the user. + * + * @author Fabien Potencier + */ +class DialogHelper extends Helper +{ + private $inputStream; + + /** + * Asks a question to the user. + * + * @param OutputInterface $output An Output instance + * @param string|array $question The question to ask + * @param string $default The default answer if none is given by the user + * + * @return string The user answer + * + * @throws \RuntimeException If there is no data to read in the input stream + */ + public function ask(OutputInterface $output, $question, $default = null) + { + $output->write($question); + + $ret = fgets($this->inputStream ?: STDIN, 4096); + if (false === $ret) { + throw new \RuntimeException('Aborted'); + } + $ret = trim($ret); + + return strlen($ret) > 0 ? $ret : $default; + } + + /** + * Asks a confirmation to the user. + * + * The question will be asked until the user answers by nothing, yes, or no. + * + * @param OutputInterface $output An Output instance + * @param string|array $question The question to ask + * @param Boolean $default The default answer if the user enters nothing + * + * @return Boolean true if the user has confirmed, false otherwise + */ + public function askConfirmation(OutputInterface $output, $question, $default = true) + { + $answer = 'z'; + while ($answer && !in_array(strtolower($answer[0]), array('y', 'n'))) { + $answer = $this->ask($output, $question); + } + + if (false === $default) { + return $answer && 'y' == strtolower($answer[0]); + } + + return !$answer || 'y' == strtolower($answer[0]); + } + + /** + * Asks for a value and validates the response. + * + * The validator receives the data to validate. It must return the + * validated data when the data is valid and throw an exception + * otherwise. + * + * @param OutputInterface $output An Output instance + * @param string|array $question The question to ask + * @param callback $validator A PHP callback + * @param integer $attempts Max number of times to ask before giving up (false by default, which means infinite) + * @param string $default The default answer if none is given by the user + * + * @return mixed + * + * @throws \Exception When any of the validators return an error + */ + public function askAndValidate(OutputInterface $output, $question, $validator, $attempts = false, $default = null) + { + $error = null; + while (false === $attempts || $attempts--) { + if (null !== $error) { + $output->writeln($this->getHelperSet()->get('formatter')->formatBlock($error->getMessage(), 'error')); + } + + $value = $this->ask($output, $question, $default); + + try { + return call_user_func($validator, $value); + } catch (\Exception $error) { + } + } + + throw $error; + } + + /** + * Sets the input stream to read from when interacting with the user. + * + * This is mainly useful for testing purpose. + * + * @param resource $stream The input stream + */ + public function setInputStream($stream) + { + $this->inputStream = $stream; + } + + /** + * Returns the helper's input stream + * + * @return string + */ + public function getInputStream() + { + return $this->inputStream; + } + + /** + * Returns the helper's canonical name. + */ + public function getName() + { + return 'dialog'; + } +} diff --git a/app/laravel/vendor/Symfony/Component/Console/Helper/FormatterHelper.php b/app/laravel/vendor/Symfony/Component/Console/Helper/FormatterHelper.php new file mode 100644 index 000000000..d3f613bb7 --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/Console/Helper/FormatterHelper.php @@ -0,0 +1,97 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +/** + * The Formatter class provides helpers to format messages. + * + * @author Fabien Potencier + */ +class FormatterHelper extends Helper +{ + /** + * Formats a message within a section. + * + * @param string $section The section name + * @param string $message The message + * @param string $style The style to apply to the section + */ + public function formatSection($section, $message, $style = 'info') + { + return sprintf('<%s>[%s] %s', $style, $section, $style, $message); + } + + /** + * Formats a message as a block of text. + * + * @param string|array $messages The message to write in the block + * @param string $style The style to apply to the whole block + * @param Boolean $large Whether to return a large block + * + * @return string The formatter message + */ + public function formatBlock($messages, $style, $large = false) + { + $messages = (array) $messages; + + $len = 0; + $lines = array(); + foreach ($messages as $message) { + $lines[] = sprintf($large ? ' %s ' : ' %s ', $message); + $len = max($this->strlen($message) + ($large ? 4 : 2), $len); + } + + $messages = $large ? array(str_repeat(' ', $len)) : array(); + foreach ($lines as $line) { + $messages[] = $line.str_repeat(' ', $len - $this->strlen($line)); + } + if ($large) { + $messages[] = str_repeat(' ', $len); + } + + foreach ($messages as &$message) { + $message = sprintf('<%s>%s', $style, $message, $style); + } + + return implode("\n", $messages); + } + + /** + * Returns the length of a string, using mb_strlen if it is available. + * + * @param string $string The string to check its length + * + * @return integer The length of the string + */ + private function strlen($string) + { + if (!function_exists('mb_strlen')) { + return strlen($string); + } + + if (false === $encoding = mb_detect_encoding($string)) { + return strlen($string); + } + + return mb_strlen($string, $encoding); + } + + /** + * Returns the helper's canonical name. + * + * @return string The canonical name of the helper + */ + public function getName() + { + return 'formatter'; + } +} diff --git a/app/laravel/vendor/Symfony/Component/Console/Helper/Helper.php b/app/laravel/vendor/Symfony/Component/Console/Helper/Helper.php new file mode 100644 index 000000000..28488cafd --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/Console/Helper/Helper.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +/** + * Helper is the base class for all helper classes. + * + * @author Fabien Potencier + */ +abstract class Helper implements HelperInterface +{ + protected $helperSet = null; + + /** + * Sets the helper set associated with this helper. + * + * @param HelperSet $helperSet A HelperSet instance + */ + public function setHelperSet(HelperSet $helperSet = null) + { + $this->helperSet = $helperSet; + } + + /** + * Gets the helper set associated with this helper. + * + * @return HelperSet A HelperSet instance + */ + public function getHelperSet() + { + return $this->helperSet; + } +} diff --git a/app/laravel/vendor/Symfony/Component/Console/Helper/HelperInterface.php b/app/laravel/vendor/Symfony/Component/Console/Helper/HelperInterface.php new file mode 100644 index 000000000..25ee51393 --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/Console/Helper/HelperInterface.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +/** + * HelperInterface is the interface all helpers must implement. + * + * @author Fabien Potencier + * + * @api + */ +interface HelperInterface +{ + /** + * Sets the helper set associated with this helper. + * + * @param HelperSet $helperSet A HelperSet instance + * + * @api + */ + function setHelperSet(HelperSet $helperSet = null); + + /** + * Gets the helper set associated with this helper. + * + * @return HelperSet A HelperSet instance + * + * @api + */ + function getHelperSet(); + + /** + * Returns the canonical name of this helper. + * + * @return string The canonical name + * + * @api + */ + function getName(); +} diff --git a/app/laravel/vendor/Symfony/Component/Console/Helper/HelperSet.php b/app/laravel/vendor/Symfony/Component/Console/Helper/HelperSet.php new file mode 100644 index 000000000..0092c4c30 --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/Console/Helper/HelperSet.php @@ -0,0 +1,104 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Helper; + +use Symfony\Component\Console\Command\Command; + +/** + * HelperSet represents a set of helpers to be used with a command. + * + * @author Fabien Potencier + */ +class HelperSet +{ + private $helpers; + private $command; + + /** + * Constructor. + * + * @param Helper[] $helpers An array of helper. + */ + public function __construct(array $helpers = array()) + { + $this->helpers = array(); + foreach ($helpers as $alias => $helper) { + $this->set($helper, is_int($alias) ? null : $alias); + } + } + + /** + * Sets a helper. + * + * @param HelperInterface $helper The helper instance + * @param string $alias An alias + */ + public function set(HelperInterface $helper, $alias = null) + { + $this->helpers[$helper->getName()] = $helper; + if (null !== $alias) { + $this->helpers[$alias] = $helper; + } + + $helper->setHelperSet($this); + } + + /** + * Returns true if the helper if defined. + * + * @param string $name The helper name + * + * @return Boolean true if the helper is defined, false otherwise + */ + public function has($name) + { + return isset($this->helpers[$name]); + } + + /** + * Gets a helper value. + * + * @param string $name The helper name + * + * @return HelperInterface The helper instance + * + * @throws \InvalidArgumentException if the helper is not defined + */ + public function get($name) + { + if (!$this->has($name)) { + throw new \InvalidArgumentException(sprintf('The helper "%s" is not defined.', $name)); + } + + return $this->helpers[$name]; + } + + /** + * Sets the command associated with this helper set. + * + * @param Command $command A Command instance + */ + public function setCommand(Command $command = null) + { + $this->command = $command; + } + + /** + * Gets the command associated with this helper set. + * + * @return Command A Command instance + */ + public function getCommand() + { + return $this->command; + } +} diff --git a/app/laravel/vendor/Symfony/Component/Console/Input/ArgvInput.php b/app/laravel/vendor/Symfony/Component/Console/Input/ArgvInput.php new file mode 100644 index 000000000..f0cfb1419 --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/Console/Input/ArgvInput.php @@ -0,0 +1,311 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +/** + * ArgvInput represents an input coming from the CLI arguments. + * + * Usage: + * + * $input = new ArgvInput(); + * + * By default, the `$_SERVER['argv']` array is used for the input values. + * + * This can be overridden by explicitly passing the input values in the constructor: + * + * $input = new ArgvInput($_SERVER['argv']); + * + * If you pass it yourself, don't forget that the first element of the array + * is the name of the running application. + * + * When passing an argument to the constructor, be sure that it respects + * the same rules as the argv one. It's almost always better to use the + * `StringInput` when you want to provide your own input. + * + * @author Fabien Potencier + * + * @see http://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html + * @see http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap12.html#tag_12_02 + * + * @api + */ +class ArgvInput extends Input +{ + private $tokens; + private $parsed; + + /** + * Constructor. + * + * @param array $argv An array of parameters from the CLI (in the argv format) + * @param InputDefinition $definition A InputDefinition instance + * + * @api + */ + public function __construct(array $argv = null, InputDefinition $definition = null) + { + if (null === $argv) { + $argv = $_SERVER['argv']; + } + + // strip the application name + array_shift($argv); + + $this->tokens = $argv; + + parent::__construct($definition); + } + + protected function setTokens(array $tokens) + { + $this->tokens = $tokens; + } + + /** + * Processes command line arguments. + */ + protected function parse() + { + $parseOptions = true; + $this->parsed = $this->tokens; + while (null !== $token = array_shift($this->parsed)) { + if ($parseOptions && '--' == $token) { + $parseOptions = false; + } elseif ($parseOptions && 0 === strpos($token, '--')) { + $this->parseLongOption($token); + } elseif ($parseOptions && '-' === $token[0]) { + $this->parseShortOption($token); + } else { + $this->parseArgument($token); + } + } + } + + /** + * Parses a short option. + * + * @param string $token The current token. + */ + private function parseShortOption($token) + { + $name = substr($token, 1); + + if (strlen($name) > 1) { + if ($this->definition->hasShortcut($name[0]) && $this->definition->getOptionForShortcut($name[0])->acceptValue()) { + // an option with a value (with no space) + $this->addShortOption($name[0], substr($name, 1)); + } else { + $this->parseShortOptionSet($name); + } + } else { + $this->addShortOption($name, null); + } + } + + /** + * Parses a short option set. + * + * @param string $name The current token + * + * @throws \RuntimeException When option given doesn't exist + */ + private function parseShortOptionSet($name) + { + $len = strlen($name); + for ($i = 0; $i < $len; $i++) { + if (!$this->definition->hasShortcut($name[$i])) { + throw new \RuntimeException(sprintf('The "-%s" option does not exist.', $name[$i])); + } + + $option = $this->definition->getOptionForShortcut($name[$i]); + if ($option->acceptValue()) { + $this->addLongOption($option->getName(), $i === $len - 1 ? null : substr($name, $i + 1)); + + break; + } else { + $this->addLongOption($option->getName(), true); + } + } + } + + /** + * Parses a long option. + * + * @param string $token The current token + */ + private function parseLongOption($token) + { + $name = substr($token, 2); + + if (false !== $pos = strpos($name, '=')) { + $this->addLongOption(substr($name, 0, $pos), substr($name, $pos + 1)); + } else { + $this->addLongOption($name, null); + } + } + + /** + * Parses an argument. + * + * @param string $token The current token + * + * @throws \RuntimeException When too many arguments are given + */ + private function parseArgument($token) + { + $c = count($this->arguments); + + // if input is expecting another argument, add it + if ($this->definition->hasArgument($c)) { + $arg = $this->definition->getArgument($c); + $this->arguments[$arg->getName()] = $arg->isArray()? array($token) : $token; + + // if last argument isArray(), append token to last argument + } elseif ($this->definition->hasArgument($c - 1) && $this->definition->getArgument($c - 1)->isArray()) { + $arg = $this->definition->getArgument($c - 1); + $this->arguments[$arg->getName()][] = $token; + + // unexpected argument + } else { + throw new \RuntimeException('Too many arguments.'); + } + } + + /** + * Adds a short option value. + * + * @param string $shortcut The short option key + * @param mixed $value The value for the option + * + * @throws \RuntimeException When option given doesn't exist + */ + private function addShortOption($shortcut, $value) + { + if (!$this->definition->hasShortcut($shortcut)) { + throw new \RuntimeException(sprintf('The "-%s" option does not exist.', $shortcut)); + } + + $this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value); + } + + /** + * Adds a long option value. + * + * @param string $name The long option key + * @param mixed $value The value for the option + * + * @throws \RuntimeException When option given doesn't exist + */ + private function addLongOption($name, $value) + { + if (!$this->definition->hasOption($name)) { + throw new \RuntimeException(sprintf('The "--%s" option does not exist.', $name)); + } + + $option = $this->definition->getOption($name); + + if (null === $value && $option->acceptValue()) { + // if option accepts an optional or mandatory argument + // let's see if there is one provided + $next = array_shift($this->parsed); + if ('-' !== $next[0]) { + $value = $next; + } else { + array_unshift($this->parsed, $next); + } + } + + if (null === $value) { + if ($option->isValueRequired()) { + throw new \RuntimeException(sprintf('The "--%s" option requires a value.', $name)); + } + + $value = $option->isValueOptional() ? $option->getDefault() : true; + } + + if ($option->isArray()) { + $this->options[$name][] = $value; + } else { + $this->options[$name] = $value; + } + } + + /** + * Returns the first argument from the raw parameters (not parsed). + * + * @return string The value of the first argument or null otherwise + */ + public function getFirstArgument() + { + foreach ($this->tokens as $token) { + if ($token && '-' === $token[0]) { + continue; + } + + return $token; + } + } + + /** + * Returns true if the raw parameters (not parsed) contain a value. + * + * This method is to be used to introspect the input parameters + * before they have been validated. It must be used carefully. + * + * @param string|array $values The value(s) to look for in the raw parameters (can be an array) + * + * @return Boolean true if the value is contained in the raw parameters + */ + public function hasParameterOption($values) + { + $values = (array) $values; + + foreach ($this->tokens as $v) { + if (in_array($v, $values)) { + return true; + } + } + + return false; + } + + /** + * Returns the value of a raw option (not parsed). + * + * This method is to be used to introspect the input parameters + * before they have been validated. It must be used carefully. + * + * @param string|array $values The value(s) to look for in the raw parameters (can be an array) + * @param mixed $default The default value to return if no result is found + * + * @return mixed The option value + */ + public function getParameterOption($values, $default = false) + { + $values = (array) $values; + + $tokens = $this->tokens; + while ($token = array_shift($tokens)) { + foreach ($values as $value) { + if (0 === strpos($token, $value)) { + if (false !== $pos = strpos($token, '=')) { + return substr($token, $pos + 1); + } + + return array_shift($tokens); + } + } + } + + return $default; + } +} diff --git a/app/laravel/vendor/Symfony/Component/Console/Input/ArrayInput.php b/app/laravel/vendor/Symfony/Component/Console/Input/ArrayInput.php new file mode 100644 index 000000000..c9d8ee98a --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/Console/Input/ArrayInput.php @@ -0,0 +1,190 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +/** + * ArrayInput represents an input provided as an array. + * + * Usage: + * + * $input = new ArrayInput(array('name' => 'foo', '--bar' => 'foobar')); + * + * @author Fabien Potencier + * + * @api + */ +class ArrayInput extends Input +{ + private $parameters; + + /** + * Constructor. + * + * @param array $parameters An array of parameters + * @param InputDefinition $definition A InputDefinition instance + * + * @api + */ + public function __construct(array $parameters, InputDefinition $definition = null) + { + $this->parameters = $parameters; + + parent::__construct($definition); + } + + /** + * Returns the first argument from the raw parameters (not parsed). + * + * @return string The value of the first argument or null otherwise + */ + public function getFirstArgument() + { + foreach ($this->parameters as $key => $value) { + if ($key && '-' === $key[0]) { + continue; + } + + return $value; + } + } + + /** + * Returns true if the raw parameters (not parsed) contain a value. + * + * This method is to be used to introspect the input parameters + * before they have been validated. It must be used carefully. + * + * @param string|array $values The values to look for in the raw parameters (can be an array) + * + * @return Boolean true if the value is contained in the raw parameters + */ + public function hasParameterOption($values) + { + $values = (array) $values; + + foreach ($this->parameters as $k => $v) { + if (!is_int($k)) { + $v = $k; + } + + if (in_array($v, $values)) { + return true; + } + } + + return false; + } + + /** + * Returns the value of a raw option (not parsed). + * + * This method is to be used to introspect the input parameters + * before they have been validated. It must be used carefully. + * + * @param string|array $values The value(s) to look for in the raw parameters (can be an array) + * @param mixed $default The default value to return if no result is found + * + * @return mixed The option value + */ + public function getParameterOption($values, $default = false) + { + $values = (array) $values; + + foreach ($this->parameters as $k => $v) { + if (is_int($k) && in_array($v, $values)) { + return true; + } elseif (in_array($k, $values)) { + return $v; + } + } + + return $default; + } + + /** + * Processes command line arguments. + */ + protected function parse() + { + foreach ($this->parameters as $key => $value) { + if (0 === strpos($key, '--')) { + $this->addLongOption(substr($key, 2), $value); + } elseif ('-' === $key[0]) { + $this->addShortOption(substr($key, 1), $value); + } else { + $this->addArgument($key, $value); + } + } + } + + /** + * Adds a short option value. + * + * @param string $shortcut The short option key + * @param mixed $value The value for the option + * + * @throws \InvalidArgumentException When option given doesn't exist + */ + private function addShortOption($shortcut, $value) + { + if (!$this->definition->hasShortcut($shortcut)) { + throw new \InvalidArgumentException(sprintf('The "-%s" option does not exist.', $shortcut)); + } + + $this->addLongOption($this->definition->getOptionForShortcut($shortcut)->getName(), $value); + } + + /** + * Adds a long option value. + * + * @param string $name The long option key + * @param mixed $value The value for the option + * + * @throws \InvalidArgumentException When option given doesn't exist + * @throws \InvalidArgumentException When a required value is missing + */ + private function addLongOption($name, $value) + { + if (!$this->definition->hasOption($name)) { + throw new \InvalidArgumentException(sprintf('The "--%s" option does not exist.', $name)); + } + + $option = $this->definition->getOption($name); + + if (null === $value) { + if ($option->isValueRequired()) { + throw new \InvalidArgumentException(sprintf('The "--%s" option requires a value.', $name)); + } + + $value = $option->isValueOptional() ? $option->getDefault() : true; + } + + $this->options[$name] = $value; + } + + /** + * Adds an argument value. + * + * @param string $name The argument name + * @param mixed $value The value for the argument + * + * @throws \InvalidArgumentException When argument given doesn't exist + */ + private function addArgument($name, $value) + { + if (!$this->definition->hasArgument($name)) { + throw new \InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name)); + } + + $this->arguments[$name] = $value; + } +} diff --git a/app/laravel/vendor/Symfony/Component/Console/Input/Input.php b/app/laravel/vendor/Symfony/Component/Console/Input/Input.php new file mode 100644 index 000000000..70291be7e --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/Console/Input/Input.php @@ -0,0 +1,211 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +/** + * Input is the base class for all concrete Input classes. + * + * Three concrete classes are provided by default: + * + * * `ArgvInput`: The input comes from the CLI arguments (argv) + * * `StringInput`: The input is provided as a string + * * `ArrayInput`: The input is provided as an array + * + * @author Fabien Potencier + */ +abstract class Input implements InputInterface +{ + protected $definition; + protected $options; + protected $arguments; + protected $interactive = true; + + /** + * Constructor. + * + * @param InputDefinition $definition A InputDefinition instance + */ + public function __construct(InputDefinition $definition = null) + { + if (null === $definition) { + $this->definition = new InputDefinition(); + } else { + $this->bind($definition); + $this->validate(); + } + } + + /** + * Binds the current Input instance with the given arguments and options. + * + * @param InputDefinition $definition A InputDefinition instance + */ + public function bind(InputDefinition $definition) + { + $this->arguments = array(); + $this->options = array(); + $this->definition = $definition; + + $this->parse(); + } + + /** + * Processes command line arguments. + */ + abstract protected function parse(); + + /** + * Validates the input. + * + * @throws \RuntimeException When not enough arguments are given + */ + public function validate() + { + if (count($this->arguments) < $this->definition->getArgumentRequiredCount()) { + throw new \RuntimeException('Not enough arguments.'); + } + } + + /** + * Checks if the input is interactive. + * + * @return Boolean Returns true if the input is interactive + */ + public function isInteractive() + { + return $this->interactive; + } + + /** + * Sets the input interactivity. + * + * @param Boolean $interactive If the input should be interactive + */ + public function setInteractive($interactive) + { + $this->interactive = (Boolean) $interactive; + } + + /** + * Returns the argument values. + * + * @return array An array of argument values + */ + public function getArguments() + { + return array_merge($this->definition->getArgumentDefaults(), $this->arguments); + } + + /** + * Returns the argument value for a given argument name. + * + * @param string $name The argument name + * + * @return mixed The argument value + * + * @throws \InvalidArgumentException When argument given doesn't exist + */ + public function getArgument($name) + { + if (!$this->definition->hasArgument($name)) { + throw new \InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name)); + } + + return isset($this->arguments[$name]) ? $this->arguments[$name] : $this->definition->getArgument($name)->getDefault(); + } + + /** + * Sets an argument value by name. + * + * @param string $name The argument name + * @param string $value The argument value + * + * @throws \InvalidArgumentException When argument given doesn't exist + */ + public function setArgument($name, $value) + { + if (!$this->definition->hasArgument($name)) { + throw new \InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name)); + } + + $this->arguments[$name] = $value; + } + + /** + * Returns true if an InputArgument object exists by name or position. + * + * @param string|integer $name The InputArgument name or position + * + * @return Boolean true if the InputArgument object exists, false otherwise + */ + public function hasArgument($name) + { + return $this->definition->hasArgument($name); + } + + /** + * Returns the options values. + * + * @return array An array of option values + */ + public function getOptions() + { + return array_merge($this->definition->getOptionDefaults(), $this->options); + } + + /** + * Returns the option value for a given option name. + * + * @param string $name The option name + * + * @return mixed The option value + * + * @throws \InvalidArgumentException When option given doesn't exist + */ + public function getOption($name) + { + if (!$this->definition->hasOption($name)) { + throw new \InvalidArgumentException(sprintf('The "%s" option does not exist.', $name)); + } + + return isset($this->options[$name]) ? $this->options[$name] : $this->definition->getOption($name)->getDefault(); + } + + /** + * Sets an option value by name. + * + * @param string $name The option name + * @param string $value The option value + * + * @throws \InvalidArgumentException When option given doesn't exist + */ + public function setOption($name, $value) + { + if (!$this->definition->hasOption($name)) { + throw new \InvalidArgumentException(sprintf('The "%s" option does not exist.', $name)); + } + + $this->options[$name] = $value; + } + + /** + * Returns true if an InputOption object exists by name. + * + * @param string $name The InputOption name + * + * @return Boolean true if the InputOption object exists, false otherwise + */ + public function hasOption($name) + { + return $this->definition->hasOption($name); + } +} diff --git a/app/laravel/vendor/Symfony/Component/Console/Input/InputArgument.php b/app/laravel/vendor/Symfony/Component/Console/Input/InputArgument.php new file mode 100644 index 000000000..e7cc93531 --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/Console/Input/InputArgument.php @@ -0,0 +1,132 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +/** + * Represents a command line argument. + * + * @author Fabien Potencier + * + * @api + */ +class InputArgument +{ + const REQUIRED = 1; + const OPTIONAL = 2; + const IS_ARRAY = 4; + + private $name; + private $mode; + private $default; + private $description; + + /** + * Constructor. + * + * @param string $name The argument name + * @param integer $mode The argument mode: self::REQUIRED or self::OPTIONAL + * @param string $description A description text + * @param mixed $default The default value (for self::OPTIONAL mode only) + * + * @throws \InvalidArgumentException When argument mode is not valid + * + * @api + */ + public function __construct($name, $mode = null, $description = '', $default = null) + { + if (null === $mode) { + $mode = self::OPTIONAL; + } elseif (!is_int($mode) || $mode > 7 || $mode < 1) { + throw new \InvalidArgumentException(sprintf('Argument mode "%s" is not valid.', $mode)); + } + + $this->name = $name; + $this->mode = $mode; + $this->description = $description; + + $this->setDefault($default); + } + + /** + * Returns the argument name. + * + * @return string The argument name + */ + public function getName() + { + return $this->name; + } + + /** + * Returns true if the argument is required. + * + * @return Boolean true if parameter mode is self::REQUIRED, false otherwise + */ + public function isRequired() + { + return self::REQUIRED === (self::REQUIRED & $this->mode); + } + + /** + * Returns true if the argument can take multiple values. + * + * @return Boolean true if mode is self::IS_ARRAY, false otherwise + */ + public function isArray() + { + return self::IS_ARRAY === (self::IS_ARRAY & $this->mode); + } + + /** + * Sets the default value. + * + * @param mixed $default The default value + * + * @throws \LogicException When incorrect default value is given + */ + public function setDefault($default = null) + { + if (self::REQUIRED === $this->mode && null !== $default) { + throw new \LogicException('Cannot set a default value except for Parameter::OPTIONAL mode.'); + } + + if ($this->isArray()) { + if (null === $default) { + $default = array(); + } elseif (!is_array($default)) { + throw new \LogicException('A default value for an array argument must be an array.'); + } + } + + $this->default = $default; + } + + /** + * Returns the default value. + * + * @return mixed The default value + */ + public function getDefault() + { + return $this->default; + } + + /** + * Returns the description text. + * + * @return string The description text + */ + public function getDescription() + { + return $this->description; + } +} diff --git a/app/laravel/vendor/Symfony/Component/Console/Input/InputDefinition.php b/app/laravel/vendor/Symfony/Component/Console/Input/InputDefinition.php new file mode 100644 index 000000000..ffae4fe97 --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/Console/Input/InputDefinition.php @@ -0,0 +1,533 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +/** + * A InputDefinition represents a set of valid command line arguments and options. + * + * Usage: + * + * $definition = new InputDefinition(array( + * new InputArgument('name', InputArgument::REQUIRED), + * new InputOption('foo', 'f', InputOption::VALUE_REQUIRED), + * )); + * + * @author Fabien Potencier + * + * @api + */ +class InputDefinition +{ + private $arguments; + private $requiredCount; + private $hasAnArrayArgument = false; + private $hasOptional; + private $options; + private $shortcuts; + + /** + * Constructor. + * + * @param array $definition An array of InputArgument and InputOption instance + * + * @api + */ + public function __construct(array $definition = array()) + { + $this->setDefinition($definition); + } + + /** + * Sets the definition of the input. + * + * @param array $definition The definition array + * + * @api + */ + public function setDefinition(array $definition) + { + $arguments = array(); + $options = array(); + foreach ($definition as $item) { + if ($item instanceof InputOption) { + $options[] = $item; + } else { + $arguments[] = $item; + } + } + + $this->setArguments($arguments); + $this->setOptions($options); + } + + /** + * Sets the InputArgument objects. + * + * @param array $arguments An array of InputArgument objects + * + * @api + */ + public function setArguments($arguments = array()) + { + $this->arguments = array(); + $this->requiredCount = 0; + $this->hasOptional = false; + $this->hasAnArrayArgument = false; + $this->addArguments($arguments); + } + + /** + * Adds an array of InputArgument objects. + * + * @param InputArgument[] $arguments An array of InputArgument objects + * + * @api + */ + public function addArguments($arguments = array()) + { + if (null !== $arguments) { + foreach ($arguments as $argument) { + $this->addArgument($argument); + } + } + } + + /** + * Adds an InputArgument object. + * + * @param InputArgument $argument An InputArgument object + * + * @throws \LogicException When incorrect argument is given + * + * @api + */ + public function addArgument(InputArgument $argument) + { + if (isset($this->arguments[$argument->getName()])) { + throw new \LogicException(sprintf('An argument with name "%s" already exist.', $argument->getName())); + } + + if ($this->hasAnArrayArgument) { + throw new \LogicException('Cannot add an argument after an array argument.'); + } + + if ($argument->isRequired() && $this->hasOptional) { + throw new \LogicException('Cannot add a required argument after an optional one.'); + } + + if ($argument->isArray()) { + $this->hasAnArrayArgument = true; + } + + if ($argument->isRequired()) { + ++$this->requiredCount; + } else { + $this->hasOptional = true; + } + + $this->arguments[$argument->getName()] = $argument; + } + + /** + * Returns an InputArgument by name or by position. + * + * @param string|integer $name The InputArgument name or position + * + * @return InputArgument An InputArgument object + * + * @throws \InvalidArgumentException When argument given doesn't exist + * + * @api + */ + public function getArgument($name) + { + $arguments = is_int($name) ? array_values($this->arguments) : $this->arguments; + + if (!$this->hasArgument($name)) { + throw new \InvalidArgumentException(sprintf('The "%s" argument does not exist.', $name)); + } + + return $arguments[$name]; + } + + /** + * Returns true if an InputArgument object exists by name or position. + * + * @param string|integer $name The InputArgument name or position + * + * @return Boolean true if the InputArgument object exists, false otherwise + * + * @api + */ + public function hasArgument($name) + { + $arguments = is_int($name) ? array_values($this->arguments) : $this->arguments; + + return isset($arguments[$name]); + } + + /** + * Gets the array of InputArgument objects. + * + * @return array An array of InputArgument objects + * + * @api + */ + public function getArguments() + { + return $this->arguments; + } + + /** + * Returns the number of InputArguments. + * + * @return integer The number of InputArguments + */ + public function getArgumentCount() + { + return $this->hasAnArrayArgument ? PHP_INT_MAX : count($this->arguments); + } + + /** + * Returns the number of required InputArguments. + * + * @return integer The number of required InputArguments + */ + public function getArgumentRequiredCount() + { + return $this->requiredCount; + } + + /** + * Gets the default values. + * + * @return array An array of default values + */ + public function getArgumentDefaults() + { + $values = array(); + foreach ($this->arguments as $argument) { + $values[$argument->getName()] = $argument->getDefault(); + } + + return $values; + } + + /** + * Sets the InputOption objects. + * + * @param array $options An array of InputOption objects + * + * @api + */ + public function setOptions($options = array()) + { + $this->options = array(); + $this->shortcuts = array(); + $this->addOptions($options); + } + + /** + * Adds an array of InputOption objects. + * + * @param InputOption[] $options An array of InputOption objects + * + * @api + */ + public function addOptions($options = array()) + { + foreach ($options as $option) { + $this->addOption($option); + } + } + + /** + * Adds an InputOption object. + * + * @param InputOption $option An InputOption object + * + * @throws \LogicException When option given already exist + * + * @api + */ + public function addOption(InputOption $option) + { + if (isset($this->options[$option->getName()]) && !$option->equals($this->options[$option->getName()])) { + throw new \LogicException(sprintf('An option named "%s" already exist.', $option->getName())); + } elseif (isset($this->shortcuts[$option->getShortcut()]) && !$option->equals($this->options[$this->shortcuts[$option->getShortcut()]])) { + throw new \LogicException(sprintf('An option with shortcut "%s" already exist.', $option->getShortcut())); + } + + $this->options[$option->getName()] = $option; + if ($option->getShortcut()) { + $this->shortcuts[$option->getShortcut()] = $option->getName(); + } + } + + /** + * Returns an InputOption by name. + * + * @param string $name The InputOption name + * + * @return InputOption A InputOption object + * + * @api + */ + public function getOption($name) + { + if (!$this->hasOption($name)) { + throw new \InvalidArgumentException(sprintf('The "--%s" option does not exist.', $name)); + } + + return $this->options[$name]; + } + + /** + * Returns true if an InputOption object exists by name. + * + * @param string $name The InputOption name + * + * @return Boolean true if the InputOption object exists, false otherwise + * + * @api + */ + public function hasOption($name) + { + return isset($this->options[$name]); + } + + /** + * Gets the array of InputOption objects. + * + * @return array An array of InputOption objects + * + * @api + */ + public function getOptions() + { + return $this->options; + } + + /** + * Returns true if an InputOption object exists by shortcut. + * + * @param string $name The InputOption shortcut + * + * @return Boolean true if the InputOption object exists, false otherwise + */ + public function hasShortcut($name) + { + return isset($this->shortcuts[$name]); + } + + /** + * Gets an InputOption by shortcut. + * + * @param string $shortcut the Shortcut name + * + * @return InputOption An InputOption object + */ + public function getOptionForShortcut($shortcut) + { + return $this->getOption($this->shortcutToName($shortcut)); + } + + /** + * Gets an array of default values. + * + * @return array An array of all default values + */ + public function getOptionDefaults() + { + $values = array(); + foreach ($this->options as $option) { + $values[$option->getName()] = $option->getDefault(); + } + + return $values; + } + + /** + * Returns the InputOption name given a shortcut. + * + * @param string $shortcut The shortcut + * + * @return string The InputOption name + * + * @throws \InvalidArgumentException When option given does not exist + */ + private function shortcutToName($shortcut) + { + if (!isset($this->shortcuts[$shortcut])) { + throw new \InvalidArgumentException(sprintf('The "-%s" option does not exist.', $shortcut)); + } + + return $this->shortcuts[$shortcut]; + } + + /** + * Gets the synopsis. + * + * @return string The synopsis + */ + public function getSynopsis() + { + $elements = array(); + foreach ($this->getOptions() as $option) { + $shortcut = $option->getShortcut() ? sprintf('-%s|', $option->getShortcut()) : ''; + $elements[] = sprintf('['.($option->isValueRequired() ? '%s--%s="..."' : ($option->isValueOptional() ? '%s--%s[="..."]' : '%s--%s')).']', $shortcut, $option->getName()); + } + + foreach ($this->getArguments() as $argument) { + $elements[] = sprintf($argument->isRequired() ? '%s' : '[%s]', $argument->getName().($argument->isArray() ? '1' : '')); + + if ($argument->isArray()) { + $elements[] = sprintf('... [%sN]', $argument->getName()); + } + } + + return implode(' ', $elements); + } + + /** + * Returns a textual representation of the InputDefinition. + * + * @return string A string representing the InputDefinition + */ + public function asText() + { + // find the largest option or argument name + $max = 0; + foreach ($this->getOptions() as $option) { + $nameLength = strlen($option->getName()) + 2; + if ($option->getShortcut()) { + $nameLength += strlen($option->getShortcut()) + 3; + } + + $max = max($max, $nameLength); + } + foreach ($this->getArguments() as $argument) { + $max = max($max, strlen($argument->getName())); + } + ++$max; + + $text = array(); + + if ($this->getArguments()) { + $text[] = 'Arguments:'; + foreach ($this->getArguments() as $argument) { + if (null !== $argument->getDefault() && (!is_array($argument->getDefault()) || count($argument->getDefault()))) { + $default = sprintf(' (default: %s)', $this->formatDefaultValue($argument->getDefault())); + } else { + $default = ''; + } + + $description = str_replace("\n", "\n".str_pad('', $max + 2, ' '), $argument->getDescription()); + + $text[] = sprintf(" %-${max}s %s%s", $argument->getName(), $description, $default); + } + + $text[] = ''; + } + + if ($this->getOptions()) { + $text[] = 'Options:'; + + foreach ($this->getOptions() as $option) { + if ($option->acceptValue() && null !== $option->getDefault() && (!is_array($option->getDefault()) || count($option->getDefault()))) { + $default = sprintf(' (default: %s)', $this->formatDefaultValue($option->getDefault())); + } else { + $default = ''; + } + + $multiple = $option->isArray() ? ' (multiple values allowed)' : ''; + $description = str_replace("\n", "\n".str_pad('', $max + 2, ' '), $option->getDescription()); + + $optionMax = $max - strlen($option->getName()) - 2; + $text[] = sprintf(" %s %-${optionMax}s%s%s%s", + '--'.$option->getName(), + $option->getShortcut() ? sprintf('(-%s) ', $option->getShortcut()) : '', + $description, + $default, + $multiple + ); + } + + $text[] = ''; + } + + return implode("\n", $text); + } + + /** + * Returns an XML representation of the InputDefinition. + * + * @param Boolean $asDom Whether to return a DOM or an XML string + * + * @return string|DOMDocument An XML string representing the InputDefinition + */ + public function asXml($asDom = false) + { + $dom = new \DOMDocument('1.0', 'UTF-8'); + $dom->formatOutput = true; + $dom->appendChild($definitionXML = $dom->createElement('definition')); + + $definitionXML->appendChild($argumentsXML = $dom->createElement('arguments')); + foreach ($this->getArguments() as $argument) { + $argumentsXML->appendChild($argumentXML = $dom->createElement('argument')); + $argumentXML->setAttribute('name', $argument->getName()); + $argumentXML->setAttribute('is_required', $argument->isRequired() ? 1 : 0); + $argumentXML->setAttribute('is_array', $argument->isArray() ? 1 : 0); + $argumentXML->appendChild($descriptionXML = $dom->createElement('description')); + $descriptionXML->appendChild($dom->createTextNode($argument->getDescription())); + + $argumentXML->appendChild($defaultsXML = $dom->createElement('defaults')); + $defaults = is_array($argument->getDefault()) ? $argument->getDefault() : (is_bool($argument->getDefault()) ? array(var_export($argument->getDefault(), true)) : ($argument->getDefault() ? array($argument->getDefault()) : array())); + foreach ($defaults as $default) { + $defaultsXML->appendChild($defaultXML = $dom->createElement('default')); + $defaultXML->appendChild($dom->createTextNode($default)); + } + } + + $definitionXML->appendChild($optionsXML = $dom->createElement('options')); + foreach ($this->getOptions() as $option) { + $optionsXML->appendChild($optionXML = $dom->createElement('option')); + $optionXML->setAttribute('name', '--'.$option->getName()); + $optionXML->setAttribute('shortcut', $option->getShortcut() ? '-'.$option->getShortcut() : ''); + $optionXML->setAttribute('accept_value', $option->acceptValue() ? 1 : 0); + $optionXML->setAttribute('is_value_required', $option->isValueRequired() ? 1 : 0); + $optionXML->setAttribute('is_multiple', $option->isArray() ? 1 : 0); + $optionXML->appendChild($descriptionXML = $dom->createElement('description')); + $descriptionXML->appendChild($dom->createTextNode($option->getDescription())); + + if ($option->acceptValue()) { + $optionXML->appendChild($defaultsXML = $dom->createElement('defaults')); + $defaults = is_array($option->getDefault()) ? $option->getDefault() : (is_bool($option->getDefault()) ? array(var_export($option->getDefault(), true)) : ($option->getDefault() ? array($option->getDefault()) : array())); + foreach ($defaults as $default) { + $defaultsXML->appendChild($defaultXML = $dom->createElement('default')); + $defaultXML->appendChild($dom->createTextNode($default)); + } + } + } + + return $asDom ? $dom : $dom->saveXml(); + } + + private function formatDefaultValue($default) + { + if (is_array($default) && $default === array_values($default)) { + return sprintf("array('%s')", implode("', '", $default)); + } + + return str_replace("\n", '', var_export($default, true)); + } +} diff --git a/app/laravel/vendor/Symfony/Component/Console/Input/InputInterface.php b/app/laravel/vendor/Symfony/Component/Console/Input/InputInterface.php new file mode 100644 index 000000000..a4a622340 --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/Console/Input/InputInterface.php @@ -0,0 +1,152 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +/** + * InputInterface is the interface implemented by all input classes. + * + * @author Fabien Potencier + */ +interface InputInterface +{ + /** + * Returns the first argument from the raw parameters (not parsed). + * + * @return string The value of the first argument or null otherwise + */ + function getFirstArgument(); + + /** + * Returns true if the raw parameters (not parsed) contain a value. + * + * This method is to be used to introspect the input parameters + * before they have been validated. It must be used carefully. + * + * @param string|array $values The values to look for in the raw parameters (can be an array) + * + * @return Boolean true if the value is contained in the raw parameters + */ + function hasParameterOption($values); + + /** + * Returns the value of a raw option (not parsed). + * + * This method is to be used to introspect the input parameters + * before they have been validated. It must be used carefully. + * + * @param string|array $values The value(s) to look for in the raw parameters (can be an array) + * @param mixed $default The default value to return if no result is found + * + * @return mixed The option value + */ + function getParameterOption($values, $default = false); + + /** + * Binds the current Input instance with the given arguments and options. + * + * @param InputDefinition $definition A InputDefinition instance + */ + function bind(InputDefinition $definition); + + /** + * Validates if arguments given are correct. + * + * Throws an exception when not enough arguments are given. + * + * @throws \RuntimeException + */ + function validate(); + + /** + * Returns all the given arguments merged with the default values. + * + * @return array + */ + function getArguments(); + + /** + * Gets argument by name. + * + * @param string $name The name of the argument + * + * @return mixed + */ + function getArgument($name); + + /** + * Sets an argument value by name. + * + * @param string $name The argument name + * @param string $value The argument value + * + * @throws \InvalidArgumentException When argument given doesn't exist + */ + function setArgument($name, $value); + + /** + * Returns true if an InputArgument object exists by name or position. + * + * @param string|integer $name The InputArgument name or position + * + * @return Boolean true if the InputArgument object exists, false otherwise + */ + function hasArgument($name); + + /** + * Returns all the given options merged with the default values. + * + * @return array + */ + function getOptions(); + + /** + * Gets an option by name. + * + * @param string $name The name of the option + * + * @return mixed + */ + function getOption($name); + + /** + * Sets an option value by name. + * + * @param string $name The option name + * @param string $value The option value + * + * @throws \InvalidArgumentException When option given doesn't exist + */ + function setOption($name, $value); + + /** + * Returns true if an InputOption object exists by name. + * + * @param string $name The InputOption name + * + * @return Boolean true if the InputOption object exists, false otherwise + */ + function hasOption($name); + + /** + * Is this input means interactive? + * + * @return Boolean + */ + function isInteractive(); + + /** + * Sets the input interactivity. + * + * @param Boolean $interactive If the input should be interactive + */ + function setInteractive($interactive); +} diff --git a/app/laravel/vendor/Symfony/Component/Console/Input/InputOption.php b/app/laravel/vendor/Symfony/Component/Console/Input/InputOption.php new file mode 100644 index 000000000..0f2604556 --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/Console/Input/InputOption.php @@ -0,0 +1,201 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +/** + * Represents a command line option. + * + * @author Fabien Potencier + * + * @api + */ +class InputOption +{ + const VALUE_NONE = 1; + const VALUE_REQUIRED = 2; + const VALUE_OPTIONAL = 4; + const VALUE_IS_ARRAY = 8; + + private $name; + private $shortcut; + private $mode; + private $default; + private $description; + + /** + * Constructor. + * + * @param string $name The option name + * @param string $shortcut The shortcut (can be null) + * @param integer $mode The option mode: One of the VALUE_* constants + * @param string $description A description text + * @param mixed $default The default value (must be null for self::VALUE_REQUIRED or self::VALUE_NONE) + * + * @throws \InvalidArgumentException If option mode is invalid or incompatible + * + * @api + */ + public function __construct($name, $shortcut = null, $mode = null, $description = '', $default = null) + { + if (0 === strpos($name, '--')) { + $name = substr($name, 2); + } + + if (empty($shortcut)) { + $shortcut = null; + } + + if (null !== $shortcut) { + if ('-' === $shortcut[0]) { + $shortcut = substr($shortcut, 1); + } + } + + if (null === $mode) { + $mode = self::VALUE_NONE; + } elseif (!is_int($mode) || $mode > 15 || $mode < 1) { + throw new \InvalidArgumentException(sprintf('Option mode "%s" is not valid.', $mode)); + } + + $this->name = $name; + $this->shortcut = $shortcut; + $this->mode = $mode; + $this->description = $description; + + if ($this->isArray() && !$this->acceptValue()) { + throw new \InvalidArgumentException('Impossible to have an option mode VALUE_IS_ARRAY if the option does not accept a value.'); + } + + $this->setDefault($default); + } + + /** + * Returns the option shortcut. + * + * @return string The shortcut + */ + public function getShortcut() + { + return $this->shortcut; + } + + /** + * Returns the option name. + * + * @return string The name + */ + public function getName() + { + return $this->name; + } + + /** + * Returns true if the option accepts a value. + * + * @return Boolean true if value mode is not self::VALUE_NONE, false otherwise + */ + public function acceptValue() + { + return $this->isValueRequired() || $this->isValueOptional(); + } + + /** + * Returns true if the option requires a value. + * + * @return Boolean true if value mode is self::VALUE_REQUIRED, false otherwise + */ + public function isValueRequired() + { + return self::VALUE_REQUIRED === (self::VALUE_REQUIRED & $this->mode); + } + + /** + * Returns true if the option takes an optional value. + * + * @return Boolean true if value mode is self::VALUE_OPTIONAL, false otherwise + */ + public function isValueOptional() + { + return self::VALUE_OPTIONAL === (self::VALUE_OPTIONAL & $this->mode); + } + + /** + * Returns true if the option can take multiple values. + * + * @return Boolean true if mode is self::VALUE_IS_ARRAY, false otherwise + */ + public function isArray() + { + return self::VALUE_IS_ARRAY === (self::VALUE_IS_ARRAY & $this->mode); + } + + /** + * Sets the default value. + * + * @param mixed $default The default value + * + * @throws \LogicException When incorrect default value is given + */ + public function setDefault($default = null) + { + if (self::VALUE_NONE === (self::VALUE_NONE & $this->mode) && null !== $default) { + throw new \LogicException('Cannot set a default value when using Option::VALUE_NONE mode.'); + } + + if ($this->isArray()) { + if (null === $default) { + $default = array(); + } elseif (!is_array($default)) { + throw new \LogicException('A default value for an array option must be an array.'); + } + } + + $this->default = $this->acceptValue() ? $default : false; + } + + /** + * Returns the default value. + * + * @return mixed The default value + */ + public function getDefault() + { + return $this->default; + } + + /** + * Returns the description text. + * + * @return string The description text + */ + public function getDescription() + { + return $this->description; + } + + /** + * Checks whether the given option equals this one + * + * @param InputOption $option option to compare + * @return Boolean + */ + public function equals(InputOption $option) + { + return $option->getName() === $this->getName() + && $option->getShortcut() === $this->getShortcut() + && $option->getDefault() === $this->getDefault() + && $option->isArray() === $this->isArray() + && $option->isValueRequired() === $this->isValueRequired() + && $option->isValueOptional() === $this->isValueOptional() + ; + } +} diff --git a/app/laravel/vendor/Symfony/Component/Console/Input/StringInput.php b/app/laravel/vendor/Symfony/Component/Console/Input/StringInput.php new file mode 100644 index 000000000..72b725bd5 --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/Console/Input/StringInput.php @@ -0,0 +1,79 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Input; + +/** + * StringInput represents an input provided as a string. + * + * Usage: + * + * $input = new StringInput('foo --bar="foobar"'); + * + * @author Fabien Potencier + * + * @api + */ +class StringInput extends ArgvInput +{ + const REGEX_STRING = '([^ ]+?)(?: |(?setTokens($this->tokenize($input)); + } + + /** + * Tokenizes a string. + * + * @param string $input The input to tokenize + * + * @throws \InvalidArgumentException When unable to parse input (should never happen) + */ + private function tokenize($input) + { + $input = preg_replace('/(\r\n|\r|\n|\t)/', ' ', $input); + + $tokens = array(); + $length = strlen($input); + $cursor = 0; + while ($cursor < $length) { + if (preg_match('/\s+/A', $input, $match, null, $cursor)) { + } elseif (preg_match('/([^="\' ]+?)(=?)('.self::REGEX_QUOTED_STRING.'+)/A', $input, $match, null, $cursor)) { + $tokens[] = $match[1].$match[2].stripcslashes(str_replace(array('"\'', '\'"', '\'\'', '""'), '', substr($match[3], 1, strlen($match[3]) - 2))); + } elseif (preg_match('/'.self::REGEX_QUOTED_STRING.'/A', $input, $match, null, $cursor)) { + $tokens[] = stripcslashes(substr($match[0], 1, strlen($match[0]) - 2)); + } elseif (preg_match('/'.self::REGEX_STRING.'/A', $input, $match, null, $cursor)) { + $tokens[] = stripcslashes($match[1]); + } else { + // should never happen + // @codeCoverageIgnoreStart + throw new \InvalidArgumentException(sprintf('Unable to parse input near "... %s ..."', substr($input, $cursor, 10))); + // @codeCoverageIgnoreEnd + } + + $cursor += strlen($match[0]); + } + + return $tokens; + } +} diff --git a/app/laravel/vendor/Symfony/Component/Console/LICENSE b/app/laravel/vendor/Symfony/Component/Console/LICENSE new file mode 100644 index 000000000..cdffe7aeb --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/Console/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2012 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/app/laravel/vendor/Symfony/Component/Console/Output/ConsoleOutput.php b/app/laravel/vendor/Symfony/Component/Console/Output/ConsoleOutput.php new file mode 100644 index 000000000..1cce3326c --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/Console/Output/ConsoleOutput.php @@ -0,0 +1,83 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +use Symfony\Component\Console\Formatter\OutputFormatter; +use Symfony\Component\Console\Formatter\OutputFormatterInterface; +use Symfony\Component\Console\Output\ConsoleOutputInterface; + +/** + * ConsoleOutput is the default class for all CLI output. It uses STDOUT. + * + * This class is a convenient wrapper around `StreamOutput`. + * + * $output = new ConsoleOutput(); + * + * This is equivalent to: + * + * $output = new StreamOutput(fopen('php://stdout', 'w')); + * + * @author Fabien Potencier + * + * @api + */ +class ConsoleOutput extends StreamOutput implements ConsoleOutputInterface +{ + private $stderr; + + /** + * Constructor. + * + * @param integer $verbosity The verbosity level (self::VERBOSITY_QUIET, self::VERBOSITY_NORMAL, + * self::VERBOSITY_VERBOSE) + * @param Boolean $decorated Whether to decorate messages or not (null for auto-guessing) + * @param OutputFormatter $formatter Output formatter instance + * + * @api + */ + public function __construct($verbosity = self::VERBOSITY_NORMAL, $decorated = null, OutputFormatterInterface $formatter = null) + { + parent::__construct(fopen('php://stdout', 'w'), $verbosity, $decorated, $formatter); + $this->stderr = new StreamOutput(fopen('php://stderr', 'w'), $verbosity, $decorated, $formatter); + } + + public function setDecorated($decorated) + { + parent::setDecorated($decorated); + $this->stderr->setDecorated($decorated); + } + + public function setFormatter(OutputFormatterInterface $formatter) + { + parent::setFormatter($formatter); + $this->stderr->setFormatter($formatter); + } + + public function setVerbosity($level) + { + parent::setVerbosity($level); + $this->stderr->setVerbosity($level); + } + + /** + * @return OutputInterface + */ + public function getErrorOutput() + { + return $this->stderr; + } + + public function setErrorOutput(OutputInterface $error) + { + $this->stderr = $error; + } +} diff --git a/app/laravel/vendor/Symfony/Component/Console/Output/ConsoleOutputInterface.php b/app/laravel/vendor/Symfony/Component/Console/Output/ConsoleOutputInterface.php new file mode 100644 index 000000000..5006b8007 --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/Console/Output/ConsoleOutputInterface.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +use Symfony\Component\Console\Output\OutputInterface; + +/** + * ConsoleOutputInterface is the interface implemented by ConsoleOutput class. + * This adds information about stderr output stream. + * + * @author Dariusz Górecki + */ +interface ConsoleOutputInterface extends OutputInterface +{ + /** + * @return OutputInterface + */ + public function getErrorOutput(); + + public function setErrorOutput(OutputInterface $error); +} diff --git a/app/laravel/vendor/Symfony/Component/Console/Output/NullOutput.php b/app/laravel/vendor/Symfony/Component/Console/Output/NullOutput.php new file mode 100644 index 000000000..f6c99ab03 --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/Console/Output/NullOutput.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +/** + * NullOutput suppresses all output. + * + * $output = new NullOutput(); + * + * @author Fabien Potencier + * + * @api + */ +class NullOutput extends Output +{ + /** + * Writes a message to the output. + * + * @param string $message A message to write to the output + * @param Boolean $newline Whether to add a newline or not + */ + public function doWrite($message, $newline) + { + } +} diff --git a/app/laravel/vendor/Symfony/Component/Console/Output/Output.php b/app/laravel/vendor/Symfony/Component/Console/Output/Output.php new file mode 100644 index 000000000..222788016 --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/Console/Output/Output.php @@ -0,0 +1,180 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +use Symfony\Component\Console\Formatter\OutputFormatterInterface; +use Symfony\Component\Console\Formatter\OutputFormatter; + +/** + * Base class for output classes. + * + * There are three levels of verbosity: + * + * * normal: no option passed (normal output - information) + * * verbose: -v (more output - debug) + * * quiet: -q (no output) + * + * @author Fabien Potencier + * + * @api + */ +abstract class Output implements OutputInterface +{ + private $verbosity; + private $formatter; + + /** + * Constructor. + * + * @param integer $verbosity The verbosity level (self::VERBOSITY_QUIET, self::VERBOSITY_NORMAL, self::VERBOSITY_VERBOSE) + * @param Boolean $decorated Whether to decorate messages or not (null for auto-guessing) + * @param OutputFormatterInterface $formatter Output formatter instance + * + * @api + */ + public function __construct($verbosity = self::VERBOSITY_NORMAL, $decorated = null, OutputFormatterInterface $formatter = null) + { + $this->verbosity = null === $verbosity ? self::VERBOSITY_NORMAL : $verbosity; + $this->formatter = null === $formatter ? new OutputFormatter() : $formatter; + $this->formatter->setDecorated((Boolean) $decorated); + } + + /** + * Sets output formatter. + * + * @param OutputFormatterInterface $formatter + * + * @api + */ + public function setFormatter(OutputFormatterInterface $formatter) + { + $this->formatter = $formatter; + } + + /** + * Returns current output formatter instance. + * + * @return OutputFormatterInterface + * + * @api + */ + public function getFormatter() + { + return $this->formatter; + } + + /** + * Sets the decorated flag. + * + * @param Boolean $decorated Whether to decorate the messages or not + * + * @api + */ + public function setDecorated($decorated) + { + $this->formatter->setDecorated((Boolean) $decorated); + } + + /** + * Gets the decorated flag. + * + * @return Boolean true if the output will decorate messages, false otherwise + * + * @api + */ + public function isDecorated() + { + return $this->formatter->isDecorated(); + } + + /** + * Sets the verbosity of the output. + * + * @param integer $level The level of verbosity + * + * @api + */ + public function setVerbosity($level) + { + $this->verbosity = (int) $level; + } + + /** + * Gets the current verbosity of the output. + * + * @return integer The current level of verbosity + * + * @api + */ + public function getVerbosity() + { + return $this->verbosity; + } + + /** + * Writes a message to the output and adds a newline at the end. + * + * @param string|array $messages The message as an array of lines of a single string + * @param integer $type The type of output + * + * @api + */ + public function writeln($messages, $type = 0) + { + $this->write($messages, true, $type); + } + + /** + * Writes a message to the output. + * + * @param string|array $messages The message as an array of lines of a single string + * @param Boolean $newline Whether to add a newline or not + * @param integer $type The type of output + * + * @throws \InvalidArgumentException When unknown output type is given + * + * @api + */ + public function write($messages, $newline = false, $type = 0) + { + if (self::VERBOSITY_QUIET === $this->verbosity) { + return; + } + + $messages = (array) $messages; + + foreach ($messages as $message) { + switch ($type) { + case OutputInterface::OUTPUT_NORMAL: + $message = $this->formatter->format($message); + break; + case OutputInterface::OUTPUT_RAW: + break; + case OutputInterface::OUTPUT_PLAIN: + $message = strip_tags($this->formatter->format($message)); + break; + default: + throw new \InvalidArgumentException(sprintf('Unknown output type given (%s)', $type)); + } + + $this->doWrite($message, $newline); + } + } + + /** + * Writes a message to the output. + * + * @param string $message A message to write to the output + * @param Boolean $newline Whether to add a newline or not + */ + abstract public function doWrite($message, $newline); +} diff --git a/app/laravel/vendor/Symfony/Component/Console/Output/OutputInterface.php b/app/laravel/vendor/Symfony/Component/Console/Output/OutputInterface.php new file mode 100644 index 000000000..8423d48c9 --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/Console/Output/OutputInterface.php @@ -0,0 +1,109 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +use Symfony\Component\Console\Formatter\OutputFormatterInterface; + +/** + * OutputInterface is the interface implemented by all Output classes. + * + * @author Fabien Potencier + * + * @api + */ +interface OutputInterface +{ + const VERBOSITY_QUIET = 0; + const VERBOSITY_NORMAL = 1; + const VERBOSITY_VERBOSE = 2; + + const OUTPUT_NORMAL = 0; + const OUTPUT_RAW = 1; + const OUTPUT_PLAIN = 2; + + /** + * Writes a message to the output. + * + * @param string|array $messages The message as an array of lines of a single string + * @param Boolean $newline Whether to add a newline or not + * @param integer $type The type of output + * + * @throws \InvalidArgumentException When unknown output type is given + * + * @api + */ + function write($messages, $newline = false, $type = 0); + + /** + * Writes a message to the output and adds a newline at the end. + * + * @param string|array $messages The message as an array of lines of a single string + * @param integer $type The type of output + * + * @api + */ + function writeln($messages, $type = 0); + + /** + * Sets the verbosity of the output. + * + * @param integer $level The level of verbosity + * + * @api + */ + function setVerbosity($level); + + /** + * Gets the current verbosity of the output. + * + * @return integer The current level of verbosity + * + * @api + */ + function getVerbosity(); + + /** + * Sets the decorated flag. + * + * @param Boolean $decorated Whether to decorate the messages or not + * + * @api + */ + function setDecorated($decorated); + + /** + * Gets the decorated flag. + * + * @return Boolean true if the output will decorate messages, false otherwise + * + * @api + */ + function isDecorated(); + + /** + * Sets output formatter. + * + * @param OutputFormatterInterface $formatter + * + * @api + */ + function setFormatter(OutputFormatterInterface $formatter); + + /** + * Returns current output formatter instance. + * + * @return OutputFormatterInterface + * + * @api + */ + function getFormatter(); +} diff --git a/app/laravel/vendor/Symfony/Component/Console/Output/StreamOutput.php b/app/laravel/vendor/Symfony/Component/Console/Output/StreamOutput.php new file mode 100644 index 000000000..de1720ffc --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/Console/Output/StreamOutput.php @@ -0,0 +1,113 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Output; + +use Symfony\Component\Console\Formatter\OutputFormatterInterface; + +/** + * StreamOutput writes the output to a given stream. + * + * Usage: + * + * $output = new StreamOutput(fopen('php://stdout', 'w')); + * + * As `StreamOutput` can use any stream, you can also use a file: + * + * $output = new StreamOutput(fopen('/path/to/output.log', 'a', false)); + * + * @author Fabien Potencier + * + * @api + */ +class StreamOutput extends Output +{ + private $stream; + + /** + * Constructor. + * + * @param mixed $stream A stream resource + * @param integer $verbosity The verbosity level (self::VERBOSITY_QUIET, self::VERBOSITY_NORMAL, + * self::VERBOSITY_VERBOSE) + * @param Boolean $decorated Whether to decorate messages or not (null for auto-guessing) + * @param OutputFormatter $formatter Output formatter instance + * + * @throws \InvalidArgumentException When first argument is not a real stream + * + * @api + */ + public function __construct($stream, $verbosity = self::VERBOSITY_NORMAL, $decorated = null, OutputFormatterInterface $formatter = null) + { + if (!is_resource($stream) || 'stream' !== get_resource_type($stream)) { + throw new \InvalidArgumentException('The StreamOutput class needs a stream as its first argument.'); + } + + $this->stream = $stream; + + if (null === $decorated) { + $decorated = $this->hasColorSupport($decorated); + } + + parent::__construct($verbosity, $decorated, $formatter); + } + + /** + * Gets the stream attached to this StreamOutput instance. + * + * @return resource A stream resource + */ + public function getStream() + { + return $this->stream; + } + + /** + * Writes a message to the output. + * + * @param string $message A message to write to the output + * @param Boolean $newline Whether to add a newline or not + * + * @throws \RuntimeException When unable to write output (should never happen) + */ + public function doWrite($message, $newline) + { + if (false === @fwrite($this->stream, $message.($newline ? PHP_EOL : ''))) { + // @codeCoverageIgnoreStart + // should never happen + throw new \RuntimeException('Unable to write output.'); + // @codeCoverageIgnoreEnd + } + + fflush($this->stream); + } + + /** + * Returns true if the stream supports colorization. + * + * Colorization is disabled if not supported by the stream: + * + * - windows without ansicon + * - non tty consoles + * + * @return Boolean true if the stream supports colorization, false otherwise + */ + protected function hasColorSupport() + { + // @codeCoverageIgnoreStart + if (DIRECTORY_SEPARATOR == '\\') { + return false !== getenv('ANSICON'); + } + + return function_exists('posix_isatty') && @posix_isatty($this->stream); + // @codeCoverageIgnoreEnd + } +} diff --git a/app/laravel/vendor/Symfony/Component/Console/README.md b/app/laravel/vendor/Symfony/Component/Console/README.md new file mode 100644 index 000000000..d903776ab --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/Console/README.md @@ -0,0 +1,48 @@ +Console Component +================= + +Console eases the creation of beautiful and testable command line interfaces. + +The Application object manages the CLI application: + + use Symfony\Component\Console\Application; + + $console = new Application(); + $console->run(); + +The ``run()`` method parses the arguments and options passed on the command +line and executes the right command. + +Registering a new command can easily be done via the ``register()`` method, +which returns a ``Command`` instance: + + use Symfony\Component\Console\Input\InputInterface; + use Symfony\Component\Console\Input\InputArgument; + use Symfony\Component\Console\Input\InputOption; + use Symfony\Component\Console\Output\OutputInterface; + + $console + ->register('ls') + ->setDefinition(array( + new InputArgument('dir', InputArgument::REQUIRED, 'Directory name'), + )) + ->setDescription('Displays the files in the given directory') + ->setCode(function (InputInterface $input, OutputInterface $output) { + $dir = $input->getArgument('dir'); + + $output->writeln(sprintf('Dir listing for %s', $dir)); + }) + ; + +You can also register new commands via classes. + +The component provides a lot of features like output coloring, input and +output abstractions (so that you can easily unit-test your commands), +validation, automatic help messages, ... + +Resources +--------- + +Unit tests: + +https://github.com/symfony/symfony/tree/master/tests/Symfony/Tests/Component/Console diff --git a/app/laravel/vendor/Symfony/Component/Console/Shell.php b/app/laravel/vendor/Symfony/Component/Console/Shell.php new file mode 100644 index 000000000..6b89b048e --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/Console/Shell.php @@ -0,0 +1,206 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Input\StringInput; +use Symfony\Component\Console\Output\ConsoleOutput; +use Symfony\Component\Process\ProcessBuilder; +use Symfony\Component\Process\PhpExecutableFinder; + +/** + * A Shell wraps an Application to add shell capabilities to it. + * + * Support for history and completion only works with a PHP compiled + * with readline support (either --with-readline or --with-libedit) + * + * @author Fabien Potencier + * @author Martin Hasoň + */ +class Shell +{ + private $application; + private $history; + private $output; + private $hasReadline; + private $prompt; + private $processIsolation; + + /** + * Constructor. + * + * If there is no readline support for the current PHP executable + * a \RuntimeException exception is thrown. + * + * @param Application $application An application instance + */ + public function __construct(Application $application) + { + $this->hasReadline = function_exists('readline'); + $this->application = $application; + $this->history = getenv('HOME').'/.history_'.$application->getName(); + $this->output = new ConsoleOutput(); + $this->prompt = $application->getName().' > '; + $this->processIsolation = false; + } + + /** + * Runs the shell. + */ + public function run() + { + $this->application->setAutoExit(false); + $this->application->setCatchExceptions(true); + + if ($this->hasReadline) { + readline_read_history($this->history); + readline_completion_function(array($this, 'autocompleter')); + } + + $this->output->writeln($this->getHeader()); + $php = null; + if ($this->processIsolation) { + $finder = new PhpExecutableFinder(); + $php = $finder->find(); + $this->output->writeln(<<Running with process isolation, you should consider this: + * each command is executed as separate process, + * commands don't support interactivity, all params must be passed explicitly, + * commands output is not colorized. + +EOF + ); + } + + while (true) { + $command = $this->readline(); + + if (false === $command) { + $this->output->writeln("\n"); + + break; + } + + if ($this->hasReadline) { + readline_add_history($command); + readline_write_history($this->history); + } + + if ($this->processIsolation) { + $pb = new ProcessBuilder(); + + $process = $pb + ->add($php) + ->add($_SERVER['argv'][0]) + ->add($command) + ->inheritEnvironmentVariables(true) + ->getProcess() + ; + + $output = $this->output; + $process->run(function($type, $data) use ($output) { + $output->writeln($data); + }); + + $ret = $process->getExitCode(); + } else { + $ret = $this->application->run(new StringInput($command), $this->output); + } + + if (0 !== $ret) { + $this->output->writeln(sprintf('The command terminated with an error status (%s)', $ret)); + } + } + } + + /** + * Returns the shell header. + * + * @return string The header string + */ + protected function getHeader() + { + return <<{$this->application->getName()} shell ({$this->application->getVersion()}). + +At the prompt, type help for some help, +or list to get a list of available commands. + +To exit the shell, type ^D. + +EOF; + } + + /** + * Tries to return autocompletion for the current entered text. + * + * @param string $text The last segment of the entered text + * @return Boolean|array A list of guessed strings or true + */ + private function autocompleter($text) + { + $info = readline_info(); + $text = substr($info['line_buffer'], 0, $info['end']); + + if ($info['point'] !== $info['end']) { + return true; + } + + // task name? + if (false === strpos($text, ' ') || !$text) { + return array_keys($this->application->all()); + } + + // options and arguments? + try { + $command = $this->application->find(substr($text, 0, strpos($text, ' '))); + } catch (\Exception $e) { + return true; + } + + $list = array('--help'); + foreach ($command->getDefinition()->getOptions() as $option) { + $list[] = '--'.$option->getName(); + } + + return $list; + } + + /** + * Reads a single line from standard input. + * + * @return string The single line from standard input + */ + private function readline() + { + if ($this->hasReadline) { + $line = readline($this->prompt); + } else { + $this->output->write($this->prompt); + $line = fgets(STDIN, 1024); + $line = (!$line && strlen($line) == 0) ? false : rtrim($line); + } + + return $line; + } + + public function getProcessIsolation() + { + return $this->processIsolation; + } + + public function setProcessIsolation($processIsolation) + { + $this->processIsolation = (Boolean) $processIsolation; + } +} diff --git a/app/laravel/vendor/Symfony/Component/Console/Tester/ApplicationTester.php b/app/laravel/vendor/Symfony/Component/Console/Tester/ApplicationTester.php new file mode 100644 index 000000000..9412fbabf --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/Console/Tester/ApplicationTester.php @@ -0,0 +1,102 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tester; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Output\StreamOutput; + +/** + * @author Fabien Potencier + */ +class ApplicationTester +{ + private $application; + private $input; + private $output; + + /** + * Constructor. + * + * @param Application $application An Application instance to test. + */ + public function __construct(Application $application) + { + $this->application = $application; + } + + /** + * Executes the application. + * + * Available options: + * + * * interactive: Sets the input interactive flag + * * decorated: Sets the output decorated flag + * * verbosity: Sets the output verbosity flag + * + * @param array $input An array of arguments and options + * @param array $options An array of options + * + * @return integer The command exit code + */ + public function run(array $input, $options = array()) + { + $this->input = new ArrayInput($input); + if (isset($options['interactive'])) { + $this->input->setInteractive($options['interactive']); + } + + $this->output = new StreamOutput(fopen('php://memory', 'w', false)); + if (isset($options['decorated'])) { + $this->output->setDecorated($options['decorated']); + } + if (isset($options['verbosity'])) { + $this->output->setVerbosity($options['verbosity']); + } + + return $this->application->run($this->input, $this->output); + } + + /** + * Gets the display returned by the last execution of the application. + * + * @return string The display + */ + public function getDisplay() + { + rewind($this->output->getStream()); + + return stream_get_contents($this->output->getStream()); + } + + /** + * Gets the input instance used by the last execution of the application. + * + * @return InputInterface The current input instance + */ + public function getInput() + { + return $this->input; + } + + /** + * Gets the output instance used by the last execution of the application. + * + * @return OutputInterface The current output instance + */ + public function getOutput() + { + return $this->output; + } +} diff --git a/app/laravel/vendor/Symfony/Component/Console/Tester/CommandTester.php b/app/laravel/vendor/Symfony/Component/Console/Tester/CommandTester.php new file mode 100644 index 000000000..52be2781f --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/Console/Tester/CommandTester.php @@ -0,0 +1,100 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Console\Tester; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Output\StreamOutput; + +/** + * @author Fabien Potencier + */ +class CommandTester +{ + private $command; + private $input; + private $output; + + /** + * Constructor. + * + * @param Command $command A Command instance to test. + */ + public function __construct(Command $command) + { + $this->command = $command; + } + + /** + * Executes the command. + * + * Available options: + * + * * interactive: Sets the input interactive flag + * * decorated: Sets the output decorated flag + * * verbosity: Sets the output verbosity flag + * + * @param array $input An array of arguments and options + * @param array $options An array of options + * + * @return integer The command exit code + */ + public function execute(array $input, array $options = array()) + { + $this->input = new ArrayInput($input); + if (isset($options['interactive'])) { + $this->input->setInteractive($options['interactive']); + } + + $this->output = new StreamOutput(fopen('php://memory', 'w', false)); + if (isset($options['decorated'])) { + $this->output->setDecorated($options['decorated']); + } + if (isset($options['verbosity'])) { + $this->output->setVerbosity($options['verbosity']); + } + + return $this->command->run($this->input, $this->output); + } + + /** + * Gets the display returned by the last execution of the command. + * + * @return string The display + */ + public function getDisplay() + { + rewind($this->output->getStream()); + + return stream_get_contents($this->output->getStream()); + } + + /** + * Gets the input instance used by the last execution of the command. + * + * @return InputInterface The current input instance + */ + public function getInput() + { + return $this->input; + } + + /** + * Gets the output instance used by the last execution of the command. + * + * @return OutputInterface The current output instance + */ + public function getOutput() + { + return $this->output; + } +} diff --git a/app/laravel/vendor/Symfony/Component/Console/composer.json b/app/laravel/vendor/Symfony/Component/Console/composer.json new file mode 100644 index 000000000..961212ec0 --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/Console/composer.json @@ -0,0 +1,30 @@ +{ + "name": "symfony/console", + "type": "library", + "description": "Symfony Console Component", + "keywords": [], + "homepage": "http://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.3.2" + }, + "autoload": { + "psr-0": { "Symfony\\Component\\Console": "" } + }, + "target-dir": "Symfony/Component/Console", + "extra": { + "branch-alias": { + "dev-master": "2.1-dev" + } + } +} diff --git a/app/laravel/vendor/Symfony/Component/HttpFoundation/ApacheRequest.php b/app/laravel/vendor/Symfony/Component/HttpFoundation/ApacheRequest.php new file mode 100755 index 000000000..ca8f8eebb --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/HttpFoundation/ApacheRequest.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +/** + * Request represents an HTTP request from an Apache server. + * + * @author Fabien Potencier + */ +class ApacheRequest extends Request +{ + /** + * {@inheritdoc} + */ + protected function prepareRequestUri() + { + return $this->server->get('REQUEST_URI'); + } + + /** + * {@inheritdoc} + */ + protected function prepareBaseUrl() + { + $baseUrl = $this->server->get('SCRIPT_NAME'); + + if (false === strpos($this->server->get('REQUEST_URI'), $baseUrl)) { + // assume mod_rewrite + return rtrim(dirname($baseUrl), '/\\'); + } + + return $baseUrl; + } + + /** + * {@inheritdoc} + */ + protected function preparePathInfo() + { + return $this->server->get('PATH_INFO') ?: substr($this->prepareRequestUri(), strlen($this->prepareBaseUrl())) ?: '/'; + } +} diff --git a/app/laravel/vendor/Symfony/Component/HttpFoundation/Cookie.php b/app/laravel/vendor/Symfony/Component/HttpFoundation/Cookie.php new file mode 100755 index 000000000..0511162aa --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/HttpFoundation/Cookie.php @@ -0,0 +1,203 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +/** + * Represents a cookie + * + * @author Johannes M. Schmitt + * + * @api + */ +class Cookie +{ + protected $name; + protected $value; + protected $domain; + protected $expire; + protected $path; + protected $secure; + protected $httpOnly; + + /** + * Constructor. + * + * @param string $name The name of the cookie + * @param string $value The value of the cookie + * @param integer|string|\DateTime $expire The time the cookie expires + * @param string $path The path on the server in which the cookie will be available on + * @param string $domain The domain that the cookie is available to + * @param Boolean $secure Whether the cookie should only be transmitted over a secure HTTPS connection from the client + * @param Boolean $httpOnly Whether the cookie will be made accessible only through the HTTP protocol + * + * @api + */ + public function __construct($name, $value = null, $expire = 0, $path = '/', $domain = null, $secure = false, $httpOnly = true) + { + // from PHP source code + if (preg_match("/[=,; \t\r\n\013\014]/", $name)) { + throw new \InvalidArgumentException(sprintf('The cookie name "%s" contains invalid characters.', $name)); + } + + if (empty($name)) { + throw new \InvalidArgumentException('The cookie name cannot be empty.'); + } + + // convert expiration time to a Unix timestamp + if ($expire instanceof \DateTime) { + $expire = $expire->format('U'); + } elseif (!is_numeric($expire)) { + $expire = strtotime($expire); + + if (false === $expire || -1 === $expire) { + throw new \InvalidArgumentException('The cookie expiration time is not valid.'); + } + } + + $this->name = $name; + $this->value = $value; + $this->domain = $domain; + $this->expire = $expire; + $this->path = empty($path) ? '/' : $path; + $this->secure = (Boolean) $secure; + $this->httpOnly = (Boolean) $httpOnly; + } + + public function __toString() + { + $str = urlencode($this->getName()).'='; + + if ('' === (string) $this->getValue()) { + $str .= 'deleted; expires='.gmdate("D, d-M-Y H:i:s T", time() - 31536001); + } else { + $str .= urlencode($this->getValue()); + + if ($this->getExpiresTime() !== 0) { + $str .= '; expires='.gmdate("D, d-M-Y H:i:s T", $this->getExpiresTime()); + } + } + + if ('/' !== $this->path) { + $str .= '; path='.$this->path; + } + + if (null !== $this->getDomain()) { + $str .= '; domain='.$this->getDomain(); + } + + if (true === $this->isSecure()) { + $str .= '; secure'; + } + + if (true === $this->isHttpOnly()) { + $str .= '; httponly'; + } + + return $str; + } + + /** + * Gets the name of the cookie. + * + * @return string + * + * @api + */ + public function getName() + { + return $this->name; + } + + /** + * Gets the value of the cookie. + * + * @return string + * + * @api + */ + public function getValue() + { + return $this->value; + } + + /** + * Gets the domain that the cookie is available to. + * + * @return string + * + * @api + */ + public function getDomain() + { + return $this->domain; + } + + /** + * Gets the time the cookie expires. + * + * @return integer + * + * @api + */ + public function getExpiresTime() + { + return $this->expire; + } + + /** + * Gets the path on the server in which the cookie will be available on. + * + * @return string + * + * @api + */ + public function getPath() + { + return $this->path; + } + + /** + * Checks whether the cookie should only be transmitted over a secure HTTPS connection from the client. + * + * @return Boolean + * + * @api + */ + public function isSecure() + { + return $this->secure; + } + + /** + * Checks whether the cookie will be made accessible only through the HTTP protocol. + * + * @return Boolean + * + * @api + */ + public function isHttpOnly() + { + return $this->httpOnly; + } + + /** + * Whether this cookie is about to be cleared + * + * @return Boolean + * + * @api + */ + public function isCleared() + { + return $this->expire < time(); + } +} diff --git a/app/laravel/vendor/Symfony/Component/HttpFoundation/File/Exception/AccessDeniedException.php b/app/laravel/vendor/Symfony/Component/HttpFoundation/File/Exception/AccessDeniedException.php new file mode 100755 index 000000000..9c7fe6812 --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/HttpFoundation/File/Exception/AccessDeniedException.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\File\Exception; + +/** + * Thrown when the access on a file was denied. + * + * @author Bernhard Schussek + */ +class AccessDeniedException extends FileException +{ + /** + * Constructor. + * + * @param string $path The path to the accessed file + */ + public function __construct($path) + { + parent::__construct(sprintf('The file %s could not be accessed', $path)); + } +} diff --git a/app/laravel/vendor/Symfony/Component/HttpFoundation/File/Exception/FileException.php b/app/laravel/vendor/Symfony/Component/HttpFoundation/File/Exception/FileException.php new file mode 100755 index 000000000..43c6cc899 --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/HttpFoundation/File/Exception/FileException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\File\Exception; + +/** + * Thrown when an error occurred in the component File + * + * @author Bernhard Schussek + */ +class FileException extends \RuntimeException +{ +} diff --git a/app/laravel/vendor/Symfony/Component/HttpFoundation/File/Exception/FileNotFoundException.php b/app/laravel/vendor/Symfony/Component/HttpFoundation/File/Exception/FileNotFoundException.php new file mode 100755 index 000000000..5b1aef8e2 --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/HttpFoundation/File/Exception/FileNotFoundException.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\File\Exception; + +/** + * Thrown when a file was not found + * + * @author Bernhard Schussek + */ +class FileNotFoundException extends FileException +{ + /** + * Constructor. + * + * @param string $path The path to the file that was not found + */ + public function __construct($path) + { + parent::__construct(sprintf('The file "%s" does not exist', $path)); + } +} diff --git a/app/laravel/vendor/Symfony/Component/HttpFoundation/File/Exception/UnexpectedTypeException.php b/app/laravel/vendor/Symfony/Component/HttpFoundation/File/Exception/UnexpectedTypeException.php new file mode 100755 index 000000000..0444b8778 --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/HttpFoundation/File/Exception/UnexpectedTypeException.php @@ -0,0 +1,20 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\File\Exception; + +class UnexpectedTypeException extends FileException +{ + public function __construct($value, $expectedType) + { + parent::__construct(sprintf('Expected argument of type %s, %s given', $expectedType, is_object($value) ? get_class($value) : gettype($value))); + } +} diff --git a/app/laravel/vendor/Symfony/Component/HttpFoundation/File/Exception/UploadException.php b/app/laravel/vendor/Symfony/Component/HttpFoundation/File/Exception/UploadException.php new file mode 100755 index 000000000..694e864d1 --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/HttpFoundation/File/Exception/UploadException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\File\Exception; + +/** + * Thrown when an error occurred during file upload + * + * @author Bernhard Schussek + */ +class UploadException extends FileException +{ +} diff --git a/app/laravel/vendor/Symfony/Component/HttpFoundation/File/File.php b/app/laravel/vendor/Symfony/Component/HttpFoundation/File/File.php new file mode 100755 index 000000000..3134ccda5 --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/HttpFoundation/File/File.php @@ -0,0 +1,129 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\File; + +use Symfony\Component\HttpFoundation\File\Exception\FileException; +use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException; +use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesser; +use Symfony\Component\HttpFoundation\File\MimeType\ExtensionGuesser; + +/** + * A file in the file system. + * + * @author Bernhard Schussek + * + * @api + */ +class File extends \SplFileInfo +{ + /** + * Constructs a new file from the given path. + * + * @param string $path The path to the file + * @param Boolean $checkPath Whether to check the path or not + * + * @throws FileNotFoundException If the given path is not a file + * + * @api + */ + public function __construct($path, $checkPath = true) + { + if ($checkPath && !is_file($path)) { + throw new FileNotFoundException($path); + } + + parent::__construct($path); + } + + /** + * Returns the extension based on the mime type. + * + * If the mime type is unknown, returns null. + * + * @return string|null The guessed extension or null if it cannot be guessed + * + * @api + */ + public function guessExtension() + { + $type = $this->getMimeType(); + $guesser = ExtensionGuesser::getInstance(); + + return $guesser->guess($type); + } + + /** + * Returns the mime type of the file. + * + * The mime type is guessed using the functions finfo(), mime_content_type() + * and the system binary "file" (in this order), depending on which of those + * is available on the current operating system. + * + * @return string|null The guessed mime type (i.e. "application/pdf") + * + * @api + */ + public function getMimeType() + { + $guesser = MimeTypeGuesser::getInstance(); + + return $guesser->guess($this->getPathname()); + } + + /** + * Returns the extension of the file. + * + * \SplFileInfo::getExtension() is not available before PHP 5.3.6 + * + * @return string The extension + * + * @api + */ + public function getExtension() + { + return pathinfo($this->getBasename(), PATHINFO_EXTENSION); + } + + /** + * Moves the file to a new location. + * + * @param string $directory The destination folder + * @param string $name The new file name + * + * @return File A File object representing the new file + * + * @throws FileException if the target file could not be created + * + * @api + */ + public function move($directory, $name = null) + { + if (!is_dir($directory)) { + if (false === @mkdir($directory, 0777, true)) { + throw new FileException(sprintf('Unable to create the "%s" directory', $directory)); + } + } elseif (!is_writable($directory)) { + throw new FileException(sprintf('Unable to write in the "%s" directory', $directory)); + } + + $target = $directory.DIRECTORY_SEPARATOR.(null === $name ? $this->getBasename() : basename($name)); + + if (!@rename($this->getPathname(), $target)) { + $error = error_get_last(); + throw new FileException(sprintf('Could not move the file "%s" to "%s" (%s)', $this->getPathname(), $target, strip_tags($error['message']))); + } + + chmod($target, 0666); + + return new File($target); + } +} diff --git a/app/laravel/vendor/Symfony/Component/HttpFoundation/File/MimeType/ExtensionGuesser.php b/app/laravel/vendor/Symfony/Component/HttpFoundation/File/MimeType/ExtensionGuesser.php new file mode 100755 index 000000000..b73cd9991 --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/HttpFoundation/File/MimeType/ExtensionGuesser.php @@ -0,0 +1,100 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\File\MimeType; + +/** + * A singleton mime type to file extension guesser. + * + * A default guesser is provided. + * You can register custom guessers by calling the register() + * method on the singleton instance. + * + * + * $guesser = ExtensionGuesser::getInstance(); + * $guesser->register(new MyCustomExtensionGuesser()); + * + * + * The last registered guesser is preferred over previously registered ones. + * + */ +class ExtensionGuesser implements ExtensionGuesserInterface +{ + /** + * The singleton instance + * @var ExtensionGuesser + */ + static private $instance = null; + + /** + * All registered ExtensionGuesserInterface instances + * @var array + */ + protected $guessers = array(); + + /** + * Returns the singleton instance + * + * @return ExtensionGuesser + */ + static public function getInstance() + { + if (null === self::$instance) { + self::$instance = new self(); + } + + return self::$instance; + } + + /** + * Registers all natively provided extension guessers + */ + private function __construct() + { + $this->register(new MimeTypeExtensionGuesser()); + } + + /** + * Registers a new extension guesser + * + * When guessing, this guesser is preferred over previously registered ones. + * + * @param ExtensionGuesserInterface $guesser + */ + public function register(ExtensionGuesserInterface $guesser) + { + array_unshift($this->guessers, $guesser); + } + + /** + * Tries to guess the extension + * + * The mime type is passed to each registered mime type guesser in reverse order + * of their registration (last registered is queried first). Once a guesser + * returns a value that is not NULL, this method terminates and returns the + * value. + * + * @param string $mimeType The mime type + * @return string The guessed extension or NULL, if none could be guessed + */ + public function guess($mimeType) + { + foreach ($this->guessers as $guesser) { + $extension = $guesser->guess($mimeType); + + if (null !== $extension) { + break; + } + } + + return $extension; + } +} diff --git a/app/laravel/vendor/Symfony/Component/HttpFoundation/File/MimeType/ExtensionGuesserInterface.php b/app/laravel/vendor/Symfony/Component/HttpFoundation/File/MimeType/ExtensionGuesserInterface.php new file mode 100755 index 000000000..5b14ef9ed --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/HttpFoundation/File/MimeType/ExtensionGuesserInterface.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\File\MimeType; + +/** + * Guesses the file extension corresponding to a given mime type + */ +interface ExtensionGuesserInterface +{ + /** + * Makes a best guess for a file extension, given a mime type + * + * @param string $mimeType The mime type + * @return string The guessed extension or NULL, if none could be guessed + */ + function guess($mimeType); +} diff --git a/app/laravel/vendor/Symfony/Component/HttpFoundation/File/MimeType/FileBinaryMimeTypeGuesser.php b/app/laravel/vendor/Symfony/Component/HttpFoundation/File/MimeType/FileBinaryMimeTypeGuesser.php new file mode 100755 index 000000000..12b84cdc9 --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/HttpFoundation/File/MimeType/FileBinaryMimeTypeGuesser.php @@ -0,0 +1,89 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\File\MimeType; + +use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException; +use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException; + +/** + * Guesses the mime type with the binary "file" (only available on *nix) + * + * @author Bernhard Schussek + */ +class FileBinaryMimeTypeGuesser implements MimeTypeGuesserInterface +{ + private $cmd; + + /** + * Constructor. + * + * The $cmd pattern must contain a "%s" string that will be replaced + * with the file name to guess. + * + * The command output must start with the mime type of the file. + * + * @param string $cmd The command to run to get the mime type of a file + */ + public function __construct($cmd = 'file -b --mime %s 2>/dev/null') + { + $this->cmd = $cmd; + } + + /** + * Returns whether this guesser is supported on the current OS + * + * @return Boolean + */ + static public function isSupported() + { + return !defined('PHP_WINDOWS_VERSION_BUILD'); + } + + /** + * Guesses the mime type of the file with the given path + * + * @see MimeTypeGuesserInterface::guess() + */ + public function guess($path) + { + if (!is_file($path)) { + throw new FileNotFoundException($path); + } + + if (!is_readable($path)) { + throw new AccessDeniedException($path); + } + + if (!self::isSupported()) { + return null; + } + + ob_start(); + + // need to use --mime instead of -i. see #6641 + passthru(sprintf($this->cmd, escapeshellarg($path)), $return); + if ($return > 0) { + ob_end_clean(); + + return null; + } + + $type = trim(ob_get_clean()); + + if (!preg_match('#^([a-z0-9\-]+/[a-z0-9\-]+)#i', $type, $match)) { + // it's not a type, but an error message + return null; + } + + return $match[1]; + } +} diff --git a/app/laravel/vendor/Symfony/Component/HttpFoundation/File/MimeType/FileinfoMimeTypeGuesser.php b/app/laravel/vendor/Symfony/Component/HttpFoundation/File/MimeType/FileinfoMimeTypeGuesser.php new file mode 100755 index 000000000..45d5a086e --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/HttpFoundation/File/MimeType/FileinfoMimeTypeGuesser.php @@ -0,0 +1,59 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\File\MimeType; + +use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException; +use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException; + +/** + * Guesses the mime type using the PECL extension FileInfo + * + * @author Bernhard Schussek + */ +class FileinfoMimeTypeGuesser implements MimeTypeGuesserInterface +{ + /** + * Returns whether this guesser is supported on the current OS/PHP setup + * + * @return Boolean + */ + static public function isSupported() + { + return function_exists('finfo_open'); + } + + /** + * Guesses the mime type of the file with the given path + * + * @see MimeTypeGuesserInterface::guess() + */ + public function guess($path) + { + if (!is_file($path)) { + throw new FileNotFoundException($path); + } + + if (!is_readable($path)) { + throw new AccessDeniedException($path); + } + + if (!self::isSupported()) { + return null; + } + + if (!$finfo = new \finfo(FILEINFO_MIME_TYPE)) { + return null; + } + + return $finfo->file($path); + } +} diff --git a/app/laravel/vendor/Symfony/Component/HttpFoundation/File/MimeType/MimeTypeExtensionGuesser.php b/app/laravel/vendor/Symfony/Component/HttpFoundation/File/MimeType/MimeTypeExtensionGuesser.php new file mode 100755 index 000000000..805f223c4 --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/HttpFoundation/File/MimeType/MimeTypeExtensionGuesser.php @@ -0,0 +1,743 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\File\Mimetype; + +/** + * Provides a best-guess mapping of mime type to file extension. + */ +class MimeTypeExtensionGuesser implements ExtensionGuesserInterface +{ + /** + * A map of mime types and their default extensions. + * + * This list has been placed under the public domain by the Apache HTTPD project. + * + * @see http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types + * + * @var array + */ + protected $defaultExtensions = array( + 'application/andrew-inset' => 'ez', + 'application/applixware' => 'aw', + 'application/atom+xml' => 'atom', + 'application/atomcat+xml' => 'atomcat', + 'application/atomsvc+xml' => 'atomsvc', + 'application/ccxml+xml' => 'ccxml', + 'application/cdmi-capability' => 'cdmia', + 'application/cdmi-container' => 'cdmic', + 'application/cdmi-domain' => 'cdmid', + 'application/cdmi-object' => 'cdmio', + 'application/cdmi-queue' => 'cdmiq', + 'application/cu-seeme' => 'cu', + 'application/davmount+xml' => 'davmount', + 'application/dssc+der' => 'dssc', + 'application/dssc+xml' => 'xdssc', + 'application/ecmascript' => 'ecma', + 'application/emma+xml' => 'emma', + 'application/epub+zip' => 'epub', + 'application/exi' => 'exi', + 'application/font-tdpfr' => 'pfr', + 'application/hyperstudio' => 'stk', + 'application/inkml+xml' => 'ink', + 'application/ipfix' => 'ipfix', + 'application/java-archive' => 'jar', + 'application/java-serialized-object' => 'ser', + 'application/java-vm' => 'class', + 'application/javascript' => 'js', + 'application/json' => 'json', + 'application/lost+xml' => 'lostxml', + 'application/mac-binhex40' => 'hqx', + 'application/mac-compactpro' => 'cpt', + 'application/mads+xml' => 'mads', + 'application/marc' => 'mrc', + 'application/marcxml+xml' => 'mrcx', + 'application/mathematica' => 'ma', + 'application/mathml+xml' => 'mathml', + 'application/mbox' => 'mbox', + 'application/mediaservercontrol+xml' => 'mscml', + 'application/metalink4+xml' => 'meta4', + 'application/mets+xml' => 'mets', + 'application/mods+xml' => 'mods', + 'application/mp21' => 'm21', + 'application/mp4' => 'mp4s', + 'application/msword' => 'doc', + 'application/mxf' => 'mxf', + 'application/octet-stream' => 'bin', + 'application/oda' => 'oda', + 'application/oebps-package+xml' => 'opf', + 'application/ogg' => 'ogx', + 'application/onenote' => 'onetoc', + 'application/oxps' => 'oxps', + 'application/patch-ops-error+xml' => 'xer', + 'application/pdf' => 'pdf', + 'application/pgp-encrypted' => 'pgp', + 'application/pgp-signature' => 'asc', + 'application/pics-rules' => 'prf', + 'application/pkcs10' => 'p10', + 'application/pkcs7-mime' => 'p7m', + 'application/pkcs7-signature' => 'p7s', + 'application/pkcs8' => 'p8', + 'application/pkix-attr-cert' => 'ac', + 'application/pkix-cert' => 'cer', + 'application/pkix-crl' => 'crl', + 'application/pkix-pkipath' => 'pkipath', + 'application/pkixcmp' => 'pki', + 'application/pls+xml' => 'pls', + 'application/postscript' => 'ai', + 'application/prs.cww' => 'cww', + 'application/pskc+xml' => 'pskcxml', + 'application/rdf+xml' => 'rdf', + 'application/reginfo+xml' => 'rif', + 'application/relax-ng-compact-syntax' => 'rnc', + 'application/resource-lists+xml' => 'rl', + 'application/resource-lists-diff+xml' => 'rld', + 'application/rls-services+xml' => 'rs', + 'application/rpki-ghostbusters' => 'gbr', + 'application/rpki-manifest' => 'mft', + 'application/rpki-roa' => 'roa', + 'application/rsd+xml' => 'rsd', + 'application/rss+xml' => 'rss', + 'application/rtf' => 'rtf', + 'application/sbml+xml' => 'sbml', + 'application/scvp-cv-request' => 'scq', + 'application/scvp-cv-response' => 'scs', + 'application/scvp-vp-request' => 'spq', + 'application/scvp-vp-response' => 'spp', + 'application/sdp' => 'sdp', + 'application/set-payment-initiation' => 'setpay', + 'application/set-registration-initiation' => 'setreg', + 'application/shf+xml' => 'shf', + 'application/smil+xml' => 'smi', + 'application/sparql-query' => 'rq', + 'application/sparql-results+xml' => 'srx', + 'application/srgs' => 'gram', + 'application/srgs+xml' => 'grxml', + 'application/sru+xml' => 'sru', + 'application/ssml+xml' => 'ssml', + 'application/tei+xml' => 'tei', + 'application/thraud+xml' => 'tfi', + 'application/timestamped-data' => 'tsd', + 'application/vnd.3gpp.pic-bw-large' => 'plb', + 'application/vnd.3gpp.pic-bw-small' => 'psb', + 'application/vnd.3gpp.pic-bw-var' => 'pvb', + 'application/vnd.3gpp2.tcap' => 'tcap', + 'application/vnd.3m.post-it-notes' => 'pwn', + 'application/vnd.accpac.simply.aso' => 'aso', + 'application/vnd.accpac.simply.imp' => 'imp', + 'application/vnd.acucobol' => 'acu', + 'application/vnd.acucorp' => 'atc', + 'application/vnd.adobe.air-application-installer-package+zip' => 'air', + 'application/vnd.adobe.fxp' => 'fxp', + 'application/vnd.adobe.xdp+xml' => 'xdp', + 'application/vnd.adobe.xfdf' => 'xfdf', + 'application/vnd.ahead.space' => 'ahead', + 'application/vnd.airzip.filesecure.azf' => 'azf', + 'application/vnd.airzip.filesecure.azs' => 'azs', + 'application/vnd.amazon.ebook' => 'azw', + 'application/vnd.americandynamics.acc' => 'acc', + 'application/vnd.amiga.ami' => 'ami', + 'application/vnd.android.package-archive' => 'apk', + 'application/vnd.anser-web-certificate-issue-initiation' => 'cii', + 'application/vnd.anser-web-funds-transfer-initiation' => 'fti', + 'application/vnd.antix.game-component' => 'atx', + 'application/vnd.apple.installer+xml' => 'mpkg', + 'application/vnd.apple.mpegurl' => 'm3u8', + 'application/vnd.aristanetworks.swi' => 'swi', + 'application/vnd.astraea-software.iota' => 'iota', + 'application/vnd.audiograph' => 'aep', + 'application/vnd.blueice.multipass' => 'mpm', + 'application/vnd.bmi' => 'bmi', + 'application/vnd.businessobjects' => 'rep', + 'application/vnd.chemdraw+xml' => 'cdxml', + 'application/vnd.chipnuts.karaoke-mmd' => 'mmd', + 'application/vnd.cinderella' => 'cdy', + 'application/vnd.claymore' => 'cla', + 'application/vnd.cloanto.rp9' => 'rp9', + 'application/vnd.clonk.c4group' => 'c4g', + 'application/vnd.cluetrust.cartomobile-config' => 'c11amc', + 'application/vnd.cluetrust.cartomobile-config-pkg' => 'c11amz', + 'application/vnd.commonspace' => 'csp', + 'application/vnd.contact.cmsg' => 'cdbcmsg', + 'application/vnd.cosmocaller' => 'cmc', + 'application/vnd.crick.clicker' => 'clkx', + 'application/vnd.crick.clicker.keyboard' => 'clkk', + 'application/vnd.crick.clicker.palette' => 'clkp', + 'application/vnd.crick.clicker.template' => 'clkt', + 'application/vnd.crick.clicker.wordbank' => 'clkw', + 'application/vnd.criticaltools.wbs+xml' => 'wbs', + 'application/vnd.ctc-posml' => 'pml', + 'application/vnd.cups-ppd' => 'ppd', + 'application/vnd.curl.car' => 'car', + 'application/vnd.curl.pcurl' => 'pcurl', + 'application/vnd.data-vision.rdz' => 'rdz', + 'application/vnd.dece.data' => 'uvf', + 'application/vnd.dece.ttml+xml' => 'uvt', + 'application/vnd.dece.unspecified' => 'uvx', + 'application/vnd.dece.zip' => 'uvz', + 'application/vnd.denovo.fcselayout-link' => 'fe_launch', + 'application/vnd.dna' => 'dna', + 'application/vnd.dolby.mlp' => 'mlp', + 'application/vnd.dpgraph' => 'dpg', + 'application/vnd.dreamfactory' => 'dfac', + 'application/vnd.dvb.ait' => 'ait', + 'application/vnd.dvb.service' => 'svc', + 'application/vnd.dynageo' => 'geo', + 'application/vnd.ecowin.chart' => 'mag', + 'application/vnd.enliven' => 'nml', + 'application/vnd.epson.esf' => 'esf', + 'application/vnd.epson.msf' => 'msf', + 'application/vnd.epson.quickanime' => 'qam', + 'application/vnd.epson.salt' => 'slt', + 'application/vnd.epson.ssf' => 'ssf', + 'application/vnd.eszigno3+xml' => 'es3', + 'application/vnd.ezpix-album' => 'ez2', + 'application/vnd.ezpix-package' => 'ez3', + 'application/vnd.fdf' => 'fdf', + 'application/vnd.fdsn.mseed' => 'mseed', + 'application/vnd.fdsn.seed' => 'seed', + 'application/vnd.flographit' => 'gph', + 'application/vnd.fluxtime.clip' => 'ftc', + 'application/vnd.framemaker' => 'fm', + 'application/vnd.frogans.fnc' => 'fnc', + 'application/vnd.frogans.ltf' => 'ltf', + 'application/vnd.fsc.weblaunch' => 'fsc', + 'application/vnd.fujitsu.oasys' => 'oas', + 'application/vnd.fujitsu.oasys2' => 'oa2', + 'application/vnd.fujitsu.oasys3' => 'oa3', + 'application/vnd.fujitsu.oasysgp' => 'fg5', + 'application/vnd.fujitsu.oasysprs' => 'bh2', + 'application/vnd.fujixerox.ddd' => 'ddd', + 'application/vnd.fujixerox.docuworks' => 'xdw', + 'application/vnd.fujixerox.docuworks.binder' => 'xbd', + 'application/vnd.fuzzysheet' => 'fzs', + 'application/vnd.genomatix.tuxedo' => 'txd', + 'application/vnd.geogebra.file' => 'ggb', + 'application/vnd.geogebra.tool' => 'ggt', + 'application/vnd.geometry-explorer' => 'gex', + 'application/vnd.geonext' => 'gxt', + 'application/vnd.geoplan' => 'g2w', + 'application/vnd.geospace' => 'g3w', + 'application/vnd.gmx' => 'gmx', + 'application/vnd.google-earth.kml+xml' => 'kml', + 'application/vnd.google-earth.kmz' => 'kmz', + 'application/vnd.grafeq' => 'gqf', + 'application/vnd.groove-account' => 'gac', + 'application/vnd.groove-help' => 'ghf', + 'application/vnd.groove-identity-message' => 'gim', + 'application/vnd.groove-injector' => 'grv', + 'application/vnd.groove-tool-message' => 'gtm', + 'application/vnd.groove-tool-template' => 'tpl', + 'application/vnd.groove-vcard' => 'vcg', + 'application/vnd.hal+xml' => 'hal', + 'application/vnd.handheld-entertainment+xml' => 'zmm', + 'application/vnd.hbci' => 'hbci', + 'application/vnd.hhe.lesson-player' => 'les', + 'application/vnd.hp-hpgl' => 'hpgl', + 'application/vnd.hp-hpid' => 'hpid', + 'application/vnd.hp-hps' => 'hps', + 'application/vnd.hp-jlyt' => 'jlt', + 'application/vnd.hp-pcl' => 'pcl', + 'application/vnd.hp-pclxl' => 'pclxl', + 'application/vnd.hydrostatix.sof-data' => 'sfd-hdstx', + 'application/vnd.hzn-3d-crossword' => 'x3d', + 'application/vnd.ibm.minipay' => 'mpy', + 'application/vnd.ibm.modcap' => 'afp', + 'application/vnd.ibm.rights-management' => 'irm', + 'application/vnd.ibm.secure-container' => 'sc', + 'application/vnd.iccprofile' => 'icc', + 'application/vnd.igloader' => 'igl', + 'application/vnd.immervision-ivp' => 'ivp', + 'application/vnd.immervision-ivu' => 'ivu', + 'application/vnd.insors.igm' => 'igm', + 'application/vnd.intercon.formnet' => 'xpw', + 'application/vnd.intergeo' => 'i2g', + 'application/vnd.intu.qbo' => 'qbo', + 'application/vnd.intu.qfx' => 'qfx', + 'application/vnd.ipunplugged.rcprofile' => 'rcprofile', + 'application/vnd.irepository.package+xml' => 'irp', + 'application/vnd.is-xpr' => 'xpr', + 'application/vnd.isac.fcs' => 'fcs', + 'application/vnd.jam' => 'jam', + 'application/vnd.jcp.javame.midlet-rms' => 'rms', + 'application/vnd.jisp' => 'jisp', + 'application/vnd.joost.joda-archive' => 'joda', + 'application/vnd.kahootz' => 'ktz', + 'application/vnd.kde.karbon' => 'karbon', + 'application/vnd.kde.kchart' => 'chrt', + 'application/vnd.kde.kformula' => 'kfo', + 'application/vnd.kde.kivio' => 'flw', + 'application/vnd.kde.kontour' => 'kon', + 'application/vnd.kde.kpresenter' => 'kpr', + 'application/vnd.kde.kspread' => 'ksp', + 'application/vnd.kde.kword' => 'kwd', + 'application/vnd.kenameaapp' => 'htke', + 'application/vnd.kidspiration' => 'kia', + 'application/vnd.kinar' => 'kne', + 'application/vnd.koan' => 'skp', + 'application/vnd.kodak-descriptor' => 'sse', + 'application/vnd.las.las+xml' => 'lasxml', + 'application/vnd.llamagraphics.life-balance.desktop' => 'lbd', + 'application/vnd.llamagraphics.life-balance.exchange+xml' => 'lbe', + 'application/vnd.lotus-1-2-3' => '123', + 'application/vnd.lotus-approach' => 'apr', + 'application/vnd.lotus-freelance' => 'pre', + 'application/vnd.lotus-notes' => 'nsf', + 'application/vnd.lotus-organizer' => 'org', + 'application/vnd.lotus-screencam' => 'scm', + 'application/vnd.lotus-wordpro' => 'lwp', + 'application/vnd.macports.portpkg' => 'portpkg', + 'application/vnd.mcd' => 'mcd', + 'application/vnd.medcalcdata' => 'mc1', + 'application/vnd.mediastation.cdkey' => 'cdkey', + 'application/vnd.mfer' => 'mwf', + 'application/vnd.mfmp' => 'mfm', + 'application/vnd.micrografx.flo' => 'flo', + 'application/vnd.micrografx.igx' => 'igx', + 'application/vnd.mif' => 'mif', + 'application/vnd.mobius.daf' => 'daf', + 'application/vnd.mobius.dis' => 'dis', + 'application/vnd.mobius.mbk' => 'mbk', + 'application/vnd.mobius.mqy' => 'mqy', + 'application/vnd.mobius.msl' => 'msl', + 'application/vnd.mobius.plc' => 'plc', + 'application/vnd.mobius.txf' => 'txf', + 'application/vnd.mophun.application' => 'mpn', + 'application/vnd.mophun.certificate' => 'mpc', + 'application/vnd.mozilla.xul+xml' => 'xul', + 'application/vnd.ms-artgalry' => 'cil', + 'application/vnd.ms-cab-compressed' => 'cab', + 'application/vnd.ms-excel' => 'xls', + 'application/vnd.ms-excel.addin.macroenabled.12' => 'xlam', + 'application/vnd.ms-excel.sheet.binary.macroenabled.12' => 'xlsb', + 'application/vnd.ms-excel.sheet.macroenabled.12' => 'xlsm', + 'application/vnd.ms-excel.template.macroenabled.12' => 'xltm', + 'application/vnd.ms-fontobject' => 'eot', + 'application/vnd.ms-htmlhelp' => 'chm', + 'application/vnd.ms-ims' => 'ims', + 'application/vnd.ms-lrm' => 'lrm', + 'application/vnd.ms-officetheme' => 'thmx', + 'application/vnd.ms-pki.seccat' => 'cat', + 'application/vnd.ms-pki.stl' => 'stl', + 'application/vnd.ms-powerpoint' => 'ppt', + 'application/vnd.ms-powerpoint.addin.macroenabled.12' => 'ppam', + 'application/vnd.ms-powerpoint.presentation.macroenabled.12' => 'pptm', + 'application/vnd.ms-powerpoint.slide.macroenabled.12' => 'sldm', + 'application/vnd.ms-powerpoint.slideshow.macroenabled.12' => 'ppsm', + 'application/vnd.ms-powerpoint.template.macroenabled.12' => 'potm', + 'application/vnd.ms-project' => 'mpp', + 'application/vnd.ms-word.document.macroenabled.12' => 'docm', + 'application/vnd.ms-word.template.macroenabled.12' => 'dotm', + 'application/vnd.ms-works' => 'wps', + 'application/vnd.ms-wpl' => 'wpl', + 'application/vnd.ms-xpsdocument' => 'xps', + 'application/vnd.mseq' => 'mseq', + 'application/vnd.musician' => 'mus', + 'application/vnd.muvee.style' => 'msty', + 'application/vnd.mynfc' => 'taglet', + 'application/vnd.neurolanguage.nlu' => 'nlu', + 'application/vnd.noblenet-directory' => 'nnd', + 'application/vnd.noblenet-sealer' => 'nns', + 'application/vnd.noblenet-web' => 'nnw', + 'application/vnd.nokia.n-gage.data' => 'ngdat', + 'application/vnd.nokia.n-gage.symbian.install' => 'n-gage', + 'application/vnd.nokia.radio-preset' => 'rpst', + 'application/vnd.nokia.radio-presets' => 'rpss', + 'application/vnd.novadigm.edm' => 'edm', + 'application/vnd.novadigm.edx' => 'edx', + 'application/vnd.novadigm.ext' => 'ext', + 'application/vnd.oasis.opendocument.chart' => 'odc', + 'application/vnd.oasis.opendocument.chart-template' => 'otc', + 'application/vnd.oasis.opendocument.database' => 'odb', + 'application/vnd.oasis.opendocument.formula' => 'odf', + 'application/vnd.oasis.opendocument.formula-template' => 'odft', + 'application/vnd.oasis.opendocument.graphics' => 'odg', + 'application/vnd.oasis.opendocument.graphics-template' => 'otg', + 'application/vnd.oasis.opendocument.image' => 'odi', + 'application/vnd.oasis.opendocument.image-template' => 'oti', + 'application/vnd.oasis.opendocument.presentation' => 'odp', + 'application/vnd.oasis.opendocument.presentation-template' => 'otp', + 'application/vnd.oasis.opendocument.spreadsheet' => 'ods', + 'application/vnd.oasis.opendocument.spreadsheet-template' => 'ots', + 'application/vnd.oasis.opendocument.text' => 'odt', + 'application/vnd.oasis.opendocument.text-master' => 'odm', + 'application/vnd.oasis.opendocument.text-template' => 'ott', + 'application/vnd.oasis.opendocument.text-web' => 'oth', + 'application/vnd.olpc-sugar' => 'xo', + 'application/vnd.oma.dd2+xml' => 'dd2', + 'application/vnd.openofficeorg.extension' => 'oxt', + 'application/vnd.openxmlformats-officedocument.presentationml.presentation' => 'pptx', + 'application/vnd.openxmlformats-officedocument.presentationml.slide' => 'sldx', + 'application/vnd.openxmlformats-officedocument.presentationml.slideshow' => 'ppsx', + 'application/vnd.openxmlformats-officedocument.presentationml.template' => 'potx', + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => 'xlsx', + 'application/vnd.openxmlformats-officedocument.spreadsheetml.template' => 'xltx', + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => 'docx', + 'application/vnd.openxmlformats-officedocument.wordprocessingml.template' => 'dotx', + 'application/vnd.osgeo.mapguide.package' => 'mgp', + 'application/vnd.osgi.dp' => 'dp', + 'application/vnd.palm' => 'pdb', + 'application/vnd.pawaafile' => 'paw', + 'application/vnd.pg.format' => 'str', + 'application/vnd.pg.osasli' => 'ei6', + 'application/vnd.picsel' => 'efif', + 'application/vnd.pmi.widget' => 'wg', + 'application/vnd.pocketlearn' => 'plf', + 'application/vnd.powerbuilder6' => 'pbd', + 'application/vnd.previewsystems.box' => 'box', + 'application/vnd.proteus.magazine' => 'mgz', + 'application/vnd.publishare-delta-tree' => 'qps', + 'application/vnd.pvi.ptid1' => 'ptid', + 'application/vnd.quark.quarkxpress' => 'qxd', + 'application/vnd.realvnc.bed' => 'bed', + 'application/vnd.recordare.musicxml' => 'mxl', + 'application/vnd.recordare.musicxml+xml' => 'musicxml', + 'application/vnd.rig.cryptonote' => 'cryptonote', + 'application/vnd.rim.cod' => 'cod', + 'application/vnd.rn-realmedia' => 'rm', + 'application/vnd.route66.link66+xml' => 'link66', + 'application/vnd.sailingtracker.track' => 'st', + 'application/vnd.seemail' => 'see', + 'application/vnd.sema' => 'sema', + 'application/vnd.semd' => 'semd', + 'application/vnd.semf' => 'semf', + 'application/vnd.shana.informed.formdata' => 'ifm', + 'application/vnd.shana.informed.formtemplate' => 'itp', + 'application/vnd.shana.informed.interchange' => 'iif', + 'application/vnd.shana.informed.package' => 'ipk', + 'application/vnd.simtech-mindmapper' => 'twd', + 'application/vnd.smaf' => 'mmf', + 'application/vnd.smart.teacher' => 'teacher', + 'application/vnd.solent.sdkm+xml' => 'sdkm', + 'application/vnd.spotfire.dxp' => 'dxp', + 'application/vnd.spotfire.sfs' => 'sfs', + 'application/vnd.stardivision.calc' => 'sdc', + 'application/vnd.stardivision.draw' => 'sda', + 'application/vnd.stardivision.impress' => 'sdd', + 'application/vnd.stardivision.math' => 'smf', + 'application/vnd.stardivision.writer' => 'sdw', + 'application/vnd.stardivision.writer-global' => 'sgl', + 'application/vnd.stepmania.package' => 'smzip', + 'application/vnd.stepmania.stepchart' => 'sm', + 'application/vnd.sun.xml.calc' => 'sxc', + 'application/vnd.sun.xml.calc.template' => 'stc', + 'application/vnd.sun.xml.draw' => 'sxd', + 'application/vnd.sun.xml.draw.template' => 'std', + 'application/vnd.sun.xml.impress' => 'sxi', + 'application/vnd.sun.xml.impress.template' => 'sti', + 'application/vnd.sun.xml.math' => 'sxm', + 'application/vnd.sun.xml.writer' => 'sxw', + 'application/vnd.sun.xml.writer.global' => 'sxg', + 'application/vnd.sun.xml.writer.template' => 'stw', + 'application/vnd.sus-calendar' => 'sus', + 'application/vnd.svd' => 'svd', + 'application/vnd.symbian.install' => 'sis', + 'application/vnd.syncml+xml' => 'xsm', + 'application/vnd.syncml.dm+wbxml' => 'bdm', + 'application/vnd.syncml.dm+xml' => 'xdm', + 'application/vnd.tao.intent-module-archive' => 'tao', + 'application/vnd.tcpdump.pcap' => 'pcap', + 'application/vnd.tmobile-livetv' => 'tmo', + 'application/vnd.trid.tpt' => 'tpt', + 'application/vnd.triscape.mxs' => 'mxs', + 'application/vnd.trueapp' => 'tra', + 'application/vnd.ufdl' => 'ufd', + 'application/vnd.uiq.theme' => 'utz', + 'application/vnd.umajin' => 'umj', + 'application/vnd.unity' => 'unityweb', + 'application/vnd.uoml+xml' => 'uoml', + 'application/vnd.vcx' => 'vcx', + 'application/vnd.visio' => 'vsd', + 'application/vnd.visionary' => 'vis', + 'application/vnd.vsf' => 'vsf', + 'application/vnd.wap.wbxml' => 'wbxml', + 'application/vnd.wap.wmlc' => 'wmlc', + 'application/vnd.wap.wmlscriptc' => 'wmlsc', + 'application/vnd.webturbo' => 'wtb', + 'application/vnd.wolfram.player' => 'nbp', + 'application/vnd.wordperfect' => 'wpd', + 'application/vnd.wqd' => 'wqd', + 'application/vnd.wt.stf' => 'stf', + 'application/vnd.xara' => 'xar', + 'application/vnd.xfdl' => 'xfdl', + 'application/vnd.yamaha.hv-dic' => 'hvd', + 'application/vnd.yamaha.hv-script' => 'hvs', + 'application/vnd.yamaha.hv-voice' => 'hvp', + 'application/vnd.yamaha.openscoreformat' => 'osf', + 'application/vnd.yamaha.openscoreformat.osfpvg+xml' => 'osfpvg', + 'application/vnd.yamaha.smaf-audio' => 'saf', + 'application/vnd.yamaha.smaf-phrase' => 'spf', + 'application/vnd.yellowriver-custom-menu' => 'cmp', + 'application/vnd.zul' => 'zir', + 'application/vnd.zzazz.deck+xml' => 'zaz', + 'application/voicexml+xml' => 'vxml', + 'application/widget' => 'wgt', + 'application/winhlp' => 'hlp', + 'application/wsdl+xml' => 'wsdl', + 'application/wspolicy+xml' => 'wspolicy', + 'application/x-7z-compressed' => '7z', + 'application/x-abiword' => 'abw', + 'application/x-ace-compressed' => 'ace', + 'application/x-authorware-bin' => 'aab', + 'application/x-authorware-map' => 'aam', + 'application/x-authorware-seg' => 'aas', + 'application/x-bcpio' => 'bcpio', + 'application/x-bittorrent' => 'torrent', + 'application/x-bzip' => 'bz', + 'application/x-bzip2' => 'bz2', + 'application/x-cdlink' => 'vcd', + 'application/x-chat' => 'chat', + 'application/x-chess-pgn' => 'pgn', + 'application/x-cpio' => 'cpio', + 'application/x-csh' => 'csh', + 'application/x-debian-package' => 'deb', + 'application/x-director' => 'dir', + 'application/x-doom' => 'wad', + 'application/x-dtbncx+xml' => 'ncx', + 'application/x-dtbook+xml' => 'dtb', + 'application/x-dtbresource+xml' => 'res', + 'application/x-dvi' => 'dvi', + 'application/x-font-bdf' => 'bdf', + 'application/x-font-ghostscript' => 'gsf', + 'application/x-font-linux-psf' => 'psf', + 'application/x-font-otf' => 'otf', + 'application/x-font-pcf' => 'pcf', + 'application/x-font-snf' => 'snf', + 'application/x-font-ttf' => 'ttf', + 'application/x-font-type1' => 'pfa', + 'application/x-font-woff' => 'woff', + 'application/x-futuresplash' => 'spl', + 'application/x-gnumeric' => 'gnumeric', + 'application/x-gtar' => 'gtar', + 'application/x-hdf' => 'hdf', + 'application/x-java-jnlp-file' => 'jnlp', + 'application/x-latex' => 'latex', + 'application/x-mobipocket-ebook' => 'prc', + 'application/x-ms-application' => 'application', + 'application/x-ms-wmd' => 'wmd', + 'application/x-ms-wmz' => 'wmz', + 'application/x-ms-xbap' => 'xbap', + 'application/x-msaccess' => 'mdb', + 'application/x-msbinder' => 'obd', + 'application/x-mscardfile' => 'crd', + 'application/x-msclip' => 'clp', + 'application/x-msdownload' => 'exe', + 'application/x-msmediaview' => 'mvb', + 'application/x-msmetafile' => 'wmf', + 'application/x-msmoney' => 'mny', + 'application/x-mspublisher' => 'pub', + 'application/x-msschedule' => 'scd', + 'application/x-msterminal' => 'trm', + 'application/x-mswrite' => 'wri', + 'application/x-netcdf' => 'nc', + 'application/x-pkcs12' => 'p12', + 'application/x-pkcs7-certificates' => 'p7b', + 'application/x-pkcs7-certreqresp' => 'p7r', + 'application/x-rar-compressed' => 'rar', + 'application/x-sh' => 'sh', + 'application/x-shar' => 'shar', + 'application/x-shockwave-flash' => 'swf', + 'application/x-silverlight-app' => 'xap', + 'application/x-stuffit' => 'sit', + 'application/x-stuffitx' => 'sitx', + 'application/x-sv4cpio' => 'sv4cpio', + 'application/x-sv4crc' => 'sv4crc', + 'application/x-tar' => 'tar', + 'application/x-tcl' => 'tcl', + 'application/x-tex' => 'tex', + 'application/x-tex-tfm' => 'tfm', + 'application/x-texinfo' => 'texinfo', + 'application/x-ustar' => 'ustar', + 'application/x-wais-source' => 'src', + 'application/x-x509-ca-cert' => 'der', + 'application/x-xfig' => 'fig', + 'application/x-xpinstall' => 'xpi', + 'application/xcap-diff+xml' => 'xdf', + 'application/xenc+xml' => 'xenc', + 'application/xhtml+xml' => 'xhtml', + 'application/xml' => 'xml', + 'application/xml-dtd' => 'dtd', + 'application/xop+xml' => 'xop', + 'application/xslt+xml' => 'xslt', + 'application/xspf+xml' => 'xspf', + 'application/xv+xml' => 'mxml', + 'application/yang' => 'yang', + 'application/yin+xml' => 'yin', + 'application/zip' => 'zip', + 'audio/adpcm' => 'adp', + 'audio/basic' => 'au', + 'audio/midi' => 'mid', + 'audio/mp4' => 'mp4a', + 'audio/mpeg' => 'mpga', + 'audio/ogg' => 'oga', + 'audio/vnd.dece.audio' => 'uva', + 'audio/vnd.digital-winds' => 'eol', + 'audio/vnd.dra' => 'dra', + 'audio/vnd.dts' => 'dts', + 'audio/vnd.dts.hd' => 'dtshd', + 'audio/vnd.lucent.voice' => 'lvp', + 'audio/vnd.ms-playready.media.pya' => 'pya', + 'audio/vnd.nuera.ecelp4800' => 'ecelp4800', + 'audio/vnd.nuera.ecelp7470' => 'ecelp7470', + 'audio/vnd.nuera.ecelp9600' => 'ecelp9600', + 'audio/vnd.rip' => 'rip', + 'audio/webm' => 'weba', + 'audio/x-aac' => 'aac', + 'audio/x-aiff' => 'aif', + 'audio/x-mpegurl' => 'm3u', + 'audio/x-ms-wax' => 'wax', + 'audio/x-ms-wma' => 'wma', + 'audio/x-pn-realaudio' => 'ram', + 'audio/x-pn-realaudio-plugin' => 'rmp', + 'audio/x-wav' => 'wav', + 'chemical/x-cdx' => 'cdx', + 'chemical/x-cif' => 'cif', + 'chemical/x-cmdf' => 'cmdf', + 'chemical/x-cml' => 'cml', + 'chemical/x-csml' => 'csml', + 'chemical/x-xyz' => 'xyz', + 'image/bmp' => 'bmp', + 'image/cgm' => 'cgm', + 'image/g3fax' => 'g3', + 'image/gif' => 'gif', + 'image/ief' => 'ief', + 'image/jpeg' => 'jpeg', + 'image/ktx' => 'ktx', + 'image/png' => 'png', + 'image/prs.btif' => 'btif', + 'image/svg+xml' => 'svg', + 'image/tiff' => 'tiff', + 'image/vnd.adobe.photoshop' => 'psd', + 'image/vnd.dece.graphic' => 'uvi', + 'image/vnd.dvb.subtitle' => 'sub', + 'image/vnd.djvu' => 'djvu', + 'image/vnd.dwg' => 'dwg', + 'image/vnd.dxf' => 'dxf', + 'image/vnd.fastbidsheet' => 'fbs', + 'image/vnd.fpx' => 'fpx', + 'image/vnd.fst' => 'fst', + 'image/vnd.fujixerox.edmics-mmr' => 'mmr', + 'image/vnd.fujixerox.edmics-rlc' => 'rlc', + 'image/vnd.ms-modi' => 'mdi', + 'image/vnd.net-fpx' => 'npx', + 'image/vnd.wap.wbmp' => 'wbmp', + 'image/vnd.xiff' => 'xif', + 'image/webp' => 'webp', + 'image/x-cmu-raster' => 'ras', + 'image/x-cmx' => 'cmx', + 'image/x-freehand' => 'fh', + 'image/x-icon' => 'ico', + 'image/x-pcx' => 'pcx', + 'image/x-pict' => 'pic', + 'image/x-portable-anymap' => 'pnm', + 'image/x-portable-bitmap' => 'pbm', + 'image/x-portable-graymap' => 'pgm', + 'image/x-portable-pixmap' => 'ppm', + 'image/x-rgb' => 'rgb', + 'image/x-xbitmap' => 'xbm', + 'image/x-xpixmap' => 'xpm', + 'image/x-xwindowdump' => 'xwd', + 'message/rfc822' => 'eml', + 'model/iges' => 'igs', + 'model/mesh' => 'msh', + 'model/vnd.collada+xml' => 'dae', + 'model/vnd.dwf' => 'dwf', + 'model/vnd.gdl' => 'gdl', + 'model/vnd.gtw' => 'gtw', + 'model/vnd.mts' => 'mts', + 'model/vnd.vtu' => 'vtu', + 'model/vrml' => 'wrl', + 'text/calendar' => 'ics', + 'text/css' => 'css', + 'text/csv' => 'csv', + 'text/html' => 'html', + 'text/n3' => 'n3', + 'text/plain' => 'txt', + 'text/prs.lines.tag' => 'dsc', + 'text/richtext' => 'rtx', + 'text/sgml' => 'sgml', + 'text/tab-separated-values' => 'tsv', + 'text/troff' => 't', + 'text/turtle' => 'ttl', + 'text/uri-list' => 'uri', + 'text/vcard' => 'vcard', + 'text/vnd.curl' => 'curl', + 'text/vnd.curl.dcurl' => 'dcurl', + 'text/vnd.curl.scurl' => 'scurl', + 'text/vnd.curl.mcurl' => 'mcurl', + 'text/vnd.dvb.subtitle' => 'sub', + 'text/vnd.fly' => 'fly', + 'text/vnd.fmi.flexstor' => 'flx', + 'text/vnd.graphviz' => 'gv', + 'text/vnd.in3d.3dml' => '3dml', + 'text/vnd.in3d.spot' => 'spot', + 'text/vnd.sun.j2me.app-descriptor' => 'jad', + 'text/vnd.wap.wml' => 'wml', + 'text/vnd.wap.wmlscript' => 'wmls', + 'text/x-asm' => 's', + 'text/x-c' => 'c', + 'text/x-fortran' => 'f', + 'text/x-pascal' => 'p', + 'text/x-java-source' => 'java', + 'text/x-setext' => 'etx', + 'text/x-uuencode' => 'uu', + 'text/x-vcalendar' => 'vcs', + 'text/x-vcard' => 'vcf', + 'video/3gpp' => '3gp', + 'video/3gpp2' => '3g2', + 'video/h261' => 'h261', + 'video/h263' => 'h263', + 'video/h264' => 'h264', + 'video/jpeg' => 'jpgv', + 'video/jpm' => 'jpm', + 'video/mj2' => 'mj2', + 'video/mp4' => 'mp4', + 'video/mpeg' => 'mpeg', + 'video/ogg' => 'ogv', + 'video/quicktime' => 'qt', + 'video/vnd.dece.hd' => 'uvh', + 'video/vnd.dece.mobile' => 'uvm', + 'video/vnd.dece.pd' => 'uvp', + 'video/vnd.dece.sd' => 'uvs', + 'video/vnd.dece.video' => 'uvv', + 'video/vnd.dvb.file' => 'dvb', + 'video/vnd.fvt' => 'fvt', + 'video/vnd.mpegurl' => 'mxu', + 'video/vnd.ms-playready.media.pyv' => 'pyv', + 'video/vnd.uvvu.mp4' => 'uvu', + 'video/vnd.vivo' => 'viv', + 'video/webm' => 'webm', + 'video/x-f4v' => 'f4v', + 'video/x-fli' => 'fli', + 'video/x-flv' => 'flv', + 'video/x-m4v' => 'm4v', + 'video/x-ms-asf' => 'asf', + 'video/x-ms-wm' => 'wm', + 'video/x-ms-wmv' => 'wmv', + 'video/x-ms-wmx' => 'wmx', + 'video/x-ms-wvx' => 'wvx', + 'video/x-msvideo' => 'avi', + 'video/x-sgi-movie' => 'movie', + 'x-conference/x-cooltalk' => 'ice', + ); + + /** + * Returns the extension based on the mime type. + * + * If the mime type is unknown, returns null. + * + * @return string|null The guessed extension or null if it cannot be guessed + */ + public function guess($mimeType) + { + return isset($this->defaultExtensions[$mimeType]) ? $this->defaultExtensions[$mimeType] : null; + } +} diff --git a/app/laravel/vendor/Symfony/Component/HttpFoundation/File/MimeType/MimeTypeGuesser.php b/app/laravel/vendor/Symfony/Component/HttpFoundation/File/MimeType/MimeTypeGuesser.php new file mode 100755 index 000000000..d73a093df --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/HttpFoundation/File/MimeType/MimeTypeGuesser.php @@ -0,0 +1,121 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\File\MimeType; + +use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException; +use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException; + +/** + * A singleton mime type guesser. + * + * By default, all mime type guessers provided by the framework are installed + * (if available on the current OS/PHP setup). You can register custom + * guessers by calling the register() method on the singleton instance. + * + * + * $guesser = MimeTypeGuesser::getInstance(); + * $guesser->register(new MyCustomMimeTypeGuesser()); + * + * + * The last registered guesser is preferred over previously registered ones. + * + * @author Bernhard Schussek + */ +class MimeTypeGuesser implements MimeTypeGuesserInterface +{ + /** + * The singleton instance + * @var MimeTypeGuesser + */ + static private $instance = null; + + /** + * All registered MimeTypeGuesserInterface instances + * @var array + */ + protected $guessers = array(); + + /** + * Returns the singleton instance + * + * @return MimeTypeGuesser + */ + static public function getInstance() + { + if (null === self::$instance) { + self::$instance = new self(); + } + + return self::$instance; + } + + /** + * Registers all natively provided mime type guessers + */ + private function __construct() + { + if (FileBinaryMimeTypeGuesser::isSupported()) { + $this->register(new FileBinaryMimeTypeGuesser()); + } + + if (FileinfoMimeTypeGuesser::isSupported()) { + $this->register(new FileinfoMimeTypeGuesser()); + } + } + + /** + * Registers a new mime type guesser + * + * When guessing, this guesser is preferred over previously registered ones. + * + * @param MimeTypeGuesserInterface $guesser + */ + public function register(MimeTypeGuesserInterface $guesser) + { + array_unshift($this->guessers, $guesser); + } + + /** + * Tries to guess the mime type of the given file + * + * The file is passed to each registered mime type guesser in reverse order + * of their registration (last registered is queried first). Once a guesser + * returns a value that is not NULL, this method terminates and returns the + * value. + * + * @param string $path The path to the file + * + * @return string The mime type or NULL, if none could be guessed + * + * @throws FileException If the file does not exist + */ + public function guess($path) + { + if (!is_file($path)) { + throw new FileNotFoundException($path); + } + + if (!is_readable($path)) { + throw new AccessDeniedException($path); + } + + if (!$this->guessers) { + throw new \LogicException('Unable to guess the mime type as no guessers are available (Did you enable the php_fileinfo extension?)'); + } + + foreach ($this->guessers as $guesser) { + if (null !== $mimeType = $guesser->guess($path)) { + return $mimeType; + } + } + } +} diff --git a/app/laravel/vendor/Symfony/Component/HttpFoundation/File/MimeType/MimeTypeGuesserInterface.php b/app/laravel/vendor/Symfony/Component/HttpFoundation/File/MimeType/MimeTypeGuesserInterface.php new file mode 100755 index 000000000..66178bb95 --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/HttpFoundation/File/MimeType/MimeTypeGuesserInterface.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\File\MimeType; + +/** + * Guesses the mime type of a file + * + * @author Bernhard Schussek + */ +interface MimeTypeGuesserInterface +{ + /** + * Guesses the mime type of the file with the given path. + * + * @param string $path The path to the file + * + * @return string The mime type or NULL, if none could be guessed + * + * @throws FileNotFoundException If the file does not exist + * @throws AccessDeniedException If the file could not be read + */ + function guess($path); +} diff --git a/app/laravel/vendor/Symfony/Component/HttpFoundation/File/UploadedFile.php b/app/laravel/vendor/Symfony/Component/HttpFoundation/File/UploadedFile.php new file mode 100755 index 000000000..4e51c5001 --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/HttpFoundation/File/UploadedFile.php @@ -0,0 +1,223 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\File; + +use Symfony\Component\HttpFoundation\File\Exception\FileException; +use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException; + +/** + * A file uploaded through a form. + * + * @author Bernhard Schussek + * @author Florian Eckerstorfer + * @author Fabien Potencier + * + * @api + */ +class UploadedFile extends File +{ + /** + * Whether the test mode is activated. + * + * Local files are used in test mode hence the code should not enforce HTTP uploads. + * + * @var Boolean + */ + private $test = false; + + /** + * The original name of the uploaded file. + * + * @var string + */ + private $originalName; + + /** + * The mime type provided by the uploader. + * + * @var string + */ + private $mimeType; + + /** + * The file size provided by the uploader. + * + * @var string + */ + private $size; + + /** + * The UPLOAD_ERR_XXX constant provided by the uploader. + * + * @var integer + */ + private $error; + + /** + * Accepts the information of the uploaded file as provided by the PHP global $_FILES. + * + * The file object is only created when the uploaded file is valid (i.e. when the + * isValid() method returns true). Otherwise the only methods that could be called + * on an UploadedFile instance are: + * + * * getClientOriginalName, + * * getClientMimeType, + * * isValid, + * * getError. + * + * Calling any other method on an non-valid instance will cause an unpredictable result. + * + * @param string $path The full temporary path to the file + * @param string $originalName The original file name + * @param string $mimeType The type of the file as provided by PHP + * @param integer $size The file size + * @param integer $error The error constant of the upload (one of PHP's UPLOAD_ERR_XXX constants) + * @param Boolean $test Whether the test mode is active + * + * @throws FileException If file_uploads is disabled + * @throws FileNotFoundException If the file does not exist + * + * @api + */ + public function __construct($path, $originalName, $mimeType = null, $size = null, $error = null, $test = false) + { + if (!ini_get('file_uploads')) { + throw new FileException(sprintf('Unable to create UploadedFile because "file_uploads" is disabled in your php.ini file (%s)', get_cfg_var('cfg_file_path'))); + } + + $this->originalName = basename($originalName); + $this->mimeType = $mimeType ?: 'application/octet-stream'; + $this->size = $size; + $this->error = $error ?: UPLOAD_ERR_OK; + $this->test = (Boolean) $test; + + parent::__construct($path, UPLOAD_ERR_OK === $this->error); + } + + /** + * Returns the original file name. + * + * It is extracted from the request from which the file has been uploaded. + * Then is should not be considered as a safe value. + * + * @return string|null The original name + * + * @api + */ + public function getClientOriginalName() + { + return $this->originalName; + } + + /** + * Returns the file mime type. + * + * It is extracted from the request from which the file has been uploaded. + * Then is should not be considered as a safe value. + * + * @return string|null The mime type + * + * @api + */ + public function getClientMimeType() + { + return $this->mimeType; + } + + /** + * Returns the file size. + * + * It is extracted from the request from which the file has been uploaded. + * Then is should not be considered as a safe value. + * + * @return integer|null The file size + * + * @api + */ + public function getClientSize() + { + return $this->size; + } + + /** + * Returns the upload error. + * + * If the upload was successful, the constant UPLOAD_ERR_OK is returned. + * Otherwise one of the other UPLOAD_ERR_XXX constants is returned. + * + * @return integer The upload error + * + * @api + */ + public function getError() + { + return $this->error; + } + + /** + * Returns whether the file was uploaded successfully. + * + * @return Boolean True if no error occurred during uploading + * + * @api + */ + public function isValid() + { + return $this->error === UPLOAD_ERR_OK; + } + + /** + * Moves the file to a new location. + * + * @param string $directory The destination folder + * @param string $name The new file name + * + * @return File A File object representing the new file + * + * @throws FileException if the file has not been uploaded via Http + * + * @api + */ + public function move($directory, $name = null) + { + if ($this->isValid() && ($this->test || is_uploaded_file($this->getPathname()))) { + return parent::move($directory, $name); + } + + throw new FileException(sprintf('The file "%s" has not been uploaded via Http', $this->getPathname())); + } + + /** + * Returns the maximum size of an uploaded file as configured in php.ini + * + * @return type The maximum size of an uploaded file in bytes + */ + static public function getMaxFilesize() + { + $max = trim(ini_get('upload_max_filesize')); + + if ('' === $max) { + return PHP_INT_MAX; + } + + switch (strtolower(substr($max, -1))) { + case 'g': + $max *= 1024; + case 'm': + $max *= 1024; + case 'k': + $max *= 1024; + } + + return (integer) $max; + } +} diff --git a/app/laravel/vendor/Symfony/Component/HttpFoundation/FileBag.php b/app/laravel/vendor/Symfony/Component/HttpFoundation/FileBag.php new file mode 100755 index 000000000..702ab84c0 --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/HttpFoundation/FileBag.php @@ -0,0 +1,158 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +use Symfony\Component\HttpFoundation\File\UploadedFile; + +/** + * FileBag is a container for HTTP headers. + * + * @author Fabien Potencier + * @author Bulat Shakirzyanov + * + * @api + */ +class FileBag extends ParameterBag +{ + static private $fileKeys = array('error', 'name', 'size', 'tmp_name', 'type'); + + /** + * Constructor. + * + * @param array $parameters An array of HTTP files + * + * @api + */ + public function __construct(array $parameters = array()) + { + $this->replace($parameters); + } + + /** + * (non-PHPdoc) + * @see Symfony\Component\HttpFoundation\ParameterBag::replace() + * + * @api + */ + public function replace(array $files = array()) + { + $this->parameters = array(); + $this->add($files); + } + + /** + * (non-PHPdoc) + * @see Symfony\Component\HttpFoundation\ParameterBag::set() + * + * @api + */ + public function set($key, $value) + { + if (is_array($value) || $value instanceof UploadedFile) { + parent::set($key, $this->convertFileInformation($value)); + } else { + throw new \InvalidArgumentException('An uploaded file must be an array or an instance of UploadedFile.'); + } + } + + /** + * (non-PHPdoc) + * @see Symfony\Component\HttpFoundation\ParameterBag::add() + * + * @api + */ + public function add(array $files = array()) + { + foreach ($files as $key => $file) { + $this->set($key, $file); + } + } + + /** + * Converts uploaded files to UploadedFile instances. + * + * @param array|UploadedFile $file A (multi-dimensional) array of uploaded file information + * + * @return array A (multi-dimensional) array of UploadedFile instances + */ + protected function convertFileInformation($file) + { + if ($file instanceof UploadedFile) { + return $file; + } + + $file = $this->fixPhpFilesArray($file); + if (is_array($file)) { + $keys = array_keys($file); + sort($keys); + + if ($keys == self::$fileKeys) { + if (UPLOAD_ERR_NO_FILE == $file['error']) { + $file = null; + } else { + $file = new UploadedFile($file['tmp_name'], $file['name'], $file['type'], $file['size'], $file['error']); + } + } else { + $file = array_map(array($this, 'convertFileInformation'), $file); + } + } + + return $file; + } + + /** + * Fixes a malformed PHP $_FILES array. + * + * PHP has a bug that the format of the $_FILES array differs, depending on + * whether the uploaded file fields had normal field names or array-like + * field names ("normal" vs. "parent[child]"). + * + * This method fixes the array to look like the "normal" $_FILES array. + * + * It's safe to pass an already converted array, in which case this method + * just returns the original array unmodified. + * + * @param array $data + * + * @return array + */ + protected function fixPhpFilesArray($data) + { + if (!is_array($data)) { + return $data; + } + + $keys = array_keys($data); + sort($keys); + + if (self::$fileKeys != $keys || !isset($data['name']) || !is_array($data['name'])) { + return $data; + } + + $files = $data; + foreach (self::$fileKeys as $k) { + unset($files[$k]); + } + + foreach (array_keys($data['name']) as $key) { + $files[$key] = $this->fixPhpFilesArray(array( + 'error' => $data['error'][$key], + 'name' => $data['name'][$key], + 'type' => $data['type'][$key], + 'tmp_name' => $data['tmp_name'][$key], + 'size' => $data['size'][$key] + )); + } + + return $files; + } +} diff --git a/app/laravel/vendor/Symfony/Component/HttpFoundation/HeaderBag.php b/app/laravel/vendor/Symfony/Component/HttpFoundation/HeaderBag.php new file mode 100755 index 000000000..f614b094f --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/HttpFoundation/HeaderBag.php @@ -0,0 +1,306 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +/** + * HeaderBag is a container for HTTP headers. + * + * @author Fabien Potencier + * + * @api + */ +class HeaderBag +{ + protected $headers; + protected $cacheControl; + + /** + * Constructor. + * + * @param array $headers An array of HTTP headers + * + * @api + */ + public function __construct(array $headers = array()) + { + $this->cacheControl = array(); + $this->headers = array(); + foreach ($headers as $key => $values) { + $this->set($key, $values); + } + } + + /** + * Returns the headers as a string. + * + * @return string The headers + */ + public function __toString() + { + if (!$this->headers) { + return ''; + } + + $beautifier = function ($name) { + return preg_replace_callback('/\-(.)/', function ($match) { return '-'.strtoupper($match[1]); }, ucfirst($name)); + }; + + $max = max(array_map('strlen', array_keys($this->headers))) + 1; + $content = ''; + ksort($this->headers); + foreach ($this->headers as $name => $values) { + foreach ($values as $value) { + $content .= sprintf("%-{$max}s %s\r\n", $beautifier($name).':', $value); + } + } + + return $content; + } + + /** + * Returns the headers. + * + * @return array An array of headers + * + * @api + */ + public function all() + { + return $this->headers; + } + + /** + * Returns the parameter keys. + * + * @return array An array of parameter keys + * + * @api + */ + public function keys() + { + return array_keys($this->headers); + } + + /** + * Replaces the current HTTP headers by a new set. + * + * @param array $headers An array of HTTP headers + * + * @api + */ + public function replace(array $headers = array()) + { + $this->headers = array(); + $this->add($headers); + } + + /** + * Adds new headers the current HTTP headers set. + * + * @param array $headers An array of HTTP headers + * + * @api + */ + public function add(array $headers) + { + foreach ($headers as $key => $values) { + $this->set($key, $values); + } + } + + /** + * Returns a header value by name. + * + * @param string $key The header name + * @param mixed $default The default value + * @param Boolean $first Whether to return the first value or all header values + * + * @return string|array The first header value if $first is true, an array of values otherwise + * + * @api + */ + public function get($key, $default = null, $first = true) + { + $key = strtr(strtolower($key), '_', '-'); + + if (!array_key_exists($key, $this->headers)) { + if (null === $default) { + return $first ? null : array(); + } + + return $first ? $default : array($default); + } + + if ($first) { + return count($this->headers[$key]) ? $this->headers[$key][0] : $default; + } + + return $this->headers[$key]; + } + + /** + * Sets a header by name. + * + * @param string $key The key + * @param string|array $values The value or an array of values + * @param Boolean $replace Whether to replace the actual value of not (true by default) + * + * @api + */ + public function set($key, $values, $replace = true) + { + $key = strtr(strtolower($key), '_', '-'); + + $values = (array) $values; + + if (true === $replace || !isset($this->headers[$key])) { + $this->headers[$key] = $values; + } else { + $this->headers[$key] = array_merge($this->headers[$key], $values); + } + + if ('cache-control' === $key) { + $this->cacheControl = $this->parseCacheControl($values[0]); + } + } + + /** + * Returns true if the HTTP header is defined. + * + * @param string $key The HTTP header + * + * @return Boolean true if the parameter exists, false otherwise + * + * @api + */ + public function has($key) + { + return array_key_exists(strtr(strtolower($key), '_', '-'), $this->headers); + } + + /** + * Returns true if the given HTTP header contains the given value. + * + * @param string $key The HTTP header name + * @param string $value The HTTP value + * + * @return Boolean true if the value is contained in the header, false otherwise + * + * @api + */ + public function contains($key, $value) + { + return in_array($value, $this->get($key, null, false)); + } + + /** + * Removes a header. + * + * @param string $key The HTTP header name + * + * @api + */ + public function remove($key) + { + $key = strtr(strtolower($key), '_', '-'); + + unset($this->headers[$key]); + + if ('cache-control' === $key) { + $this->cacheControl = array(); + } + } + + /** + * Returns the HTTP header value converted to a date. + * + * @param string $key The parameter key + * @param \DateTime $default The default value + * + * @return \DateTime The filtered value + * + * @api + */ + public function getDate($key, \DateTime $default = null) + { + if (null === $value = $this->get($key)) { + return $default; + } + + if (false === $date = \DateTime::createFromFormat(DATE_RFC2822, $value)) { + throw new \RuntimeException(sprintf('The %s HTTP header is not parseable (%s).', $key, $value)); + } + + return $date; + } + + public function addCacheControlDirective($key, $value = true) + { + $this->cacheControl[$key] = $value; + + $this->set('Cache-Control', $this->getCacheControlHeader()); + } + + public function hasCacheControlDirective($key) + { + return array_key_exists($key, $this->cacheControl); + } + + public function getCacheControlDirective($key) + { + return array_key_exists($key, $this->cacheControl) ? $this->cacheControl[$key] : null; + } + + public function removeCacheControlDirective($key) + { + unset($this->cacheControl[$key]); + + $this->set('Cache-Control', $this->getCacheControlHeader()); + } + + protected function getCacheControlHeader() + { + $parts = array(); + ksort($this->cacheControl); + foreach ($this->cacheControl as $key => $value) { + if (true === $value) { + $parts[] = $key; + } else { + if (preg_match('#[^a-zA-Z0-9._-]#', $value)) { + $value = '"'.$value.'"'; + } + + $parts[] = "$key=$value"; + } + } + + return implode(', ', $parts); + } + + /** + * Parses a Cache-Control HTTP header. + * + * @param string $header The value of the Cache-Control HTTP header + * + * @return array An array representing the attribute values + */ + protected function parseCacheControl($header) + { + $cacheControl = array(); + preg_match_all('#([a-zA-Z][a-zA-Z_-]*)\s*(?:=(?:"([^"]*)"|([^ \t",;]*)))?#', $header, $matches, PREG_SET_ORDER); + foreach ($matches as $match) { + $cacheControl[strtolower($match[1])] = isset($match[2]) && $match[2] ? $match[2] : (isset($match[3]) ? $match[3] : true); + } + + return $cacheControl; + } +} diff --git a/app/laravel/vendor/Symfony/Component/HttpFoundation/JsonResponse.php b/app/laravel/vendor/Symfony/Component/HttpFoundation/JsonResponse.php new file mode 100755 index 000000000..8e02926e2 --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/HttpFoundation/JsonResponse.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +/** + * Response represents an HTTP response in JSON format. + * + * @author Igor Wiedler + */ +class JsonResponse extends Response +{ + /** + * Constructor. + * + * @param mixed $data The response data + * @param integer $status The response status code + * @param array $headers An array of response headers + */ + public function __construct($data = array(), $status = 200, $headers = array()) + { + // root should be JSON object, not array + if (is_array($data) && 0 === count($data)) { + $data = new \ArrayObject(); + } + + parent::__construct( + json_encode($data), + $status, + array_merge(array('Content-Type' => 'application/json'), $headers) + ); + } + + /** + * {@inheritDoc} + */ + static public function create($data = array(), $status = 200, $headers = array()) + { + return new static($data, $status, $headers); + } +} diff --git a/app/laravel/vendor/Symfony/Component/HttpFoundation/LICENSE b/app/laravel/vendor/Symfony/Component/HttpFoundation/LICENSE new file mode 100755 index 000000000..cdffe7aeb --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/HttpFoundation/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2012 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/app/laravel/vendor/Symfony/Component/HttpFoundation/LaravelRequest.php b/app/laravel/vendor/Symfony/Component/HttpFoundation/LaravelRequest.php new file mode 100644 index 000000000..7b0d46725 --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/HttpFoundation/LaravelRequest.php @@ -0,0 +1,38 @@ +server->get('CONTENT_TYPE'), 'application/x-www-form-urlencoded') + || (0 === strpos($request->server->get('HTTP_CONTENT_TYPE'), 'application/x-www-form-urlencoded'))) + && in_array(strtoupper($request->server->get('REQUEST_METHOD', 'GET')), array('PUT', 'DELETE', 'PATCH')) + ) { + parse_str($request->getContent(), $data); + if (magic_quotes()) $data = array_strip_slashes($data); + $request->request = new ParameterBag($data); + } + + return $request; + } + + /** + * Get the root URL of the application. + * + * @return string + */ + public function getRootUrl() + { + return $this->getScheme().'://'.$this->getHttpHost().$this->getBasePath(); + } + +} \ No newline at end of file diff --git a/app/laravel/vendor/Symfony/Component/HttpFoundation/LaravelResponse.php b/app/laravel/vendor/Symfony/Component/HttpFoundation/LaravelResponse.php new file mode 100644 index 000000000..9fe45ecf3 --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/HttpFoundation/LaravelResponse.php @@ -0,0 +1,40 @@ + + * + * @api + */ +class LaravelResponse extends Response +{ + + /** + * Sends HTTP headers and content. + * + * @return Response + * + * @api + */ + public function send() + { + $this->sendHeaders(); + $this->sendContent(); + + return $this; + } + + /** + * Finishes the request for PHP-FastCGI + * + * @return void + */ + public function finish() + { + if (function_exists('fastcgi_finish_request')) { + fastcgi_finish_request(); + } + } + +} diff --git a/app/laravel/vendor/Symfony/Component/HttpFoundation/ParameterBag.php b/app/laravel/vendor/Symfony/Component/HttpFoundation/ParameterBag.php new file mode 100755 index 000000000..a5b04da03 --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/HttpFoundation/ParameterBag.php @@ -0,0 +1,281 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +/** + * ParameterBag is a container for key/value pairs. + * + * @author Fabien Potencier + * + * @api + */ +class ParameterBag +{ + /** + * Parameter storage. + * + * @var array + */ + protected $parameters; + + /** + * Constructor. + * + * @param array $parameters An array of parameters + * + * @api + */ + public function __construct(array $parameters = array()) + { + $this->parameters = $parameters; + } + + /** + * Returns the parameters. + * + * @return array An array of parameters + * + * @api + */ + public function all() + { + return $this->parameters; + } + + /** + * Returns the parameter keys. + * + * @return array An array of parameter keys + * + * @api + */ + public function keys() + { + return array_keys($this->parameters); + } + + /** + * Replaces the current parameters by a new set. + * + * @param array $parameters An array of parameters + * + * @api + */ + public function replace(array $parameters = array()) + { + $this->parameters = $parameters; + } + + /** + * Adds parameters. + * + * @param array $parameters An array of parameters + * + * @api + */ + public function add(array $parameters = array()) + { + $this->parameters = array_replace($this->parameters, $parameters); + } + + /** + * Returns a parameter by name. + * + * @param string $path The key + * @param mixed $default The default value if the parameter key does not exist + * @param boolean $deep If true, a path like foo[bar] will find deeper items + * + * @api + */ + public function get($path, $default = null, $deep = false) + { + if (!$deep || false === $pos = strpos($path, '[')) { + return array_key_exists($path, $this->parameters) ? $this->parameters[$path] : $default; + } + + $root = substr($path, 0, $pos); + if (!array_key_exists($root, $this->parameters)) { + return $default; + } + + $value = $this->parameters[$root]; + $currentKey = null; + for ($i=$pos,$c=strlen($path); $i<$c; $i++) { + $char = $path[$i]; + + if ('[' === $char) { + if (null !== $currentKey) { + throw new \InvalidArgumentException(sprintf('Malformed path. Unexpected "[" at position %d.', $i)); + } + + $currentKey = ''; + } elseif (']' === $char) { + if (null === $currentKey) { + throw new \InvalidArgumentException(sprintf('Malformed path. Unexpected "]" at position %d.', $i)); + } + + if (!is_array($value) || !array_key_exists($currentKey, $value)) { + return $default; + } + + $value = $value[$currentKey]; + $currentKey = null; + } else { + if (null === $currentKey) { + throw new \InvalidArgumentException(sprintf('Malformed path. Unexpected "%s" at position %d.', $char, $i)); + } + + $currentKey .= $char; + } + } + + if (null !== $currentKey) { + throw new \InvalidArgumentException(sprintf('Malformed path. Path must end with "]".')); + } + + return $value; + } + + /** + * Sets a parameter by name. + * + * @param string $key The key + * @param mixed $value The value + * + * @api + */ + public function set($key, $value) + { + $this->parameters[$key] = $value; + } + + /** + * Returns true if the parameter is defined. + * + * @param string $key The key + * + * @return Boolean true if the parameter exists, false otherwise + * + * @api + */ + public function has($key) + { + return array_key_exists($key, $this->parameters); + } + + /** + * Removes a parameter. + * + * @param string $key The key + * + * @api + */ + public function remove($key) + { + unset($this->parameters[$key]); + } + + /** + * Returns the alphabetic characters of the parameter value. + * + * @param string $key The parameter key + * @param mixed $default The default value if the parameter key does not exist + * @param boolean $deep If true, a path like foo[bar] will find deeper items + * + * @return string The filtered value + * + * @api + */ + public function getAlpha($key, $default = '', $deep = false) + { + return preg_replace('/[^[:alpha:]]/', '', $this->get($key, $default, $deep)); + } + + /** + * Returns the alphabetic characters and digits of the parameter value. + * + * @param string $key The parameter key + * @param mixed $default The default value if the parameter key does not exist + * @param boolean $deep If true, a path like foo[bar] will find deeper items + * + * @return string The filtered value + * + * @api + */ + public function getAlnum($key, $default = '', $deep = false) + { + return preg_replace('/[^[:alnum:]]/', '', $this->get($key, $default, $deep)); + } + + /** + * Returns the digits of the parameter value. + * + * @param string $key The parameter key + * @param mixed $default The default value if the parameter key does not exist + * @param boolean $deep If true, a path like foo[bar] will find deeper items + * + * @return string The filtered value + * + * @api + */ + public function getDigits($key, $default = '', $deep = false) + { + // we need to remove - and + because they're allowed in the filter + return str_replace(array('-', '+'), '', $this->filter($key, $default, $deep, FILTER_SANITIZE_NUMBER_INT)); + } + + /** + * Returns the parameter value converted to integer. + * + * @param string $key The parameter key + * @param mixed $default The default value if the parameter key does not exist + * @param boolean $deep If true, a path like foo[bar] will find deeper items + * + * @return string The filtered value + * + * @api + */ + public function getInt($key, $default = 0, $deep = false) + { + return (int) $this->get($key, $default, $deep); + } + + /** + * Filter key. + * + * @param string $key Key. + * @param mixed $default Default = null. + * @param boolean $deep Default = false. + * @param integer $filter FILTER_* constant. + * @param mixed $options Filter options. + * + * @see http://php.net/manual/en/function.filter-var.php + * + * @return mixed + */ + public function filter($key, $default = null, $deep = false, $filter=FILTER_DEFAULT, $options=array()) + { + $value = $this->get($key, $default, $deep); + + // Always turn $options into an array - this allows filter_var option shortcuts. + if (!is_array($options) && $options) { + $options = array('flags' => $options); + } + + // Add a convenience check for arrays. + if (is_array($value) && !isset($options['flags'])) { + $options['flags'] = FILTER_REQUIRE_ARRAY; + } + + return filter_var($value, $filter, $options); + } +} diff --git a/app/laravel/vendor/Symfony/Component/HttpFoundation/README.md b/app/laravel/vendor/Symfony/Component/HttpFoundation/README.md new file mode 100755 index 000000000..88adfed75 --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/HttpFoundation/README.md @@ -0,0 +1,47 @@ +HttpFoundation Component +======================== + +HttpFoundation defines an object-oriented layer for the HTTP specification. + +It provides an abstraction for requests, responses, uploaded files, cookies, +sessions, ... + +In this example, we get a Request object from the current PHP global +variables: + + use Symfony\Component\HttpFoundation\Request; + use Symfony\Component\HttpFoundation\Response; + + $request = Request::createFromGlobals(); + echo $request->getPathInfo(); + +You can also create a Request directly -- that's interesting for unit testing: + + $request = Request::create('/?foo=bar', 'GET'); + echo $request->getPathInfo(); + +And here is how to create and send a Response: + + $response = new Response('Not Found', 404, array('Content-Type' => 'text/plain')); + $response->send(); + +The Request and the Response classes have many other methods that implement +the HTTP specification. + +Loading +------- + +If you are using PHP 5.3.x you must add the following to your autoloader: + + // SessionHandlerInterface + if (!interface_exists('SessionHandlerInterface')) { + $loader->registerPrefixFallback(__DIR__.'/../vendor/symfony/src/Symfony/Component/HttpFoundation/Resources/stubs'); + } + + +Resources +--------- + +Unit tests: + +https://github.com/symfony/symfony/tree/master/tests/Symfony/Tests/Component/HttpFoundation diff --git a/app/laravel/vendor/Symfony/Component/HttpFoundation/RedirectResponse.php b/app/laravel/vendor/Symfony/Component/HttpFoundation/RedirectResponse.php new file mode 100755 index 000000000..27676ec0d --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/HttpFoundation/RedirectResponse.php @@ -0,0 +1,83 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +/** + * RedirectResponse represents an HTTP response doing a redirect. + * + * @author Fabien Potencier + * + * @api + */ +class RedirectResponse extends Response +{ + protected $targetUrl; + + /** + * Creates a redirect response so that it conforms to the rules defined for a redirect status code. + * + * @param string $url The URL to redirect to + * @param integer $status The status code (302 by default) + * @param array $headers The headers (Location is always set to the given url) + * + * @see http://tools.ietf.org/html/rfc2616#section-10.3 + * + * @api + */ + public function __construct($url, $status = 302, $headers = array()) + { + if (empty($url)) { + throw new \InvalidArgumentException('Cannot redirect to an empty URL.'); + } + + $this->targetUrl = $url; + + parent::__construct( + sprintf(' + + + + + + Redirecting to %1$s + + + Redirecting to %1$s. + +', htmlspecialchars($url, ENT_QUOTES, 'UTF-8')), + $status, + array_merge($headers, array('Location' => $url)) + ); + + if (!$this->isRedirect()) { + throw new \InvalidArgumentException(sprintf('The HTTP status code is not a redirect ("%s" given).', $status)); + } + } + + /** + * {@inheritDoc} + */ + static public function create($url = '', $status = 302, $headers = array()) + { + return new static($url, $status, $headers); + } + + /** + * Returns the target URL. + * + * @return string target URL + */ + public function getTargetUrl() + { + return $this->targetUrl; + } +} diff --git a/app/laravel/vendor/Symfony/Component/HttpFoundation/Request.php b/app/laravel/vendor/Symfony/Component/HttpFoundation/Request.php new file mode 100755 index 000000000..eb200b85a --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/HttpFoundation/Request.php @@ -0,0 +1,1413 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +use Symfony\Component\HttpFoundation\Session\SessionInterface; + +/** + * Request represents an HTTP request. + * + * @author Fabien Potencier + * + * @api + */ +class Request +{ + static protected $trustProxy = false; + + /** + * @var \Symfony\Component\HttpFoundation\ParameterBag + * + * @api + */ + public $attributes; + + /** + * @var \Symfony\Component\HttpFoundation\ParameterBag + * + * @api + */ + public $request; + + /** + * @var \Symfony\Component\HttpFoundation\ParameterBag + * + * @api + */ + public $query; + + /** + * @var \Symfony\Component\HttpFoundation\ServerBag + * + * @api + */ + public $server; + + /** + * @var \Symfony\Component\HttpFoundation\FileBag + * + * @api + */ + public $files; + + /** + * @var \Symfony\Component\HttpFoundation\ParameterBag + * + * @api + */ + public $cookies; + + /** + * @var \Symfony\Component\HttpFoundation\HeaderBag + * + * @api + */ + public $headers; + + /** + * @var string + */ + protected $content; + + /** + * @var string + */ + protected $languages; + + /** + * @var string + */ + protected $charsets; + + /** + * @var string + */ + protected $acceptableContentTypes; + + /** + * @var string + */ + protected $pathInfo; + + /** + * @var string + */ + protected $requestUri; + + /** + * @var string + */ + protected $baseUrl; + + /** + * @var string + */ + protected $basePath; + + /** + * @var string + */ + protected $method; + + /** + * @var string + */ + protected $format; + + /** + * @var \Symfony\Component\HttpFoundation\Session\SessionInterface + */ + protected $session; + + /** + * @var string + */ + protected $locale; + + /** + * @var string + */ + protected $defaultLocale = 'en'; + + /** + * @var string + */ + static protected $formats; + + /** + * Constructor. + * + * @param array $query The GET parameters + * @param array $request The POST parameters + * @param array $attributes The request attributes (parameters parsed from the PATH_INFO, ...) + * @param array $cookies The COOKIE parameters + * @param array $files The FILES parameters + * @param array $server The SERVER parameters + * @param string $content The raw body data + * + * @api + */ + public function __construct(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null) + { + $this->initialize($query, $request, $attributes, $cookies, $files, $server, $content); + } + + /** + * Sets the parameters for this request. + * + * This method also re-initializes all properties. + * + * @param array $query The GET parameters + * @param array $request The POST parameters + * @param array $attributes The request attributes (parameters parsed from the PATH_INFO, ...) + * @param array $cookies The COOKIE parameters + * @param array $files The FILES parameters + * @param array $server The SERVER parameters + * @param string $content The raw body data + * + * @api + */ + public function initialize(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null) + { + $this->request = new ParameterBag($request); + $this->query = new ParameterBag($query); + $this->attributes = new ParameterBag($attributes); + $this->cookies = new ParameterBag($cookies); + $this->files = new FileBag($files); + $this->server = new ServerBag($server); + $this->headers = new HeaderBag($this->server->getHeaders()); + + $this->content = $content; + $this->languages = null; + $this->charsets = null; + $this->acceptableContentTypes = null; + $this->pathInfo = null; + $this->requestUri = null; + $this->baseUrl = null; + $this->basePath = null; + $this->method = null; + $this->format = null; + } + + /** + * Creates a new request with values from PHP's super globals. + * + * @return Request A new request + * + * @api + */ + static public function createFromGlobals() + { + $request = new static($_GET, $_POST, array(), $_COOKIE, $_FILES, $_SERVER); + + if (0 === strpos($request->server->get('CONTENT_TYPE'), 'application/x-www-form-urlencoded') + && in_array(strtoupper($request->server->get('REQUEST_METHOD', 'GET')), array('PUT', 'DELETE', 'PATCH')) + ) { + parse_str($request->getContent(), $data); + $request->request = new ParameterBag($data); + } + + return $request; + } + + /** + * Creates a Request based on a given URI and configuration. + * + * @param string $uri The URI + * @param string $method The HTTP method + * @param array $parameters The request (GET) or query (POST) parameters + * @param array $cookies The request cookies ($_COOKIE) + * @param array $files The request files ($_FILES) + * @param array $server The server parameters ($_SERVER) + * @param string $content The raw body data + * + * @return Request A Request instance + * + * @api + */ + static public function create($uri, $method = 'GET', $parameters = array(), $cookies = array(), $files = array(), $server = array(), $content = null) + { + $defaults = array( + 'SERVER_NAME' => 'localhost', + 'SERVER_PORT' => 80, + 'HTTP_HOST' => 'localhost', + 'HTTP_USER_AGENT' => 'Symfony/2.X', + 'HTTP_ACCEPT' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', + 'HTTP_ACCEPT_LANGUAGE' => 'en-us,en;q=0.5', + 'HTTP_ACCEPT_CHARSET' => 'ISO-8859-1,utf-8;q=0.7,*;q=0.7', + 'REMOTE_ADDR' => '127.0.0.1', + 'SCRIPT_NAME' => '', + 'SCRIPT_FILENAME' => '', + 'SERVER_PROTOCOL' => 'HTTP/1.1', + 'REQUEST_TIME' => time(), + ); + + $components = parse_url($uri); + if (isset($components['host'])) { + $defaults['SERVER_NAME'] = $components['host']; + $defaults['HTTP_HOST'] = $components['host']; + } + + if (isset($components['scheme'])) { + if ('https' === $components['scheme']) { + $defaults['HTTPS'] = 'on'; + $defaults['SERVER_PORT'] = 443; + } + } + + if (isset($components['port'])) { + $defaults['SERVER_PORT'] = $components['port']; + $defaults['HTTP_HOST'] = $defaults['HTTP_HOST'].':'.$components['port']; + } + + if (isset($components['user'])) { + $defaults['PHP_AUTH_USER'] = $components['user']; + } + + if (isset($components['pass'])) { + $defaults['PHP_AUTH_PW'] = $components['pass']; + } + + if (!isset($components['path'])) { + $components['path'] = ''; + } + + switch (strtoupper($method)) { + case 'POST': + case 'PUT': + case 'DELETE': + $defaults['CONTENT_TYPE'] = 'application/x-www-form-urlencoded'; + case 'PATCH': + $request = $parameters; + $query = array(); + break; + default: + $request = array(); + $query = $parameters; + break; + } + + if (isset($components['query'])) { + $queryString = html_entity_decode($components['query']); + parse_str($queryString, $qs); + if (is_array($qs)) { + $query = array_replace($qs, $query); + } + } + $queryString = http_build_query($query); + + $uri = $components['path'].($queryString ? '?'.$queryString : ''); + + $server = array_replace($defaults, $server, array( + 'REQUEST_METHOD' => strtoupper($method), + 'PATH_INFO' => '', + 'REQUEST_URI' => $uri, + 'QUERY_STRING' => $queryString, + )); + + return new static($query, $request, array(), $cookies, $files, $server, $content); + } + + /** + * Clones a request and overrides some of its parameters. + * + * @param array $query The GET parameters + * @param array $request The POST parameters + * @param array $attributes The request attributes (parameters parsed from the PATH_INFO, ...) + * @param array $cookies The COOKIE parameters + * @param array $files The FILES parameters + * @param array $server The SERVER parameters + * + * @api + */ + public function duplicate(array $query = null, array $request = null, array $attributes = null, array $cookies = null, array $files = null, array $server = null) + { + $dup = clone $this; + if ($query !== null) { + $dup->query = new ParameterBag($query); + } + if ($request !== null) { + $dup->request = new ParameterBag($request); + } + if ($attributes !== null) { + $dup->attributes = new ParameterBag($attributes); + } + if ($cookies !== null) { + $dup->cookies = new ParameterBag($cookies); + } + if ($files !== null) { + $dup->files = new FileBag($files); + } + if ($server !== null) { + $dup->server = new ServerBag($server); + $dup->headers = new HeaderBag($dup->server->getHeaders()); + } + $dup->languages = null; + $dup->charsets = null; + $dup->acceptableContentTypes = null; + $dup->pathInfo = null; + $dup->requestUri = null; + $dup->baseUrl = null; + $dup->basePath = null; + $dup->method = null; + $dup->format = null; + + return $dup; + } + + /** + * Clones the current request. + * + * Note that the session is not cloned as duplicated requests + * are most of the time sub-requests of the main one. + */ + public function __clone() + { + $this->query = clone $this->query; + $this->request = clone $this->request; + $this->attributes = clone $this->attributes; + $this->cookies = clone $this->cookies; + $this->files = clone $this->files; + $this->server = clone $this->server; + $this->headers = clone $this->headers; + } + + /** + * Returns the request as a string. + * + * @return string The request + */ + public function __toString() + { + return + sprintf('%s %s %s', $this->getMethod(), $this->getRequestUri(), $this->server->get('SERVER_PROTOCOL'))."\r\n". + $this->headers."\r\n". + $this->getContent(); + } + + /** + * Overrides the PHP global variables according to this request instance. + * + * It overrides $_GET, $_POST, $_REQUEST, $_SERVER, $_COOKIE, and $_FILES. + * + * @api + */ + public function overrideGlobals() + { + $_GET = $this->query->all(); + $_POST = $this->request->all(); + $_SERVER = $this->server->all(); + $_COOKIE = $this->cookies->all(); + // FIXME: populate $_FILES + + foreach ($this->headers->all() as $key => $value) { + $key = strtoupper(str_replace('-', '_', $key)); + if (in_array($key, array('CONTENT_TYPE', 'CONTENT_LENGTH'))) { + $_SERVER[$key] = implode(', ', $value); + } else { + $_SERVER['HTTP_'.$key] = implode(', ', $value); + } + } + + // FIXME: should read variables_order and request_order + // to know which globals to merge and in which order + $_REQUEST = array_merge($_GET, $_POST); + } + + /** + * Trusts $_SERVER entries coming from proxies. + * + * You should only call this method if your application + * is hosted behind a reverse proxy that you manage. + * + * @api + */ + static public function trustProxyData() + { + self::$trustProxy = true; + } + + /** + * Returns true if $_SERVER entries coming from proxies are trusted, + * false otherwise. + * + * @return boolean + */ + static public function isProxyTrusted() + { + return self::$trustProxy; + } + + /** + * Gets a "parameter" value. + * + * This method is mainly useful for libraries that want to provide some flexibility. + * + * Order of precedence: GET, PATH, POST, COOKIE + * + * Avoid using this method in controllers: + * + * * slow + * * prefer to get from a "named" source + * + * It is better to explicity get request parameters from the appropriate + * public property instead (query, request, attributes, ...). + * + * @param string $key the key + * @param mixed $default the default value + * @param type $deep is parameter deep in multidimensional array + * + * @return mixed + */ + public function get($key, $default = null, $deep = false) + { + return $this->query->get($key, $this->attributes->get($key, $this->request->get($key, $default, $deep), $deep), $deep); + } + + /** + * Gets the Session. + * + * @return SessionInterface|null The session + * + * @api + */ + public function getSession() + { + return $this->session; + } + + /** + * Whether the request contains a Session which was started in one of the + * previous requests. + * + * @return boolean + * + * @api + */ + public function hasPreviousSession() + { + // the check for $this->session avoids malicious users trying to fake a session cookie with proper name + $sessionName = $this->hasSession() ? $this->session->getName() : null; + + return $this->cookies->has($sessionName) && $this->hasSession(); + } + + /** + * Whether the request contains a Session object. + * + * @return boolean + * + * @api + */ + public function hasSession() + { + return null !== $this->session; + } + + /** + * Sets the Session. + * + * @param SessionInterface $session The Session + * + * @api + */ + public function setSession(SessionInterface $session) + { + $this->session = $session; + } + + /** + * Returns the client IP address. + * + * @return string The client IP address + * + * @api + */ + public function getClientIp() + { + if (self::$trustProxy) { + if ($this->server->has('HTTP_CLIENT_IP')) { + return $this->server->get('HTTP_CLIENT_IP'); + } elseif ($this->server->has('HTTP_X_FORWARDED_FOR')) { + $clientIp = explode(',', $this->server->get('HTTP_X_FORWARDED_FOR'), 2); + + return isset($clientIp[0]) ? trim($clientIp[0]) : ''; + } + } + + return $this->server->get('REMOTE_ADDR'); + } + + /** + * Returns current script name. + * + * @return string + * + * @api + */ + public function getScriptName() + { + return $this->server->get('SCRIPT_NAME', $this->server->get('ORIG_SCRIPT_NAME', '')); + } + + /** + * Returns the path being requested relative to the executed script. + * + * The path info always starts with a /. + * + * Suppose this request is instantiated from /mysite on localhost: + * + * * http://localhost/mysite returns an empty string + * * http://localhost/mysite/about returns '/about' + * * http://localhost/mysite/about?var=1 returns '/about' + * + * @return string + * + * @api + */ + public function getPathInfo() + { + if (null === $this->pathInfo) { + $this->pathInfo = $this->preparePathInfo(); + } + + return $this->pathInfo; + } + + /** + * Returns the root path from which this request is executed. + * + * Suppose that an index.php file instantiates this request object: + * + * * http://localhost/index.php returns an empty string + * * http://localhost/index.php/page returns an empty string + * * http://localhost/web/index.php return '/web' + * + * @return string + * + * @api + */ + public function getBasePath() + { + if (null === $this->basePath) { + $this->basePath = $this->prepareBasePath(); + } + + return $this->basePath; + } + + /** + * Returns the root url from which this request is executed. + * + * The base URL never ends with a /. + * + * This is similar to getBasePath(), except that it also includes the + * script filename (e.g. index.php) if one exists. + * + * @return string + * + * @api + */ + public function getBaseUrl() + { + if (null === $this->baseUrl) { + $this->baseUrl = $this->prepareBaseUrl(); + } + + return $this->baseUrl; + } + + /** + * Gets the request's scheme. + * + * @return string + * + * @api + */ + public function getScheme() + { + return $this->isSecure() ? 'https' : 'http'; + } + + /** + * Returns the port on which the request is made. + * + * @return string + * + * @api + */ + public function getPort() + { + if (self::$trustProxy && $this->headers->has('X-Forwarded-Port')) { + return $this->headers->get('X-Forwarded-Port'); + } else { + return $this->server->get('SERVER_PORT'); + } + } + + /** + * Returns the user. + * + * @return string|null + */ + public function getUser() + { + return $this->server->get('PHP_AUTH_USER'); + } + + /** + * Returns the password. + * + * @return string|null + */ + public function getPassword() + { + return $this->server->get('PHP_AUTH_PW'); + } + + /** + * Returns the HTTP host being requested. + * + * The port name will be appended to the host if it's non-standard. + * + * @return string + * + * @api + */ + public function getHttpHost() + { + $scheme = $this->getScheme(); + $port = $this->getPort(); + + if (('http' == $scheme && $port == 80) || ('https' == $scheme && $port == 443)) { + return $this->getHost(); + } + + return $this->getHost().':'.$port; + } + + /** + * Returns the requested URI. + * + * @return string + * + * @api + */ + public function getRequestUri() + { + if (null === $this->requestUri) { + $this->requestUri = $this->prepareRequestUri(); + } + + return $this->requestUri; + } + + /** + * Generates a normalized URI for the Request. + * + * @return string A normalized URI for the Request + * + * @see getQueryString() + * + * @api + */ + public function getUri() + { + $qs = $this->getQueryString(); + if (null !== $qs) { + $qs = '?'.$qs; + } + + $auth = ''; + if ($user = $this->getUser()) { + $auth = $user; + } + + if ($pass = $this->getPassword()) { + $auth .= ":$pass"; + } + + if ('' !== $auth) { + $auth .= '@'; + } + + return $this->getScheme().'://'.$auth.$this->getHttpHost().$this->getBaseUrl().$this->getPathInfo().$qs; + } + + /** + * Generates a normalized URI for the given path. + * + * @param string $path A path to use instead of the current one + * + * @return string The normalized URI for the path + * + * @api + */ + public function getUriForPath($path) + { + return $this->getScheme().'://'.$this->getHttpHost().$this->getBaseUrl().$path; + } + + /** + * Generates the normalized query string for the Request. + * + * It builds a normalized query string, where keys/value pairs are alphabetized + * and have consistent escaping. + * + * @return string|null A normalized query string for the Request + * + * @api + */ + public function getQueryString() + { + if (!$qs = $this->server->get('QUERY_STRING')) { + return null; + } + + $parts = array(); + $order = array(); + + foreach (explode('&', $qs) as $segment) { + if (false === strpos($segment, '=')) { + $parts[] = $segment; + $order[] = $segment; + } else { + $tmp = explode('=', rawurldecode($segment), 2); + $parts[] = rawurlencode($tmp[0]).'='.rawurlencode($tmp[1]); + $order[] = $tmp[0]; + } + } + array_multisort($order, SORT_ASC, $parts); + + return implode('&', $parts); + } + + /** + * Checks whether the request is secure or not. + * + * @return Boolean + * + * @api + */ + public function isSecure() + { + return ( + (strtolower($this->server->get('HTTPS')) == 'on' || $this->server->get('HTTPS') == 1) + || + (self::$trustProxy && strtolower($this->headers->get('SSL_HTTPS')) == 'on' || $this->headers->get('SSL_HTTPS') == 1) + || + (self::$trustProxy && strtolower($this->headers->get('X_FORWARDED_PROTO')) == 'https') + ); + } + + /** + * Returns the host name. + * + * @return string + * + * @api + */ + public function getHost() + { + if (self::$trustProxy && $host = $this->headers->get('X_FORWARDED_HOST')) { + $elements = explode(',', $host); + + $host = trim($elements[count($elements) - 1]); + } else { + if (!$host = $this->headers->get('HOST')) { + if (!$host = $this->server->get('SERVER_NAME')) { + $host = $this->server->get('SERVER_ADDR', ''); + } + } + } + + // Remove port number from host + $host = preg_replace('/:\d+$/', '', $host); + + return trim($host); + } + + /** + * Sets the request method. + * + * @param string $method + * + * @api + */ + public function setMethod($method) + { + $this->method = null; + $this->server->set('REQUEST_METHOD', $method); + } + + /** + * Gets the request method. + * + * The method is always an uppercased string. + * + * @return string The request method + * + * @api + */ + public function getMethod() + { + if (null === $this->method) { + $this->method = strtoupper($this->server->get('REQUEST_METHOD', 'GET')); + if ('POST' === $this->method) { + $this->method = strtoupper($this->headers->get('X-HTTP-METHOD-OVERRIDE', $this->request->get('_method', 'POST'))); + } + } + + return $this->method; + } + + /** + * Gets the mime type associated with the format. + * + * @param string $format The format + * + * @return string The associated mime type (null if not found) + * + * @api + */ + public function getMimeType($format) + { + if (null === static::$formats) { + static::initializeFormats(); + } + + return isset(static::$formats[$format]) ? static::$formats[$format][0] : null; + } + + /** + * Gets the format associated with the mime type. + * + * @param string $mimeType The associated mime type + * + * @return string The format (null if not found) + * + * @api + */ + public function getFormat($mimeType) + { + if (false !== $pos = strpos($mimeType, ';')) { + $mimeType = substr($mimeType, 0, $pos); + } + + if (null === static::$formats) { + static::initializeFormats(); + } + + foreach (static::$formats as $format => $mimeTypes) { + if (in_array($mimeType, (array) $mimeTypes)) { + return $format; + } + } + + return null; + } + + /** + * Associates a format with mime types. + * + * @param string $format The format + * @param string|array $mimeTypes The associated mime types (the preferred one must be the first as it will be used as the content type) + * + * @api + */ + public function setFormat($format, $mimeTypes) + { + if (null === static::$formats) { + static::initializeFormats(); + } + + static::$formats[$format] = is_array($mimeTypes) ? $mimeTypes : array($mimeTypes); + } + + /** + * Gets the request format. + * + * Here is the process to determine the format: + * + * * format defined by the user (with setRequestFormat()) + * * _format request parameter + * * $default + * + * @param string $default The default format + * + * @return string The request format + * + * @api + */ + public function getRequestFormat($default = 'html') + { + if (null === $this->format) { + $this->format = $this->get('_format', $default); + } + + return $this->format; + } + + /** + * Sets the request format. + * + * @param string $format The request format. + * + * @api + */ + public function setRequestFormat($format) + { + $this->format = $format; + } + + /** + * Gets the format associated with the request. + * + * @return string The format (null if no content type is present) + * + * @api + */ + public function getContentType() + { + return $this->getFormat($this->server->get('CONTENT_TYPE')); + } + + /** + * Sets the default locale. + * + * @param string $locale + * + * @api + */ + public function setDefaultLocale($locale) + { + $this->setPhpDefaultLocale($this->defaultLocale = $locale); + } + + /** + * Sets the locale. + * + * @param string $locale + * + * @api + */ + public function setLocale($locale) + { + $this->setPhpDefaultLocale($this->locale = $locale); + } + + /** + * Get the locale. + * + * @return string + */ + public function getLocale() + { + return null === $this->locale ? $this->defaultLocale : $this->locale; + } + + /** + * Checks whether the method is safe or not. + * + * @return Boolean + * + * @api + */ + public function isMethodSafe() + { + return in_array($this->getMethod(), array('GET', 'HEAD')); + } + + /** + * Returns the request body content. + * + * @param Boolean $asResource If true, a resource will be returned + * + * @return string|resource The request body content or a resource to read the body stream. + */ + public function getContent($asResource = false) + { + if (false === $this->content || (true === $asResource && null !== $this->content)) { + throw new \LogicException('getContent() can only be called once when using the resource return type.'); + } + + if (true === $asResource) { + $this->content = false; + + return fopen('php://input', 'rb'); + } + + if (null === $this->content) { + $this->content = file_get_contents('php://input'); + } + + return $this->content; + } + + /** + * Gets the Etags. + * + * @return array The entity tags + */ + public function getETags() + { + return preg_split('/\s*,\s*/', $this->headers->get('if_none_match'), null, PREG_SPLIT_NO_EMPTY); + } + + public function isNoCache() + { + return $this->headers->hasCacheControlDirective('no-cache') || 'no-cache' == $this->headers->get('Pragma'); + } + + /** + * Returns the preferred language. + * + * @param array $locales An array of ordered available locales + * + * @return string|null The preferred locale + * + * @api + */ + public function getPreferredLanguage(array $locales = null) + { + $preferredLanguages = $this->getLanguages(); + + if (empty($locales)) { + return isset($preferredLanguages[0]) ? $preferredLanguages[0] : null; + } + + if (!$preferredLanguages) { + return $locales[0]; + } + + $preferredLanguages = array_values(array_intersect($preferredLanguages, $locales)); + + return isset($preferredLanguages[0]) ? $preferredLanguages[0] : $locales[0]; + } + + /** + * Gets a list of languages acceptable by the client browser. + * + * @return array Languages ordered in the user browser preferences + * + * @api + */ + public function getLanguages() + { + if (null !== $this->languages) { + return $this->languages; + } + + $languages = $this->splitHttpAcceptHeader($this->headers->get('Accept-Language')); + $this->languages = array(); + foreach ($languages as $lang => $q) { + if (strstr($lang, '-')) { + $codes = explode('-', $lang); + if ($codes[0] == 'i') { + // Language not listed in ISO 639 that are not variants + // of any listed language, which can be registered with the + // i-prefix, such as i-cherokee + if (count($codes) > 1) { + $lang = $codes[1]; + } + } else { + for ($i = 0, $max = count($codes); $i < $max; $i++) { + if ($i == 0) { + $lang = strtolower($codes[0]); + } else { + $lang .= '_'.strtoupper($codes[$i]); + } + } + } + } + + $this->languages[] = $lang; + } + + return $this->languages; + } + + /** + * Gets a list of charsets acceptable by the client browser. + * + * @return array List of charsets in preferable order + * + * @api + */ + public function getCharsets() + { + if (null !== $this->charsets) { + return $this->charsets; + } + + return $this->charsets = array_keys($this->splitHttpAcceptHeader($this->headers->get('Accept-Charset'))); + } + + /** + * Gets a list of content types acceptable by the client browser + * + * @return array List of content types in preferable order + * + * @api + */ + public function getAcceptableContentTypes() + { + if (null !== $this->acceptableContentTypes) { + return $this->acceptableContentTypes; + } + + return $this->acceptableContentTypes = array_keys($this->splitHttpAcceptHeader($this->headers->get('Accept'))); + } + + /** + * Returns true if the request is a XMLHttpRequest. + * + * It works if your JavaScript library set an X-Requested-With HTTP header. + * It is known to work with Prototype, Mootools, jQuery. + * + * @return Boolean true if the request is an XMLHttpRequest, false otherwise + * + * @api + */ + public function isXmlHttpRequest() + { + return 'XMLHttpRequest' == $this->headers->get('X-Requested-With'); + } + + /** + * Splits an Accept-* HTTP header. + * + * @param string $header Header to split + * + * @return array Array indexed by the values of the Accept-* header in preferred order + */ + public function splitHttpAcceptHeader($header) + { + if (!$header) { + return array(); + } + + $values = array(); + foreach (array_filter(explode(',', $header)) as $value) { + // Cut off any q-value that might come after a semi-colon + if (preg_match('/;\s*(q=.*$)/', $value, $match)) { + $q = (float) substr(trim($match[1]), 2); + $value = trim(substr($value, 0, -strlen($match[0]))); + } else { + $q = 1; + } + + if (0 < $q) { + $values[trim($value)] = $q; + } + } + + arsort($values); + reset($values); + + return $values; + } + + /* + * The following methods are derived from code of the Zend Framework (1.10dev - 2010-01-24) + * + * Code subject to the new BSD license (http://framework.zend.com/license/new-bsd). + * + * Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) + */ + + protected function prepareRequestUri() + { + $requestUri = ''; + + if ($this->headers->has('X_REWRITE_URL') && false !== stripos(PHP_OS, 'WIN')) { + // check this first so IIS will catch + $requestUri = $this->headers->get('X_REWRITE_URL'); + } elseif ($this->server->get('IIS_WasUrlRewritten') == '1' && $this->server->get('UNENCODED_URL') != '') { + // IIS7 with URL Rewrite: make sure we get the unencoded url (double slash problem) + $requestUri = $this->server->get('UNENCODED_URL'); + } elseif ($this->server->has('REQUEST_URI')) { + $requestUri = $this->server->get('REQUEST_URI'); + // HTTP proxy reqs setup request uri with scheme and host [and port] + the url path, only use url path + $schemeAndHttpHost = $this->getScheme().'://'.$this->getHttpHost(); + if (strpos($requestUri, $schemeAndHttpHost) === 0) { + $requestUri = substr($requestUri, strlen($schemeAndHttpHost)); + } + } elseif ($this->server->has('ORIG_PATH_INFO')) { + // IIS 5.0, PHP as CGI + $requestUri = $this->server->get('ORIG_PATH_INFO'); + if ($this->server->get('QUERY_STRING')) { + $requestUri .= '?'.$this->server->get('QUERY_STRING'); + } + } + + return $requestUri; + } + + /** + * Prepares the base URL. + * + * @return string + */ + protected function prepareBaseUrl() + { + $filename = basename($this->server->get('SCRIPT_FILENAME')); + + if (basename($this->server->get('SCRIPT_NAME')) === $filename) { + $baseUrl = $this->server->get('SCRIPT_NAME'); + } elseif (basename($this->server->get('PHP_SELF')) === $filename) { + $baseUrl = $this->server->get('PHP_SELF'); + } elseif (basename($this->server->get('ORIG_SCRIPT_NAME')) === $filename) { + $baseUrl = $this->server->get('ORIG_SCRIPT_NAME'); // 1and1 shared hosting compatibility + } else { + // Backtrack up the script_filename to find the portion matching + // php_self + $path = $this->server->get('PHP_SELF', ''); + $file = $this->server->get('SCRIPT_FILENAME', ''); + $segs = explode('/', trim($file, '/')); + $segs = array_reverse($segs); + $index = 0; + $last = count($segs); + $baseUrl = ''; + do { + $seg = $segs[$index]; + $baseUrl = '/'.$seg.$baseUrl; + ++$index; + } while (($last > $index) && (false !== ($pos = strpos($path, $baseUrl))) && (0 != $pos)); + } + + // Does the baseUrl have anything in common with the request_uri? + $requestUri = $this->getRequestUri(); + + if ($baseUrl && 0 === strpos($requestUri, $baseUrl)) { + // full $baseUrl matches + return $baseUrl; + } + + if ($baseUrl && 0 === strpos($requestUri, dirname($baseUrl))) { + // directory portion of $baseUrl matches + return rtrim(dirname($baseUrl), '/'); + } + + $truncatedRequestUri = $requestUri; + if (($pos = strpos($requestUri, '?')) !== false) { + $truncatedRequestUri = substr($requestUri, 0, $pos); + } + + $basename = basename($baseUrl); + if (empty($basename) || !strpos($truncatedRequestUri, $basename)) { + // no match whatsoever; set it blank + return ''; + } + + // If using mod_rewrite or ISAPI_Rewrite strip the script filename + // out of baseUrl. $pos !== 0 makes sure it is not matching a value + // from PATH_INFO or QUERY_STRING + if ((strlen($requestUri) >= strlen($baseUrl)) && ((false !== ($pos = strpos($requestUri, $baseUrl))) && ($pos !== 0))) { + $baseUrl = substr($requestUri, 0, $pos + strlen($baseUrl)); + } + + return rtrim($baseUrl, '/'); + } + + /** + * Prepares the base path. + * + * @return string base path + */ + protected function prepareBasePath() + { + $filename = basename($this->server->get('SCRIPT_FILENAME')); + $baseUrl = $this->getBaseUrl(); + if (empty($baseUrl)) { + return ''; + } + + if (basename($baseUrl) === $filename) { + $basePath = dirname($baseUrl); + } else { + $basePath = $baseUrl; + } + + if ('\\' === DIRECTORY_SEPARATOR) { + $basePath = str_replace('\\', '/', $basePath); + } + + return rtrim($basePath, '/'); + } + + /** + * Prepares the path info. + * + * @return string path info + */ + protected function preparePathInfo() + { + $baseUrl = $this->getBaseUrl(); + + if (null === ($requestUri = $this->getRequestUri())) { + return '/'; + } + + $pathInfo = '/'; + + // Remove the query string from REQUEST_URI + if ($pos = strpos($requestUri, '?')) { + $requestUri = substr($requestUri, 0, $pos); + } + + if ((null !== $baseUrl) && (false === ($pathInfo = substr(urldecode($requestUri), strlen(urldecode($baseUrl)))))) { + // If substr() returns false then PATH_INFO is set to an empty string + return '/'; + } elseif (null === $baseUrl) { + return $requestUri; + } + + return (string) $pathInfo; + } + + /** + * Initializes HTTP request formats. + */ + static protected function initializeFormats() + { + static::$formats = array( + 'html' => array('text/html', 'application/xhtml+xml'), + 'txt' => array('text/plain'), + 'js' => array('application/javascript', 'application/x-javascript', 'text/javascript'), + 'css' => array('text/css'), + 'json' => array('application/json', 'application/x-json'), + 'xml' => array('text/xml', 'application/xml', 'application/x-xml'), + 'rdf' => array('application/rdf+xml'), + 'atom' => array('application/atom+xml'), + 'rss' => array('application/rss+xml'), + ); + } + + /** + * Sets the default PHP locale. + * + * @param string $locale + */ + private function setPhpDefaultLocale($locale) + { + // if either the class Locale doesn't exist, or an exception is thrown when + // setting the default locale, the intl module is not installed, and + // the call can be ignored: + try { + if (class_exists('Locale', false)) { + \Locale::setDefault($locale); + } + } catch (\Exception $e) { + } + } +} diff --git a/app/laravel/vendor/Symfony/Component/HttpFoundation/RequestMatcher.php b/app/laravel/vendor/Symfony/Component/HttpFoundation/RequestMatcher.php new file mode 100755 index 000000000..0ca082d70 --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/HttpFoundation/RequestMatcher.php @@ -0,0 +1,221 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +/** + * RequestMatcher compares a pre-defined set of checks against a Request instance. + * + * @author Fabien Potencier + * + * @api + */ +class RequestMatcher implements RequestMatcherInterface +{ + /** + * @var string + */ + private $path; + + /** + * @var string + */ + private $host; + + /** + * @var string + */ + private $methods; + + /** + * @var string + */ + private $ip; + + /** + * Attributes. + * + * @var array + */ + private $attributes; + + public function __construct($path = null, $host = null, $methods = null, $ip = null, array $attributes = array()) + { + $this->path = $path; + $this->host = $host; + $this->methods = $methods; + $this->ip = $ip; + $this->attributes = $attributes; + } + + /** + * Adds a check for the URL host name. + * + * @param string $regexp A Regexp + */ + public function matchHost($regexp) + { + $this->host = $regexp; + } + + /** + * Adds a check for the URL path info. + * + * @param string $regexp A Regexp + */ + public function matchPath($regexp) + { + $this->path = $regexp; + } + + /** + * Adds a check for the client IP. + * + * @param string $ip A specific IP address or a range specified using IP/netmask like 192.168.1.0/24 + */ + public function matchIp($ip) + { + $this->ip = $ip; + } + + /** + * Adds a check for the HTTP method. + * + * @param string|array $method An HTTP method or an array of HTTP methods + */ + public function matchMethod($method) + { + $this->methods = array_map('strtoupper', is_array($method) ? $method : array($method)); + } + + /** + * Adds a check for request attribute. + * + * @param string $key The request attribute name + * @param string $regexp A Regexp + */ + public function matchAttribute($key, $regexp) + { + $this->attributes[$key] = $regexp; + } + + /** + * {@inheritdoc} + * + * @api + */ + public function matches(Request $request) + { + if (null !== $this->methods && !in_array($request->getMethod(), $this->methods)) { + return false; + } + + foreach ($this->attributes as $key => $pattern) { + if (!preg_match('#'.str_replace('#', '\\#', $pattern).'#', $request->attributes->get($key))) { + return false; + } + } + + if (null !== $this->path) { + $path = str_replace('#', '\\#', $this->path); + + if (!preg_match('#'.$path.'#', $request->getPathInfo())) { + return false; + } + } + + if (null !== $this->host && !preg_match('#'.str_replace('#', '\\#', $this->host).'#', $request->getHost())) { + return false; + } + + if (null !== $this->ip && !$this->checkIp($request->getClientIp(), $this->ip)) { + return false; + } + + return true; + } + + /** + * Validates an IP address. + * + * @param string $requestIp + * @param string $ip + * + * @return boolean True valid, false if not. + */ + protected function checkIp($requestIp, $ip) + { + // IPv6 address + if (false !== strpos($requestIp, ':')) { + return $this->checkIp6($requestIp, $ip); + } else { + return $this->checkIp4($requestIp, $ip); + } + } + + /** + * Validates an IPv4 address. + * + * @param string $requestIp + * @param string $ip + * + * @return boolean True valid, false if not. + */ + protected function checkIp4($requestIp, $ip) + { + if (false !== strpos($ip, '/')) { + list($address, $netmask) = explode('/', $ip, 2); + + if ($netmask < 1 || $netmask > 32) { + return false; + } + } else { + $address = $ip; + $netmask = 32; + } + + return 0 === substr_compare(sprintf('%032b', ip2long($requestIp)), sprintf('%032b', ip2long($address)), 0, $netmask); + } + + /** + * Validates an IPv6 address. + * + * @author David Soria Parra + * @see https://github.com/dsp/v6tools + * + * @param string $requestIp + * @param string $ip + * + * @return boolean True valid, false if not. + */ + protected function checkIp6($requestIp, $ip) + { + if (!defined('AF_INET6')) { + throw new \RuntimeException('Unable to check Ipv6. Check that PHP was not compiled with option "disable-ipv6".'); + } + + list($address, $netmask) = explode('/', $ip, 2); + + $bytesAddr = unpack("n*", inet_pton($address)); + $bytesTest = unpack("n*", inet_pton($requestIp)); + + for ($i = 1, $ceil = ceil($netmask / 16); $i <= $ceil; $i++) { + $left = $netmask - 16 * ($i-1); + $left = ($left <= 16) ? $left : 16; + $mask = ~(0xffff >> $left) & 0xffff; + if (($bytesAddr[$i] & $mask) != ($bytesTest[$i] & $mask)) { + return false; + } + } + + return true; + } +} diff --git a/app/laravel/vendor/Symfony/Component/HttpFoundation/RequestMatcherInterface.php b/app/laravel/vendor/Symfony/Component/HttpFoundation/RequestMatcherInterface.php new file mode 100755 index 000000000..506ec7940 --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/HttpFoundation/RequestMatcherInterface.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +/** + * RequestMatcherInterface is an interface for strategies to match a Request. + * + * @author Fabien Potencier + * + * @api + */ +interface RequestMatcherInterface +{ + /** + * Decides whether the rule(s) implemented by the strategy matches the supplied request. + * + * @param Request $request The request to check for a match + * + * @return Boolean true if the request matches, false otherwise + * + * @api + */ + function matches(Request $request); +} diff --git a/app/laravel/vendor/Symfony/Component/HttpFoundation/Resources/stubs/SessionHandlerInterface.php b/app/laravel/vendor/Symfony/Component/HttpFoundation/Resources/stubs/SessionHandlerInterface.php new file mode 100755 index 000000000..4378b814b --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/HttpFoundation/Resources/stubs/SessionHandlerInterface.php @@ -0,0 +1,98 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * SessionHandlerInterface + * + * Provides forward compatability with PHP 5.4 + * + * Extensive documentation can be found at php.net, see links: + * + * @see http://php.net/sessionhandlerinterface + * @see http://php.net/session.customhandler + * @see http://php.net/session-set-save-handler + * + * @author Drak + */ +interface SessionHandlerInterface +{ + /** + * Open session. + * + * @see http://php.net/sessionhandlerinterface.open + * + * @param string $savePath Save path. + * @param string $sessionName Session Name. + * + * @throws \RuntimeException If something goes wrong starting the session. + * + * @return boolean + */ + function open($savePath, $sessionName); + + /** + * Close session. + * + * @see http://php.net/sessionhandlerinterface.close + * + * @return boolean + */ + function close(); + + /** + * Read session. + * + * @see http://php.net/sessionhandlerinterface.read + * + * @throws \RuntimeException On fatal error but not "record not found". + * + * @return string String as stored in persistent storage or empty string in all other cases. + */ + function read($sessionId); + + /** + * Commit session to storage. + * + * @see http://php.net/sessionhandlerinterface.write + * + * @param string $sessionId Session ID. + * @param string $data Session serialized data to save. + * + * @return boolean + */ + function write($sessionId, $data); + + /** + * Destroys this session. + * + * @see http://php.net/sessionhandlerinterface.destroy + * + * @param string $sessionId Session ID. + * + * @throws \RuntimeException On fatal error. + * + * @return boolean + */ + function destroy($sessionId); + + /** + * Garbage collection for storage. + * + * @see http://php.net/sessionhandlerinterface.gc + * + * @param integer $lifetime Max lifetime in seconds to keep sessions stored. + * + * @throws \RuntimeException On fatal error. + * + * @return boolean + */ + function gc($lifetime); +} diff --git a/app/laravel/vendor/Symfony/Component/HttpFoundation/Response.php b/app/laravel/vendor/Symfony/Component/HttpFoundation/Response.php new file mode 100755 index 000000000..3a0a22e4a --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/HttpFoundation/Response.php @@ -0,0 +1,1112 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +/** + * Response represents an HTTP response. + * + * @author Fabien Potencier + * + * @api + */ +class Response +{ + /** + * @var \Symfony\Component\HttpFoundation\ResponseHeaderBag + */ + public $headers; + + /** + * @var string + */ + protected $content; + + /** + * @var string + */ + protected $version; + + /** + * @var integer + */ + protected $statusCode; + + /** + * @var string + */ + protected $statusText; + + /** + * @var string + */ + protected $charset; + + /** + * Status codes translation table. + * + * The list of codes is complete according to the + * {@link http://www.iana.org/assignments/http-status-codes/ Hypertext Transfer Protocol (HTTP) Status Code Registry} + * (last updated 2012-02-13). + * + * Unless otherwise noted, the status code is defined in RFC2616. + * + * @var array + */ + static public $statusTexts = array( + 100 => 'Continue', + 101 => 'Switching Protocols', + 102 => 'Processing', // RFC2518 + 200 => 'OK', + 201 => 'Created', + 202 => 'Accepted', + 203 => 'Non-Authoritative Information', + 204 => 'No Content', + 205 => 'Reset Content', + 206 => 'Partial Content', + 207 => 'Multi-Status', // RFC4918 + 208 => 'Already Reported', // RFC5842 + 226 => 'IM Used', // RFC3229 + 300 => 'Multiple Choices', + 301 => 'Moved Permanently', + 302 => 'Found', + 303 => 'See Other', + 304 => 'Not Modified', + 305 => 'Use Proxy', + 306 => 'Reserved', + 307 => 'Temporary Redirect', + 400 => 'Bad Request', + 401 => 'Unauthorized', + 402 => 'Payment Required', + 403 => 'Forbidden', + 404 => 'Not Found', + 405 => 'Method Not Allowed', + 406 => 'Not Acceptable', + 407 => 'Proxy Authentication Required', + 408 => 'Request Timeout', + 409 => 'Conflict', + 410 => 'Gone', + 411 => 'Length Required', + 412 => 'Precondition Failed', + 413 => 'Request Entity Too Large', + 414 => 'Request-URI Too Long', + 415 => 'Unsupported Media Type', + 416 => 'Requested Range Not Satisfiable', + 417 => 'Expectation Failed', + 418 => 'I\'m a teapot', + 422 => 'Unprocessable Entity', // RFC4918 + 423 => 'Locked', // RFC4918 + 424 => 'Failed Dependency', // RFC4918 + 425 => 'Reserved for WebDAV advanced collections expired proposal', // RFC2817 + 426 => 'Upgrade Required', // RFC2817 + 428 => 'Precondition Required', // RFC-nottingham-http-new-status-04 + 429 => 'Too Many Requests', // RFC-nottingham-http-new-status-04 + 431 => 'Request Header Fields Too Large', // RFC-nottingham-http-new-status-04 + 500 => 'Internal Server Error', + 501 => 'Not Implemented', + 502 => 'Bad Gateway', + 503 => 'Service Unavailable', + 504 => 'Gateway Timeout', + 505 => 'HTTP Version Not Supported', + 506 => 'Variant Also Negotiates (Experimental)', // [RFC2295] + 507 => 'Insufficient Storage', // RFC4918 + 508 => 'Loop Detected', // RFC5842 + 510 => 'Not Extended', // RFC2774 + 511 => 'Network Authentication Required', // RFC-nottingham-http-new-status-04 + ); + + /** + * Constructor. + * + * @param string $content The response content + * @param integer $status The response status code + * @param array $headers An array of response headers + * + * @api + */ + public function __construct($content = '', $status = 200, $headers = array()) + { + $this->headers = new ResponseHeaderBag($headers); + $this->setContent($content); + $this->setStatusCode($status); + $this->setProtocolVersion('1.0'); + if (!$this->headers->has('Date')) { + $this->setDate(new \DateTime(null, new \DateTimeZone('UTC'))); + } + } + + /** + * Factory method for chainability + * + * Example: + * + * return Response::create($body, 200) + * ->setSharedMaxAge(300); + * + * @param string $content The response content + * @param integer $status The response status code + * @param array $headers An array of response headers + * + * @return Response + */ + static public function create($content = '', $status = 200, $headers = array()) + { + return new static($content, $status, $headers); + } + + /** + * Returns the Response as an HTTP string. + * + * The string representation of the Resonse is the same as the + * one that will be sent to the client only if the prepare() method + * has been called before. + * + * @return string The Response as an HTTP string + * + * @see prepare() + */ + public function __toString() + { + return + sprintf('HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText)."\r\n". + $this->headers."\r\n". + $this->getContent(); + } + + /** + * Clones the current Response instance. + */ + public function __clone() + { + $this->headers = clone $this->headers; + } + + /** + * Prepares the Response before it is sent to the client. + * + * This method tweaks the Response to ensure that it is + * compliant with RFC 2616. Most of the changes are based on + * the Request that is "associated" with this Response. + * + * @param Request $request A Request instance + */ + public function prepare(Request $request) + { + $headers = $this->headers; + + if ($this->isInformational() || in_array($this->statusCode, array(204, 304))) { + $this->setContent(''); + } + + // Content-type based on the Request + if (!$headers->has('Content-Type')) { + $format = $request->getRequestFormat(); + if (null !== $format && $mimeType = $request->getMimeType($format)) { + $headers->set('Content-Type', $mimeType); + } + } + + // Fix Content-Type + $charset = $this->charset ?: 'UTF-8'; + if (!$headers->has('Content-Type')) { + $headers->set('Content-Type', 'text/html; charset='.$charset); + } elseif (0 === strpos($headers->get('Content-Type'), 'text/') && false === strpos($headers->get('Content-Type'), 'charset')) { + // add the charset + $headers->set('Content-Type', $headers->get('Content-Type').'; charset='.$charset); + } + + // Fix Content-Length + if ($headers->has('Transfer-Encoding')) { + $headers->remove('Content-Length'); + } + + if ('HEAD' === $request->getMethod()) { + // cf. RFC2616 14.13 + $length = $headers->get('Content-Length'); + $this->setContent(''); + if ($length) { + $headers->set('Content-Length', $length); + } + } + } + + /** + * Sends HTTP headers. + * + * @return Response + */ + public function sendHeaders() + { + // headers have already been sent by the developer + if (headers_sent()) { + return $this; + } + + // status + header(sprintf('HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText)); + + // headers + foreach ($this->headers->all() as $name => $values) { + foreach ($values as $value) { + header($name.': '.$value, false); + } + } + + // cookies + foreach ($this->headers->getCookies() as $cookie) { + setcookie($cookie->getName(), $cookie->getValue(), $cookie->getExpiresTime(), $cookie->getPath(), $cookie->getDomain(), $cookie->isSecure(), $cookie->isHttpOnly()); + } + + return $this; + } + + /** + * Sends content for the current web response. + * + * @return Response + */ + public function sendContent() + { + echo $this->content; + + return $this; + } + + /** + * Sends HTTP headers and content. + * + * @return Response + * + * @api + */ + public function send() + { + $this->sendHeaders(); + $this->sendContent(); + + if (function_exists('fastcgi_finish_request')) { + fastcgi_finish_request(); + } + + return $this; + } + + /** + * Sets the response content. + * + * Valid types are strings, numbers, and objects that implement a __toString() method. + * + * @param mixed $content + * + * @return Response + * + * @api + */ + public function setContent($content) + { + if (null !== $content && !is_string($content) && !is_numeric($content) && !is_callable(array($content, '__toString'))) { + throw new \UnexpectedValueException('The Response content must be a string or object implementing __toString(), "'.gettype($content).'" given.'); + } + + $this->content = (string) $content; + + return $this; + } + + /** + * Gets the current response content. + * + * @return string Content + * + * @api + */ + public function getContent() + { + return $this->content; + } + + /** + * Sets the HTTP protocol version (1.0 or 1.1). + * + * @param string $version The HTTP protocol version + * + * @return Response + * + * @api + */ + public function setProtocolVersion($version) + { + $this->version = $version; + + return $this; + } + + /** + * Gets the HTTP protocol version. + * + * @return string The HTTP protocol version + * + * @api + */ + public function getProtocolVersion() + { + return $this->version; + } + + /** + * Sets the response status code. + * + * @param integer $code HTTP status code + * @param string $text HTTP status text + * + * @return Response + * + * @throws \InvalidArgumentException When the HTTP status code is not valid + * + * @api + */ + public function setStatusCode($code, $text = null) + { + $this->statusCode = (int) $code; + if ($this->isInvalid()) { + throw new \InvalidArgumentException(sprintf('The HTTP status code "%s" is not valid.', $code)); + } + + $this->statusText = false === $text ? '' : (null === $text ? self::$statusTexts[$this->statusCode] : $text); + + return $this; + } + + /** + * Retrieves the status code for the current web response. + * + * @return string Status code + * + * @api + */ + public function getStatusCode() + { + return $this->statusCode; + } + + /** + * Sets the response charset. + * + * @param string $charset Character set + * + * @return Response + * + * @api + */ + public function setCharset($charset) + { + $this->charset = $charset; + + return $this; + } + + /** + * Retrieves the response charset. + * + * @return string Character set + * + * @api + */ + public function getCharset() + { + return $this->charset; + } + + /** + * Returns true if the response is worth caching under any circumstance. + * + * Responses marked "private" with an explicit Cache-Control directive are + * considered uncacheable. + * + * Responses with neither a freshness lifetime (Expires, max-age) nor cache + * validator (Last-Modified, ETag) are considered uncacheable. + * + * @return Boolean true if the response is worth caching, false otherwise + * + * @api + */ + public function isCacheable() + { + if (!in_array($this->statusCode, array(200, 203, 300, 301, 302, 404, 410))) { + return false; + } + + if ($this->headers->hasCacheControlDirective('no-store') || $this->headers->getCacheControlDirective('private')) { + return false; + } + + return $this->isValidateable() || $this->isFresh(); + } + + /** + * Returns true if the response is "fresh". + * + * Fresh responses may be served from cache without any interaction with the + * origin. A response is considered fresh when it includes a Cache-Control/max-age + * indicator or Expiration header and the calculated age is less than the freshness lifetime. + * + * @return Boolean true if the response is fresh, false otherwise + * + * @api + */ + public function isFresh() + { + return $this->getTtl() > 0; + } + + /** + * Returns true if the response includes headers that can be used to validate + * the response with the origin server using a conditional GET request. + * + * @return Boolean true if the response is validateable, false otherwise + * + * @api + */ + public function isValidateable() + { + return $this->headers->has('Last-Modified') || $this->headers->has('ETag'); + } + + /** + * Marks the response as "private". + * + * It makes the response ineligible for serving other clients. + * + * @return Response + * + * @api + */ + public function setPrivate() + { + $this->headers->removeCacheControlDirective('public'); + $this->headers->addCacheControlDirective('private'); + + return $this; + } + + /** + * Marks the response as "public". + * + * It makes the response eligible for serving other clients. + * + * @return Response + * + * @api + */ + public function setPublic() + { + $this->headers->addCacheControlDirective('public'); + $this->headers->removeCacheControlDirective('private'); + + return $this; + } + + /** + * Returns true if the response must be revalidated by caches. + * + * This method indicates that the response must not be served stale by a + * cache in any circumstance without first revalidating with the origin. + * When present, the TTL of the response should not be overridden to be + * greater than the value provided by the origin. + * + * @return Boolean true if the response must be revalidated by a cache, false otherwise + * + * @api + */ + public function mustRevalidate() + { + return $this->headers->hasCacheControlDirective('must-revalidate') || $this->headers->has('must-proxy-revalidate'); + } + + /** + * Returns the Date header as a DateTime instance. + * + * @return \DateTime A \DateTime instance + * + * @throws \RuntimeException When the header is not parseable + * + * @api + */ + public function getDate() + { + return $this->headers->getDate('Date'); + } + + /** + * Sets the Date header. + * + * @param \DateTime $date A \DateTime instance + * + * @return Response + * + * @api + */ + public function setDate(\DateTime $date) + { + $date->setTimezone(new \DateTimeZone('UTC')); + $this->headers->set('Date', $date->format('D, d M Y H:i:s').' GMT'); + + return $this; + } + + /** + * Returns the age of the response. + * + * @return integer The age of the response in seconds + */ + public function getAge() + { + if ($age = $this->headers->get('Age')) { + return $age; + } + + return max(time() - $this->getDate()->format('U'), 0); + } + + /** + * Marks the response stale by setting the Age header to be equal to the maximum age of the response. + * + * @return Response + * + * @api + */ + public function expire() + { + if ($this->isFresh()) { + $this->headers->set('Age', $this->getMaxAge()); + } + + return $this; + } + + /** + * Returns the value of the Expires header as a DateTime instance. + * + * @return \DateTime A DateTime instance + * + * @api + */ + public function getExpires() + { + return $this->headers->getDate('Expires'); + } + + /** + * Sets the Expires HTTP header with a DateTime instance. + * + * If passed a null value, it removes the header. + * + * @param \DateTime $date A \DateTime instance + * + * @return Response + * + * @api + */ + public function setExpires(\DateTime $date = null) + { + if (null === $date) { + $this->headers->remove('Expires'); + } else { + $date = clone $date; + $date->setTimezone(new \DateTimeZone('UTC')); + $this->headers->set('Expires', $date->format('D, d M Y H:i:s').' GMT'); + } + + return $this; + } + + /** + * Sets the number of seconds after the time specified in the response's Date + * header when the the response should no longer be considered fresh. + * + * First, it checks for a s-maxage directive, then a max-age directive, and then it falls + * back on an expires header. It returns null when no maximum age can be established. + * + * @return integer|null Number of seconds + * + * @api + */ + public function getMaxAge() + { + if ($age = $this->headers->getCacheControlDirective('s-maxage')) { + return $age; + } + + if ($age = $this->headers->getCacheControlDirective('max-age')) { + return $age; + } + + if (null !== $this->getExpires()) { + return $this->getExpires()->format('U') - $this->getDate()->format('U'); + } + + return null; + } + + /** + * Sets the number of seconds after which the response should no longer be considered fresh. + * + * This methods sets the Cache-Control max-age directive. + * + * @param integer $value Number of seconds + * + * @return Response + * + * @api + */ + public function setMaxAge($value) + { + $this->headers->addCacheControlDirective('max-age', $value); + + return $this; + } + + /** + * Sets the number of seconds after which the response should no longer be considered fresh by shared caches. + * + * This methods sets the Cache-Control s-maxage directive. + * + * @param integer $value Number of seconds + * + * @return Response + * + * @api + */ + public function setSharedMaxAge($value) + { + $this->setPublic(); + $this->headers->addCacheControlDirective('s-maxage', $value); + + return $this; + } + + /** + * Returns the response's time-to-live in seconds. + * + * It returns null when no freshness information is present in the response. + * + * When the responses TTL is <= 0, the response may not be served from cache without first + * revalidating with the origin. + * + * @return integer The TTL in seconds + * + * @api + */ + public function getTtl() + { + if ($maxAge = $this->getMaxAge()) { + return $maxAge - $this->getAge(); + } + + return null; + } + + /** + * Sets the response's time-to-live for shared caches. + * + * This method adjusts the Cache-Control/s-maxage directive. + * + * @param integer $seconds Number of seconds + * + * @return Response + * + * @api + */ + public function setTtl($seconds) + { + $this->setSharedMaxAge($this->getAge() + $seconds); + + return $this; + } + + /** + * Sets the response's time-to-live for private/client caches. + * + * This method adjusts the Cache-Control/max-age directive. + * + * @param integer $seconds Number of seconds + * + * @return Response + * + * @api + */ + public function setClientTtl($seconds) + { + $this->setMaxAge($this->getAge() + $seconds); + + return $this; + } + + /** + * Returns the Last-Modified HTTP header as a DateTime instance. + * + * @return \DateTime A DateTime instance + * + * @api + */ + public function getLastModified() + { + return $this->headers->getDate('Last-Modified'); + } + + /** + * Sets the Last-Modified HTTP header with a DateTime instance. + * + * If passed a null value, it removes the header. + * + * @param \DateTime $date A \DateTime instance + * + * @return Response + * + * @api + */ + public function setLastModified(\DateTime $date = null) + { + if (null === $date) { + $this->headers->remove('Last-Modified'); + } else { + $date = clone $date; + $date->setTimezone(new \DateTimeZone('UTC')); + $this->headers->set('Last-Modified', $date->format('D, d M Y H:i:s').' GMT'); + } + + return $this; + } + + /** + * Returns the literal value of the ETag HTTP header. + * + * @return string The ETag HTTP header + * + * @api + */ + public function getEtag() + { + return $this->headers->get('ETag'); + } + + /** + * Sets the ETag value. + * + * @param string $etag The ETag unique identifier + * @param Boolean $weak Whether you want a weak ETag or not + * + * @return Response + * + * @api + */ + public function setEtag($etag = null, $weak = false) + { + if (null === $etag) { + $this->headers->remove('Etag'); + } else { + if (0 !== strpos($etag, '"')) { + $etag = '"'.$etag.'"'; + } + + $this->headers->set('ETag', (true === $weak ? 'W/' : '').$etag); + } + + return $this; + } + + /** + * Sets the response's cache headers (validation and/or expiration). + * + * Available options are: etag, last_modified, max_age, s_maxage, private, and public. + * + * @param array $options An array of cache options + * + * @return Response + * + * @api + */ + public function setCache(array $options) + { + if ($diff = array_diff(array_keys($options), array('etag', 'last_modified', 'max_age', 's_maxage', 'private', 'public'))) { + throw new \InvalidArgumentException(sprintf('Response does not support the following options: "%s".', implode('", "', array_values($diff)))); + } + + if (isset($options['etag'])) { + $this->setEtag($options['etag']); + } + + if (isset($options['last_modified'])) { + $this->setLastModified($options['last_modified']); + } + + if (isset($options['max_age'])) { + $this->setMaxAge($options['max_age']); + } + + if (isset($options['s_maxage'])) { + $this->setSharedMaxAge($options['s_maxage']); + } + + if (isset($options['public'])) { + if ($options['public']) { + $this->setPublic(); + } else { + $this->setPrivate(); + } + } + + if (isset($options['private'])) { + if ($options['private']) { + $this->setPrivate(); + } else { + $this->setPublic(); + } + } + + return $this; + } + + /** + * Modifies the response so that it conforms to the rules defined for a 304 status code. + * + * This sets the status, removes the body, and discards any headers + * that MUST NOT be included in 304 responses. + * + * @return Response + * + * @see http://tools.ietf.org/html/rfc2616#section-10.3.5 + * + * @api + */ + public function setNotModified() + { + $this->setStatusCode(304); + $this->setContent(null); + + // remove headers that MUST NOT be included with 304 Not Modified responses + foreach (array('Allow', 'Content-Encoding', 'Content-Language', 'Content-Length', 'Content-MD5', 'Content-Type', 'Last-Modified') as $header) { + $this->headers->remove($header); + } + + return $this; + } + + /** + * Returns true if the response includes a Vary header. + * + * @return Boolean true if the response includes a Vary header, false otherwise + * + * @api + */ + public function hasVary() + { + return (Boolean) $this->headers->get('Vary'); + } + + /** + * Returns an array of header names given in the Vary header. + * + * @return array An array of Vary names + * + * @api + */ + public function getVary() + { + if (!$vary = $this->headers->get('Vary')) { + return array(); + } + + return is_array($vary) ? $vary : preg_split('/[\s,]+/', $vary); + } + + /** + * Sets the Vary header. + * + * @param string|array $headers + * @param Boolean $replace Whether to replace the actual value of not (true by default) + * + * @return Response + * + * @api + */ + public function setVary($headers, $replace = true) + { + $this->headers->set('Vary', $headers, $replace); + + return $this; + } + + /** + * Determines if the Response validators (ETag, Last-Modified) match + * a conditional value specified in the Request. + * + * If the Response is not modified, it sets the status code to 304 and + * removes the actual content by calling the setNotModified() method. + * + * @param Request $request A Request instance + * + * @return Boolean true if the Response validators match the Request, false otherwise + * + * @api + */ + public function isNotModified(Request $request) + { + $lastModified = $request->headers->get('If-Modified-Since'); + $notModified = false; + if ($etags = $request->getEtags()) { + $notModified = (in_array($this->getEtag(), $etags) || in_array('*', $etags)) && (!$lastModified || $this->headers->get('Last-Modified') == $lastModified); + } elseif ($lastModified) { + $notModified = $lastModified == $this->headers->get('Last-Modified'); + } + + if ($notModified) { + $this->setNotModified(); + } + + return $notModified; + } + + // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html + /** + * Is response invalid? + * + * @return Boolean + * + * @api + */ + public function isInvalid() + { + return $this->statusCode < 100 || $this->statusCode >= 600; + } + + /** + * Is response informative? + * + * @return Boolean + * + * @api + */ + public function isInformational() + { + return $this->statusCode >= 100 && $this->statusCode < 200; + } + + /** + * Is response successful? + * + * @return Boolean + * + * @api + */ + public function isSuccessful() + { + return $this->statusCode >= 200 && $this->statusCode < 300; + } + + /** + * Is the response a redirect? + * + * @return Boolean + * + * @api + */ + public function isRedirection() + { + return $this->statusCode >= 300 && $this->statusCode < 400; + } + + /** + * Is there a client error? + * + * @return Boolean + * + * @api + */ + public function isClientError() + { + return $this->statusCode >= 400 && $this->statusCode < 500; + } + + /** + * Was there a server side error? + * + * @return Boolean + * + * @api + */ + public function isServerError() + { + return $this->statusCode >= 500 && $this->statusCode < 600; + } + + /** + * Is the response OK? + * + * @return Boolean + * + * @api + */ + public function isOk() + { + return 200 === $this->statusCode; + } + + /** + * Is the reponse forbidden? + * + * @return Boolean + * + * @api + */ + public function isForbidden() + { + return 403 === $this->statusCode; + } + + /** + * Is the response a not found error? + * + * @return Boolean + * + * @api + */ + public function isNotFound() + { + return 404 === $this->statusCode; + } + + /** + * Is the response a redirect of some form? + * + * @param string $location + * + * @return Boolean + * + * @api + */ + public function isRedirect($location = null) + { + return in_array($this->statusCode, array(201, 301, 302, 303, 307)) && (null === $location ?: $location == $this->headers->get('Location')); + } + + /** + * Is the response empty? + * + * @return Boolean + * + * @api + */ + public function isEmpty() + { + return in_array($this->statusCode, array(201, 204, 304)); + } +} diff --git a/app/laravel/vendor/Symfony/Component/HttpFoundation/ResponseHeaderBag.php b/app/laravel/vendor/Symfony/Component/HttpFoundation/ResponseHeaderBag.php new file mode 100755 index 000000000..11615b96c --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/HttpFoundation/ResponseHeaderBag.php @@ -0,0 +1,293 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +/** + * ResponseHeaderBag is a container for Response HTTP headers. + * + * @author Fabien Potencier + * + * @api + */ +class ResponseHeaderBag extends HeaderBag +{ + const COOKIES_FLAT = 'flat'; + const COOKIES_ARRAY = 'array'; + + const DISPOSITION_ATTACHMENT = 'attachment'; + const DISPOSITION_INLINE = 'inline'; + + /** + * @var array + */ + protected $computedCacheControl = array(); + + /** + * @var array + */ + protected $cookies = array(); + + /** + * Constructor. + * + * @param array $headers An array of HTTP headers + * + * @api + */ + public function __construct(array $headers = array()) + { + parent::__construct($headers); + + if (!isset($this->headers['cache-control'])) { + $this->set('cache-control', ''); + } + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + $cookies = ''; + foreach ($this->getCookies() as $cookie) { + $cookies .= 'Set-Cookie: '.$cookie."\r\n"; + } + + return parent::__toString().$cookies; + } + + /** + * {@inheritdoc} + * + * @api + */ + public function replace(array $headers = array()) + { + parent::replace($headers); + + if (!isset($this->headers['cache-control'])) { + $this->set('cache-control', ''); + } + } + + /** + * {@inheritdoc} + * + * @api + */ + public function set($key, $values, $replace = true) + { + parent::set($key, $values, $replace); + + // ensure the cache-control header has sensible defaults + if (in_array(strtr(strtolower($key), '_', '-'), array('cache-control', 'etag', 'last-modified', 'expires'))) { + $computed = $this->computeCacheControlValue(); + $this->headers['cache-control'] = array($computed); + $this->computedCacheControl = $this->parseCacheControl($computed); + } + } + + /** + * {@inheritdoc} + * + * @api + */ + public function remove($key) + { + parent::remove($key); + + if ('cache-control' === strtr(strtolower($key), '_', '-')) { + $this->computedCacheControl = array(); + } + } + + /** + * {@inheritdoc} + */ + public function hasCacheControlDirective($key) + { + return array_key_exists($key, $this->computedCacheControl); + } + + /** + * {@inheritdoc} + */ + public function getCacheControlDirective($key) + { + return array_key_exists($key, $this->computedCacheControl) ? $this->computedCacheControl[$key] : null; + } + + /** + * Sets a cookie. + * + * @param Cookie $cookie + * + * @api + */ + public function setCookie(Cookie $cookie) + { + $this->cookies[$cookie->getDomain()][$cookie->getPath()][$cookie->getName()] = $cookie; + } + + /** + * Removes a cookie from the array, but does not unset it in the browser + * + * @param string $name + * @param string $path + * @param string $domain + * + * @api + */ + public function removeCookie($name, $path = '/', $domain = null) + { + if (null === $path) { + $path = '/'; + } + + unset($this->cookies[$domain][$path][$name]); + + if (empty($this->cookies[$domain][$path])) { + unset($this->cookies[$domain][$path]); + + if (empty($this->cookies[$domain])) { + unset($this->cookies[$domain]); + } + } + } + + /** + * Returns an array with all cookies + * + * @param string $format + * + * @throws \InvalidArgumentException When the $format is invalid + * + * @return array + * + * @api + */ + public function getCookies($format = self::COOKIES_FLAT) + { + if (!in_array($format, array(self::COOKIES_FLAT, self::COOKIES_ARRAY))) { + throw new \InvalidArgumentException(sprintf('Format "%s" invalid (%s).', $format, implode(', ', array(self::COOKIES_FLAT, self::COOKIES_ARRAY)))); + } + + if (self::COOKIES_ARRAY === $format) { + return $this->cookies; + } + + $flattenedCookies = array(); + foreach ($this->cookies as $path) { + foreach ($path as $cookies) { + foreach ($cookies as $cookie) { + $flattenedCookies[] = $cookie; + } + } + } + + return $flattenedCookies; + } + + /** + * Clears a cookie in the browser + * + * @param string $name + * @param string $path + * @param string $domain + * + * @api + */ + public function clearCookie($name, $path = '/', $domain = null) + { + $this->setCookie(new Cookie($name, null, 1, $path, $domain)); + } + + /** + * Generates a HTTP Content-Disposition field-value. + * + * @param string $disposition One of "inline" or "attachment" + * @param string $filename A unicode string + * @param string $filenameFallback A string containing only ASCII characters that + * is semantically equivalent to $filename. If the filename is already ASCII, + * it can be omitted, or just copied from $filename + * + * @return string A string suitable for use as a Content-Disposition field-value. + * + * @throws \InvalidArgumentException + * @see RFC 6266 + */ + public function makeDisposition($disposition, $filename, $filenameFallback = '') + { + if (!in_array($disposition, array(self::DISPOSITION_ATTACHMENT, self::DISPOSITION_INLINE))) { + throw new \InvalidArgumentException(sprintf('The disposition must be either "%s" or "%s".', self::DISPOSITION_ATTACHMENT, self::DISPOSITION_INLINE)); + } + + if (!$filenameFallback) { + $filenameFallback = $filename; + } + + // filenameFallback is not ASCII. + if (!preg_match('/^[\x20-\x7e]*$/', $filenameFallback)) { + throw new \InvalidArgumentException('The filename fallback must only contain ASCII characters.'); + } + + // percent characters aren't safe in fallback. + if (false !== strpos($filenameFallback, '%')) { + throw new \InvalidArgumentException('The filename fallback cannot contain the "%" character.'); + } + + // path separators aren't allowed in either. + if (preg_match('#[/\\\\]#', $filename) || preg_match('#[/\\\\]#', $filenameFallback)) { + throw new \InvalidArgumentException('The filename and the fallback cannot contain the "/" and "\\" characters.'); + } + + $output = sprintf('%s; filename="%s"', $disposition, str_replace(array('\\', '"'), array('\\\\', '\\"'), $filenameFallback)); + + if ($filename != $filenameFallback) { + $output .= sprintf("; filename*=utf-8''%s", str_replace(array("'", '(', ')', '*'), array('%27', '%28', '%29', '%2A'), urlencode($filename))); + } + + return $output; + } + + /** + * Returns the calculated value of the cache-control header. + * + * This considers several other headers and calculates or modifies the + * cache-control header to a sensible, conservative value. + * + * @return string + */ + protected function computeCacheControlValue() + { + if (!$this->cacheControl && !$this->has('ETag') && !$this->has('Last-Modified') && !$this->has('Expires')) { + return 'no-cache'; + } + + if (!$this->cacheControl) { + // conservative by default + return 'private, must-revalidate'; + } + + $header = $this->getCacheControlHeader(); + if (isset($this->cacheControl['public']) || isset($this->cacheControl['private'])) { + return $header; + } + + // public if s-maxage is defined, private otherwise + if (!isset($this->cacheControl['s-maxage'])) { + return $header.', private'; + } + + return $header; + } +} diff --git a/app/laravel/vendor/Symfony/Component/HttpFoundation/ServerBag.php b/app/laravel/vendor/Symfony/Component/HttpFoundation/ServerBag.php new file mode 100755 index 000000000..9b57f9ee0 --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/HttpFoundation/ServerBag.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +/** + * ServerBag is a container for HTTP headers from the $_SERVER variable. + * + * @author Fabien Potencier + * @author Bulat Shakirzyanov + */ +class ServerBag extends ParameterBag +{ + /** + * Gets the HTTP headers. + * + * @return string + */ + public function getHeaders() + { + $headers = array(); + foreach ($this->parameters as $key => $value) { + if (0 === strpos($key, 'HTTP_')) { + $headers[substr($key, 5)] = $value; + } + // CONTENT_* are not prefixed with HTTP_ + elseif (in_array($key, array('CONTENT_LENGTH', 'CONTENT_MD5', 'CONTENT_TYPE'))) { + $headers[$key] = $this->parameters[$key]; + } + } + + // PHP_AUTH_USER/PHP_AUTH_PW + if (isset($this->parameters['PHP_AUTH_USER'])) { + $pass = isset($this->parameters['PHP_AUTH_PW']) ? $this->parameters['PHP_AUTH_PW'] : ''; + $headers['AUTHORIZATION'] = 'Basic '.base64_encode($this->parameters['PHP_AUTH_USER'].':'.$pass); + } + + return $headers; + } +} diff --git a/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Attribute/AttributeBag.php b/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Attribute/AttributeBag.php new file mode 100755 index 000000000..d1bcb0ffb --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Attribute/AttributeBag.php @@ -0,0 +1,137 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Attribute; + +/** + * This class relates to session attribute storage + */ +class AttributeBag implements AttributeBagInterface +{ + private $name = 'attributes'; + + /** + * @var string + */ + private $storageKey; + + /** + * @var array + */ + protected $attributes = array(); + + /** + * Constructor. + * + * @param string $storageKey The key used to store flashes in the session. + */ + public function __construct($storageKey = '_sf2_attributes') + { + $this->storageKey = $storageKey; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return $this->name; + } + + public function setName($name) + { + $this->name = $name; + } + + /** + * {@inheritdoc} + */ + public function initialize(array &$attributes) + { + $this->attributes = &$attributes; + } + + /** + * {@inheritdoc} + */ + public function getStorageKey() + { + return $this->storageKey; + } + + /** + * {@inheritdoc} + */ + public function has($name) + { + return array_key_exists($name, $this->attributes); + } + + /** + * {@inheritdoc} + */ + public function get($name, $default = null) + { + return array_key_exists($name, $this->attributes) ? $this->attributes[$name] : $default; + } + + /** + * {@inheritdoc} + */ + public function set($name, $value) + { + $this->attributes[$name] = $value; + } + + /** + * {@inheritdoc} + */ + public function all() + { + return $this->attributes; + } + + /** + * {@inheritdoc} + */ + public function replace(array $attributes) + { + $this->attributes = array(); + foreach ($attributes as $key => $value) { + $this->set($key, $value); + } + } + + /** + * {@inheritdoc} + */ + public function remove($name) + { + $retval = null; + if (array_key_exists($name, $this->attributes)) { + $retval = $this->attributes[$name]; + unset($this->attributes[$name]); + } + + return $retval; + } + + /** + * {@inheritdoc} + */ + public function clear() + { + $return = $this->attributes; + $this->attributes = array(); + + return $return; + } +} diff --git a/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Attribute/AttributeBagInterface.php b/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Attribute/AttributeBagInterface.php new file mode 100755 index 000000000..ec6d93c02 --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Attribute/AttributeBagInterface.php @@ -0,0 +1,72 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Attribute; + +use Symfony\Component\HttpFoundation\Session\SessionBagInterface; + +/** + * Attributes store. + * + * @author Drak + */ +interface AttributeBagInterface extends SessionBagInterface +{ + /** + * Checks if an attribute is defined. + * + * @param string $name The attribute name + * + * @return Boolean true if the attribute is defined, false otherwise + */ + function has($name); + + /** + * Returns an attribute. + * + * @param string $name The attribute name + * @param mixed $default The default value if not found. + * + * @return mixed + */ + function get($name, $default = null); + + /** + * Sets an attribute. + * + * @param string $name + * @param mixed $value + */ + function set($name, $value); + + /** + * Returns attributes. + * + * @return array Attributes + */ + function all(); + + /** + * Sets attributes. + * + * @param array $attributes Attributes + */ + function replace(array $attributes); + + /** + * Removes an attribute. + * + * @param string $name + * + * @return mixed The removed value + */ + function remove($name); +} diff --git a/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Attribute/NamespacedAttributeBag.php b/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Attribute/NamespacedAttributeBag.php new file mode 100755 index 000000000..138aa3614 --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Attribute/NamespacedAttributeBag.php @@ -0,0 +1,154 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Attribute; + +/** + * This class provides structured storage of session attributes using + * a name spacing character in the key. + * + * @author Drak + */ +class NamespacedAttributeBag extends AttributeBag +{ + /** + * Namespace character. + * + * @var string + */ + private $namespaceCharacter; + + /** + * Constructor. + * + * @param string $storageKey Session storage key. + * @param string $namespaceCharacter Namespace character to use in keys. + */ + public function __construct($storageKey = '_sf2_attributes', $namespaceCharacter = '/') + { + $this->namespaceCharacter = $namespaceCharacter; + parent::__construct($storageKey); + } + + /** + * {@inheritdoc} + */ + public function has($name) + { + $attributes = $this->resolveAttributePath($name); + $name = $this->resolveKey($name); + + return array_key_exists($name, $attributes); + } + + /** + * {@inheritdoc} + */ + public function get($name, $default = null) + { + $attributes = $this->resolveAttributePath($name); + $name = $this->resolveKey($name); + + return array_key_exists($name, $attributes) ? $attributes[$name] : $default; + } + + /** + * {@inheritdoc} + */ + public function set($name, $value) + { + $attributes = & $this->resolveAttributePath($name, true); + $name = $this->resolveKey($name); + $attributes[$name] = $value; + } + + /** + * {@inheritdoc} + */ + public function remove($name) + { + $retval = null; + $attributes = & $this->resolveAttributePath($name); + $name = $this->resolveKey($name); + if (array_key_exists($name, $attributes)) { + $retval = $attributes[$name]; + unset($attributes[$name]); + } + + return $retval; + } + + /** + * Resolves a path in attributes property and returns it as a reference. + * + * This method allows structured namespacing of session attributes. + * + * @param string $name Key name + * @param boolean $writeContext Write context, default false + * + * @return array + */ + protected function &resolveAttributePath($name, $writeContext = false) + { + $array = & $this->attributes; + $name = (strpos($name, $this->namespaceCharacter) === 0) ? substr($name, 1) : $name; + + // Check if there is anything to do, else return + if (!$name) { + return $array; + } + + $parts = explode($this->namespaceCharacter, $name); + if (count($parts) < 2) { + if (!$writeContext) { + return $array; + } + + $array[$parts[0]] = array(); + + return $array; + } + + unset($parts[count($parts)-1]); + + foreach ($parts as $part) { + if (!array_key_exists($part, $array)) { + if (!$writeContext) { + return $array; + } + + $array[$part] = array(); + } + + $array = & $array[$part]; + } + + return $array; + } + + /** + * Resolves the key from the name. + * + * This is the last part in a dot separated string. + * + * @param string $name + * + * @return string + */ + protected function resolveKey($name) + { + if (strpos($name, $this->namespaceCharacter) !== false) { + $name = substr($name, strrpos($name, $this->namespaceCharacter)+1, strlen($name)); + } + + return $name; + } +} diff --git a/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Flash/AutoExpireFlashBag.php b/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Flash/AutoExpireFlashBag.php new file mode 100755 index 000000000..10257847e --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Flash/AutoExpireFlashBag.php @@ -0,0 +1,171 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Flash; + +/** + * AutoExpireFlashBag flash message container. + * + * @author Drak + */ +class AutoExpireFlashBag implements FlashBagInterface +{ + private $name = 'flashes'; + + /** + * Flash messages. + * + * @var array + */ + private $flashes = array(); + + /** + * The storage key for flashes in the session + * + * @var string + */ + private $storageKey; + + /** + * Constructor. + * + * @param string $storageKey The key used to store flashes in the session. + */ + public function __construct($storageKey = '_sf2_flashes') + { + $this->storageKey = $storageKey; + $this->flashes = array('display' => array(), 'new' => array()); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return $this->name; + } + + public function setName($name) + { + $this->name = $name; + } + + /** + * {@inheritdoc} + */ + public function initialize(array &$flashes) + { + $this->flashes = &$flashes; + + // The logic: messages from the last request will be stored in new, so we move them to previous + // This request we will show what is in 'display'. What is placed into 'new' this time round will + // be moved to display next time round. + $this->flashes['display'] = array_key_exists('new', $this->flashes) ? $this->flashes['new'] : array(); + $this->flashes['new'] = array(); + } + + /** + * {@inheritdoc} + */ + public function peek($type, $default = null) + { + return $this->has($type) ? $this->flashes['display'][$type] : $default; + } + + /** + * {@inheritdoc} + */ + public function peekAll() + { + return array_key_exists('display', $this->flashes) ? (array)$this->flashes['display'] : array(); + } + + /** + * {@inheritdoc} + */ + public function get($type, $default = null) + { + $return = $default; + + if (!$this->has($type)) { + return $return; + } + + if (isset($this->flashes['display'][$type])) { + $return = $this->flashes['display'][$type]; + unset($this->flashes['display'][$type]); + } + + return $return; + } + + /** + * {@inheritdoc} + */ + public function all() + { + $return = $this->flashes['display']; + $this->flashes = array('new' => array(), 'display' => array()); + + return $return; + } + + /** + * {@inheritdoc} + */ + public function setAll(array $messages) + { + $this->flashes['new'] = $messages; + } + + /** + * {@inheritdoc} + */ + public function set($type, $message) + { + $this->flashes['new'][$type] = $message; + } + + /** + * {@inheritdoc} + */ + public function has($type) + { + return array_key_exists($type, $this->flashes['display']); + } + + /** + * {@inheritdoc} + */ + public function keys() + { + return array_keys($this->flashes['display']); + } + + /** + * {@inheritdoc} + */ + public function getStorageKey() + { + return $this->storageKey; + } + + /** + * {@inheritdoc} + */ + public function clear() + { + $return = $this->all(); + $this->flashes = array('display' => array(), 'new' => array()); + + return $return; + } +} diff --git a/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Flash/FlashBag.php b/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Flash/FlashBag.php new file mode 100755 index 000000000..c0b4ee57a --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Flash/FlashBag.php @@ -0,0 +1,158 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Flash; + +/** + * FlashBag flash message container. + * + * @author Drak + */ +class FlashBag implements FlashBagInterface +{ + private $name = 'flashes'; + + /** + * Flash messages. + * + * @var array + */ + private $flashes = array(); + + /** + * The storage key for flashes in the session + * + * @var string + */ + private $storageKey; + + /** + * Constructor. + * + * @param string $storageKey The key used to store flashes in the session. + */ + public function __construct($storageKey = '_sf2_flashes') + { + $this->storageKey = $storageKey; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return $this->name; + } + + public function setName($name) + { + $this->name = $name; + } + + /** + * {@inheritdoc} + */ + public function initialize(array &$flashes) + { + $this->flashes = &$flashes; + } + + /** + * {@inheritdoc} + */ + public function peek($type, $default = null) + { + return $this->has($type) ? $this->flashes[$type] : $default; + } + + /** + * {@inheritdoc} + */ + public function peekAll() + { + return $this->flashes; + } + + /** + * {@inheritdoc} + */ + public function get($type, $default = null) + { + if (!$this->has($type)) { + return $default; + } + + $return = $this->flashes[$type]; + + unset($this->flashes[$type]); + + return $return; + } + + /** + * {@inheritdoc} + */ + public function all() + { + $return = $this->peekAll(); + $this->flashes = array(); + + return $return; + } + + /** + * {@inheritdoc} + */ + public function set($type, $message) + { + $this->flashes[$type] = $message; + } + + /** + * {@inheritdoc} + */ + public function setAll(array $messages) + { + $this->flashes = $messages; + } + + /** + * {@inheritdoc} + */ + public function has($type) + { + return array_key_exists($type, $this->flashes); + } + + /** + * {@inheritdoc} + */ + public function keys() + { + return array_keys($this->flashes); + } + + /** + * {@inheritdoc} + */ + public function getStorageKey() + { + return $this->storageKey; + } + + /** + * {@inheritdoc} + */ + public function clear() + { + return $this->all(); + } +} diff --git a/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Flash/FlashBagInterface.php b/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Flash/FlashBagInterface.php new file mode 100755 index 000000000..0c45d74bb --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Flash/FlashBagInterface.php @@ -0,0 +1,85 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Flash; + +use Symfony\Component\HttpFoundation\Session\SessionBagInterface; + +/** + * FlashBagInterface. + * + * @author Drak + */ +interface FlashBagInterface extends SessionBagInterface +{ + /** + * Registers a message for a given type. + * + * @param string $type + * @param string $message + */ + function set($type, $message); + + /** + * Gets flash message for a given type. + * + * @param string $type Message category type. + * @param string $default Default value if $type doee not exist. + * + * @return string + */ + function peek($type, $default = null); + + /** + * Gets all flash messages. + * + * @return array + */ + function peekAll(); + + /** + * Gets and clears flash from the stack. + * + * @param string $type + * @param string $default Default value if $type doee not exist. + * + * @return string + */ + function get($type, $default = null); + + /** + * Gets and clears flashes from the stack. + * + * @return array + */ + function all(); + + /** + * Sets all flash messages. + */ + function setAll(array $messages); + + /** + * Has flash messages for a given type? + * + * @param string $type + * + * @return boolean + */ + function has($type); + + /** + * Returns a list of all defined types. + * + * @return array + */ + function keys(); +} diff --git a/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Session.php b/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Session.php new file mode 100755 index 000000000..13c644887 --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Session.php @@ -0,0 +1,300 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session; + +use Symfony\Component\HttpFoundation\Session\Storage\SessionStorageInterface; +use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag; +use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBagInterface; +use Symfony\Component\HttpFoundation\Session\Flash\FlashBag; +use Symfony\Component\HttpFoundation\Session\Flash\FlashBagInterface; +use Symfony\Component\HttpFoundation\Session\SessionBagInterface; +use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage; + +/** + * Session. + * + * @author Fabien Potencier + * @author Drak + * + * @api + */ +class Session implements SessionInterface +{ + /** + * Storage driver. + * + * @var SessionStorageInterface + */ + protected $storage; + + /** + * @var string + */ + private $flashName; + + /** + * @var string + */ + private $attributeName; + + /** + * Constructor. + * + * @param SessionStorageInterface $storage A SessionStorageInterface instance. + * @param AttributeBagInterface $attributes An AttributeBagInterface instance, (defaults null for default AttributeBag) + * @param FlashBagInterface $flashes A FlashBagInterface instance (defaults null for default FlashBag) + */ + public function __construct(SessionStorageInterface $storage = null, AttributeBagInterface $attributes = null, FlashBagInterface $flashes = null) + { + $this->storage = $storage ?: new NativeSessionStorage(); + + $attributeBag = $attributes ?: new AttributeBag(); + $this->attributeName = $attributeBag->getName(); + $this->registerBag($attributeBag); + + $flashBag = $flashes ?: new FlashBag(); + $this->flashName = $flashBag->getName(); + $this->registerBag($flashBag); + } + + /** + * {@inheritdoc} + */ + public function start() + { + return $this->storage->start(); + } + + /** + * {@inheritdoc} + */ + public function has($name) + { + return $this->storage->getBag($this->attributeName)->has($name); + } + + /** + * {@inheritdoc} + */ + public function get($name, $default = null) + { + return $this->storage->getBag($this->attributeName)->get($name, $default); + } + + /** + * {@inheritdoc} + */ + public function set($name, $value) + { + $this->storage->getBag($this->attributeName)->set($name, $value); + } + + /** + * {@inheritdoc} + */ + public function all() + { + return $this->storage->getBag($this->attributeName)->all(); + } + + /** + * {@inheritdoc} + */ + public function replace(array $attributes) + { + $this->storage->getBag($this->attributeName)->replace($attributes); + } + + /** + * {@inheritdoc} + */ + public function remove($name) + { + return $this->storage->getBag($this->attributeName)->remove($name); + } + + /** + * {@inheritdoc} + */ + public function clear() + { + $this->storage->getBag($this->attributeName)->clear(); + } + + /** + * {@inheritdoc} + */ + public function invalidate() + { + $this->storage->clear(); + + return $this->storage->regenerate(true); + } + + /** + * {@inheritdoc} + */ + public function migrate($destroy = false) + { + return $this->storage->regenerate($destroy); + } + + /** + * {@inheritdoc} + */ + public function save() + { + $this->storage->save(); + } + + /** + * {@inheritdoc} + */ + public function getId() + { + return $this->storage->getId(); + } + + /** + * {@inheritdoc} + */ + public function setId($id) + { + $this->storage->setId($id); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return $this->storage->getName(); + } + + /** + * {@inheritdoc} + */ + public function setName($name) + { + $this->storage->setName($name); + } + + /** + * Registers a SessionBagInterface with the session. + * + * @param SessionBagInterface $bag + */ + public function registerBag(SessionBagInterface $bag) + { + $this->storage->registerBag($bag); + } + + /** + * Get's a bag instance. + * + * @param string $name + * + * @return SessionBagInterface + */ + public function getBag($name) + { + return $this->storage->getBag($name); + } + + /** + * Gets the flashbag interface. + * + * @return FlashBagInterface + */ + public function getFlashBag() + { + return $this->getBag($this->flashName); + } + + // the following methods are kept for compatibility with Symfony 2.0 (they will be removed for Symfony 2.3) + + /** + * @return array + * + * @deprecated since 2.1, will be removed from 2.3 + */ + public function getFlashes() + { + return $this->getBag('flashes')->all(); + } + + /** + * @param array $values + * + * @deprecated since 2.1, will be removed from 2.3 + */ + public function setFlashes($values) + { + $this->getBag('flashes')->setAll($values); + } + + /** + * @param string $name + * @param string $default + * + * @return string + * + * @deprecated since 2.1, will be removed from 2.3 + */ + public function getFlash($name, $default = null) + { + return $this->getBag('flashes')->get($name, $default); + } + + /** + * @param string $name + * @param string $value + * + * @deprecated since 2.1, will be removed from 2.3 + */ + public function setFlash($name, $value) + { + $this->getBag('flashes')->set($name, $value); + } + + /** + * @param string $name + * + * @return Boolean + * + * @deprecated since 2.1, will be removed from 2.3 + */ + public function hasFlash($name) + { + return $this->getBag('flashes')->has($name); + } + + /** + * @param string $name + * + * @deprecated since 2.1, will be removed from 2.3 + */ + public function removeFlash($name) + { + $this->getBag('flashes')->get($name); + } + + /** + * @return array + * + * @deprecated since 2.1, will be removed from 2.3 + */ + public function clearFlashes() + { + return $this->getBag('flashes')->clear(); + } +} diff --git a/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/SessionBagInterface.php b/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/SessionBagInterface.php new file mode 100755 index 000000000..50c2d4bd0 --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/SessionBagInterface.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session; + +/** + * Session Bag store. + * + * @author Drak + */ +interface SessionBagInterface +{ + /** + * Gets this bag's name + * + * @return string + */ + function getName(); + + /** + * Initializes the Bag + * + * @param array $array + */ + function initialize(array &$array); + + /** + * Gets the storage key for this bag. + * + * @return string + */ + function getStorageKey(); + + /** + * Clears out data from bag. + * + * @return mixed Whatever data was contained. + */ + function clear(); +} diff --git a/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/SessionInterface.php b/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/SessionInterface.php new file mode 100755 index 000000000..4e4962de4 --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/SessionInterface.php @@ -0,0 +1,167 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session; + +/** + * Interface for the session. + * + * @author Drak + */ +interface SessionInterface +{ + /** + * Starts the session storage. + * + * @return Boolean True if session started. + * + * @throws \RuntimeException If session fails to start. + * + * @api + */ + function start(); + + /** + * Returns the session ID. + * + * @return string The session ID. + * + * @api + */ + function getId(); + + /** + * Sets the session ID + * + * @param string $id + * + * @api + */ + function setId($id); + + /** + * Returns the session name. + * + * @return mixed The session name. + * + * @api + */ + function getName(); + + /** + * Sets the session name. + * + * @param string $name + * + * @api + */ + function setName($name); + + /** + * Invalidates the current session. + * + * Clears all session attributes and flashes and regenerates the + * session and deletes the old session from persistence. + * + * @return Boolean True if session invalidated, false if error. + * + * @api + */ + function invalidate(); + + /** + * Migrates the current session to a new session id while maintaining all + * session attributes. + * + * @param Boolean $destroy Whether to delete the old session or leave it to garbage collection. + * + * @return Boolean True if session migrated, false if error. + * + * @api + */ + function migrate($destroy = false); + + /** + * Force the session to be saved and closed. + * + * This method is generally not required for real sessions as + * the session will be automatically saved at the end of + * code execution. + */ + function save(); + + /** + * Checks if an attribute is defined. + * + * @param string $name The attribute name + * + * @return Boolean true if the attribute is defined, false otherwise + * + * @api + */ + function has($name); + + /** + * Returns an attribute. + * + * @param string $name The attribute name + * @param mixed $default The default value if not found. + * + * @return mixed + * + * @api + */ + function get($name, $default = null); + + /** + * Sets an attribute. + * + * @param string $name + * @param mixed $value + * + * @api + */ + function set($name, $value); + + /** + * Returns attributes. + * + * @return array Attributes + * + * @api + */ + function all(); + + /** + * Sets attributes. + * + * @param array $attributes Attributes + */ + function replace(array $attributes); + + /** + * Removes an attribute. + * + * @param string $name + * + * @return mixed The removed value + * + * @api + */ + function remove($name); + + /** + * Clears all attributes. + * + * @api + */ + function clear(); +} diff --git a/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/MemcacheSessionHandler.php b/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/MemcacheSessionHandler.php new file mode 100755 index 000000000..00488fd0d --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/MemcacheSessionHandler.php @@ -0,0 +1,139 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Storage\Handler; + +/** + * MemcacheSessionHandler. + * + * @author Drak + */ +class MemcacheSessionHandler implements \SessionHandlerInterface +{ + /** + * Memcache driver. + * + * @var \Memcache + */ + private $memcache; + + /** + * Configuration options. + * + * @var array + */ + private $memcacheOptions; + + /** + * Key prefix for shared environments. + * + * @var string + */ + private $prefix; + + /** + * Constructor. + * + * @param \Memcache $memcache A \Memcache instance + * @param array $memcacheOptions An associative array of Memcache options + * @param array $options Session configuration options. + */ + public function __construct(\Memcache $memcache, array $memcacheOptions = array(), array $options = array()) + { + $this->memcache = $memcache; + + // defaults + if (!isset($memcacheOptions['serverpool'])) { + $memcacheOptions['serverpool'] = array(array( + 'host' => '127.0.0.1', + 'port' => 11211, + 'timeout' => 1, + 'persistent' => false, + 'weight' => 1, + 'retry_interval' => 15, + )); + } + + $memcacheOptions['expiretime'] = isset($memcacheOptions['expiretime']) ? (int)$memcacheOptions['expiretime'] : 86400; + $this->prefix = isset($memcacheOptions['prefix']) ? $memcacheOptions['prefix'] : 'sf2s'; + + $this->memcacheOptions = $memcacheOptions; + } + + protected function addServer(array $server) + { + if (!array_key_exists('host', $server)) { + throw new \InvalidArgumentException('host key must be set'); + } + + $server['port'] = isset($server['port']) ? (int)$server['port'] : 11211; + $server['timeout'] = isset($server['timeout']) ? (int)$server['timeout'] : 1; + $server['persistent'] = isset($server['persistent']) ? (bool)$server['persistent'] : false; + $server['weight'] = isset($server['weight']) ? (int)$server['weight'] : 1; + $server['retry_interval'] = isset($server['retry_interval']) ? (int)$server['retry_interval'] : 15; + + $this->memcache->addserver($server['host'], $server['port'], $server['persistent'],$server['weight'],$server['timeout'],$server['retry_interval']); + + } + + /** + * {@inheritdoc} + */ + public function open($savePath, $sessionName) + { + foreach ($this->memcacheOptions['serverpool'] as $server) { + $this->addServer($server); + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function close() + { + return $this->memcache->close(); + } + + /** + * {@inheritdoc} + */ + public function read($sessionId) + { + return $this->memcache->get($this->prefix.$sessionId) ?: ''; + } + + /** + * {@inheritdoc} + */ + public function write($sessionId, $data) + { + return $this->memcache->set($this->prefix.$sessionId, $data, 0, $this->memcacheOptions['expiretime']); + } + + /** + * {@inheritdoc} + */ + public function destroy($sessionId) + { + return $this->memcache->delete($this->prefix.$sessionId); + } + + /** + * {@inheritdoc} + */ + public function gc($lifetime) + { + // not required here because memcache will auto expire the records anyhow. + return true; + } +} diff --git a/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/MemcachedSessionHandler.php b/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/MemcachedSessionHandler.php new file mode 100755 index 000000000..71770dda8 --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/MemcachedSessionHandler.php @@ -0,0 +1,130 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Storage\Handler; + +/** + * MemcachedSessionHandler. + * + * Memcached based session storage handler based on the Memcached class + * provided by the PHP memcached extension. + * + * @see http://php.net/memcached + * + * @author Drak + */ +class MemcachedSessionHandler implements \SessionHandlerInterface +{ + /** + * Memcached driver. + * + * @var \Memcached + */ + private $memcached; + + /** + * Configuration options. + * + * @var array + */ + private $memcachedOptions; + + /** + * Constructor. + * + * @param \Memcached $memcached A \Memcached instance + * @param array $memcachedOptions An associative array of Memcached options + * @param array $options Session configuration options. + */ + public function __construct(\Memcached $memcached, array $memcachedOptions = array(), array $options = array()) + { + $this->memcached = $memcached; + + // defaults + if (!isset($memcachedOptions['serverpool'])) { + $memcachedOptions['serverpool'][] = array( + 'host' => '127.0.0.1', + 'port' => 11211, + 'weight' => 1); + } + + $memcachedOptions['expiretime'] = isset($memcachedOptions['expiretime']) ? (int)$memcachedOptions['expiretime'] : 86400; + + $this->memcached->setOption(\Memcached::OPT_PREFIX_KEY, isset($memcachedOptions['prefix']) ? $memcachedOptions['prefix'] : 'sf2s'); + + $this->memcachedOptions = $memcachedOptions; + } + + /** + * {@inheritdoc} + */ + public function open($savePath, $sessionName) + { + return $this->memcached->addServers($this->memcachedOptions['serverpool']); + } + + /** + * {@inheritdoc} + */ + public function close() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function read($sessionId) + { + return $this->memcached->get($sessionId) ?: ''; + } + + /** + * {@inheritdoc} + */ + public function write($sessionId, $data) + { + return $this->memcached->set($sessionId, $data, $this->memcachedOptions['expiretime']); + } + + /** + * {@inheritdoc} + */ + public function destroy($sessionId) + { + return $this->memcached->delete($sessionId); + } + + /** + * {@inheritdoc} + */ + public function gc($lifetime) + { + // not required here because memcached will auto expire the records anyhow. + return true; + } + + /** + * Adds a server to the memcached handler. + * + * @param array $server + */ + protected function addServer(array $server) + { + if (array_key_exists('host', $server)) { + throw new \InvalidArgumentException('host key must be set'); + } + $server['port'] = isset($server['port']) ? (int)$server['port'] : 11211; + $server['timeout'] = isset($server['timeout']) ? (int)$server['timeout'] : 1; + $server['presistent'] = isset($server['presistent']) ? (bool)$server['presistent'] : false; + $server['weight'] = isset($server['weight']) ? (bool)$server['weight'] : 1; + } +} diff --git a/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/NativeFileSessionHandler.php b/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/NativeFileSessionHandler.php new file mode 100755 index 000000000..422e3a79a --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/NativeFileSessionHandler.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Storage\Handler; + +/** + * NativeFileSessionHandler. + * + * Native session handler using PHP's built in file storage. + * + * @author Drak + */ +class NativeFileSessionHandler extends NativeSessionHandler +{ + /** + * Constructor. + * + * @param string $savePath Path of directory to save session files. Default null will leave setting as defined by PHP. + */ + public function __construct($savePath = null) + { + if (null === $savePath) { + $savePath = ini_get('session.save_path'); + } + + if ($savePath && !is_dir($savePath)) { + mkdir($savePath, 0777, true); + } + + ini_set('session.save_handler', 'files'); + ini_set('session.save_path', $savePath); + } +} diff --git a/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/NativeMemcacheSessionHandler.php b/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/NativeMemcacheSessionHandler.php new file mode 100755 index 000000000..baacf2927 --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/NativeMemcacheSessionHandler.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Storage\Handler; + +/** + * NativeMemcacheSessionHandler. + * + * Driver for the memcache session save hadlers provided by the memcache PHP extension. + * + * @see http://php.net/memcache + * + * @author Drak + */ +class NativeMemcacheSessionHandler extends NativeSessionHandler +{ + /** + * Constructor. + * + * @param string $savePath Path of memcache server. + * @param array $options Session configuration options. + */ + public function __construct($savePath = 'tcp://127.0.0.1:11211?persistent=0', array $options = array()) + { + if (!extension_loaded('memcache')) { + throw new \RuntimeException('PHP does not have "memcache" session module registered'); + } + + if (null === $savePath) { + $savePath = ini_get('session.save_path'); + } + + ini_set('session.save_handler', 'memcache'); + ini_set('session.save_path', $savePath); + + $this->setOptions($options); + } + + /** + * Set any memcached ini values. + * + * @see http://php.net/memcache.ini + */ + protected function setOptions(array $options) + { + foreach ($options as $key => $value) { + if (in_array($key, array( + 'memcache.allow_failover', 'memcache.max_failover_attempts', + 'memcache.chunk_size', 'memcache.default_port', 'memcache.hash_strategy', + 'memcache.hash_function', 'memcache.protocol', 'memcache.redundancy', + 'memcache.session_redundancy', 'memcache.compress_threshold', + 'memcache.lock_timeout'))) { + ini_set($key, $value); + } + } + } +} diff --git a/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/NativeMemcachedSessionHandler.php b/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/NativeMemcachedSessionHandler.php new file mode 100755 index 000000000..d84bdfbe6 --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/NativeMemcachedSessionHandler.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Storage\Handler; + +/** + * NativeMemcachedSessionHandler. + * + * Driver for the memcached session save hadlers provided by the memcached PHP extension. + * + * @see http://php.net/memcached.sessions + * + * @author Drak + */ +class NativeMemcachedSessionHandler extends NativeSessionHandler +{ + /** + * Constructor. + * + * @param string $savePath Comma separated list of servers: e.g. memcache1.example.com:11211,memcache2.example.com:11211 + * @param array $options Session configuration options. + */ + public function __construct($savePath = '127.0.0.1:11211', array $options = array()) + { + if (!extension_loaded('memcached')) { + throw new \RuntimeException('PHP does not have "memcached" session module registered'); + } + + if (null === $savePath) { + $savePath = ini_get('session.save_path'); + } + + ini_set('session.save_handler', 'memcached'); + ini_set('session.save_path', $savePath); + + $this->setOptions($options); + } + + /** + * Set any memcached ini values. + * + * @see https://github.com/php-memcached-dev/php-memcached/blob/master/memcached.ini + */ + protected function setOptions(array $options) + { + foreach ($options as $key => $value) { + if (in_array($key, array( + 'memcached.sess_locking', 'memcached.sess_lock_wait', + 'memcached.sess_prefix', 'memcached.compression_type', + 'memcached.compression_factor', 'memcached.compression_threshold', + 'memcached.serializer'))) { + ini_set($key, $value); + } + } + } +} diff --git a/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/NativeSessionHandler.php b/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/NativeSessionHandler.php new file mode 100755 index 000000000..1260ad0d2 --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/NativeSessionHandler.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Storage\Handler; + +/** + * Adds SessionHandler functionality if available. + * + * @see http://php.net/sessionhandler + */ + +if (version_compare(phpversion(), '5.4.0', '>=')) { + class NativeSessionHandler extends \SessionHandler {} +} else { + class NativeSessionHandler {} +} diff --git a/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/NativeSqliteSessionHandler.php b/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/NativeSqliteSessionHandler.php new file mode 100755 index 000000000..098cc8a68 --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/NativeSqliteSessionHandler.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Storage\Handler; + +/** + * NativeSqliteSessionHandler. + * + * Driver for the sqlite session save hadlers provided by the SQLite PHP extension. + * + * @author Drak + */ +class NativeSqliteSessionHandler extends NativeSessionHandler +{ + /** + * Constructor. + * + * @param string $savePath Path to SQLite database file itself. + * @param array $options Session configuration options. + */ + public function __construct($savePath, array $options = array()) + { + if (!extension_loaded('sqlite')) { + throw new \RuntimeException('PHP does not have "sqlite" session module registered'); + } + + if (null === $savePath) { + $savePath = ini_get('session.save_path'); + } + + ini_set('session.save_handler', 'sqlite'); + ini_set('session.save_path', $savePath); + + $this->setOptions($options); + } + + /** + * Set any sqlite ini values. + * + * @see http://php.net/sqlite.configuration + */ + protected function setOptions(array $options) + { + foreach ($options as $key => $value) { + if (in_array($key, array('sqlite.assoc_case'))) { + ini_set($key, $value); + } + } + } +} diff --git a/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/NullSessionHandler.php b/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/NullSessionHandler.php new file mode 100755 index 000000000..dd9f0c79a --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/NullSessionHandler.php @@ -0,0 +1,72 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Storage\Handler; + +/** + * NullSessionHandler. + * + * Can be used in unit testing or in a sitation where persisted sessions are not desired. + * + * @author Drak + * + * @api + */ +class NullSessionHandler implements \SessionHandlerInterface +{ + /** + * {@inheritdoc} + */ + public function open($savePath, $sessionName) + { + return true; + } + + /** + * {@inheritdoc} + */ + public function close() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function read($sessionId) + { + return ''; + } + + /** + * {@inheritdoc} + */ + public function write($sessionId, $data) + { + return true; + } + + /** + * {@inheritdoc} + */ + public function destroy($sessionId) + { + return true; + } + + /** + * {@inheritdoc} + */ + public function gc($lifetime) + { + return true; + } +} diff --git a/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php b/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php new file mode 100755 index 000000000..28dccba80 --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Handler/PdoSessionHandler.php @@ -0,0 +1,221 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Storage\Handler; + +/** + * PdoSessionHandler. + * + * @author Fabien Potencier + * @author Michael Williams + */ +class PdoSessionHandler implements \SessionHandlerInterface +{ + /** + * PDO instance. + * + * @var \PDO + */ + private $pdo; + + /** + * Database options. + * + * + * @var array + */ + private $dbOptions; + + /** + * Constructor. + * + * @param \PDO $pdo A \PDO instance + * @param array $dbOptions An associative array of DB options + * @param array $options Session configuration options + * + * @throws \InvalidArgumentException When "db_table" option is not provided + */ + public function __construct(\PDO $pdo, array $dbOptions = array(), array $options = array()) + { + if (!array_key_exists('db_table', $dbOptions)) { + throw new \InvalidArgumentException('You must provide the "db_table" option for a PdoSessionStorage.'); + } + + $this->pdo = $pdo; + $this->dbOptions = array_merge(array( + 'db_id_col' => 'sess_id', + 'db_data_col' => 'sess_data', + 'db_time_col' => 'sess_time', + ), $dbOptions); + } + + /** + * {@inheritdoc} + */ + public function open($path, $name) + { + return true; + } + + /** + * {@inheritdoc} + */ + public function close() + { + return true; + } + + /** + * {@inheritdoc} + */ + public function destroy($id) + { + // get table/column + $dbTable = $this->dbOptions['db_table']; + $dbIdCol = $this->dbOptions['db_id_col']; + + // delete the record associated with this id + $sql = "DELETE FROM $dbTable WHERE $dbIdCol = :id"; + + try { + $stmt = $this->pdo->prepare($sql); + $stmt->bindParam(':id', $id, \PDO::PARAM_STR); + $stmt->execute(); + } catch (\PDOException $e) { + throw new \RuntimeException(sprintf('PDOException was thrown when trying to manipulate session data: %s', $e->getMessage()), 0, $e); + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function gc($lifetime) + { + // get table/column + $dbTable = $this->dbOptions['db_table']; + $dbTimeCol = $this->dbOptions['db_time_col']; + + // delete the session records that have expired + $sql = "DELETE FROM $dbTable WHERE $dbTimeCol < (:time - $lifetime)"; + + try { + $stmt = $this->pdo->prepare($sql); + $stmt->bindValue(':time', time(), \PDO::PARAM_INT); + $stmt->execute(); + } catch (\PDOException $e) { + throw new \RuntimeException(sprintf('PDOException was thrown when trying to manipulate session data: %s', $e->getMessage()), 0, $e); + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function read($id) + { + // get table/columns + $dbTable = $this->dbOptions['db_table']; + $dbDataCol = $this->dbOptions['db_data_col']; + $dbIdCol = $this->dbOptions['db_id_col']; + + try { + $sql = "SELECT $dbDataCol FROM $dbTable WHERE $dbIdCol = :id"; + + $stmt = $this->pdo->prepare($sql); + $stmt->bindParam(':id', $id, \PDO::PARAM_STR); + + $stmt->execute(); + // it is recommended to use fetchAll so that PDO can close the DB cursor + // we anyway expect either no rows, or one row with one column. fetchColumn, seems to be buggy #4777 + $sessionRows = $stmt->fetchAll(\PDO::FETCH_NUM); + + if (count($sessionRows) == 1) { + return base64_decode($sessionRows[0][0]); + } + + // session does not exist, create it + $this->createNewSession($id); + + return ''; + } catch (\PDOException $e) { + throw new \RuntimeException(sprintf('PDOException was thrown when trying to read the session data: %s', $e->getMessage()), 0, $e); + } + } + + /** + * {@inheritdoc} + */ + public function write($id, $data) + { + // get table/column + $dbTable = $this->dbOptions['db_table']; + $dbDataCol = $this->dbOptions['db_data_col']; + $dbIdCol = $this->dbOptions['db_id_col']; + $dbTimeCol = $this->dbOptions['db_time_col']; + + $sql = ('mysql' === $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME)) + ? "INSERT INTO $dbTable ($dbIdCol, $dbDataCol, $dbTimeCol) VALUES (:id, :data, :time) " + ."ON DUPLICATE KEY UPDATE $dbDataCol = VALUES($dbDataCol), $dbTimeCol = CASE WHEN $dbTimeCol = :time THEN (VALUES($dbTimeCol) + 1) ELSE VALUES($dbTimeCol) END" + : "UPDATE $dbTable SET $dbDataCol = :data, $dbTimeCol = :time WHERE $dbIdCol = :id"; + + try { + //session data can contain non binary safe characters so we need to encode it + $encoded = base64_encode($data); + $stmt = $this->pdo->prepare($sql); + $stmt->bindParam(':id', $id, \PDO::PARAM_STR); + $stmt->bindParam(':data', $encoded, \PDO::PARAM_STR); + $stmt->bindValue(':time', time(), \PDO::PARAM_INT); + $stmt->execute(); + + if (!$stmt->rowCount()) { + // No session exists in the database to update. This happens when we have called + // session_regenerate_id() + $this->createNewSession($id, $data); + } + } catch (\PDOException $e) { + throw new \RuntimeException(sprintf('PDOException was thrown when trying to write the session data: %s', $e->getMessage()), 0, $e); + } + + return true; + } + + /** + * Creates a new session with the given $id and $data + * + * @param string $id + * @param string $data + * + * @return boolean True. + */ + private function createNewSession($id, $data = '') + { + // get table/column + $dbTable = $this->dbOptions['db_table']; + $dbDataCol = $this->dbOptions['db_data_col']; + $dbIdCol = $this->dbOptions['db_id_col']; + $dbTimeCol = $this->dbOptions['db_time_col']; + + $sql = "INSERT INTO $dbTable ($dbIdCol, $dbDataCol, $dbTimeCol) VALUES (:id, :data, :time)"; + + //session data can contain non binary safe characters so we need to encode it + $encoded = base64_encode($data); + $stmt = $this->pdo->prepare($sql); + $stmt->bindParam(':id', $id, \PDO::PARAM_STR); + $stmt->bindParam(':data', $encoded, \PDO::PARAM_STR); + $stmt->bindValue(':time', time(), \PDO::PARAM_INT); + $stmt->execute(); + + return true; + } +} diff --git a/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/MockArraySessionStorage.php b/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/MockArraySessionStorage.php new file mode 100755 index 000000000..6f1e279f4 --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/MockArraySessionStorage.php @@ -0,0 +1,218 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Storage; + +use Symfony\Component\HttpFoundation\Session\SessionBagInterface; + +/** + * MockArraySessionStorage mocks the session for unit tests. + * + * No PHP session is actually started since a session can be initialized + * and shutdown only once per PHP execution cycle. + * + * When doing functional testing, you should use MockFileSessionStorage instead. + * + * @author Fabien Potencier + * @author Bulat Shakirzyanov + * @author Drak + */ +class MockArraySessionStorage implements SessionStorageInterface +{ + /** + * @var string + */ + protected $id = ''; + + /** + * @var string + */ + protected $name; + + /** + * @var boolean + */ + protected $started = false; + + /** + * @var boolean + */ + protected $closed = false; + + /** + * @var array + */ + protected $data = array(); + + /** + * Constructor. + * + * @param string $name Session name + */ + public function __construct($name = 'MOCKSESSID') + { + $this->name = $name; + } + + /** + * Sets the session data. + * + * @param array $array + */ + public function setSessionData(array $array) + { + $this->data = $array; + } + + /** + * {@inheritdoc} + */ + public function start() + { + if ($this->started && !$this->closed) { + return true; + } + + if (empty($this->id)) { + $this->id = $this->generateId(); + } + + $this->loadSession(); + + return true; + } + + + /** + * {@inheritdoc} + */ + public function regenerate($destroy = false) + { + if (!$this->started) { + $this->start(); + } + + $this->id = $this->generateId(); + + return true; + } + + /** + * {@inheritdoc} + */ + public function getId() + { + return $this->id; + } + + /** + * {@inheritdoc} + */ + public function setId($id) + { + if ($this->started) { + throw new \LogicException('Cannot set session ID after the session has started.'); + } + + $this->id = $id; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return $this->name; + } + + /** + * {@inheritdoc} + */ + public function setName($name) + { + $this->name = $name; + } + + /** + * {@inheritdoc} + */ + public function save() + { + // nothing to do since we don't persist the session data + $this->closed = false; + } + + /** + * {@inheritdoc} + */ + public function clear() + { + // clear out the bags + foreach ($this->bags as $bag) { + $bag->clear(); + } + + // clear out the session + $this->data = array(); + + // reconnect the bags to the session + $this->loadSession(); + } + + /** + * {@inheritdoc} + */ + public function registerBag(SessionBagInterface $bag) + { + $this->bags[$bag->getName()] = $bag; + } + + /** + * {@inheritdoc} + */ + public function getBag($name) + { + if (!isset($this->bags[$name])) { + throw new \InvalidArgumentException(sprintf('The SessionBagInterface %s is not registered.', $name)); + } + + if (!$this->started) { + $this->start(); + } + + return $this->bags[$name]; + } + + /** + * Generates a session ID. + * + * This doesn't need to be particularly cryptographically secure since this is just + * a mock. + * + * @return string + */ + protected function generateId() + { + return sha1(uniqid(mt_rand())); + } + + protected function loadSession() + { + foreach ($this->bags as $bag) { + $key = $bag->getStorageKey(); + $this->data[$key] = isset($this->data[$key]) ? $this->data[$key] : array(); + $bag->initialize($this->data[$key]); + } + + $this->started = true; + $this->closed = false; + } +} diff --git a/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/MockFileSessionStorage.php b/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/MockFileSessionStorage.php new file mode 100755 index 000000000..24457319f --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/MockFileSessionStorage.php @@ -0,0 +1,126 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Storage; + +/** + * MockFileSessionStorage is used to mock sessions for + * functional testing when done in a single PHP process. + * + * No PHP session is actually started since a session can be initialized + * and shutdown only once per PHP execution cycle and this class does + * not pollute any session related globals, including session_*() functions + * or session.* PHP ini directives. + * + * @author Drak + */ +class MockFileSessionStorage extends MockArraySessionStorage +{ + /** + * @var string + */ + private $savePath; + + /** + * Constructor. + * + * @param string $savePath Path of directory to save session files. + * @param string $name Session name. + */ + public function __construct($savePath = null, $name = 'MOCKSESSID') + { + if (null === $savePath) { + $savePath = sys_get_temp_dir(); + } + + if (!is_dir($savePath)) { + mkdir($savePath, 0777, true); + } + + $this->savePath = $savePath; + + parent::__construct($name); + } + + /** + * {@inheritdoc} + */ + public function start() + { + if ($this->started) { + return true; + } + + if (!$this->id) { + $this->id = $this->generateId(); + } + + $this->read(); + + $this->started = true; + + return true; + } + + /** + * {@inheritdoc} + */ + public function regenerate($destroy = false) + { + if ($destroy) { + $this->destroy(); + } + + $this->id = $this->generateId(); + + return true; + } + + /** + * {@inheritdoc} + */ + public function save() + { + file_put_contents($this->getFilePath(), serialize($this->data)); + } + + /** + * Deletes a session from persistent storage. + * Deliberately leaves session data in memory intact. + */ + private function destroy() + { + if (is_file($this->getFilePath())) { + unlink($this->getFilePath()); + } + } + + /** + * Calculate path to file. + * + * @return string File path + */ + private function getFilePath() + { + return $this->savePath.'/'.$this->id.'.mocksess'; + } + + /** + * Reads session from storage and loads session. + */ + private function read() + { + $filePath = $this->getFilePath(); + $this->data = is_readable($filePath) && is_file($filePath) ? unserialize(file_get_contents($filePath)) : array(); + + $this->loadSession(); + } +} diff --git a/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/NativeSessionStorage.php b/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/NativeSessionStorage.php new file mode 100755 index 000000000..5252bf55f --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/NativeSessionStorage.php @@ -0,0 +1,347 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Storage; + +use Symfony\Component\HttpFoundation\Session\SessionBagInterface; +use Symfony\Component\HttpFoundation\Session\Storage\Proxy\NativeProxy; +use Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy; +use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy; + +/** + * This provides a base class for session attribute storage. + * + * @author Drak + */ +class NativeSessionStorage implements SessionStorageInterface +{ + /** + * Array of SessionBagInterface + * + * @var array + */ + protected $bags; + + /** + * @var boolean + */ + protected $started = false; + + /** + * @var boolean + */ + protected $closed = false; + + /** + * @var AbstractProxy + */ + protected $saveHandler; + + /** + * Constructor. + * + * Depending on how you want the storage driver to behave you probably + * want top override this constructor entirely. + * + * List of options for $options array with their defaults. + * @see http://php.net/session.configuration for options + * but we omit 'session.' from the beginning of the keys for convenience. + * + * auto_start, "0" + * cache_limiter, "nocache" (use "0" to prevent headers from being sent entirely). + * cookie_domain, "" + * cookie_httponly, "" + * cookie_lifetime, "0" + * cookie_path, "/" + * cookie_secure, "" + * entropy_file, "" + * entropy_length, "0" + * gc_divisor, "100" + * gc_maxlifetime, "1440" + * gc_probability, "1" + * hash_bits_per_character, "4" + * hash_function, "0" + * name, "PHPSESSID" + * referer_check, "" + * serialize_handler, "php" + * use_cookies, "1" + * use_only_cookies, "1" + * use_trans_sid, "0" + * upload_progress.enabled, "1" + * upload_progress.cleanup, "1" + * upload_progress.prefix, "upload_progress_" + * upload_progress.name, "PHP_SESSION_UPLOAD_PROGRESS" + * upload_progress.freq, "1%" + * upload_progress.min-freq, "1" + * url_rewriter.tags, "a=href,area=href,frame=src,form=,fieldset=" + * + * @param array $options Session configuration options. + * @param object $handler SessionHandlerInterface. + */ + public function __construct(array $options = array(), $handler = null) + { + // sensible defaults + ini_set('session.auto_start', 0); // by default we prefer to explicitly start the session using the class. + ini_set('session.cache_limiter', ''); // disable by default because it's managed by HeaderBag (if used) + ini_set('session.use_cookies', 1); + + if (version_compare(phpversion(), '5.4.0', '>=')) { + session_register_shutdown(); + } else { + register_shutdown_function('session_write_close'); + } + + $this->setOptions($options); + $this->setSaveHandler($handler); + } + + /** + * Gets the save handler instance. + * + * @return AbstractProxy + */ + public function getSaveHandler() + { + return $this->saveHandler; + } + + /** + * {@inheritdoc} + */ + public function start() + { + if ($this->started && !$this->closed) { + return true; + } + + // catch condition where session was started automatically by PHP + if (!$this->started && !$this->closed && $this->saveHandler->isActive() + && $this->saveHandler->isSessionHandlerInterface()) { + $this->loadSession(); + + return true; + } + + if (ini_get('session.use_cookies') && headers_sent()) { + throw new \RuntimeException('Failed to start the session because headers have already been sent.'); + } + + // start the session + if (!session_start()) { + throw new \RuntimeException('Failed to start the session'); + } + + $this->loadSession(); + + if (!$this->saveHandler->isWrapper() && !$this->saveHandler->isSessionHandlerInterface()) { + $this->saveHandler->setActive(false); + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function getId() + { + if (!$this->started) { + return ''; // returning empty is consistent with session_id() behaviour + } + + return $this->saveHandler->getId(); + } + + /** + * {@inheritdoc} + */ + public function setId($id) + { + return $this->saveHandler->setId($id); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return $this->saveHandler->getName(); + } + + /** + * {@inheritdoc} + */ + public function setName($name) + { + $this->saveHandler->setName($name); + } + + /** + * {@inheritdoc} + */ + public function regenerate($destroy = false) + { + return session_regenerate_id($destroy); + } + + /** + * {@inheritdoc} + */ + public function save() + { + session_write_close(); + + if (!$this->saveHandler->isWrapper() && !$this->getSaveHandler()->isSessionHandlerInterface()) { + $this->saveHandler->setActive(false); + } + + $this->closed = true; + } + + /** + * {@inheritdoc} + */ + public function clear() + { + // clear out the bags + foreach ($this->bags as $bag) { + $bag->clear(); + } + + // clear out the session + $_SESSION = array(); + + // reconnect the bags to the session + $this->loadSession(); + } + + /** + * {@inheritdoc} + */ + public function registerBag(SessionBagInterface $bag) + { + $this->bags[$bag->getName()] = $bag; + } + + /** + * {@inheritdoc} + */ + public function getBag($name) + { + if (!isset($this->bags[$name])) { + throw new \InvalidArgumentException(sprintf('The SessionBagInterface %s is not registered.', $name)); + } + + if (ini_get('session.auto_start') && !$this->started) { + $this->start(); + } elseif ($this->saveHandler->isActive() && !$this->started) { + $this->loadSession(); + } + + return $this->bags[$name]; + } + + /** + * Sets session.* ini variables. + * + * For convenience we omit 'session.' from the beginning of the keys. + * Explicitly ignores other ini keys. + * + * @param array $options Session ini directives array(key => value). + * + * @see http://php.net/session.configuration + */ + public function setOptions(array $options) + { + foreach ($options as $key => $value) { + if (in_array($key, array( + 'auto_start', 'cache_limiter', 'cookie_domain', 'cookie_httponly', + 'cookie_lifetime', 'cookie_path', 'cookie_secure', + 'entropy_file', 'entropy_length', 'gc_divisor', + 'gc_maxlifetime', 'gc_probability', 'hash_bits_per_character', + 'hash_function', 'name', 'referer_check', + 'serialize_handler', 'use_cookies', + 'use_only_cookies', 'use_trans_sid', 'upload_progress.enabled', + 'upload_progress.cleanup', 'upload_progress.prefix', 'upload_progress.name', + 'upload_progress.freq', 'upload_progress.min-freq', 'url_rewriter.tags'))) { + ini_set('session.'.$key, $value); + } + } + } + + /** + * Registers save handler as a PHP session handler. + * + * To use internal PHP session save handlers, override this method using ini_set with + * session.save_handlers and session.save_path e.g. + * + * ini_set('session.save_handlers', 'files'); + * ini_set('session.save_path', /tmp'); + * + * @see http://php.net/session-set-save-handler + * @see http://php.net/sessionhandlerinterface + * @see http://php.net/sessionhandler + * + * @param object $saveHandler Default null means NativeProxy. + */ + public function setSaveHandler($saveHandler = null) + { + // Wrap $saveHandler in proxy + if (!$saveHandler instanceof AbstractProxy && $saveHandler instanceof \SessionHandlerInterface) { + $saveHandler = new SessionHandlerProxy($saveHandler); + } elseif (!$saveHandler instanceof AbstractProxy) { + $saveHandler = new NativeProxy($saveHandler); + } + + $this->saveHandler = $saveHandler; + + if ($this->saveHandler instanceof \SessionHandlerInterface) { + if (version_compare(phpversion(), '5.4.0', '>=')) { + session_set_save_handler($this->saveHandler, false); + } else { + session_set_save_handler( + array($this->saveHandler, 'open'), + array($this->saveHandler, 'close'), + array($this->saveHandler, 'read'), + array($this->saveHandler, 'write'), + array($this->saveHandler, 'destroy'), + array($this->saveHandler, 'gc') + ); + } + } + } + + /** + * Load the session with attributes. + * + * After starting the session, PHP retrieves the session from whatever handlers + * are set to (either PHP's internal, or a custom save handler set with session_set_save_handler()). + * PHP takes the return value from the read() handler, unserializes it + * and populates $_SESSION with the result automatically. + * + * @param array|null $session + */ + protected function loadSession(array &$session = null) + { + if (null === $session) { + $session = &$_SESSION; + } + + foreach ($this->bags as $bag) { + $key = $bag->getStorageKey(); + $session[$key] = isset($session[$key]) ? $session[$key] : array(); + $bag->initialize($session[$key]); + } + + $this->started = true; + $this->closed = false; + } +} diff --git a/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Proxy/AbstractProxy.php b/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Proxy/AbstractProxy.php new file mode 100755 index 000000000..09f9efa09 --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Proxy/AbstractProxy.php @@ -0,0 +1,135 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Storage\Proxy; + +/** + * AbstractProxy. + * + * @author Drak + */ +abstract class AbstractProxy +{ + /** + * Flag if handler wraps an internal PHP session handler (using \SessionHandler). + * + * @var boolean + */ + protected $wrapper = false; + + /** + * @var boolean + */ + protected $active = false; + + /** + * @var string + */ + protected $saveHandlerName; + + /** + * Gets the session.save_handler name. + * + * @return string + */ + public function getSaveHandlerName() + { + return $this->saveHandlerName; + } + + /** + * Is this proxy handler and instance of \SessionHandlerInterface. + * + * @return boolean + */ + public function isSessionHandlerInterface() + { + return ($this instanceof \SessionHandlerInterface); + } + + /** + * Returns true if this handler wraps an internal PHP session save handler using \SessionHandler. + * + * @return bool + */ + public function isWrapper() + { + return $this->wrapper; + } + + /** + * Has a session started? + * + * @return bool + */ + public function isActive() + { + return $this->active; + } + + /** + * Sets the active flag. + * + * @param bool $flag + */ + public function setActive($flag) + { + $this->active = (bool) $flag; + } + + /** + * Gets the session ID. + * + * @return string + */ + public function getId() + { + return session_id(); + } + + /** + * Sets the session ID. + * + * @param string $id + */ + public function setId($id) + { + if ($this->isActive()) { + throw new \LogicException('Cannot change the ID of an active session'); + } + + session_id($id); + } + + /** + * Gets the session name. + * + * @return string + */ + public function getName() + { + return session_name(); + } + + /** + * Sets the session name. + * + * @param string $name + */ + public function setName($name) + { + if ($this->isActive()) { + throw new \LogicException('Cannot change the name of an active session'); + } + + session_name($name); + } +} diff --git a/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Proxy/NativeProxy.php b/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Proxy/NativeProxy.php new file mode 100755 index 000000000..5bb2c712e --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Proxy/NativeProxy.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Storage\Proxy; + +/** + * NativeProxy. + * + * This proxy is built-in session handlers in PHP 5.3.x + * + * @author Drak + */ +class NativeProxy extends AbstractProxy +{ + /** + * Constructor. + */ + public function __construct() + { + // this makes an educated guess as to what the handler is since it should already be set. + $this->saveHandlerName = ini_get('session.save_handler'); + } + + /** + * Returns true if this handler wraps an internal PHP session save handler using \SessionHandler. + * + * @return bool False. + */ + public function isWrapper() + { + return false; + } +} diff --git a/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Proxy/SessionHandlerProxy.php b/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Proxy/SessionHandlerProxy.php new file mode 100755 index 000000000..e925d628d --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/Proxy/SessionHandlerProxy.php @@ -0,0 +1,95 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Storage\Proxy; + +/** + * SessionHandler proxy. + * + * @author Drak + */ +class SessionHandlerProxy extends AbstractProxy implements \SessionHandlerInterface +{ + /** + * @var \SessionHandlerInterface + */ + protected $handler; + + /** + * Constructor. + * + * @param \SessionHandlerInterface $handler + */ + public function __construct(\SessionHandlerInterface $handler) + { + $this->handler = $handler; + $this->wrapper = ($handler instanceof \SessionHandler); + $this->saveHandlerName = $this->wrapper ? ini_get('session.save_handler') : 'user'; + } + + // \SessionHandlerInterface + + /** + * {@inheritdoc} + */ + public function open($savePath, $sessionName) + { + $return = (bool)$this->handler->open($savePath, $sessionName); + + if (true === $return) { + $this->active = true; + } + + return $return; + } + + /** + * {@inheritdoc} + */ + public function close() + { + $this->active = false; + + return (bool) $this->handler->close(); + } + + /** + * {@inheritdoc} + */ + public function read($id) + { + return (string) $this->handler->read($id); + } + + /** + * {@inheritdoc} + */ + public function write($id, $data) + { + return (bool) $this->handler->write($id, $data); + } + + /** + * {@inheritdoc} + */ + public function destroy($id) + { + return (bool) $this->handler->destroy($id); + } + + /** + * {@inheritdoc} + */ + public function gc($maxlifetime) + { + return (bool) $this->handler->gc($maxlifetime); + } +} diff --git a/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/SessionStorageInterface.php b/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/SessionStorageInterface.php new file mode 100755 index 000000000..8bf2e5d32 --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/HttpFoundation/Session/Storage/SessionStorageInterface.php @@ -0,0 +1,126 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Session\Storage; + +use Symfony\Component\HttpFoundation\Session\SessionBagInterface; + +/** + * StorageInterface. + * + * @author Fabien Potencier + * @author Drak + * + * @api + */ +interface SessionStorageInterface +{ + /** + * Starts the session. + * + * @throws \RuntimeException If something goes wrong starting the session. + * + * @return boolean True if started. + * + * @api + */ + function start(); + + /** + * Returns the session ID + * + * @return string The session ID or empty. + * + * @api + */ + function getId(); + + /** + * Sets the session ID + * + * @param string $id + * + * @api + */ + function setId($id); + + /** + * Returns the session name + * + * @return mixed The session name. + * + * @api + */ + function getName(); + + /** + * Sets the session name + * + * @param string $name + * + * @api + */ + function setName($name); + + /** + * Regenerates id that represents this storage. + * + * This method must invoke session_regenerate_id($destroy) unless + * this interface is used for a storage object designed for unit + * or functional testing where a real PHP session would interfere + * with testing. + * + * Note regenerate+destroy should not clear the session data in memory + * only delete the session data from persistent storage. + * + * @param Boolean $destroy Destroy session when regenerating? + * + * @return Boolean True if session regenerated, false if error + * + * @throws \RuntimeException If an error occurs while regenerating this storage + * + * @api + */ + function regenerate($destroy = false); + + /** + * Force the session to be saved and closed. + * + * This method must invoke session_write_close() unless this interface is + * used for a storage object design for unit or functional testing where + * a real PHP session would interfere with testing, in which case it + * it should actually persist the session data if required. + */ + function save(); + + /** + * Clear all session data in memory. + */ + function clear(); + + /** + * Gets a SessionBagInterface by name. + * + * @param string $name + * + * @return SessionBagInterface + * + * @throws \InvalidArgumentException If the bag does not exist + */ + function getBag($name); + + /** + * Registers a SessionBagInterface for use. + * + * @param SessionBagInterface $bag + */ + function registerBag(SessionBagInterface $bag); +} diff --git a/app/laravel/vendor/Symfony/Component/HttpFoundation/StreamedResponse.php b/app/laravel/vendor/Symfony/Component/HttpFoundation/StreamedResponse.php new file mode 100755 index 000000000..1952a848b --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/HttpFoundation/StreamedResponse.php @@ -0,0 +1,129 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +/** + * StreamedResponse represents a streamed HTTP response. + * + * A StreamedResponse uses a callback for its content. + * + * The callback should use the standard PHP functions like echo + * to stream the response back to the client. The flush() method + * can also be used if needed. + * + * @see flush() + * + * @author Fabien Potencier + * + * @api + */ +class StreamedResponse extends Response +{ + protected $callback; + protected $streamed; + + /** + * Constructor. + * + * @param mixed $callback A valid PHP callback + * @param integer $status The response status code + * @param array $headers An array of response headers + * + * @api + */ + public function __construct($callback = null, $status = 200, $headers = array()) + { + parent::__construct(null, $status, $headers); + + if (null !== $callback) { + $this->setCallback($callback); + } + $this->streamed = false; + } + + /** + * {@inheritDoc} + */ + public static function create($callback = null, $status = 200, $headers = array()) + { + return new static($callback, $status, $headers); + } + + /** + * Sets the PHP callback associated with this Response. + * + * @param mixed $callback A valid PHP callback + */ + public function setCallback($callback) + { + if (!is_callable($callback)) { + throw new \LogicException('The Response callback must be a valid PHP callable.'); + } + $this->callback = $callback; + } + + /** + * @{inheritdoc} + */ + public function prepare(Request $request) + { + if ('1.0' != $request->server->get('SERVER_PROTOCOL')) { + $this->setProtocolVersion('1.1'); + } + + $this->headers->set('Cache-Control', 'no-cache'); + + parent::prepare($request); + } + + /** + * @{inheritdoc} + * + * This method only sends the content once. + */ + public function sendContent() + { + if ($this->streamed) { + return; + } + + $this->streamed = true; + + if (null === $this->callback) { + throw new \LogicException('The Response callback must not be null.'); + } + + call_user_func($this->callback); + } + + /** + * @{inheritdoc} + * + * @throws \LogicException when the content is not null + */ + public function setContent($content) + { + if (null !== $content) { + throw new \LogicException('The content cannot be set on a StreamedResponse instance.'); + } + } + + /** + * @{inheritdoc} + * + * @return false + */ + public function getContent() + { + return false; + } +} diff --git a/app/laravel/vendor/Symfony/Component/HttpFoundation/composer.json b/app/laravel/vendor/Symfony/Component/HttpFoundation/composer.json new file mode 100755 index 000000000..d0f1015c0 --- /dev/null +++ b/app/laravel/vendor/Symfony/Component/HttpFoundation/composer.json @@ -0,0 +1,33 @@ +{ + "name": "symfony/http-foundation", + "type": "library", + "description": "Symfony HttpFoundation Component", + "keywords": [], + "homepage": "http://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.3.2" + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\HttpFoundation": "", + "SessionHandlerInterface": "Symfony/Component/HttpFoundation/Resources/stubs" + } + }, + "target-dir": "Symfony/Component/HttpFoundation", + "extra": { + "branch-alias": { + "dev-master": "2.1-dev" + } + } +} diff --git a/app/laravel/view.php b/app/laravel/view.php index 76d8d40d8..b9854d2a0 100644 --- a/app/laravel/view.php +++ b/app/laravel/view.php @@ -37,6 +37,27 @@ class View implements ArrayAccess { */ public static $names = array(); + /** + * The cache content of loaded view files. + * + * @var array + */ + public static $cache = array(); + + /** + * THe last view to be rendered. + * + * @var string + */ + public static $last; + + /** + * The render operations taking place. + * + * @var int + */ + public static $render_count = 0; + /** * The Laravel view loader event name. * @@ -103,13 +124,19 @@ public function __construct($view, $data = array()) } /** - * Get the path to a given view on disk. + * Determine if the given view exists. * - * @param string $view - * @return string + * @param string $view + * @param boolean $return_path + * @return string|bool */ - protected function path($view) + public static function exists($view, $return_path = false) { + if (starts_with($view, 'name: ') and array_key_exists($name = substr($view, 6), static::$names)) + { + $view = static::$names[$name]; + } + list($bundle, $view) = Bundle::parse($view); $view = str_replace('.', '/', $view); @@ -117,9 +144,25 @@ protected function path($view) // We delegate the determination of view paths to the view loader event // so that the developer is free to override and manage the loading // of views in any way they see fit for their application. - $path = Event::first(static::loader, array($bundle, $view)); + $path = Event::until(static::loader, array($bundle, $view)); if ( ! is_null($path)) + { + return $return_path ? $path : true; + } + + return false; + } + + /** + * Get the path to a given view on disk. + * + * @param string $view + * @return string + */ + protected function path($view) + { + if ($path = $this->exists($view,true)) { return $path; } @@ -139,7 +182,7 @@ public static function file($bundle, $view, $directory) { $directory = str_finish($directory, DS); - // Views may have either the default PHP file extension of the "Blade" + // Views may have either the default PHP file extension or the "Blade" // extension, so we will need to check for both in the view path // and return the first one we find for the given view. if (file_exists($path = $directory.$view.EXT)) @@ -226,13 +269,18 @@ public static function name($view, $name) * }); *
    * - * @param string $view - * @param Closure $composer + * @param string|array $views + * @param Closure $composer * @return void */ - public static function composer($view, $composer) + public static function composer($views, $composer) { - Event::listen("laravel.composing: {$view}", $composer); + $views = (array) $views; + + foreach ($views as $view) + { + Event::listen("laravel.composing: {$view}", $composer); + } } /** @@ -262,7 +310,7 @@ public static function render_each($view, array $data, $iterator, $empty = 'raw| } // If there is no data in the array, we will render the contents of - // the "empty" view. Alternative, the "empty view" can be a raw + // the "empty" view. Alternatively, the "empty view" can be a raw // string that is prefixed with "raw|" for convenience. else { @@ -286,24 +334,32 @@ public static function render_each($view, array $data, $iterator, $empty = 'raw| */ public function render() { - // To allow bundles or other pieces of the application to modify the - // view before it is rendered, we'll fire an event, passing in the - // view instance so it can modified. - $composer = "laravel.composing: {$this->view}"; + static::$render_count++; + + Event::fire("laravel.composing: {$this->view}", array($this)); - Event::fire($composer, array($this)); + $contents = null; // If there are listeners to the view engine event, we'll pass them // the view so they can render it according to their needs, which // allows easy attachment of other view parsers. if (Event::listeners(static::engine)) { - $result = Event::first(static::engine, array($this)); + $result = Event::until(static::engine, array($this)); - if ($result !== false) return $result; + if ( ! is_null($result)) $contents = $result; } - return $this->get(); + if (is_null($contents)) $contents = $this->get(); + + static::$render_count--; + + if (static::$render_count == 0) + { + Section::$sections = array(); + } + + return $contents; } /** @@ -315,6 +371,11 @@ public function get() { $__data = $this->data(); + // The contents of each view file is cached in an array for the + // request since partial views may be rendered inside of for + // loops which could incur performance penalties. + $__contents = $this->load(); + ob_start() and extract($__data, EXTR_SKIP); // We'll include the view contents for parsing within a catcher @@ -322,18 +383,47 @@ public function get() // will throw it out to the exception handler. try { - include $this->path; + eval('?>'.$__contents); } // If we caught an exception, we'll silently flush the output // buffer so that no partially rendered views get thrown out - // to the client and confuse the user. + // to the client and confuse the user with junk. catch (\Exception $e) { ob_get_clean(); throw $e; } - return ob_get_clean(); + $content = ob_get_clean(); + + // The view filter event gives us a last chance to modify the + // evaluated contents of the view and return them. This lets + // us do something like run the contents through Jade, etc. + if (Event::listeners('view.filter')) + { + return Event::first('view.filter', array($content, $this->path)); + } + + return $content; + } + + /** + * Get the contents of the view file from disk. + * + * @return string + */ + protected function load() + { + static::$last = array('name' => $this->view, 'path' => $this->path); + + if (isset(static::$cache[$this->path])) + { + return static::$cache[$this->path]; + } + else + { + return static::$cache[$this->path] = file_get_contents($this->path); + } } /** @@ -500,4 +590,20 @@ public function __toString() return $this->render(); } + /** + * Magic Method for handling dynamic functions. + * + * This method handles calls to dynamic with helpers. + */ + public function __call($method, $parameters) + { + if (strpos($method, 'with_') === 0) + { + $key = substr($method, 5); + return $this->with($key, $parameters[0]); + } + + throw new \Exception("Method [$method] is not defined on the View class."); + } + } \ No newline at end of file diff --git a/app/paths.php b/app/paths.php index 0f434320b..9d0dd842f 100644 --- a/app/paths.php +++ b/app/paths.php @@ -3,66 +3,77 @@ * Laravel - A PHP Framework For Web Artisans * * @package Laravel - * @version 3.1.5 + * @version 3.2.0 * @author Taylor Otwell * @link http://laravel.com */ -// -------------------------------------------------------------- -// Initialize the web variable if it doesn't exist. -// -------------------------------------------------------------- -if ( ! isset($web)) $web = false; +/* +|---------------------------------------------------------------- +| Application Environemtns +|---------------------------------------------------------------- +| +| Laravel takes a dead simple approach to environments, and we +| think you'll love it. Just specify which URLs belongs to a +| given environment, and when you access your application +| from a URL matching that pattern, we'll be sure to +| merge in that environment's configuration files. +| +*/ -// -------------------------------------------------------------- -// Define the directory separator for the environment. -// -------------------------------------------------------------- -if ( ! defined('DS')) -{ - define('DS', DIRECTORY_SEPARATOR); -} +$environments = array( -// -------------------------------------------------------------- -// Define the path to the base directory. -// -------------------------------------------------------------- -$GLOBALS['laravel_paths']['base'] = __DIR__.DS; + 'local' => array('http://localhost*', '*.dev'), + +); // -------------------------------------------------------------- // The path to the application directory. // -------------------------------------------------------------- -$paths['app'] = 'app/application'; +$paths['app'] = 'application'; // -------------------------------------------------------------- // The path to the Laravel directory. // -------------------------------------------------------------- -$paths['sys'] = 'app/laravel'; +$paths['sys'] = 'laravel'; // -------------------------------------------------------------- // The path to the bundles directory. // -------------------------------------------------------------- -$paths['bundle'] = 'app/bundles'; +$paths['bundle'] = 'bundles'; // -------------------------------------------------------------- // The path to the storage directory. // -------------------------------------------------------------- -$paths['storage'] = 'app/storage'; +$paths['storage'] = 'storage'; // -------------------------------------------------------------- -// The path to the vendor directory. +// The path to the public directory. // -------------------------------------------------------------- -$paths['vendor'] = 'app/vendor'; +$paths['public'] = '..'; + +// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*- +// END OF USER CONFIGURATION. HERE BE DRAGONS! +// *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*- // -------------------------------------------------------------- -// The path to the public directory. +// Change to the current working directory. // -------------------------------------------------------------- -if ($web) -{ - $GLOBALS['laravel_paths']['public'] = realpath('').DS; -} -else +chdir(__DIR__); + +// -------------------------------------------------------------- +// Define the directory separator for the environment. +// -------------------------------------------------------------- +if ( ! defined('DS')) { - $paths['public'] = 'public'; + define('DS', DIRECTORY_SEPARATOR); } +// -------------------------------------------------------------- +// Define the path to the base directory. +// -------------------------------------------------------------- +$GLOBALS['laravel_paths']['base'] = __DIR__.DS; + // -------------------------------------------------------------- // Define each constant if it hasn't been defined. // -------------------------------------------------------------- @@ -99,4 +110,4 @@ function path($path) function set_path($path, $value) { $GLOBALS['laravel_paths'][$path] = $value; -} \ No newline at end of file +}