diff --git a/application/config/database.php b/application/config/database.php index e4852ba3..d5e6ac1b 100644 --- a/application/config/database.php +++ b/application/config/database.php @@ -113,7 +113,11 @@ return array( 'redis' => array( - 'default' => array('host' => '127.0.0.1', 'port' => 6379), + 'default' => array( + 'host' => '127.0.0.1', + 'port' => 6379, + 'database' => 0 + ), ), diff --git a/application/language/en/validation.php b/application/language/en/validation.php index 8c230f21..8285f1e9 100644 --- a/application/language/en/validation.php +++ b/application/language/en/validation.php @@ -36,6 +36,7 @@ return array( "in" => "The selected :attribute is invalid.", "integer" => "The :attribute must be an integer.", "ip" => "The :attribute must be a valid IP address.", + "match" => "The :attribute format is invalid.", "max" => array( "numeric" => "The :attribute must be less than :max.", "file" => "The :attribute must be less than :max kilobytes.", diff --git a/changes.txt b/changes.txt index cf990e5c..ba30f948 100644 --- a/changes.txt +++ b/changes.txt @@ -20,4 +20,8 @@ Changes for 3.1: - Added controller::detect method. - Added Cache::forever method. - Controller layouts now resolved in Laravel\Controller __construct. - - Rewrote Eloquent and included in core. \ No newline at end of file + - Rewrote Eloquent and included in core. + - Added "match" validation rule. + - Fixed table prefix bug. + - Added Form::macro method. + - Added Route::forward method. \ No newline at end of file diff --git a/laravel/cli/tasks/migrate/migrator.php b/laravel/cli/tasks/migrate/migrator.php index 0ee059ad..5bf7617b 100644 --- a/laravel/cli/tasks/migrate/migrator.php +++ b/laravel/cli/tasks/migrate/migrator.php @@ -154,9 +154,9 @@ class Migrator extends Task { // the bundle name and string migration name as an unique ID // for the migrations, allowing us to easily identify which // migrations have been run for each bundle. - $table->string('bundle'); + $table->string('bundle', 50); - $table->string('name'); + $table->string('name', 200); // When running a migration command, we will store a batch // ID with each of the rows on the table. This will allow diff --git a/laravel/database/grammar.php b/laravel/database/grammar.php index 9dcb34ee..13d92d8e 100644 --- a/laravel/database/grammar.php +++ b/laravel/database/grammar.php @@ -89,21 +89,34 @@ abstract class Grammar { // Since columns may be prefixed with their corresponding table // name so as to not make them ambiguous, we will need to wrap // the table and the column in keyword identifiers. - foreach (explode('.', $value) as $segment) + $segments = explode('.', $value); + + foreach ($segments as $key => $value) { - if ($segment == '*') + if ($key == 0 and count($segments) > 1) { - $wrapped[] = $segment; + $wrapped[] = $this->wrap_table($value); } else { - $wrapped[] = sprintf($this->wrapper, $segment); + $wrapped[] = $this->wrap_value($value); } } return implode('.', $wrapped); } + /** + * Wrap a single string value in keyword identifiers. + * + * @param string $value + * @return string + */ + protected function wrap_value($value) + { + return ($value !== '*') ? sprintf($this->wrapper, $value) : $value; + } + /** * Create query parameters from an array of values. * diff --git a/laravel/database/query.php b/laravel/database/query.php index e19708ae..9d221176 100644 --- a/laravel/database/query.php +++ b/laravel/database/query.php @@ -182,7 +182,7 @@ class Query { * @param string $column2 * @return Query */ - public function left_join($table, $column1, $operator, $column2) + public function left_join($table, $column1, $operator = null, $column2 = null) { return $this->join($table, $column1, $operator, $column2, 'LEFT'); } diff --git a/laravel/database/schema/grammars/grammar.php b/laravel/database/schema/grammars/grammar.php index ad0dfbcc..1d3390a1 100644 --- a/laravel/database/schema/grammars/grammar.php +++ b/laravel/database/schema/grammars/grammar.php @@ -32,7 +32,22 @@ abstract class Grammar extends \Laravel\Database\Grammar { $sql = "ALTER TABLE $table ADD CONSTRAINT $name "; - return $sql .= "FOREIGN KEY ($foreign) REFERENCES $on ($referenced)"; + $sql .= "FOREIGN KEY ($foreign) REFERENCES $on ($referenced)"; + + // Finally we will check for any "on delete" or "on update" options for + // the foreign key. These control the behavior of the constraint when + // an update or delete statement is run against the record. + if ( ! is_null($command->on_delete)) + { + $sql .= " ON DELETE {$command->on_delete}"; + } + + if ( ! is_null($command->on_update)) + { + $sql .= " ON UPDATE {$command->on_update}"; + } + + return $sql; } /** diff --git a/laravel/file.php b/laravel/file.php index 8d3cee74..0346c83c 100644 --- a/laravel/file.php +++ b/laravel/file.php @@ -68,6 +68,30 @@ class File { if (static::exists($path)) @unlink($path); } + /** + * Move a file to a new location. + * + * @param string $path + * @param string $target + * @return void + */ + public static function move($path, $target) + { + return rename($path, $target); + } + + /** + * Copy a file to a new location. + * + * @param string $path + * @param string $target + * @return void + */ + public static function copy($path, $target) + { + return copy($path, $target); + } + /** * Extract the file extension from a file path. * @@ -173,6 +197,18 @@ class File { return false; } + /** + * Create a new directory. + * + * @param string $path + * @param int $chmod + * @return void + */ + public static function mkdir($path, $chmod = 0777) + { + return ( ! is_dir($path)) ? mkdir($path, $chmod, true) : true; + } + /** * Move a directory from one location to another. * @@ -183,7 +219,7 @@ class File { */ public static function mvdir($source, $destination, $options = fIterator::SKIP_DOTS) { - static::cpdir($source, $destination, true, $options); + return static::cpdir($source, $destination, true, $options); } /** @@ -197,7 +233,7 @@ class File { */ public static function cpdir($source, $destination, $delete = false, $options = fIterator::SKIP_DOTS) { - if ( ! is_dir($source)) return; + if ( ! is_dir($source)) return false; // First we need to create the destination directory if it doesn't // already exists. This directory hosts all of the assets we copy @@ -221,7 +257,7 @@ class File { { $path = $item->getRealPath(); - static::cpdir($path, $location, $delete, $options); + if (! static::cpdir($path, $location, $delete, $options)) return false; if ($delete) @rmdir($item->getRealPath()); } @@ -231,22 +267,25 @@ class File { // files with the same name. else { - copy($item->getRealPath(), $location); + if(! copy($item->getRealPath(), $location)) return false; if ($delete) @unlink($item->getRealPath()); } } if ($delete) rmdir($source); + + return true; } /** * Recursively delete a directory. * * @param string $directory + * @param bool $preserve * @return void */ - public static function rmdir($directory) + public static function rmdir($directory, $preserve = false) { if ( ! is_dir($directory)) return; @@ -267,7 +306,18 @@ class File { } } - @rmdir($directory); + if ( ! $preserve) @rmdir($directory); + } + + /** + * Empty the specified directory of all files and folders. + * + * @param string $directory + * @return void + */ + public static function cleandir($directory) + { + return static::rmdir($directory, true); } /** diff --git a/laravel/form.php b/laravel/form.php index 155bb6d5..a768f503 100644 --- a/laravel/form.php +++ b/laravel/form.php @@ -7,7 +7,26 @@ class Form { * * @var array */ - protected static $labels = array(); + public static $labels = array(); + + /** + * The registered custom macros. + * + * @var array + */ + public static $macros = array(); + + /** + * Registers a custom macro. + * + * @param string $name + * @param Closure $input + * @return void + */ + public static function macro($name, $macro) + { + static::$macros[$name] = $macro; + } /** * Open a HTML form. @@ -52,8 +71,7 @@ class Form { // Since PUT and DELETE methods are not actually supported by HTML forms, // we'll create a hidden input element that contains the request method - // and set the actual request method to POST. Laravel will look for the - // hidden element to determine the request method. + // and set the actual request method variable to POST. if ($method == 'PUT' or $method == 'DELETE') { $append = static::hidden(Request::spoofer, $method); @@ -541,8 +559,7 @@ class Form { { // If an ID has been explicitly specified in the attributes, we will // use that ID. Otherwise, we will look for an ID in the array of - // label names as this makes it convenient to give input elements - // the same ID as their corresponding labels. + // label names so labels and their elements have the same ID. if (array_key_exists('id', $attributes)) { return $attributes['id']; @@ -554,4 +571,21 @@ class Form { } } + /** + * Dynamically handle calls to custom macros. + * + * @param string $method + * @param array $parameters + * @return mixed + */ + public static function __callStatic($method, $parameters) + { + if (isset(static::$macros[$method])) + { + return call_user_func_array(static::$macros[$method], $parameters); + } + + throw new \Exception("Method [$method] does not exist."); + } + } diff --git a/laravel/redis.php b/laravel/redis.php index 446b7f15..d00b2f52 100644 --- a/laravel/redis.php +++ b/laravel/redis.php @@ -16,6 +16,13 @@ class Redis { */ protected $port; + /** + * The databse number the connection selects on load. + * + * @var int + */ + protected $database; + /** * The connection to the Redis database. * @@ -35,12 +42,14 @@ class Redis { * * @param string $host * @param string $port + * @param int $database * @return void */ - public function __construct($host, $port) + public function __construct($host, $port, $database = 0) { $this->host = $host; $this->port = $port; + $this->database = $database; } /** @@ -68,7 +77,9 @@ class Redis { throw new \Exception("Redis database [$name] is not defined."); } - static::$databases[$name] = new static($config['host'], $config['port']); + extract($config); + + static::$databases[$name] = new static($host, $port, $database); } return static::$databases[$name]; @@ -95,6 +106,17 @@ class Redis { $response = trim(fgets($this->connection, 512)); + return $this->parse($response); + } + + /** + * Parse and return the response from the Redis database. + * + * @param string $response + * @return mixed + */ + protected function parse($response) + { switch (substr($response, 0, 1)) { case '-': @@ -131,6 +153,8 @@ class Redis { throw new \Exception("Error making Redis connection: {$error} - {$message}"); } + $this->select($this->database); + return $this->connection; } @@ -191,18 +215,21 @@ class Redis { list($read, $response, $size) = array(0, '', substr($head, 1)); - do + if ($size > 0) { - // Calculate and read the appropriate bytes off of the Redis response. - // We'll read off the response in 1024 byte chunks until the entire - // response has been read from the database. - $block = (($remaining = $size - $read) < 1024) ? $remaining : 1024; + do + { + // Calculate and read the appropriate bytes off of the Redis response. + // We'll read off the response in 1024 byte chunks until the entire + // response has been read from the database. + $block = (($remaining = $size - $read) < 1024) ? $remaining : 1024; - $response .= fread($this->connection, $block); + $response .= fread($this->connection, $block); - $read += $block; + $read += $block; - } while ($read < $size); + } while ($read < $size); + } // The response ends with a trailing CRLF. So, we need to read that off // of the end of the file stream to get it out of the way of the next @@ -225,11 +252,11 @@ class Redis { $response = array(); // Iterate through each bulk response in the multi-bulk and parse it out - // using the "bulk" method since a multi-bulk response is just a list of - // plain old bulk responses. + // using the "parse" method since a multi-bulk response is just a list + // of plain old Redis database responses. for ($i = 0; $i < $count; $i++) { - $response[] = $this->bulk(trim(fgets($this->connection, 512))); + $response[] = $this->parse(trim(fgets($this->connection, 512))); } return $response; diff --git a/laravel/routing/route.php b/laravel/routing/route.php index 4f4896df..6349a0e8 100644 --- a/laravel/routing/route.php +++ b/laravel/routing/route.php @@ -381,4 +381,16 @@ class Route { Filter::register($name, $callback); } + /** + * Calls the specified route and returns its response. + * + * @param string $method + * @param string $uri + * @return Response + */ + public static function forward($method, $uri) + { + return Router::route(strtoupper($method), $uri)->call(); + } + } \ No newline at end of file diff --git a/laravel/validator.php b/laravel/validator.php index 4d6d1683..77ac724a 100644 --- a/laravel/validator.php +++ b/laravel/validator.php @@ -611,6 +611,18 @@ class Validator { return preg_match('/^([-a-z0-9_-])+$/i', $value); } + /** + * Validate that an attribute passes a regular expression check. + * + * @param string $attribute + * @param mixed $value + * @return bool + */ + protected function validate_match($attribute, $value, $parameters) + { + return preg_match($parameters[0], $value); + } + /** * Validate the MIME type of a file upload attribute is in a set of MIME types. * diff --git a/readme.md b/readme.md index da5fb487..e05ef395 100644 --- a/readme.md +++ b/readme.md @@ -41,7 +41,7 @@ Laravel is a clean and classy framework for PHP web development. Freeing you fro Contributions are encouraged and welcome; however, please review the Developer Certificate of Origin in the "license.txt" file included in the repository. All commits must be signed off using the "-s" switch. - git commit -s -m "thie commit will be signed off automatically!" + git commit -s -m "this commit will be signed off automatically!" ### License