Commit a6eaa069 authored by Taylor Otwell's avatar Taylor Otwell

refactoring routing and comments.

parent a44ca9d5
......@@ -117,6 +117,8 @@ class Arr {
*/
public static function without($array, $without = array())
{
$without = (array) $without;
foreach ((array) $array as $key => $value)
{
if (in_array($value, $without)) unset($array[$key]);
......
......@@ -57,8 +57,8 @@ abstract class Driver {
abstract public function put($key, $value, $minutes);
/**
* Get an item from the cache. If the item doesn't exist in the cache, store
* the default value in the cache and return it.
* Get an item from the cache. If the item doesn't exist in the
* cache, store the default value in the cache and return it.
*
* <code>
* // Get an item from the cache, or cache a value for 15 minutes if it doesn't exist
......
<?php namespace Laravel\Cache\Drivers; use Laravel\Config, Memcache;
<?php namespace Laravel\Cache\Drivers; use Memcache, Laravel\Config;
class Memcached extends Driver {
......
......@@ -12,8 +12,8 @@ class Manager {
/**
* Get a cache driver instance.
*
* If no driver name is specified, the default cache driver will be returned
* as defined in the cache configuration file.
* If no driver name is specified, the default cache driver will be
* returned as defined in the cache configuration file.
*
* <code>
* // Get the default cache driver instance
......@@ -46,8 +46,8 @@ class Manager {
/**
* Pass all other methods to the default cache driver.
*
* Passing method calls to the driver instance provides a convenient API for the developer
* when always using the default cache driver.
* Passing method calls to the driver instance provides a convenient API
* for the developer when always using the default cache driver.
*
* <code>
* // Call the "get" method on the default driver
......
......@@ -339,12 +339,12 @@ abstract class Model {
$this->relating_table = (is_null($table)) ? $this->intermediate_table($model) : $table;
// Allowing the overriding of the foreign and associated keys provides the flexibility for
// self-referential many-to-many relationships, such as a "buddy list".
// Allowing the overriding of the foreign and associated keys provides
// the flexibility for self-referential many-to-many relationships.
$this->relating_key = (is_null($foreign_key)) ? strtolower(static::model_name($this)).'_id' : $foreign_key;
// The associated key is the foreign key name of the related model. So, if the related model
// is "Role", the associated key on the intermediate table would be "role_id".
// The associated key is the foreign key name of the related model.
// If the related model is "Role", the key would be "role_id".
$associated_key = (is_null($associated_key)) ? strtolower(static::model_name($model)).'_id' : $associated_key;
return static::query($model)
......
......@@ -262,20 +262,18 @@ class Grammar {
*/
public function insert(Query $query, $values)
{
// Force every insert to be treated like a batch insert.
// This simply makes creating the SQL syntax a little
// easier on us since we can always treat the values
// as if is an array containing multiple inserts.
// Force every insert to be treated like a batch insert. This simple makes
// creating the SQL syntax a little easier on us since we can always treat
// the values as if it is an array containing multiple inserts.
if ( ! is_array(reset($values))) $values = array($values);
// Since we only care about the column names, we can pass
// any of the insert arrays into the "columnize" method.
// The names should be the same for every insert.
// Since we only care about the column names, we can pass any of the insert
// arrays into the "columnize" method. The names should be the same for
// every insert to the table.
$columns = $this->columnize(array_keys(reset($values)));
// We need to create a string of comma-delimited insert
// segments. Each segment contains PDO place-holders for
// each value being inserted into the table.
// We need to create a string of comma-delimited insert segments. Each segment
// contains PDO place-holders for each value being inserted into the table.
$parameters = implode(', ', array_fill(0, count($values), '('.$this->parameterize(reset($values)).')'));
return 'INSERT INTO '.$this->wrap($query->from).' ('.$columns.') VALUES '.$parameters;
......@@ -314,9 +312,9 @@ class Grammar {
}
/**
* The following functions primarily serve as utility functions
* for the grammar. They perform tasks such as wrapping values
* in keyword identifiers or creating variable lists of bindings.
* The following functions primarily serve as utility functions for
* the grammar. They perform tasks such as wrapping values in keyword
* identifiers or creating variable lists of bindings.
*/
/**
......@@ -385,7 +383,7 @@ class Grammar {
* Get the appropriate query parameter string for a value.
*
* If the value is an expression, the raw expression string should
* will be returned, otherwise, the parameter place-holder will be
* be returned, otherwise, the parameter place-holder will be
* returned by the method.
*
* @param mixed $value
......
......@@ -24,8 +24,8 @@ class Query {
public $selects;
/**
* If the query is performing an aggregate function, this will contain the column
* and and function to use when aggregating.
* If the query is performing an aggregate function, this will contain
* the column and and function to use when aggregating.
*
* @var array
*/
......@@ -250,9 +250,6 @@ class Query {
*/
public function where_in($column, $values, $connector = 'AND', $not = false)
{
// The type set in this method will be used by the query grammar to call the
// appropriate compiler function for the where clause. For cleanliness, the
// compiler for "not in" and "in" statements is broken into two functions.
$type = ($not) ? 'where_not_in' : 'where_in';
$this->wheres[] = compact('type', 'column', 'values', 'connector');
......@@ -309,9 +306,6 @@ class Query {
*/
public function where_null($column, $connector = 'AND', $not = false)
{
// The type set in this method will be used by the query grammar to call the
// appropriate compiler function for the where clause. For cleanliness, the
// compiler for "not null" and "null" statements is broken into two functions.
$type = ($not) ? 'where_not_null' : 'where_null';
$this->wheres[] = compact('type', 'column', 'connector');
......@@ -371,11 +365,13 @@ class Query {
// Split the column names from the connectors.
$segments = preg_split('/(_and_|_or_)/i', $finder, -1, PREG_SPLIT_DELIM_CAPTURE);
// The connector variable will determine which connector will be used for the condition.
// We'll change it as we come across new connectors in the dynamic method string.
// The connector variable will determine which connector will be
// used for the condition. We'll change it as we come across new
// connectors in the dynamic method string.
//
// The index variable helps us get the correct parameter value for the where condition.
// We increment it each time we add a condition.
// The index variable helps us get the correct parameter value
// for the where condition. We increment it each time we add
// a condition to the query.
$connector = 'AND';
$index = 0;
......@@ -501,8 +497,9 @@ class Query {
$results = $this->connection->query($this->grammar->select($this), $this->bindings);
// Reset the SELECT clause so more queries can be performed using the same instance.
// This is helpful for getting aggregates and then getting actual results.
// Reset the SELECT clause so more queries can be performed using
// the same instance. This is helpful for getting aggregates and
// then getting actual results.
$this->selects = null;
return $results;
......@@ -521,8 +518,9 @@ class Query {
$result = $this->connection->only($this->grammar->select($this), $this->bindings);
// Reset the aggregate so more queries can be performed using the same instance.
// This is helpful for getting aggregates and then getting actual results.
// Reset the aggregate so more queries can be performed using
// the same instance. This is helpful for getting aggregates
// and then getting actual results.
$this->aggregate = null;
return $result;
......@@ -537,8 +535,9 @@ class Query {
*/
public function paginate($per_page = 20, $columns = array('*'))
{
// Calculate the current page for the request. The page number will be validated
// and adjusted by the Paginator class, so we can assume it is valid.
// Calculate the current page for the request. The page number
// will be validated and adjusted by the Paginator class,
// so we can assume it is valid.
$page = Paginator::page($total = $this->count(), $per_page);
return Paginator::make($this->for_page($page, $per_page)->get($columns), $total, $per_page);
......@@ -552,9 +551,9 @@ class Query {
*/
public function insert($values)
{
// Force every insert to be treated like a batch insert. This simply makes creating
// the binding array easier. We will simply loop through each inserted row and merge
// the values together to get one big binding array.
// Force every insert to be treated like a batch insert to make creating
// the binding array simpler since we can just spin through the inserted
// rows as if there/ was more than one every time.
if ( ! is_array(reset($values))) $values = array($values);
$bindings = array();
......@@ -568,7 +567,8 @@ class Query {
}
/**
* Insert an array of values into the database table and return the value of the ID column.
* Insert an array of values into the database table and
* return the value of the ID column.
*
* @param array $values
* @param string $sequence
......@@ -626,7 +626,9 @@ class Query {
*/
public function update($values)
{
return $this->connection->query($this->grammar->update($this, $values), array_merge(array_values($values), $this->bindings));
$bindings = array_merge(array_values($values), $this->bindings);
return $this->connection->query($this->grammar->update($this, $values), $bindings);
}
/**
......@@ -647,8 +649,8 @@ class Query {
/**
* Magic Method for handling dynamic functions.
*
* This method handles all calls to aggregate functions as well as the construction
* of dynamic where clauses via the "dynamic_where" method.
* This method handles all calls to aggregate functions as well
* as the construction of dynamic where clauses.
*/
public function __call($method, $parameters)
{
......@@ -659,7 +661,14 @@ class Query {
if (in_array($method, array('abs', 'count', 'min', 'max', 'avg', 'sum')))
{
return ($method == 'count') ? $this->aggregate(strtoupper($method), '*') : $this->aggregate(strtoupper($method), $parameters[0]);
if ($method == 'count')
{
return $this->aggregate(strtoupper($method), '*');
}
else
{
return $this->aggregate(strtoupper($method), $parameters[0]);
}
}
throw new \Exception("Method [$method] is not defined on the Query class.");
......
......@@ -129,7 +129,7 @@ if (Config::$items['session']['driver'] !== '')
{
$flash = array(Input::old_input => Input::get());
Session\Manager::close($driver, $transporter, $flash);
Session\Manager::close($flash);
}
/**
......
......@@ -59,25 +59,18 @@ abstract class Controller {
$response = call_user_func_array(array($controller, $method), $parameters);
}
$filters = array_merge($controller->filters('after'), array('after'));
// The after filter and the framework expects all responses to
// be instances of the Response class. If the route did not
// return an instsance of Response, we will make on now.
if ( ! $response instanceof Response) $response = new Response($response);
$filters = array_merge($this->filters('after'), array('after'));
Filter::run($filters, array($response));
return $response;
}
/**
* Determine if a given controller method is callable.
*
* @param string $method
* @return bool
*/
protected static function hidden($method)
{
return $method == 'before' or $method == 'after' or strncmp($method, '_', 1) == 0;
}
/**
* Resolve a controller name to a controller instance.
*
......@@ -123,6 +116,17 @@ abstract class Controller {
return false;
}
/**
* Determine if a given controller method is callable.
*
* @param string $method
* @return bool
*/
protected static function hidden($method)
{
return $method == 'before' or $method == 'after' or strncmp($method, '_', 1) == 0;
}
/**
* Get an array of filter names defined for the destination.
*
......
......@@ -50,25 +50,28 @@ class Loader {
{
$routes = (file_exists($path = $this->base.'routes'.EXT)) ? require $path : array();
return array_merge($this->nested(Arr::without(explode('/', $uri), array(''))), $routes);
$segments = Arr::without(explode('/', $uri), '');
return array_merge($this->nested($segments), $routes);
}
/**
* Get the appropriate routes from the routes directory for a given URI.
*
* This method works backwards through the URI segments until we find the
* deepest possible matching route directory. Once the deepest directory
* is found, all of the applicable routes will be returend.
*
* @param array $segments
* @return array
*/
protected function nested($segments)
{
// Work backwards through the URI segments until we find the deepest possible
// matching route directory. Once we find it, we will return those routes.
foreach (array_reverse($segments, true) as $key => $value)
{
if (file_exists($path = $this->nest.implode('/', array_slice($segments, 0, $key + 1)).EXT))
{
return require $path;
}
$path = $this->nest.implode('/', array_slice($segments, 0, $key + 1)).EXT;
if (file_exists($path)) return require $path;
}
return array();
......@@ -77,6 +80,10 @@ class Loader {
/**
* Get every route defined for the application.
*
* The entire routes directory will be searched recursively to gather
* every route for the application. Of course, the routes in the root
* routes file will be returned as well.
*
* @return array
*/
public function everything()
......@@ -90,8 +97,8 @@ class Loader {
$routes = array_merge($routes, require $path);
}
// Since route files can be nested deep within the route directory,
// we need to recursively spin through each directory
if ( ! is_dir($this->nest)) return $routes;
$iterator = new Iterator(new DirectoryIterator($this->nest), Iterator::SELF_FIRST);
foreach ($iterator as $file)
......
......@@ -104,8 +104,14 @@ class Route {
{
if ($response instanceof Delegate)
{
return $response;
return Controller::call($response, $this->parameters);
}
else
{
// The after filter and the framework expects all responses to
// be instances of the Response class. If the route did not
// return an instsance of Response, we will make on now.
if ( ! $response instanceof Response) $response = new Response($response);
$filters = array_merge($this->filters('after'), array('after'));
......@@ -113,10 +119,9 @@ class Route {
return $response;
}
else
{
return Response::error('404');
}
return Response::error('404');
}
/**
......
......@@ -88,13 +88,8 @@ class Router {
*/
public function find($name)
{
// First we will check the cache of route names. If we have already found the given route,
// we will simply return that route from the cache to improve performance.
if (array_key_exists($name, $this->names)) return $this->names[$name];
// Spin through every route defined for the application searching for a route that has
// a name matching the name passed to the method. If the route is found, it will be
// cached in the array of named routes and returned.
foreach ($this->loader->everything() as $key => $value)
{
if (is_array($value) and isset($value['name']) and $value['name'] === $name)
......@@ -116,11 +111,10 @@ class Router {
$routes = $this->loader->load($uri);
// Put the request method and URI in route form. Routes begin with
// the request method and a forward slash.
// the request method and a forward slash followed by the URI.
$destination = $method.' /'.trim($uri, '/');
// Check for a literal route match first. If we find one, there is
// no need to spin through all of the routes.
// Check for a literal route match first...
if (isset($routes[$destination]))
{
return Request::$route = new Route($destination, $routes[$destination], array());
......@@ -130,24 +124,44 @@ class Router {
{
// Only check routes that have multiple URIs or wildcards since other
// routes would have been caught by the check for literal matches.
if (strpos($keys, '(') !== false or strpos($keys, ',') !== false )
if (strpos($keys, '(') !== false or strpos($keys, ',') !== false)
{
if ( ! is_null($route = $this->match($destination, $keys, $callback)))
{
return Request::$route = $route;
}
}
}
return Request::$route = $this->controller($method, $uri, $destination);
}
/**
* Attempt to match a given route destination to a given route.
*
* The destination's methods and URIs will be compared against the route's.
* If there is a match, the Route instance will be returned, otherwise null
* will be returned by the method.
*
* @param string $destination
* @param array $keys
* @param mixed $callback
* @return mixed
*/
protected function match($destination, $keys, $callback)
{
foreach (explode(', ', $keys) as $key)
{
// Append the provided formats to the route as an optional regular expression.
if ( ! is_null($formats = $this->provides($callback))) $key .= '(\.('.implode('|', $formats).'))?';
if (preg_match('#^'.$this->translate_wildcards($key).'$#', $destination))
if (preg_match('#^'.$this->wildcards($key).'$#', $destination))
{
return Request::$route = new Route($keys, $callback, $this->parameters($destination, $key));
}
return new Route($keys, $callback, $this->parameters($destination, $key));
}
}
}
return Request::$route = $this->route_to_controller($method, $uri, $destination);
}
/**
* Attempt to find a controller for the incoming request.
*
......@@ -156,10 +170,11 @@ class Router {
* @param string $destination
* @return Route
*/
protected function route_to_controller($method, $uri, $destination)
protected function controller($method, $uri, $destination)
{
// If the request is to the root of the application, an ad-hoc route will be generated
// to the home controller's "index" method, making it the default controller method.
// If the request is to the root of the application, an ad-hoc route
// will be generated to the home controller's "index" method, making
// it the default controller method.
if ($uri === '/') return new Route($method.' /', 'home@index');
$segments = explode('/', trim($uri, '/'));
......@@ -228,7 +243,7 @@ class Router {
* @param string $key
* @return string
*/
protected function translate_wildcards($key)
protected function wildcards($key)
{
$replacements = 0;
......
......@@ -43,7 +43,7 @@ class Auth {
* Get the current user of the application.
*
* This method will call the "user" closure in the authentication configuration file.
* If the user is not authenticated, null will be returned.
* If the user is not authenticated, null will be returned by the methd.
*
* If no user exists in the session, the method will check for a "remember me"
* cookie and attempt to login the user based on the value of that cookie.
......@@ -75,6 +75,10 @@ class Auth {
/**
* Attempt to login a user based on a long-lived "remember me" cookie.
*
* We should be able to trust the cookie is valid, since all cookies
* set by Laravel include a fingerprint hash. So, the cookie should
* be safe to use within this method.
*
* @param string $cookie
* @return mixed
*/
......@@ -82,14 +86,7 @@ class Auth {
{
$cookie = explode('|', Crypter::decrypt($cookie));
// If there are not at least two elements in the array, the decrypted value
// is not valid and we wil just bail out of the method since the cookie may
// have been tampered with and should not be considered trustworthy.
if (count($cookie) < 2) return;
list($id, $username, $config) = array($cookie[0], $cookie[1], Config::get('auth'));
if ( ! is_null($user = call_user_func($config['user'], $id)) and $user->{$config['username']} === $username)
if ( ! is_null($user = call_user_func(Config::get('auth.user'), $cookie[0])))
{
static::login($user);
......@@ -100,14 +97,12 @@ class Auth {
/**
* Attempt to log a user into the application.
*
* If the given credentials are valid, the user will be logged into
* the application and their user ID will be stored in the session
* via the "login" method.
* If the credentials are valid, the user will be logged into the application
* and their user ID will be stored in the session via the "login" method.
*
* The user may also be "remembered". When this option is set, the user
* will be automatically logged into the application for one year via
* an encrypted cookie containing their ID. Of course, if the user logs
* out of the application, they will no longer be remembered.
* The user may also be "remembered", which will keep the user logged into the
* application for one year or until they logout. The user is rememberd via
* an encrypted cookie.
*
* @param string $username
* @param string $password
......@@ -139,7 +134,7 @@ class Auth {
{
static::$user = $user;
if ($remember) static::remember($user->id, $user->{Config::get('auth.username')});
if ($remember) static::remember($user->id);
Session::put(Auth::user_key, $user->id);
}
......@@ -148,17 +143,16 @@ class Auth {
* Set a cookie so that users are "remembered" and don't need to login.
*
* @param string $id
* @param string $username
* @return void
*/
protected static function remember($id, $username)
protected static function remember($id)
{
$cookie = Crypter::encrypt($id.'|'.$username.'|'.Str::random(40));
$cookie = 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 settings are the same.
// 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 settings are the same.
$config = Config::get('session');
Cookie::forever(Auth::remember_key, $cookie, $config['path'], $config['domain'], $config['secure']);
......@@ -167,9 +161,9 @@ class Auth {
/**
* Log the current user out of the application.
*
* The "logout" closure in the authenciation configuration file
* will be called. All authentication cookies will be deleted
* and the user ID will be removed from the session.
* The "logout" closure in the authenciation configuration file will be
* called. All authentication cookies will be deleted and the user ID
* will be removed from the session.
*
* @return void
*/
......
<?php namespace Laravel\Security; use Laravel\Config;
if (trim(Config::get('application.key')) === '')
if (trim(Config::$items['application']['key']) === '')
{
throw new \Exception('The encryption class may not be used without an application key.');
}
......@@ -24,9 +24,8 @@ class Crypter {
/**
* Encrypt a string using Mcrypt.
*
* The string will be encrypted using the cipher and mode specified
* when the crypter instance was created, and the final result will
* be base64 encoded.
* The string will be encrypted using the cipher and mode specified when the
* crypter instance was created, and the final result will be base64 encoded.
*
* <code>
* // Encrypt a string using the Mcrypt PHP extension
......@@ -55,7 +54,9 @@ class Crypter {
$iv = mcrypt_create_iv(static::iv_size(), $randomizer);
return base64_encode($iv.mcrypt_encrypt(static::$cipher, Config::get('application.key'), $value, static::$mode, $iv));
$key = Config::$items['application']['key'];
return base64_encode($iv.mcrypt_encrypt(static::$cipher, $key, $value, static::$mode, $iv));
}
/**
......@@ -85,7 +86,9 @@ class Crypter {
$value = substr($value, static::iv_size());
return rtrim(mcrypt_decrypt(static::$cipher, Config::get('application.key'), $value, static::$mode, $iv), "\0");
$key = Config::$items['application']['key'];
return rtrim(mcrypt_decrypt(static::$cipher, $key, $value, static::$mode, $iv), "\0");
}
/**
......
......@@ -5,10 +5,11 @@ class Hasher {
/**
* Hash a password using the Bcrypt hashing scheme.
*
* Bcrypt provides a future-proof hashing algorithm by allowing the number
* of "rounds" to be increased, thus increasing the time is takes to generate
* the hashed value. The longer is takes to generate the hash, the more
* impractical a rainbow table attack against the hashes becomes.
* Bcrypt provides a future-proof hashing algorithm by allowing the
* number of "rounds" to be increased, thus increasing the time it
* takes to generate the hashed value. The longer it takes takes
* to generate the hash, the more impractical a rainbow table
* attack against the hashes becomes.
*
* <code>
* // Create a Bcrypt hash of a value
......@@ -42,14 +43,14 @@ class Hasher {
/**
* Get a salt for use during Bcrypt hashing.
*
* Bcrypt expects salts to be 22 alpha-numeric characters including
* dots and forward slashes. OpenSSL will be used if available and
* the Str::random method will be used if it isn't.
*
* @return string
*/
protected static function salt()
{
// If OpenSSL is installed, we will use it to gather random bytes for generating
// the salt value. Otherwise, we will use the Str::random method. Bcrypt expects
// the salt to be a 22 character alpha-numeric string. The salt may also contain
// dots, plus signs, and forward slashes.
if (function_exists('openssl_random_pseudo_bytes'))
{
return substr(strtr(base64_encode(openssl_random_pseudo_bytes(16)), '+', '.'), 0 , 22);
......
......@@ -29,6 +29,20 @@ class Manager {
*/
public static $regenerated = false;
/**
* The driver being used by the session.
*
* @var Drivers\Driver
*/
protected static $driver;
/**
* The session ID transporter used by the session.
*
* @var Transporters\Transpoter
*/
protected static $transporter;
/**
* Start the session handling for the current request.
*
......@@ -40,28 +54,30 @@ class Manager {
{
$config = Config::$items['session'];
static::$session = $driver->load($transporter->get($config));
$session = $driver->load($transporter->get($config));
// If the session is expired, a new session will be generated and all of
// the data from the previous session will be lost. The new session will
// be assigned a random, long string ID to uniquely identify it among
// the application's current users.
if (is_null(static::$session) or (time() - static::$session['last_activity']) > ($config['lifetime'] * 60))
if (is_null($session) or (time() - $session['last_activity']) > ($config['lifetime'] * 60))
{
static::$exists = false;
static::$session = array('id' => Str::random(40), 'data' => array());
$session = array('id' => Str::random(40), 'data' => array());
}
static::$session = $session;
// If a CSRF token is not present in the session, we will generate one.
// These tokens are generated per session to protect against Cross-Site
// Request Forgery attacks on the application. It is up to the developer
// to take advantage of them using the token methods on the Form class
// and the "csrf" route filter.
// Request Forgery attacks on the application.
if ( ! static::has('csrf_token'))
{
static::put('csrf_token', Str::random(16));
}
list(static::$driver, static::$transporter) = array($driver, $transporter);
}
/**
......@@ -253,12 +269,10 @@ class Manager {
/**
* Close the session handling for the request.
*
* @param Drivers\Driver $driver
* @param Transporters\Transporter $transporter
* @param array $flash
* @return void
*/
public static function close(Driver $driver, Transporter $transporter, $flash = array())
public static function close($flash = array())
{
$config = Config::$items['session'];
......@@ -267,17 +281,17 @@ class Manager {
static::flash($key, $value);
}
$driver->save(static::age(), $config, static::$exists);
static::$driver->save(static::age(), $config, static::$exists);
$transporter->put(static::$session['id'], $config);
static::$transporter->put(static::$session['id'], $config);
// Some session drivers may implement the Sweeper interface, meaning the
// driver must do its garbage collection manually. Alternatively, some
// drivers such as APC and Memcached are not required to manually
// clean up their sessions.
if (mt_rand(1, $config['sweepage'][1]) <= $config['sweepage'][0] and $driver instanceof Drivers\Sweeper)
if (mt_rand(1, $config['sweepage'][1]) <= $config['sweepage'][0] and static::$driver instanceof Drivers\Sweeper)
{
$driver->sweep(time() - ($config['lifetime'] * 60));
static::$driver->sweep(time() - ($config['lifetime'] * 60));
}
}
......
......@@ -99,12 +99,17 @@ class Str {
}
/**
* Limit the number of chars in a string
* Limit the number of characters in a string.
*
* Word integrity is preserved, so the number of characters in the
* truncated string will be rounded to the nearest word ending.
*
* <code>
* // Limit the characters
* echo Str::limit_chars('taylor otwell', 3);
* results in 'tay...'
* // Returns "Taylor..."
* echo Str::limit('Taylor Otwell', 3);
*
* // Limit the number of characters and append a custom ending
* echo Str::limit('Taylor Otwell', 3, '---');
* </code>
*
* @param string $value
......@@ -112,25 +117,24 @@ class Str {
* @param string $end
* @return string
*/
public static function limit($value, $length = 100, $end = '...')
public static function limit($value, $limit = 100, $end = '...')
{
if (static::length($value) <= $length) return $value;
if (static::length($value) < $limit) return $value;
if (function_exists('mb_substr'))
{
return mb_substr($value, 0, $length, Config::get('application.encoding')).$end;
}
$limit = preg_replace('/\s+?(\S+)?$/', '', substr($value, 0, $limit));
return substr($value, 0, $length).$end;
return (static::length($limit) == static::length($value)) ? $value : $limit.$end;
}
/**
* Limit the number of words in a string
*
* <code>
* // Limit the words
* echo Str::limit_chars('This is a sentence.', 3);
* results in 'This is a...'
* // Returns "This is a..."
* echo Str::words('This is a sentence.', 3);
*
* // Limit the number of words and append a custom ending
* echo Str::words('This is a sentence.', 3, '---');
* </code>
*
* @param string $value
......@@ -138,13 +142,13 @@ class Str {
* @param string $end
* @return string
*/
public static function limit_words($value, $length = 100, $end = '...')
public static function words($value, $words = 100, $end = '...')
{
$count = str_word_count($value,1);
$count = str_word_count($value, 1);
if ($count <= $length) return $value;
if ($count <= $words) return $value;
return implode(' ',array_slice($count,0,$length)).$end;
return implode(' ', array_slice($count, 0, $words)).$end;
}
/**
......
......@@ -93,7 +93,12 @@ class Messages {
{
if (is_null($key)) return $this->all($format);
return (array_key_exists($key, $this->messages)) ? $this->format($this->messages[$key], $format) : array();
if (array_key_exists($key, $this->messages))
{
return $this->format($this->messages[$key], $format);
}
return array();
}
/**
......
......@@ -10,11 +10,25 @@ use Laravel\Database\Manager as DB;
class Validator {
/**
* The registered custom validators.
* The database connection that should be used by the validator.
*
* @var Database\Connection
*/
public $connection;
/**
* The array being validated.
*
* @var array
*/
protected static $validators = array();
public $attributes;
/**
* The post-validation error messages.
*
* @var Messages
*/
public $errors;
/**
* The validation rules.
......@@ -38,39 +52,25 @@ class Validator {
protected $language;
/**
* The size related validation rules.
* The registered custom validators.
*
* @var array
*/
protected $size_rules = array('size', 'between', 'min', 'max');
protected static $validators = array();
/**
* The numeric related validation rules.
* The size related validation rules.
*
* @var array
*/
protected $numeric_rules = array('numeric', 'integer');
/**
* The database connection that should be used by the validator.
*
* @var Database\Connection
*/
public $connection;
protected $size_rules = array('size', 'between', 'min', 'max');
/**
* The array being validated.
* The numeric related validation rules.
*
* @var array
*/
public $attributes;
/**
* The post-validation error messages.
*
* @var Messages
*/
public $errors;
protected $numeric_rules = array('numeric', 'integer');
/**
* Create a new validator instance.
......@@ -163,29 +163,50 @@ class Validator {
throw new \Exception("Validation rule [$rule] doesn't exist.");
}
// No validation will be run for attributes that do not exist unless the rule being validated
// is "required" or "accepted". No other rules have implicit "required" checks.
// Extract the actual value for the attribute. We don't want every rule
// to worry about obtaining the value from the array of attributes.
$value = (isset($this->attributes[$attribute])) ? $this->attributes[$attribute] : null;
// No validation will be run for attributes that do not exist unless the
// rule being validated is "required" or "accepted". No other rules have
// implicit "required" checks for validation.
if ( ! $this->validate_required($attribute) and ! in_array($rule, array('required', 'accepted'))) return;
if ( ! $this->$validator($attribute, $parameters, $this))
if ( ! $this->$validator($attribute, $value, $parameters, $this))
{
$message = $this->format_message($this->get_message($attribute, $rule), $attribute, $rule, $parameters);
$this->errors->add($attribute, $message, $attribute, $rule, $parameters);
$this->error($attribute, $rule, $parameters);
}
}
/**
* Add an error message to the validator's collection of messages.
*
* @param string $attribute
* @param string $rule
* @param array $parameters
* @return void
*/
protected function error($attribute, $rule, $parameters)
{
$message = $this->get_message($attribute, $rule);
$message = $this->format_message($message, $attribute, $rule, $parameters);
$this->errors->add($attribute, $message);
}
/**
* Validate that a required attribute exists in the attributes array.
*
* @param string $attribute
* @param mixed $value
* @return bool
*/
protected function validate_required($attribute)
protected function validate_required($attribute, $value)
{
if ( ! array_key_exists($attribute, $this->attributes)) return false;
if (is_null($value)) return false;
if (is_string($this->attributes[$attribute]) and trim($this->attributes[$attribute]) === '') return false;
if (is_string($value) and trim($value) === '') return false;
return true;
}
......@@ -194,12 +215,11 @@ class Validator {
* Validate that an attribute has a matching confirmation attribute.
*
* @param string $attribute
* @param mixed $value
* @return bool
*/
protected function validate_confirmed($attribute)
protected function validate_confirmed($attribute, $value)
{
$value = $this->attributes[$attribute];
$confirmation = $this->attributes[$attribute.'_confirmation'];
return array_key_exists($attribute.'_confirmation', $this->attributes) and $value == $confirmation;
......@@ -211,12 +231,11 @@ class Validator {
* This validation rule implies the attribute is "required".
*
* @param string $attribute
* @param mixed $value
* @return bool
*/
protected function validate_accepted($attribute)
protected function validate_accepted($attribute, $value)
{
$value = $this->attributes[$attribute];
return $this->validate_required($attribute) and ($value == 'yes' or $value == '1');
}
......@@ -224,32 +243,35 @@ class Validator {
* Validate that an attribute is numeric.
*
* @param string $attribute
* @param mixed $value
* @return bool
*/
protected function validate_numeric($attribute)
protected function validate_numeric($attribute, $value)
{
return is_numeric($this->attributes[$attribute]);
return is_numeric($value);
}
/**
* Validate that an attribute is an integer.
*
* @param string $attribute
* @param mixed $value
* @return bool
*/
protected function validate_integer($attribute)
protected function validate_integer($attribute, $value)
{
return filter_var($this->attributes[$attribute], FILTER_VALIDATE_INT) !== false;
return filter_var($value, FILTER_VALIDATE_INT) !== false;
}
/**
* Validate the size of an attribute.
*
* @param string $attribute
* @param mixed $value
* @param array $parameters
* @return bool
*/
protected function validate_size($attribute, $parameters)
protected function validate_size($attribute, $value, $parameters)
{
return $this->get_size($attribute) == $parameters[0];
}
......@@ -258,10 +280,11 @@ class Validator {
* Validate the size of an attribute is between a set of values.
*
* @param string $attribute
* @param mixed $value
* @param array $parameters
* @return bool
*/
protected function validate_between($attribute, $parameters)
protected function validate_between($attribute, $value, $parameters)
{
return $this->get_size($attribute) >= $parameters[0] and $this->get_size($attribute) <= $parameters[1];
}
......@@ -270,10 +293,11 @@ class Validator {
* Validate the size of an attribute is greater than a minimum value.
*
* @param string $attribute
* @param mixed $value
* @param array $parameters
* @return bool
*/
protected function validate_min($attribute, $parameters)
protected function validate_min($attribute, $value, $parameters)
{
return $this->get_size($attribute) >= $parameters[0];
}
......@@ -282,10 +306,11 @@ class Validator {
* Validate the size of an attribute is less than a maximum value.
*
* @param string $attribute
* @param mixed $value
* @param array $parameters
* @return bool
*/
protected function validate_max($attribute, $parameters)
protected function validate_max($attribute, $value, $parameters)
{
return $this->get_size($attribute) <= $parameters[0];
}
......@@ -305,31 +330,40 @@ class Validator {
$value = $this->attributes[$attribute];
return (array_key_exists($attribute, Input::file())) ? $value['size'] / 1024 : Str::length(trim($value));
if (array_key_exists($attribute, Input::file()))
{
return $value['size'] / 1024;
}
else
{
return Str::length(trim($value));
}
}
/**
* Validate an attribute is contained within a list of values.
*
* @param string $attribute
* @param mixed $value
* @param array $parameters
* @return bool
*/
protected function validate_in($attribute, $parameters)
protected function validate_in($attribute, $value, $parameters)
{
return in_array($this->attributes[$attribute], $parameters);
return in_array($value, $parameters);
}
/**
* Validate an attribute is not contained within a list of values.
*
* @param string $attribute
* @param mixed $value
* @param array $parameters
* @return bool
*/
protected function validate_not_in($attribute, $parameters)
protected function validate_not_in($attribute, $value, $parameters)
{
return ! in_array($this->attributes[$attribute], $parameters);
return ! in_array($value, $parameters);
}
/**
......@@ -338,49 +372,53 @@ class Validator {
* If a database column is not specified, the attribute name will be used.
*
* @param string $attribute
* @param mixed $value
* @param array $parameters
* @return bool
*/
protected function validate_unique($attribute, $parameters)
protected function validate_unique($attribute, $value, $parameters)
{
if ( ! isset($parameters[1])) $parameters[1] = $attribute;
if (is_null($this->connection)) $this->connection = DB::connection();
return $this->connection->table($parameters[0])->where($parameters[1], '=', $this->attributes[$attribute])->count() == 0;
return $this->connection->table($parameters[0])->where($parameters[1], '=', $value)->count() == 0;
}
/**
* Validate than an attribute is a valid e-mail address.
*
* @param string $attribute
* @param mixed $value
* @return bool
*/
protected function validate_email($attribute)
protected function validate_email($attribute, $value)
{
return filter_var($this->attributes[$attribute], FILTER_VALIDATE_EMAIL) !== false;
return filter_var($value, FILTER_VALIDATE_EMAIL) !== false;
}
/**
* Validate than an attribute is a valid URL.
*
* @param string $attribute
* @param mixed $value
* @return bool
*/
protected function validate_url($attribute)
protected function validate_url($attribute, $value)
{
return filter_var($this->attributes[$attribute], FILTER_VALIDATE_URL) !== false;
return filter_var($value, FILTER_VALIDATE_URL) !== false;
}
/**
* Validate that an attribute is an active URL.
*
* @param string $attribute
* @param mixed $value
* @return bool
*/
protected function validate_active_url($attribute)
protected function validate_active_url($attribute, $value)
{
$url = str_replace(array('http://', 'https://', 'ftp://'), '', Str::lower($this->attributes[$attribute]));
$url = str_replace(array('http://', 'https://', 'ftp://'), '', Str::lower($value));
return checkdnsrr($url);
}
......@@ -389,9 +427,10 @@ class Validator {
* Validate the MIME type of a file is an image MIME type.
*
* @param string $attribute
* @param mixed $value
* @return bool
*/
protected function validate_image($attribute)
protected function validate_image($attribute, $value)
{
return $this->validate_mimes($attribute, array('jpg', 'png', 'gif', 'bmp'));
}
......@@ -400,33 +439,36 @@ class Validator {
* Validate than an attribute contains only alphabetic characters.
*
* @param string $attribute
* @param mixed $value
* @return bool
*/
protected function validate_alpha($attribute)
protected function validate_alpha($attribute, $value)
{
return preg_match('/^([a-z])+$/i', $this->attributes[$attribute]);
return preg_match('/^([a-z])+$/i', $value);
}
/**
* Validate than an attribute contains only alpha-numeric characters.
*
* @param string $attribute
* @param mixed $value
* @return bool
*/
protected function validate_alpha_num($attribute)
protected function validate_alpha_num($attribute, $value)
{
return preg_match('/^([a-z0-9])+$/i', $this->attributes[$attribute]);
return preg_match('/^([a-z0-9])+$/i', $value);
}
/**
* Validate than an attribute contains only alpha-numeric characters, dashes, and underscores.
*
* @param string $attribute
* @param mixed $value
* @return bool
*/
protected function validate_alpha_dash($attribute)
protected function validate_alpha_dash($attribute, $value)
{
return preg_match('/^([-a-z0-9_-])+$/i', $this->attributes[$attribute]);
return preg_match('/^([-a-z0-9_-])+$/i', $value);
}
/**
......@@ -549,9 +591,9 @@ class Validator {
*/
protected function parse($rule)
{
// The format for specifying validation rules and parameters follows a {rule}:{parameters}
// convention. For instance, "max:3" specifies that the value may only be 3 characters in
// length. And "unique:users" specifies that a value must be unique on the "users" table.
// The format for specifying validation rules and parameters follows
// a {rule}:{parameters} convention. For instance, "max:3" specifies
// that the value may only be 3 characters in length.
$parameters = (($colon = strpos($rule, ':')) !== false) ? explode(',', substr($rule, $colon + 1)) : array();
return array(is_numeric($colon) ? substr($rule, 0, $colon) : $rule, $parameters);
......@@ -586,9 +628,9 @@ class Validator {
*/
public function __call($method, $parameters)
{
// First we will slice the "validate_" prefix off of the validator since custom
// validators are not registered with such a prefix. We will then call the validator
// and pass it the parameters we received.
// First we will slice the "validate_" prefix off of the validator
// since custom validators are not registered with such a prefix.
// Then, if a custom validator exists, we will call it.
if (isset(static::$validators[$method = substr($method, 9)]))
{
return call_user_func_array(static::$validators[$method], $parameters);
......
......@@ -133,9 +133,10 @@ class View {
{
if (is_null(static::$composers)) static::$composers = require APP_PATH.'composers'.EXT;
// The view's name may specified in several different ways in the composers file.
// The composer may simple have a string value, which is the name. Or, the composer
// could have an array value in which a "name" key exists.
// The view's name may specified in several different ways in the
// composers file. The composer may simple have a string value,
// which is the name. Or, the composer could have an array
// value in which a "name" key exists.
foreach (static::$composers as $key => $value)
{
if ($name === $value or (is_array($value) and $name === Arr::get($value, 'name'))) return $key;
......@@ -170,17 +171,15 @@ class View {
{
static::compose($this);
// All nested views and responses are evaluated before the main view. This allows
// the assets used by these views to be added to the asset container before the
// All nested views and responses are evaluated before the main view.
// This allows the assets used by these views to be added to the asset
// container before the
// main view is evaluated and dumps the links to the assets.
foreach ($this->data as &$data)
{
if ($data instanceof View or $data instanceof Response) $data = $data->render();
}
// We don't want the view's contents to be rendered immediately, so we will fire
// up an output buffer to catch the view output. The output of the view will be
// rendered automatically later in the request lifecycle.
ob_start() and extract($this->data, EXTR_SKIP);
// If the view is a "Blade" view, we need to check the view for modifications
......@@ -200,14 +199,15 @@ class View {
*/
protected function compile()
{
// For simplicity, compiled views are stored in a single directory by the MD5 hash of
// their name. This allows us to avoid recreating the entire view directory structure
// within the compiled views directory.
// For simplicity, compiled views are stored in a single directory by
// the MD5 hash of their name. This allows us to avoid recreating the
// entire view directory structure within the compiled views directory.
$compiled = STORAGE_PATH.'views/'.md5($this->view);
// The view will only be re-compiled if the view has been modified since the last compiled
// version of the view was created or no compiled view exists. Otherwise, the path will
// be returned without re-compiling.
// The view will only be re-compiled if the view has been modified
// since the last compiled version of the view was created or no
// compiled view exists. Otherwise, the path will be returned
// without re-compiling.
if ((file_exists($compiled) and filemtime($this->path) > filemtime($compiled)) or ! file_exists($compiled))
{
file_put_contents($compiled, Blade::parse($this->path));
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment