Commit a6eaa069 authored by Taylor Otwell's avatar Taylor Otwell

refactoring routing and comments.

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