commit a188d62105532fcf2a2839309fb71b862d904612 Author: Taylor Otwell Date: Wed Jun 8 23:45:08 2011 -0500 initial commit of laravel! diff --git a/application/cache/.gitignore b/application/cache/.gitignore new file mode 100644 index 000000000..e69de29bb diff --git a/application/config/application.php b/application/config/application.php new file mode 100644 index 000000000..c97aef582 --- /dev/null +++ b/application/config/application.php @@ -0,0 +1,98 @@ + 'http://localhost/index.php', + + /* + |-------------------------------------------------------------------------- + | Application Language + |-------------------------------------------------------------------------- + | + | The default language of your application. This language will be used by + | default by the Lang library when doing string localization. + | + | If you are not using the Lang library, this option isn't really important. + | + */ + + 'language' => 'en', + + /* + |-------------------------------------------------------------------------- + | Application Timezone + |-------------------------------------------------------------------------- + | + | The default timezone of your application. This timezone will be used when + | Laravel needs a date, such as when writing to a log file. + | + */ + + 'timezone' => 'UTC', + + /* + |-------------------------------------------------------------------------- + | Application Key + |-------------------------------------------------------------------------- + | + | Your application key should be a 32 character string that is totally + | random and secret. This key is used by the encryption class to generate + | secure, encrypted strings. + | + | If you will not be using the encryption class, this doesn't matter. + | + */ + + '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', + 'Form' => 'System\\Form', + 'Hash' => 'System\\Hash', + 'HTML' => 'System\\HTML', + 'Inflector' => 'System\\Inflector', + 'Input' => 'System\\Input', + 'Lang' => 'System\\Lang', + '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/auth.php b/application/config/auth.php new file mode 100644 index 000000000..1f9942528 --- /dev/null +++ b/application/config/auth.php @@ -0,0 +1,32 @@ + 'User', + + /* + |-------------------------------------------------------------------------- + | Authentication Username + |-------------------------------------------------------------------------- + | + | The authentication username is the column on your users table that + | is considered the username of the user. Typically, this is either "email" + | or "username". However, you are free to make it whatever you wish. + | + */ + + 'username' => 'email', + +); \ No newline at end of file diff --git a/application/config/cache.php b/application/config/cache.php new file mode 100644 index 000000000..1364446ab --- /dev/null +++ b/application/config/cache.php @@ -0,0 +1,52 @@ + 'file', + + /* + |-------------------------------------------------------------------------- + | Memcached Servers + |-------------------------------------------------------------------------- + | + | Here you can define the Memcached servers used by your application. + | + | Memcached is a free and open source, high-performance, distributed memory + | object caching system, generic in nature, but intended for use in speeding + | up dynamic web applications by alleviating database load. + | + | For more information about Memcached, check out: http://memcached.org + | + */ + + '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/db.php b/application/config/db.php new file mode 100644 index 000000000..de245564e --- /dev/null +++ b/application/config/db.php @@ -0,0 +1,60 @@ + 'sqlite', + + /* + |-------------------------------------------------------------------------- + | Database Connections + |-------------------------------------------------------------------------- + | + | Here you can define all of the databases used by your application. + | + | Supported Drivers: 'mysql', 'pgsql', 'sqlite'. + | + | Note: When using the SQLite driver, the path and "sqlite" extention will + | be added automatically. You only need to specify the database name. + | + */ + + 'connections' => array( + + 'sqlite' => array( + 'driver' => 'sqlite', + 'database' => 'application', + ), + + 'mysql' => array( + 'driver' => 'mysql', + 'host' => 'localhost', + 'database' => 'database', + 'username' => 'root', + 'password' => 'password', + 'charset' => 'utf8', + ), + + 'pgsql' => array( + 'driver' => 'pgsql', + 'host' => 'localhost', + 'database' => 'database', + 'username' => 'root', + 'password' => 'password', + 'charset' => 'utf8', + ), + + ), + +); \ No newline at end of file diff --git a/application/config/error.php b/application/config/error.php new file mode 100644 index 000000000..dd021e466 --- /dev/null +++ b/application/config/error.php @@ -0,0 +1,34 @@ + true, + + /* + |-------------------------------------------------------------------------- + | Error Logging + |-------------------------------------------------------------------------- + | + | Would you like errors to be logged? Error logging can be extremely + | helpful when debugging a production application. + | + | Note: When error logging is enabled, errors will be logged even when + | error detail is disabled. + | + */ + + 'log' => false, + +); \ No newline at end of file diff --git a/application/config/session.php b/application/config/session.php new file mode 100644 index 000000000..8a18cfce4 --- /dev/null +++ b/application/config/session.php @@ -0,0 +1,89 @@ + '', + + /* + |-------------------------------------------------------------------------- + | Session Database + |-------------------------------------------------------------------------- + | + | The database table on which the session should be stored. + | + | If you are not using database based sessions, don't worry about this. + | + */ + + 'table' => 'sessions', + + /* + |-------------------------------------------------------------------------- + | Session Lifetime + |-------------------------------------------------------------------------- + | + | How many minutes can a session be idle before expiring? + | + */ + + 'lifetime' => 60, + + /* + |-------------------------------------------------------------------------- + | Session Expiration On Close + |-------------------------------------------------------------------------- + | + | Should the session expire when the user's web browser closes? + | + */ + + 'expire_on_close' => false, + + /* + |-------------------------------------------------------------------------- + | Session Cookie Path + |-------------------------------------------------------------------------- + | + | The path for which the session cookie is available. + | + */ + + 'path' => '/', + + /* + |-------------------------------------------------------------------------- + | Session Cookie Domain + |-------------------------------------------------------------------------- + | + | The domain for which the session cookie is available. + | + */ + + 'domain' => null, + + /* + |-------------------------------------------------------------------------- + | Session Cookie HTTPS + |-------------------------------------------------------------------------- + | + | Should the session cookie only be transported over HTTPS? + | + */ + + 'https' => false, + +); \ No newline at end of file diff --git a/application/db/.gitignore b/application/db/.gitignore new file mode 100644 index 000000000..e69de29bb diff --git a/application/filters.php b/application/filters.php new file mode 100644 index 000000000..5da720da9 --- /dev/null +++ b/application/filters.php @@ -0,0 +1,41 @@ + function() + { + // Do stuff before every request is executed. + }, + + + 'after' => function($response) + { + // Do stuff after every request is executed. + }, + + + 'auth' => function() + { + return ( ! Auth::check()) ? Redirect::to_login() : null; + }, + + + 'csrf' => function() + { + return (Input::get('csrf_token') !== Form::raw_token()) ? Response::view('error/500', 500) : null; + }, + +); \ No newline at end of file diff --git a/application/lang/en/.gitignore b/application/lang/en/.gitignore new file mode 100644 index 000000000..e69de29bb diff --git a/application/logs/.gitignore b/application/logs/.gitignore new file mode 100644 index 000000000..e69de29bb diff --git a/application/models/.gitignore b/application/models/.gitignore new file mode 100644 index 000000000..e69de29bb diff --git a/application/packages/.gitignore b/application/packages/.gitignore new file mode 100644 index 000000000..e69de29bb diff --git a/application/routes.php b/application/routes.php new file mode 100644 index 000000000..e9f03c024 --- /dev/null +++ b/application/routes.php @@ -0,0 +1,25 @@ + function() + { + return View::make('home/index'); + }, + +); \ No newline at end of file diff --git a/application/sessions/.gitignore b/application/sessions/.gitignore new file mode 100644 index 000000000..e69de29bb diff --git a/application/views/error/404.php b/application/views/error/404.php new file mode 100644 index 000000000..beca74937 --- /dev/null +++ b/application/views/error/404.php @@ -0,0 +1,56 @@ + + + + + 404 - Not Found + + + + + + +
+ + +

+ +
+ The resource you requested was not found. +

+ Would you like go to our home page instead? +
+
+ + \ No newline at end of file diff --git a/application/views/error/500.php b/application/views/error/500.php new file mode 100644 index 000000000..213a9ea3f --- /dev/null +++ b/application/views/error/500.php @@ -0,0 +1,56 @@ + + + + + 500 - Internal Server Error + + + + + + +
+ + +

+ +
+ An error occured while we were processing your request. +

+ Would you like go to our home page instead? +
+
+ + \ No newline at end of file diff --git a/application/views/home/index.php b/application/views/home/index.php new file mode 100644 index 000000000..59b997e89 --- /dev/null +++ b/application/views/home/index.php @@ -0,0 +1,77 @@ + + + + + Welcome To Laravel! + + + + + + + + + +
+

Laravel

+ +
+ You have successfully installed Laravel. + +

+ + Perhaps you would like to peruse the documentation or contribute on GitHub? +
+ + +
+ + \ No newline at end of file diff --git a/license.txt b/license.txt new file mode 100644 index 000000000..49ab775e3 --- /dev/null +++ b/license.txt @@ -0,0 +1,19 @@ +Copyright (c) 2011 Taylor Otwell - taylorotwell@gmail.com + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/public/.htaccess b/public/.htaccess new file mode 100644 index 000000000..1676044f2 --- /dev/null +++ b/public/.htaccess @@ -0,0 +1,5 @@ +Options +FollowSymLinks +RewriteEngine on +RewriteCond %{REQUEST_FILENAME} !-f +RewriteCond %{REQUEST_FILENAME} !-d +RewriteRule . /index.php [L] \ No newline at end of file diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 000000000..90c94b357 Binary files /dev/null and b/public/favicon.ico differ diff --git a/public/index.php b/public/index.php new file mode 100644 index 000000000..b3a7b1971 --- /dev/null +++ b/public/index.php @@ -0,0 +1,133 @@ +call(); + } + else + { + $response = System\Response::view('error/404', 404); + } +} +else +{ + $response = ( ! $response instanceof System\Response) ? new System\Response($response) : $response; +} + +// ---------------------------------------------------------- +// Execute the global "after" filter. +// ---------------------------------------------------------- +System\Filter::call('after', array($response)); + +// -------------------------------------------------------------- +// Close the session. +// -------------------------------------------------------------- +if (System\Config::get('session.driver') != '') +{ + System\Session::close(); +} + +// -------------------------------------------------------------- +// Send the response to the browser. +// -------------------------------------------------------------- +$response->send(); \ No newline at end of file diff --git a/system/auth.php b/system/auth.php new file mode 100644 index 000000000..198c32bd4 --- /dev/null +++ b/system/auth.php @@ -0,0 +1,135 @@ +first(); + + if ( ! is_null($user)) + { + // ----------------------------------------------------- + // Hash the password. + // ----------------------------------------------------- + $password = (isset($user->salt)) ? Hash::make($password, $user->salt)->value : sha1($password); + + // ----------------------------------------------------- + // Verify that the passwords match. + // ----------------------------------------------------- + if ($user->password == $password) + { + // ----------------------------------------------------- + // Set the user property. + // ----------------------------------------------------- + static::$user = $user; + + // ----------------------------------------------------- + // Store the user ID in the session. + // ----------------------------------------------------- + Session::put(static::$key, $user->id); + + return true; + } + } + + return false; + } + + /** + * Logout the current user of the application. + * + * @return void + */ + public static function logout() + { + // ----------------------------------------------------- + // Remove the user ID from the session. + // ----------------------------------------------------- + Session::forget(static::$key); + + // ----------------------------------------------------- + // Clear the current user variable. + // ----------------------------------------------------- + static::$user = null; + } + + /** + * Get the authentication model. + * + * @return string + */ + private static function model() + { + return '\\'.Config::get('auth.model'); + } + +} \ No newline at end of file diff --git a/system/benchmark.php b/system/benchmark.php new file mode 100644 index 000000000..c0d71eb2a --- /dev/null +++ b/system/benchmark.php @@ -0,0 +1,49 @@ +get($key))); + } + + /** + * Get an item from the cache. + * + * @param string $key + * @param mixed $default + * @return mixed + */ + public function get($key, $default = null) + { + // -------------------------------------------------- + // If the item has already been loaded, return it. + // -------------------------------------------------- + if (array_key_exists($key, $this->items)) + { + return $this->items[$key]; + } + + // -------------------------------------------------- + // Verify that the cache file exists. + // -------------------------------------------------- + if ( ! file_exists(APP_PATH.'cache/'.$key)) + { + return $default; + } + + // -------------------------------------------------- + // Read the contents of the cache file. + // -------------------------------------------------- + $cache = file_get_contents(APP_PATH.'cache/'.$key); + + // -------------------------------------------------- + // Has the cache expired? The UNIX expiration time + // is stored at the beginning of the file. + // -------------------------------------------------- + if (time() >= substr($cache, 0, 10)) + { + $this->forget($key); + + return $default; + } + + return $this->items[$key] = unserialize(substr($cache, 10)); + } + + /** + * Write an item to the cache. + * + * @param string $key + * @param mixed $value + * @param int $minutes + * @return void + */ + public function put($key, $value, $minutes) + { + // -------------------------------------------------- + // The expiration time is stored as a UNIX timestamp + // at the beginning of the cache file. + // -------------------------------------------------- + file_put_contents(APP_PATH.'cache/'.$key, (time() + ($minutes * 60)).serialize($value), LOCK_EX); + } + + /** + * Delete an item from the cache. + * + * @param string $key + * @return void + */ + public function forget($key) + { + @unlink(APP_PATH.'cache/'.$key); + } + +} \ No newline at end of file diff --git a/system/cache/driver/memcached.php b/system/cache/driver/memcached.php new file mode 100644 index 000000000..22834727c --- /dev/null +++ b/system/cache/driver/memcached.php @@ -0,0 +1,80 @@ +get($key))); + } + + /** + * Get an item from the cache. + * + * @param string $key + * @param mixed $default + * @return mixed + */ + public function get($key, $default = null) + { + // -------------------------------------------------- + // If the item has already been loaded, return it. + // -------------------------------------------------- + if (array_key_exists($key, $this->items)) + { + return $this->items[$key]; + } + + // -------------------------------------------------- + // Attempt to the get the item from cache. + // -------------------------------------------------- + $cache = \System\Memcached::instance()->get(\System\Config::get('cache.key').$key); + + // -------------------------------------------------- + // Verify that the item was retrieved. + // -------------------------------------------------- + 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) + { + \System\Memcached::instance()->set(\System\Config::get('cache.key').$key, $value, 0, $minutes * 60); + } + + /** + * Delete an item from the cache. + * + * @param string $key + * @return void + */ + public function forget($key) + { + \System\Memcached::instance()->delete(\System\Config::get('cache.key').$key); + } + +} \ No newline at end of file diff --git a/system/cache/factory.php b/system/cache/factory.php new file mode 100644 index 000000000..227d906c7 --- /dev/null +++ b/system/cache/factory.php @@ -0,0 +1,26 @@ +prepare($sql); + + // --------------------------------------------------- + // Execute the query with the bindings. + // --------------------------------------------------- + $result = $query->execute($bindings); + + // --------------------------------------------------- + // For SELECT statements, return the results in an + // array of stdClasses. + // + // For UPDATE and DELETE statements, return the number + // or rows affected by the query. + // + // For INSERT statements, return a boolean. + // --------------------------------------------------- + if (strpos(Str::upper($sql), 'SELECT') === 0) + { + return $query->fetchAll(\PDO::FETCH_CLASS, 'stdClass'); + } + elseif (strpos(Str::upper($sql), 'UPDATE') === 0 or strpos(Str::upper($sql), 'DELETE') === 0) + { + return $query->rowCount(); + } + else + { + return $result; + } + } + + /** + * Begin a fluent query against a table. + * + * @param string $table + * @param string $connection + * @return Query + */ + public static function table($table, $connection = null) + { + return new DB\Query($table, $connection); + } + +} \ No newline at end of file diff --git a/system/db/connector.php b/system/db/connector.php new file mode 100644 index 000000000..196170d13 --- /dev/null +++ b/system/db/connector.php @@ -0,0 +1,58 @@ + \PDO::CASE_LOWER, + \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION, + \PDO::ATTR_ORACLE_NULLS => \PDO::NULL_NATURAL, + \PDO::ATTR_STRINGIFY_FETCHES => false, + ); + + /** + * Establish a PDO database connection. + * + * @param object $config + * @return PDO + */ + public static function connect($config) + { + // --------------------------------------------------- + // Establish a SQLite PDO connection. + // --------------------------------------------------- + if ($config->driver == 'sqlite') + { + return new \PDO('sqlite:'.APP_PATH.'db/'.$config->database.'.sqlite', null, null, static::$options); + } + // --------------------------------------------------- + // Establish a MySQL or Postgres PDO connection. + // --------------------------------------------------- + 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); + + // --------------------------------------------------- + // Set the correct character set. + // --------------------------------------------------- + if (isset($config->charset)) + { + $connection->prepare("SET NAMES '".$config->charset."'")->execute(); + } + + return $connection; + } + // --------------------------------------------------- + // If the driver isn't supported, bail out. + // --------------------------------------------------- + else + { + throw new \Exception('Database driver '.$config->driver.' is not supported.'); + } + } + +} \ No newline at end of file diff --git a/system/db/eloquent.php b/system/db/eloquent.php new file mode 100644 index 000000000..f6070776e --- /dev/null +++ b/system/db/eloquent.php @@ -0,0 +1,353 @@ +includes = func_get_args(); + + return $model; + } + + /** + * Get a model by the primary key. + * + * @param int $id + * @return mixed + */ + public static function find($id) + { + return Eloquent\Factory::make(get_called_class())->where('id', '=', $id)->first(); + } + + /** + * Get an array of models from the database. + * + * @return array + */ + private function _get() + { + return Eloquent\Hydrate::from($this); + } + + /** + * Get the first model result + * + * @return mixed + */ + private function _first() + { + // ----------------------------------------------------- + // Load the hydrated models. + // ----------------------------------------------------- + $results = Eloquent\Hydrate::from($this->take(1)); + + // ----------------------------------------------------- + // Return the first result. + // ----------------------------------------------------- + if (count($results) > 0) + { + reset($results); + + return current($results); + } + } + + /** + * Retrieve the query for a 1:1 relationship. + * + * @param string $model + * @return mixed + */ + public function has_one($model) + { + return Eloquent\Relate::has_one($model, $this); + } + + /** + * Retrieve the query for a 1:* relationship. + * + * @param string $model + * @return mixed + */ + public function has_many($model) + { + return Eloquent\Relate::has_many($model, $this); + } + + /** + * Retrieve the query for a 1:1 belonging relationship. + * + * @param string $model + * @return mixed + */ + public function belongs_to($model) + { + // ----------------------------------------------------- + // Get the calling function name. + // ----------------------------------------------------- + list(, $caller) = debug_backtrace(false); + + return Eloquent\Relate::belongs_to($caller, $model, $this); + } + + /** + * Retrieve the query for a *:* relationship. + * + * @param string $model + * @return mixed + */ + public function has_many_and_belongs_to($model) + { + return Eloquent\Relate::has_many_and_belongs_to($model, $this); + } + + /** + * Save the model to the database. + * + * @return void + */ + public function save() + { + Eloquent\Warehouse::store($this); + } + + /** + * Magic method for retrieving model attributes. + */ + public function __get($key) + { + // ----------------------------------------------------- + // Check the ignored attributes first. + // ----------------------------------------------------- + if (array_key_exists($key, $this->ignore)) + { + return $this->ignore[$key]; + } + + // ----------------------------------------------------- + // Is the attribute actually a relationship? + // ----------------------------------------------------- + if (method_exists($this, $key)) + { + // ----------------------------------------------------- + // Get the query / model for the relationship. + // ----------------------------------------------------- + $model = $this->$key(); + + // ----------------------------------------------------- + // Return the relationship results. + // ----------------------------------------------------- + return ($this->relating == 'has_one' or $this->relating == 'belongs_to') + ? $this->ignore[$key] = $model->first() + : $this->ignore[$key] = $model->get(); + } + + // ----------------------------------------------------- + // Check the "regular" attributes. + // ----------------------------------------------------- + return (array_key_exists($key, $this->attributes)) ? $this->attributes[$key] : null; + } + + /** + * Magic Method for setting model attributes. + */ + public function __set($key, $value) + { + // ----------------------------------------------------- + // Is the key actually a relationship? + // ----------------------------------------------------- + if (method_exists($this, $key)) + { + $this->ignore[$key] = $value; + } + else + { + // ----------------------------------------------------- + // Add the value to the attributes. + // ----------------------------------------------------- + $this->attributes[$key] = $value; + $this->dirty[$key] = $value; + } + } + + /** + * Magic Method for determining if a model attribute is set. + */ + public function __isset($key) + { + return (array_key_exists($key, $this->attributes) or array_key_exists($key, $this->ignore)); + } + + /** + * Magic Method for unsetting model attributes. + */ + public function __unset($key) + { + unset($this->attributes[$key]); + unset($this->ignore[$key]); + unset($this->dirty[$key]); + } + + /** + * Magic Method for handling dynamic method calls. + */ + public function __call($method, $parameters) + { + // ----------------------------------------------------- + // Is the "get" method being called? + // ----------------------------------------------------- + if ($method == 'get') + { + return $this->_get(); + } + + // ----------------------------------------------------- + // Is the "first" method being called? + // ----------------------------------------------------- + if ($method == 'first') + { + return $this->_first(); + } + + // ----------------------------------------------------- + // If the method is an aggregate function, just return + // the aggregate value from the query. + // ----------------------------------------------------- + if (in_array($method, array('count', 'sum', 'min', 'max', 'avg'))) + { + return call_user_func_array(array($this->query, $method), $parameters); + } + + // ----------------------------------------------------- + // Pass the method call to the query instance. + // ----------------------------------------------------- + call_user_func_array(array($this->query, $method), $parameters); + + return $this; + } + + /** + * Magic Method for handling dynamic static method calls. + */ + public static function __callStatic($method, $parameters) + { + // ----------------------------------------------------- + // Create a new model instance. + // ----------------------------------------------------- + $model = Eloquent\Factory::make(get_called_class()); + + // ----------------------------------------------------- + // Do we need to return the entire table? + // ----------------------------------------------------- + if ($method == 'get') + { + return $model->_get(); + } + + // ----------------------------------------------------- + // Do we need to return the first model from the table? + // ----------------------------------------------------- + if ($method == 'first') + { + return $model->_first(); + } + + // ----------------------------------------------------- + // If the method is an aggregate function, just return + // the aggregate value from the query. + // ----------------------------------------------------- + if (in_array($method, array('count', 'sum', 'min', 'max', 'avg'))) + { + return call_user_func_array(array($model->query, $method), $parameters); + } + + // ----------------------------------------------------- + // Pass the method call to the query instance. + // ----------------------------------------------------- + call_user_func_array(array($model->query, $method), $parameters); + + return $model; + } + +} \ No newline at end of file diff --git a/system/db/eloquent/factory.php b/system/db/eloquent/factory.php new file mode 100644 index 000000000..3d792581a --- /dev/null +++ b/system/db/eloquent/factory.php @@ -0,0 +1,26 @@ +query = \System\DB\Query::table(Meta::table($class)); + + return $model; + } + +} \ No newline at end of file diff --git a/system/db/eloquent/hydrate.php b/system/db/eloquent/hydrate.php new file mode 100644 index 000000000..925e2915f --- /dev/null +++ b/system/db/eloquent/hydrate.php @@ -0,0 +1,272 @@ +query->get()); + + // ----------------------------------------------------- + // Load all of the eager relationships. + // ----------------------------------------------------- + if (count($results) > 0) + { + foreach ($eloquent->includes as $include) + { + // ----------------------------------------------------- + // Verify the relationship is defined. + // ----------------------------------------------------- + if ( ! method_exists($eloquent, $include)) + { + throw new \Exception("Attempting to eager load [$include], but the relationship is not defined."); + } + + // ----------------------------------------------------- + // Eagerly load the relationship. + // ----------------------------------------------------- + static::eagerly($eloquent, $include, $results); + } + } + + return $results; + } + + /** + * Hydrate the base models for a query. + * + * @param string $class + * @param array $models + * @return array + */ + private static function base($class, $models) + { + // ----------------------------------------------------- + // Initialize the hydrated model array. + // ----------------------------------------------------- + $results = array(); + + // ----------------------------------------------------- + // Hydrate the models from the results. + // ----------------------------------------------------- + foreach ($models as $model) + { + // ----------------------------------------------------- + // Instantiate a new model instance. + // ----------------------------------------------------- + $result = new $class; + + // ----------------------------------------------------- + // Set the model's attributes. + // ----------------------------------------------------- + $result->attributes = (array) $model; + + // ----------------------------------------------------- + // Indicate that the model already exists. + // ----------------------------------------------------- + $result->exists = true; + + // ----------------------------------------------------- + // Add the hydrated model to the array of models. + // The array is keyed by the primary keys of the models. + // ----------------------------------------------------- + $results[$result->id] = $result; + } + + return $results; + } + + /** + * Eagerly load a relationship. + * + * @param object $eloquent + * @param string $include + * @param array $results + * @return void + */ + private static function eagerly($eloquent, $include, &$results) + { + // ----------------------------------------------------- + // Get the relationship Eloquent model. + // + // We spoof the "belongs_to" key to allow the query + // to be fetched without any problems. + // ----------------------------------------------------- + $eloquent->attributes[$spoof = $include.'_id'] = 0; + + $model = $eloquent->$include(); + + unset($eloquent->attributes[$spoof]); + + // ----------------------------------------------------- + // Reset the WHERE clause on the query. + // ----------------------------------------------------- + $model->query->where = 'WHERE 1 = 1'; + + // ----------------------------------------------------- + // Reset the bindings on the query. + // ----------------------------------------------------- + $model->query->bindings = array(); + + // ----------------------------------------------------- + // Initialize the relationship on the parent models. + // ----------------------------------------------------- + foreach ($results as &$result) + { + $result->ignore[$include] = (strpos($eloquent->relating, 'has_many') === 0) ? array() : null; + } + + // ----------------------------------------------------- + // Eagerly load a 1:1 or 1:* relationship. + // ----------------------------------------------------- + if ($eloquent->relating == 'has_one' or $eloquent->relating == 'has_many') + { + static::eagerly_load_one_or_many($eloquent->relating_key, $eloquent->relating, $include, $model, $results); + } + // ----------------------------------------------------- + // Eagerly load a 1:1 (belonging) relationship. + // ----------------------------------------------------- + elseif ($eloquent->relating == 'belongs_to') + { + static::eagerly_load_belonging($eloquent->relating_key, $include, $model, $results); + } + // ----------------------------------------------------- + // Eagerly load a *:* relationship. + // ----------------------------------------------------- + else + { + static::eagerly_load_many_to_many($eloquent->relating_key, $eloquent->relating_table, strtolower(get_class($eloquent)).'_id', $include, $model, $results); + } + } + + /** + * Eagerly load a 1:1 or 1:* relationship. + * + * @param string $relating_key + * @param string $relating + * @param string $include + * @param object $model + * @param array $results + * @return void + */ + private static function eagerly_load_one_or_many($relating_key, $relating, $include, $model, &$results) + { + // ----------------------------------------------------- + // Get the related models. + // ----------------------------------------------------- + $inclusions = $model->where_in($relating_key, array_keys($results))->get(); + + // ----------------------------------------------------- + // Match the child models with their parent. + // ----------------------------------------------------- + foreach ($inclusions as $key => $inclusion) + { + if ($relating == 'has_one') + { + $results[$inclusion->$relating_key]->ignore[$include] = $inclusion; + } + else + { + $results[$inclusion->$relating_key]->ignore[$include][$inclusion->id] = $inclusion; + } + } + } + + /** + * Eagerly load a 1:1 belonging relationship. + * + * @param string $relating_key + * @param string $include + * @param object $model + * @param array $results + * @return void + */ + private static function eagerly_load_belonging($relating_key, $include, $model, &$results) + { + // ----------------------------------------------------- + // Gather the keys from the parent models. + // ----------------------------------------------------- + $keys = array(); + + foreach ($results as &$result) + { + $keys[] = $result->$relating_key; + } + + // ----------------------------------------------------- + // Get the related models. + // ----------------------------------------------------- + $inclusions = $model->where_in('id', array_unique($keys))->get(); + + // ----------------------------------------------------- + // Match the child models with their parent. + // ----------------------------------------------------- + foreach ($results as &$result) + { + $result->ignore[$include] = $inclusions[$result->$relating_key]; + } + } + + /** + * Eagerly load a many-to-many relationship. + * + * @param string $relating_key + * @param string $relating_table + * @param string $foreign_key + * @param string $include + * @param object $model + * @param array $results + * @return void + */ + private static function eagerly_load_many_to_many($relating_key, $relating_table, $foreign_key, $include, $model, &$results) + { + // ----------------------------------------------------- + // Reset the SELECT clause. + // ----------------------------------------------------- + $model->query->select = null; + + // ----------------------------------------------------- + // Retrieve the raw results as stdClasses. + // + // We also add the foreign key to the select which will allow us + // to match the models back to their parents. + // ----------------------------------------------------- + $inclusions = $model->query->where_in($relating_key, array_keys($results))->get(Meta::table(get_class($model)).'.*', $relating_table.'.'.$foreign_key); + + // ----------------------------------------------------- + // Get the class name of the related model. + // ----------------------------------------------------- + $class = get_class($model); + + // ----------------------------------------------------- + // Create the related models. + // ----------------------------------------------------- + foreach ($inclusions as $inclusion) + { + $related = new $class; + + $related->exists = true; + $related->attributes = (array) $inclusion; + + // ----------------------------------------------------- + // Remove the foreign key from the attributes since it + // was only added to the query to help us match the models. + // ----------------------------------------------------- + unset($related->attributes[$foreign_key]); + + // ----------------------------------------------------- + // Add the related model to the parent model's array. + // ----------------------------------------------------- + $results[$inclusion->$foreign_key]->ignore[$include][$inclusion->id] = $related; + } + } + +} \ No newline at end of file diff --git a/system/db/eloquent/meta.php b/system/db/eloquent/meta.php new file mode 100644 index 000000000..0900c3c5c --- /dev/null +++ b/system/db/eloquent/meta.php @@ -0,0 +1,24 @@ +relating = __FUNCTION__; + + // ----------------------------------------------------- + // Return the Eloquent model. + // ----------------------------------------------------- + return static::has_one_or_many($model, $eloquent); + } + + /** + * Retrieve the query for a 1:* relationship. + * + * @param string $model + * @param object $eloquent + * @return mixed + */ + public static function has_many($model, $eloquent) + { + // ----------------------------------------------------- + // Set the relating type. + // ----------------------------------------------------- + $eloquent->relating = __FUNCTION__; + + // ----------------------------------------------------- + // Return the Eloquent model. + // ----------------------------------------------------- + return static::has_one_or_many($model, $eloquent); + } + + /** + * Retrieve the query for a 1:1 or 1:* relationship. + * + * @param string $model + * @param object $eloquent + * @return mixed + */ + private static function has_one_or_many($model, $eloquent) + { + // ----------------------------------------------------- + // Set the relating key. + // ----------------------------------------------------- + $eloquent->relating_key = \System\Str::lower(get_class($eloquent)).'_id'; + + return Factory::make($model)->where($eloquent->relating_key, '=', $eloquent->id); + } + + /** + * Retrieve the query for a 1:1 belonging relationship. + * + * @param array $caller + * @param string $model + * @param object $eloquent + * @return mixed + */ + public static function belongs_to($caller, $model, $eloquent) + { + // ----------------------------------------------------- + // Set the relating type. + // ----------------------------------------------------- + $eloquent->relating = __FUNCTION__; + + // ----------------------------------------------------- + // Set the relating key. + // ----------------------------------------------------- + $eloquent->relating_key = $caller['function'].'_id'; + + // ----------------------------------------------------- + // Return the Eloquent model. + // ----------------------------------------------------- + return Factory::make($model)->where('id', '=', $eloquent->attributes[$eloquent->relating_key]); + } + + /** + * Retrieve the query for a *:* relationship. + * + * @param string $model + * @param object $eloquent + * @return mixed + */ + public static function has_many_and_belongs_to($model, $eloquent) + { + // ----------------------------------------------------- + // Get the models involved in the relationship. + // ----------------------------------------------------- + $models = array(\System\Str::lower($model), \System\Str::lower(get_class($eloquent))); + + // ----------------------------------------------------- + // Sort the model names involved in the relationship. + // ----------------------------------------------------- + sort($models); + + // ----------------------------------------------------- + // Get the intermediate table name, which is the names + // of the two related models alphabetized. + // ----------------------------------------------------- + $eloquent->relating_table = implode('_', $models); + + // ----------------------------------------------------- + // Set the relating type. + // ----------------------------------------------------- + $eloquent->relating = __FUNCTION__; + + // ----------------------------------------------------- + // Set the relating key. + // ----------------------------------------------------- + $eloquent->relating_key = $eloquent->relating_table.'.'.\System\Str::lower(get_class($eloquent)).'_id'; + + // ----------------------------------------------------- + // Return the Eloquent model. + // ----------------------------------------------------- + return Factory::make($model) + ->select(Meta::table($model).'.*') + ->join($eloquent->relating_table, Meta::table($model).'.id', '=', $eloquent->relating_table.'.'.\System\Str::lower($model).'_id') + ->where($eloquent->relating_key, '=', $eloquent->id); + } + +} \ No newline at end of file diff --git a/system/db/eloquent/warehouse.php b/system/db/eloquent/warehouse.php new file mode 100644 index 000000000..9eff22ef0 --- /dev/null +++ b/system/db/eloquent/warehouse.php @@ -0,0 +1,66 @@ +query = \System\DB\Query::table(Meta::table($model)); + + // ----------------------------------------------------- + // Set the activity timestamps. + // ----------------------------------------------------- + if (property_exists($model, 'timestamps') and $model::$timestamps) + { + static::timestamp($eloquent); + } + + // ----------------------------------------------------- + // If the model exists in the database, update it. + // Otherwise, insert the model and set the ID. + // ----------------------------------------------------- + if ($eloquent->exists) + { + return $eloquent->query->where('id', '=', $eloquent->attributes['id'])->update($eloquent->dirty); + } + else + { + $eloquent->attributes['id'] = $eloquent->query->insert_get_id($eloquent->attributes); + } + + // ----------------------------------------------------- + // Set the existence flag to true. + // ----------------------------------------------------- + $eloquent->exists = true; + } + + /** + * Set the activity timestamps on a model. + * + * @param object $eloquent + * @return void + */ + private static function timestamp($eloquent) + { + $eloquent->updated_at = date('Y-m-d H:i:s'); + + if ( ! $eloquent->exists) + { + $eloquent->created_at = $eloquent->updated_at; + } + } + +} \ No newline at end of file diff --git a/system/db/query.php b/system/db/query.php new file mode 100644 index 000000000..16fb634a4 --- /dev/null +++ b/system/db/query.php @@ -0,0 +1,593 @@ +connection = (is_null($connection)) ? \System\Config::get('db.default') : $connection; + + // --------------------------------------------------- + // Build the FROM clause. + // --------------------------------------------------- + $this->from = 'FROM '.$this->wrap($this->table = $table); + } + + /** + * Create a new query instance. + * + * @param string $table + * @param string $connection + * @return Query + */ + public static function table($table, $connection = null) + { + return new static($table, $connection); + } + + /** + * Force the query to return distinct results. + * + * @return Query + */ + public function distinct() + { + $this->distinct = true; + return $this; + } + + /** + * Add columns to the SELECT clause. + * + * @return Query + */ + public function select() + { + // --------------------------------------------------- + // Handle DISTINCT selections. + // --------------------------------------------------- + $this->select = ($this->distinct) ? 'SELECT DISTINCT ' : 'SELECT '; + + // --------------------------------------------------- + // Wrap all of the columns in keyword identifiers. + // --------------------------------------------------- + $this->select .= implode(', ', array_map(array($this, 'wrap'), func_get_args())); + + return $this; + } + + /** + * Add a join to the query. + * + * @param string $table + * @param string $column1 + * @param string $operator + * @param string $column2 + * @param string $type + * @return Query + */ + public function join($table, $column1, $operator, $column2, $type = 'INNER') + { + $this->from .= ' '.$type.' JOIN '.$this->wrap($table).' ON '.$this->wrap($column1).' '.$operator.' '.$this->wrap($column2); + return $this; + } + + /** + * Add a left join to the query. + * + * @param string $table + * @param string $column1 + * @param string $operator + * @param string $column2 + * @return Query + */ + public function left_join($table, $column1, $operator, $column2) + { + return $this->join($table, $column1, $operator, $column2, 'LEFT'); + } + + /** + * Add a raw where condition to the query. + * + * @param string $where + * @param array $bindings + * @param string $connector + * @return Query + */ + public function raw_where($where, $bindings = array(), $connector = 'AND') + { + $this->where .= ' '.$connector.' '.$where; + $this->bindings = array_merge($this->bindings, $bindings); + + return $this; + } + + /** + * Add a raw or where condition to the query. + * + * @param string $where + * @param array $bindings + * @return Query + */ + public function raw_or_where($where, $bindings = array()) + { + return $this->raw_where($where, $bindings, 'OR'); + } + + /** + * Add a where condition to the query. + * + * @param string $column + * @param string $operator + * @param mixed $value + * @param string $connector + * @return Query + */ + public function where($column, $operator, $value, $connector = 'AND') + { + $this->where .= ' '.$connector.' '.$this->wrap($column).' '.$operator.' ?'; + $this->bindings[] = $value; + + return $this; + } + + /** + * Add an or where condition to the query. + * + * @param string $column + * @param string $operator + * @param mixed $value + * @return Query + */ + public function or_where($column, $operator, $value) + { + return $this->where($column, $operator, $value, 'OR'); + } + + /** + * Add a where in condition to the query. + * + * @param string $column + * @param array $values + * @param string $connector + * @return Query + */ + public function where_in($column, $values, $connector = 'AND') + { + $this->where .= ' '.$connector.' '.$this->wrap($column).' IN ('.$this->parameterize($values).')'; + $this->bindings = array_merge($this->bindings, $values); + + return $this; + } + + /** + * Add an or where in condition to the query. + * + * @param string $column + * @param array $values + * @return Query + */ + public function or_where_in($column, $values) + { + return $this->where_in($column, $values, 'OR'); + } + + /** + * Add a where not in condition to the query. + * + * @param string $column + * @param array $values + * @param string $connector + * @return Query + */ + public function where_not_in($column, $values, $connector = 'AND') + { + $this->where .= ' '.$connector.' '.$this->wrap($column).' NOT IN ('.$this->parameterize($values).')'; + $this->bindings = array_merge($this->bindings, $values); + + return $this; + } + + /** + * Add an or where not in condition to the query. + * + * @param string $column + * @param array $values + * @return Query + */ + public function or_where_not_in($column, $values) + { + return $this->where_not_in($column, $values, 'OR'); + } + + /** + * Add a where null condition to the query. + * + * @param string $column + * @param string $connector + * @return Query + */ + public function where_null($column, $connector = 'AND') + { + $this->where .= ' '.$connector.' '.$this->wrap($column).' IS NULL'; + return $this; + } + + /** + * Add an or where null condition to the query. + * + * @param string $column + * @return Query + */ + public function or_where_null($column) + { + return $this->where_null($column, 'OR'); + } + + /** + * Add a where not null condition to the query. + * + * @param string $column + * @param string $connector + * @return Query + */ + public function where_not_null($column, $connector = 'AND') + { + $this->where .= ' '.$connector.' '.$this->wrap($column).' IS NOT NULL'; + return $this; + } + + /** + * Add an or where not null condition to the query. + * + * @param string $column + * @return Query + */ + public function or_where_not_null($column) + { + return $this->where_not_null($column, 'OR'); + } + + /** + * Add an ordering to the query. + * + * @param string $column + * @param string $direction + * @return Query + */ + public function order_by($column, $direction) + { + $this->orderings[] = $this->wrap($column).' '.\System\Str::upper($direction); + return $this; + } + + /** + * Set the query offset. + * + * @param int $value + * @return Query + */ + public function skip($value) + { + $this->offset = $value; + return $this; + } + + /** + * Set the query limit. + * + * @param int $value + * @return Query + */ + public function take($value) + { + $this->limit = $value; + return $this; + } + + /** + * Find a record by the primary key. + * + * @param int $id + * @return object + */ + public function find($id) + { + // --------------------------------------------------- + // Set the primary key. + // --------------------------------------------------- + $this->where('id', '=', $id); + + // --------------------------------------------------- + // Get the first result. + // --------------------------------------------------- + return $this->first(); + } + + /** + * Execute the query as a SELECT statement and return the first result. + * + * @return object + */ + public function first() + { + return (count($results = call_user_func_array(array($this->take(1), 'get'), func_get_args())) > 0) ? $results[0] : null; + } + + /** + * Execute the query as a SELECT statement. + * + * @return array + */ + public function get() + { + // --------------------------------------------------- + // Initialize the SELECT clause if it's null. + // --------------------------------------------------- + if (is_null($this->select)) + { + call_user_func_array(array($this, 'select'), (count(func_get_args()) > 0) ? func_get_args() : array('*')); + } + + return \System\DB::query(Query\Compiler::select($this), $this->bindings, $this->connection); + } + + /** + * Get an aggregate value. + * + * @param string $aggregate + * @param string $column + * @return mixed + */ + private function aggregate($aggregator, $column) + { + // --------------------------------------------------- + // Build the SELECT clause. + // --------------------------------------------------- + $this->select = 'SELECT '.$aggregator.'('.$this->wrap($column).') AS '.$this->wrap('aggregate'); + + // --------------------------------------------------- + // Execute the statement. + // --------------------------------------------------- + $results = \System\DB::query(Query\Compiler::select($this), $this->bindings); + + return $results[0]->aggregate; + } + + /** + * Execute an INSERT statement. + * + * @param array $values + * @return bool + */ + public function insert($values) + { + return \System\DB::query(Query\Compiler::insert($this, $values), array_values($values), $this->connection); + } + + /** + * Execute an INSERT statement and get the insert ID. + * + * @param array $values + * @return int + */ + public function insert_get_id($values) + { + // --------------------------------------------------- + // Compile the SQL statement. + // --------------------------------------------------- + $sql = Query\Compiler::insert($this, $values); + + // --------------------------------------------------- + // The Postgres PDO implementation does not cleanly + // implement the last insert ID function. So, we'll + // use the RETURNING clause available in Postgres. + // --------------------------------------------------- + if (\System\DB::connection($this->connection)->getAttribute(\PDO::ATTR_DRIVER_NAME) == 'pgsql') + { + // --------------------------------------------------- + // Add the RETURNING clause to the SQL. + // --------------------------------------------------- + $sql .= ' RETURNING '.$this->wrap('id'); + + // --------------------------------------------------- + // Prepare the PDO statement. + // --------------------------------------------------- + $query = \System\DB::connection($this->connection)->prepare($sql); + + // --------------------------------------------------- + // Execute the PDO statement. + // --------------------------------------------------- + $query->execute(array_values($values)); + + // --------------------------------------------------- + // Fetch the insert ID from the results. + // --------------------------------------------------- + $result = $query->fetch(\PDO::FETCH_ASSOC); + + return $result['id']; + } + // --------------------------------------------------- + // When using MySQL or SQLite, we can just use the PDO + // last insert ID function. + // --------------------------------------------------- + else + { + // --------------------------------------------------- + // Execute the statement. + // --------------------------------------------------- + \System\DB::query($sql, array_values($values), $this->connection); + + // --------------------------------------------------- + // Get the last insert ID. + // --------------------------------------------------- + return \System\DB::connection($this->connection)->lastInsertId(); + } + } + + /** + * Execute the query as an UPDATE statement. + * + * @param array $values + * @return bool + */ + public function update($values) + { + return \System\DB::query(Query\Compiler::update($this, $values), array_merge(array_values($values), $this->bindings), $this->connection); + } + + /** + * Execute the query as a DELETE statement. + * + * @param int $id + * @return bool + */ + public function delete($id = null) + { + // --------------------------------------------------- + // Set the primary key. + // --------------------------------------------------- + if ( ! is_null($id)) + { + $this->where('id', '=', $id); + } + + // --------------------------------------------------- + // Execute the statement. + // --------------------------------------------------- + return \System\DB::query(Query\Compiler::delete($this), $this->bindings, $this->connection); + } + + /** + * Wrap a value in keyword identifiers. + * + * @param string $value + * @param string $wrap + * @return string + */ + public function wrap($value, $wrap = '"') + { + // --------------------------------------------------- + // If the application is using MySQL, we need to use + // a non-standard keyword identifier. + // --------------------------------------------------- + if (\System\DB::connection($this->connection)->getAttribute(\PDO::ATTR_DRIVER_NAME) == 'mysql') + { + $wrap = '`'; + } + + // --------------------------------------------------- + // Wrap the element in keyword identifiers. + // --------------------------------------------------- + return implode('.', array_map(function($segment) use ($wrap) {return ($segment != '*') ? $wrap.$segment.$wrap : $segment;}, explode('.', $value))); + } + + /** + * Create query parameters from an array of values. + * + * @param array $values + * @return string + */ + public function parameterize($values) + { + return implode(', ', array_fill(0, count($values), '?')); + } + + /** + * Magic Method for handling dynamic functions. + */ + public function __call($method, $parameters) + { + // --------------------------------------------------- + // Handle any of the aggregate functions. + // --------------------------------------------------- + if (in_array($method, array('count', 'min', 'max', 'avg', 'sum'))) + { + return ($method == 'count') ? $this->aggregate(\System\Str::upper($method), '*') : $this->aggregate(\System\Str::upper($method), $parameters[0]); + } + else + { + throw new \Exception("Method [$method] is not defined on the Query class."); + } + } + +} \ No newline at end of file diff --git a/system/db/query/compiler.php b/system/db/query/compiler.php new file mode 100644 index 000000000..53adcb801 --- /dev/null +++ b/system/db/query/compiler.php @@ -0,0 +1,116 @@ +select.' '.$query->from.' '.$query->where; + + // --------------------------------------------------- + // Add the ORDER BY clause. + // --------------------------------------------------- + if (count($query->orderings) > 0) + { + $sql .= ' ORDER BY '.implode(', ', $query->orderings); + } + + // --------------------------------------------------- + // Add the LIMIT. + // --------------------------------------------------- + if ( ! is_null($query->limit)) + { + $sql .= ' LIMIT '.$query->limit; + } + + // --------------------------------------------------- + // Add the OFFSET. + // --------------------------------------------------- + if ( ! is_null($query->offset)) + { + $sql .= ' OFFSET '.$query->offset; + } + + return $sql; + } + + /** + * Build a SQL INSERT statement. + * + * @param Query $query + * @param array $values + * @return string + */ + public static function insert($query, $values) + { + // --------------------------------------------------- + // Start the query. Add the table name. + // --------------------------------------------------- + $sql = 'INSERT INTO '.$query->table.' ('; + + // --------------------------------------------------- + // Wrap each column name in keyword identifiers. + // --------------------------------------------------- + $columns = array(); + + foreach (array_keys($values) as $column) + { + $columns[] = $query->wrap($column); + } + + // --------------------------------------------------- + // Concatenate the column names and values. + // --------------------------------------------------- + return $sql .= implode(', ', $columns).') VALUES ('.$query->parameterize($values).')'; + } + + /** + * Build a SQL UPDATE statement. + * + * @param Query $query + * @param array $values + * @return string + */ + public static function update($query, $values) + { + // --------------------------------------------------- + // Start the query. Add the table name. + // --------------------------------------------------- + $sql = 'UPDATE '.$query->table.' SET '; + + // --------------------------------------------------- + // Wrap each column name in keyword identifiers. + // --------------------------------------------------- + $columns = array(); + + foreach (array_keys($values) as $column) + { + $columns[] = $query->wrap($column).' = ?'; + } + + // --------------------------------------------------- + // Concatenate the column names and the WHERE clause. + // --------------------------------------------------- + return $sql .= implode(', ', $columns).' '.$query->where; + } + + /** + * Build a SQL DELETE statement. + * + * @param Query $query + * @return string + */ + public static function delete($query) + { + return 'DELETE FROM '.$query->wrap($query->table).' '.$query->where; + } + +} \ No newline at end of file diff --git a/system/download.php b/system/download.php new file mode 100644 index 000000000..605673750 --- /dev/null +++ b/system/download.php @@ -0,0 +1,145 @@ + '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' + ); + + /** + * Create a download response. + * + * @param string $path + * @param string $name + * @return Response + */ + public static function file($path, $name = null) + { + // ------------------------------------------------- + // If no name was specified, just use the basename. + // ------------------------------------------------- + if (is_null($name)) + { + $name = basename($path); + } + + // ------------------------------------------------- + // Set the headers to force the download to occur. + // ------------------------------------------------- + return Response::make(file_get_contents($path))->header('Content-Description', 'File Transfer') + ->header('Content-Type', static::mime(pathinfo($path, PATHINFO_EXTENSION))) + ->header('Content-Disposition', 'attachment; filename="'.$name.'"') + ->header('Content-Transfer-Encoding', 'binary') + ->header('Expires', 0) + ->header('Cache-Control', 'must-revalidate, post-check=0, pre-check=0') + ->header('Pragma', 'public') + ->header('Content-Length', filesize($path)); + } + + /** + * Get a MIME type by extension. + * + * @param string $extension + * @param string $default + * @return string + */ + public static function mime($extension, $default = 'application/octet-stream') + { + return (array_key_exists($extension, static::$mimes)) ? static::$mimes[$extension] : $default; + } + +} \ No newline at end of file diff --git a/system/error.php b/system/error.php new file mode 100644 index 000000000..e29a3fe9e --- /dev/null +++ b/system/error.php @@ -0,0 +1,156 @@ + 'Error', + E_ERROR => 'Error', + E_WARNING => 'Warning', + E_PARSE => 'Parsing Error', + E_NOTICE => 'Notice', + E_CORE_ERROR => 'Core Error', + E_CORE_WARNING => 'Core Warning', + E_COMPILE_ERROR => 'Compile Error', + E_COMPILE_WARNING => 'Compile Warning', + E_USER_ERROR => 'User Error', + E_USER_WARNING => 'User Warning', + E_USER_NOTICE => 'User Notice', + E_STRICT => 'Runtime Notice' + ); + + /** + * Handle an exception. + * + * @param Exception $e + * @return void + */ + public static function handle($e) + { + // ----------------------------------------------------- + // Clean the output buffer. + // ----------------------------------------------------- + if (ob_get_level() > 0) + { + ob_clean(); + } + + // ----------------------------------------------------- + // Get the error severity. + // ----------------------------------------------------- + $severity = (array_key_exists($e->getCode(), static::$levels)) ? static::$levels[$e->getCode()] : $e->getCode(); + + // ----------------------------------------------------- + // Get the error file. Views require special handling + // since view errors occur within eval'd code. + // ----------------------------------------------------- + if (strpos($e->getFile(), 'view.php') !== false and strpos($e->getFile(), "eval()'d code") !== false) + { + $file = APP_PATH.'views/'.View::$last.EXT; + } + else + { + $file = $e->getFile(); + } + + // ----------------------------------------------------- + // Trim the period off of the error message. + // ----------------------------------------------------- + $message = rtrim($e->getMessage(), '.'); + + // ----------------------------------------------------- + // Log the error. + // ----------------------------------------------------- + if (Config::get('error.log')) + { + Log::error($message.' in '.$e->getFile().' on line '.$e->getLine()); + } + + if (Config::get('error.detail')) + { + // ----------------------------------------------------- + // Build the error view. + // ----------------------------------------------------- + $view = View::make('error/exception') + ->bind('severity', $severity) + ->bind('message', $message) + ->bind('file', $file) + ->bind('line', $e->getLine()) + ->bind('trace', $e->getTraceAsString()) + ->bind('contexts', static::context($file, $e->getLine())); + + // ----------------------------------------------------- + // Send the detailed error response. + // ----------------------------------------------------- + Response::make($view, 500)->send(); + } + else + { + // ----------------------------------------------------- + // Send the generic error response. + // ----------------------------------------------------- + Response::make(View::make('error/500'), 500)->send(); + } + + exit(1); + } + + /** + * Get the file context of an exception. + * + * @param string $path + * @param int $line + * @param int $padding + * @return array + */ + private static function context($path, $line, $padding = 5) + { + // ----------------------------------------------------- + // Verify that the file exists. + // ----------------------------------------------------- + if (file_exists($path)) + { + // ----------------------------------------------------- + // Get the contents of the file. + // ----------------------------------------------------- + $file = file($path, FILE_IGNORE_NEW_LINES); + + // ----------------------------------------------------- + // Unshift the array. + // ----------------------------------------------------- + array_unshift($file, ''); + + // ----------------------------------------------------- + // Calculate the starting position. + // ----------------------------------------------------- + $start = $line - $padding; + + if ($start < 0) + { + $start = 0; + } + + // ----------------------------------------------------- + // Calculate the context length. + // ----------------------------------------------------- + $length = ($line - $start) + $padding + 1; + + if (($start + $length) > count($file) - 1) + { + $length = null; + } + + // ----------------------------------------------------- + // Return the context. + // ----------------------------------------------------- + return array_slice($file, $start, $length, true); + } + + return array(); + } + +} \ No newline at end of file diff --git a/system/filter.php b/system/filter.php new file mode 100644 index 000000000..6b72719d7 --- /dev/null +++ b/system/filter.php @@ -0,0 +1,51 @@ +'; + + // ------------------------------------------------------- + // If the method is PUT or DELETE, we'll need to spoof it + // using a hidden input field. + // + // For more information, see the Input library. + // ------------------------------------------------------- + if ($method == 'PUT' or $method == 'DELETE') + { + $html .= PHP_EOL.static::hidden('request_method', $method); + } + + return $html.PHP_EOL; + } + + /** + * Generate a hidden field containing the current CSRF token. + * + * @return string + */ + public static function token() + { + return static::hidden('csrf_token', static::raw_token()); + } + + /** + * Retrieve the current CSRF token. + * + * @return string + */ + public static function raw_token() + { + // ------------------------------------------------------- + // Verify that sessions are enabled. + // ------------------------------------------------------- + if (Config::get('session.driver') == '') + { + throw new \Exception('Sessions must be enabled to retrieve a CSRF token.'); + } + + return Session::get('csrf_token'); + } + + /** + * Create a HTML text input element. + * + * @param string $name + * @param string $value + * @param array $attributes + * @return string + */ + public static function text($name, $value = null, $attributes = array()) + { + return static::input('text', $name, $value, $attributes); + } + + /** + * Create a HTML password input element. + * + * @param string $name + * @param array $attributes + * @return string + */ + public static function password($name, $attributes = array()) + { + return static::input('password', $name, null, $attributes); + } + + /** + * Create a HTML hidden input element. + * + * @param string $name + * @param array $attributes + * @return string + */ + public static function hidden($name, $value = null, $attributes = array()) + { + return static::input('hidden', $name, $value, $attributes); + } + + /** + * Create a HTML file input element. + * + * @param string $name + * @param array $attributes + * @return string + */ + public static function file($name, $attributes = array()) + { + return static::input('file', $name, null, $attributes); + } + + /** + * Create a HTML submit input element. + * + * @param string $name + * @param array $attributes + * @return string + */ + public static function submit($value, $attributes = array()) + { + return static::input('submit', null, $value, $attributes); + } + + /** + * Create a HTML button element. + * + * @param string $name + * @param string $value + * @param array $attributes + * @return string + */ + public static function button($value, $attributes = array()) + { + return ''.$value.''.PHP_EOL; + } + + /** + * Create a HTML checkbox input element. + * + * @param string $name + * @param string $value + * @param bool $checked + * @param array $attributes + * @return string + */ + public static function checkbox($name, $value = null, $checked = false, $attributes = array()) + { + return static::checkable('checkbox', $name, $value, $checked, $attributes); + } + + /** + * Create a HTML radio button input element. + * + * @param string $name + * @param string $value + * @param bool $checked + * @param array $attributes + * @return string + */ + public static function radio($name, $value = null, $checked = false, $attributes = array()) + { + return static::checkable('radio', $name, $value, $checked, $attributes); + } + + /** + * Create a checkable input element. + * + * @param string $type + * @param string $name + * @param string $value + * @param bool $checked + * @param array $attributes + * @return string + */ + private static function checkable($type, $name, $value, $checked, $attributes) + { + // ------------------------------------------------------- + // Set the checked attribute. + // ------------------------------------------------------- + if ($checked === true) + { + $attributes['checked'] = 'checked'; + } + + return static::input($type, $name, $value, $attributes); + } + + /** + * Create a HTML textarea element. + * + * @param string $name + * @param string $value + * @param array $attributes + * @return string + */ + public static function textarea($name, $value = '', $attributes = array()) + { + // ------------------------------------------------------- + // Add the name to the attributes. + // ------------------------------------------------------- + $attributes['name'] = $name; + + // ------------------------------------------------------- + // Set the default number of rows. + // ------------------------------------------------------- + if ( ! isset($attributes['rows'])) + { + $attributes['rows'] = 10; + } + + // ------------------------------------------------------- + // Set the default number of columns. + // ------------------------------------------------------- + if ( ! isset($attributes['cols'])) + { + $attributes['cols'] = 50; + } + + return ''.Str::entities($value).''.PHP_EOL; + } + + /** + * Create a HTML select element. + * + * @param string $name + * @param array $options + * @param string $selected + * @param array $attributes + * @return string + */ + public static function select($name, $options = array(), $selected = null, $attributes = array()) + { + // ------------------------------------------------------- + // Set the name attribute. + // ------------------------------------------------------- + $attributes['name'] = $name; + + // ------------------------------------------------------- + // Initialize the options array. + // ------------------------------------------------------- + $html_options = array(); + + // ------------------------------------------------------- + // Build the options in HTML. + // ------------------------------------------------------- + foreach ($options as $value => $display) + { + $option_attributes = array(); + + // ------------------------------------------------------- + // Set the value attribute. + // ------------------------------------------------------- + $option_attributes['value'] = $value; + + // ------------------------------------------------------- + // Set the selected attribute. + // ------------------------------------------------------- + $option_attributes['selected'] = ($value == $selected) ? 'selected' : null; + + // ------------------------------------------------------- + // Add the option HTML to the array of options. + // ------------------------------------------------------- + $html_options[] = ''.$display.''; + } + + return ''.implode('', $html_options).''.PHP_EOL; + } + + /** + * Create a HTML input element. + * + * @param string $name + * @param mixed $value + * @param array $attributes + * @return string + */ + private static function input($type, $name, $value = null, $attributes = array()) + { + // ------------------------------------------------------- + // Set the type attribute. + // ------------------------------------------------------- + $attributes['type'] = $type; + + // ------------------------------------------------------- + // Set the name attribute. + // ------------------------------------------------------- + $attributes['name'] = $name; + + // ------------------------------------------------------- + // Set the value attribute. + // ------------------------------------------------------- + $attributes['value'] = $value; + + return ''.PHP_EOL; + } + +} \ No newline at end of file diff --git a/system/hash.php b/system/hash.php new file mode 100644 index 000000000..2d14f2677 --- /dev/null +++ b/system/hash.php @@ -0,0 +1,52 @@ +salt = (is_null($salt)) ? Str::random(16) : $salt; + + // -------------------------------------------------------------- + // Perform a salted, SHA-1 hash on the value. + // -------------------------------------------------------------- + $this->value = sha1($value.$this->salt); + } + + /** + * Factory for creating hash instances. + * + * @access public + * @param string $value + * @param string $salt + * @return Hash + */ + public static function make($value, $salt = null) + { + return new self($value, $salt); + } + +} \ No newline at end of file diff --git a/system/html.php b/system/html.php new file mode 100644 index 000000000..7c6eac52c --- /dev/null +++ b/system/html.php @@ -0,0 +1,275 @@ +'.PHP_EOL; + } + + /** + * Generate a CSS reference. + * + * @param string $url + * @return string + */ + public static function style($url, $media = 'all') + { + return ''.PHP_EOL; + } + + /** + * Generate a HTML link. + * + * @param string $url + * @param string $title + * @param array $attributes + * @param bool $https + * @return string + */ + public static function link($url, $title, $attributes = array(), $https = false) + { + return ''.Str::entities($title).''; + } + + /** + * Generate a HTTPS HTML link. + * + * @param string $url + * @param string $title + * @param array $attributes + * @return string + */ + public static function secure_link($url, $title, $attributes) + { + return static::link($url, $title, $attributes, true); + } + + /** + * Generate an HTML mailto link. + * + * @param string $email + * @param string $title + * @param array $attributes + * @return string + */ + public static function mailto($email, $title = null, $attributes = array()) + { + // ------------------------------------------------------- + // Obfuscate the e-mail address. + // ------------------------------------------------------- + $email = static::email($email); + + // ------------------------------------------------------- + // If no title is specified, just use the e-mail address. + // ------------------------------------------------------- + if (is_null($title)) + { + $title = $email; + } + + return ''.$title.''; + } + + /** + * Obfuscate an e-mail address to prevent spam-bots from sniffing it. + * + * @param string $email + * @return string + */ + public static function email($email) + { + return str_replace('@', '@', static::obfuscate($email)); + } + + /** + * Generate an HTML image. + * + * @param string $url + * @param string $alt + * @param array $attributes + * @return string + */ + public static function image($url, $alt = '', $attributes = array()) + { + // ------------------------------------------------------- + // Add the "alt" tag to the attributes. + // ------------------------------------------------------- + $attributes['alt'] = Str::entities($alt); + + return ''; + } + + /** + * Generate HTML breaks. + * + * @param int $count + * @return string + */ + public static function breaks($count = 1) + { + return str_repeat('
', $count); + } + + /** + * Generate non-breaking spaces. + * + * @param int $count + * @return string + */ + public static function spaces($count = 1) + { + return str_repeat(' ', $count); + } + + /** + * Generate an ordered list. + * + * @param array $list + * @param array $attributes + * @return string + */ + public static function ol($list, $attributes = array()) + { + return static::list_elements('ol', $list, $attributes); + } + + /** + * Generate an un-ordered list. + * + * @param array $list + * @param array $attributes + * @return string + */ + public static function ul($list, $attributes = array()) + { + return static::list_elements('ul', $list, $attributes); + } + + /** + * Generate an ordered or un-ordered list. + * + * @param string $type + * @param array $list + * @param array $attributes + * @return string + */ + private static function list_elements($type, $list, $attributes) + { + // ------------------------------------------------------- + // Verify the list is an array. + // ------------------------------------------------------- + if ( ! is_array($list)) + { + return ''; + } + + // ------------------------------------------------------- + // Initialize the output value. + // ------------------------------------------------------- + $html = ''; + + // ------------------------------------------------------- + // Add the list items. + // ------------------------------------------------------- + foreach ($list as $key => $value) + { + $html .= '
  • '.Str::entities($value).'
  • '; + } + + // ------------------------------------------------------- + // Build the list opening tag. + // ------------------------------------------------------- + $start = '<'.$type.static::attributes($attributes).'>'; + + return $start.$html.''; + } + + /** + * Build a list of HTML attributes. + * + * @param array $attributes + * @return string + */ + public static function attributes($attributes) + { + $html = array(); + + foreach ($attributes as $key => $value) + { + // ------------------------------------------------------- + // If the value is null, skip it. + // ------------------------------------------------------- + if (is_null($value)) + { + continue; + } + + // ------------------------------------------------------- + // Add the HTML attribute to the array of attributes. + // ------------------------------------------------------- + $html[] = $key.'="'.Str::entities($value).'"'; + } + + // ------------------------------------------------------- + // Concatenate all of the attributes together. + // ------------------------------------------------------- + if (count($html) > 0) + { + return ' '.implode(' ', $html); + } + else + { + return ''; + } + } + + /** + * Obfuscate a string to prevent spam-bots from sniffing it. + * + * @param string $value + * @return string + */ + public static function obfuscate($value) + { + $safe = ''; + + // ------------------------------------------------------- + // Spin through the string letter by letter. + // ------------------------------------------------------- + foreach (str_split($value) as $letter) + { + switch (rand(1, 3)) + { + // ------------------------------------------------------- + // Convert the letter to its entity representation. + // ------------------------------------------------------- + case 1: + $safe .= '&#'.ord($letter).';'; + break; + + // ------------------------------------------------------- + // Convert the letter to a Hex character code. + // ------------------------------------------------------- + case 2: + $safe .= '&#x'.dechex(ord($letter)).';'; + break; + + // ------------------------------------------------------- + // No encoding. + // ------------------------------------------------------- + case 3: + $safe .= $letter; + } + } + + return $safe; + } + +} \ No newline at end of file diff --git a/system/inflector.php b/system/inflector.php new file mode 100644 index 000000000..f732127e2 --- /dev/null +++ b/system/inflector.php @@ -0,0 +1,230 @@ + "$1zes", + '/^(ox)$/i' => "$1en", + '/([m|l])ouse$/i' => "$1ice", + '/(matr|vert|ind)ix|ex$/i' => "$1ices", + '/(x|ch|ss|sh)$/i' => "$1es", + '/([^aeiouy]|qu)y$/i' => "$1ies", + '/(hive)$/i' => "$1s", + '/(?:([^f])fe|([lr])f)$/i' => "$1$2ves", + '/(shea|lea|loa|thie)f$/i' => "$1ves", + '/sis$/i' => "ses", + '/([ti])um$/i' => "$1a", + '/(tomat|potat|ech|her|vet)o$/i' => "$1oes", + '/(bu)s$/i' => "$1ses", + '/(alias)$/i' => "$1es", + '/(octop)us$/i' => "$1i", + '/(ax|test)is$/i' => "$1es", + '/(us)$/i' => "$1es", + '/s$/i' => "s", + '/$/' => "s" + ); + + /** + * Singular word forms. + * + * @var array + */ + private static $singular = array( + '/(quiz)zes$/i' => "$1", + '/(matr)ices$/i' => "$1ix", + '/(vert|ind)ices$/i' => "$1ex", + '/^(ox)en$/i' => "$1", + '/(alias)es$/i' => "$1", + '/(octop|vir)i$/i' => "$1us", + '/(cris|ax|test)es$/i' => "$1is", + '/(shoe)s$/i' => "$1", + '/(o)es$/i' => "$1", + '/(bus)es$/i' => "$1", + '/([m|l])ice$/i' => "$1ouse", + '/(x|ch|ss|sh)es$/i' => "$1", + '/(m)ovies$/i' => "$1ovie", + '/(s)eries$/i' => "$1eries", + '/([^aeiouy]|qu)ies$/i' => "$1y", + '/([lr])ves$/i' => "$1f", + '/(tive)s$/i' => "$1", + '/(hive)s$/i' => "$1", + '/(li|wi|kni)ves$/i' => "$1fe", + '/(shea|loa|lea|thie)ves$/i' => "$1f", + '/(^analy)ses$/i' => "$1sis", + '/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i' => "$1$2sis", + '/([ti])a$/i' => "$1um", + '/(n)ews$/i' => "$1ews", + '/(h|bl)ouses$/i' => "$1ouse", + '/(corpse)s$/i' => "$1", + '/(us)es$/i' => "$1", + '/(us|ss)$/i' => "$1", + '/s$/i' => "", + ); + + /** + * Irregular word forms. + * + * @var array + */ + private static $irregular = array( + 'move' => 'moves', + 'foot' => 'feet', + 'goose' => 'geese', + 'sex' => 'sexes', + 'child' => 'children', + 'man' => 'men', + 'tooth' => 'teeth', + 'person' => 'people', + ); + + /** + * Uncountable word forms. + * + * @var array + */ + private static $uncountable = array( + 'sheep', + 'fish', + 'deer', + 'series', + 'species', + 'money', + 'rice', + 'information', + 'equipment', + ); + + /** + * Convert a word to its plural form. + * + * @param string $value + * @return string + */ + public static function plural($value) + { + // ----------------------------------------------------- + // Have we already converted this word? + // ----------------------------------------------------- + if (array_key_exists($value, static::$plural_cache)) + { + return static::$plural_cache[$value]; + } + + // ----------------------------------------------------- + // Are the singular and plural forms the same? + // ----------------------------------------------------- + if (in_array(Str::lower($value), static::$uncountable)) + { + return static::$plural_cache[$value] = $value; + } + + // ----------------------------------------------------- + // Is the plural form irregular? + // ----------------------------------------------------- + foreach (static::$irregular as $pattern => $irregular) + { + $pattern = '/'.$pattern.'$/i'; + + if (preg_match($pattern, $value)) + { + return static::$plural_cache[$value] = preg_replace($pattern, $irregular, $value); + } + } + + // ----------------------------------------------------- + // Check the plural forms for matches. + // ----------------------------------------------------- + foreach (static::$plural as $pattern => $plural) + { + if (preg_match($pattern, $value)) + { + return static::$plural_cache[$value] = preg_replace($pattern, $plural, $value); + } + } + + return static::$plural_cache[$value] = $value; + } + + /** + * Convert a word to its singular form. + * + * @param string $value + * @return string + */ + public static function singular($value) + { + // ----------------------------------------------------- + // Have we already converted this word? + // ----------------------------------------------------- + if (array_key_exists($value, static::$singular_cache)) + { + return static::$singular_cache[$value]; + } + + // ----------------------------------------------------- + // Are the singular and plural forms the same? + // ----------------------------------------------------- + if (in_array(Str::lower($value), static::$uncountable)) + { + return static::$singular_cache[$value] = $value; + } + + // ----------------------------------------------------- + // Is the plural form irregular? + // ----------------------------------------------------- + foreach (static::$irregular as $irregular => $pattern) + { + $pattern = '/'.$pattern.'$/i'; + + if (preg_match($pattern, $value)) + { + return static::$singular_cache[$value] = preg_replace($pattern, $irregular, $value); + } + } + + // ----------------------------------------------------- + // Check the singular forms for matches. + // ----------------------------------------------------- + foreach (static::$singular as $pattern => $singular) + { + if (preg_match($pattern, $value)) + { + return static::$singular_cache[$value] = preg_replace($pattern, $singular, $value); + } + } + + return static::$singular_cache[$value] = $value; + } + + /** + * Get the plural form of a word if the count is greater than zero. + * + * @param string $value + * @param int $count + * @return string + */ + public static function plural_if($value, $count) + { + return ($count > 1) ? static::plural($value) : $value; + } + +} \ No newline at end of file diff --git a/system/input.php b/system/input.php new file mode 100644 index 000000000..418ad92a5 --- /dev/null +++ b/system/input.php @@ -0,0 +1,135 @@ +key = $key; + } + + /** + * Create a Lang instance for a language line. + * + * @param string $key + * @return Lang + */ + public static function line($key) + { + return new static($key); + } + + /** + * Get the language line for a given language. + * + * @param string $language + * @return string + */ + public function get($language = null) + { + // -------------------------------------------------------------- + // If no language was specified, use the default language. + // -------------------------------------------------------------- + if (is_null($language)) + { + $language = Config::get('application.language'); + } + + // -------------------------------------------------------------- + // Extract the file and item from the key. + // -------------------------------------------------------------- + list($file, $line) = $this->parse($this->key); + + // -------------------------------------------------------------- + // Load the language file. + // -------------------------------------------------------------- + $this->load($file, $language); + + // -------------------------------------------------------------- + // Get the language line. + // -------------------------------------------------------------- + if (array_key_exists($line, static::$lines[$language.$file])) + { + $line = static::$lines[$language.$file][$line]; + } + else + { + throw new \Exception("Language line [$line] does not exist for language [$language]"); + } + + // -------------------------------------------------------------- + // Make all place-holder replacements. + // -------------------------------------------------------------- + foreach ($this->replacements as $key => $value) + { + $line = str_replace(':'.$key, $value, $line); + } + + return $line; + } + + /** + * Parse a language key. + * + * @param string $key + * @return array + */ + private function parse($key) + { + // --------------------------------------------- + // Get the key segments. + // --------------------------------------------- + $segments = explode('.', $key); + + // --------------------------------------------- + // Validate the key format. + // --------------------------------------------- + if (count($segments) < 2) + { + throw new \Exception("Invalid language key [$key]."); + } + + // --------------------------------------------- + // Return the file and item name. + // --------------------------------------------- + return array($segments[0], implode('.', array_slice($segments, 1))); + } + + /** + * Load a language file. + * + * @param string $file + * @param string $language + * @return void + */ + private function load($file, $language) + { + // -------------------------------------------------------------- + // Do not load the file if it has already been loaded. + // -------------------------------------------------------------- + if (in_array($language.$file, static::$loaded)) + { + return; + } + + // -------------------------------------------------------------- + // Does the language file exist? + // -------------------------------------------------------------- + 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]."); + } + + // -------------------------------------------------------------- + // Add the file to the array of loaded files. + // -------------------------------------------------------------- + static::$loaded[] = $language.$file; + } + + /** + * Set the place-holder replacements. + * + * @param array $replacements + * @return Lang + */ + public function replace($replacements) + { + $this->replacements = $replacements; + return $this; + } + +} \ No newline at end of file diff --git a/system/loader.php b/system/loader.php new file mode 100644 index 000000000..aa4573901 --- /dev/null +++ b/system/loader.php @@ -0,0 +1,50 @@ +addServer($server['host'], $server['port'], true, $server['weight']); + } + + // ----------------------------------------------------- + // Verify Memcache was configured successfully. + // ----------------------------------------------------- + if ($memcache->getVersion() === false) + { + throw new \Exception('Memcached is configured. However, no connections could be made. Please verify your memcached configuration.'); + } + + static::$instance = $memcache; + } + + return static::$instance; + } + +} \ No newline at end of file diff --git a/system/redirect.php b/system/redirect.php new file mode 100644 index 000000000..5655d512a --- /dev/null +++ b/system/redirect.php @@ -0,0 +1,66 @@ +header('Refresh', '0;url='.$url) + : Response::make('', $status)->header('Location', $url); + } + + /** + * Create a redirect response to a HTTPS URL. + * + * @param string $url + * @param string $method + * @param int $status + * @return Response + */ + public static function to_secure($url, $method = 'location', $status = 302) + { + return static::to($url, $method, $status, true); + } + + /** + * Magic Method to handle redirecting to routes. + */ + public static function __callStatic($method, $parameters) + { + // ---------------------------------------------------- + // Dynamically redirect to a secure route URL. + // ---------------------------------------------------- + if (strpos($method, 'to_secure_') === 0) + { + return static::to(URL::to_route(substr($method, 10), $parameters, true)); + } + + // ---------------------------------------------------- + // Dynamically redirect a route URL. + // ---------------------------------------------------- + if (strpos($method, 'to_') === 0) + { + return static::to(URL::to_route(substr($method, 3), $parameters)); + } + + throw new \Exception("Method [$method] is not defined on the Redirect class."); + } + +} \ No newline at end of file diff --git a/system/request.php b/system/request.php new file mode 100644 index 000000000..f2073513d --- /dev/null +++ b/system/request.php @@ -0,0 +1,136 @@ + 'Continue', + 101 => 'Switching Protocols', + 200 => 'OK', + 201 => 'Created', + 202 => 'Accepted', + 203 => 'Non-Authoritative Information', + 204 => 'No Content', + 205 => 'Reset Content', + 206 => 'Partial Content', + 207 => 'Multi-Status', + 300 => 'Multiple Choices', + 301 => 'Moved Permanently', + 302 => 'Found', + 303 => 'See Other', + 304 => 'Not Modified', + 305 => 'Use Proxy', + 307 => 'Temporary Redirect', + 400 => 'Bad Request', + 401 => 'Unauthorized', + 402 => 'Payment Required', + 403 => 'Forbidden', + 404 => 'Not Found', + 405 => 'Method Not Allowed', + 406 => 'Not Acceptable', + 407 => 'Proxy Authentication Required', + 408 => 'Request Timeout', + 409 => 'Conflict', + 410 => 'Gone', + 411 => 'Length Required', + 412 => 'Precondition Failed', + 413 => 'Request Entity Too Large', + 414 => 'Request-URI Too Long', + 415 => 'Unsupported Media Type', + 416 => 'Requested Range Not Satisfiable', + 417 => 'Expectation Failed', + 422 => 'Unprocessable Entity', + 423 => 'Locked', + 424 => 'Failed Dependency', + 500 => 'Internal Server Error', + 501 => 'Not Implemented', + 502 => 'Bad Gateway', + 503 => 'Service Unavailable', + 504 => 'Gateway Timeout', + 505 => 'HTTP Version Not Supported', + 507 => 'Insufficient Storage', + 509 => 'Bandwidth Limit Exceeded' + ); + + /** + * Create a new response instance. + * + * @param mixed $content + * @param int $status + */ + public function __construct($content, $status = 200) + { + $this->content = $content; + $this->status = $status; + } + + /** + * Factory for creating new response instances. + * + * @param string $content + * @param int $status + * @return Response + */ + public static function make($content, $status = 200) + { + return new static($content, $status); + } + + /** + * Factory for creating new view response instances. + * + * @param string $view + * @param int $status + * @return Response + */ + public static function view($view, $status = 200) + { + return static::make(View::make($view), $status); + } + + /** + * Send the response to the browser. + * + * @return void + */ + public function send() + { + // ------------------------------------------------- + // Set the content type if it has not been set. + // ------------------------------------------------- + if ( ! array_key_exists('Content-Type', $this->headers)) + { + $this->header('Content-Type', 'text/html; charset=utf-8'); + } + + // ------------------------------------------------- + // Send the headers to the browser. + // ------------------------------------------------- + if ( ! headers_sent()) + { + // ------------------------------------------------- + // Send the HTTP protocol and status code. + // ------------------------------------------------- + $protocol = (isset($_SERVER['SERVER_PROTOCOL'])) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.1'; + + header($protocol.' '.$this->status.' '.$this->statuses[$this->status]); + + // ------------------------------------------------- + // Send the rest of the headers. + // ------------------------------------------------- + foreach ($this->headers as $name => $value) + { + header($name.': '.$value, true); + } + } + + // ------------------------------------------------- + // Send the content of the response to the browser. + // ------------------------------------------------- + echo (string) $this->content; + } + + /** + * Add a header to the response. + * + * @param string $name + * @param string $value + * @return Response + */ + public function header($name, $value) + { + $this->headers[$name] = $value; + return $this; + } + + /** + * Add an item to the session flash data. + * + * @param string $key + * @param mixed $value + * @return Response + */ + public function with($key, $value) + { + if (Config::get('session.driver') != '') + { + Session::flash($key, $value); + } + + return $this; + } + + /** + * Determine if the response is a redirect. + * + * @return bool + */ + public function is_redirect() + { + return $this->status == 301 or $this->status == 302; + } + + /** + * Magic Method for getting response View data. + */ + public function __get($key) + { + // ------------------------------------------------------ + // Attempt to get the data from the View. + // ------------------------------------------------------ + if ($this->content instanceof View) + { + return $this->content->$key; + } + } + + /** + * Magic Method for setting response View data. + */ + public function __set($key, $value) + { + // ------------------------------------------------------ + // Attempt to set the data on the View. + // ------------------------------------------------------ + if ($this->content instanceof View) + { + $this->content->bind($key, $value); + } + } + + /** + * Magic Method for handling dynamic method calls. + */ + public function __call($method, $parameters) + { + // ------------------------------------------------------ + // Attempt to the pass the method to the View instance. + // ------------------------------------------------------ + if ($this->content instanceof View and method_exists($this->content, $method)) + { + call_user_func_array(array($this->content, $method), $parameters); + + return $this; + } + + throw new \Exception("Method [$method] does not exist on the Response class."); + } + + /** + * Get the content of the response. + */ + public function __toString() + { + return (string) $this->content; + } + +} \ No newline at end of file diff --git a/system/route.php b/system/route.php new file mode 100644 index 000000000..26a3fd5fb --- /dev/null +++ b/system/route.php @@ -0,0 +1,85 @@ +route = $route; + $this->parameters = $parameters; + } + + /** + * Execute the route function. + * + * @param mixed $route + * @param array $parameters + * @return mixed + */ + public function call() + { + $response = null; + + // -------------------------------------------------------------- + // If the route just has a callback, call it. + // -------------------------------------------------------------- + if (is_callable($this->route)) + { + $response = call_user_func_array($this->route, $this->parameters); + } + // -------------------------------------------------------------- + // The route value is an array. We'll need to evaluate it. + // -------------------------------------------------------------- + elseif (is_array($this->route)) + { + // -------------------------------------------------------------- + // Call the "before" route filters. + // -------------------------------------------------------------- + $response = isset($this->route['before']) ? Filter::call($this->route['before']) : null; + + // -------------------------------------------------------------- + // Call the route callback. + // -------------------------------------------------------------- + if (is_null($response) and isset($this->route['do'])) + { + $response = call_user_func_array($this->route['do'], $this->parameters); + } + } + + // -------------------------------------------------------------- + // Make sure the response is a Response instance. + // -------------------------------------------------------------- + $response = ( ! $response instanceof Response) ? new Response($response) : $response; + + // -------------------------------------------------------------- + // Call the "after" route filters. + // -------------------------------------------------------------- + if (is_array($this->route) and isset($this->route['after'])) + { + Filter::call($this->route['after'], array($response)); + } + + return $response; + } + +} \ No newline at end of file diff --git a/system/router.php b/system/router.php new file mode 100644 index 000000000..6f8d0ea27 --- /dev/null +++ b/system/router.php @@ -0,0 +1,148 @@ + $callback) + { + // -------------------------------------------------------------- + // Only check routes that have multiple URIs or wildcards. + // All other routes would have been caught by a literal match. + // -------------------------------------------------------------- + if (strpos($keys, '(') !== false or strpos($keys, ',') !== false ) + { + // -------------------------------------------------------------- + // Multiple routes can be assigned to a callback using commas. + // -------------------------------------------------------------- + foreach (explode(', ', $keys) as $route) + { + // -------------------------------------------------------------- + // Change wildcards into regular expressions. + // -------------------------------------------------------------- + $route = str_replace(':num', '[0-9]+', str_replace(':any', '.+', $route)); + + // -------------------------------------------------------------- + // Test the route for a match. + // -------------------------------------------------------------- + if (preg_match('#^'.$route.'$#', $method.' '.$uri)) + { + return new Route($callback, static::parameters(explode('/', $uri), explode('/', $route))); + } + } + } + } + } + + /** + * Find a route by name. + * + * @param string $name + * @return array + */ + public static function find($name) + { + // ---------------------------------------------------- + // Have we already looked up this named route? + // ---------------------------------------------------- + if (array_key_exists($name, static::$names)) + { + return static::$names[$name]; + } + + // ---------------------------------------------------- + // Instantiate the recursive array iterator. + // ---------------------------------------------------- + $arrayIterator = new \RecursiveArrayIterator(static::$routes); + + // ---------------------------------------------------- + // Instantiate the recursive iterator iterator. + // ---------------------------------------------------- + $recursiveIterator = new \RecursiveIteratorIterator($arrayIterator); + + // ---------------------------------------------------- + // Iterate through the routes searching for a route + // name that matches the given name. + // ---------------------------------------------------- + foreach ($recursiveIterator as $iterator) + { + $route = $recursiveIterator->getSubIterator(); + + if ($route['name'] == $name) + { + return static::$names[$name] = array($arrayIterator->key() => iterator_to_array($route)); + } + } + } + + /** + * Get the parameters that should be passed to the route callback. + * + * @param array $uri_segments + * @param array $route_segments + * @return array + */ + private static function parameters($uri_segments, $route_segments) + { + $parameters = array(); + + // -------------------------------------------------------------- + // Spin through the route segments looking for parameters. + // -------------------------------------------------------------- + for ($i = 0; $i < count($route_segments); $i++) + { + // -------------------------------------------------------------- + // Any segment wrapped in parentheses is a parameter. + // -------------------------------------------------------------- + if (strpos($route_segments[$i], '(') === 0) + { + $parameters[] = $uri_segments[$i]; + } + } + + return $parameters; + } + +} \ No newline at end of file diff --git a/system/session.php b/system/session.php new file mode 100644 index 000000000..3ff446611 --- /dev/null +++ b/system/session.php @@ -0,0 +1,281 @@ +load($id); + } + + // ----------------------------------------------------- + // If the session is invalid, start a new one. + // ----------------------------------------------------- + if (is_null($id) or is_null(static::$session) or (time() - static::$session['last_activity']) > (Config::get('session.lifetime') * 60)) + { + static::$session['id'] = Str::random(40); + static::$session['data'] = array(); + } + + // ----------------------------------------------------- + // Generate a CSRF token if one does not exist. + // ----------------------------------------------------- + if ( ! static::has('csrf_token')) + { + static::put('csrf_token', Str::random(16)); + } + + // ----------------------------------------------------- + // Set the last activity timestamp for the user. + // ----------------------------------------------------- + static::$session['last_activity'] = time(); + } + + /** + * Determine if the session contains an item. + * + * @param string $key + * @return bool + */ + public static function has($key) + { + return array_key_exists($key, static::$session['data']) or + array_key_exists(':old:'.$key, static::$session['data']) or + array_key_exists(':new:'.$key, static::$session['data']); + } + + /** + * Get an item from the session. + * + * @param string $key + * @return mixed + */ + public static function get($key, $default = null) + { + if (static::has($key)) + { + if (array_key_exists($key, static::$session['data'])) + { + return static::$session['data'][$key]; + } + // ----------------------------------------------------- + // Check the flash data for the item. + // ----------------------------------------------------- + elseif (array_key_exists(':old:'.$key, static::$session['data'])) + { + return static::$session['data'][':old:'.$key]; + } + elseif (array_key_exists(':new:'.$key, static::$session['data'])) + { + return static::$session['data'][':new:'.$key]; + } + } + + return $default; + } + + /** + * Get an item from the session and delete it. + * + * @param string $key + * @return mixed + */ + public static function once($key, $default = null) + { + // ----------------------------------------------------- + // Get the item from the session. + // ----------------------------------------------------- + $value = static::get($key, $default); + + // ----------------------------------------------------- + // Delete the item from the session. + // ----------------------------------------------------- + static::forget($key); + + return $value; + } + + /** + * Write an item to the session. + * + * @param string $key + * @param mixed $value + * @return void + */ + public static function put($key, $value) + { + static::$session['data'][$key] = $value; + } + + /** + * Write a flash item to the session. + * + * @param string $key + * @param mixed $value + * @return void + */ + public static function flash($key, $value) + { + static::put(':new:'.$key, $value); + } + + /** + * Remove an item from the session. + * + * @param string $key + * @return void + */ + public static function forget($key) + { + unset(static::$session['data'][$key]); + } + + /** + * Remove all items from the session. + * + * @return void + */ + public static function flush() + { + static::$session['data'] = array(); + } + + /** + * Regenerate the session ID. + * + * @return void + */ + public static function regenerate() + { + // ----------------------------------------------------- + // Delete the old session from storage. + // ----------------------------------------------------- + static::driver()->delete(static::$session['id']); + + // ----------------------------------------------------- + // Create a new session ID. + // ----------------------------------------------------- + static::$session['id'] = Str::random(40); + } + + /** + * Close the session. + * + * @return void + */ + public static function close() + { + // ----------------------------------------------------- + // Flash the old input into the session. + // ----------------------------------------------------- + static::flash('laravel_old_input', Input::get()); + + // ----------------------------------------------------- + // Age the session flash data. + // ----------------------------------------------------- + static::age_flash(); + + // ----------------------------------------------------- + // Save the session to storage. + // ----------------------------------------------------- + static::driver()->save(static::$session); + + if ( ! headers_sent()) + { + // ----------------------------------------------------- + // Calculate the cookie lifetime. + // ----------------------------------------------------- + $lifetime = (Config::get('session.expire_on_close')) ? 0 : Config::get('session.lifetime'); + + // ----------------------------------------------------- + // Write the session cookie. + // ----------------------------------------------------- + Cookie::put('laravel_session', static::$session['id'], $lifetime, Config::get('session.path'), Config::get('session.domain'), Config::get('session.https')); + } + + // ----------------------------------------------------- + // Perform session garbage collection (2% chance). + // ----------------------------------------------------- + if (mt_rand(1, 100) <= 2) + { + static::driver()->sweep(time() - (Config::get('session.lifetime') * 60)); + } + } + + /** + * Age the session flash data. + * + * @return void + */ + private static function age_flash() + { + // ----------------------------------------------------- + // Expire all of the old flash data. + // ----------------------------------------------------- + foreach (static::$session['data'] as $key => $value) + { + if (strpos($key, ':old:') === 0) + { + static::forget($key); + } + } + + // ----------------------------------------------------- + // Age all of the new flash data. + // ----------------------------------------------------- + foreach (static::$session['data'] as $key => $value) + { + if (strpos($key, ':new:') === 0) + { + // ----------------------------------------------------- + // Create an :old: flash item. + // ----------------------------------------------------- + static::put(':old:'.substr($key, 5), $value); + + // ----------------------------------------------------- + // Forget the :new: flash item. + // ----------------------------------------------------- + static::forget($key); + } + } + } + +} \ No newline at end of file diff --git a/system/session/driver.php b/system/session/driver.php new file mode 100644 index 000000000..ebf2ab4e2 --- /dev/null +++ b/system/session/driver.php @@ -0,0 +1,37 @@ +query()->find($id); + + // ----------------------------------------------------- + // If the session was found, return it. + // ----------------------------------------------------- + if ( ! is_null($session)) + { + return array('id' => $session->id, 'last_activity' => $session->last_activity, 'data' => unserialize($session->data)); + } + } + + /** + * Save a session. + * + * @param array $session + * @return void + */ + public function save($session) + { + // ----------------------------------------------------- + // Delete the existing session row. + // ----------------------------------------------------- + $this->delete($session['id']); + + // ----------------------------------------------------- + // Insert a new session row. + // ----------------------------------------------------- + $this->query()->insert(array('id' => $session['id'], 'last_activity' => $session['last_activity'], 'data' => serialize($session['data']))); + } + + /** + * Delete a session by ID. + * + * @param string $id + * @return void + */ + public function delete($id) + { + $this->query()->where('id', '=', $id)->delete(); + } + + /** + * Delete all expired sessions. + * + * @param int $expiration + * @return void + */ + public function sweep($expiration) + { + $this->query()->where('last_activity', '<', $expiration)->delete(); + } + + /** + * Get a session database query. + * + * @return Query + */ + private function query() + { + return \System\DB::table(\System\Config::get('session.table')); + } + +} \ No newline at end of file diff --git a/system/session/driver/file.php b/system/session/driver/file.php new file mode 100644 index 000000000..840a5483c --- /dev/null +++ b/system/session/driver/file.php @@ -0,0 +1,64 @@ +get($id); + } + + /** + * Save a session. + * + * @param array $session + * @return void + */ + public function save($session) + { + \System\Cache::driver('memcached')->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('memcached')->forget($id); + } + + /** + * Delete all expired sessions. + * + * @param int $expiration + * @return void + */ + public function sweep($expiration) + { + // Memcached sessions will expire automatically. + } + +} \ No newline at end of file diff --git a/system/session/factory.php b/system/session/factory.php new file mode 100644 index 000000000..be0efa38f --- /dev/null +++ b/system/session/factory.php @@ -0,0 +1,29 @@ += $limit) + { + // ----------------------------------------------------- + // Trim the output. + // ----------------------------------------------------- + $out = trim($out); + + // ----------------------------------------------------- + // Add the ending character to the string. + // ----------------------------------------------------- + return (strlen($out) == strlen($value)) ? $out : $out.$end; + } + } + } + + /** + * Censor a string. + * + * @param string $value + * @param array $censored + * @param string $replacement + * @return string + */ + public static function censor($value, $censored, $replacement = '####') + { + // ----------------------------------------------------- + // Pad the value with spaces. + // ----------------------------------------------------- + $value = ' '.$value.' '; + + // ----------------------------------------------------- + // Assume the word will be book-ended by the following. + // ----------------------------------------------------- + $delim = '[-_\'\"`(){}<>\[\]|!?@#%&,.:;^~*+=\/ 0-9\n\r\t]'; + + // ----------------------------------------------------- + // Replace the censored words. + // ----------------------------------------------------- + foreach ($censored as $word) + { + if ($replacement != '') + { + $value = preg_replace("/({$delim})(".str_replace('\*', '\w*?', preg_quote($word, '/')).")({$delim})/i", "\\1{$replacement}\\3", $value); + } + else + { + $value = preg_replace("/({$delim})(".str_replace('\*', '\w*?', preg_quote($word, '/')).")({$delim})/ie", "'\\1'.str_repeat('#', strlen('\\2')).'\\3'", $value); + } + } + + return trim($value); + } + +} \ No newline at end of file diff --git a/system/url.php b/system/url.php new file mode 100644 index 000000000..46fdc0308 --- /dev/null +++ b/system/url.php @@ -0,0 +1,132 @@ +view = $view; + $this->data = $data; + + // ----------------------------------------------------- + // Get the contents of the view from the file system. + // ----------------------------------------------------- + $this->content = $this->load($view); + } + + /** + * Create a new view instance. + * + * @param string $view + * @param array $data + * @return View + */ + public static function make($view, $data = array()) + { + return new self($view, $data); + } + + /** + * Load the content of a view. + * + * @param string $view + * @return string + */ + private function load($view) + { + // ----------------------------------------------------- + // Is the view in the application directory? + // ----------------------------------------------------- + if (file_exists($path = APP_PATH.'views/'.$view.EXT)) + { + return file_get_contents($path); + } + // ----------------------------------------------------- + // Is the view in the system directory? + // ----------------------------------------------------- + elseif (file_exists($path = SYS_PATH.'views/'.$view.EXT)) + { + return file_get_contents($path); + } + // ----------------------------------------------------- + // Could not locate the view... bail out. + // ----------------------------------------------------- + else + { + throw new \Exception("View [$view] doesn't exist."); + } + } + + /** + * Add a key / value pair to the view data. + * + * @param string $key + * @param mixed $value + * @return View + */ + public function bind($key, $value) + { + $this->data[$key] = $value; + return $this; + } + + /** + * Get the parsed content of the view. + * + * @return string + */ + public function get() + { + // ----------------------------------------------------- + // Set the name of the last rendered view. + // ----------------------------------------------------- + static::$last = $this->view; + + // ----------------------------------------------------- + // Get the content of all of the sub-views. + // ----------------------------------------------------- + foreach ($this->data as &$data) + { + if ($data instanceof View or $data instanceof Response) + { + $data = (string) $data; + } + } + + // ----------------------------------------------------- + // Extract the view data into the local scope. + // ----------------------------------------------------- + extract($this->data, EXTR_SKIP); + + // ----------------------------------------------------- + // Start an output buffer to catch the content. + // ----------------------------------------------------- + ob_start(); + + // ----------------------------------------------------- + // Echo the content of the view. + // ----------------------------------------------------- + echo eval('?>'.$this->content); + + // ----------------------------------------------------- + // Get the contents of the output buffer. + // ----------------------------------------------------- + return ob_get_clean(); + } + + /** + * Magic Method for getting items from the view data. + */ + public function __get($key) + { + return $this->data[$key]; + } + + /** + * Magic Method for setting items in the view data. + */ + public function __set($key, $value) + { + $this->bind($key, $value); + } + + /** + * Magic Method for determining if an item is in the view data. + */ + public function __isset($key) + { + return array_key_exists($key, $this->data); + } + + /** + * Magic Method for removing an item from the view data. + */ + public function __unset($key) + { + unset($this->data[$key]); + } + + /** + * Get the parsed content of the View. + */ + public function __toString() + { + return $this->get(); + } + +} \ No newline at end of file diff --git a/system/views/error/exception.php b/system/views/error/exception.php new file mode 100644 index 000000000..f06f5f81b --- /dev/null +++ b/system/views/error/exception.php @@ -0,0 +1,78 @@ + + + + + Laravel - Error + + + + + + +
    +

    + +
    +

    Message:

    + in on line . +
    + +
    +

    Stack Trace:

    + +
    +
    + +
    +

    Context:

    + + 0) { ?> + + $context) { ?> +
    + + + + Context unavailable. + +
    +
    + + \ No newline at end of file