diff --git a/application/config/aliases.php b/application/config/aliases.php new file mode 100644 index 00000000..d7d3f4b6 --- /dev/null +++ b/application/config/aliases.php @@ -0,0 +1,44 @@ + 'System\\Auth', + 'Benchmark' => 'System\\Benchmark', + 'Cache' => 'System\\Cache', + 'Config' => 'System\\Config', + 'Cookie' => 'System\\Cookie', + 'Crypt' => 'System\\Crypt', + 'Date' => 'System\\Date', + 'DB' => 'System\\DB', + 'Eloquent' => 'System\\DB\\Eloquent', + 'File' => 'System\\File', + 'Form' => 'System\\Form', + 'Hash' => 'System\\Hash', + 'HTML' => 'System\\HTML', + 'Inflector' => 'System\\Inflector', + 'Input' => 'System\\Input', + 'Lang' => 'System\\Lang', + 'Log' => 'System\\Log', + 'URL' => 'System\\URL', + 'Redirect' => 'System\\Redirect', + 'Request' => 'System\\Request', + 'Response' => 'System\\Response', + 'Session' => 'System\\Session', + 'Str' => 'System\\Str', + 'Text' => 'System\\Text', + 'Validator' => 'System\\Validator', + 'View' => 'System\\View', + +); \ No newline at end of file diff --git a/application/config/application.php b/application/config/application.php index fcafe721..2f55b140 100644 --- a/application/config/application.php +++ b/application/config/application.php @@ -80,45 +80,4 @@ return array( 'key' => '', - /* - |-------------------------------------------------------------------------- - | Class Aliases - |-------------------------------------------------------------------------- - | - | Here, you can specify any class aliases that you would like registered - | when Laravel loads. Aliases are lazy-loaded, so add as many as you want. - | - | We have already setup a few to make your life easier. - | - */ - - 'aliases' => array( - 'Auth' => 'System\\Auth', - 'Benchmark' => 'System\\Benchmark', - 'Cache' => 'System\\Cache', - 'Config' => 'System\\Config', - 'Cookie' => 'System\\Cookie', - 'Crypt' => 'System\\Crypt', - 'Date' => 'System\\Date', - 'DB' => 'System\\DB', - 'Download' => 'System\\Download', - 'Eloquent' => 'System\\DB\\Eloquent', - 'File' => 'System\\File', - 'Form' => 'System\\Form', - 'Hash' => 'System\\Hash', - 'HTML' => 'System\\HTML', - 'Inflector' => 'System\\Inflector', - 'Input' => 'System\\Input', - 'Lang' => 'System\\Lang', - 'Log' => 'System\\Log', - 'URL' => 'System\\URL', - 'Redirect' => 'System\\Redirect', - 'Request' => 'System\\Request', - 'Response' => 'System\\Response', - 'Session' => 'System\\Session', - 'Str' => 'System\\Str', - 'Text' => 'System\\Text', - 'View' => 'System\View', - ), - ); \ No newline at end of file diff --git a/application/config/cache.php b/application/config/cache.php index 1364446a..65ac2602 100644 --- a/application/config/cache.php +++ b/application/config/cache.php @@ -12,12 +12,24 @@ return array( | Caching can be used to increase the performance of your application | by storing commonly accessed data in memory or in a file. | - | Supported Drivers: 'file', 'memcached'. + | Supported Drivers: 'file', 'memcached', 'apc'. | */ 'driver' => 'file', + /* + |-------------------------------------------------------------------------- + | Cache Key + |-------------------------------------------------------------------------- + | + | This key will be prepended to items stored using Memcached and APC to + | prevent collisions with other applications on the server. + | + */ + + 'key' => 'laravel', + /* |-------------------------------------------------------------------------- | Memcached Servers @@ -35,18 +47,6 @@ return array( 'servers' => array( array('host' => '127.0.0.1', 'port' => 11211, 'weight' => 100), - ), - - /* - |-------------------------------------------------------------------------- - | Memcached Key - |-------------------------------------------------------------------------- - | - | This key will be prepended to items stored using Memcached to avoid - | collisions with other applications on the server. - | - */ - - 'key' => 'laravel', + ), ); \ No newline at end of file diff --git a/application/config/mimes.php b/application/config/mimes.php new file mode 100644 index 00000000..e2bd4fbb --- /dev/null +++ b/application/config/mimes.php @@ -0,0 +1,97 @@ + 'application/mac-binhex40', + 'cpt' => 'application/mac-compactpro', + 'csv' => array('text/x-comma-separated-values', 'text/comma-separated-values', 'application/octet-stream'), + 'bin' => 'application/macbinary', + 'dms' => 'application/octet-stream', + 'lha' => 'application/octet-stream', + 'lzh' => 'application/octet-stream', + 'exe' => array('application/octet-stream', 'application/x-msdownload'), + 'class' => 'application/octet-stream', + 'psd' => 'application/x-photoshop', + 'so' => 'application/octet-stream', + 'sea' => 'application/octet-stream', + 'dll' => 'application/octet-stream', + 'oda' => 'application/oda', + 'pdf' => array('application/pdf', 'application/x-download'), + 'ai' => 'application/postscript', + 'eps' => 'application/postscript', + 'ps' => 'application/postscript', + 'smi' => 'application/smil', + 'smil' => 'application/smil', + 'mif' => 'application/vnd.mif', + 'xls' => array('application/excel', 'application/vnd.ms-excel', 'application/msexcel'), + 'ppt' => array('application/powerpoint', 'application/vnd.ms-powerpoint'), + 'wbxml' => 'application/wbxml', + 'wmlc' => 'application/wmlc', + 'dcr' => 'application/x-director', + 'dir' => 'application/x-director', + 'dxr' => 'application/x-director', + 'dvi' => 'application/x-dvi', + 'gtar' => 'application/x-gtar', + 'gz' => 'application/x-gzip', + 'php' => array('application/x-httpd-php', 'text/x-php'), + 'php4' => 'application/x-httpd-php', + 'php3' => 'application/x-httpd-php', + 'phtml' => 'application/x-httpd-php', + 'phps' => 'application/x-httpd-php-source', + 'js' => 'application/x-javascript', + 'swf' => 'application/x-shockwave-flash', + 'sit' => 'application/x-stuffit', + 'tar' => 'application/x-tar', + 'tgz' => array('application/x-tar', 'application/x-gzip-compressed'), + 'xhtml' => 'application/xhtml+xml', + 'xht' => 'application/xhtml+xml', + 'zip' => array('application/x-zip', 'application/zip', 'application/x-zip-compressed'), + 'mid' => 'audio/midi', + 'midi' => 'audio/midi', + 'mpga' => 'audio/mpeg', + 'mp2' => 'audio/mpeg', + 'mp3' => array('audio/mpeg', 'audio/mpg', 'audio/mpeg3', 'audio/mp3'), + 'aif' => 'audio/x-aiff', + 'aiff' => 'audio/x-aiff', + 'aifc' => 'audio/x-aiff', + 'ram' => 'audio/x-pn-realaudio', + 'rm' => 'audio/x-pn-realaudio', + 'rpm' => 'audio/x-pn-realaudio-plugin', + 'ra' => 'audio/x-realaudio', + 'rv' => 'video/vnd.rn-realvideo', + 'wav' => 'audio/x-wav', + 'bmp' => 'image/bmp', + 'gif' => 'image/gif', + 'jpeg' => array('image/jpeg', 'image/pjpeg'), + 'jpg' => array('image/jpeg', 'image/pjpeg'), + 'jpe' => array('image/jpeg', 'image/pjpeg'), + 'png' => 'image/png', + 'tiff' => 'image/tiff', + 'tif' => 'image/tiff', + 'css' => 'text/css', + 'html' => 'text/html', + 'htm' => 'text/html', + 'shtml' => 'text/html', + 'txt' => 'text/plain', + 'text' => 'text/plain', + 'log' => array('text/plain', 'text/x-log'), + 'rtx' => 'text/richtext', + 'rtf' => 'text/rtf', + 'xml' => 'text/xml', + 'xsl' => 'text/xml', + 'mpeg' => 'video/mpeg', + 'mpg' => 'video/mpeg', + 'mpe' => 'video/mpeg', + 'qt' => 'video/quicktime', + 'mov' => 'video/quicktime', + 'avi' => 'video/x-msvideo', + 'movie' => 'video/x-sgi-movie', + 'doc' => 'application/msword', + 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'word' => array('application/msword', 'application/octet-stream'), + 'xl' => 'application/excel', + 'eml' => 'message/rfc822', + 'json' => array('application/json', 'text/json'), + +); \ No newline at end of file diff --git a/application/config/session.php b/application/config/session.php index 8a18cfce..be0e3e0d 100644 --- a/application/config/session.php +++ b/application/config/session.php @@ -12,7 +12,7 @@ return array( | Since HTTP is stateless, sessions are used to maintain "state" across | multiple requests from the same user of your application. | - | Supported Drivers: 'file', 'db', 'memcached'. + | Supported Drivers: 'file', 'db', 'memcached', 'apc'. | */ diff --git a/application/lang/en/validation.php b/application/lang/en/validation.php new file mode 100644 index 00000000..fa09a0be --- /dev/null +++ b/application/lang/en/validation.php @@ -0,0 +1,51 @@ + "The :attribute must be accepted.", + "confirmation_of" => "The :attribute confirmation does not match.", + "exclusion_of" => "The :attribute value is invalid.", + "format_of" => "The :attribute format is invalid.", + "inclusion_of" => "The :attribute value is invalid.", + "presence_of" => "The :attribute can't be empty.", + "uniqueness_of" => "The :attribute has already been taken.", + "with_callback" => "The :attribute is invalid.", + + /* + |-------------------------------------------------------------------------- + | Numericality_Of Validation Messages + |-------------------------------------------------------------------------- + */ + + "number_not_valid" => "The :attribute must be a number.", + "number_not_integer" => "The :attribute must be an integer.", + "number_wrong_size" => "The :attribute must be :size.", + "number_too_big" => "The :attribute must be no more than :max.", + "number_too_small" => "The :attribute must be at least :min.", + + /* + |-------------------------------------------------------------------------- + | Length_Of Validation Messages + |-------------------------------------------------------------------------- + */ + + "string_wrong_size" => "The :attribute must be :size characters.", + "string_too_big" => "The :attribute must be no more than :max characters.", + "string_too_small" => "The :attribute must be at least :min characters.", + + /* + |-------------------------------------------------------------------------- + | Upload_Of Validation Messages + |-------------------------------------------------------------------------- + */ + + "file_wrong_type" => "The :attribute must be a file of type: :types.", + "file_too_big" => "The :attribute exceeds size limit of :maxkb.", + +); \ No newline at end of file diff --git a/application/cache/.gitignore b/application/libraries/.gitignore similarity index 100% rename from application/cache/.gitignore rename to application/libraries/.gitignore diff --git a/application/db/.gitignore b/application/storage/cache/.gitignore similarity index 100% rename from application/db/.gitignore rename to application/storage/cache/.gitignore diff --git a/application/lang/en/.gitignore b/application/storage/db/.gitignore similarity index 100% rename from application/lang/en/.gitignore rename to application/storage/db/.gitignore diff --git a/application/logs/.gitignore b/application/storage/logs/.gitignore similarity index 100% rename from application/logs/.gitignore rename to application/storage/logs/.gitignore diff --git a/application/sessions/.gitignore b/application/storage/sessions/.gitignore similarity index 100% rename from application/sessions/.gitignore rename to application/storage/sessions/.gitignore diff --git a/application/views/home/index.php b/application/views/home/index.php index 59b997e8..4e17bf50 100644 --- a/application/views/home/index.php +++ b/application/views/home/index.php @@ -4,74 +4,76 @@ Welcome To Laravel! - - + + + - - + +
-

Laravel

- -
- You have successfully installed Laravel. +

Installation Complete!

-

+

Ready to dig in? Start building your application in the application/routes.php file.

- Perhaps you would like to peruse the documentation or contribute on GitHub? -
- - -
+

Need to learn more? Peruse our wonderful documentation.

+ \ No newline at end of file diff --git a/public/index.php b/public/index.php index 8e6a9e70..7ecf345f 100644 --- a/public/index.php +++ b/public/index.php @@ -3,7 +3,7 @@ * Laravel - A clean and classy framework for PHP web development. * * @package Laravel - * @version 1.0.0 Beta 2 + * @version 1.1.0 * @author Taylor Otwell * @license MIT License * @link http://laravel.com @@ -17,10 +17,11 @@ define('LARAVEL_START', microtime(true)); // -------------------------------------------------------------- // Define the framework paths. // -------------------------------------------------------------- +define('BASE_PATH', realpath('../').'/'); define('APP_PATH', realpath('../application').'/'); define('SYS_PATH', realpath('../system').'/'); -define('BASE_PATH', realpath('../').'/'); define('PUBLIC_PATH', realpath(__DIR__.'/')); +define('PACKAGE_PATH', APP_PATH.'packages/'); // -------------------------------------------------------------- // Define the PHP file extension. @@ -28,21 +29,15 @@ define('PUBLIC_PATH', realpath(__DIR__.'/')); define('EXT', '.php'); // -------------------------------------------------------------- -// Load the configuration, error, and string classes. +// Load the configuration class. // -------------------------------------------------------------- require SYS_PATH.'config'.EXT; -require SYS_PATH.'str'.EXT; // -------------------------------------------------------------- // Register the auto-loader. // -------------------------------------------------------------- spl_autoload_register(require SYS_PATH.'loader'.EXT); -// -------------------------------------------------------------- -// Set the Laravel starting time in the Benchmark class. -// -------------------------------------------------------------- -System\Benchmark::$marks['laravel'] = LARAVEL_START; - // -------------------------------------------------------------- // Set the error reporting level. // -------------------------------------------------------------- @@ -88,7 +83,7 @@ if (System\Config::get('session.driver') != '') // -------------------------------------------------------------- // Execute the global "before" filter. // -------------------------------------------------------------- -$response = System\Filter::call('before', array(), true); +$response = System\Route\Filter::call('before', array(), true); // -------------------------------------------------------------- // Only execute the route function if the "before" filter did @@ -121,7 +116,7 @@ else // ---------------------------------------------------------- // Execute the global "after" filter. // ---------------------------------------------------------- -System\Filter::call('after', array($response)); +System\Route\Filter::call('after', array($response)); // ---------------------------------------------------------- // Stringify the response. diff --git a/readme.md b/readme.md index d3405c9c..30adf4bc 100644 --- a/readme.md +++ b/readme.md @@ -1,3 +1,5 @@ # Laravel - A Clean & Classy PHP Framework -## For more information, visit [http://laravel.com](http://laravel.com/ "Laravel") +### For more information, visit [http://laravel.com](http://laravel.com) + +### For complete documentation, visit [http://docs.laravel.com](http://docs.laravel.com) \ No newline at end of file diff --git a/system/cache/driver/apc.php b/system/cache/driver/apc.php new file mode 100644 index 00000000..195aa0ad --- /dev/null +++ b/system/cache/driver/apc.php @@ -0,0 +1,71 @@ +get($key))); + } + + /** + * Get an item from the cache. + * + * @param string $key + * @param mixed $default + * @return mixed + */ + public function get($key, $default = null) + { + if (array_key_exists($key, $this->items)) + { + return $this->items[$key]; + } + + $cache = apc_fetch(\System\Config::get('cache.key').$key); + + if ($cache === false) + { + return $default; + } + + return $this->items[$key] = $cache; + } + + /** + * Write an item to the cache. + * + * @param string $key + * @param mixed $value + * @param int $minutes + * @return void + */ + public function put($key, $value, $minutes) + { + apc_store(\System\Config::get('cache.key').$key, $value, $minutes * 60); + } + + /** + * Delete an item from the cache. + * + * @param string $key + * @return void + */ + public function forget($key) + { + apc_delete(\System\Config::get('cache.key').$key); + } + +} \ No newline at end of file diff --git a/system/cache/driver/file.php b/system/cache/driver/file.php index 852bdb15..0994f75d 100644 --- a/system/cache/driver/file.php +++ b/system/cache/driver/file.php @@ -34,12 +34,12 @@ class File implements \System\Cache\Driver { return $this->items[$key]; } - if ( ! file_exists(APP_PATH.'cache/'.$key)) + if ( ! file_exists(APP_PATH.'storage/cache/'.$key)) { return $default; } - $cache = file_get_contents(APP_PATH.'cache/'.$key); + $cache = file_get_contents(APP_PATH.'storage/cache/'.$key); // -------------------------------------------------- // Has the cache expired? The UNIX expiration time @@ -65,7 +65,7 @@ class File implements \System\Cache\Driver { */ public function put($key, $value, $minutes) { - file_put_contents(APP_PATH.'cache/'.$key, (time() + ($minutes * 60)).serialize($value), LOCK_EX); + file_put_contents(APP_PATH.'storage/cache/'.$key, (time() + ($minutes * 60)).serialize($value), LOCK_EX); } /** @@ -76,7 +76,7 @@ class File implements \System\Cache\Driver { */ public function forget($key) { - @unlink(APP_PATH.'cache/'.$key); + @unlink(APP_PATH.'storage/cache/'.$key); } } \ No newline at end of file diff --git a/system/cache/factory.php b/system/cache/factory.php index 227d906c..245f4c85 100644 --- a/system/cache/factory.php +++ b/system/cache/factory.php @@ -18,6 +18,9 @@ class Factory { case 'memcached': return new Driver\Memcached; + case 'apc': + return new Driver\APC; + default: throw new \Exception("Cache driver [$driver] is not supported."); } diff --git a/system/config.php b/system/config.php index f1180da4..d0105ac1 100644 --- a/system/config.php +++ b/system/config.php @@ -9,35 +9,57 @@ class Config { */ private static $items = array(); + /** + * Determine if a configuration item exists. + * + * @param string $key + * @return bool + */ + public static function has($key) + { + return ! is_null(static::get($key)); + } + /** * Get a configuration item. * * @param string $key + * @param string $default * @return mixed */ - public static function get($key) + public static function get($key, $default = null) { // ----------------------------------------------------- // If a dot is not present, we will just return the // entire configuration array. + // + // If the configuration file does not exist, the default + // value will be returned. // ----------------------------------------------------- if(strpos($key, '.') === false) { static::load($key); - return static::$items[$key]; + return (array_key_exists($key, static::$items)) ? static::$items[$key] : $default; } list($file, $key) = static::parse($key); static::load($file); - if (array_key_exists($key, static::$items[$file])) + // ----------------------------------------------------- + // If the file doesn't exist, return the default. + // ----------------------------------------------------- + if ( ! array_key_exists($file, static::$items)) { - return static::$items[$file][$key]; + return $default; } - throw new \Exception("Configuration item [$key] is not defined."); + // ----------------------------------------------------- + // Return the configuration item. If the item doesn't + // exist, the default value will be returned. + // ----------------------------------------------------- + return (array_key_exists($key, static::$items[$file])) ? static::$items[$file][$key] : $default; } /** @@ -91,18 +113,13 @@ class Config { public static function load($file) { // ----------------------------------------------------- - // If we have already loaded the file, bail out. + // Bail out if already loaded or doesn't exist. // ----------------------------------------------------- - if (array_key_exists($file, static::$items)) + if (array_key_exists($file, static::$items) or ! file_exists($path = APP_PATH.'config/'.$file.EXT)) { return; } - if ( ! file_exists($path = APP_PATH.'config/'.$file.EXT)) - { - throw new \Exception("Configuration file [$file] does not exist."); - } - // ----------------------------------------------------- // Load the configuration array into the array of items. // The items array is keyed by filename. diff --git a/system/db.php b/system/db.php index fbdcaf1f..0f064ef8 100644 --- a/system/db.php +++ b/system/db.php @@ -66,11 +66,11 @@ class DB { // // For all other statements, return a boolean. // --------------------------------------------------- - if (strpos(Str::upper($sql), 'SELECT') === 0) + if (strpos(strtoupper($sql), 'SELECT') === 0) { return $query->fetchAll(\PDO::FETCH_CLASS, 'stdClass'); } - elseif (strpos(Str::upper($sql), 'UPDATE') === 0 or strpos(Str::upper($sql), 'DELETE') === 0) + elseif (strpos(strtoupper($sql), 'UPDATE') === 0 or strpos(strtoupper($sql), 'DELETE') === 0) { return $query->rowCount(); } @@ -105,4 +105,22 @@ class DB { return static::connection($connection)->getAttribute(\PDO::ATTR_DRIVER_NAME); } + /** + * Get the table prefix for a database connection. + * + * @param string $connection + * @return string + */ + public static function prefix($connection = null) + { + $connections = Config::get('db.connections'); + + if (is_null($connection)) + { + $connection = Config::get('db.default'); + } + + return (array_key_exists('prefix', $connections[$connection])) ? $connections[$connection]['prefix'] : ''; + } + } \ No newline at end of file diff --git a/system/db/connector.php b/system/db/connector.php index 602ec96e..25a50375 100644 --- a/system/db/connector.php +++ b/system/db/connector.php @@ -33,7 +33,7 @@ class Connector { // If the database doesn't exist there, maybe the full // path was specified as the database name? // ----------------------------------------------------- - if (file_exists($path = APP_PATH.'db/'.$config->database.'.sqlite')) + if (file_exists($path = APP_PATH.'storage/db/'.$config->database.'.sqlite')) { return new \PDO('sqlite:'.$path, null, null, static::$options); } @@ -41,14 +41,31 @@ class Connector { { return new \PDO('sqlite:'.$config->database, null, null, static::$options); } + else + { + throw new \Exception("SQLite database [".$config->database."] could not be found."); + } } // ----------------------------------------------------- // Connect to MySQL or Postgres. // ----------------------------------------------------- elseif ($config->driver == 'mysql' or $config->driver == 'pgsql') { - $connection = new \PDO($config->driver.':host='.$config->host.';dbname='.$config->database, $config->username, $config->password, static::$options); + // ----------------------------------------------------- + // Build the PDO connection DSN. + // ----------------------------------------------------- + $dsn = $config->driver.':host='.$config->host.';dbname='.$config->database; + if (isset($config->port)) + { + $dsn .= ';port='.$config->port; + } + + $connection = new \PDO($dsn, $config->username, $config->password, static::$options); + + // ----------------------------------------------------- + // Set the appropriate character set for the datbase. + // ----------------------------------------------------- if (isset($config->charset)) { $connection->prepare("SET NAMES '".$config->charset."'")->execute(); diff --git a/system/db/eloquent.php b/system/db/eloquent.php index be5d4d29..9263faab 100644 --- a/system/db/eloquent.php +++ b/system/db/eloquent.php @@ -1,6 +1,7 @@ relating_key = (is_null($foreign_key)) ? Str::lower(get_class($this)).'_id' : $foreign_key; + $this->relating_key = (is_null($foreign_key)) ? strtolower(get_class($this)).'_id' : $foreign_key; return static::make($model)->where($this->relating_key, '=', $this->id); } @@ -276,7 +277,7 @@ abstract class Eloquent { sort($models); - $this->relating_table = Str::lower($models[0].'_'.$models[1]); + $this->relating_table = strtolower($models[0].'_'.$models[1]); } // ----------------------------------------------------- @@ -286,11 +287,11 @@ abstract class Eloquent { // // This is the same convention as has_one and has_many. // ----------------------------------------------------- - $this->relating_key = Str::lower(get_class($this)).'_id'; + $this->relating_key = strtolower(get_class($this)).'_id'; return static::make($model) ->select(static::table($model).'.*') - ->join($this->relating_table, static::table($model).'.id', '=', $this->relating_table.'.'.Str::lower($model).'_id') + ->join($this->relating_table, static::table($model).'.id', '=', $this->relating_table.'.'.strtolower($model).'_id') ->where($this->relating_table.'.'.$this->relating_key, '=', $this->id); } @@ -365,7 +366,7 @@ abstract class Eloquent { // ----------------------------------------------------- if ($this->exists) { - return Query::table(static::table(get_class($this)))->delete($this->id) == 1; + return Query::table(static::table(get_class($this)))->delete($this->id); } return $this->query->delete($id); @@ -477,7 +478,7 @@ abstract class Eloquent { { $model = static::make(get_called_class()); - if ($method == 'get') + if ($method == 'get' or $method == 'all') { return $model->_get(); } diff --git a/system/db/eloquent/hydrator.php b/system/db/eloquent/hydrator.php index ab068fed..f4ba7e02 100644 --- a/system/db/eloquent/hydrator.php +++ b/system/db/eloquent/hydrator.php @@ -78,8 +78,9 @@ class Hydrator { // ----------------------------------------------------- // Get the relationship Eloquent model. // - // We temporarily spoof the "belongs_to" key to allow - // the query to be fetched without any problems. + // We temporarily spoof the belongs_to key to allow the + // query to be fetched without any problems, since the + // belongs_to method actually gets the attribute. // ----------------------------------------------------- $eloquent->attributes[$spoof = $include.'_id'] = 0; diff --git a/system/db/query.php b/system/db/query.php index 3e28f20d..85e02b2f 100644 --- a/system/db/query.php +++ b/system/db/query.php @@ -326,7 +326,7 @@ class Query { */ public function order_by($column, $direction) { - $this->orderings[] = $this->wrap($column).' '.Str::upper($direction); + $this->orderings[] = $this->wrap($column).' '.strtoupper($direction); return $this; } @@ -500,9 +500,23 @@ class Query { */ public function __call($method, $parameters) { + // --------------------------------------------------------- + // Dynamic methods allows the building of very expressive + // queries. All dynamic methods start with "where_". + // + // Ex: DB::table('users')->where_email($email)->first(); + // --------------------------------------------------------- + if (strpos($method, 'where_') === 0) + { + return Query\Dynamic::build($method, $parameters, $this); + } + + // --------------------------------------------------------- + // Handle any of the aggregate functions. + // --------------------------------------------------------- if (in_array($method, array('count', 'min', 'max', 'avg', 'sum'))) { - return ($method == 'count') ? $this->aggregate(Str::upper($method), '*') : $this->aggregate(Str::upper($method), $parameters[0]); + return ($method == 'count') ? $this->aggregate(strtoupper($method), '*') : $this->aggregate(strtoupper($method), $parameters[0]); } throw new \Exception("Method [$method] is not defined on the Query class."); diff --git a/system/db/query/dynamic.php b/system/db/query/dynamic.php new file mode 100644 index 00000000..354ac57b --- /dev/null +++ b/system/db/query/dynamic.php @@ -0,0 +1,65 @@ +where($segment, '=', $parameters[$index], $connector); + + $index++; + } + else + { + $connector = trim(strtoupper($segment), '_'); + } + } + + return $query; + } + +} \ No newline at end of file diff --git a/system/file.php b/system/file.php index b46ca705..7156243b 100644 --- a/system/file.php +++ b/system/file.php @@ -2,104 +2,6 @@ class File { - /** - * Extensions and their matching MIME types. - * - * @var array - */ - public static $mimes = array( - 'hqx' => 'application/mac-binhex40', - 'cpt' => 'application/mac-compactpro', - 'csv' => 'text/x-comma-separated-values', - 'bin' => 'application/macbinary', - 'dms' => 'application/octet-stream', - 'lha' => 'application/octet-stream', - 'lzh' => 'application/octet-stream', - 'exe' => 'application/octet-stream', - 'class' => 'application/octet-stream', - 'psd' => 'application/x-photoshop', - 'so' => 'application/octet-stream', - 'sea' => 'application/octet-stream', - 'dll' => 'application/octet-stream', - 'oda' => 'application/oda', - 'pdf' => 'application/pdf', - 'ai' => 'application/postscript', - 'eps' => 'application/postscript', - 'ps' => 'application/postscript', - 'smi' => 'application/smil', - 'smil' => 'application/smil', - 'mif' => 'application/vnd.mif', - 'xls' => 'application/excel', - 'ppt' => 'application/powerpoint', - 'wbxml' => 'application/wbxml', - 'wmlc' => 'application/wmlc', - 'dcr' => 'application/x-director', - 'dir' => 'application/x-director', - 'dxr' => 'application/x-director', - 'dvi' => 'application/x-dvi', - 'gtar' => 'application/x-gtar', - 'gz' => 'application/x-gzip', - 'php' => 'application/x-httpd-php', - 'php4' => 'application/x-httpd-php', - 'php3' => 'application/x-httpd-php', - 'phtml' => 'application/x-httpd-php', - 'phps' => 'application/x-httpd-php-source', - 'js' => 'application/x-javascript', - 'swf' => 'application/x-shockwave-flash', - 'sit' => 'application/x-stuffit', - 'tar' => 'application/x-tar', - 'tgz' => 'application/x-tar', - 'xhtml' => 'application/xhtml+xml', - 'xht' => 'application/xhtml+xml', - 'zip' => 'application/x-zip', - 'mid' => 'audio/midi', - 'midi' => 'audio/midi', - 'mpga' => 'audio/mpeg', - 'mp2' => 'audio/mpeg', - 'mp3' => 'audio/mpeg', - 'aif' => 'audio/x-aiff', - 'aiff' => 'audio/x-aiff', - 'aifc' => 'audio/x-aiff', - 'ram' => 'audio/x-pn-realaudio', - 'rm' => 'audio/x-pn-realaudio', - 'rpm' => 'audio/x-pn-realaudio-plugin', - 'ra' => 'audio/x-realaudio', - 'rv' => 'video/vnd.rn-realvideo', - 'wav' => 'audio/x-wav', - 'bmp' => 'image/bmp', - 'gif' => 'image/gif', - 'jpeg' => 'image/jpeg', - 'jpg' => 'image/jpeg', - 'jpe' => 'image/jpeg', - 'png' => 'image/png', - 'tiff' => 'image/tiff', - 'tif' => 'image/tiff', - 'css' => 'text/css', - 'html' => 'text/html', - 'htm' => 'text/html', - 'shtml' => 'text/html', - 'txt' => 'text/plain', - 'text' => 'text/plain', - 'log' => 'text/plain', - 'rtx' => 'text/richtext', - 'rtf' => 'text/rtf', - 'xml' => 'text/xml', - 'xsl' => 'text/xml', - 'mpeg' => 'video/mpeg', - 'mpg' => 'video/mpeg', - 'mpe' => 'video/mpeg', - 'qt' => 'video/quicktime', - 'mov' => 'video/quicktime', - 'avi' => 'video/x-msvideo', - 'movie' => 'video/x-sgi-movie', - 'doc' => 'application/msword', - 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', - 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', - 'word' => 'application/msword', - 'xl' => 'application/excel', - 'eml' => 'message/rfc822' - ); - /** * Get the contents of a file. * @@ -155,7 +57,35 @@ class File { */ public static function mime($extension, $default = 'application/octet-stream') { - return (array_key_exists($extension, static::$mimes)) ? static::$mimes[$extension] : $default; + $mimes = Config::get('mimes'); + + if (array_key_exists($extension, $mimes)) + { + return (is_array($mimes[$extension])) ? $mimes[$extension][0] : $mimes[$extension]; + } + + return $default; + } + + /** + * Determine if a file is a given type. + * + * The Fileinfo PHP extension will be used to determine the MIME type + * of the file. Any extension in the mimes configuration array may + * be passed as a type. + */ + public static function is($extension, $path) + { + $mimes = Config::get('mimes'); + + if ( ! array_key_exists($extension, $mimes)) + { + throw new \Exception("File extension [$extension] is unknown. Cannot determine file type."); + } + + $mime = finfo_file(finfo_open(FILEINFO_MIME_TYPE), $path); + + return (is_array($mimes[$extension])) ? in_array($mime, $mimes[$extension]) : $mime === $mimes[$extension]; } /** @@ -186,4 +116,20 @@ class File { return $response; } + /** + * Move an uploaded file to storage. + * + * @param string $key + * @param string $path + * @return bool + */ + public static function upload($key, $path) + { + if ( ! array_key_exists($key, $_FILES)) + { + return false; + } + + return move_uploaded_file($_FILES[$key]['tmp_name'], $path); + } } \ No newline at end of file diff --git a/system/form.php b/system/form.php index 2e3ffb42..49d60b36 100644 --- a/system/form.php +++ b/system/form.php @@ -50,6 +50,20 @@ class Form { return $html.PHP_EOL; } + /** + * Open a HTML form that accepts file uploads. + * + * @param string $action + * @param string $method + * @param array $attributes + * @return string + */ + public static function open_multipart($action = null, $method = 'POST', $attributes = array()) + { + $attributes['enctype'] = 'multipart/form-data'; + return static::open($action, $method, $attributes); + } + /** * Close a HTML form. * @@ -100,7 +114,6 @@ class Form { public static function label($name, $value, $attributes = array()) { static::$labels[] = $name; - return ''.PHP_EOL; } diff --git a/system/html.php b/system/html.php index 298046f5..89968130 100644 --- a/system/html.php +++ b/system/html.php @@ -10,7 +10,7 @@ class HTML { */ public static function entities($value) { - return htmlentities($value, ENT_QUOTES, Config::get('application.encoding'), false); + return htmlentities($value, ENT_QUOTES, Config::get('application.encoding'), false); } /** @@ -21,7 +21,7 @@ class HTML { */ public static function script($url) { - return ''.PHP_EOL; + return ''.PHP_EOL; } /** @@ -32,7 +32,7 @@ class HTML { */ public static function style($url, $media = 'all') { - return ''.PHP_EOL; + return ''.PHP_EOL; } /** @@ -76,6 +76,34 @@ class HTML { return static::link($url, $title, $attributes, false, true); } + /** + * Generate an HTML link to a route. + * + * @param string $name + * @param string $title + * @param array $parameters + * @param array $attributes + * @return string + */ + public static function link_to_route($name, $title, $parameters = array(), $attributes = array(), $https = false) + { + return static::link(URL::to_route($name, $parameters, $https), $title, $attributes); + } + + /** + * Generate an HTTPS HTML link to a route. + * + * @param string $name + * @param string $title + * @param array $parameters + * @param array $attributes + * @return string + */ + public static function link_to_secure_route($name, $title, $parameters = array(), $attributes = array()) + { + return static::link_to_route($name, $title, $parameters, $attributes, true); + } + /** * Generate an HTML mailto link. * @@ -250,4 +278,28 @@ class HTML { return $safe; } + /** + * Magic Method for handling dynamic static methods. + */ + public static function __callStatic($method, $parameters) + { + // ------------------------------------------------------- + // Handle the dynamic creation of links to secure routes. + // ------------------------------------------------------- + if (strpos($method, 'link_to_secure_') === 0) + { + array_unshift($parameters, substr($method, 15)); + return forward_static_call_array('HTML::link_to_secure_route', $parameters); + } + + // ------------------------------------------------------- + // Handle the dynamic creation of links to routes. + // ------------------------------------------------------- + if (strpos($method, 'link_to_') === 0) + { + array_unshift($parameters, substr($method, 8)); + return forward_static_call_array('HTML::link_to_route', $parameters); + } + } + } \ No newline at end of file diff --git a/system/inflector.php b/system/inflector.php index 981c5a48..44e0ca61 100644 --- a/system/inflector.php +++ b/system/inflector.php @@ -126,7 +126,7 @@ class Inflector { return static::$plural_cache[$value]; } - if (in_array(Str::lower($value), static::$uncountable)) + if (in_array(strtolower($value), static::$uncountable)) { return static::$plural_cache[$value] = $value; } @@ -165,7 +165,7 @@ class Inflector { return static::$singular_cache[$value]; } - if (in_array(Str::lower($value), static::$uncountable)) + if (in_array(strtolower($value), static::$uncountable)) { return static::$singular_cache[$value] = $value; } diff --git a/system/input.php b/system/input.php index 1e9fd973..ffcc911c 100644 --- a/system/input.php +++ b/system/input.php @@ -69,6 +69,18 @@ class Input { return Arr::get(Session::get('laravel_old_input', array()), $key, $default); } + /** + * Get an item from the uploaded file data. + * + * @param string $key + * @param mixed $default + * @return array + */ + public static function file($key = null, $default = null) + { + return Arr::get($_FILES, $key, $default); + } + /** * Hydrate the input data for the request. * diff --git a/system/lang.php b/system/lang.php index 6778c5eb..d74fd017 100644 --- a/system/lang.php +++ b/system/lang.php @@ -2,13 +2,6 @@ class Lang { - /** - * All of the loaded language files. - * - * @var array - */ - private static $loaded = array(); - /** * All of the loaded language lines. * @@ -55,24 +48,30 @@ class Lang { } /** - * Get the language line for a given language. + * Get the language line. * - * @param string $language + * @param mixed $default * @return string */ - public function get($language = null) + public function get($default = null) { - if (is_null($language)) - { - $language = Config::get('application.language'); - } + $language = Config::get('application.language'); list($file, $line) = $this->parse($this->key); $this->load($file, $language); + // -------------------------------------------------------------- + // If the language file did not exist, return the default value. + // -------------------------------------------------------------- + if ( ! array_key_exists($language.$file, static::$lines)) + { + return $default; + } + // -------------------------------------------------------------- // Get the language line from the appropriate file array. + // If the line doesn't exist, return the default value. // -------------------------------------------------------------- if (array_key_exists($line, static::$lines[$language.$file])) { @@ -80,7 +79,7 @@ class Lang { } else { - throw new \Exception("Language line [$line] does not exist for language [$language]"); + return $default; } // -------------------------------------------------------------- @@ -127,27 +126,15 @@ class Lang { private function load($file, $language) { // -------------------------------------------------------------- - // If we have already loaded the language file, bail out. + // If we have already loaded the language file or the file + // doesn't exist, bail out. // -------------------------------------------------------------- - if (in_array($language.$file, static::$loaded)) + if (array_key_exists($language.$file, static::$lines) or ! file_exists($path = APP_PATH.'lang/'.$language.'/'.$file.EXT)) { return; } - // -------------------------------------------------------------- - // Load the language file into the array of lines. The array - // is keyed by the language and file name. - // -------------------------------------------------------------- - if (file_exists($path = APP_PATH.'lang/'.$language.'/'.$file.EXT)) - { - static::$lines[$language.$file] = require $path; - } - else - { - throw new \Exception("Language file [$file] does not exist for language [$language]."); - } - - static::$loaded[] = $language.$file; + static::$lines[$language.$file] = require $path; } /** diff --git a/system/loader.php b/system/loader.php index e7c963a8..86960f89 100644 --- a/system/loader.php +++ b/system/loader.php @@ -4,16 +4,16 @@ * This function is registered on the auto-loader stack by the front controller. */ return function($class) { - + // ---------------------------------------------------------- // Replace namespace slashes with directory slashes. // ---------------------------------------------------------- - $file = System\Str::lower(str_replace('\\', '/', $class)); + $file = strtolower(str_replace('\\', '/', $class)); // ---------------------------------------------------------- // Should the class be aliased? // ---------------------------------------------------------- - if (array_key_exists($class, $aliases = System\Config::get('application.aliases'))) + if (array_key_exists($class, $aliases = System\Config::get('aliases'))) { return class_alias($aliases[$class], $class); } @@ -33,9 +33,9 @@ return function($class) { require $path; } // ---------------------------------------------------------- - // Is the class in the application/packages directory? + // Is the class in the application/libraries directory? // ---------------------------------------------------------- - elseif (file_exists($path = APP_PATH.'packages/'.$file.EXT)) + elseif (file_exists($path = APP_PATH.'libraries/'.$file.EXT)) { require $path; } diff --git a/system/log.php b/system/log.php index 9c330a89..eba9d5d0 100644 --- a/system/log.php +++ b/system/log.php @@ -47,7 +47,7 @@ class Log { // ----------------------------------------------------- // Create the yearly and monthly directories if needed. // ----------------------------------------------------- - static::make_directory($directory = APP_PATH.'logs/'.date('Y')); + static::make_directory($directory = APP_PATH.'storage/logs/'.date('Y')); static::make_directory($directory .= '/'.date('m')); // ----------------------------------------------------- diff --git a/system/redirect.php b/system/redirect.php index 51662493..70d764bb 100644 --- a/system/redirect.php +++ b/system/redirect.php @@ -77,6 +77,8 @@ class Redirect { */ public static function __callStatic($method, $parameters) { + $parameters = (isset($parameters[0])) ? $parameters[0] : array(); + // ---------------------------------------------------- // Dynamically redirect to a secure route URL. // ---------------------------------------------------- diff --git a/system/request.php b/system/request.php index 5e933bee..70986c41 100644 --- a/system/request.php +++ b/system/request.php @@ -65,24 +65,20 @@ class Request { // ------------------------------------------------------- // Remove the application index and any extra slashes. // ------------------------------------------------------- - $uri = trim(str_replace('/index.php', '', $uri), '/'); + $index = Config::get('application.index'); + + if (strpos($uri, '/'.$index) === 0) + { + $uri = (string) substr($uri, strlen('/'.$index)); + } + + $uri = trim($uri, '/'); // ------------------------------------------------------- // If the requests is to the root of the application, we // always return a single forward slash. // ------------------------------------------------------- - return ($uri == '') ? '/' : Str::lower($uri); - } - - /** - * Determine if the route handling the request is a given name. - * - * @param string $name - * @return bool - */ - public static function is($name) - { - return (is_array(static::$route->callback) and isset(static::$route->callback['name']) and static::$route->callback['name'] === $name); + return ($uri == '') ? '/' : strtolower($uri); } /** @@ -120,16 +116,6 @@ class Request { } } - /** - * Determine if the request is using HTTPS. - * - * @return bool - */ - public static function secure() - { - return (static::protocol() == 'https'); - } - /** * Get the HTTP protocol for the request. * @@ -140,14 +126,35 @@ class Request { return (isset($_SERVER['HTTPS']) and $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http'; } + /** + * Determine if the request is using HTTPS. + * + * @return bool + */ + public static function is_secure() + { + return (static::protocol() == 'https'); + } + /** * Determine if the request is an AJAX request. * * @return bool */ - public static function ajax() + public static function is_ajax() { - return (isset($_SERVER['HTTP_X_REQUESTED_WITH']) and Str::lower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest'); + return (isset($_SERVER['HTTP_X_REQUESTED_WITH']) and strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest'); + } + + /** + * Determine if the route handling the request is a given name. + * + * @param string $name + * @return bool + */ + public static function route_is($name) + { + return (is_array(static::$route->callback) and isset(static::$route->callback['name']) and static::$route->callback['name'] === $name); } /** @@ -160,9 +167,9 @@ class Request { // // Example: Request::is_login() // -------------------------------------------------------------- - if (strpos($method, 'is_') === 0) + if (strpos($method, 'route_is_') === 0) { - return static::is(substr($method, 3)); + return static::route_is(substr($method, 9)); } } diff --git a/system/route.php b/system/route.php index 8210a7f3..451bf718 100644 --- a/system/route.php +++ b/system/route.php @@ -63,7 +63,7 @@ class Route { // ------------------------------------------------------------ elseif (is_array($this->callback)) { - $response = isset($this->callback['before']) ? Filter::call($this->callback['before'], array(), true) : null; + $response = isset($this->callback['before']) ? Route\Filter::call($this->callback['before'], array(), true) : null; // ------------------------------------------------------------ // We verify that the before filters did not return a response @@ -80,7 +80,7 @@ class Route { if (is_array($this->callback) and isset($this->callback['after'])) { - Filter::call($this->callback['after'], array($response)); + Route\Filter::call($this->callback['after'], array($response)); } return $response; diff --git a/system/filter.php b/system/route/filter.php similarity index 97% rename from system/filter.php rename to system/route/filter.php index e3ca5500..6b1c59f7 100644 --- a/system/filter.php +++ b/system/route/filter.php @@ -1,4 +1,4 @@ -get($id); + } + + /** + * Save a session. + * + * @param array $session + * @return void + */ + public function save($session) + { + \System\Cache::driver('apc')->put($session['id'], $session, \System\Config::get('session.lifetime')); + } + + /** + * Delete a session by ID. + * + * @param string $id + * @return void + */ + public function delete($id) + { + \System\Cache::driver('apc')->forget($id); + } + + /** + * Delete all expired sessions. + * + * @param int $expiration + * @return void + */ + public function sweep($expiration) + { + // APC sessions will expire automatically. + } + +} \ No newline at end of file diff --git a/system/session/driver/file.php b/system/session/driver/file.php index d59f4258..47c70f10 100644 --- a/system/session/driver/file.php +++ b/system/session/driver/file.php @@ -10,7 +10,7 @@ class File implements \System\Session\Driver { */ public function load($id) { - if (file_exists($path = APP_PATH.'sessions/'.$id)) + if (file_exists($path = APP_PATH.'storage/sessions/'.$id)) { return unserialize(file_get_contents($path)); } @@ -24,7 +24,7 @@ class File implements \System\Session\Driver { */ public function save($session) { - file_put_contents(APP_PATH.'sessions/'.$session['id'], serialize($session), LOCK_EX); + file_put_contents(APP_PATH.'storage/sessions/'.$session['id'], serialize($session), LOCK_EX); } /** @@ -35,7 +35,7 @@ class File implements \System\Session\Driver { */ public function delete($id) { - @unlink(APP_PATH.'sessions/'.$id); + @unlink(APP_PATH.'storage/sessions/'.$id); } /** @@ -46,7 +46,7 @@ class File implements \System\Session\Driver { */ public function sweep($expiration) { - foreach (glob(APP_PATH.'sessions/*') as $file) + foreach (glob(APP_PATH.'storage/sessions/*') as $file) { if (filetype($file) == 'file' and filemtime($file) < $expiration) { diff --git a/system/session/factory.php b/system/session/factory.php index be0efa38..822cd074 100644 --- a/system/session/factory.php +++ b/system/session/factory.php @@ -21,6 +21,9 @@ class Factory { case 'memcached': return new Driver\Memcached; + case 'apc': + return new Driver\APC; + default: throw new \Exception("Session driver [$driver] is not supported."); } diff --git a/system/str.php b/system/str.php index 7ab40159..ec446c34 100644 --- a/system/str.php +++ b/system/str.php @@ -46,6 +46,17 @@ class Str { return (function_exists('mb_convert_case')) ? mb_convert_case($value, MB_CASE_TITLE, Config::get('application.encoding')) : ucwords(strtolower($value)); } + /** + * Get the length of a string. + * + * @param string $value + * @return int + */ + public static function length($value) + { + return function_exists('mb_strlen') ? mb_strlen($value, Config::get('application.encoding')) : strlen($value); + } + /** * Generate a random alpha or alpha-numeric string. * diff --git a/system/url.php b/system/url.php index b048dd7d..dfcd8208 100644 --- a/system/url.php +++ b/system/url.php @@ -142,6 +142,8 @@ class URL { */ public static function __callStatic($method, $parameters) { + $parameters = (isset($parameters[0])) ? $parameters[0] : array(); + // ---------------------------------------------------- // Dynamically create a secure route URL. // ---------------------------------------------------- diff --git a/system/validation/error_collector.php b/system/validation/error_collector.php new file mode 100644 index 00000000..b0bbee9b --- /dev/null +++ b/system/validation/error_collector.php @@ -0,0 +1,123 @@ +messages = $messages; + } + + /** + * Add an error message to the collector. + * + * Duplicate messages will not be added. + * + * @param string $attribute + * @param string $message + * @return void + */ + public function add($attribute, $message) + { + // ------------------------------------------------------------- + // Make sure the error message is not duplicated. + // + // For example, the Nullable rules can add a "required" message. + // If the same message has already been added we don't want to + // add it again. + // ------------------------------------------------------------- + if ( ! array_key_exists($attribute, $this->messages) or ! is_array($this->messages[$attribute]) or ! in_array($message, $this->messages[$attribute])) + { + $this->messages[$attribute][] = $message; + } + } + + /** + * Determine if errors exist for an attribute. + * + * @param string $attribute + * @return bool + */ + public function has($attribute) + { + return $this->first($attribute) !== ''; + } + + /** + * Get the first error message for an attribute. + * + * @param string $attribute + * @return string + */ + public function first($attribute) + { + return (count($messages = $this->get($attribute)) > 0) ? $messages[0] : ''; + } + + /** + * Get all of the error messages for an attribute. + * + * If no attribute is specified, all of the error messages will be returned. + * + * @param string $attribute + * @param string $format + * @return array + */ + public function get($attribute = null, $format = ':message') + { + if (is_null($attribute)) + { + return $this->all($format); + } + + return (array_key_exists($attribute, $this->messages)) ? $this->format($this->messages[$attribute], $format) : array(); + } + + /** + * Get all of the error messages. + * + * @param string $format + * @return array + */ + public function all($format = ':message') + { + $all = array(); + + // --------------------------------------------------------- + // Add each error message to the array of messages. Each + // messages will have the specified format applied to it. + // --------------------------------------------------------- + foreach ($this->messages as $messages) + { + $all = array_merge($all, $this->format($messages, $format)); + } + + return $all; + } + + /** + * Format an array of messages. + * + * @param array $messages + * @param string $format + * @return array + */ + private function format($messages, $format) + { + array_walk($messages, function(&$message, $key) use ($format) { $message = str_replace(':message', $message, $format); }); + + return $messages; + } + +} \ No newline at end of file diff --git a/system/validation/message.php b/system/validation/message.php new file mode 100644 index 00000000..50d93746 --- /dev/null +++ b/system/validation/message.php @@ -0,0 +1,144 @@ +error)) + { + $class = explode('\\', get_class($rule)); + + $rule->error = strtolower(end($class)); + } + + return (is_null($rule->message)) ? Lang::line('validation.'.$rule->error)->get() : $rule->message; + } + + /** + * Get the error message for a Rangable rule. + * + * @param Rule $rule + * @return string + */ + private static function get_rangable_message($rule) + { + // --------------------------------------------------------- + // Rangable rules sometimes set a "presence_of" error. + // + // This occurs when an attribute is null and the option to + // allow null values has not been set. + // --------------------------------------------------------- + if ($rule->error == 'presence_of') + { + return static::get_message($rule); + } + + // --------------------------------------------------------- + // Slice "number_" or "string_" off of the error type. + // --------------------------------------------------------- + $error_type = substr($rule->error, 7); + + return (is_null($rule->$error_type)) ? Lang::line('validation.'.$rule->error)->get() : $rule->$error_type; + } + + /** + * Get the error message for an Upload_Of rule. + * + * @param Rule $rule + * @return string + */ + private static function get_upload_of_message($rule) + { + // --------------------------------------------------------- + // Slice "file_" off of the error type. + // --------------------------------------------------------- + $error_type = substr($rule->error, 5); + + return (is_null($rule->$error_type)) ? Lang::line('validation.'.$rule->error)->get() : $rule->$error_type; + } + + /** + * Prepare an error message for display. All place-holders will be replaced + * with their actual values. + * + * @param Rule $rule + * @param string $attribute + * @param string $message + * @return string + */ + private static function prepare($rule, $attribute, $message) + { + // --------------------------------------------------------- + // The rangable rule messages have three place-holders that + // must be replaced. + // + // :max = The maximum size of the attribute. + // :min = The minimum size of the attribute. + // :size = The exact size the attribute must be. + // --------------------------------------------------------- + if ($rule instanceof Rangable_Rule) + { + $message = str_replace(':max', $rule->maximum, $message); + $message = str_replace(':min', $rule->minimum, $message); + $message = str_replace(':size', $rule->size, $message); + } + // --------------------------------------------------------- + // The Upload_Of rule message have two place-holders taht + // must be replaced. + // + // :max = The maximum file size of the upload (kilobytes). + // :types = The allowed file types for the upload. + // --------------------------------------------------------- + elseif ($rule instanceof Rules\Upload_Of) + { + $message = str_replace(':max', $rule->maximum, $message); + + if (is_array($rule->types)) + { + $message = str_replace(':types', implode(', ', $rule->types), $message); + } + } + + return str_replace(':attribute', Lang::line('attributes.'.$attribute)->get(str_replace('_', ' ', $attribute)), $message); + } + +} \ No newline at end of file diff --git a/system/validation/nullable_rule.php b/system/validation/nullable_rule.php new file mode 100644 index 00000000..40f43f6e --- /dev/null +++ b/system/validation/nullable_rule.php @@ -0,0 +1,94 @@ +allow_null) + { + $this->error = 'presence_of'; + } + + return is_null($this->error); + } + + // ------------------------------------------------------------- + // Make sure the attribute is not an empty string. An error + // will be raised if the attribute is empty and empty strings + // are not allowed, halting the child's validation. + // ------------------------------------------------------------- + elseif (Str::length((string) $attributes[$attribute]) == 0 and ! $this->allow_empty) + { + $this->error = 'presence_of'; + + return false; + } + } + + /** + * Allow a empty and null to be considered valid. + * + * @return Nullable_Rule + */ + public function not_required() + { + return $this->allow_empty()->allow_null(); + } + + /** + * Allow empty to be considered valid. + * + * @return Nullable_Rule + */ + public function allow_empty() + { + $this->allow_empty = true; + return $this; + } + + /** + * Allow null to be considered valid. + * + * @return Nullable_Rule + */ + public function allow_null() + { + $this->allow_null = true; + return $this; + } + +} \ No newline at end of file diff --git a/system/validation/rangable_rule.php b/system/validation/rangable_rule.php new file mode 100644 index 00000000..1845ca29 --- /dev/null +++ b/system/validation/rangable_rule.php @@ -0,0 +1,145 @@ +size = $size; + return $this; + } + + /** + * Set the minimum and maximum size of the attribute. + * + * @param int $minimum + * @param int $maximum + * @return Rangable_Rule + */ + public function between($minimum, $maximum) + { + $this->minimum = $minimum; + $this->maximum = $maximum; + + return $this; + } + + /** + * Set the minimum size the attribute. + * + * @param int $minimum + * @return Rangable_Rule + */ + public function minimum($minimum) + { + $this->minimum = $minimum; + return $this; + } + + /** + * Set the maximum size the attribute. + * + * @param int $maximum + * @return Rangable_Rule + */ + public function maximum($maximum) + { + $this->maximum = $maximum; + return $this; + } + + /** + * Set the validation error message. + * + * @param string $message + * @return Rangable_Rule + */ + public function message($message) + { + return $this->wrong_size($message)->too_big($message)->too_small($message); + } + + /** + * Set the "wrong size" error message. + * + * @param string $message + * @return Rangable_Rule + */ + public function wrong_size($message) + { + $this->wrong_size = $message; + return $this; + } + + /** + * Set the "too big" error message. + * + * @param string $message + * @return Rangable_Rule + */ + public function too_big($message) + { + $this->too_big = $message; + return $this; + } + + /** + * Set the "too small" error message. + * + * @param string $message + * @return Rangable_Rule + */ + public function too_small($message) + { + $this->too_small = $message; + return $this; + } + +} \ No newline at end of file diff --git a/system/validation/rule.php b/system/validation/rule.php new file mode 100644 index 00000000..fa0c6692 --- /dev/null +++ b/system/validation/rule.php @@ -0,0 +1,72 @@ +attributes = $attributes; + } + + /** + * Run the validation rule. + * + * @param array $attributes + * @param Error_Collector $errors + * @return void + */ + public function validate($attributes, $errors) + { + foreach ($this->attributes as $attribute) + { + $this->error = null; + + if ( ! $this->check($attribute, $attributes)) + { + $errors->add($attribute, Message::get($this, $attribute)); + } + } + } + + /** + * Set the validation error message. + * + * @param string $message + * @return Rule + */ + public function message($message) + { + $this->message = $message; + return $this; + } + +} \ No newline at end of file diff --git a/system/validation/rules/acceptance_of.php b/system/validation/rules/acceptance_of.php new file mode 100644 index 00000000..02b505a9 --- /dev/null +++ b/system/validation/rules/acceptance_of.php @@ -0,0 +1,39 @@ +accepts; + } + + /** + * Set the accepted value. + * + * @param string $value + * @return Acceptance_Of + */ + public function accepts($value) + { + $this->accepts = $value; + return $this; + } + +} \ No newline at end of file diff --git a/system/validation/rules/confirmation_of.php b/system/validation/rules/confirmation_of.php new file mode 100644 index 00000000..a6d7de0e --- /dev/null +++ b/system/validation/rules/confirmation_of.php @@ -0,0 +1,25 @@ +reserved); + } + + /** + * Set the reserved values for the attribute + * + * @param array $reserved + * @return Exclusion_Of + */ + public function from($reserved) + { + $this->reserved = $reserved; + return $this; + } + +} \ No newline at end of file diff --git a/system/validation/rules/format_of.php b/system/validation/rules/format_of.php new file mode 100644 index 00000000..dfcebb16 --- /dev/null +++ b/system/validation/rules/format_of.php @@ -0,0 +1,43 @@ +expression, $attributes[$attribute]); + } + + /** + * Set the regular expression. + * + * @param string $expression + * @return Format_Of + */ + public function using($expression) + { + $this->expression = $expression; + return $this; + } + +} \ No newline at end of file diff --git a/system/validation/rules/inclusion_of.php b/system/validation/rules/inclusion_of.php new file mode 100644 index 00000000..f92ace6f --- /dev/null +++ b/system/validation/rules/inclusion_of.php @@ -0,0 +1,43 @@ +accepted); + } + + /** + * Set the accepted values for the attribute. + * + * @param array $accepted + * @return Inclusion_Of + */ + public function in($accepted) + { + $this->accepted = $accepted; + return $this; + } + +} \ No newline at end of file diff --git a/system/validation/rules/length_of.php b/system/validation/rules/length_of.php new file mode 100644 index 00000000..f53ff5aa --- /dev/null +++ b/system/validation/rules/length_of.php @@ -0,0 +1,49 @@ +size) and Str::length($value) !== $this->size) + { + $this->error = 'string_wrong_size'; + } + // --------------------------------------------------------- + // Validate the maximum length of the attribute. + // --------------------------------------------------------- + elseif ( ! is_null($this->maximum) and Str::length($value) > $this->maximum) + { + $this->error = 'string_too_big'; + } + // --------------------------------------------------------- + // Validate the minimum length of the attribute. + // --------------------------------------------------------- + elseif ( ! is_null($this->minimum) and Str::length($value) < $this->minimum) + { + $this->error = 'string_too_small'; + } + + return is_null($this->error); + } + +} \ No newline at end of file diff --git a/system/validation/rules/numericality_of.php b/system/validation/rules/numericality_of.php new file mode 100644 index 00000000..43813ba7 --- /dev/null +++ b/system/validation/rules/numericality_of.php @@ -0,0 +1,116 @@ +error = 'number_not_valid'; + } + // --------------------------------------------------------- + // Validate the attribute is an integer. + // --------------------------------------------------------- + elseif ($this->only_integer and filter_var($attributes[$attribute], FILTER_VALIDATE_INT) === false) + { + $this->error = 'number_not_integer'; + } + // --------------------------------------------------------- + // Validate the exact size of the attribute. + // --------------------------------------------------------- + elseif ( ! is_null($this->size) and $attributes[$attribute] != $this->size) + { + $this->error = 'number_wrong_size'; + } + // --------------------------------------------------------- + // Validate the maximum size of the attribute. + // --------------------------------------------------------- + elseif ( ! is_null($this->maximum) and $attributes[$attribute] > $this->maximum) + { + $this->error = 'number_too_big'; + } + // --------------------------------------------------------- + // Validate the minimum size of the attribute. + // --------------------------------------------------------- + elseif ( ! is_null($this->minimum) and $attributes[$attribute] < $this->minimum) + { + $this->error = 'number_too_small'; + } + + return is_null($this->error); + } + + /** + * Specify that the attribute must be an integer. + * + * @return Numericality_Of + */ + public function only_integer() + { + $this->only_integer = true; + return $this; + } + + /** + * Set the "not valid" error message. + * + * @param string $message + * @return Numericality_Of + */ + public function not_valid($message) + { + $this->not_valid = $message; + return $this; + } + + /** + * Set the "not integer" error message. + * + * @param string $message + * @return Numericality_Of + */ + public function not_integer($message) + { + $this->not_integer = $message; + return $this; + } + +} \ No newline at end of file diff --git a/system/validation/rules/presence_of.php b/system/validation/rules/presence_of.php new file mode 100644 index 00000000..8926e967 --- /dev/null +++ b/system/validation/rules/presence_of.php @@ -0,0 +1,29 @@ +column)) + { + $this->column = $attribute; + } + + return DB::table($this->table)->where($this->column, '=', $attributes[$attribute])->count() == 0; + } + + /** + * Set the database table and column. + * + * The attribute name will be used as the column name if no other + * column name is specified. + * + * @param string $table + * @param string $column + * @return Uniqueness_Of + */ + public function on($table, $column = null) + { + $this->table = $table; + $this->column = $column; + + return $this; + } + +} \ No newline at end of file diff --git a/system/validation/rules/upload_of.php b/system/validation/rules/upload_of.php new file mode 100644 index 00000000..4dfba8ca --- /dev/null +++ b/system/validation/rules/upload_of.php @@ -0,0 +1,160 @@ +allow_null) + { + $this->error = 'presence_of'; + } + + return is_null($this->error); + } + + // ----------------------------------------------------- + // Uploaded files are stored in the $_FILES array, so + // we use that array instead of the $attributes. + // ----------------------------------------------------- + $file = Input::file($attribute); + + if ( ! is_null($this->maximum) and $file['size'] > $this->maximum * 1000) + { + $this->error = 'file_too_big'; + } + + // ----------------------------------------------------- + // The File::is method uses the Fileinfo PHP extension + // to determine the MIME type of the file. + // ----------------------------------------------------- + foreach ($this->types as $type) + { + if (File::is($type, $file['tmp_name'])) + { + break; + } + + $this->error = 'file_wrong_type'; + } + + return is_null($this->error); + } + + /** + * Set the acceptable file types. + * + * @return Upload_Of + */ + public function is() + { + $this->types = func_get_args(); + return $this; + } + + /** + * Require that the uploaded file is an image type. + * + * @return Upload_Of + */ + public function is_image() + { + $this->types = array_merge($this->types, array('jpg', 'gif', 'png', 'bmp')); + return $this; + } + + /** + * Set the maximum file size in kilobytes. + * + * @param int $maximum + * @return Upload_Of + */ + public function maximum($maximum) + { + $this->maximum = $maximum; + return $this; + } + + /** + * Set the validation error message. + * + * @param string $message + * @return Upload_Of + */ + public function message($message) + { + return $this->wrong_type($message)->too_big($message); + } + + /** + * Set the "wrong type" error message. + * + * @param string $message + * @return Upload_Of + */ + public function wrong_type($message) + { + $this->wrong_type = $message; + return $this; + } + + /** + * Set the "too big" error message. + * + * @param string $message + * @return Upload_Of + */ + public function too_big($message) + { + $this->too_big = $message; + return $this; + } + +} \ No newline at end of file diff --git a/system/validation/rules/with_callback.php b/system/validation/rules/with_callback.php new file mode 100644 index 00000000..ed1d2ff3 --- /dev/null +++ b/system/validation/rules/with_callback.php @@ -0,0 +1,48 @@ +callback)) + { + throw new \Exception("The validation callback for the [$attribute] attribute is not callable."); + } + + if ( ! is_null($nullable = parent::check($attribute, $attributes))) + { + return $nullable; + } + + return call_user_func($this->callback, $attributes[$attribute]); + } + + /** + * Set the validation callback. + * + * @param function $callback + * @return With_Callback + */ + public function using($callback) + { + $this->callback = $callback; + return $this; + } + +} \ No newline at end of file diff --git a/system/validator.php b/system/validator.php new file mode 100644 index 00000000..895a22d4 --- /dev/null +++ b/system/validator.php @@ -0,0 +1,94 @@ +errors = new Validation\Error_Collector; + + // --------------------------------------------------------- + // If the source is an Eloquent model, use the model's + // attributes as the validation attributes. + // --------------------------------------------------------- + $this->attributes = ($target instanceof DB\Eloquent) ? $target->attributes : (array) $target; + } + + /** + * Create a new Validator instance. + * + * @param mixed $target + * @return Validator + */ + public static function make($target = array()) + { + return new static($target); + } + + /** + * Determine if the attributes pass all of the validation rules. + * + * @return bool + */ + public function is_valid() + { + $this->errors->messages = array(); + + foreach ($this->rules as $rule) + { + // --------------------------------------------------------- + // The error collector is passed to the rule so that the + // rule may conveniently add error messages. + // --------------------------------------------------------- + $rule->validate($this->attributes, $this->errors); + } + + return count($this->errors->messages) == 0; + } + + /** + * Magic Method for dynamically creating validation rules. + */ + public function __call($method, $parameters) + { + // --------------------------------------------------------- + // Check if the validation rule is defined in the rules + // directory. If it is, create a new rule and return it. + // --------------------------------------------------------- + if (file_exists(SYS_PATH.'validation/rules/'.$method.EXT)) + { + $rule = '\\System\\Validation\\Rules\\'.$method; + + return $this->rules[] = new $rule($parameters); + } + + throw new \Exception("Method [$method] does not exist on Validator class."); + } + +} \ No newline at end of file diff --git a/system/view.php b/system/view.php index 7a314211..990b2dcd 100644 --- a/system/view.php +++ b/system/view.php @@ -85,6 +85,9 @@ class View { // We include the view into the local scope within a // try / catch block to catch any exceptions that may // occur while the view is rendering. + // + // Otherwise, a white screen of death will be shown + // if an exception occurs while rendering the view. // ----------------------------------------------------- try { @@ -101,9 +104,8 @@ class View { /** * Get the full path to the view. * - * Views are cascaded, so the application directory views - * will take precedence over the system directory's views - * of the same name. + * Views are cascaded, so the application directory views will take + * precedence over system directory views of the same name. * * @return string */