libkohana3.1-php-3.1.5/0000755000000000000000000000000011771133710013156 5ustar rootrootlibkohana3.1-php-3.1.5/LICENSE.md0000644000000000000000000000325611771113720014567 0ustar rootroot# Kohana License Agreement This license is a legal agreement between you and the Kohana Team for the use of Kohana Framework (the "Software"). By obtaining the Software you agree to comply with the terms and conditions of this license. Copyright (c) 2007-2010 Kohana Team All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the Kohana nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. libkohana3.1-php-3.1.5/README.md0000644000000000000000000000017511771117072014443 0ustar rootroot# Kohana PHP Framework, version 3.1 (release) This is the current release version of [Kohana](http://kohanaframework.org/). libkohana3.1-php-3.1.5/application/0000755000000000000000000000000011771133710015461 5ustar rootrootlibkohana3.1-php-3.1.5/application/bootstrap.php0000644000000000000000000000703411772106714020220 0ustar rootroot" */ if (isset($_SERVER['KOHANA_ENV'])) { Kohana::$environment = constant('Kohana::'.strtoupper($_SERVER['KOHANA_ENV'])); } /** * Initialize Kohana, setting the default options. * * The following options are available: * * - string base_url path, and optionally domain, of your application NULL * - string index_file name of your index file, usually "index.php" index.php * - string charset internal character set used for input and output utf-8 * - string cache_dir set the internal cache directory APPPATH/cache * - boolean errors enable or disable error handling TRUE * - boolean profile enable or disable internal profiling TRUE * - boolean caching enable or disable internal caching FALSE */ Kohana::init(array( 'base_url' => '/', )); /** * Attach the file write to logging. Multiple writers are supported. */ Kohana::$log->attach(new Log_File(APPPATH.'logs')); /** * Attach a file reader to config. Multiple readers are supported. */ Kohana::$config->attach(new Config_File); /** * Enable modules. Modules are referenced by a relative or absolute path. */ Kohana::modules(array( // 'auth' => MODPATH.'auth', // Basic authentication // 'cache' => MODPATH.'cache', // Caching with multiple backends // 'codebench' => MODPATH.'codebench', // Benchmarking tool // 'database' => MODPATH.'database', // Database access // 'image' => MODPATH.'image', // Image manipulation // 'orm' => MODPATH.'orm', // Object Relationship Mapping // 'unittest' => MODPATH.'unittest', // Unit testing // 'userguide' => MODPATH.'userguide', // User guide and API documentation )); /** * Set the routes. Each route must have a minimum of a name, a URI and a set of * defaults for the URI. */ Route::set('default', '((/(/)))') ->defaults(array( 'controller' => 'welcome', 'action' => 'index', )); libkohana3.1-php-3.1.5/application/cache/0000755000000000000000000000000011771133710016524 5ustar rootrootlibkohana3.1-php-3.1.5/application/classes/0000755000000000000000000000000011771133710017116 5ustar rootrootlibkohana3.1-php-3.1.5/application/classes/controller/0000755000000000000000000000000011771133710021301 5ustar rootrootlibkohana3.1-php-3.1.5/application/classes/controller/welcome.php0000644000000000000000000000031711772106714023453 0ustar rootrootresponse->body('hello, world!'); } } // End Welcome libkohana3.1-php-3.1.5/application/classes/model/0000755000000000000000000000000011771133710020216 5ustar rootrootlibkohana3.1-php-3.1.5/application/config/0000755000000000000000000000000011771133710016726 5ustar rootrootlibkohana3.1-php-3.1.5/application/i18n/0000755000000000000000000000000011771133710016240 5ustar rootrootlibkohana3.1-php-3.1.5/application/logs/0000755000000000000000000000000011771133710016425 5ustar rootrootlibkohana3.1-php-3.1.5/application/messages/0000755000000000000000000000000011771133710017270 5ustar rootrootlibkohana3.1-php-3.1.5/application/views/0000755000000000000000000000000011771133710016616 5ustar rootrootlibkohana3.1-php-3.1.5/example.htaccess0000644000000000000000000000100511771113720016323 0ustar rootroot# Turn on URL rewriting RewriteEngine On # Installation directory RewriteBase / # Protect hidden files from being viewed Order Deny,Allow Deny From All # Protect application and system files from being viewed RewriteRule ^(?:application|modules|system)\b.* index.php/$0 [L] # Allow any files or directories that exist to be displayed directly RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d # Rewrite all other URLs to index.php/URL RewriteRule .* index.php/$0 [PT] libkohana3.1-php-3.1.5/index.php0000644000000000000000000000633411771113720015003 0ustar rootroot= 5.3, it is recommended to disable * deprecated notices. Disable with: E_ALL & ~E_DEPRECATED */ error_reporting(E_ALL | E_STRICT); /** * End of standard configuration! Changing any of the code below should only be * attempted by those with a working knowledge of Kohana internals. * * @see http://kohanaframework.org/guide/using.configuration */ // Set the full path to the docroot define('DOCROOT', realpath(dirname(__FILE__)).DIRECTORY_SEPARATOR); // Make the application relative to the docroot, for symlink'd index.php if ( ! is_dir($application) AND is_dir(DOCROOT.$application)) $application = DOCROOT.$application; // Make the modules relative to the docroot, for symlink'd index.php if ( ! is_dir($modules) AND is_dir(DOCROOT.$modules)) $modules = DOCROOT.$modules; // Make the system relative to the docroot, for symlink'd index.php if ( ! is_dir($system) AND is_dir(DOCROOT.$system)) $system = DOCROOT.$system; // Define the absolute paths for configured directories define('APPPATH', realpath($application).DIRECTORY_SEPARATOR); define('MODPATH', realpath($modules).DIRECTORY_SEPARATOR); define('SYSPATH', realpath($system).DIRECTORY_SEPARATOR); // Clean up the configuration vars unset($application, $modules, $system); if (file_exists('install'.EXT)) { // Load the installation check return include 'install'.EXT; } /** * Define the start time of the application, used for profiling. */ if ( ! defined('KOHANA_START_TIME')) { define('KOHANA_START_TIME', microtime(TRUE)); } /** * Define the memory usage at the start of the application, used for profiling. */ if ( ! defined('KOHANA_START_MEMORY')) { define('KOHANA_START_MEMORY', memory_get_usage()); } // Bootstrap the application require APPPATH.'bootstrap'.EXT; /** * Execute the main request. A source of the URI can be passed, eg: $_SERVER['PATH_INFO']. * If no source is specified, the URI will be automatically detected. */ echo Request::factory() ->execute() ->send_headers() ->body(); libkohana3.1-php-3.1.5/install.php0000644000000000000000000002033311772106714015343 0ustar rootroot Kohana Installation

Environment Tests

The following tests have been run to determine if Kohana will work in your environment. If any of the tests have failed, consult the documentation for more information on how to correct the problem.

=')): ?>
PHP Version Kohana requires PHP 5.2.3 or newer, this version is .
System Directory The configured system directory does not exist or does not contain required files.
Application Directory The configured application directory does not exist or does not contain required files.
Cache Directory The directory is not writable.
Logs Directory The directory is not writable.
PCRE UTF-8 PCRE has not been compiled with UTF-8 support. PCRE has not been compiled with Unicode property support. Pass
SPL Enabled Pass PHP SPL is either not loaded or not compiled in.
Reflection Enabled Pass PHP reflection is either not loaded or not compiled in.
Filters Enabled Pass The filter extension is either not loaded or not compiled in.
Iconv Extension Loaded Pass The iconv extension is not loaded.
Mbstring Not Overloaded The mbstring extension is overloading PHP's native string functions. Pass
Character Type (CTYPE) Extension The ctype extension is not enabled. Pass
URI Determination Pass Neither $_SERVER['REQUEST_URI'], $_SERVER['PHP_SELF'], or $_SERVER['PATH_INFO'] is available.

✘ Kohana may not work correctly with your environment.

✔ Your environment passed all requirements.
Remove or rename the install file now.

Optional Tests

The following extensions are not required to run the Kohana core, but if enabled can provide access to additional classes.

PECL HTTP Enabled Pass Kohana can use the http extension for the Request_Client_External class.
cURL Enabled Pass Kohana can use the cURL extension for the Request_Client_External class.
mcrypt Enabled Pass Kohana requires mcrypt for the Encrypt class.
GD Enabled Pass Kohana requires GD v2 for the Image class.
MySQL Enabled Pass Kohana can use the MySQL extension to support MySQL databases.
PDO Enabled Pass Kohana can use PDO to support additional databases.
libkohana3.1-php-3.1.5/modules/0000755000000000000000000000000011771133710014626 5ustar rootrootlibkohana3.1-php-3.1.5/modules/auth/0000755000000000000000000000000011771133710015567 5ustar rootrootlibkohana3.1-php-3.1.5/modules/auth/README.md0000644000000000000000000000174311771113722017054 0ustar rootrootNew Age Auth --- I've forked the main Auth module because there were some fundamental flaws with it: 1. It's trivial to [bruteforce](http://dev.kohanaframework.org/issues/3163) publicly hidden salt hashes. - I've fixed this by switching the password hashing algorithm to the more secure secret-key based hash_hmac method. 2. ORM drivers were included. - I've fixed this by simply removing them. They cause confusion with new users because they think that Auth requires ORM. The only driver currently provided by default is the file driver. 3. Auth::get_user()'s api is inconsistent because it returns different data types. - I've fixed this by returning an empty user model by default. You can override what gets returned (if you've changed your user model class name for instance) by overloading the get_user() method in your application. These changes should be merged into the mainline branch eventually, but they completely break the API, so likely won't be done until 3.1.libkohana3.1-php-3.1.5/modules/auth/classes/0000755000000000000000000000000011771133710017224 5ustar rootrootlibkohana3.1-php-3.1.5/modules/auth/classes/auth/0000755000000000000000000000000011771133710020165 5ustar rootrootlibkohana3.1-php-3.1.5/modules/auth/classes/auth/file.php0000644000000000000000000000015311772106744021624 0ustar rootroot_users = Arr::get($config, 'users', array()); } /** * Logs a user in. * * @param string $username Username * @param string $password Password * @param boolean $remember Enable autologin (not supported) * @return boolean */ protected function _login($username, $password, $remember) { if (isset($this->_users[$username]) AND $this->_users[$username] === $password) { // Complete the login return $this->complete_login($username); } // Login failed return FALSE; } /** * Forces a user to be logged in, without specifying a password. * * @param mixed $username Username * @return boolean */ public function force_login($username) { // Complete the login return $this->complete_login($username); } /** * Get the stored password for a username. * * @param mixed $username Username * @return string */ public function password($username) { return Arr::get($this->_users, $username, FALSE); } /** * Compare password with original (plain text). Works for current (logged in) user * * @param string $password Password * @return boolean */ public function check_password($password) { $username = $this->get_user(); if ($username === FALSE) { return FALSE; } return ($password === $this->password($username)); } } // End Auth File libkohana3.1-php-3.1.5/modules/auth/classes/kohana/auth.php0000644000000000000000000000770411772106744022157 0ustar rootrootget('driver')) { $type = 'file'; } // Set the session class name $class = 'Auth_'.ucfirst($type); // Create a new session instance Auth::$_instance = new $class($config); } return Auth::$_instance; } protected $_session; protected $_config; /** * Loads Session and configuration options. * * @param array $config Config Options * @return void */ public function __construct($config = array()) { // Save the config in the object $this->_config = $config; $this->_session = Session::instance(); } abstract protected function _login($username, $password, $remember); abstract public function password($username); abstract public function check_password($password); /** * Gets the currently logged in user from the session. * Returns NULL if no user is currently logged in. * * @param mixed $default Default value to return if the user is currently not logged in. * @return mixed */ public function get_user($default = NULL) { return $this->_session->get($this->_config['session_key'], $default); } /** * Attempt to log in a user by using an ORM object and plain-text password. * * @param string $username Username to log in * @param string $password Password to check against * @param boolean $remember Enable autologin * @return boolean */ public function login($username, $password, $remember = FALSE) { if (empty($password)) return FALSE; if (is_string($password)) { // Create a hashed password $password = $this->hash($password); } return $this->_login($username, $password, $remember); } /** * Log out a user by removing the related session variables. * * @param boolean $destroy Completely destroy the session * @param boolean $logout_all Remove all tokens for user * @return boolean */ public function logout($destroy = FALSE, $logout_all = FALSE) { if ($destroy === TRUE) { // Destroy the session completely $this->_session->destroy(); } else { // Remove the user from the session $this->_session->delete($this->_config['session_key']); // Regenerate session_id $this->_session->regenerate(); } // Double check return ! $this->logged_in(); } /** * Check if there is an active session. Optionally allows checking for a * specific role. * * @param string $role role name * @return mixed */ public function logged_in($role = NULL) { return ($this->get_user() !== NULL); } /** * Creates a hashed hmac password from a plaintext password. This * method is deprecated, [Auth::hash] should be used instead. * * @deprecated * @param string $password Plaintext password */ public function hash_password($password) { return $this->hash($password); } /** * Perform a hmac hash, using the configured method. * * @param string string to hash * @return string */ public function hash($str) { if ( ! $this->_config['hash_key']) throw new Kohana_Exception('A valid hash key must be set in your auth config.'); return hash_hmac($this->_config['hash_method'], $str, $this->_config['hash_key']); } protected function complete_login($user) { // Regenerate session_id $this->_session->regenerate(); // Store username in session $this->_session->set($this->_config['session_key'], $user); return TRUE; } } // End Auth libkohana3.1-php-3.1.5/modules/auth/config/0000755000000000000000000000000011771133710017034 5ustar rootrootlibkohana3.1-php-3.1.5/modules/auth/config/auth.php0000644000000000000000000000057111772106744020521 0ustar rootroot 'file', 'hash_method' => 'sha256', 'hash_key' => NULL, 'lifetime' => 1209600, 'session_key' => 'auth_user', // Username/password combinations for the Auth File driver 'users' => array( // 'admin' => 'b3154acf3a344170077d11bdb5fff31532f679a1919e716a02', ), ); libkohana3.1-php-3.1.5/modules/auth/guide/0000755000000000000000000000000011771133710016664 5ustar rootrootlibkohana3.1-php-3.1.5/modules/auth/guide/auth/0000755000000000000000000000000011771133710017625 5ustar rootrootlibkohana3.1-php-3.1.5/modules/auth/guide/auth/config.md0000644000000000000000000000000011771113722021403 0ustar rootrootlibkohana3.1-php-3.1.5/modules/auth/guide/auth/edit.md0000644000000000000000000000000011771113722021063 0ustar rootrootlibkohana3.1-php-3.1.5/modules/auth/guide/auth/index.md0000644000000000000000000000000011771113722021245 0ustar rootrootlibkohana3.1-php-3.1.5/modules/auth/guide/auth/login.md0000644000000000000000000000000011771113722021246 0ustar rootrootlibkohana3.1-php-3.1.5/modules/auth/guide/auth/menu.md0000644000000000000000000000022611771113722021114 0ustar rootroot## [Auth]() - [Config](config) - [User Model](user) - [Register Users](register) - [Log in and out](login) - [Edit User](edit) - [Using Roles](roles) libkohana3.1-php-3.1.5/modules/auth/guide/auth/register.md0000644000000000000000000000000011771113722021762 0ustar rootrootlibkohana3.1-php-3.1.5/modules/auth/guide/auth/roles.md0000644000000000000000000000000011771113722021262 0ustar rootrootlibkohana3.1-php-3.1.5/modules/auth/guide/auth/user.md0000644000000000000000000000000011771113722021114 0ustar rootrootlibkohana3.1-php-3.1.5/modules/cache/0000755000000000000000000000000011771133710015671 5ustar rootrootlibkohana3.1-php-3.1.5/modules/cache/README.md0000644000000000000000000000426711771117076017170 0ustar rootrootKohana Cache library ==================== The cache library for Kohana 3 provides a simple interface to the most common cache solutions. Developers are free to add their own caching solutions that follow the cache design pattern defined within this module. Supported cache solutions ------------------------- Currently this module supports the following cache methods. 1. APC 2. eAccelerator 3. Memcache 4. Memcached-tags (Supports tags) 5. SQLite (Supports tags) 6. File 7. Xcache 8. Wincache Planned support --------------- In the near future, additional support for the following methods will be included. 1. Memcached Introduction to caching ----------------------- To use caching to the maximum potential, your application should be designed with caching in mind from the outset. In general, the most effective caches contain lots of small collections of data that are the result of expensive computational operations, such as searching through a large data set. There are many different caching methods available for PHP, from the very basic file based caching to opcode caching in eAccelerator and APC. Caching engines that use physical memory over disk based storage are always faster, however many do not support more advanced features such as tagging. Using Cache ----------- To use Kohana Cache, download and extract the latest stable release of Kohana Cache from [Github](http://github.com/samsoir/kohana-cache). Place the module into your Kohana instances modules folder. Finally enable the module within the application bootstrap within the section entitled _modules_. Quick example ------------- The following is a quick example of how to use Kohana Cache. The example is using the SQLite driver. 'bar', 'apples' => 'pear', 'BDFL' => 'Shadowhand'); // Save the data to cache, with an id of test_id and a lifetime of 10 minutes $mycache->set('test_id', $data, 600); // Retrieve the data from cache $retrieved_data = $mycache->get('test_id'); // Remove the cache item $mycache->delete('test_id'); // Clear the cache of all stored items $mycache->delete_all(); libkohana3.1-php-3.1.5/modules/cache/classes/0000755000000000000000000000000011771133710017326 5ustar rootrootlibkohana3.1-php-3.1.5/modules/cache/classes/cache/0000755000000000000000000000000011771133710020371 5ustar rootrootlibkohana3.1-php-3.1.5/modules/cache/classes/cache/apc.php0000644000000000000000000000015111772106744021652 0ustar rootroot array( // Driver group * 'driver' => 'apc', // using APC driver * ), * ) * * In cases where only one cache group is required, if the group is named `default` there is * no need to pass the group name when instantiating a cache instance. * * #### General cache group configuration settings * * Below are the settings available to all types of cache driver. * * Name | Required | Description * -------------- | -------- | --------------------------------------------------------------- * driver | __YES__ | (_string_) The driver type to use * * ### System requirements * * * Kohana 3.0.x * * PHP 5.2.4 or greater * * APC PHP extension * * @package Kohana/Cache * @category Base * @author Kohana Team * @copyright (c) 2009-2012 Kohana Team * @license http://kohanaphp.com/license */ class Kohana_Cache_Apc extends Cache { /** * Check for existence of the APC extension This method cannot be invoked externally. The driver must * be instantiated using the `Cache::instance()` method. * * @param array $config configuration * @throws Kohana_Cache_Exception */ protected function __construct(array $config) { if ( ! extension_loaded('apc')) { throw new Kohana_Cache_Exception('PHP APC extension is not available.'); } parent::__construct($config); } /** * Retrieve a cached value entry by id. * * // Retrieve cache entry from apc group * $data = Cache::instance('apc')->get('foo'); * * // Retrieve cache entry from apc group and return 'bar' if miss * $data = Cache::instance('apc')->get('foo', 'bar'); * * @param string $id id of cache to entry * @param string $default default value to return if cache miss * @return mixed * @throws Kohana_Cache_Exception */ public function get($id, $default = NULL) { $data = apc_fetch($this->_sanitize_id($id), $success); return $success ? $data : $default; } /** * Set a value to cache with id and lifetime * * $data = 'bar'; * * // Set 'bar' to 'foo' in apc group, using default expiry * Cache::instance('apc')->set('foo', $data); * * // Set 'bar' to 'foo' in apc group for 30 seconds * Cache::instance('apc')->set('foo', $data, 30); * * @param string $id id of cache entry * @param string $data data to set to cache * @param integer $lifetime lifetime in seconds * @return boolean */ public function set($id, $data, $lifetime = NULL) { if ($lifetime === NULL) { $lifetime = Arr::get($this->_config, 'default_expire', Cache::DEFAULT_EXPIRE); } return apc_store($this->_sanitize_id($id), $data, $lifetime); } /** * Delete a cache entry based on id * * // Delete 'foo' entry from the apc group * Cache::instance('apc')->delete('foo'); * * @param string $id id to remove from cache * @return boolean */ public function delete($id) { return apc_delete($this->_sanitize_id($id)); } /** * Delete all cache entries. * * Beware of using this method when * using shared memory cache systems, as it will wipe every * entry within the system for all clients. * * // Delete all cache entries in the apc group * Cache::instance('apc')->delete_all(); * * @return boolean */ public function delete_all() { return apc_clear_cache('user'); } } libkohana3.1-php-3.1.5/modules/cache/classes/kohana/cache/arithmetic.php0000644000000000000000000000213311771131762024500 0ustar rootroot array( // Driver group * 'driver' => 'eaccelerator', // using Eaccelerator driver * ), * ) * * In cases where only one cache group is required, if the group is named `default` there is * no need to pass the group name when instantiating a cache instance. * * #### General cache group configuration settings * * Below are the settings available to all types of cache driver. * * Name | Required | Description * -------------- | -------- | --------------------------------------------------------------- * driver | __YES__ | (_string_) The driver type to use * * ### System requirements * * * Kohana 3.0.x * * PHP 5.2.4 or greater * * Eaccelerator PHP extension * * @package Kohana/Cache * @category Base * @author Kohana Team * @copyright (c) 2009-2012 Kohana Team * @license http://kohanaphp.com/license */ class Kohana_Cache_Eaccelerator extends Cache { /** * Check for existence of the eAccelerator extension This method cannot be invoked externally. The driver must * be instantiated using the `Cache::instance()` method. * * @param array $config configuration * @throws Kohana_Cache_Exception */ protected function __construct(array $config) { if ( ! extension_loaded('eaccelerator')) { throw new Kohana_Cache_Exception('PHP eAccelerator extension is not available.'); } parent::__construct($config); } /** * Retrieve a cached value entry by id. * * // Retrieve cache entry from eaccelerator group * $data = Cache::instance('eaccelerator')->get('foo'); * * // Retrieve cache entry from eaccelerator group and return 'bar' if miss * $data = Cache::instance('eaccelerator')->get('foo', 'bar'); * * @param string $id id of cache to entry * @param string $default default value to return if cache miss * @return mixed * @throws Kohana_Cache_Exception */ public function get($id, $default = NULL) { return (($data = eaccelerator_get($this->_sanitize_id($id))) === FALSE) ? $default : $data; } /** * Set a value to cache with id and lifetime * * $data = 'bar'; * * // Set 'bar' to 'foo' in eaccelerator group, using default expiry * Cache::instance('eaccelerator')->set('foo', $data); * * // Set 'bar' to 'foo' in eaccelerator group for 30 seconds * Cache::instance('eaccelerator')->set('foo', $data, 30); * * @param string $id id of cache entry * @param string $data data to set to cache * @param integer $lifetime lifetime in seconds * @return boolean */ public function set($id, $data, $lifetime = NULL) { if ($lifetime === NULL) { $lifetime = time() + Arr::get($this->_config, 'default_expire', Cache::DEFAULT_EXPIRE); } return eaccelerator_put($this->_sanitize_id($id), $data, $lifetime); } /** * Delete a cache entry based on id * * // Delete 'foo' entry from the eaccelerator group * Cache::instance('eaccelerator')->delete('foo'); * * @param string $id id to remove from cache * @return boolean */ public function delete($id) { return eaccelerator_rm($this->_sanitize_id($id)); } /** * Delete all cache entries. * * Beware of using this method when * using shared memory cache systems, as it will wipe every * entry within the system for all clients. * * // Delete all cache entries in the eaccelerator group * Cache::instance('eaccelerator')->delete_all(); * * @return boolean */ public function delete_all() { return eaccelerator_clean(); } } libkohana3.1-php-3.1.5/modules/cache/classes/kohana/cache/exception.php0000644000000000000000000000047411772106744024356 0ustar rootroot array( // File driver group * 'driver' => 'file', // using File driver * 'cache_dir' => APPPATH.'cache/.kohana_cache', // Cache location * ), * ) * * In cases where only one cache group is required, if the group is named `default` there is * no need to pass the group name when instantiating a cache instance. * * #### General cache group configuration settings * * Below are the settings available to all types of cache driver. * * Name | Required | Description * -------------- | -------- | --------------------------------------------------------------- * driver | __YES__ | (_string_) The driver type to use * cache_dir | __NO__ | (_string_) The cache directory to use for this cache instance * * ### System requirements * * * Kohana 3.0.x * * PHP 5.2.4 or greater * * @package Kohana/Cache * @category Base * @author Kohana Team * @copyright (c) 2009-2012 Kohana Team * @license http://kohanaphp.com/license */ class Kohana_Cache_File extends Cache implements Kohana_Cache_GarbageCollect { /** * Creates a hashed filename based on the string. This is used * to create shorter unique IDs for each cache filename. * * // Create the cache filename * $filename = Cache_File::filename($this->_sanitize_id($id)); * * @param string $string string to hash into filename * @return string */ protected static function filename($string) { return sha1($string).'.json'; } /** * @var string the caching directory */ protected $_cache_dir; /** * Constructs the file cache driver. This method cannot be invoked externally. The file cache driver must * be instantiated using the `Cache::instance()` method. * * @param array $config config * @throws Kohana_Cache_Exception */ protected function __construct(array $config) { // Setup parent parent::__construct($config); try { $directory = Arr::get($this->_config, 'cache_dir', Kohana::$cache_dir); $this->_cache_dir = new SplFileInfo($directory); } // PHP < 5.3 exception handle catch (ErrorException $e) { $this->_cache_dir = $this->_make_directory($directory, 0777, TRUE); } // PHP >= 5.3 exception handle catch (UnexpectedValueException $e) { $this->_cache_dir = $this->_make_directory($directory, 0777, TRUE); } // If the defined directory is a file, get outta here if ($this->_cache_dir->isFile()) { throw new Kohana_Cache_Exception('Unable to create cache directory as a file already exists : :resource', array(':resource' => $this->_cache_dir->getRealPath())); } // Check the read status of the directory if ( ! $this->_cache_dir->isReadable()) { throw new Kohana_Cache_Exception('Unable to read from the cache directory :resource', array(':resource' => $this->_cache_dir->getRealPath())); } // Check the write status of the directory if ( ! $this->_cache_dir->isWritable()) { throw new Kohana_Cache_Exception('Unable to write to the cache directory :resource', array(':resource' => $this->_cache_dir->getRealPath())); } } /** * Retrieve a cached value entry by id. * * // Retrieve cache entry from file group * $data = Cache::instance('file')->get('foo'); * * // Retrieve cache entry from file group and return 'bar' if miss * $data = Cache::instance('file')->get('foo', 'bar'); * * @param string $id id of cache to entry * @param string $default default value to return if cache miss * @return mixed * @throws Kohana_Cache_Exception */ public function get($id, $default = NULL) { $filename = Cache_File::filename($this->_sanitize_id($id)); $directory = $this->_resolve_directory($filename); // Wrap operations in try/catch to handle notices try { // Open file $file = new SplFileInfo($directory.$filename); // If file does not exist if ( ! $file->isFile()) { // Return default value return $default; } else { // Open the file and extract the json $json = $file->openFile()->current(); // Decode the json into PHP object $data = json_decode($json); // Test the expiry if ($data->expiry < time()) { // Delete the file $this->_delete_file($file, NULL, TRUE); // Return default value return $default; } else { return ($data->type === 'string') ? $data->payload : unserialize($data->payload); } } } catch (ErrorException $e) { // Handle ErrorException caused by failed unserialization if ($e->getCode() === E_NOTICE) { throw new Kohana_Cache_Exception(__METHOD__.' failed to unserialize cached object with message : '.$e->getMessage()); } // Otherwise throw the exception throw $e; } } /** * Set a value to cache with id and lifetime * * $data = 'bar'; * * // Set 'bar' to 'foo' in file group, using default expiry * Cache::instance('file')->set('foo', $data); * * // Set 'bar' to 'foo' in file group for 30 seconds * Cache::instance('file')->set('foo', $data, 30); * * @param string $id id of cache entry * @param string $data data to set to cache * @param integer $lifetime lifetime in seconds * @return boolean */ public function set($id, $data, $lifetime = NULL) { $filename = Cache_File::filename($this->_sanitize_id($id)); $directory = $this->_resolve_directory($filename); // If lifetime is NULL if ($lifetime === NULL) { // Set to the default expiry $lifetime = Arr::get($this->_config, 'default_expire', Cache::DEFAULT_EXPIRE); } // Open directory $dir = new SplFileInfo($directory); // If the directory path is not a directory if ( ! $dir->isDir()) { // Create the directory if ( ! mkdir($directory, 0777, TRUE)) { throw new Kohana_Cache_Exception(__METHOD__.' unable to create directory : :directory', array(':directory' => $directory)); } // chmod to solve potential umask issues chmod($directory, 0777); } // Open file to inspect $resouce = new SplFileInfo($directory.$filename); $file = $resouce->openFile('w'); try { $type = gettype($data); // Serialize the data $data = json_encode( (object) array( 'payload' => ($type === 'string') ? $data : serialize($data), 'expiry' => time() + $lifetime, 'type' => $type )); $size = strlen($data); } catch (ErrorException $e) { // If serialize through an error exception if ($e->getCode() === E_NOTICE) { // Throw a caching error throw new Kohana_Cache_Exception(__METHOD__.' failed to serialize data for caching with message : '.$e->getMessage()); } // Else rethrow the error exception throw $e; } try { $file->fwrite($data, $size); return (bool) $file->fflush(); } catch (Exception $e) { throw $e; } } /** * Delete a cache entry based on id * * // Delete 'foo' entry from the file group * Cache::instance('file')->delete('foo'); * * @param string $id id to remove from cache * @return boolean */ public function delete($id) { $filename = Cache_File::filename($this->_sanitize_id($id)); $directory = $this->_resolve_directory($filename); return $this->_delete_file(new SplFileInfo($directory.$filename), NULL, TRUE); } /** * Delete all cache entries. * * Beware of using this method when * using shared memory cache systems, as it will wipe every * entry within the system for all clients. * * // Delete all cache entries in the file group * Cache::instance('file')->delete_all(); * * @return boolean */ public function delete_all() { return $this->_delete_file($this->_cache_dir, TRUE); } /** * Garbage collection method that cleans any expired * cache entries from the cache. * * @return void */ public function garbage_collect() { $this->_delete_file($this->_cache_dir, TRUE, FALSE, TRUE); return; } /** * Deletes files recursively and returns FALSE on any errors * * // Delete a file or folder whilst retaining parent directory and ignore all errors * $this->_delete_file($folder, TRUE, TRUE); * * @param SplFileInfo $file file * @param boolean $retain_parent_directory retain the parent directory * @param boolean $ignore_errors ignore_errors to prevent all exceptions interrupting exec * @param boolean $only_expired only expired files * @return boolean * @throws Kohana_Cache_Exception */ protected function _delete_file(SplFileInfo $file, $retain_parent_directory = FALSE, $ignore_errors = FALSE, $only_expired = FALSE) { // Allow graceful error handling try { // If is file if ($file->isFile()) { try { // If only expired is not set if ($only_expired === FALSE) { // We want to delete the file $delete = TRUE; } // Otherwise... else { // Assess the file expiry to flag it for deletion $json = $file->openFile('r')->current(); $data = json_decode($json); $delete = $data->expiry < time(); } // If the delete flag is set if ($delete === TRUE) { // Try to delete unlink($file->getRealPath()); } } catch (ErrorException $e) { // Catch any delete file warnings if ($e->getCode() === E_WARNING) { throw new Kohana_Cache_Exception(__METHOD__.' failed to delete file : :file', array(':file' => $file->getRealPath())); } } } // Else, is directory elseif ($file->isDir()) { // Create new DirectoryIterator $files = new DirectoryIterator($file->getPathname()); // Iterate over each entry while ($files->valid()) { // Extract the entry name $name = $files->getFilename(); // If the name is not a dot if ($name != '.' AND $name != '..' AND substr($file->getFilename(), 0, 1) == '.') { // Create new file resource $fp = new SplFileInfo($files->getRealPath()); // Delete the file $this->_delete_file($fp); } // Move the file pointer on $files->next(); } // If set to retain parent directory, return now if ($retain_parent_directory) { return TRUE; } try { // Remove the files iterator // (fixes Windows PHP which has permission issues with open iterators) unset($files); // Try to remove the parent directory return rmdir($file->getRealPath()); } catch (ErrorException $e) { // Catch any delete directory warnings if ($e->getCode() === E_WARNING) { throw new Kohana_Cache_Exception(__METHOD__.' failed to delete directory : :directory', array(':directory' => $file->getRealPath())); } } } } // Catch all exceptions catch (Exception $e) { // If ignore_errors is on if ($ignore_errors === TRUE) { // Return return FALSE; } // Throw exception throw $e; } } /** * Resolves the cache directory real path from the filename * * // Get the realpath of the cache folder * $realpath = $this->_resolve_directory($filename); * * @param string $filename filename to resolve * @return string */ protected function _resolve_directory($filename) { return $this->_cache_dir->getRealPath().DIRECTORY_SEPARATOR.$filename[0].$filename[1].DIRECTORY_SEPARATOR; } /** * Makes the cache directory if it doesn't exist. Simply a wrapper for * `mkdir` to ensure DRY principles * * @see http://php.net/manual/en/function.mkdir.php * @param string $directory * @param integer $mode * @param boolean $recursive * @param resource $context * @return SplFileInfo * @throws Kohana_Cache_Exception */ protected function _make_directory($directory, $mode = 0777, $recursive = FALSE, $context = NULL) { if ( ! mkdir($directory, $mode, $recursive, $context)) { throw new Kohana_Cache_Exception('Failed to create the defined cache directory : :directory', array(':directory' => $directory)); } chmod($directory, $mode); return new SplFileInfo($directory); } } libkohana3.1-php-3.1.5/modules/cache/classes/kohana/cache/garbagecollect.php0000644000000000000000000000122511772106744025311 0ustar rootroot array( // Default group * 'driver' => 'memcache', // using Memcache driver * 'servers' => array( // Available server definitions * // First memcache server server * array( * 'host' => 'localhost', * 'port' => 11211, * 'persistent' => FALSE * 'weight' => 1, * 'timeout' => 1, * 'retry_interval' => 15, * 'status' => TRUE, * 'instant_death' => TRUE, * 'failure_callback' => array('className', 'classMethod') * ), * // Second memcache server * array( * 'host' => '192.168.1.5', * 'port' => 22122, * 'persistent' => TRUE * ) * ), * 'compression' => FALSE, // Use compression? * ), * ) * * In cases where only one cache group is required, if the group is named `default` there is * no need to pass the group name when instantiating a cache instance. * * #### General cache group configuration settings * * Below are the settings available to all types of cache driver. * * Name | Required | Description * -------------- | -------- | --------------------------------------------------------------- * driver | __YES__ | (_string_) The driver type to use * servers | __YES__ | (_array_) Associative array of server details, must include a __host__ key. (see _Memcache server configuration_ below) * compression | __NO__ | (_boolean_) Use data compression when caching * * #### Memcache server configuration * * The following settings should be used when defining each memcache server * * Name | Required | Description * ---------------- | -------- | --------------------------------------------------------------- * host | __YES__ | (_string_) The host of the memcache server, i.e. __localhost__; or __127.0.0.1__; or __memcache.domain.tld__ * port | __NO__ | (_integer_) Point to the port where memcached is listening for connections. Set this parameter to 0 when using UNIX domain sockets. Default __11211__ * persistent | __NO__ | (_boolean_) Controls the use of a persistent connection. Default __TRUE__ * weight | __NO__ | (_integer_) Number of buckets to create for this server which in turn control its probability of it being selected. The probability is relative to the total weight of all servers. Default __1__ * timeout | __NO__ | (_integer_) Value in seconds which will be used for connecting to the daemon. Think twice before changing the default value of 1 second - you can lose all the advantages of caching if your connection is too slow. Default __1__ * retry_interval | __NO__ | (_integer_) Controls how often a failed server will be retried, the default value is 15 seconds. Setting this parameter to -1 disables automatic retry. Default __15__ * status | __NO__ | (_boolean_) Controls if the server should be flagged as online. Default __TRUE__ * failure_callback | __NO__ | (_[callback](http://www.php.net/manual/en/language.pseudo-types.php#language.types.callback)_) Allows the user to specify a callback function to run upon encountering an error. The callback is run before failover is attempted. The function takes two parameters, the hostname and port of the failed server. Default __NULL__ * * ### System requirements * * * Kohana 3.0.x * * PHP 5.2.4 or greater * * Memcache (plus Memcached-tags for native tagging support) * * Zlib * * @package Kohana/Cache * @category Base * @version 2.0 * @author Kohana Team * @copyright (c) 2009-2012 Kohana Team * @license http://kohanaphp.com/license */ class Kohana_Cache_Memcache extends Cache { // Memcache has a maximum cache lifetime of 30 days const CACHE_CEILING = 2592000; /** * Memcache resource * * @var Memcache */ protected $_memcache; /** * Flags to use when storing values * * @var string */ protected $_flags; /** * The default configuration for the memcached server * * @var array */ protected $_default_config = array(); /** * Constructs the memcache Kohana_Cache object * * @param array $config configuration * @throws Kohana_Cache_Exception */ protected function __construct(array $config) { // Check for the memcache extention if ( ! extension_loaded('memcache')) { throw new Kohana_Cache_Exception('Memcache PHP extention not loaded'); } parent::__construct($config); // Setup Memcache $this->_memcache = new Memcache; // Load servers from configuration $servers = Arr::get($this->_config, 'servers', NULL); if ( ! $servers) { // Throw an exception if no server found throw new Kohana_Cache_Exception('No Memcache servers defined in configuration'); } // Setup default server configuration $this->_default_config = array( 'host' => 'localhost', 'port' => 11211, 'persistent' => FALSE, 'weight' => 1, 'timeout' => 1, 'retry_interval' => 15, 'status' => TRUE, 'instant_death' => TRUE, 'failure_callback' => array($this, '_failed_request'), ); // Add the memcache servers to the pool foreach ($servers as $server) { // Merge the defined config with defaults $server += $this->_default_config; if ( ! $this->_memcache->addServer($server['host'], $server['port'], $server['persistent'], $server['weight'], $server['timeout'], $server['retry_interval'], $server['status'], $server['failure_callback'])) { throw new Kohana_Cache_Exception('Memcache could not connect to host \':host\' using port \':port\'', array(':host' => $server['host'], ':port' => $server['port'])); } } // Setup the flags $this->_flags = Arr::get($this->_config, 'compression', FALSE) ? MEMCACHE_COMPRESSED : FALSE; } /** * Retrieve a cached value entry by id. * * // Retrieve cache entry from memcache group * $data = Cache::instance('memcache')->get('foo'); * * // Retrieve cache entry from memcache group and return 'bar' if miss * $data = Cache::instance('memcache')->get('foo', 'bar'); * * @param string $id id of cache to entry * @param string $default default value to return if cache miss * @return mixed * @throws Kohana_Cache_Exception */ public function get($id, $default = NULL) { // Get the value from Memcache $value = $this->_memcache->get($this->_sanitize_id($id)); // If the value wasn't found, normalise it if ($value === FALSE) { $value = (NULL === $default) ? NULL : $default; } // Return the value return $value; } /** * Set a value to cache with id and lifetime * * $data = 'bar'; * * // Set 'bar' to 'foo' in memcache group for 10 minutes * if (Cache::instance('memcache')->set('foo', $data, 600)) * { * // Cache was set successfully * return * } * * @param string $id id of cache entry * @param mixed $data data to set to cache * @param integer $lifetime lifetime in seconds, maximum value 2592000 * @return boolean */ public function set($id, $data, $lifetime = 3600) { // If the lifetime is greater than the ceiling if ($lifetime > Cache_Memcache::CACHE_CEILING) { // Set the lifetime to maximum cache time $lifetime = Cache_Memcache::CACHE_CEILING + time(); } // Else if the lifetime is greater than zero elseif ($lifetime > 0) { $lifetime += time(); } // Else else { // Normalise the lifetime $lifetime = 0; } // Set the data to memcache return $this->_memcache->set($this->_sanitize_id($id), $data, $this->_flags, $lifetime); } /** * Delete a cache entry based on id * * // Delete the 'foo' cache entry immediately * Cache::instance('memcache')->delete('foo'); * * // Delete the 'bar' cache entry after 30 seconds * Cache::instance('memcache')->delete('bar', 30); * * @param string $id id of entry to delete * @param integer $timeout timeout of entry, if zero item is deleted immediately, otherwise the item will delete after the specified value in seconds * @return boolean */ public function delete($id, $timeout = 0) { // Delete the id return $this->_memcache->delete($this->_sanitize_id($id), $timeout); } /** * Delete all cache entries. * * Beware of using this method when * using shared memory cache systems, as it will wipe every * entry within the system for all clients. * * // Delete all cache entries in the default group * Cache::instance('memcache')->delete_all(); * * @return boolean */ public function delete_all() { $result = $this->_memcache->flush(); // We must sleep after flushing, or overwriting will not work! // @see http://php.net/manual/en/function.memcache-flush.php#81420 sleep(1); return $result; } /** * Callback method for Memcache::failure_callback to use if any Memcache call * on a particular server fails. This method switches off that instance of the * server if the configuration setting `instant_death` is set to `TRUE`. * * @param string $hostname * @param integer $port * @return void|boolean * @since 3.0.8 */ public function _failed_request($hostname, $port) { if ( ! $this->_config['instant_death']) return; // Setup non-existent host $host = FALSE; // Get host settings from configuration foreach ($this->_config['servers'] as $server) { // Merge the defaults, since they won't always be set $server += $this->_default_config; // We're looking at the failed server if ($hostname == $server['host'] and $port == $server['port']) { // Server to disable, since it failed $host = $server; continue; } } if ( ! $host) return; else { return $this->_memcache->setServerParams( $host['host'], $host['port'], $host['timeout'], $host['retry_interval'], FALSE, // Server is offline array($this, '_failed_request' )); } } }libkohana3.1-php-3.1.5/modules/cache/classes/kohana/cache/memcachetag.php0000644000000000000000000000340511772106744024613 0ustar rootroot_memcache, 'tag_add')) { throw new Kohana_Cache_Exception('Memcached-tags PHP plugin not present. Please see http://code.google.com/p/memcached-tags/ for more information'); } } /** * Set a value based on an id with tags * * @param string $id id * @param mixed $data data * @param integer $lifetime lifetime [Optional] * @param array $tags tags [Optional] * @return boolean */ public function set_with_tags($id, $data, $lifetime = NULL, array $tags = NULL) { $id = $this->_sanitize_id($id); $result = $this->set($id, $data, $lifetime); if ($result and $tags) { foreach ($tags as $tag) { $this->_memcache->tag_add($tag, $id); } } return $result; } /** * Delete cache entries based on a tag * * @param string $tag tag * @return boolean */ public function delete_tag($tag) { return $this->_memcache->tag_delete($tag); } /** * Find cache entries based on a tag * * @param string $tag tag * @return void * @throws Kohana_Cache_Exception */ public function find($tag) { throw new Kohana_Cache_Exception('Memcached-tags does not support finding by tag'); } } libkohana3.1-php-3.1.5/modules/cache/classes/kohana/cache/sqlite.php0000644000000000000000000002034311772106744023656 0ustar rootroot_config, 'database', NULL); if ($database === NULL) { throw new Kohana_Cache_Exception('Database path not available in Kohana Cache configuration'); } // Load new Sqlite DB $this->_db = new PDO('sqlite:'.$database); // Test for existing DB $result = $this->_db->query("SELECT * FROM sqlite_master WHERE name = 'caches' AND type = 'table'")->fetchAll(); // If there is no table, create a new one if (0 == count($result)) { $database_schema = Arr::get($this->_config, 'schema', NULL); if ($database_schema === NULL) { throw new Kohana_Cache_Exception('Database schema not found in Kohana Cache configuration'); } try { // Create the caches table $this->_db->query(Arr::get($this->_config, 'schema', NULL)); } catch (PDOException $e) { throw new Kohana_Cache_Exception('Failed to create new SQLite caches table with the following error : :error', array(':error' => $e->getMessage())); } } } /** * Retrieve a value based on an id * * @param string $id id * @param string $default default [Optional] Default value to return if id not found * @return mixed * @throws Kohana_Cache_Exception */ public function get($id, $default = NULL) { // Prepare statement $statement = $this->_db->prepare('SELECT id, expiration, cache FROM caches WHERE id = :id LIMIT 0, 1'); // Try and load the cache based on id try { $statement->execute(array(':id' => $this->_sanitize_id($id))); } catch (PDOException $e) { throw new Kohana_Cache_Exception('There was a problem querying the local SQLite3 cache. :error', array(':error' => $e->getMessage())); } if ( ! $result = $statement->fetch(PDO::FETCH_OBJ)) { return $default; } // If the cache has expired if ($result->expiration != 0 and $result->expiration <= time()) { // Delete it and return default value $this->delete($id); return $default; } // Otherwise return cached object else { // Disable notices for unserializing $ER = error_reporting(~E_NOTICE); // Return the valid cache data $data = unserialize($result->cache); // Turn notices back on error_reporting($ER); // Return the resulting data return $data; } } /** * Set a value based on an id. Optionally add tags. * * @param string $id id * @param mixed $data data * @param integer $lifetime lifetime [Optional] * @return boolean */ public function set($id, $data, $lifetime = NULL) { return (bool) $this->set_with_tags($id, $data, $lifetime); } /** * Delete a cache entry based on id * * @param string $id id * @return boolean * @throws Kohana_Cache_Exception */ public function delete($id) { // Prepare statement $statement = $this->_db->prepare('DELETE FROM caches WHERE id = :id'); // Remove the entry try { $statement->execute(array(':id' => $this->_sanitize_id($id))); } catch (PDOException $e) { throw new Kohana_Cache_Exception('There was a problem querying the local SQLite3 cache. :error', array(':error' => $e->getMessage())); } return (bool) $statement->rowCount(); } /** * Delete all cache entries * * @return boolean */ public function delete_all() { // Prepare statement $statement = $this->_db->prepare('DELETE FROM caches'); // Remove the entry try { $statement->execute(); } catch (PDOException $e) { throw new Cache_Exception('There was a problem querying the local SQLite3 cache. :error', array(':error' => $e->getMessage())); } return (bool) $statement->rowCount(); } /** * Set a value based on an id. Optionally add tags. * * @param string $id id * @param mixed $data data * @param integer $lifetime lifetime [Optional] * @param array $tags tags [Optional] * @return boolean * @throws Kohana_Cache_Exception */ public function set_with_tags($id, $data, $lifetime = NULL, array $tags = NULL) { // Serialize the data $data = serialize($data); // Normalise tags $tags = (NULL === $tags) ? NULL : ('<'.implode('>,<', $tags).'>'); // Setup lifetime if ($lifetime === NULL) { $lifetime = (0 === Arr::get('default_expire', NULL)) ? 0 : (Arr::get($this->_config, 'default_expire', Cache::DEFAULT_EXPIRE) + time()); } else { $lifetime = (0 === $lifetime) ? 0 : ($lifetime + time()); } // Prepare statement // $this->exists() may throw Kohana_Cache_Exception, no need to catch/rethrow $statement = $this->exists($id) ? $this->_db->prepare('UPDATE caches SET expiration = :expiration, cache = :cache, tags = :tags WHERE id = :id') : $this->_db->prepare('INSERT INTO caches (id, cache, expiration, tags) VALUES (:id, :cache, :expiration, :tags)'); // Try to insert try { $statement->execute(array(':id' => $this->_sanitize_id($id), ':cache' => $data, ':expiration' => $lifetime, ':tags' => $tags)); } catch (PDOException $e) { throw new Kohana_Cache_Exception('There was a problem querying the local SQLite3 cache. :error', array(':error' => $e->getMessage())); } return (bool) $statement->rowCount(); } /** * Delete cache entries based on a tag * * @param string $tag tag * @return boolean * @throws Kohana_Cache_Exception */ public function delete_tag($tag) { // Prepare the statement $statement = $this->_db->prepare('DELETE FROM caches WHERE tags LIKE :tag'); // Try to delete try { $statement->execute(array(':tag' => "%<{$tag}>%")); } catch (PDOException $e) { throw new Cache_Exception('There was a problem querying the local SQLite3 cache. :error', array(':error' => $e->getMessage())); } return (bool) $statement->rowCount(); } /** * Find cache entries based on a tag * * @param string $tag tag * @return array * @throws Kohana_Cache_Exception */ public function find($tag) { // Prepare the statement $statement = $this->_db->prepare('SELECT id, cache FROM caches WHERE tags LIKE :tag'); // Try to find try { if ( ! $statement->execute(array(':tag' => "%<{$tag}>%"))) { return array(); } } catch (PDOException $e) { throw new Kohana_Cache_Exception('There was a problem querying the local SQLite3 cache. :error', array(':error' => $e->getMessage())); } $result = array(); while ($row = $statement->fetchObject()) { // Disable notices for unserializing $ER = error_reporting(~E_NOTICE); $result[$row->id] = unserialize($row->cache); // Turn notices back on error_reporting($ER); } return $result; } /** * Garbage collection method that cleans any expired * cache entries from the cache. * * @return void */ public function garbage_collect() { // Create the sequel statement $statement = $this->_db->prepare('DELETE FROM caches WHERE expiration < :expiration'); try { $statement->execute(array(':expiration' => time())); } catch (PDOException $e) { throw new Kohana_Cache_Exception('There was a problem querying the local SQLite3 cache. :error', array(':error' => $e->getMessage())); } } /** * Tests whether an id exists or not * * @param string $id id * @return boolean * @throws Kohana_Cache_Exception */ protected function exists($id) { $statement = $this->_db->prepare('SELECT id FROM caches WHERE id = :id'); try { $statement->execute(array(':id' => $this->_sanitize_id($id))); } catch (PDOExeption $e) { throw new Kohana_Cache_Exception('There was a problem querying the local SQLite3 cache. :error', array(':error' => $e->getMessage())); } return (bool) $statement->fetchAll(); } } libkohana3.1-php-3.1.5/modules/cache/classes/kohana/cache/tagging.php0000644000000000000000000000170411772106744023775 0ustar rootroot array( // Driver group * 'driver' => 'wincache', // using wincache driver * ), * ) * * In cases where only one cache group is required, if the group is named `default` there is * no need to pass the group name when instantiating a cache instance. * * #### General cache group configuration settings * * Below are the settings available to all types of cache driver. * * Name | Required | Description * -------------- | -------- | --------------------------------------------------------------- * driver | __YES__ | (_string_) The driver type to use * * ### System requirements * * * Windows XP SP3 with IIS 5.1 and » FastCGI Extension * * Windows Server 2003 with IIS 6.0 and » FastCGI Extension * * Windows Vista SP1 with IIS 7.0 and FastCGI Module * * Windows Server 2008 with IIS 7.0 and FastCGI Module * * Windows 7 with IIS 7.5 and FastCGI Module * * Windows Server 2008 R2 with IIS 7.5 and FastCGI Module * * PHP 5.2.X, Non-thread-safe build * * PHP 5.3 X86, Non-thread-safe VC9 build * * @package Kohana/Cache * @category Base * @author Kohana Team * @copyright (c) 2009-2012 Kohana Team * @license http://kohanaphp.com/license */ class Kohana_Cache_Wincache extends Cache { /** * Check for existence of the wincache extension This method cannot be invoked externally. The driver must * be instantiated using the `Cache::instance()` method. * * @param array $config configuration * @throws Kohana_Cache_Exception */ protected function __construct(array $config) { if ( ! extension_loaded('wincache')) { throw new Kohana_Cache_Exception('PHP wincache extension is not available.'); } parent::__construct($config); } /** * Retrieve a cached value entry by id. * * // Retrieve cache entry from wincache group * $data = Cache::instance('wincache')->get('foo'); * * // Retrieve cache entry from wincache group and return 'bar' if miss * $data = Cache::instance('wincache')->get('foo', 'bar'); * * @param string $id id of cache to entry * @param string $default default value to return if cache miss * @return mixed * @throws Kohana_Cache_Exception */ public function get($id, $default = NULL) { $data = wincache_ucache_get($this->_sanitize_id($id), $success); return $success ? $data : $default; } /** * Set a value to cache with id and lifetime * * $data = 'bar'; * * // Set 'bar' to 'foo' in wincache group, using default expiry * Cache::instance('wincache')->set('foo', $data); * * // Set 'bar' to 'foo' in wincache group for 30 seconds * Cache::instance('wincache')->set('foo', $data, 30); * * @param string $id id of cache entry * @param string $data data to set to cache * @param integer $lifetime lifetime in seconds * @return boolean */ public function set($id, $data, $lifetime = NULL) { if ($lifetime === NULL) { $lifetime = Arr::get($this->_config, 'default_expire', Cache::DEFAULT_EXPIRE); } return wincache_ucache_set($this->_sanitize_id($id), $data, $lifetime); } /** * Delete a cache entry based on id * * // Delete 'foo' entry from the wincache group * Cache::instance('wincache')->delete('foo'); * * @param string $id id to remove from cache * @return boolean */ public function delete($id) { return wincache_ucache_delete($this->_sanitize_id($id)); } /** * Delete all cache entries. * * Beware of using this method when * using shared memory cache systems, as it will wipe every * entry within the system for all clients. * * // Delete all cache entries in the wincache group * Cache::instance('wincache')->delete_all(); * * @return boolean */ public function delete_all() { return wincache_ucache_clear(); } } libkohana3.1-php-3.1.5/modules/cache/classes/kohana/cache/xcache.php0000644000000000000000000000353511772106744023614 0ustar rootroot_sanitize_id($id))) === NULL) ? $default : $data; } /** * Set a value based on an id. Optionally add tags. * * @param string $id id * @param string $data data * @param integer $lifetime lifetime [Optional] * @return boolean */ public function set($id, $data, $lifetime = NULL) { if (NULL === $lifetime) { $lifetime = Arr::get($this->_config, 'default_expire', Cache::DEFAULT_EXPIRE); } return xcache_set($this->_sanitize_id($id), $data, $lifetime); } /** * Delete a cache entry based on id * * @param string $id id * @return boolean */ public function delete($id) { return xcache_unset($this->_sanitize_id($id)); } /** * Delete all cache entries * To use this method xcache.admin.enable_auth has to be Off in xcache.ini * * @return void */ public function delete_all() { xcache_clear_cache(XC_TYPE_PHP, 0); } } libkohana3.1-php-3.1.5/modules/cache/classes/kohana/cache.php0000644000000000000000000001717111772106744022362 0ustar rootroot array( // Default group * 'driver' => 'memcache', // using Memcache driver * 'servers' => array( // Available server definitions * array( * 'host' => 'localhost', * 'port' => 11211, * 'persistent' => FALSE * ) * ), * 'compression' => FALSE, // Use compression? * ), * ) * * In cases where only one cache group is required, if the group is named `default` there is * no need to pass the group name when instantiating a cache instance. * * #### General cache group configuration settings * * Below are the settings available to all types of cache driver. * * Name | Required | Description * -------------- | -------- | --------------------------------------------------------------- * driver | __YES__ | (_string_) The driver type to use * * Details of the settings specific to each driver are available within the drivers documentation. * * ### System requirements * * * Kohana 3.0.x * * PHP 5.2.4 or greater * * @package Kohana/Cache * @category Base * @version 2.0 * @author Kohana Team * @copyright (c) 2009-2012 Kohana Team * @license http://kohanaphp.com/license */ abstract class Kohana_Cache { const DEFAULT_EXPIRE = 3600; /** * @var string default driver to use */ public static $default = 'file'; /** * @var Kohana_Cache instances */ public static $instances = array(); /** * Creates a singleton of a Kohana Cache group. If no group is supplied * the __default__ cache group is used. * * // Create an instance of the default group * $default_group = Cache::instance(); * * // Create an instance of a group * $foo_group = Cache::instance('foo'); * * // Access an instantiated group directly * $foo_group = Cache::$instances['default']; * * @param string $group the name of the cache group to use [Optional] * @return Cache * @throws Kohana_Cache_Exception */ public static function instance($group = NULL) { // If there is no group supplied if ($group === NULL) { // Use the default setting $group = Cache::$default; } if (isset(Cache::$instances[$group])) { // Return the current group if initiated already return Cache::$instances[$group]; } $config = Kohana::config('cache'); if ( ! $config->offsetExists($group)) { throw new Kohana_Cache_Exception('Failed to load Kohana Cache group: :group', array(':group' => $group)); } $config = $config->get($group); // Create a new cache type instance $cache_class = 'Cache_'.ucfirst($config['driver']); Cache::$instances[$group] = new $cache_class($config); // Return the instance return Cache::$instances[$group]; } /** * @var Config */ protected $_config; /** * Ensures singleton pattern is observed, loads the default expiry * * @param array $config configuration */ protected function __construct(array $config) { $this->_config = $config; } /** * Overload the __clone() method to prevent cloning * * @return void * @throws Kohana_Cache_Exception */ public function __clone() { throw new Kohana_Cache_Exception('Cloning of Kohana_Cache objects is forbidden'); } /** * Retrieve a cached value entry by id. * * // Retrieve cache entry from default group * $data = Cache::instance()->get('foo'); * * // Retrieve cache entry from default group and return 'bar' if miss * $data = Cache::instance()->get('foo', 'bar'); * * // Retrieve cache entry from memcache group * $data = Cache::instance('memcache')->get('foo'); * * @param string $id id of cache to entry * @param string $default default value to return if cache miss * @return mixed * @throws Kohana_Cache_Exception */ abstract public function get($id, $default = NULL); /** * Set a value to cache with id and lifetime * * $data = 'bar'; * * // Set 'bar' to 'foo' in default group, using default expiry * Cache::instance()->set('foo', $data); * * // Set 'bar' to 'foo' in default group for 30 seconds * Cache::instance()->set('foo', $data, 30); * * // Set 'bar' to 'foo' in memcache group for 10 minutes * if (Cache::instance('memcache')->set('foo', $data, 600)) * { * // Cache was set successfully * return * } * * @param string $id id of cache entry * @param string $data data to set to cache * @param integer $lifetime lifetime in seconds * @return boolean */ abstract public function set($id, $data, $lifetime = 3600); /** * Delete a cache entry based on id * * // Delete 'foo' entry from the default group * Cache::instance()->delete('foo'); * * // Delete 'foo' entry from the memcache group * Cache::instance('memcache')->delete('foo') * * @param string $id id to remove from cache * @return boolean */ abstract public function delete($id); /** * Delete all cache entries. * * Beware of using this method when * using shared memory cache systems, as it will wipe every * entry within the system for all clients. * * // Delete all cache entries in the default group * Cache::instance()->delete_all(); * * // Delete all cache entries in the memcache group * Cache::instance('memcache')->delete_all(); * * @return boolean */ abstract public function delete_all(); /** * Replaces troublesome characters with underscores. * * // Sanitize a cache id * $id = $this->_sanitize_id($id); * * @param string $id id of cache to sanitize * @return string */ protected function _sanitize_id($id) { // Change slashes and spaces to underscores return str_replace(array('/', '\\', ' '), '_', $id); } } // End Kohana_Cache libkohana3.1-php-3.1.5/modules/cache/config/0000755000000000000000000000000011771133710017136 5ustar rootrootlibkohana3.1-php-3.1.5/modules/cache/config/cache.php0000644000000000000000000000415211772106744020724 0ustar rootroot array ( 'driver' => 'memcache', 'default_expire' => 3600, 'compression' => FALSE, // Use Zlib compression (can cause issues with integers) 'servers' => array ( array ( 'host' => 'localhost', // Memcache Server 'port' => 11211, // Memcache port number 'persistent' => FALSE, // Persistent connection 'weight' => 1, 'timeout' => 1, 'retry_interval' => 15, 'status' => TRUE, ), ), 'instant_death' => TRUE, // Take server offline immediately on first fail (no retry) ), 'memcachetag' => array ( 'driver' => 'memcachetag', 'default_expire' => 3600, 'compression' => FALSE, // Use Zlib compression (can cause issues with integers) 'servers' => array ( array ( 'host' => 'localhost', // Memcache Server 'port' => 11211, // Memcache port number 'persistent' => FALSE, // Persistent connection 'weight' => 1, 'timeout' => 1, 'retry_interval' => 15, 'status' => TRUE, ), ), 'instant_death' => TRUE, ), 'apc' => array ( 'driver' => 'apc', 'default_expire' => 3600, ), 'wincache' => array ( 'driver' => 'wincache', 'default_expire' => 3600, ), 'sqlite' => array ( 'driver' => 'sqlite', 'default_expire' => 3600, 'database' => APPPATH.'cache/kohana-cache.sql3', 'schema' => 'CREATE TABLE caches(id VARCHAR(127) PRIMARY KEY, tags VARCHAR(255), expiration INTEGER, cache TEXT)', ), 'eaccelerator' => array ( 'driver' => 'eaccelerator', ), 'xcache' => array ( 'driver' => 'xcache', 'default_expire' => 3600, ), 'file' => array ( 'driver' => 'file', 'cache_dir' => APPPATH.'cache', 'default_expire' => 3600, ) );libkohana3.1-php-3.1.5/modules/cache/config/userguide.php0000644000000000000000000000127011772106744021653 0ustar rootroot array( // This should be the path to this modules userguide pages, without the 'guide/'. Ex: '/guide/modulename/' would be 'modulename' 'cache' => array( // Whether this modules userguide pages should be shown 'enabled' => TRUE, // The name that should show up on the userguide index page 'name' => 'Cache', // A short description of this module, shown on the index page 'description' => 'Common interface for caching engines.', // Copyright message, shown in the footer for this module 'copyright' => '© 2008–2012 Kohana Team', ) ) );libkohana3.1-php-3.1.5/modules/cache/guide/0000755000000000000000000000000011771133710016766 5ustar rootrootlibkohana3.1-php-3.1.5/modules/cache/guide/cache/0000755000000000000000000000000011771133710020031 5ustar rootrootlibkohana3.1-php-3.1.5/modules/cache/guide/cache/config.md0000644000000000000000000001431211772106744021631 0ustar rootroot# Kohana Cache configuration Kohana Cache uses configuration groups to create cache instances. A configuration group can use any supported driver, with successive groups using multiple instances of the same driver type. The default cache group is loaded based on the `Cache::$default` setting. It is set to the `file` driver as standard, however this can be changed within the `/application/boostrap.php` file. // Change the default cache driver to memcache Cache::$default = 'memcache'; // Load the memcache cache driver using default setting $memcache = Cache::instance(); ## Group settings Below are the default cache configuration groups for each supported driver. Add to- or override these settings within the `application/config/cache.php` file. Name | Required | Description -------------- | -------- | --------------------------------------------------------------- driver | __YES__ | (_string_) The driver type to use default_expire | __NO__ | (_string_) The driver type to use 'file' => array ( 'driver' => 'file', 'cache_dir' => APPPATH.'cache/.kohana_cache', 'default_expire' => 3600, ), ## Memcache & Memcached-tag settings Name | Required | Description -------------- | -------- | --------------------------------------------------------------- driver | __YES__ | (_string_) The driver type to use servers | __YES__ | (_array_) Associative array of server details, must include a __host__ key. (see _Memcache server configuration_ below) compression | __NO__ | (_boolean_) Use data compression when caching ### Memcache server configuration Name | Required | Description ---------------- | -------- | --------------------------------------------------------------- host | __YES__ | (_string_) The host of the memcache server, i.e. __localhost__; or __127.0.0.1__; or __memcache.domain.tld__ port | __NO__ | (_integer_) Point to the port where memcached is listening for connections. Set this parameter to 0 when using UNIX domain sockets. Default __11211__ persistent | __NO__ | (_boolean_) Controls the use of a persistent connection. Default __TRUE__ weight | __NO__ | (_integer_) Number of buckets to create for this server which in turn control its probability of it being selected. The probability is relative to the total weight of all servers. Default __1__ timeout | __NO__ | (_integer_) Value in seconds which will be used for connecting to the daemon. Think twice before changing the default value of 1 second - you can lose all the advantages of caching if your connection is too slow. Default __1__ retry_interval | __NO__ | (_integer_) Controls how often a failed server will be retried, the default value is 15 seconds. Setting this parameter to -1 disables automatic retry. Default __15__ status | __NO__ | (_boolean_) Controls if the server should be flagged as online. Default __TRUE__ failure_callback | __NO__ | (_[callback](http://www.php.net/manual/en/language.pseudo-types.php#language.types.callback)_) Allows the user to specify a callback function to run upon encountering an error. The callback is run before failover is attempted. The function takes two parameters, the hostname and port of the failed server. Default __NULL__ 'memcache' => array ( 'driver' => 'memcache', 'default_expire' => 3600, 'compression' => FALSE, // Use Zlib compression (can cause issues with integers) 'servers' => array ( array ( 'host' => 'localhost', // Memcache Server 'port' => 11211, // Memcache port number 'persistent' => FALSE, // Persistent connection ), ), ), 'memcachetag' => array ( 'driver' => 'memcachetag', 'default_expire' => 3600, 'compression' => FALSE, // Use Zlib compression (can cause issues with integers) 'servers' => array ( array ( 'host' => 'localhost', // Memcache Server 'port' => 11211, // Memcache port number 'persistent' => FALSE, // Persistent connection ), ), ), ## APC settings 'apc' => array ( 'driver' => 'apc', 'default_expire' => 3600, ), ## SQLite settings 'sqlite' => array ( 'driver' => 'sqlite', 'default_expire' => 3600, 'database' => APPPATH.'cache/kohana-cache.sql3', 'schema' => 'CREATE TABLE caches(id VARCHAR(127) PRIMARY KEY, tags VARCHAR(255), expiration INTEGER, cache TEXT)', ), ## Eaccelerator settings 'eaccelerator' array ( 'driver' => 'eaccelerator', ), ## Xcache settings 'xcache' => array ( 'driver' => 'xcache', 'default_expire' => 3600, ), ## File settings 'file' => array ( 'driver' => 'file', 'cache_dir' => 'cache/.kohana_cache', 'default_expire' => 3600, ) ## Override existing configuration group The following example demonstrates how to override an existing configuration setting, using the config file in `/application/config/cache.php`. array ( 'driver' => 'memcache', // Use Memcached as the default driver 'default_expire' => 8000, // Overide default expiry 'servers' => array ( // Add a new server array ( 'host' => 'cache.domain.tld', 'port' => 11211, 'persistent' => FALSE ) ), 'compression' => FALSE ) ); ## Add new configuration group The following example demonstrates how to add a new configuration setting, using the config file in `/application/config/cache.php`. array ( 'driver' => 'apc', // Use Memcached as the default driver 'default_expire' => 1000, // Overide default expiry ) );libkohana3.1-php-3.1.5/modules/cache/guide/cache/examples.md0000644000000000000000000000000011771113722022160 0ustar rootrootlibkohana3.1-php-3.1.5/modules/cache/guide/cache/index.md0000644000000000000000000001005211771117076021467 0ustar rootroot# About Kohana Cache [Kohana_Cache] provides a common interface to a variety of caching engines. [Kohana_Cache_Tagging] is supported where available natively to the cache system. Kohana Cache supports multiple instances of cache engines through a grouped singleton pattern. ## Supported cache engines * APC ([Cache_Apc]) * eAccelerator ([Cache_Eaccelerator]) * File ([Cache_File]) * Memcached ([Cache_Memcache]) * Memcached-tags ([Cache_Memcachetag]) * SQLite ([Cache_Sqlite]) * Xcache ([Cache_Xcache]) ## Introduction to caching Caching should be implemented with consideration. Generally, caching the result of resources is faster than reprocessing them. Choosing what, how and when to cache is vital. [PHP APC](http://php.net/manual/en/book.apc.php) is one of the fastest caching systems available, closely followed by [Memcached](http://memcached.org/). [SQLite](http://www.sqlite.org/) and File caching are two of the slowest cache methods, however usually faster than reprocessing a complex set of instructions. Caching engines that use memory are considerably faster than file based alternatives. But memory is limited whereas disk space is plentiful. If caching large datasets, such as large database result sets, it is best to use file caching. [!!] Cache drivers require the relevant PHP extensions to be installed. APC, eAccelerator, Memecached and Xcache all require non-standard PHP extensions. ## What the Kohana Cache module does (and does not do) This module provides a simple abstracted interface to a wide selection of popular PHP cache engines. The caching API provides the basic caching methods implemented across all solutions, memory, network or disk based. Basic key / value storing is supported by all drivers, with additional tagging and garbage collection support where implemented or required. _Kohana Cache_ does not provide HTTP style caching for clients (web browsers) and/or proxies (_Varnish_, _Squid_). There are other Kohana modules that provide this functionality. ## Choosing a cache provider Getting and setting values to cache is very simple when using the _Kohana Cache_ interface. The hardest choice is choosing which cache engine to use. When choosing a caching engine, the following criteria must be considered: 1. __Does the cache need to be distributed?__ This is an important consideration as it will severely limit the options available to solutions such as Memcache when a distributed solution is required. 2. __Does the cache need to be fast?__ In almost all cases retrieving data from a cache is faster than execution. However generally memory based caching is considerably faster than disk based caching (see table below). 3. __How much cache is required?__ Cache is not endless, and memory based caches are subject to a considerably more limited storage resource. Driver | Storage | Speed | Tags | Distributed | Automatic Garbage Collection | Notes ---------------- | ------------ | --------- | -------- | ----------- | ---------------------------- | ----------------------- APC | __Memory__ | Excellent | No | No | Yes | Widely available PHP opcode caching solution, improves php execution performance eAccelerator | __Memory__ | Excellent | No | No | Yes | Limited support and no longer developed. Included for legacy systems File | __Disk__ | Poor | No | No | No | Marginally faster than execution Memcache (tag) | __Memory__ | Good | No (yes) | Yes | Yes | Generally fast distributed solution, but has a speed hit due to variable network latency Sqlite | __Disk__ | Poor | Yes | No | No | Marginally faster than execution Xcache | __Memory__ | Excellent | Yes | No | Yes | Very fast memory solution and alternative to APC It is possible to have hybrid cache solutions that use a combination of the engines above in different contexts. This is supported with _Kohana Cache_ as well. ## Minimum requirements * Kohana 3.0.4 * PHP 5.2.4 or greaterlibkohana3.1-php-3.1.5/modules/cache/guide/cache/menu.md0000644000000000000000000000006711771113722021323 0ustar rootroot## [Cache]() - [Configuration](config) - [Usage](usage)libkohana3.1-php-3.1.5/modules/cache/guide/cache/usage.md0000644000000000000000000002075511771117076021477 0ustar rootroot# Kohana Cache usage [Kohana_Cache] provides a simple interface allowing getting, setting and deleting of cached values. Two interfaces included in _Kohana Cache_ additionally provide _tagging_ and _garbage collection_ where they are supported by the respective drivers. ## Getting a new cache instance Creating a new _Kohana Cache_ instance is simple, however it must be done using the [Cache::instance] method, rather than the traditional `new` constructor. // Create a new instance of cache using the default group $cache = Cache::instance(); The default group will use whatever is set to [Cache::$default] and must have a corresponding [configuration](cache.config) definition for that group. To create a cache instance using a group other than the _default_, simply provide the group name as an argument. // Create a new instance of the memcache group $memcache = Cache::instance('memcache'); If there is a cache instance already instantiated then you can get it directly from the class member. [!!] Beware that this can cause issues if you do not test for the instance before trying to access it. // Check for the existance of the cache driver if (isset(Cache::$instances['memcache'])) { // Get the existing cache instance directly (faster) $memcache = Cache::$instances['memcache']; } else { // Get the cache driver instance (slower) $memcache = Cache::instance('memcache'); } ## Setting and getting variables to and from cache The cache library supports scalar and object values, utilising object serialization where required (or not supported by the caching engine). This means that the majority or objects can be cached without any modification. [!!] Serialisation does not work with resource handles, such as filesystem, curl or socket resources. ### Setting a value to cache Setting a value to cache using the [Cache::set] method can be done in one of two ways; either using the Cache instance interface, which is good for atomic operations; or getting an instance and using that for multiple operations. The first example demonstrates how to quickly load and set a value to the default cache instance. // Create a cachable object $object = new stdClass; // Set a property $object->foo = 'bar'; // Cache the object using default group (quick interface) with default time (3600 seconds) Cache::instance()->set('foo', $object); If multiple cache operations are required, it is best to assign an instance of Cache to a variable and use that as below. // Set the object using a defined group for a defined time period (30 seconds) $memcache = Cache::instance('memcache'); $memcache->set('foo', $object, 30); #### Setting a value with tags Certain cache drivers support setting values with tags. To set a value to cache with tags using the following interface. // Get a cache instance that supports tags $memcache = Cache::instance('memcachetag'); // Test for tagging interface if ($memcache instanceof Kohana_Cache_Tagging) { // Set a value with some tags for 30 seconds $memcache->set('foo', $object, 30, array('snafu', 'stfu', 'fubar')); } // Otherwise set without tags else { // Set a value for 30 seconds $memcache->set('foo', $object, 30); } It is possible to implement custom tagging solutions onto existing or new cache drivers by implementing the [Kohana_Cache_Tagging] interface. Kohana_Cache only applies the interface to drivers that support tagging natively as standard. ### Getting a value from cache Getting variables back from cache is achieved using the [Cache::get] method using a single key to identify the cache entry. // Retrieve a value from cache (quickly) $object = Cache::instance()->get('foo'); In cases where the requested key is not available or the entry has expired, a default value will be returned (__NULL__ by default). It is possible to define the default value as the key is requested. // If the cache key is available (with default value set to FALSE) if ($object = Cache::instance()->get('foo', FALSE)) { // Do something } else { // Do something else } #### Getting values from cache using tags It is possible to retrieve values from cache grouped by tag, using the [Cache::find] method with drivers that support tagging. [!!] The __Memcachetag__ driver does not support the `Cache::find($tag)` interface and will throw an exception. // Get an instance of cache $cache = Cache::instance('memcachetag'); // Wrap in a try/catch statement to gracefully handle memcachetag try { // Find values based on tag return $cache->find('snafu'); } catch (Kohana_Cache_Exception $e) { // Handle gracefully return FALSE; } ### Deleting values from cache Deleting variables is very similar to the getting and setting methods already described. Deleting operations are split into three categories: - __Delete value by key__. Deletes a cached value by the associated key. - __Delete all values__. Deletes all caches values stored in the cache instance. - __Delete values by tag__. Deletes all values that have the supplied tag. This is only supported by Memcached-Tag and Sqlite. #### Delete value by key To delete a specific value by its associated key: // If the cache entry for 'foo' is deleted if (Cache::instance()->delete('foo')) { // Cache entry successfully deleted, do something } By default a `TRUE` value will be returned. However a `FALSE` value will be returned in instances where the key did not exist in the cache. #### Delete all values To delete all values in a specific instance: // If all cache items where deleted successfully if (Cache::instance()->delete_all()) { // Do something } It is also possible to delete all cache items in every instance: // For each cache instance foreach (Cache::$instances as $group => $instance) { if ($instance->delete_all()) { var_dump('instance : '.$group.' has been flushed!'); } } #### Delete values by tag Some of the caching drivers support deleting by tag. This will remove all the cached values that are associated with a specific tag. Below is an example of how to robustly handle deletion by tag. // Get cache instance $cache = Cache::instance(); // Check for tagging interface if ($cache instanceof Kohana_Cache_Tagging) { // Delete all entries by the tag 'snafu' $cache->delete_tag('snafu'); } #### Garbage Collection Garbage Collection (GC) is the cleaning of expired cache entries. For the most part, caching engines will take care of garbage collection internally. However a few of the file based systems do not handle this task and in these circumstances it would be prudent to garbage collect at a predetermined frequency. If no garbage collection is executed, the resource storing the cache entries will eventually fill and become unusable. When not automated, garbage collection is the responsibility of the developer. It is prudent to have a GC probability value that dictates how likely the garbage collection routing will be run. An example of such a system is demonstrated below. // Get a cache instance $cache_file = Cache::instance('file'); // Set a GC probability of 10% $gc = 10; // If the GC probability is a hit if (rand(0,99) <= $gc and $cache_file instanceof Kohana_Cache_GarbageCollect) { // Garbage Collect $cache_file->garbage_collect(); } # Interfaces Kohana Cache comes with two interfaces that are implemented where the drivers support them: - __[Kohana_Cache_Tagging] for tagging support on cache entries__ - [Cache_MemcacheTag] - [Cache_Sqlite] - __[Kohana_Cache_GarbageCollect] for garbage collection with drivers without native support__ - [Cache_File] - [Cache_Sqlite] When using interface specific caching features, ensure that code checks for the required interface before using the methods supplied. The following example checks whether the garbage collection interface is available before calling the `garbage_collect` method. // Create a cache instance $cache = Cache::instance(); // Test for Garbage Collection if ($cache instanceof Kohana_Cache_GarbageCollect) { // Collect garbage $cache->garbage_collect(); }libkohana3.1-php-3.1.5/modules/cache/guide/cache.usage.md0000644000000000000000000002075511771117076021476 0ustar rootroot# Kohana Cache usage [Kohana_Cache] provides a simple interface allowing getting, setting and deleting of cached values. Two interfaces included in _Kohana Cache_ additionally provide _tagging_ and _garbage collection_ where they are supported by the respective drivers. ## Getting a new cache instance Creating a new _Kohana Cache_ instance is simple, however it must be done using the [Cache::instance] method, rather than the traditional `new` constructor. // Create a new instance of cache using the default group $cache = Cache::instance(); The default group will use whatever is set to [Cache::$default] and must have a corresponding [configuration](cache.config) definition for that group. To create a cache instance using a group other than the _default_, simply provide the group name as an argument. // Create a new instance of the memcache group $memcache = Cache::instance('memcache'); If there is a cache instance already instantiated then you can get it directly from the class member. [!!] Beware that this can cause issues if you do not test for the instance before trying to access it. // Check for the existance of the cache driver if (isset(Cache::$instances['memcache'])) { // Get the existing cache instance directly (faster) $memcache = Cache::$instances['memcache']; } else { // Get the cache driver instance (slower) $memcache = Cache::instance('memcache'); } ## Setting and getting variables to and from cache The cache library supports scalar and object values, utilising object serialization where required (or not supported by the caching engine). This means that the majority or objects can be cached without any modification. [!!] Serialisation does not work with resource handles, such as filesystem, curl or socket resources. ### Setting a value to cache Setting a value to cache using the [Cache::set] method can be done in one of two ways; either using the Cache instance interface, which is good for atomic operations; or getting an instance and using that for multiple operations. The first example demonstrates how to quickly load and set a value to the default cache instance. // Create a cachable object $object = new stdClass; // Set a property $object->foo = 'bar'; // Cache the object using default group (quick interface) with default time (3600 seconds) Cache::instance()->set('foo', $object); If multiple cache operations are required, it is best to assign an instance of Cache to a variable and use that as below. // Set the object using a defined group for a defined time period (30 seconds) $memcache = Cache::instance('memcache'); $memcache->set('foo', $object, 30); #### Setting a value with tags Certain cache drivers support setting values with tags. To set a value to cache with tags using the following interface. // Get a cache instance that supports tags $memcache = Cache::instance('memcachetag'); // Test for tagging interface if ($memcache instanceof Kohana_Cache_Tagging) { // Set a value with some tags for 30 seconds $memcache->set('foo', $object, 30, array('snafu', 'stfu', 'fubar')); } // Otherwise set without tags else { // Set a value for 30 seconds $memcache->set('foo', $object, 30); } It is possible to implement custom tagging solutions onto existing or new cache drivers by implementing the [Kohana_Cache_Tagging] interface. Kohana_Cache only applies the interface to drivers that support tagging natively as standard. ### Getting a value from cache Getting variables back from cache is achieved using the [Cache::get] method using a single key to identify the cache entry. // Retrieve a value from cache (quickly) $object = Cache::instance()->get('foo'); In cases where the requested key is not available or the entry has expired, a default value will be returned (__NULL__ by default). It is possible to define the default value as the key is requested. // If the cache key is available (with default value set to FALSE) if ($object = Cache::instance()->get('foo', FALSE)) { // Do something } else { // Do something else } #### Getting values from cache using tags It is possible to retrieve values from cache grouped by tag, using the [Cache::find] method with drivers that support tagging. [!!] The __Memcachetag__ driver does not support the `Cache::find($tag)` interface and will throw an exception. // Get an instance of cache $cache = Cache::instance('memcachetag'); // Wrap in a try/catch statement to gracefully handle memcachetag try { // Find values based on tag return $cache->find('snafu'); } catch (Kohana_Cache_Exception $e) { // Handle gracefully return FALSE; } ### Deleting values from cache Deleting variables is very similar to the getting and setting methods already described. Deleting operations are split into three categories: - __Delete value by key__. Deletes a cached value by the associated key. - __Delete all values__. Deletes all caches values stored in the cache instance. - __Delete values by tag__. Deletes all values that have the supplied tag. This is only supported by Memcached-Tag and Sqlite. #### Delete value by key To delete a specific value by its associated key: // If the cache entry for 'foo' is deleted if (Cache::instance()->delete('foo')) { // Cache entry successfully deleted, do something } By default a `TRUE` value will be returned. However a `FALSE` value will be returned in instances where the key did not exist in the cache. #### Delete all values To delete all values in a specific instance: // If all cache items where deleted successfully if (Cache::instance()->delete_all()) { // Do something } It is also possible to delete all cache items in every instance: // For each cache instance foreach (Cache::$instances as $group => $instance) { if ($instance->delete_all()) { var_dump('instance : '.$group.' has been flushed!'); } } #### Delete values by tag Some of the caching drivers support deleting by tag. This will remove all the cached values that are associated with a specific tag. Below is an example of how to robustly handle deletion by tag. // Get cache instance $cache = Cache::instance(); // Check for tagging interface if ($cache instanceof Kohana_Cache_Tagging) { // Delete all entries by the tag 'snafu' $cache->delete_tag('snafu'); } #### Garbage Collection Garbage Collection (GC) is the cleaning of expired cache entries. For the most part, caching engines will take care of garbage collection internally. However a few of the file based systems do not handle this task and in these circumstances it would be prudent to garbage collect at a predetermined frequency. If no garbage collection is executed, the resource storing the cache entries will eventually fill and become unusable. When not automated, garbage collection is the responsibility of the developer. It is prudent to have a GC probability value that dictates how likely the garbage collection routing will be run. An example of such a system is demonstrated below. // Get a cache instance $cache_file = Cache::instance('file'); // Set a GC probability of 10% $gc = 10; // If the GC probability is a hit if (rand(0,99) <= $gc and $cache_file instanceof Kohana_Cache_GarbageCollect) { // Garbage Collect $cache_file->garbage_collect(); } # Interfaces Kohana Cache comes with two interfaces that are implemented where the drivers support them: - __[Kohana_Cache_Tagging] for tagging support on cache entries__ - [Cache_MemcacheTag] - [Cache_Sqlite] - __[Kohana_Cache_GarbageCollect] for garbage collection with drivers without native support__ - [Cache_File] - [Cache_Sqlite] When using interface specific caching features, ensure that code checks for the required interface before using the methods supplied. The following example checks whether the garbage collection interface is available before calling the `garbage_collect` method. // Create a cache instance $cache = Cache::instance(); // Test for Garbage Collection if ($cache instanceof Kohana_Cache_GarbageCollect) { // Collect garbage $cache->garbage_collect(); }libkohana3.1-php-3.1.5/modules/cache/tests/0000755000000000000000000000000011771133710017033 5ustar rootrootlibkohana3.1-php-3.1.5/modules/cache/tests/cache/0000755000000000000000000000000011771133710020076 5ustar rootrootlibkohana3.1-php-3.1.5/modules/cache/tests/cache/CacheBasicMethodsTest.php0000644000000000000000000001407611771131762024755 0ustar rootroot_cache_driver; $this->_cache_driver = $cache; return $this; } /** * Data provider for test_set_get() * * @return array */ public function provider_set_get() { $object = new StdClass; $object->foo = 'foo'; $object->bar = 'bar'; $html_text = << TESTTEXT; return array( array( array( 'id' => 'string', // Key to set to cache 'value' => 'foobar', // Value to set to key 'ttl' => 0, // Time to live 'wait' => FALSE, // Test wait time to let cache expire 'type' => 'string', // Type test 'default' => NULL // Default value get should return ), 'foobar' ), array( array( 'id' => 'integer', 'value' => 101010, 'ttl' => 0, 'wait' => FALSE, 'type' => 'integer', 'default' => NULL ), 101010 ), array( array( 'id' => 'float', 'value' => 10.00, 'ttl' => 0, 'wait' => FALSE, 'type' => 'float', 'default' => NULL ), 10.00 ), array( array( 'id' => 'array', 'value' => array( 'key' => 'foo', 'value' => 'bar' ), 'ttl' => 0, 'wait' => FALSE, 'type' => 'array', 'default' => NULL ), array( 'key' => 'foo', 'value' => 'bar' ) ), array( array( 'id' => 'boolean', 'value' => TRUE, 'ttl' => 0, 'wait' => FALSE, 'type' => 'boolean', 'default' => NULL ), TRUE ), array( array( 'id' => 'null', 'value' => NULL, 'ttl' => 0, 'wait' => FALSE, 'type' => 'null', 'default' => NULL ), NULL ), array( array( 'id' => 'object', 'value' => $object, 'ttl' => 0, 'wait' => FALSE, 'type' => 'object', 'default' => NULL ), $object ), array( array( 'id' => 'bar\\ with / troublesome key', 'value' => 'foo bar snafu', 'ttl' => 0, 'wait' => FALSE, 'type' => 'string', 'default' => NULL ), 'foo bar snafu' ), array( array( 'id' => 'bar', 'value' => 'foo', 'ttl' => 3, 'wait' => 5, 'type' => 'null', 'default' => NULL ), NULL ), array( array( 'id' => 'snafu', 'value' => 'fubar', 'ttl' => 3, 'wait' => 5, 'type' => 'string', 'default' => 'something completely different!' ), 'something completely different!' ), array( array( 'id' => 'new line test with HTML', 'value' => $html_text, 'ttl' => 10, 'wait' => FALSE, 'type' => 'string', 'default' => NULL, ), $html_text ) ); } /** * Tests the [Cache::set()] method, testing; * * - The value is cached * - The lifetime is respected * - The returned value type is as expected * - The default not-found value is respected * * @dataProvider provider_set_get * * @param array data * @param mixed expected * @return void */ public function test_set_get(array $data, $expected) { $cache = $this->cache(); extract($data); $this->assertTrue($cache->set($id, $value, $ttl)); if ($wait !== FALSE) { // Lets let the cache expire sleep($wait); } $result = $cache->get($id, $default); $this->assertEquals($expected, $result); $this->assertInternalType($type, $result); unset($id, $value, $ttl, $wait, $type, $default); } /** * Tests the [Cache::delete()] method, testing; * * - The a cached value is deleted from cache * - The cache returns a TRUE value upon deletion * - The cache returns a FALSE value if no value exists to delete * * @return void */ public function test_delete() { // Init $cache = $this->cache(); $cache->delete_all(); // Test deletion if real cached value if ( ! $cache->set('test_delete_1', 'This should not be here!', 0)) { $this->fail('Unable to set cache value to delete!'); } // Test delete returns TRUE and check the value is gone $this->assertTrue($cache->delete('test_delete_1')); $this->assertNull($cache->get('test_delete_1')); // Test non-existant cache value returns FALSE if no error $this->assertFalse($cache->delete('test_delete_1')); } /** * Tests [Cache::delete_all()] works as specified * * @return void * @uses Kohana_CacheBasicMethodsTest::provider_set_get() */ public function test_delete_all() { // Init $cache = $this->cache(); $data = $this->provider_set_get(); foreach ($data as $key => $values) { extract($values[0]); if ( ! $cache->set($id, $value)) { $this->fail('Unable to set: '.$key.' => '.$value.' to cache'); } unset($id, $value, $ttl, $wait, $type, $default); } // Test delete_all is successful $this->assertTrue($cache->delete_all()); foreach ($data as $key => $values) { // Verify data has been purged $this->assertSame('Cache Deleted!', $cache->get($values[0]['id'], 'Cache Deleted!')); } } } // End Kohana_CacheBasicMethodsTestlibkohana3.1-php-3.1.5/modules/cache/tests/cache/CacheTest.php0000644000000000000000000001023511771131762022460 0ustar rootrootsetExpectedException('Cache_Exception'); } try { $cache = Cache::instance($group); } catch (Cache_Exception $e) { $this->assertSame($expected, $e->getMessage()); throw $e; } $this->assertInstanceOf(get_class($expected), $cache); $this->assertSame($expected->config(), $cache->config()); } /** * Tests that `clone($cache)` will be prevented to maintain singleton * * @return void * @expectedException Cache_Exception */ public function test_cloning_fails() { try { $cache_clone = clone(Cache::instance('file')); } catch (Cache_Exception $e) { $this->assertSame('Cloning of Kohana_Cache objects is forbidden', $e->getMessage()); throw $e; } } /** * Data provider for test_config * * @return array */ public function provider_config() { return array( array( array( 'server' => 'otherhost', 'port' => 5555, 'persistent' => TRUE, ), NULL, Kohana_CacheTest::EXPECT_SELF, array( 'server' => 'otherhost', 'port' => 5555, 'persistent' => TRUE, ), ), array( 'foo', 'bar', Kohana_CacheTest::EXPECT_SELF, array( 'foo' => 'bar' ) ), array( 'server', NULL, NULL, array() ), array( NULL, NULL, array(), array() ) ); } /** * Tests the config method behaviour * * @dataProvider provider_config * * @param mixed key value to set or get * @param mixed value to set to key * @param mixed expected result from [Cache::config()] * @param array expected config within cache * @return void */ public function test_config($key, $value, $expected_result, array $expected_config) { $cache = $this->getMock('Cache_File', NULL, array(), '', FALSE); if ($expected_result === Kohana_CacheTest::EXPECT_SELF) { $expected_result = $cache; } $this->assertSame($expected_result, $cache->config($key, $value)); $this->assertSame($expected_config, $cache->config()); } /** * Data provider for test_sanitize_id * * @return array */ public function provider_sanitize_id() { return array( array( 'foo', 'foo' ), array( 'foo+-!@', 'foo+-!@' ), array( 'foo/bar', 'foo_bar', ), array( 'foo\\bar', 'foo_bar' ), array( 'foo bar', 'foo_bar' ), array( 'foo\\bar snafu/stfu', 'foo_bar_snafu_stfu' ) ); } /** * Tests the [Cache::_sanitize_id()] method works as expected. * This uses some nasty reflection techniques to access a protected * method. * * @dataProvider provider_sanitize_id * * @param string id * @param string expected * @return void */ public function test_sanitize_id($id, $expected) { $cache = $this->getMock('Cache', array( 'get', 'set', 'delete', 'delete_all' ), array(array()), '', FALSE ); $cache_reflection = new ReflectionClass($cache); $sanitize_id = $cache_reflection->getMethod('_sanitize_id'); $sanitize_id->setAccessible(TRUE); $this->assertSame($expected, $sanitize_id->invoke($cache, $id)); } } // End Kohana_CacheTestlibkohana3.1-php-3.1.5/modules/cache/tests/cache/FileTest.php0000644000000000000000000000360511771131762022337 0ustar rootrootcache(Cache::instance('file')); } /** * Tests that ignored files are not removed from file cache * * @return void */ public function test_ignore_delete_file() { $cache = $this->cache(); $config = Kohana::$config->load('cache')->file; $file = $config['cache_dir'].'/.gitignore'; // Lets pollute the cache folder file_put_contents($file, 'foobar'); $this->assertTrue($cache->delete_all()); $this->assertTrue(file_exists($file)); $this->assertEquals('foobar', file_get_contents($file)); unlink($file); } /** * Provider for test_utf8 * * @return array */ public function provider_utf8() { return array( array( 'This is â ütf-8 Ӝ☃ string', 'This is â ütf-8 Ӝ☃ string' ), array( '㆓㆕㆙㆛', '㆓㆕㆙㆛' ), array( 'அஆஇஈஊ', 'அஆஇஈஊ' ) ); } /** * Tests the file driver supports utf-8 strings * * @dataProvider provider_utf8 * * @return void */ public function test_utf8($input, $expected) { $cache = $this->cache(); $cache->set('utf8', $input); $this->assertSame($expected, $cache->get('utf8')); } } // End Kohana_SqliteTest libkohana3.1-php-3.1.5/modules/cache/tests/cache/KohanaCacheTest.php0000644000000000000000000000443211771117076023606 0ustar rootrootdelete_all(); self::$test_instance->set('testGet1', 'foo', 3600); } public function tearDown() { self::$test_instance->delete_all(); self::$test_instance = NULL; } /** * Tests the cache static instance method */ public function testInstance() { $file_instance = Cache::instance('file'); $file_instance2 = Cache::instance('file'); // Try and load a Cache instance $this->assertType('Kohana_Cache', Cache::instance()); $this->assertType('Kohana_Cache_File', $file_instance); // Test instances are only initialised once $this->assertTrue(spl_object_hash($file_instance) == spl_object_hash($file_instance2)); // Test the publically accessible Cache instance store $this->assertTrue(spl_object_hash(Cache::$instances['file']) == spl_object_hash($file_instance)); // Get the constructor method $constructorMethod = new ReflectionMethod($file_instance, '__construct'); // Test the constructor for hidden visibility $this->assertTrue($constructorMethod->isProtected(), '__construct is does not have protected visibility'); } public function testGet() { // Try and get a non property $this->assertNull(self::$test_instance->get('testGet0')); // Try and get a non property with default return value $this->assertEquals('bar', self::$test_instance->get('testGet0', 'bar')); // Try and get a real cached property $this->assertEquals('foo', self::$test_instance->get('testGet1')); } public function testSet() { $value = 'foobar'; $value2 = 'snafu'; // Set a new property $this->assertTrue(self::$test_instance->set('testSet1', $value)); // Test the property exists $this->assertEquals(self::$test_instance->get('testSet1'), $value); // Test short set $this->assertTrue(self::$test_instance->set('testSet2', $value2, 3)); // Test the property exists $this->assertEquals(self::$test_instance->get('testSet2'), $value2); // Allow test2 to expire sleep(4); // Test the property has expired $this->assertNull(self::$test_instance->get('testSet2')); } public function testDelete() { } public function testDeleteAll() { } }libkohana3.1-php-3.1.5/modules/cache/tests/cache/SqliteTest.php0000644000000000000000000000164511771131762022723 0ustar rootrootmarkTestSkipped('SQLite PDO PHP Extension is not available'); } $this->cache(Cache::instance('sqlite')); } } // End Kohana_SqliteTestlibkohana3.1-php-3.1.5/modules/cache/tests/cache/WincacheTest.php0000644000000000000000000000164711771131762023205 0ustar rootrootmarkTestSkipped('Wincache PHP Extension is not available'); } $this->cache(Cache::instance('wincache')); } } // End Kohana_WincacheTestlibkohana3.1-php-3.1.5/modules/cache/tests/cache/arithmetic/0000755000000000000000000000000011771133710022227 5ustar rootrootlibkohana3.1-php-3.1.5/modules/cache/tests/cache/arithmetic/ApcTest.php0000644000000000000000000000357611771131762024323 0ustar rootrootmarkTestSkipped('APC PHP Extension is not available'); } if (ini_get('apc.enable_cli') != '1') { $this->markTestSkipped('Unable to test APC in CLI mode. To fix '. 'place "apc.enable_cli=1" in your php.ini file'); } $this->cache(Cache::instance('apc')); } /** * Tests the [Cache::set()] method, testing; * * - The value is cached * - The lifetime is respected * - The returned value type is as expected * - The default not-found value is respected * * This test doesn't test the TTL as there is a known bug/feature * in APC that prevents the same request from killing cache on timeout. * * @link http://pecl.php.net/bugs/bug.php?id=16814 * * @dataProvider provider_set_get * * @param array data * @param mixed expected * @return void */ public function test_set_get(array $data, $expected) { if ($data['wait'] !== FALSE) { $this->markTestSkipped('Unable to perform TTL test in CLI, see: '. 'http://pecl.php.net/bugs/bug.php?id=16814 for more info!'); } parent::test_set_get($data, $expected); } } // End Kohana_ApcTestlibkohana3.1-php-3.1.5/modules/cache/tests/cache/arithmetic/CacheArithmeticMethods.php0000644000000000000000000000515011771131762027307 0ustar rootrootcache(); if ($cache instanceof Cache) { $cache->delete_all(); } } /** * Provider for test_increment * * @return array */ public function provider_increment() { return array( array( 0, array( 'id' => 'increment_test_1', 'step' => 1 ), 1 ), array( 1, array( 'id' => 'increment_test_2', 'step' => 1 ), 2 ), array( 5, array( 'id' => 'increment_test_3', 'step' => 5 ), 10 ), array( NULL, array( 'id' => 'increment_test_4', 'step' => 1 ), FALSE ), ); } /** * Test for [Cache_Arithmetic::increment()] * * @dataProvider provider_increment * * @param integer start state * @param array increment arguments * @return void */ public function test_increment( $start_state = NULL, array $inc_args, $expected) { $cache = $this->cache(); if ($start_state !== NULL) { $cache->set($inc_args['id'], $start_state, 0); } $this->assertSame( $expected, $cache->increment( $inc_args['id'], $inc_args['step'] ) ); } /** * Provider for test_decrement * * @return array */ public function provider_decrement() { return array( array( 10, array( 'id' => 'decrement_test_1', 'step' => 1 ), 9 ), array( 10, array( 'id' => 'decrement_test_2', 'step' => 2 ), 8 ), array( 50, array( 'id' => 'decrement_test_3', 'step' => 5 ), 45 ), array( NULL, array( 'id' => 'decrement_test_4', 'step' => 1 ), FALSE ), ); } /** * Test for [Cache_Arithmetic::decrement()] * * @dataProvider provider_decrement * * @param integer start state * @param array decrement arguments * @return void */ public function test_decrement( $start_state = NULL, array $dec_args, $expected) { $cache = $this->cache(); if ($start_state !== NULL) { $cache->set($dec_args['id'], $start_state, 0); } $this->assertSame( $expected, $cache->decrement( $dec_args['id'], $dec_args['step'] ) ); } } // End Kohana_CacheArithmeticMethodsTest libkohana3.1-php-3.1.5/modules/cache/tests/cache/arithmetic/MemcacheTest.php0000644000000000000000000000457111771131762025316 0ustar rootrootmarkTestSkipped('Memcache PHP Extension is not available'); } if ( ! $config = Kohana::$config->load('cache')->memcache) { $this->markTestSkipped('Unable to load Memcache configuration'); } $memcache = new Memcache; if ( ! $memcache->connect($config['servers'][0]['host'], $config['servers'][0]['port'])) { $this->markTestSkipped('Unable to connect to memcache server @ '. $config['servers'][0]['host'].':'. $config['servers'][0]['port']); } if ($memcache->getVersion() === FALSE) { $this->markTestSkipped('Memcache server @ '. $config['servers'][0]['host'].':'. $config['servers'][0]['port']. ' not responding!'); } unset($memcache); $this->cache(Cache::instance('memcache')); } /** * Tests that multiple values set with Memcache do not cause unexpected * results. For accurate results, this should be run with a memcache * configuration that includes multiple servers. * * This is to test #4110 * * @link http://dev.kohanaframework.org/issues/4110 * @return void */ public function test_multiple_set() { $cache = $this->cache(); $id_set = 'set_id'; $ttl = 300; $data = array( 'foobar', 0, 1.0, new stdClass, array('foo', 'bar' => 1), TRUE, NULL, FALSE ); $previous_set = $cache->get($id_set, NULL); foreach ($data as $value) { // Use Equals over Sames as Objects will not be equal $this->assertEquals($previous_set, $cache->get($id_set, NULL)); $cache->set($id_set, $value, $ttl); $previous_set = $value; } } } // End Kohana_CacheArithmeticMemcacheTestlibkohana3.1-php-3.1.5/modules/cache/tests/phpunit.xml0000644000000000000000000000107311771117076021254 0ustar rootroot cache/ libkohana3.1-php-3.1.5/modules/codebench/0000755000000000000000000000000011771133710016540 5ustar rootrootlibkohana3.1-php-3.1.5/modules/codebench/classes/0000755000000000000000000000000011771133710020175 5ustar rootrootlibkohana3.1-php-3.1.5/modules/codebench/classes/bench/0000755000000000000000000000000011771133710021254 5ustar rootrootlibkohana3.1-php-3.1.5/modules/codebench/classes/bench/arrcallback.php0000644000000000000000000000314311772106744024237 0ustar rootroot */ class Bench_ArrCallback extends Codebench { public $description = 'Parsing command[param,param] strings in Arr::callback(): http://github.com/shadowhand/kohana/commit/c3aaae849164bf92a486e29e736a265b350cb4da#L0R127'; public $loops = 10000; public $subjects = array ( // Valid callback strings 'foo', 'foo::bar', 'foo[apple,orange]', 'foo::bar[apple,orange]', '[apple,orange]', // no command, only params 'foo[[apple],[orange]]', // params with brackets inside // Invalid callback strings 'foo[apple,orange', // no closing bracket ); public function bench_shadowhand($subject) { // The original regex we're trying to optimize if (preg_match('/([^\[]*+)\[(.*)\]/', $subject, $match)) return $match; } public function bench_geert_regex_1($subject) { // Added ^ and $ around the whole pattern if (preg_match('/^([^\[]*+)\[(.*)\]$/', $subject, $matches)) return $matches; } public function bench_geert_regex_2($subject) { // A rather experimental approach using \K which requires PCRE 7.2 ~ PHP 5.2.4 // Note: $matches[0] = params, $matches[1] = command if (preg_match('/^([^\[]*+)\[\K.*(?=\]$)/', $subject, $matches)) return $matches; } public function bench_geert_str($subject) { // A native string function approach which beats all the regexes if (strpos($subject, '[') !== FALSE AND substr($subject, -1) === ']') return explode('[', substr($subject, 0, -1), 2); } }libkohana3.1-php-3.1.5/modules/codebench/classes/bench/autolinkemails.php0000644000000000000000000000410311772106744025014 0ustar rootroot */ class Bench_AutoLinkEmails extends Codebench { public $description = 'Fixing #2772, and comparing some possibilities.'; public $loops = 1000; public $subjects = array ( '
  • voorzitter@xxxx.com
  • vicevoorzitter@xxxx.com
', ); // The original function, with str_replace replaced by preg_replace. Looks clean. public function bench_match_all_loop($subject) { if (preg_match_all('~\b(?|58;)(?!\.)[-+_a-z0-9.]++(?|58;)(?!\.)[-+_a-z0-9.]++(?|58;)(?!\.)[-+_a-z0-9.]++(?|58;)(?!\.)[-+_a-z0-9.]++(? */ class Bench_DateSpan extends Codebench { public $description = 'Optimization for Date::span().'; public $loops = 1000; public $subjects = array(); public function __construct() { parent::__construct(); $this->subjects = array( time(), time() - Date::MONTH, time() - Date::YEAR, time() - Date::YEAR * 10, ); } // Original method public static function bench_span_original($remote, $local = NULL, $output = 'years,months,weeks,days,hours,minutes,seconds') { // Array with the output formats $output = preg_split('/[^a-z]+/', strtolower( (string) $output)); // Invalid output if (empty($output)) return FALSE; // Make the output values into keys extract(array_flip($output), EXTR_SKIP); if ($local === NULL) { // Calculate the span from the current time $local = time(); } // Calculate timespan (seconds) $timespan = abs($remote - $local); if (isset($years)) { $timespan -= Date::YEAR * ($years = (int) floor($timespan / Date::YEAR)); } if (isset($months)) { $timespan -= Date::MONTH * ($months = (int) floor($timespan / Date::MONTH)); } if (isset($weeks)) { $timespan -= Date::WEEK * ($weeks = (int) floor($timespan / Date::WEEK)); } if (isset($days)) { $timespan -= Date::DAY * ($days = (int) floor($timespan / Date::DAY)); } if (isset($hours)) { $timespan -= Date::HOUR * ($hours = (int) floor($timespan / Date::HOUR)); } if (isset($minutes)) { $timespan -= Date::MINUTE * ($minutes = (int) floor($timespan / Date::MINUTE)); } // Seconds ago, 1 if (isset($seconds)) { $seconds = $timespan; } // Remove the variables that cannot be accessed unset($timespan, $remote, $local); // Deny access to these variables $deny = array_flip(array('deny', 'key', 'difference', 'output')); // Return the difference $difference = array(); foreach ($output as $key) { if (isset($$key) AND ! isset($deny[$key])) { // Add requested key to the output $difference[$key] = $$key; } } // Invalid output formats string if (empty($difference)) return FALSE; // If only one output format was asked, don't put it in an array if (count($difference) === 1) return current($difference); // Return array return $difference; } // Using an array for the output public static function bench_span_use_array($remote, $local = NULL, $output = 'years,months,weeks,days,hours,minutes,seconds') { // Array with the output formats $output = preg_split('/[^a-z]+/', strtolower( (string) $output)); // Invalid output if (empty($output)) return FALSE; // Convert the list of outputs to an associative array $output = array_combine($output, array_fill(0, count($output), 0)); // Make the output values into keys extract(array_flip($output), EXTR_SKIP); if ($local === NULL) { // Calculate the span from the current time $local = time(); } // Calculate timespan (seconds) $timespan = abs($remote - $local); if (isset($output['years'])) { $timespan -= Date::YEAR * ($output['years'] = (int) floor($timespan / Date::YEAR)); } if (isset($output['months'])) { $timespan -= Date::MONTH * ($output['months'] = (int) floor($timespan / Date::MONTH)); } if (isset($output['weeks'])) { $timespan -= Date::WEEK * ($output['weeks'] = (int) floor($timespan / Date::WEEK)); } if (isset($output['days'])) { $timespan -= Date::DAY * ($output['days'] = (int) floor($timespan / Date::DAY)); } if (isset($output['hours'])) { $timespan -= Date::HOUR * ($output['hours'] = (int) floor($timespan / Date::HOUR)); } if (isset($output['minutes'])) { $timespan -= Date::MINUTE * ($output['minutes'] = (int) floor($timespan / Date::MINUTE)); } // Seconds ago, 1 if (isset($output['seconds'])) { $output['seconds'] = $timespan; } if (count($output) === 1) { // Only a single output was requested, return it return array_pop($output); } // Return array return $output; } }libkohana3.1-php-3.1.5/modules/codebench/classes/bench/explodelimit.php0000644000000000000000000000163711772106744024503 0ustar rootroot */ class Bench_ExplodeLimit extends Codebench { public $description = 'Having a look at the effect of adding a limit to the explode function.
http://stackoverflow.com/questions/1308149/how-to-get-a-part-of-url-between-4th-and-5th-slashes'; public $loops = 10000; public $subjects = array ( 'http://example.com/articles/123a/view', 'http://example.com/articles/123a/view/x/x/x/x/x', 'http://example.com/articles/123a/view/x/x/x/x/x/x/x/x/x/x/x/x/x/x/x/x/x/x', ); public function bench_explode_without_limit($subject) { $parts = explode('/', $subject); return $parts[4]; } public function bench_explode_with_limit($subject) { $parts = explode('/', $subject, 6); return $parts[4]; } }libkohana3.1-php-3.1.5/modules/codebench/classes/bench/gruberurl.php0000644000000000000000000000404111772106744024005 0ustar rootroot */ class Bench_GruberURL extends Codebench { public $description = 'Optimization for http://daringfireball.net/2009/11/liberal_regex_for_matching_urls'; public $loops = 10000; public $subjects = array ( 'http://foo.com/blah_blah', 'http://foo.com/blah_blah/', '(Something like http://foo.com/blah_blah)', 'http://foo.com/blah_blah_(wikipedia)', '(Something like http://foo.com/blah_blah_(wikipedia))', 'http://foo.com/blah_blah.', 'http://foo.com/blah_blah/.', '', '', 'http://foo.com/blah_blah,', 'http://www.example.com/wpstyle/?p=364.', 'http://✪df.ws/e7l', 'rdar://1234', 'rdar:/1234', 'x-yojimbo-item://6303E4C1-xxxx-45A6-AB9D-3A908F59AE0E', 'message://%3c330e7f8409726r6a4ba78dkf1fd71420c1bf6ff@mail.gmail.com%3e', 'http://➡.ws/䨹', 'www.➡.ws/䨹', 'http://example.com', 'Just a www.example.com link.', // To test the use of possessive quatifiers: 'httpppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp', ); public function bench_daringfireball($subject) { // Original regex by John Gruber preg_match('~\b(([\w-]+://?|www[.])[^\s()<>]+(?:\([\w\d]+\)|([^[:punct:]\s]|/)))~', $subject, $matches); return (empty($matches)) ? FALSE : $matches[0]; } public function bench_daringfireball_v2($subject) { // Removed outer capturing parentheses, made another pair non-capturing preg_match('~\b(?:[\w-]+://?|www[.])[^\s()<>]+(?:\([\w\d]+\)|(?:[^[:punct:]\s]|/))~', $subject, $matches); return (empty($matches)) ? FALSE : $matches[0]; } public function bench_daringfireball_v3($subject) { // Made quantifiers possessive where possible preg_match('~\b(?:[\w-]++://?+|www[.])[^\s()<>]+(?:\([\w\d]++\)|(?:[^[:punct:]\s]|/))~', $subject, $matches); return (empty($matches)) ? FALSE : $matches[0]; } }libkohana3.1-php-3.1.5/modules/codebench/classes/bench/ltrimdigits.php0000644000000000000000000000104711772106744024332 0ustar rootroot */ class Bench_LtrimDigits extends Codebench { public $description = 'Chopping off leading digits: regex vs ltrim.'; public $loops = 100000; public $subjects = array ( '123digits', 'no-digits', ); public function bench_regex($subject) { return preg_replace('/^\d+/', '', $subject); } public function bench_ltrim($subject) { return ltrim($subject, '0..9'); } }libkohana3.1-php-3.1.5/modules/codebench/classes/bench/mddobaseurl.php0000644000000000000000000000404011772106744024274 0ustar rootroot */ class Bench_MDDoBaseURL extends Codebench { public $description = 'Optimization for the doBaseURL() method of Kohana_Kodoc_Markdown for the Kohana Userguide.'; public $loops = 10000; public $subjects = array ( // Valid matches '[filesystem](about.filesystem)', '[filesystem](about.filesystem "Optional title")', '[same page link](#id)', '[object oriented](http://wikipedia.org/wiki/Object-Oriented_Programming)', // Invalid matches '![this is image syntax](about.filesystem)', '[filesystem](about.filesystem', ); public function bench_original($subject) { // The original regex contained a bug, which is fixed here for benchmarking purposes. // At the very start of the regex, (?!!) has been replace by (? */ class Bench_MDDoImageURL extends Codebench { public $description = 'Optimization for the doImageURL() method of Kohana_Kodoc_Markdown for the Kohana Userguide.'; public $loops = 10000; public $subjects = array ( // Valid matches '![Alt text](http://img.skitch.com/20091019-rud5mmqbf776jwua6hx9nm1n.png)', '![Alt text](https://img.skitch.com/20091019-rud5mmqbf776jwua6hx9nm1n.png)', '![Alt text](otherprotocol://image.png "Optional title")', '![Alt text](img/install.png "Optional title")', '![Alt text containing [square] brackets](img/install.png)', '![Empty src]()', // Invalid matches '![Alt text](img/install.png "No closing parenthesis"', ); public function bench_original($subject) { return preg_replace_callback('~!\[(.+?)\]\((\S*(?:\s*".+?")?)\)~', array($this, '_add_image_url_original'), $subject); } protected function _add_image_url_original($matches) { if ($matches[2] AND strpos($matches[2], '://') === FALSE) { // Add the base url to the link URL $matches[2] = 'http://BASE/'.$matches[2]; } // Recreate the link return "![{$matches[1]}]({$matches[2]})"; } public function bench_optimized_callback($subject) { // Moved the check for "://" to the regex, simplifying the callback function return preg_replace_callback('~!\[(.+?)\]\((?!\w++://)(\S*(?:\s*+".+?")?)\)~', array($this, '_add_image_url_optimized'), $subject); } protected function _add_image_url_optimized($matches) { // Add the base url to the link URL $matches[2] = 'http://BASE/'.$matches[2]; // Recreate the link return "![{$matches[1]}]({$matches[2]})"; } public function bench_callback_gone($subject) { // All the optimized callback was doing now, is prepend some text to the URL. // We don't need a callback for that, and that should be clearly faster. return preg_replace('~(!\[.+?\]\()(?!\w++://)(\S*(?:\s*+".+?")?\))~', '$1http://BASE/$2', $subject); } }libkohana3.1-php-3.1.5/modules/codebench/classes/bench/mddoincludeviews.php0000644000000000000000000000237211772106744025346 0ustar rootroot */ class Bench_MDDoIncludeViews extends Codebench { public $description = 'Optimization for the doIncludeViews() method of Kohana_Kodoc_Markdown for the Kohana Userguide.'; public $loops = 10000; public $subjects = array ( // Valid matches '{{one}} two {{three}}', '{{userguide/examples/hello_world_error}}', // Invalid matches '{}', '{{}}', '{{userguide/examples/hello_world_error}', '{{userguide/examples/hello_world_error }}', '{{userguide/examples/{{hello_world_error }}', ); public function bench_original($subject) { preg_match_all('/{{(\S+?)}}/m', $subject, $matches, PREG_SET_ORDER); return $matches; } public function bench_possessive($subject) { // Using a possessive character class // Removed useless /m modifier preg_match_all('/{{([^\s{}]++)}}/', $subject, $matches, PREG_SET_ORDER); return $matches; } public function bench_lookaround($subject) { // Using lookaround to move $mathes[1] into $matches[0] preg_match_all('/(?<={{)[^\s{}]++(?=}})/', $subject, $matches, PREG_SET_ORDER); return $matches; } }libkohana3.1-php-3.1.5/modules/codebench/classes/bench/stripnullbytes.php0000644000000000000000000000150111772106744025075 0ustar rootroot */ class Bench_StripNullBytes extends Codebench { public $description = 'String replacement comparisons related to #2676.'; public $loops = 1000; public $subjects = array ( "\0", "\0\0\0\0\0\0\0\0\0\0", "bla\0bla\0bla\0bla\0bla\0bla\0bla\0bla\0bla\0bla", "blablablablablablablablablablablablablablablabla", ); public function bench_str_replace($subject) { return str_replace("\0", '', $subject); } public function bench_strtr($subject) { return strtr($subject, array("\0" => '')); } public function bench_preg_replace($subject) { return preg_replace('~\0+~', '', $subject); } }libkohana3.1-php-3.1.5/modules/codebench/classes/bench/transliterate.php0000644000000000000000000000415311772106744024661 0ustar rootroot */ class Bench_Transliterate extends Codebench { public $description = 'Inspired by: http://forum.kohanaframework.org/comments.php?DiscussionID=6113'; public $loops = 10; public $subjects = array ( // ASCII 'a', 'b', 'c', 'd', '1', '2', '3', // Non-ASCII 'à', 'ô', 'ď', 'ḟ', 'ë', 'š', 'ơ', 'ß', 'ă', 'ř', 'ț', 'ň', 'ā', 'ķ', 'ŝ', 'ỳ', 'ņ', 'ĺ', 'ħ', 'ṗ', 'ó', 'ú', 'ě', 'é', 'ç', 'ẁ', 'ċ', 'õ', 'ṡ', 'ø', 'ģ', 'ŧ', 'ș', 'ė', 'ĉ', 'ś', 'î', 'ű', 'ć', 'ę', 'ŵ', 'ṫ', 'ū', 'č', 'ö', 'è', 'ŷ', 'ą', 'ł', 'ų', 'ů', 'ş', 'ğ', 'ļ', 'ƒ', 'ž', 'ẃ', 'ḃ', 'å', 'ì', 'ï', 'ḋ', 'ť', 'ŗ', 'ä', 'í', 'ŕ', 'ê', 'ü', 'ò', 'ē', 'ñ', 'ń', 'ĥ', 'ĝ', 'đ', 'ĵ', 'ÿ', 'ũ', 'ŭ', 'ư', 'ţ', 'ý', 'ő', 'â', 'ľ', 'ẅ', 'ż', 'ī', 'ã', 'ġ', 'ṁ', 'ō', 'ĩ', 'ù', 'į', 'ź', 'á', 'û', 'þ', 'ð', 'æ', 'µ', 'ĕ', 'ı', 'À', 'Ô', 'Ď', 'Ḟ', 'Ë', 'Š', 'Ơ', 'Ă', 'Ř', 'Ț', 'Ň', 'Ā', 'Ķ', 'Ĕ', 'Ŝ', 'Ỳ', 'Ņ', 'Ĺ', 'Ħ', 'Ṗ', 'Ó', 'Ú', 'Ě', 'É', 'Ç', 'Ẁ', 'Ċ', 'Õ', 'Ṡ', 'Ø', 'Ģ', 'Ŧ', 'Ș', 'Ė', 'Ĉ', 'Ś', 'Î', 'Ű', 'Ć', 'Ę', 'Ŵ', 'Ṫ', 'Ū', 'Č', 'Ö', 'È', 'Ŷ', 'Ą', 'Ł', 'Ų', 'Ů', 'Ş', 'Ğ', 'Ļ', 'Ƒ', 'Ž', 'Ẃ', 'Ḃ', 'Å', 'Ì', 'Ï', 'Ḋ', 'Ť', 'Ŗ', 'Ä', 'Í', 'Ŕ', 'Ê', 'Ü', 'Ò', 'Ē', 'Ñ', 'Ń', 'Ĥ', 'Ĝ', 'Đ', 'Ĵ', 'Ÿ', 'Ũ', 'Ŭ', 'Ư', 'Ţ', 'Ý', 'Ő', 'Â', 'Ľ', 'Ẅ', 'Ż', 'Ī', 'Ã', 'Ġ', 'Ṁ', 'Ō', 'Ĩ', 'Ù', 'Į', 'Ź', 'Á', 'Û', 'Þ', 'Ð', 'Æ', 'İ', ); public function bench_utf8($subject) { return UTF8::transliterate_to_ascii($subject); } public function bench_iconv($subject) { // Note: need to suppress errors on iconv because some chars trigger the following notice: // "Detected an illegal character in input string" return preg_replace('~[^-a-z0-9]+~i', '', @iconv('UTF-8', 'ASCII//TRANSLIT', $subject)); } }libkohana3.1-php-3.1.5/modules/codebench/classes/bench/urlsite.php0000644000000000000000000000574411772106744023476 0ustar rootroot */ class Bench_URLSite extends Codebench { public $description = 'http://dev.kohanaframework.org/issues/3110'; public $loops = 1000; public $subjects = array ( '', 'news', 'news/', '/news/', 'news/page/5', 'news/page:5', 'http://example.com/', 'http://example.com/hello', 'http://example.com:80/', 'http://user:pass@example.com/', ); public function __construct() { foreach ($this->subjects as $subject) { // Automatically create URIs with query string and/or fragment part appended $this->subjects[] = $subject.'?query=string'; $this->subjects[] = $subject.'#fragment'; $this->subjects[] = $subject.'?query=string#fragment'; } parent::__construct(); } public function bench_original($uri) { // Get the path from the URI $path = trim(parse_url($uri, PHP_URL_PATH), '/'); if ($query = parse_url($uri, PHP_URL_QUERY)) { $query = '?'.$query; } if ($fragment = parse_url($uri, PHP_URL_FRAGMENT)) { $fragment = '#'.$fragment; } return $path.$query.$fragment; } public function bench_explode($uri) { // Chop off possible scheme, host, port, user and pass parts $path = preg_replace('~^[-a-z0-9+.]++://[^/]++/?~', '', trim($uri, '/')); $fragment = ''; $explode = explode('#', $path, 2); if (isset($explode[1])) { $path = $explode[0]; $fragment = '#'.$explode[1]; } $query = ''; $explode = explode('?', $path, 2); if (isset($explode[1])) { $path = $explode[0]; $query = '?'.$explode[1]; } return $path.$query.$fragment; } public function bench_regex($uri) { preg_match('~^(?:[-a-z0-9+.]++://[^/]++/?)?([^?#]++)?(\?[^#]*+)?(#.*)?~', trim($uri, '/'), $matches); $path = Arr::get($matches, 1, ''); $query = Arr::get($matches, 2, ''); $fragment = Arr::get($matches, 3, ''); return $path.$query.$fragment; } public function bench_regex_without_arrget($uri) { preg_match('~^(?:[-a-z0-9+.]++://[^/]++/?)?([^?#]++)?(\?[^#]*+)?(#.*)?~', trim($uri, '/'), $matches); $path = isset($matches[1]) ? $matches[1] : ''; $query = isset($matches[2]) ? $matches[2] : ''; $fragment = isset($matches[3]) ? $matches[3] : ''; return $path.$query.$fragment; } // And then I thought, why do all the work of extracting the query and fragment parts and then reappending them? // Just leaving them alone should be fine, right? As a bonus we get a very nice speed boost. public function bench_less_is_more($uri) { // Chop off possible scheme, host, port, user and pass parts $path = preg_replace('~^[-a-z0-9+.]++://[^/]++/?~', '', trim($uri, '/')); return $path; } public function bench_less_is_more_with_strpos_optimization($uri) { if (strpos($uri, '://') !== FALSE) { // Chop off possible scheme, host, port, user and pass parts $uri = preg_replace('~^[-a-z0-9+.]++://[^/]++/?~', '', trim($uri, '/')); } return $uri; } }libkohana3.1-php-3.1.5/modules/codebench/classes/bench/userfuncarray.php0000644000000000000000000000221111772106744024662 0ustar rootroot */ class Bench_UserFuncArray extends Codebench { public $description = 'Testing the speed difference of using call_user_func_array compared to counting args and doing manual calls.'; public $loops = 100000; public $subjects = array ( // Argument sets array(), array('one'), array('one', 'two'), array('one', 'two', 'three'), ); public function bench_count_args($args) { $name = 'callme'; switch (count($args)) { case 1: $this->$name($args[0]); break; case 2: $this->$name($args[0], $args[1]); break; case 3: $this->$name($args[0], $args[1], $args[2]); break; case 4: $this->$name($args[0], $args[1], $args[2], $args[3]); break; default: call_user_func_array(array($this, $name), $args); break; } } public function bench_direct_call($args) { $name = 'callme'; call_user_func_array(array($this, $name), $args); } protected function callme() { return count(func_get_args()); } }libkohana3.1-php-3.1.5/modules/codebench/classes/bench/validcolor.php0000644000000000000000000000612311772106744024135 0ustar rootroot */ class Bench_ValidColor extends Codebench { public $description = 'Optimization for Validate::color(). See: http://forum.kohanaphp.com/comments.php?DiscussionID=2192. Note that the methods with an _invalid suffix contain flawed regexes and should be completely discarded. I left them in here for educational purposes, and to remind myself to think harder and test more thoroughly. It can\'t be that I only found out so late in the game. For the regex explanation have a look at the forum topic mentioned earlier.'; public $loops = 10000; public $subjects = array ( // Valid colors 'aaA', '123', '000000', '#123456', '#abcdef', // Invalid colors 'ggg', '1234', '#1234567', "#000\n", '}§è!çà%$z', ); // Note that I added the D modifier to corey's regexes. We need to match exactly // the same if we want the benchmarks to be of any value. public function bench_corey_regex_1_invalid($subject) { return (bool) preg_match('/^#?([0-9a-f]{1,2}){3}$/iD', $subject); } public function bench_corey_regex_2($subject) { return (bool) preg_match('/^#?([0-9a-f]){3}(([0-9a-f]){3})?$/iD', $subject); } // Optimized corey_regex_1 // Using non-capturing parentheses and a possessive interval public function bench_geert_regex_1a_invalid($subject) { return (bool) preg_match('/^#?(?:[0-9a-f]{1,2}+){3}$/iD', $subject); } // Optimized corey_regex_2 // Removed useless parentheses, made the remaining ones non-capturing public function bench_geert_regex_2a($subject) { return (bool) preg_match('/^#?[0-9a-f]{3}(?:[0-9a-f]{3})?$/iD', $subject); } // Optimized geert_regex_1a // Possessive "#" public function bench_geert_regex_1b_invalid($subject) { return (bool) preg_match('/^#?+(?:[0-9a-f]{1,2}+){3}$/iD', $subject); } // Optimized geert_regex_2a // Possessive "#" public function bench_geert_regex_2b($subject) { return (bool) preg_match('/^#?+[0-9a-f]{3}(?:[0-9a-f]{3})?$/iD', $subject); } // Using \z instead of $ public function bench_salathe_regex_1($subject) { return (bool) preg_match('/^#?+[0-9a-f]{3}(?:[0-9a-f]{3})?\z/i', $subject); } // Using \A instead of ^ public function bench_salathe_regex_2($subject) { return (bool) preg_match('/\A#?+[0-9a-f]{3}(?:[0-9a-f]{3})?\z/i', $subject); } // A solution without regex public function bench_geert_str($subject) { if ($subject[0] === '#') { $subject = substr($subject, 1); } $strlen = strlen($subject); return (($strlen === 3 OR $strlen === 6) AND ctype_xdigit($subject)); } // An ugly, but fast, solution without regex public function bench_salathe_str($subject) { if ($subject[0] === '#') { $subject = substr($subject, 1); } // TRUE if: // 1. $subject is 6 or 3 chars long // 2. $subject contains only hexadecimal digits return (((isset($subject[5]) AND ! isset($subject[6])) OR (isset($subject[2]) AND ! isset($subject[3]))) AND ctype_xdigit($subject)); } }libkohana3.1-php-3.1.5/modules/codebench/classes/bench/validurl.php0000644000000000000000000000431711772106744023624 0ustar rootroot */ class Bench_ValidURL extends Codebench { public $description = 'filter_var vs regex: http://dev.kohanaframework.org/issues/2847'; public $loops = 1000; public $subjects = array ( // Valid 'http://google.com', 'http://google.com/', 'http://google.com/?q=abc', 'http://google.com/#hash', 'http://localhost', 'http://hello-world.pl', 'http://hello--world.pl', 'http://h.e.l.l.0.pl', 'http://server.tld/get/info', 'http://127.0.0.1', 'http://127.0.0.1:80', 'http://user@127.0.0.1', 'http://user:pass@127.0.0.1', 'ftp://my.server.com', 'rss+xml://rss.example.com', // Invalid 'http://google.2com', 'http://google.com?q=abc', 'http://google.com#hash', 'http://hello-.pl', 'http://hel.-lo.world.pl', 'http://ww£.google.com', 'http://127.0.0.1234', 'http://127.0.0.1.1', 'http://user:@127.0.0.1', "http://finalnewline.com\n", ); public function bench_filter_var($url) { return (bool) filter_var($url, FILTER_VALIDATE_URL, FILTER_FLAG_HOST_REQUIRED); } public function bench_regex($url) { // Based on http://www.apps.ietf.org/rfc/rfc1738.html#sec-5 if ( ! preg_match( '~^ # scheme [-a-z0-9+.]++:// # username:password (optional) (?: [-a-z0-9$_.+!*\'(),;?&=%]++ # username (?::[-a-z0-9$_.+!*\'(),;?&=%]++)? # password (optional) @ )? (?: # ip address \d{1,3}+(?:\.\d{1,3}+){3}+ | # or # hostname (captured) ( (?!-)[-a-z0-9]{1,63}+(? 253) return FALSE; // An extra check for the top level domain // It must start with a letter $tld = ltrim(substr($matches[1], (int) strrpos($matches[1], '.')), '.'); return ctype_alpha($tld[0]); } }libkohana3.1-php-3.1.5/modules/codebench/classes/codebench.php0000644000000000000000000000015111772106744022625 0ustar rootrootrequest->redirect('codebench/'.trim($_POST['class'])); } // Pass the class name on to the view $this->template->class = (string) $class; // Try to load the class, then run it if (Kohana::auto_load($class) === TRUE) { $codebench = new $class; $this->template->codebench = $codebench->run(); } } } libkohana3.1-php-3.1.5/modules/codebench/classes/kohana/0000755000000000000000000000000011771133710021436 5ustar rootrootlibkohana3.1-php-3.1.5/modules/codebench/classes/kohana/codebench.php0000644000000000000000000001737211772106744024103 0ustar rootroot 'A', 150 => 'B', 200 => 'C', 300 => 'D', 500 => 'E', 'default' => 'F', ); /** * Constructor. * * @return void */ public function __construct() { // Set the maximum execution time set_time_limit(Kohana::config('codebench')->max_execution_time); } /** * Runs Codebench on the extending class. * * @return array benchmark output */ public function run() { // Array of all methods to loop over $methods = array_filter(get_class_methods($this), array($this, '_method_filter')); // Make sure the benchmark runs at least once, // also if no subject data has been provided. if (empty($this->subjects)) { $this->subjects = array('NULL' => NULL); } // Initialize benchmark output $codebench = array ( 'class' => get_class($this), 'description' => $this->description, 'loops' => array ( 'base' => (int) $this->loops, 'total' => (int) $this->loops * count($this->subjects) * count($methods), ), 'subjects' => $this->subjects, 'benchmarks' => array(), ); // Benchmark each method foreach ($methods as $method) { // Initialize benchmark output for this method $codebench['benchmarks'][$method] = array('time' => 0, 'memory' => 0); // Using Reflection because simply calling $this->$method($subject) in the loop below // results in buggy benchmark times correlating to the length of the method name. $reflection = new ReflectionMethod(get_class($this), $method); // Benchmark each subject on each method foreach ($this->subjects as $subject_key => $subject) { // Prerun each method/subject combo before the actual benchmark loop. // This way relatively expensive initial processes won't be benchmarked, e.g. autoloading. // At the same time we capture the return here so we don't have to do that in the loop anymore. $return = $reflection->invoke($this, $subject); // Start the timer for one subject $token = Profiler::start('codebench', $method.$subject_key); // The heavy work for ($i = 0; $i < $this->loops; ++$i) { $reflection->invoke($this, $subject); } // Stop and read the timer $benchmark = Profiler::total($token); // Benchmark output specific to the current method and subject $codebench['benchmarks'][$method]['subjects'][$subject_key] = array ( 'return' => $return, 'time' => $benchmark[0], 'memory' => $benchmark[1], ); // Update method totals $codebench['benchmarks'][$method]['time'] += $benchmark[0]; $codebench['benchmarks'][$method]['memory'] += $benchmark[1]; } } // Initialize the fastest and slowest benchmarks for both methods and subjects, time and memory, // these values will be overwritten using min() and max() later on. // The 999999999 values look like a hack, I know, but they work, // unless your method runs for more than 31 years or consumes over 1GB of memory. $fastest_method = $fastest_subject = array('time' => 999999999, 'memory' => 999999999); $slowest_method = $slowest_subject = array('time' => 0, 'memory' => 0); // Find the fastest and slowest benchmarks, needed for the percentage calculations foreach ($methods as $method) { // Update the fastest and slowest method benchmarks $fastest_method['time'] = min($fastest_method['time'], $codebench['benchmarks'][$method]['time']); $fastest_method['memory'] = min($fastest_method['memory'], $codebench['benchmarks'][$method]['memory']); $slowest_method['time'] = max($slowest_method['time'], $codebench['benchmarks'][$method]['time']); $slowest_method['memory'] = max($slowest_method['memory'], $codebench['benchmarks'][$method]['memory']); foreach ($this->subjects as $subject_key => $subject) { // Update the fastest and slowest subject benchmarks $fastest_subject['time'] = min($fastest_subject['time'], $codebench['benchmarks'][$method]['subjects'][$subject_key]['time']); $fastest_subject['memory'] = min($fastest_subject['memory'], $codebench['benchmarks'][$method]['subjects'][$subject_key]['memory']); $slowest_subject['time'] = max($slowest_subject['time'], $codebench['benchmarks'][$method]['subjects'][$subject_key]['time']); $slowest_subject['memory'] = max($slowest_subject['memory'], $codebench['benchmarks'][$method]['subjects'][$subject_key]['memory']); } } // Percentage calculations for methods foreach ($codebench['benchmarks'] as & $method) { // Calculate percentage difference relative to fastest and slowest methods $method['percent']['fastest']['time'] = (empty($fastest_method['time'])) ? 0 : ($method['time'] / $fastest_method['time'] * 100); $method['percent']['fastest']['memory'] = (empty($fastest_method['memory'])) ? 0 : ($method['memory'] / $fastest_method['memory'] * 100); $method['percent']['slowest']['time'] = (empty($slowest_method['time'])) ? 0 : ($method['time'] / $slowest_method['time'] * 100); $method['percent']['slowest']['memory'] = (empty($slowest_method['memory'])) ? 0 : ($method['memory'] / $slowest_method['memory'] * 100); // Assign a grade for time and memory to each method $method['grade']['time'] = $this->_grade($method['percent']['fastest']['time']); $method['grade']['memory'] = $this->_grade($method['percent']['fastest']['memory']); // Percentage calculations for subjects foreach ($method['subjects'] as & $subject) { // Calculate percentage difference relative to fastest and slowest subjects for this method $subject['percent']['fastest']['time'] = (empty($fastest_subject['time'])) ? 0 : ($subject['time'] / $fastest_subject['time'] * 100); $subject['percent']['fastest']['memory'] = (empty($fastest_subject['memory'])) ? 0 : ($subject['memory'] / $fastest_subject['memory'] * 100); $subject['percent']['slowest']['time'] = (empty($slowest_subject['time'])) ? 0 : ($subject['time'] / $slowest_subject['time'] * 100); $subject['percent']['slowest']['memory'] = (empty($slowest_subject['memory'])) ? 0 : ($subject['memory'] / $slowest_subject['memory'] * 100); // Assign a grade letter for time and memory to each subject $subject['grade']['time'] = $this->_grade($subject['percent']['fastest']['time']); $subject['grade']['memory'] = $this->_grade($subject['percent']['fastest']['memory']); } } return $codebench; } /** * Callback for array_filter(). * Filters out all methods not to benchmark. * * @param string method name * @return boolean */ protected function _method_filter($method) { // Only benchmark methods with the "bench" prefix return (substr($method, 0, 5) === 'bench'); } /** * Returns the applicable grade letter for a score. * * @param integer|double score * @return string grade letter */ protected function _grade($score) { foreach ($this->grades as $max => $grade) { if ($max === 'default') continue; if ($score <= $max) return $grade; } return $this->grades['default']; } } libkohana3.1-php-3.1.5/modules/codebench/config/0000755000000000000000000000000011771133710020005 5ustar rootrootlibkohana3.1-php-3.1.5/modules/codebench/config/codebench.php0000644000000000000000000000054311772106744022442 0ustar rootroot 0, /** * Expand all benchmark details by default. */ 'expand_all' => FALSE, ); libkohana3.1-php-3.1.5/modules/codebench/config/userguide.php0000644000000000000000000000126211772106744022523 0ustar rootroot array( // This should be the path to this modules userguide pages, without the 'guide/'. Ex: '/guide/modulename/' would be 'modulename' 'codebench' => array( // Whether this modules userguide pages should be shown 'enabled' => TRUE, // The name that should show up on the userguide index page 'name' => 'Codebench', // A short description of this module, shown on the index page 'description' => 'Code benchmarking tool.', // Copyright message, shown in the footer for this module 'copyright' => '© 2008–2012 Kohana Team', ) ) );libkohana3.1-php-3.1.5/modules/codebench/guide/0000755000000000000000000000000011771133710017635 5ustar rootrootlibkohana3.1-php-3.1.5/modules/codebench/guide/codebench/0000755000000000000000000000000011771133710021547 5ustar rootrootlibkohana3.1-php-3.1.5/modules/codebench/guide/codebench/index.md0000644000000000000000000001171011771113724023203 0ustar rootroot# Using Codebench [!!] The contents of this page are taken (with some minor changes) from and are copyright Geert De Deckere. For a long time I have been using a quick-and-dirty `benchmark.php` file to optimize bits of PHP code, many times regex-related stuff. The file contained not much more than a [gettimeofday](http://php.net/gettimeofday) function wrapped around a `for` loop. It worked, albeit not very efficiently. Something more solid was needed. I set out to create a far more usable piece of software to aid in the everlasting quest to squeeze every millisecond out of those regular expressions. ## Codebench Goals ### Benchmark multiple regular expressions at once Being able to compare the speed of an arbitrary amount of regular expressions would be tremendously useful. In case you are wondering—yes, I had been writing down benchmark times for each regex, uncommenting them one by one. You get the idea. Those days should be gone forever now. ### Benchmark multiple subjects at once What gets overlooked too often when testing and optimizing regular expressions is the fact that speed can vastly differ depending on the subjects, also known as input or target strings. Just because your regular expression matches, say, a valid email address quickly, does not necessarily mean it will quickly realize when an invalid email is provided. I plan to write a follow-up article with hands-on regex examples to demonstrate this point. Anyway, Codebench allows you to create an array of subjects which will be passed to each benchmark. ### Make it flexible enough to work for all PCRE functions Initially I named the module “Regexbench”. I quickly realized, though, it would be flexible enough to benchmark all kinds of PHP code, hence the change to “Codebench”. While tools specifically built to help profiling PCRE functions, like [preg_match](http://php.net/preg_match) or [preg_replace](http://php.net/preg_replace), definitely have their use, more flexibility was needed here. You should be able to compare all kinds of constructions like combinations of PCRE functions and native PHP string functions. ### Create clean and portable benchmark cases Throwing valuable benchmark data away every time I needed to optimize another regular expression had to stop. A clean file containing the complete set of all regex variations to compare, together with the set of subjects to test them against, would be more than welcome. Moreover, it would be easy to exchange benchmark cases with others. ### Visualize the benchmarks Obviously providing a visual representation of the benchmark results, via simple graphs, would make interpreting them easier. Having not to think about Internet Explorer for once, made writing CSS a whole lot more easy and fun. It resulted in some fine graphs which are fully resizable. Below are two screenshots of Codebench in action. `Valid_Color` is a class made for benchmarking different ways to validate hexadecimal HTML color values, e.g. `#FFF`. If you are interested in the story behind the actual regular expressions, take a look at [this topic in the Kohana forums](http://forum.kohanaphp.com/comments.php?DiscussionID=2192). ![Benchmarking several ways to validate HTML color values](codebench_screenshot1.png) **Benchmarking seven ways to validate HTML color values** ![Collapsable results per subject for each method](codebench_screenshot2.png) **Collapsable results per subject for each method** ## Working with Codebench Codebench is included in Kohana 3, but if you need you [can download it](http://github.com/kohana/codebench/) from GitHub. Be sure Codebench is activated in your `application/bootstrap.php`. Creating your own benchmarks is just a matter of creating a class that extends the Codebench class. The class should go in `classes/bench` and the class name should have the `Bench_` prefix. Put the code parts you want to compare into separate methods. Be sure to prefix those methods with `bench_`, other methods will not be benchmarked. Glance at the files in `modules/codebench/classes/bench/` for more examples. Here is another short example with some extra explanations. // classes/bench/ltrimdigits.php class Bench_LtrimDigits extends Codebench { // Some optional explanatory comments about the benchmark file. // HTML allowed. URLs will be converted to links automatically. public $description = 'Chopping off leading digits: regex vs ltrim.'; // How many times to execute each method per subject. // Total loops = loops * number of methods * number of subjects public $loops = 100000; // The subjects to supply iteratively to your benchmark methods. public $subjects = array ( '123digits', 'no-digits', ); public function bench_regex($subject) { return preg_replace('/^\d+/', '', $subject); } public function bench_ltrim($subject) { return ltrim($subject, '0..9'); } } And the winner is… [ltrim](http://php.net/ltrim). Happy benchmarking!libkohana3.1-php-3.1.5/modules/codebench/guide/codebench/menu.md0000644000000000000000000000002011771113724023030 0ustar rootroot## [Codebench]()libkohana3.1-php-3.1.5/modules/codebench/init.php0000644000000000000000000000037311772106744020227 0ustar rootroot)') ->defaults(array( 'controller' => 'codebench', 'action' => 'index', 'class' => NULL)); libkohana3.1-php-3.1.5/modules/codebench/media/0000755000000000000000000000000011771133710017617 5ustar rootrootlibkohana3.1-php-3.1.5/modules/codebench/media/guide/0000755000000000000000000000000011771133710020714 5ustar rootrootlibkohana3.1-php-3.1.5/modules/codebench/media/guide/codebench/0000755000000000000000000000000011771133710022626 5ustar rootrootlibkohana3.1-php-3.1.5/modules/codebench/media/guide/codebench/codebench_screenshot1.png0000644000000000000000000003672311771113724027602 0ustar rootrootPNG  IHDRN4tEXtSoftwareAdobe ImageReadyqe<`PLTEJA#(ÚŖ<I͕Vydsټcq3 = IDATx읉8cM3jr* AWA A@ C!t:t:A/ z+Ϊ'P_}0NU /cDDAO/@F}`rw>@A?B9NC1{S~"\_o %@?u Te-SZ[ЯB !@J~g3 ]e2_9o:z=bc97@cLJSAfjhW~][~ I`[â?Q߰8NGB|>;0EKDT,HEO[o$j"(uґQɈS,zfbAo <_U<?,㫠wߒ>]i5tD,0ј*\3{\D ]n Ƣ7ֈa_tn`\1T+qVK=;t݃γl_Y̙z=T -q[tIQceH=3>׽(u4?}ŢƋ_tz?st7zX"#Qq>(Ntɼ ][t[="sb ]]G\^7v{|yi~r,zE+ﰧ\U0  ZtX'n.fz,?8f ҁEI`ht_x8SGע3t?qޒm.L:,ӁE/eVsЏB^gugo#x킙L@༪V@Y- e6~qed2:j{gЗ]C' =o[zX]y)qPx'fzpf|Q)^((?=CZ4'BK{YD+}:z0rLPzyŀ LN|c赱4:E8d/.= KaRZg ]zzr}tgu=u zzSQ}" k2zgYβ_E-ЯgOv=m0sow+Qwڛ1K#Z׏+ |./l-~oeX=Op*}2 x^;st3\B oчQjѣп zcfe sЯD*"st~Xt'A tk,WX7gk|U)]MwZt>rqTh<6NVwUYtGެz{>W^ &,Ir`A=\~skݏqG{LH/cz,:%ntvLmn +eC?W+?1۝IQSIig~ X\`ѡ%1?|џXjx,:3CYo>/nP,:aa!W,:ߢ`ץ,هE}ABҢ_2t?t:A A@ C!C!t:A A@ C@ C!t:A۠A[h4L\97A :8 & ! 'y^C8~tAO[o~>o|} jC?8ᛠ)gߙ~_xwPGvYe< 8~7Of[DO z=M%s/g?٧,_hх{С]qȝtnUw6iQh3ڱ͆~_^Sa3a}SgՀpϊoC1@nt}dG |>'C E>C:7f۲ {Ԣh6tq熝Ƥ-F }v#ފ8FyOsq7W|8W3vǍ6*N8ި:E3oC ;Xet-z4f3by!g]/z2&gi8A9փF֢{7f[w6ÅUL@,ŎijfAOQOG-yɛe^v6iLM=`Y3^˧/KCz<^Nffs&A?y9gAg[Y:'b@/-w͍XISX3@t/8j\ o*^l[KG%NEAώ|.@weΔXo:sOX \_K/YaO|<\iGy>bM}][gfN濍;Ld\;4;sug|·?5rlž>>G,yӞƬ]Ҷ5?g?q]\SEϛ|+֮"]r^T"H=Vx\go6kifwY2F k]- AlS!KOlxO2{+W}]z_3^Bs/]ixgwQM:tx*b<~矀A[:Hw| ? ]!zOtt:Ak.kUq  f9K`^p@F:=q CsI:[o=|Fx"~{հxzѰ;ygx#6fZ3C#=?^7-?4e= ?O~E^F>\^3-@2nM?ز={}2n}]%jf?1],F=1lggo-„lau=52E>YG4eOMu^C/xff=RLt%=m9{CG!Z=w].g+V]2*vՅ7L#.+(ݗ3{+qЫnm"sܩܼ`_X^/y8m2ũHF٘[X݈[΋| mm( a~e}Yuc6i+FU21L}KZ>3 4i7z{-#9W/iqMvf9#A]3?p7J.|d:sm@/vT4*tsvs}gga sv2ŪwtXguϙ'.r`~8hnq>y9gA/x3ijM 뾴7b g'mOa΋ 9{֫*u Tl,J<{آ݆S#fcA5+]u} h;2HgntiLwfr>i%4'V6Rzgy t}v< `SXݮ\6mƥ'ݡw=HM;?&??PL.9f;;ndL;bnngʮku7zK(]OܕEgbRk{?B(O k}Z^,OlxO2{/']ze_3^BCAw/"AW A u&<: AoG'C!]8H -4A -!C!t~tk.kA3 . .%? {|9t:A/9<߾3syC=Lߛo^ 7AߛZ?Gi^˚Zt5T%Y_ԗx^=0РS ^ C}Ha$w4L7}tP5JTsr(R_Ox#}fz~#< Θ /GA 5NTQF#J3"B۔0G&'dp%E<-qʮ՗Qct]?)m@?usqt8NiH1//t~G&ZWA7=O zЃt=4B0NVOpcDIs@OD=[GabY=q'Z}bp M MS9Ts>9u$H w_=4>ʟZtz}*=A0z%н֫^<Vl[t jKxMA-](d/y 4jy,YctxYS L @۽/T1;' 3=8uy.[h.h<Ь]eΚ'#ZGO͘F= MY zIShlFv-Į48lkqʮ5ZJvQ<ڥJzج/o<&O"N]^v . Aa&n?᭤^A@ 蟃B/u:AC@xn2c~tBA@: Ui -pA{ C@ C!X^wY^󯂎 f \0!\Kce? :U!hCL:@ ! 7{_:A&w>0ygaǨBakOSaMЭ$|W<  Y<*}[b5O'C{WR$Fo|qѬ@gO 4y/$TA'-A={vJC냮:~Ћ>Hq<2fYzK& 7PgVhϔT5T%YE}%<)Ad<%)Ymr䛠TvzڔsXCհ+MFVcTETL5|"zZ eW r>afz~#Ų: J3J6'2}ܼGA 5NTq89Ҍ6e?̑׉ t:k]/ f=evn~k~oz#5-Bw_=4>ʷA7%S@ދ$AWyA;oVtTK㪔Aà'KyT[o j(y.kjY]ݔX/rhB=G0=WͰ1dѓ RBue ZKV@0^b @۽/T1EPri1K1/1߽հ<}|^LwgkRēĚɸ'ѓe3k5xQGU4IЋ^JDcu9lí{)3,[lh]'vId^SvVڵ 6b(> ?v .xɇkxޮvYz}П3 'pC@RF/| CB@}!I-:E|tCA@: U&::t:A AOcy ey:`~3~%:0@[삻 z z ұ:A:n A:n At<_=Lto){o^ `MЭD|׊aC(o ~x{qn'@@&#@ @/ynig^/]?\C zPΊn01xAY3c3[t=@Ftu G1K#Ar|#P0c}hҼP5jK e}%<~jz|WzA^$0S`&ЛrξEab}x*J[Ќ9]ak9^]/'F+zz#< :~GDYu&uw0'0XuA ^즩ijk癞QOGAO`#%n25EY;Aw^E]WGXB:)(t13.]+0õXtz}*st@s[ƛRJ=64Ֆ8Z(qQxޡ.kjY]ݔX/ftQwΙ-i-K1ZJCǒ5'ytxYS3sv*fgd@KL!ANi뮋rV4[<}|p4qh֮ 2FgOkn`SOh'fLY[kn}p>"qЫJDcv4~tklm"Ѻ~OJd^SvV]= i Ïx"LԟD\ ٔ]v}B[LwIg# >sqX@ǓZ tw<MA[:Hw| ? ]!zOtt:Ak.kUq  f9K` t<tI:{CB=C/p @;Y<ճ 0 臡o~=<<1!:Y<*}[b5u&޳wŜHzС=f:|jA9t:(_z:G6d%²G@8~+tAvC/9_=brˀ^즩iU6@3ycJ QГe<\7m袬9xt]/tr~~WG̣eS<eГ%bf]@mW@`lkgonJէNq*z0*{֫*ۋo+׫A:W)<ͳAO>rC j(aڕM- нk%=,jzA/=G9z|EO^'VBue ZK֜YeM-YVn{Q_bv gzA/zvs߽R_^Wso9zx1Uqh֮ ԞFJ͟Okn'꣉udٌ){k ޭOhrC$zUKh.m5s%2go?e+v-Į4iPM8eךXoCˀuM5mթ̐M5OzZ~  .':t:AOj !%w7~ɷA hzs'AttF:Aoɿ:Ao)A A@ CX^wY^:._p 8_p , ?=~H#:@ !v @ 5$ HS =< ez4`7AB~PM>UA?g3j,g=q>ZI}7"A<*Χ~/g?ci>2{SZ@2egc:jfKDo6q*@'rs1^1@ ɞH}hhNُ@QMru_iw5\Ѵ.wn x3rc1MspT8u9%r쭣01ͬ i5n\*Q9Ġi*|} )2u$HdX-]]j tYVVy tm̭?.f @^Hy{Jq8|Y9 |i=zrɻvݯXp. fDĮywtܠ{9hWUδzN,/*es4ܢ'Lj7W@겦5 Vo zf˞yU f qL'z%,bLmղW,8^2蹷ɗ蠡5;=<3xs<]s߽N )! +ϼ9zxV|h Ԟ^V`&#ZGO!j<'z]Gu%^!䠧Qq74<\GWG+薝]H~Qg^S>q I@ lڦ Ðnn<&O/OMn H5N?fp)í q8 ,&q_w tto: v}.OWAù :A)A A@ C!t:t:A A@ C!!t:A A@ %@ _~ n mjˋz}qt~{@~J ״en: 8FPqV΂y3 ~p[tܓoz20ayXQ{΁:e1b8Ŏ# 6Ć#+qE/6Loơf䞼xaFF1b]"Op T LQ|gE3Gb%-*LD=!DV R a=oEߟddtLr+۟?O|NjO'yֆgIT3S|Cݵ}LBl y=y fۀ^z%.t<bfcz+A 1¨e&Xxfvg#&n¢n:e3neؗ4._УDSv O:n'4 f ]ϐ>zrxV4eX=KĿ6Х2 gH%]ۖn hD=E/-ܳ,w-:lzMAVS[~3E0A?-=9?h tnwWugjSw"I~ۥ+׽idmEMBZdOG tǮwr(p ,]ݼ@ߟhYk!A@ Cb׻I 4/S۷5=PnzwG"OD: G@+t:/͵;Mlŵr ;`{sC77~&`*^ZW^OTm$V%~Нu'V_tѴ~iͻw DžƵrgHݖ7Z֮ 7|}옝`'g)~нv_VrfS9kW,Vʼny|/iF۠6F(~eyjf U޲ۨ/DɆ+2V}@ɶZ' 澘9y⍉zFxIџbgXUJfՖxTOPE[ȣ?;ˁϚJ| Lhr[Qf7F9#[1'>Qz-u? = -k^LbXZn&qmg~G҆^;t~U054D7%+*A -!}-f({F>qpx>.He8[cXCPFxIџ+\Kݑmɏ>t&IdQ).5-Gɟ Q?.A#ʇb,:9ڢj.&ٌs=apwU=A6E t:A A@ C!C!t:Aۛn%ڝOݾtN5_l3iS'mEB\mqYS*D_ju(zK/R\->e ͞j?5Aπ?޶66M׻X+~40*B&;R&S۟~:z1й'a]'+װ5ɜgm"ڶɗYE=e[^;'/,Y2ےA-qrҧ)V5ۘ 8NKݩX2D=m,kuv"Lb2療}Q1u6#CzkN-\~mdw<[f A9ɏ+z<$֓ۅ"bT9 %$aDٜmCI{B:k܊)t`1Ƈ&%Q*g_= l%4ܾo 7SlvүbLlWGrqVe\.8L hg255{kNCk9f'{@mnE<@kixU='a1Wr츥 =M0Je#ϧ-@eV5!XNlsrjeu?NPNY|+gtrw.s ֍P~.<,z!J+Z/8pV[լ83L]Uݺz8I^~\ QY'(5Rl~2.6Qw{EɖMYv5}eVUvt6A餧꬚iWrKIz4}[\UK; )tاcSjUv]f1c tE(c(52Woaס`"Dgq,GsL'=),Jq\ xސjj}UvQRM0N.G˶J-ѩQ P2@sz@yе|7=,"s3)VZ^UwI'm}޷[o}!񓃞tii-?x)϶Au}kBf?϶AؽA AЋG~ |x4y~/ZGL'zQNk ߳q{k5Kq>ӟ~uO7v=*-x\ (tl cCwKG}ۻAG⬰þftß=,w-q$|Mz;؈ҾxmVz|W4?zC|O:z/ zj]bp&{C!t:A:A A@ g539sxIENDB`libkohana3.1-php-3.1.5/modules/codebench/media/guide/codebench/codebench_screenshot2.png0000644000000000000000000003240711771113724027576 0ustar rootrootPNG  IHDR|katEXtSoftwareAdobe ImageReadyqe<PLTEZJ(oưʙ<ƩOr&Fωr~>쎒ָՙRS>߲{ğt;{~v;|岹ҾŊ忇̽ˬ 'ϼlݙi3IDATx {7ck;*SKxJ/uNEDO먬u{֝b'!xPCCCCCCCCPr^X@t*l7zRp!}CХ8} :s=ڮit.;KA0:*N@)``tx+tr`t#~Jt foQik^-2¸厤;'>}/ oK>Kg@#F &==~ol2R7F?.{50:tFi&{Ԫݪ[d~+z3:UI F.R֖>u[m8S5ɫ|K WǾ[Cw|A޻ӇUUߗ p!Cg(7߷w`t|UD7intJs GzSg]j(&P~ルrkZ|!U))'z#%~(F7+w/nOy{7{y.])0oʏm'NvXЬw_>oΧI?Ǹ\ Y:shxFdܭgc}X5N]lrSUVWVcen] C_Ǔf9*[T9˛F7Gin6dtg>ޟ= iM{9R.[ nߤoCʥ䡺L_Zaѡ35xEm]]1Ǟ{ '{l2W]SߖǾ;kFi;'ߗ'z`B_H߭wN][[}}ХL[`E?5.9zQ7OV^K eLJ"~wyDPwWwB3Z\O[? Ou蜍n5ccGgޚU~uiw=14@[Ρ_>*}u,k]{ ߷JV )ww'os꘱/@y}IwyAQy]t'ØnIMw2SZRn@gv; }57ӦIV⌚arSk3f!S5ҕ}rǧ"urJA|w]=9Yjs={ִq(/+0·\~jgL'4*?U%w5mj󫠤<4 M4iMInБH\b'zԨʔ 0p=2=ӸoMy"o+'L:qiXK'2K ib*L_g&fC9ΧԳBrN~3E"O|{\'Z!EC3:5W"sbT̯eA5=r<78Cc9%)Q]Y6M+ xCi+duG:A?ݖCfͫ\vmEsu%?';La9{Uc0hsW2 6c۱z3Ud4슌ޡy9˸UoXfx6:dӃ,zəF?zpS6> xCBwGo3gUA{UE|QOib-X3 zV큚˜ɓ٭i\Й|I7%7J&˂ԎyT<] &Cm޳謗^ 7qɪ>3-bX|)󹘛~/oB1NP⭛)^]w-qe["Jdv܂+_\OA>Rl ϳJ?4:ڧ8 sbu+v~5[5㞻|{vAf܎O6vAmt`t`t`t`t`tblAP֌ CCCCC]f*&0:@y Cta$l:k=墪d\5޸zBѣ4Je GE*JÎ*؋k ӺXkn=CIo}X7!oX,m{Z,F_s{=^ʻY&{uhi{05pkgY^IQ+SGUP=4lR-Öe$nV&5)ˇF3{M?k41dܩ`bXe3gQ:CEr@N~ g6;άfoU~ k+:uli6RtwiSb.ucit>ggһ^^yz?T;ūu fuGkt韧iױi9e~+eW]syUw)+_3G5Nvu$'ew=]xsh8o<W#.257˫gEʙ>_xy 5Y!xg\C[̆Fvvvĝq'&x7ݣ"2]_LNMxk!C-󫫫?UbAIWa)8d}T!:yCCCCBt!BF t!!!!!ѡb+^vxxz ϣ^ ,B{BZ%޸:dF5v@\:OYm[:+&dJ^$dQYѿvD)EC vW?ˉRx>,N:oX $Y{],2VKE׼3 c@a&LM\ 'n@S%{< <H\;d<Ȣ,^To'[!+w YYdѧ1 5 Eܩo,h"{3!'kWE8V9<:׸`-Y\OAM`1MDwIpVŧ "w,{-x]d1o *zOiQɒh3d1I}zd> ('Vq.! @lP( 哔ă,ѣ 9yEAMOŴDPq9?KUC; 4,A (&/UIȢbgݵ?eE|,Vdzij<,"wGnκ*bF\D+j9a G1nf?(L7d YK=~,z,:&m0Y%dQ^I]it7dԯA$;fʰi9Mwdѿ5o,pnEBxC-go\bcxi%zN ^" p9S CAAAA7 AЅu F F F F F xJkP}N/f0:[qH*)%d{YŇ,BdF5"!kC.,aZ(e5$d1b))@a]%QJ!Ej*7-&dT+)4욤H51ddFYHC{Mo{han@@l2 }YL,h%=듫(PdSa"[V}efуE),zŇj( 5#@n9`ڠFCg9E7.dD+)dQTy?d1FeGA]ѝtnY YgtxSFvʊE? z=_I䣇@^iqg (6دj?E]B,&z]m8Sl_6slWLόQaxaa% T*}QB3WQtvdQ(}oȢ=Y|,6YLid !J9uB H}vEMAσ,+ I5&mpY)5,rF?d;FE+>) bfې=6dydQ7GEZ6ǀ,&6qϘHȢ83zfhEao@2h7{Us(dQnd= H?ט b6Lm+d5+,{!1ހ,Aؤ͐|tU54/G@ r"o5d1F A%M/@cv,nQ_l,.rPw@i=Y݃dQLҭ_`j%dѓEDϺkʊ68Y(Ȣ+!MwRhE(a dBR|/zi`Ȣ%}oȢzYYLinhAKG,zG ;YTe$]16Bv>[ pX|Ȣ{|Ȣwg\mǁ,dBZΰ,6Vv_%1U< A!F F F F ݅ BACCCCCCGEz[F" 7QE5jCi %eDž,z[N| W0+}?b+-̢8_AUKbDϺkٚ >Bg ͈l܇UeJ>"d;Fطli6;wx2_6sAf:;n!n"gdqd1F fł,/#lYTa3V! _.Ȣpb E[C/θ(O~)dKj9Ϛ8_%^-FωtY*S pCCCCBt!BF t!!!!!ѡ`+&D-v0:[qHҪ.%d:Y]|dWshg>M,GnаTuhRo YԶIEa7ېt+( ю Y [-8YB2^TW=.~MR E׼3 kXǀn 99@O ݀,,'2dQ;ds.(4b<Ȣ_ r8YrXyQ9 3%蛴vȢޚ旐Er% Y~'=,lPK"h:~t !!]ɢdž,>o, Y7ZFZveFr/b2p^Œ.hj9Yt%O͙3G,~CVtWMxh FF,~4&+ӿ5RkOἄumpVŧ bǕ1R^08yZc@?+U=t튌72G5rm,棳"FT}dQNƉFo՗T7>۞@GEWDE#C?!5='Ţ}?^:pd~-;2YdqODvBWE1I~MetIW>A}W3mU ̞YwOY!Yt87~OόQ阐Ϟu k^Wg1ja,ٱNجnȢ]k"IIuTIP"NtE[Af Y Y%]${5:_:9 (M1 z$dQv犼8wI3Eo@?mί/,dBZΰ,6Vv_odq; ^(dqP0d=;ZK JF,x;mcIg0"chYѳg&Ud0b1-5ө~d Y]غ2zM,1S2.iů d:떻 dQ쯅,-ԏ MKӦپ3ːŽ3#w3zk^d& (Y\7:mO=6 YTtŊ Y<^kC{VF,R6>T7!WZEdq,J χ2:3m<7tI謻)`#dq Yp Qav¾eKd1xҧXiSqg,c3EG8ZݐE}7d3z(UάEƤ37΂ѓ}=@ Y  G,/쮼b.8SQVC!^}mxdQ81dq YkY| %@.θ@[Ⴒ*Z3 ' D4Y&@WX=JY:V&dJKBIEa7ېt+(7d|+vkbvTbqHzS%7XY/^߆Z,^>ȼ =FgPA6YLY Ofdz,j!' exvR ˋuߟ 퀌sMk\e=>dȢ YߺCIluett,%]Xt͊,A*ސœ2zxt[]Uɖt d-E@Hᷗqҳ062)3_s@fJ7iEҭ9`Er =d;N; v,W%ę,zl6Ȣ>nZE;2?9i8/aFA;5E?>kqf>3zH؝.<33 ö @=kY\DB?]\UF) k֖y 2 OŎpgt+Ky" e^Y<{HlaZU܋H$ٞܒ;37G@ r"om6Ndt6zť=68 , GܻxRF O̺mWas`&ْBi|YdqODvBWE1I~MetIW>A}W3mU ̞YwOY!Yt87~_gƨxbaa^[Z ]Y䰳>mɖdC )[,`FtŚ<5?f(YA7Yd+0b#Z, YE_AOθ`RY,UEU_,^PE?" Uݗ%*A)`t`t`t`t].D;A0:A0:A0:A0:A0:tl%)Ջ<% C1u)$J]B; EKT,:* {Y]@/) 0׮(h 8䞐Ӫаk(2Oɥ1ymwBf].rϩVRL(%Y?[# z݀,ֵ$m0{ϖ,cCOJÞ|Fl@YR1BW(^ѥKAE),zѐŌ,YL&gjY YTDL Y<* }ʍܮ:=LahdZZdQO7,&hŒ ,'q!'U%arZMIZWM8lq b.hlm9c@Uu 6 .[|>hlBIg,Ci&&m1gv́Eh] HȢȢ`K`o+ȢYs$ fǂ,zc3,&- Y;?:<jat/xYY1zm0 (]Ҿ-sNF]}*?D)0^4=ؼJ{=h|,WEi-E~qbgݵlM !3Yf-i%cTY>YxLA0:A0:A0:A0:A.A"dtB     :.bª OAIpzѡغ[GVmq.!:/%Ӈ,F.YѿtM%qdq#s7zhض:*h#m EkWIRA;k( 7Z Gа*^4SjHsbcO F׼E+=N`~bXd1e)nZ1 -F)k,&6qϘ";3͙3_YsCݱ,@ET}dQNm,bҍJm,{9<1r_YYcސEo7d3zdq%A7Y%dQ00u̕ xj Ye5GF/Av]EoY,K,.N3BxC-gXm{BK/7KdNcxLCAAAA7Av*p:]+F/, |at.R0:(_>JFIENDB`libkohana3.1-php-3.1.5/modules/codebench/views/0000755000000000000000000000000011771133710017675 5ustar rootrootlibkohana3.1-php-3.1.5/modules/codebench/views/codebench.php0000644000000000000000000002433111772106744022333 0ustar rootroot <?php if ($class !== ''): ?> <?php echo $class, ' · ' ?> <?php endif; ?>Codebench

Library not found No methods found to benchmark

Remember to prefix the methods you want to benchmark with “bench”.
You might also want to overwrite Codebench->method_filter().

    $benchmark) { ?>
  • +%

    $subject) { ?>
    Benchmarks per subject for
    subject → return s
    [] → () s
Raw output:', Kohana::debug($codebench) ?> libkohana3.1-php-3.1.5/modules/database/0000755000000000000000000000000011771133710016372 5ustar rootrootlibkohana3.1-php-3.1.5/modules/database/classes/0000755000000000000000000000000011771133710020027 5ustar rootrootlibkohana3.1-php-3.1.5/modules/database/classes/config/0000755000000000000000000000000011771133710021274 5ustar rootrootlibkohana3.1-php-3.1.5/modules/database/classes/config/database/0000755000000000000000000000000011771133710023040 5ustar rootrootlibkohana3.1-php-3.1.5/modules/database/classes/config/database/reader.php0000644000000000000000000000060311771131764025021 0ustar rootroot_db_instance = $config['instance']; } if (isset($config['table_name'])) { $this->_table_name = $config['table_name']; } } /** * Tries to load the specificed configuration group * * Returns FALSE if group does not exist or an array if it does * * @param string $group Configuration group * @return boolean|array */ public function load($group) { $query = DB::select('config_key', 'config_value') ->from($this->_table_name) ->where('group_name', '=', $group) ->execute($this->_db_instance); return count($query) ? array_map('unserialize', $query->as_array('config_key', 'config_value')) : FALSE; } } libkohana3.1-php-3.1.5/modules/database/classes/kohana/config/database/writer.php0000644000000000000000000000533711771131764026345 0ustar rootroot_loaded_keys[$group] = array_combine(array_keys($config), array_keys($config)); } return $config; } /** * Writes the passed config for $group * * Returns chainable instance on success or throws * Kohana_Config_Exception on failure * * @param string $group The config group * @param string $key The config key to write to * @param array $config The configuration to write * @return boolean */ public function write($group, $key, $config) { $config = serialize($config); // Check to see if we've loaded the config from the table already if (isset($this->_loaded_keys[$group][$key])) { $this->_update($group, $key, $config); } else { // Attempt to run an insert query // This may fail if the config key already exists in the table // and we don't know about it try { $this->_insert($group, $key, $config); } catch (Database_Exception $e) { // Attempt to run an update instead $this->_update($group, $key, $config); } } return TRUE; } /** * Insert the config values into the table * * @param string $group The config group * @param string $key The config key to write to * @param array $config The serialized configuration to write * @return boolean */ protected function _insert($group, $key, $config) { DB::insert($this->_table_name, array('group_name', 'config_key', 'config_value')) ->values(array($group, $key, $config)) ->execute($this->_db_instance); return $this; } /** * Update the config values in the table * * @param string $group The config group * @param string $key The config key to write to * @param array $config The serialized configuration to write * @return boolean */ protected function _update($group, $key, $config) { DB::update($this->_table_name) ->set(array('config_value' => $config)) ->where('group_name', '=', $group) ->where('config_key', '=', $key) ->execute($this->_db_instance); return $this; } } libkohana3.1-php-3.1.5/modules/database/classes/kohana/config/database.php0000644000000000000000000000515611772106746025033 0ustar rootroot_database_instance = $config['instance']; } elseif ($this->_database_instance === NULL) { $this->_database_instance = Database::$default; } if (isset($config['table'])) { $this->_database_table = $config['table']; } parent::__construct(); } /** * Query the configuration table for all values for this group and * unserialize each of the values. * * @param string $group group name * @param array $config configuration array * @return $this clone of the current object */ public function load($group, array $config = NULL) { if ($config === NULL AND $group !== 'database') { // Load all of the configuration values for this group $query = DB::select('config_key', 'config_value') ->from($this->_database_table) ->where('group_name', '=', $group) ->execute($this->_database_instance); if (count($query) > 0) { // Unserialize the configuration values $config = array_map('unserialize', $query->as_array('config_key', 'config_value')); } } return parent::load($group, $config); } /** * Overload setting offsets to insert or update the database values as * changes occur. * * @param string $key array key * @param mixed $value new value * @return mixed */ public function offsetSet($key, $value) { if ( ! $this->offsetExists($key)) { // Insert a new value DB::insert($this->_database_table, array('group_name', 'config_key', 'config_value')) ->values(array($this->_configuration_group, $key, serialize($value))) ->execute($this->_database_instance); } elseif ($this->offsetGet($key) !== $value) { // Update the value DB::update($this->_database_table) ->value('config_value', serialize($value)) ->where('group_name', '=', $this->_configuration_group) ->where('config_key', '=', $key) ->execute($this->_database_instance); } return parent::offsetSet($key, $value); } } // End Kohana_Config_Database libkohana3.1-php-3.1.5/modules/database/classes/kohana/database/0000755000000000000000000000000011771133710023034 5ustar rootrootlibkohana3.1-php-3.1.5/modules/database/classes/kohana/database/exception.php0000644000000000000000000000050011772106746025550 0ustar rootroot_value = $value; } /** * Get the expression value as a string. * * $sql = $expression->value(); * * @return string */ public function value() { return (string) $this->_value; } /** * Return the value of the expression as a string. * * echo $expression; * * @return string * @uses Database_Expression::value */ public function __toString() { return $this->value(); } } // End Database_Expression libkohana3.1-php-3.1.5/modules/database/classes/kohana/database/mysql/0000755000000000000000000000000011771133710024201 5ustar rootrootlibkohana3.1-php-3.1.5/modules/database/classes/kohana/database/mysql/result.php0000644000000000000000000000330111772106746026237 0ustar rootroot_total_rows = mysql_num_rows($result); } public function __destruct() { if (is_resource($this->_result)) { mysql_free_result($this->_result); } } public function seek($offset) { if ($this->offsetExists($offset) AND mysql_data_seek($this->_result, $offset)) { // Set the current row to the offset $this->_current_row = $this->_internal_row = $offset; return TRUE; } else { return FALSE; } } public function current() { if ($this->_current_row !== $this->_internal_row AND ! $this->seek($this->_current_row)) return NULL; // Increment internal row for optimization assuming rows are fetched in order $this->_internal_row++; if ($this->_as_object === TRUE) { // Return an stdClass return mysql_fetch_object($this->_result); } elseif (is_string($this->_as_object)) { // Return an object of given class name return mysql_fetch_object($this->_result, $this->_as_object, $this->_object_params); } else { // Return an array of the row return mysql_fetch_assoc($this->_result); } } } // End Database_MySQL_Result_Select libkohana3.1-php-3.1.5/modules/database/classes/kohana/database/mysql.php0000644000000000000000000003020211772106746024721 0ustar rootroot_connection) return; if (Database_MySQL::$_set_names === NULL) { // Determine if we can use mysql_set_charset(), which is only // available on PHP 5.2.3+ when compiled against MySQL 5.0+ Database_MySQL::$_set_names = ! function_exists('mysql_set_charset'); } // Extract the connection parameters, adding required variabels extract($this->_config['connection'] + array( 'database' => '', 'hostname' => '', 'username' => '', 'password' => '', 'persistent' => FALSE, )); // Prevent this information from showing up in traces unset($this->_config['connection']['username'], $this->_config['connection']['password']); try { if ($persistent) { // Create a persistent connection $this->_connection = mysql_pconnect($hostname, $username, $password); } else { // Create a connection and force it to be a new link $this->_connection = mysql_connect($hostname, $username, $password, TRUE); } } catch (Exception $e) { // No connection exists $this->_connection = NULL; throw new Database_Exception(':error', array(':error' => $e->getMessage()), $e->getCode()); } // \xFF is a better delimiter, but the PHP driver uses underscore $this->_connection_id = sha1($hostname.'_'.$username.'_'.$password); $this->_select_db($database); if ( ! empty($this->_config['charset'])) { // Set the character set $this->set_charset($this->_config['charset']); } } /** * Select the database * * @param string $database Database * @return void */ protected function _select_db($database) { if ( ! mysql_select_db($database, $this->_connection)) { // Unable to select database throw new Database_Exception(':error', array(':error' => mysql_error($this->_connection)), mysql_errno($this->_connection)); } Database_MySQL::$_current_databases[$this->_connection_id] = $database; } public function disconnect() { try { // Database is assumed disconnected $status = TRUE; if (is_resource($this->_connection)) { if ($status = mysql_close($this->_connection)) { // Clear the connection $this->_connection = NULL; // Clear the instance parent::disconnect(); } } } catch (Exception $e) { // Database is probably not disconnected $status = ! is_resource($this->_connection); } return $status; } public function set_charset($charset) { // Make sure the database is connected $this->_connection or $this->connect(); if (Database_MySQL::$_set_names === TRUE) { // PHP is compiled against MySQL 4.x $status = (bool) mysql_query('SET NAMES '.$this->quote($charset), $this->_connection); } else { // PHP is compiled against MySQL 5.x $status = mysql_set_charset($charset, $this->_connection); } if ($status === FALSE) { throw new Database_Exception(':error', array(':error' => mysql_error($this->_connection)), mysql_errno($this->_connection)); } } public function query($type, $sql, $as_object = FALSE, array $params = NULL) { // Make sure the database is connected $this->_connection or $this->connect(); if ( ! empty($this->_config['profiling'])) { // Benchmark this query for the current instance $benchmark = Profiler::start("Database ({$this->_instance})", $sql); } if ( ! empty($this->_config['connection']['persistent']) AND $this->_config['connection']['database'] !== Database_MySQL::$_current_databases[$this->_connection_id]) { // Select database on persistent connections $this->_select_db($this->_config['connection']['database']); } // Execute the query if (($result = mysql_query($sql, $this->_connection)) === FALSE) { if (isset($benchmark)) { // This benchmark is worthless Profiler::delete($benchmark); } throw new Database_Exception(':error [ :query ]', array(':error' => mysql_error($this->_connection), ':query' => $sql), mysql_errno($this->_connection)); } if (isset($benchmark)) { Profiler::stop($benchmark); } // Set the last query $this->last_query = $sql; if ($type === Database::SELECT) { // Return an iterator of results return new Database_MySQL_Result($result, $sql, $as_object, $params); } elseif ($type === Database::INSERT) { // Return a list of insert id and rows created return array( mysql_insert_id($this->_connection), mysql_affected_rows($this->_connection), ); } else { // Return the number of rows affected return mysql_affected_rows($this->_connection); } } public function datatype($type) { static $types = array ( 'blob' => array('type' => 'string', 'binary' => TRUE, 'character_maximum_length' => '65535'), 'bool' => array('type' => 'bool'), 'bigint unsigned' => array('type' => 'int', 'min' => '0', 'max' => '18446744073709551615'), 'datetime' => array('type' => 'string'), 'decimal unsigned' => array('type' => 'float', 'exact' => TRUE, 'min' => '0'), 'double' => array('type' => 'float'), 'double precision unsigned' => array('type' => 'float', 'min' => '0'), 'double unsigned' => array('type' => 'float', 'min' => '0'), 'enum' => array('type' => 'string'), 'fixed' => array('type' => 'float', 'exact' => TRUE), 'fixed unsigned' => array('type' => 'float', 'exact' => TRUE, 'min' => '0'), 'float unsigned' => array('type' => 'float', 'min' => '0'), 'int unsigned' => array('type' => 'int', 'min' => '0', 'max' => '4294967295'), 'integer unsigned' => array('type' => 'int', 'min' => '0', 'max' => '4294967295'), 'longblob' => array('type' => 'string', 'binary' => TRUE, 'character_maximum_length' => '4294967295'), 'longtext' => array('type' => 'string', 'character_maximum_length' => '4294967295'), 'mediumblob' => array('type' => 'string', 'binary' => TRUE, 'character_maximum_length' => '16777215'), 'mediumint' => array('type' => 'int', 'min' => '-8388608', 'max' => '8388607'), 'mediumint unsigned' => array('type' => 'int', 'min' => '0', 'max' => '16777215'), 'mediumtext' => array('type' => 'string', 'character_maximum_length' => '16777215'), 'national varchar' => array('type' => 'string'), 'numeric unsigned' => array('type' => 'float', 'exact' => TRUE, 'min' => '0'), 'nvarchar' => array('type' => 'string'), 'point' => array('type' => 'string', 'binary' => TRUE), 'real unsigned' => array('type' => 'float', 'min' => '0'), 'set' => array('type' => 'string'), 'smallint unsigned' => array('type' => 'int', 'min' => '0', 'max' => '65535'), 'text' => array('type' => 'string', 'character_maximum_length' => '65535'), 'tinyblob' => array('type' => 'string', 'binary' => TRUE, 'character_maximum_length' => '255'), 'tinyint' => array('type' => 'int', 'min' => '-128', 'max' => '127'), 'tinyint unsigned' => array('type' => 'int', 'min' => '0', 'max' => '255'), 'tinytext' => array('type' => 'string', 'character_maximum_length' => '255'), 'year' => array('type' => 'string'), ); $type = str_replace(' zerofill', '', $type); if (isset($types[$type])) return $types[$type]; return parent::datatype($type); } /** * Start a SQL transaction * * @link http://dev.mysql.com/doc/refman/5.0/en/set-transaction.html * * @param string $mode Isolation level * @return boolean */ public function begin($mode = NULL) { // Make sure the database is connected $this->_connection or $this->connect(); if ($mode AND ! mysql_query("SET TRANSACTION ISOLATION LEVEL $mode", $this->_connection)) { throw new Database_Exception(':error', array(':error' => mysql_error($this->_connection)), mysql_errno($this->_connection)); } return (bool) mysql_query('START TRANSACTION', $this->_connection); } /** * Commit a SQL transaction * * @return boolean */ public function commit() { // Make sure the database is connected $this->_connection or $this->connect(); return (bool) mysql_query('COMMIT', $this->_connection); } /** * Rollback a SQL transaction * * @return boolean */ public function rollback() { // Make sure the database is connected $this->_connection or $this->connect(); return (bool) mysql_query('ROLLBACK', $this->_connection); } public function list_tables($like = NULL) { if (is_string($like)) { // Search for table names $result = $this->query(Database::SELECT, 'SHOW TABLES LIKE '.$this->quote($like), FALSE); } else { // Find all table names $result = $this->query(Database::SELECT, 'SHOW TABLES', FALSE); } $tables = array(); foreach ($result as $row) { $tables[] = reset($row); } return $tables; } public function list_columns($table, $like = NULL, $add_prefix = TRUE) { // Quote the table name $table = ($add_prefix === TRUE) ? $this->quote_table($table) : $table; if (is_string($like)) { // Search for column names $result = $this->query(Database::SELECT, 'SHOW FULL COLUMNS FROM '.$table.' LIKE '.$this->quote($like), FALSE); } else { // Find all column names $result = $this->query(Database::SELECT, 'SHOW FULL COLUMNS FROM '.$table, FALSE); } $count = 0; $columns = array(); foreach ($result as $row) { list($type, $length) = $this->_parse_type($row['Type']); $column = $this->datatype($type); $column['column_name'] = $row['Field']; $column['column_default'] = $row['Default']; $column['data_type'] = $type; $column['is_nullable'] = ($row['Null'] == 'YES'); $column['ordinal_position'] = ++$count; switch ($column['type']) { case 'float': if (isset($length)) { list($column['numeric_precision'], $column['numeric_scale']) = explode(',', $length); } break; case 'int': if (isset($length)) { // MySQL attribute $column['display'] = $length; } break; case 'string': switch ($column['data_type']) { case 'binary': case 'varbinary': $column['character_maximum_length'] = $length; break; case 'char': case 'varchar': $column['character_maximum_length'] = $length; case 'text': case 'tinytext': case 'mediumtext': case 'longtext': $column['collation_name'] = $row['Collation']; break; case 'enum': case 'set': $column['collation_name'] = $row['Collation']; $column['options'] = explode('\',\'', substr($length, 1, -1)); break; } break; } // MySQL attributes $column['comment'] = $row['Comment']; $column['extra'] = $row['Extra']; $column['key'] = $row['Key']; $column['privileges'] = $row['Privileges']; $columns[$row['Field']] = $column; } return $columns; } public function escape($value) { // Make sure the database is connected $this->_connection or $this->connect(); if (($value = mysql_real_escape_string( (string) $value, $this->_connection)) === FALSE) { throw new Database_Exception(':error', array(':error' => mysql_error($this->_connection)), mysql_errno($this->_connection)); } // SQL standard is to use single-quotes for all values return "'$value'"; } } // End Database_MySQL libkohana3.1-php-3.1.5/modules/database/classes/kohana/database/pdo.php0000644000000000000000000001125211772106746024342 0ustar rootroot_config['identifier'])) { // Allow the identifier to be overloaded per-connection $this->_identifier = (string) $this->_config['identifier']; } } public function connect() { if ($this->_connection) return; // Extract the connection parameters, adding required variabels extract($this->_config['connection'] + array( 'dsn' => '', 'username' => NULL, 'password' => NULL, 'persistent' => FALSE, )); // Clear the connection parameters for security unset($this->_config['connection']); // Force PDO to use exceptions for all errors $attrs = array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION); if ( ! empty($persistent)) { // Make the connection persistent $attrs[PDO::ATTR_PERSISTENT] = TRUE; } try { // Create a new PDO connection $this->_connection = new PDO($dsn, $username, $password, $attrs); } catch (PDOException $e) { throw new Database_Exception(':error', array(':error' => $e->getMessage()), $e->getCode()); } if ( ! empty($this->_config['charset'])) { // Set the character set $this->set_charset($this->_config['charset']); } } public function disconnect() { // Destroy the PDO object $this->_connection = NULL; return parent::disconnect(); } public function set_charset($charset) { // Make sure the database is connected $this->_connection or $this->connect(); // Execute a raw SET NAMES query $this->_connection->exec('SET NAMES '.$this->quote($charset)); } public function query($type, $sql, $as_object = FALSE, array $params = NULL) { // Make sure the database is connected $this->_connection or $this->connect(); if ( ! empty($this->_config['profiling'])) { // Benchmark this query for the current instance $benchmark = Profiler::start("Database ({$this->_instance})", $sql); } try { $result = $this->_connection->query($sql); } catch (Exception $e) { if (isset($benchmark)) { // This benchmark is worthless Profiler::delete($benchmark); } // Convert the exception in a database exception throw new Database_Exception(':error [ :query ]', array( ':error' => $e->getMessage(), ':query' => $sql ), $e->getCode()); } if (isset($benchmark)) { Profiler::stop($benchmark); } // Set the last query $this->last_query = $sql; if ($type === Database::SELECT) { // Convert the result into an array, as PDOStatement::rowCount is not reliable if ($as_object === FALSE) { $result->setFetchMode(PDO::FETCH_ASSOC); } elseif (is_string($as_object)) { $result->setFetchMode(PDO::FETCH_CLASS, $as_object, $params); } else { $result->setFetchMode(PDO::FETCH_CLASS, 'stdClass'); } $result = $result->fetchAll(); // Return an iterator of results return new Database_Result_Cached($result, $sql, $as_object, $params); } elseif ($type === Database::INSERT) { // Return a list of insert id and rows created return array( $this->_connection->lastInsertId(), $result->rowCount(), ); } else { // Return the number of rows affected return $result->rowCount(); } } public function begin($mode = NULL) { // Make sure the database is connected $this->_connection or $this->connect(); return $this->_connection->beginTransaction(); } public function commit() { // Make sure the database is connected $this->_connection or $this->connect(); return $this->_connection->commit(); } public function rollback() { // Make sure the database is connected $this->_connection or $this->connect(); return $this->_connection->rollBack(); } public function list_tables($like = NULL) { throw new Kohana_Exception('Database method :method is not supported by :class', array(':method' => __FUNCTION__, ':class' => __CLASS__)); } public function list_columns($table, $like = NULL, $add_prefix = TRUE) { throw new Kohana_Exception('Database method :method is not supported by :class', array(':method' => __FUNCTION__, ':class' => __CLASS__)); } public function escape($value) { // Make sure the database is connected $this->_connection or $this->connect(); return $this->_connection->quote($value); } } // End Database_PDO libkohana3.1-php-3.1.5/modules/database/classes/kohana/database/query/0000755000000000000000000000000011771133710024201 5ustar rootrootlibkohana3.1-php-3.1.5/modules/database/classes/kohana/database/query/builder/0000755000000000000000000000000011771133710025627 5ustar rootrootlibkohana3.1-php-3.1.5/modules/database/classes/kohana/database/query/builder/delete.php0000644000000000000000000000360511772106746027620 0ustar rootroot_table = $table; } // Start the query with no SQL return parent::__construct(Database::DELETE, ''); } /** * Sets the table to delete from. * * @param mixed $table table name or array($table, $alias) or object * @return $this */ public function table($table) { $this->_table = $table; return $this; } /** * Compile the SQL query and return it. * * @param object $db Database instance * @return string */ public function compile(Database $db) { // Start a deletion query $query = 'DELETE FROM '.$db->quote_table($this->_table); if ( ! empty($this->_where)) { // Add deletion conditions $query .= ' WHERE '.$this->_compile_conditions($db, $this->_where); } if ( ! empty($this->_order_by)) { // Add sorting $query .= ' '.$this->_compile_order_by($db, $this->_order_by); } if ($this->_limit !== NULL) { // Add limiting $query .= ' LIMIT '.$this->_limit; } $this->_sql = $query; return parent::compile($db); } public function reset() { $this->_table = NULL; $this->_where = array(); $this->_parameters = array(); $this->_sql = NULL; return $this; } } // End Database_Query_Builder_Delete libkohana3.1-php-3.1.5/modules/database/classes/kohana/database/query/builder/insert.php0000644000000000000000000000713111772106746027660 0ustar rootroot_table = $table; } if ($columns) { // Set the column names $this->_columns = $columns; } // Start the query with no SQL return parent::__construct(Database::INSERT, ''); } /** * Sets the table to insert into. * * @param mixed $table table name or array($table, $alias) or object * @return $this */ public function table($table) { $this->_table = $table; return $this; } /** * Set the columns that will be inserted. * * @param array $columns column names * @return $this */ public function columns(array $columns) { $this->_columns = $columns; return $this; } /** * Adds or overwrites values. Multiple value sets can be added. * * @param array $values values list * @param ... * @return $this */ public function values(array $values) { if ( ! is_array($this->_values)) { throw new Kohana_Exception('INSERT INTO ... SELECT statements cannot be combined with INSERT INTO ... VALUES'); } // Get all of the passed values $values = func_get_args(); $this->_values = array_merge($this->_values, $values); return $this; } /** * Use a sub-query to for the inserted values. * * @param object $query Database_Query of SELECT type * @return $this */ public function select(Database_Query $query) { if ($query->type() !== Database::SELECT) { throw new Kohana_Exception('Only SELECT queries can be combined with INSERT queries'); } $this->_values = $query; return $this; } /** * Compile the SQL query and return it. * * @param object $db Database instance * @return string */ public function compile(Database $db) { // Start an insertion query $query = 'INSERT INTO '.$db->quote_table($this->_table); // Add the column names $query .= ' ('.implode(', ', array_map(array($db, 'quote_column'), $this->_columns)).') '; if (is_array($this->_values)) { // Callback for quoting values $quote = array($db, 'quote'); $groups = array(); foreach ($this->_values as $group) { foreach ($group as $offset => $value) { if ((is_string($value) AND array_key_exists($value, $this->_parameters)) === FALSE) { // Quote the value, it is not a parameter $group[$offset] = $db->quote($value); } } $groups[] = '('.implode(', ', $group).')'; } // Add the values $query .= 'VALUES '.implode(', ', $groups); } else { // Add the sub-query $query .= (string) $this->_values; } $this->_sql = $query; return parent::compile($db);; } public function reset() { $this->_table = NULL; $this->_columns = $this->_values = array(); $this->_parameters = array(); $this->_sql = NULL; return $this; } } // End Database_Query_Builder_Insert libkohana3.1-php-3.1.5/modules/database/classes/kohana/database/query/builder/join.php0000644000000000000000000000612611772106746027316 0ustar rootroot_table = $table; if ($type !== NULL) { // Set the JOIN type $this->_type = (string) $type; } } /** * Adds a new condition for joining. * * @param mixed $c1 column name or array($column, $alias) or object * @param string $op logic operator * @param mixed $c2 column name or array($column, $alias) or object * @return $this */ public function on($c1, $op, $c2) { if ( ! empty($this->_using)) { throw new Kohana_Exception('JOIN ... ON ... cannot be combined with JOIN ... USING ...'); } $this->_on[] = array($c1, $op, $c2); return $this; } /** * Adds a new condition for joining. * * @param string $columns column name * @return $this */ public function using($columns) { if ( ! empty($this->_on)) { throw new Kohana_Exception('JOIN ... ON ... cannot be combined with JOIN ... USING ...'); } $columns = func_get_args(); $this->_using = array_merge($this->_using, $columns); return $this; } /** * Compile the SQL partial for a JOIN statement and return it. * * @param object $db Database instance * @return string */ public function compile(Database $db) { if ($this->_type) { $sql = strtoupper($this->_type).' JOIN'; } else { $sql = 'JOIN'; } // Quote the table name that is being joined $sql .= ' '.$db->quote_table($this->_table); if ( ! empty($this->_using)) { // Quote and concat the columns $sql .= ' USING ('.implode(', ', array_map(array($db, 'quote_column'), $this->_using)).')'; } else { $conditions = array(); foreach ($this->_on as $condition) { // Split the condition list($c1, $op, $c2) = $condition; if ($op) { // Make the operator uppercase and spaced $op = ' '.strtoupper($op); } // Quote each of the columns used for the condition $conditions[] = $db->quote_column($c1).$op.' '.$db->quote_column($c2); } // Concat the conditions "... AND ..." $sql .= ' ON ('.implode(' AND ', $conditions).')'; } return $sql; } public function reset() { $this->_type = $this->_table = NULL; $this->_on = array(); } } // End Database_Query_Builder_Join libkohana3.1-php-3.1.5/modules/database/classes/kohana/database/query/builder/select.php0000644000000000000000000002165411772106746027641 0ustar rootroot_select = $columns; } // Start the query with no actual SQL statement parent::__construct(Database::SELECT, ''); } /** * Enables or disables selecting only unique columns using "SELECT DISTINCT" * * @param boolean $value enable or disable distinct columns * @return $this */ public function distinct($value) { $this->_distinct = (bool) $value; return $this; } /** * Choose the columns to select from. * * @param mixed $columns column name or array($column, $alias) or object * @return $this */ public function select($columns = NULL) { $columns = func_get_args(); $this->_select = array_merge($this->_select, $columns); return $this; } /** * Choose the columns to select from, using an array. * * @param array $columns list of column names or aliases * @return $this */ public function select_array(array $columns) { $this->_select = array_merge($this->_select, $columns); return $this; } /** * Choose the tables to select "FROM ..." * * @param mixed $table table name or array($table, $alias) or object * @return $this */ public function from($tables) { $tables = func_get_args(); $this->_from = array_merge($this->_from, $tables); return $this; } /** * Adds addition tables to "JOIN ...". * * @param mixed $table column name or array($column, $alias) or object * @param string $type join type (LEFT, RIGHT, INNER, etc) * @return $this */ public function join($table, $type = NULL) { $this->_join[] = $this->_last_join = new Database_Query_Builder_Join($table, $type); return $this; } /** * Adds "ON ..." conditions for the last created JOIN statement. * * @param mixed $c1 column name or array($column, $alias) or object * @param string $op logic operator * @param mixed $c2 column name or array($column, $alias) or object * @return $this */ public function on($c1, $op, $c2) { $this->_last_join->on($c1, $op, $c2); return $this; } /** * Adds "USING ..." conditions for the last created JOIN statement. * * @param string $columns column name * @return $this */ public function using($columns) { $columns = func_get_args(); call_user_func_array(array($this->_last_join, 'using'), $columns); return $this; } /** * Creates a "GROUP BY ..." filter. * * @param mixed $columns column name or array($column, $alias) or object * @return $this */ public function group_by($columns) { $columns = func_get_args(); $this->_group_by = array_merge($this->_group_by, $columns); return $this; } /** * Alias of and_having() * * @param mixed $column column name or array($column, $alias) or object * @param string $op logic operator * @param mixed $value column value * @return $this */ public function having($column, $op, $value = NULL) { return $this->and_having($column, $op, $value); } /** * Creates a new "AND HAVING" condition for the query. * * @param mixed $column column name or array($column, $alias) or object * @param string $op logic operator * @param mixed $value column value * @return $this */ public function and_having($column, $op, $value = NULL) { $this->_having[] = array('AND' => array($column, $op, $value)); return $this; } /** * Creates a new "OR HAVING" condition for the query. * * @param mixed $column column name or array($column, $alias) or object * @param string $op logic operator * @param mixed $value column value * @return $this */ public function or_having($column, $op, $value = NULL) { $this->_having[] = array('OR' => array($column, $op, $value)); return $this; } /** * Alias of and_having_open() * * @return $this */ public function having_open() { return $this->and_having_open(); } /** * Opens a new "AND HAVING (...)" grouping. * * @return $this */ public function and_having_open() { $this->_having[] = array('AND' => '('); return $this; } /** * Opens a new "OR HAVING (...)" grouping. * * @return $this */ public function or_having_open() { $this->_having[] = array('OR' => '('); return $this; } /** * Closes an open "AND HAVING (...)" grouping. * * @return $this */ public function having_close() { return $this->and_having_close(); } /** * Closes an open "AND HAVING (...)" grouping. * * @return $this */ public function and_having_close() { $this->_having[] = array('AND' => ')'); return $this; } /** * Closes an open "OR HAVING (...)" grouping. * * @return $this */ public function or_having_close() { $this->_having[] = array('OR' => ')'); return $this; } /** * Adds an other UNION clause. * * @param mixed $select if string, it must be the name of a table. Else * must be an instance of Database_Query_Builder_Select * @param boolean $all decides if it's an UNION or UNION ALL clause * @return $this */ public function union($select, $all = TRUE) { if (is_string($select)) { $select = DB::select()->from($select); } if ( ! $select instanceof Database_Query_Builder_Select) throw new Kohana_Exception('first parameter must be a string or an instance of Database_Query_Builder_Select'); $this->_union []= array('select' => $select, 'all' => $all); return $this; } /** * Start returning results after "OFFSET ..." * * @param integer $number starting result number * @return $this */ public function offset($number) { $this->_offset = (int) $number; return $this; } /** * Compile the SQL query and return it. * * @param object $db Database instance * @return string */ public function compile(Database $db) { // Callback to quote columns $quote_column = array($db, 'quote_column'); // Callback to quote tables $quote_table = array($db, 'quote_table'); // Start a selection query $query = 'SELECT '; if ($this->_distinct === TRUE) { // Select only unique results $query .= 'DISTINCT '; } if (empty($this->_select)) { // Select all columns $query .= '*'; } else { // Select all columns $query .= implode(', ', array_unique(array_map($quote_column, $this->_select))); } if ( ! empty($this->_from)) { // Set tables to select from $query .= ' FROM '.implode(', ', array_unique(array_map($quote_table, $this->_from))); } if ( ! empty($this->_join)) { // Add tables to join $query .= ' '.$this->_compile_join($db, $this->_join); } if ( ! empty($this->_where)) { // Add selection conditions $query .= ' WHERE '.$this->_compile_conditions($db, $this->_where); } if ( ! empty($this->_group_by)) { // Add grouping $query .= ' '.$this->_compile_group_by($db, $this->_group_by); } if ( ! empty($this->_having)) { // Add filtering conditions $query .= ' HAVING '.$this->_compile_conditions($db, $this->_having); } if ( ! empty($this->_order_by)) { // Add sorting $query .= ' '.$this->_compile_order_by($db, $this->_order_by); } if ($this->_limit !== NULL) { // Add limiting $query .= ' LIMIT '.$this->_limit; } if ($this->_offset !== NULL) { // Add offsets $query .= ' OFFSET '.$this->_offset; } if ( ! empty($this->_union)) { foreach ($this->_union as $u) { $query .= ' UNION '; if ($u['all'] === TRUE) { $query .= 'ALL '; } $query .= $u['select']->compile($db); } } $this->_sql = $query; return parent::compile($db); } public function reset() { $this->_select = $this->_from = $this->_join = $this->_where = $this->_group_by = $this->_having = $this->_order_by = $this->_union = array(); $this->_distinct = FALSE; $this->_limit = $this->_offset = $this->_last_join = NULL; $this->_parameters = array(); $this->_sql = NULL; return $this; } } // End Database_Query_Select libkohana3.1-php-3.1.5/modules/database/classes/kohana/database/query/builder/update.php0000644000000000000000000000517411772106746027643 0ustar rootroot_table = $table; } // Start the query with no SQL return parent::__construct(Database::UPDATE, ''); } /** * Sets the table to update. * * @param mixed $table table name or array($table, $alias) or object * @return $this */ public function table($table) { $this->_table = $table; return $this; } /** * Set the values to update with an associative array. * * @param array $pairs associative (column => value) list * @return $this */ public function set(array $pairs) { foreach ($pairs as $column => $value) { $this->_set[] = array($column, $value); } return $this; } /** * Set the value of a single column. * * @param mixed $column table name or array($table, $alias) or object * @param mixed $value column value * @return $this */ public function value($column, $value) { $this->_set[] = array($column, $value); return $this; } /** * Compile the SQL query and return it. * * @param object $db Database instance * @return string */ public function compile(Database $db) { // Start an update query $query = 'UPDATE '.$db->quote_table($this->_table); // Add the columns to update $query .= ' SET '.$this->_compile_set($db, $this->_set); if ( ! empty($this->_where)) { // Add selection conditions $query .= ' WHERE '.$this->_compile_conditions($db, $this->_where); } if ( ! empty($this->_order_by)) { // Add sorting $query .= ' '.$this->_compile_order_by($db, $this->_order_by); } if ($this->_limit !== NULL) { // Add limiting $query .= ' LIMIT '.$this->_limit; } $this->_sql = $query; return parent::compile($db); } public function reset() { $this->_table = NULL; $this->_set = $this->_where = array(); $this->_limit = NULL; $this->_parameters = array(); $this->_sql = NULL; return $this; } } // End Database_Query_Builder_Update libkohana3.1-php-3.1.5/modules/database/classes/kohana/database/query/builder/where.php0000644000000000000000000000630211772106746027465 0ustar rootrootand_where($column, $op, $value); } /** * Creates a new "AND WHERE" condition for the query. * * @param mixed $column column name or array($column, $alias) or object * @param string $op logic operator * @param mixed $value column value * @return $this */ public function and_where($column, $op, $value) { $this->_where[] = array('AND' => array($column, $op, $value)); return $this; } /** * Creates a new "OR WHERE" condition for the query. * * @param mixed $column column name or array($column, $alias) or object * @param string $op logic operator * @param mixed $value column value * @return $this */ public function or_where($column, $op, $value) { $this->_where[] = array('OR' => array($column, $op, $value)); return $this; } /** * Alias of and_where_open() * * @return $this */ public function where_open() { return $this->and_where_open(); } /** * Opens a new "AND WHERE (...)" grouping. * * @return $this */ public function and_where_open() { $this->_where[] = array('AND' => '('); return $this; } /** * Opens a new "OR WHERE (...)" grouping. * * @return $this */ public function or_where_open() { $this->_where[] = array('OR' => '('); return $this; } /** * Closes an open "AND WHERE (...)" grouping. * * @return $this */ public function where_close() { return $this->and_where_close(); } /** * Closes an open "AND WHERE (...)" grouping. * * @return $this */ public function and_where_close() { $this->_where[] = array('AND' => ')'); return $this; } /** * Closes an open "OR WHERE (...)" grouping. * * @return $this */ public function or_where_close() { $this->_where[] = array('OR' => ')'); return $this; } /** * Applies sorting with "ORDER BY ..." * * @param mixed $column column name or array($column, $alias) or object * @param string $direction direction of sorting * @return $this */ public function order_by($column, $direction = NULL) { $this->_order_by[] = array($column, $direction); return $this; } /** * Return up to "LIMIT ..." results * * @param integer $number maximum results to return * @return $this */ public function limit($number) { $this->_limit = (int) $number; return $this; } } // End Database_Query_Builder_Where libkohana3.1-php-3.1.5/modules/database/classes/kohana/database/query/builder.php0000644000000000000000000001266311772106746026362 0ustar rootrootcompile($db); } return implode(' ', $statements); } /** * Compiles an array of conditions into an SQL partial. Used for WHERE * and HAVING. * * @param object $db Database instance * @param array $conditions condition statements * @return string */ protected function _compile_conditions(Database $db, array $conditions) { $last_condition = NULL; $sql = ''; foreach ($conditions as $group) { // Process groups of conditions foreach ($group as $logic => $condition) { if ($condition === '(') { if ( ! empty($sql) AND $last_condition !== '(') { // Include logic operator $sql .= ' '.$logic.' '; } $sql .= '('; } elseif ($condition === ')') { $sql .= ')'; } else { if ( ! empty($sql) AND $last_condition !== '(') { // Add the logic operator $sql .= ' '.$logic.' '; } // Split the condition list($column, $op, $value) = $condition; if ($value === NULL) { if ($op === '=') { // Convert "val = NULL" to "val IS NULL" $op = 'IS'; } elseif ($op === '!=') { // Convert "val != NULL" to "valu IS NOT NULL" $op = 'IS NOT'; } } // Database operators are always uppercase $op = strtoupper($op); if ($op === 'BETWEEN' AND is_array($value)) { // BETWEEN always has exactly two arguments list($min, $max) = $value; if ((is_string($min) AND array_key_exists($min, $this->_parameters)) === FALSE) { // Quote the value, it is not a parameter $min = $db->quote($min); } if ((is_string($max) AND array_key_exists($max, $this->_parameters)) === FALSE) { // Quote the value, it is not a parameter $max = $db->quote($max); } // Quote the min and max value $value = $min.' AND '.$max; } elseif ((is_string($value) AND array_key_exists($value, $this->_parameters)) === FALSE) { // Quote the value, it is not a parameter $value = $db->quote($value); } if ($column) { if (is_array($column)) { // Use the column name $column = $db->quote_identifier(reset($column)); } else { // Apply proper quoting to the column $column = $db->quote_column($column); } } // Append the statement to the query $sql .= trim($column.' '.$op.' '.$value); } $last_condition = $condition; } } return $sql; } /** * Compiles an array of set values into an SQL partial. Used for UPDATE. * * @param object $db Database instance * @param array $values updated values * @return string */ protected function _compile_set(Database $db, array $values) { $set = array(); foreach ($values as $group) { // Split the set list ($column, $value) = $group; // Quote the column name $column = $db->quote_column($column); if ((is_string($value) AND array_key_exists($value, $this->_parameters)) === FALSE) { // Quote the value, it is not a parameter $value = $db->quote($value); } $set[$column] = $column.' = '.$value; } return implode(', ', $set); } /** * Compiles an array of GROUP BY columns into an SQL partial. * * @param object $db Database instance * @param array $columns * @return string */ protected function _compile_group_by(Database $db, array $columns) { $group = array(); foreach ($columns as $column) { if (is_array($column)) { // Use the column alias $column = $db->quote_identifier(end($column)); } else { // Apply proper quoting to the column $column = $db->quote_column($column); } $group[] = $column; } return 'GROUP BY '.implode(', ', $group); } /** * Compiles an array of ORDER BY statements into an SQL partial. * * @param object $db Database instance * @param array $columns sorting columns * @return string */ protected function _compile_order_by(Database $db, array $columns) { $sort = array(); foreach ($columns as $group) { list ($column, $direction) = $group; if (is_array($column)) { // Use the column alias $column = $db->quote_identifier(end($column)); } else { // Apply proper quoting to the column $column = $db->quote_column($column); } if ($direction) { // Make the direction uppercase $direction = ' '.strtoupper($direction); } $sort[] = $column.$direction; } return 'ORDER BY '.implode(', ', $sort); } /** * Reset the current builder status. * * @return $this */ abstract public function reset(); } // End Database_Query_Builder libkohana3.1-php-3.1.5/modules/database/classes/kohana/database/query.php0000644000000000000000000001160611772106746024730 0ustar rootroot_type = $type; $this->_sql = $sql; } /** * Return the SQL query string. * * @return string */ final public function __toString() { try { // Return the SQL string return $this->compile(Database::instance()); } catch (Exception $e) { return Kohana_Exception::text($e); } } /** * Get the type of the query. * * @return integer */ public function type() { return $this->_type; } /** * Enables the query to be cached for a specified amount of time. * * @param integer $lifetime number of seconds to cache * @return $this * @uses Kohana::$cache_life */ public function cached($lifetime = NULL) { if ($lifetime === NULL) { // Use the global setting $lifetime = Kohana::$cache_life; } $this->_lifetime = $lifetime; return $this; } /** * Returns results as associative arrays * * @return $this */ public function as_assoc() { $this->_as_object = FALSE; $this->_object_params = array(); return $this; } /** * Returns results as objects * * @param string $class classname or TRUE for stdClass * @param array $params * @return $this */ public function as_object($class = TRUE, array $params = NULL) { $this->_as_object = $class; if ($params) { // Add object parameters $this->_object_params = $params; } return $this; } /** * Set the value of a parameter in the query. * * @param string $param parameter key to replace * @param mixed $value value to use * @return $this */ public function param($param, $value) { // Add or overload a new parameter $this->_parameters[$param] = $value; return $this; } /** * Bind a variable to a parameter in the query. * * @param string $param parameter key to replace * @param mixed $var variable to use * @return $this */ public function bind($param, & $var) { // Bind a value to a variable $this->_parameters[$param] =& $var; return $this; } /** * Add multiple parameters to the query. * * @param array $params list of parameters * @return $this */ public function parameters(array $params) { // Merge the new parameters in $this->_parameters = $params + $this->_parameters; return $this; } /** * Compile the SQL query and return it. Replaces any parameters with their * given values. * * @param object $db Database instance * @return string */ public function compile(Database $db) { // Import the SQL locally $sql = $this->_sql; if ( ! empty($this->_parameters)) { // Quote all of the values $values = array_map(array($db, 'quote'), $this->_parameters); // Replace the values in the SQL $sql = strtr($sql, $values); } return $sql; } /** * Execute the current query on the given database. * * @param mixed $db Database instance or name of instance * @return object Database_Result for SELECT queries * @return mixed the insert id for INSERT queries * @return integer number of affected rows for all other queries */ public function execute($db = NULL) { if ( ! is_object($db)) { // Get the database instance $db = Database::instance($db); } // Compile the SQL query $sql = $this->compile($db); if ($this->_lifetime !== NULL AND $this->_type === Database::SELECT) { // Set the cache key based on the database instance name and SQL $cache_key = 'Database::query("'.$db.'", "'.$sql.'")'; if (($result = Kohana::cache($cache_key, NULL, $this->_lifetime)) !== NULL) { // Return a cached result return new Database_Result_Cached($result, $sql, $this->_as_object, $this->_object_params); } } // Execute the query $result = $db->query($this->_type, $sql, $this->_as_object, $this->_object_params); if (isset($cache_key)) { // Cache the result array Kohana::cache($cache_key, $result->as_array(), $this->_lifetime); } return $result; } } // End Database_Query libkohana3.1-php-3.1.5/modules/database/classes/kohana/database/result/0000755000000000000000000000000011771133710024352 5ustar rootrootlibkohana3.1-php-3.1.5/modules/database/classes/kohana/database/result/cached.php0000644000000000000000000000211411772106746026302 0ustar rootroot_total_rows = count($result); } public function __destruct() { // Cached results do not use resources } public function cached() { return $this; } public function seek($offset) { if ($this->offsetExists($offset)) { $this->_current_row = $offset; return TRUE; } else { return FALSE; } } public function current() { // Return an array of the row return $this->valid() ? $this->_result[$this->_current_row] : NULL; } } // End Database_Result_Cached libkohana3.1-php-3.1.5/modules/database/classes/kohana/database/result.php0000644000000000000000000001451611772106746025104 0ustar rootroot_result = $result; // Store the SQL locally $this->_query = $sql; if (is_object($as_object)) { // Get the object class name $as_object = get_class($as_object); } // Results as objects or associative arrays $this->_as_object = $as_object; if ($params) { // Object constructor params $this->_object_params = $params; } } /** * Result destruction cleans up all open result sets. * * @return void */ abstract public function __destruct(); /** * Get a cached database result from the current result iterator. * * $cachable = serialize($result->cached()); * * @return Database_Result_Cached * @since 3.0.5 */ public function cached() { return new Database_Result_Cached($this->as_array(), $this->_query, $this->_as_object); } /** * Return all of the rows in the result as an array. * * // Indexed array of all rows * $rows = $result->as_array(); * * // Associative array of rows by "id" * $rows = $result->as_array('id'); * * // Associative array of rows, "id" => "name" * $rows = $result->as_array('id', 'name'); * * @param string $key column for associative keys * @param string $value column for values * @return array */ public function as_array($key = NULL, $value = NULL) { $results = array(); if ($key === NULL AND $value === NULL) { // Indexed rows foreach ($this as $row) { $results[] = $row; } } elseif ($key === NULL) { // Indexed columns if ($this->_as_object) { foreach ($this as $row) { $results[] = $row->$value; } } else { foreach ($this as $row) { $results[] = $row[$value]; } } } elseif ($value === NULL) { // Associative rows if ($this->_as_object) { foreach ($this as $row) { $results[$row->$key] = $row; } } else { foreach ($this as $row) { $results[$row[$key]] = $row; } } } else { // Associative columns if ($this->_as_object) { foreach ($this as $row) { $results[$row->$key] = $row->$value; } } else { foreach ($this as $row) { $results[$row[$key]] = $row[$value]; } } } $this->rewind(); return $results; } /** * Return the named column from the current row. * * // Get the "id" value * $id = $result->get('id'); * * @param string $name column to get * @param mixed $default default value if the column does not exist * @return mixed */ public function get($name, $default = NULL) { $row = $this->current(); if ($this->_as_object) { if (isset($row->$name)) return $row->$name; } else { if (isset($row[$name])) return $row[$name]; } return $default; } /** * Implements [Countable::count], returns the total number of rows. * * echo count($result); * * @return integer */ public function count() { return $this->_total_rows; } /** * Implements [ArrayAccess::offsetExists], determines if row exists. * * if (isset($result[10])) * { * // Row 10 exists * } * * @param int $offset * @return boolean */ public function offsetExists($offset) { return ($offset >= 0 AND $offset < $this->_total_rows); } /** * Implements [ArrayAccess::offsetGet], gets a given row. * * $row = $result[10]; * * @param int $offset * @return mixed */ public function offsetGet($offset) { if ( ! $this->seek($offset)) return NULL; return $this->current(); } /** * Implements [ArrayAccess::offsetSet], throws an error. * * [!!] You cannot modify a database result. * * @param int $offset * @param mixed $value * @return void * @throws Kohana_Exception */ final public function offsetSet($offset, $value) { throw new Kohana_Exception('Database results are read-only'); } /** * Implements [ArrayAccess::offsetUnset], throws an error. * * [!!] You cannot modify a database result. * * @param int $offset * @return void * @throws Kohana_Exception */ final public function offsetUnset($offset) { throw new Kohana_Exception('Database results are read-only'); } /** * Implements [Iterator::key], returns the current row number. * * echo key($result); * * @return integer */ public function key() { return $this->_current_row; } /** * Implements [Iterator::next], moves to the next row. * * next($result); * * @return $this */ public function next() { ++$this->_current_row; return $this; } /** * Implements [Iterator::prev], moves to the previous row. * * prev($result); * * @return $this */ public function prev() { --$this->_current_row; return $this; } /** * Implements [Iterator::rewind], sets the current row to zero. * * rewind($result); * * @return $this */ public function rewind() { $this->_current_row = 0; return $this; } /** * Implements [Iterator::valid], checks if the current row exists. * * [!!] This method is only used internally. * * @return boolean */ public function valid() { return $this->offsetExists($this->_current_row); } } // End Database_Result libkohana3.1-php-3.1.5/modules/database/classes/kohana/database.php0000644000000000000000000004463711772106746023575 0ustar rootroot$name; } if ( ! isset($config['type'])) { throw new Kohana_Exception('Database type not defined in :name configuration', array(':name' => $name)); } // Set the driver class name $driver = 'Database_'.ucfirst($config['type']); // Create the database connection instance new $driver($name, $config); } return Database::$instances[$name]; } /** * @var string the last query executed */ public $last_query; // Character that is used to quote identifiers protected $_identifier = '"'; // Instance name protected $_instance; // Raw server connection protected $_connection; // Configuration array protected $_config; /** * Stores the database configuration locally and name the instance. * * [!!] This method cannot be accessed directly, you must use [Database::instance]. * * @return void */ protected function __construct($name, array $config) { // Set the instance name $this->_instance = $name; // Store the config locally $this->_config = $config; if (empty($this->_config['table_prefix'])) { $this->_config['table_prefix'] = ''; } // Store the database instance Database::$instances[$name] = $this; } /** * Disconnect from the database when the object is destroyed. * * // Destroy the database instance * unset(Database::instances[(string) $db], $db); * * [!!] Calling `unset($db)` is not enough to destroy the database, as it * will still be stored in `Database::$instances`. * * @return void */ final public function __destruct() { $this->disconnect(); } /** * Returns the database instance name. * * echo (string) $db; * * @return string */ final public function __toString() { return $this->_instance; } /** * Connect to the database. This is called automatically when the first * query is executed. * * $db->connect(); * * @throws Database_Exception * @return void */ abstract public function connect(); /** * Disconnect from the database. This is called automatically by [Database::__destruct]. * Clears the database instance from [Database::$instances]. * * $db->disconnect(); * * @return boolean */ public function disconnect() { unset(Database::$instances[$this->_instance]); return TRUE; } /** * Set the connection character set. This is called automatically by [Database::connect]. * * $db->set_charset('utf8'); * * @throws Database_Exception * @param string $charset character set name * @return void */ abstract public function set_charset($charset); /** * Perform an SQL query of the given type. * * // Make a SELECT query and use objects for results * $db->query(Database::SELECT, 'SELECT * FROM groups', TRUE); * * // Make a SELECT query and use "Model_User" for the results * $db->query(Database::SELECT, 'SELECT * FROM users LIMIT 1', 'Model_User'); * * @param integer $type Database::SELECT, Database::INSERT, etc * @param string $sql SQL query * @param mixed $as_object result object class string, TRUE for stdClass, FALSE for assoc array * @param array $params object construct parameters for result class * @return object Database_Result for SELECT queries * @return array list (insert id, row count) for INSERT queries * @return integer number of affected rows for all other queries */ abstract public function query($type, $sql, $as_object = FALSE, array $params = NULL); /** * Start a SQL transaction * * // Start the transactions * $db->begin(); * * try { * DB::insert('users')->values($user1)... * DB::insert('users')->values($user2)... * // Insert successful commit the changes * $db->commit(); * } * catch (Database_Exception $e) * { * // Insert failed. Rolling back changes... * $db->rollback(); * } * * @param string $mode transaction mode * @return boolean */ abstract public function begin($mode = NULL); /** * Commit the current transaction * * // Commit the database changes * $db->commit(); * * @return boolean */ abstract public function commit(); /** * Abort the current transaction * * // Undo the changes * $db->rollback(); * * @return boolean */ abstract public function rollback(); /** * Count the number of records in a table. * * // Get the total number of records in the "users" table * $count = $db->count_records('users'); * * @param mixed $table table name string or array(query, alias) * @return integer */ public function count_records($table) { // Quote the table name $table = $this->quote_table($table); return $this->query(Database::SELECT, 'SELECT COUNT(*) AS total_row_count FROM '.$table, FALSE) ->get('total_row_count'); } /** * Returns a normalized array describing the SQL data type * * $db->datatype('char'); * * @param string $type SQL data type * @return array */ public function datatype($type) { static $types = array ( // SQL-92 'bit' => array('type' => 'string', 'exact' => TRUE), 'bit varying' => array('type' => 'string'), 'char' => array('type' => 'string', 'exact' => TRUE), 'char varying' => array('type' => 'string'), 'character' => array('type' => 'string', 'exact' => TRUE), 'character varying' => array('type' => 'string'), 'date' => array('type' => 'string'), 'dec' => array('type' => 'float', 'exact' => TRUE), 'decimal' => array('type' => 'float', 'exact' => TRUE), 'double precision' => array('type' => 'float'), 'float' => array('type' => 'float'), 'int' => array('type' => 'int', 'min' => '-2147483648', 'max' => '2147483647'), 'integer' => array('type' => 'int', 'min' => '-2147483648', 'max' => '2147483647'), 'interval' => array('type' => 'string'), 'national char' => array('type' => 'string', 'exact' => TRUE), 'national char varying' => array('type' => 'string'), 'national character' => array('type' => 'string', 'exact' => TRUE), 'national character varying' => array('type' => 'string'), 'nchar' => array('type' => 'string', 'exact' => TRUE), 'nchar varying' => array('type' => 'string'), 'numeric' => array('type' => 'float', 'exact' => TRUE), 'real' => array('type' => 'float'), 'smallint' => array('type' => 'int', 'min' => '-32768', 'max' => '32767'), 'time' => array('type' => 'string'), 'time with time zone' => array('type' => 'string'), 'timestamp' => array('type' => 'string'), 'timestamp with time zone' => array('type' => 'string'), 'varchar' => array('type' => 'string'), // SQL:1999 'binary large object' => array('type' => 'string', 'binary' => TRUE), 'blob' => array('type' => 'string', 'binary' => TRUE), 'boolean' => array('type' => 'bool'), 'char large object' => array('type' => 'string'), 'character large object' => array('type' => 'string'), 'clob' => array('type' => 'string'), 'national character large object' => array('type' => 'string'), 'nchar large object' => array('type' => 'string'), 'nclob' => array('type' => 'string'), 'time without time zone' => array('type' => 'string'), 'timestamp without time zone' => array('type' => 'string'), // SQL:2003 'bigint' => array('type' => 'int', 'min' => '-9223372036854775808', 'max' => '9223372036854775807'), // SQL:2008 'binary' => array('type' => 'string', 'binary' => TRUE, 'exact' => TRUE), 'binary varying' => array('type' => 'string', 'binary' => TRUE), 'varbinary' => array('type' => 'string', 'binary' => TRUE), ); if (isset($types[$type])) return $types[$type]; return array(); } /** * List all of the tables in the database. Optionally, a LIKE string can * be used to search for specific tables. * * // Get all tables in the current database * $tables = $db->list_tables(); * * // Get all user-related tables * $tables = $db->list_tables('user%'); * * @param string $like table to search for * @return array */ abstract public function list_tables($like = NULL); /** * Lists all of the columns in a table. Optionally, a LIKE string can be * used to search for specific fields. * * // Get all columns from the "users" table * $columns = $db->list_columns('users'); * * // Get all name-related columns * $columns = $db->list_columns('users', '%name%'); * * // Get the columns from a table that doesn't use the table prefix * $columns = $db->list_columns('users', NULL, FALSE); * * @param string $table table to get columns from * @param string $like column to search for * @param boolean $add_prefix whether to add the table prefix automatically or not * @return array */ abstract public function list_columns($table, $like = NULL, $add_prefix = TRUE); /** * Extracts the text between parentheses, if any. * * // Returns: array('CHAR', '6') * list($type, $length) = $db->_parse_type('CHAR(6)'); * * @param string $type * @return array list containing the type and length, if any */ protected function _parse_type($type) { if (($open = strpos($type, '(')) === FALSE) { // No length specified return array($type, NULL); } // Closing parenthesis $close = strrpos($type, ')', $open); // Length without parentheses $length = substr($type, $open + 1, $close - 1 - $open); // Type without the length $type = substr($type, 0, $open).substr($type, $close + 1); return array($type, $length); } /** * Return the table prefix defined in the current configuration. * * $prefix = $db->table_prefix(); * * @return string */ public function table_prefix() { return $this->_config['table_prefix']; } /** * Quote a value for an SQL query. * * $db->quote(NULL); // 'NULL' * $db->quote(10); // 10 * $db->quote('fred'); // 'fred' * * Objects passed to this function will be converted to strings. * [Database_Expression] objects will use the value of the expression. * [Database_Query] objects will be compiled and converted to a sub-query. * All other objects will be converted using the `__toString` method. * * @param mixed $value any value to quote * @return string * @uses Database::escape */ public function quote($value) { if ($value === NULL) { return 'NULL'; } elseif ($value === TRUE) { return "'1'"; } elseif ($value === FALSE) { return "'0'"; } elseif (is_object($value)) { if ($value instanceof Database_Query) { // Create a sub-query return '('.$value->compile($this).')'; } elseif ($value instanceof Database_Expression) { // Use a raw expression return $value->value(); } else { // Convert the object to a string return $this->quote( (string) $value); } } elseif (is_array($value)) { return '('.implode(', ', array_map(array($this, __FUNCTION__), $value)).')'; } elseif (is_int($value)) { return (int) $value; } elseif (is_float($value)) { // Convert to non-locale aware float to prevent possible commas return sprintf('%F', $value); } return $this->escape($value); } /** * Quote a database column name and add the table prefix if needed. * * $column = $db->quote_column($column); * * You can also use SQL methods within identifiers. * * // The value of "column" will be quoted * $column = $db->quote_column('COUNT("column")'); * * @param mixed $column column name or array(column, alias) * @return string * @uses Database::quote_identifier * @uses Database::table_prefix */ public function quote_column($column) { if (is_array($column)) { list($column, $alias) = $column; } if ($column instanceof Database_Query) { // Create a sub-query $column = '('.$column->compile($this).')'; } elseif ($column instanceof Database_Expression) { // Use a raw expression $column = $column->value(); } else { // Convert to a string $column = (string) $column; if ($column === '*') { return $column; } elseif (strpos($column, '"') !== FALSE) { // Quote the column in FUNC("column") identifiers $column = preg_replace('/"(.+?)"/e', '$this->quote_column("$1")', $column); } elseif (strpos($column, '.') !== FALSE) { $parts = explode('.', $column); if ($prefix = $this->table_prefix()) { // Get the offset of the table name, 2nd-to-last part $offset = count($parts) - 2; // Add the table prefix to the table name $parts[$offset] = $prefix.$parts[$offset]; } foreach ($parts as & $part) { if ($part !== '*') { // Quote each of the parts $part = $this->_identifier.$part.$this->_identifier; } } $column = implode('.', $parts); } else { $column = $this->_identifier.$column.$this->_identifier; } } if (isset($alias)) { $column .= ' AS '.$this->_identifier.$alias.$this->_identifier; } return $column; } /** * Quote a database table name and adds the table prefix if needed. * * $table = $db->quote_table($table); * * @param mixed $table table name or array(table, alias) * @return string * @uses Database::quote_identifier * @uses Database::table_prefix */ public function quote_table($table) { if (is_array($table)) { list($table, $alias) = $table; } if ($table instanceof Database_Query) { // Create a sub-query $table = '('.$table->compile($this).')'; } elseif ($table instanceof Database_Expression) { // Use a raw expression $table = $table->value(); } else { // Convert to a string $table = (string) $table; if (strpos($table, '.') !== FALSE) { $parts = explode('.', $table); if ($prefix = $this->table_prefix()) { // Get the offset of the table name, last part $offset = count($parts) - 1; // Add the table prefix to the table name $parts[$offset] = $prefix.$parts[$offset]; } foreach ($parts as & $part) { // Quote each of the parts $part = $this->_identifier.$part.$this->_identifier; } $table = implode('.', $parts); } else { // Add the table prefix $table = $this->_identifier.$this->table_prefix().$table.$this->_identifier; } } if (isset($alias)) { // Attach table prefix to alias $table .= ' AS '.$this->_identifier.$this->table_prefix().$alias.$this->_identifier; } return $table; } /** * Quote a database identifier * * Objects passed to this function will be converted to strings. * [Database_Expression] objects will use the value of the expression. * [Database_Query] objects will be compiled and converted to a sub-query. * All other objects will be converted using the `__toString` method. * * @param mixed $value any identifier * @return string */ public function quote_identifier($value) { if (is_array($value)) { list($value, $alias) = $value; } if ($value instanceof Database_Query) { // Create a sub-query $value = '('.$value->compile($this).')'; } elseif ($value instanceof Database_Expression) { // Use a raw expression $value = $value->value(); } else { // Convert to a string $value = (string) $value; if (strpos($value, '.') !== FALSE) { $parts = explode('.', $value); foreach ($parts as & $part) { // Quote each of the parts $part = $this->_identifier.$part.$this->_identifier; } $value = implode('.', $parts); } else { $value = $this->_identifier.$value.$this->_identifier; } } if (isset($alias)) { $value .= ' AS '.$this->_identifier.$alias.$this->_identifier; } return $value; } /** * Sanitize a string by escaping characters that could cause an SQL * injection attack. * * $value = $db->escape('any string'); * * @param string $value value to quote * @return string */ abstract public function escape($value); } // End Database_Connection libkohana3.1-php-3.1.5/modules/database/classes/kohana/db.php0000644000000000000000000001052211772106746022400 0ustar rootroot[`DB::select_array()`](#select_array) | [Database_Query_Builder_Select] * [`DB::update()`](#update) | [Database_Query_Builder_Update] * [`DB::delete()`](#delete) | [Database_Query_Builder_Delete] * [`DB::expr()`](#expr) | [Database_Expression] * * You pass the same parameters to these functions as you pass to the objects they return. * * @package Kohana/Database * @category Base * @author Kohana Team * @copyright (c) 2009 Kohana Team * @license http://kohanaphp.com/license */ class Kohana_DB { /** * Create a new [Database_Query] of the given type. * * // Create a new SELECT query * $query = DB::query(Database::SELECT, 'SELECT * FROM users'); * * // Create a new DELETE query * $query = DB::query(Database::DELETE, 'DELETE FROM users WHERE id = 5'); * * Specifying the type changes the returned result. When using * `Database::SELECT`, a [Database_Query_Result] will be returned. * `Database::INSERT` queries will return the insert id and number of rows. * For all other queries, the number of affected rows is returned. * * @param integer $type type: Database::SELECT, Database::UPDATE, etc * @param string $sql SQL statement * @return Database_Query */ public static function query($type, $sql) { return new Database_Query($type, $sql); } /** * Create a new [Database_Query_Builder_Select]. Each argument will be * treated as a column. To generate a `foo AS bar` alias, use an array. * * // SELECT id, username * $query = DB::select('id', 'username'); * * // SELECT id AS user_id * $query = DB::select(array('id', 'user_id')); * * @param mixed $columns column name or array($column, $alias) or object * @return Database_Query_Builder_Select */ public static function select($columns = NULL) { return new Database_Query_Builder_Select(func_get_args()); } /** * Create a new [Database_Query_Builder_Select] from an array of columns. * * // SELECT id, username * $query = DB::select_array(array('id', 'username')); * * @param array $columns columns to select * @return Database_Query_Builder_Select */ public static function select_array(array $columns = NULL) { return new Database_Query_Builder_Select($columns); } /** * Create a new [Database_Query_Builder_Insert]. * * // INSERT INTO users (id, username) * $query = DB::insert('users', array('id', 'username')); * * @param string $table table to insert into * @param array $columns list of column names or array($column, $alias) or object * @return Database_Query_Builder_Insert */ public static function insert($table = NULL, array $columns = NULL) { return new Database_Query_Builder_Insert($table, $columns); } /** * Create a new [Database_Query_Builder_Update]. * * // UPDATE users * $query = DB::update('users'); * * @param string $table table to update * @return Database_Query_Builder_Update */ public static function update($table = NULL) { return new Database_Query_Builder_Update($table); } /** * Create a new [Database_Query_Builder_Delete]. * * // DELETE FROM users * $query = DB::delete('users'); * * @param string $table table to delete from * @return Database_Query_Builder_Delete */ public static function delete($table = NULL) { return new Database_Query_Builder_Delete($table); } /** * Create a new [Database_Expression] which is not escaped. An expression * is the only way to use SQL functions within query builders. * * $expression = DB::expr('COUNT(users.id)'); * $query = DB::update('users')->set(array('login_count' => DB::expr('login_count + 1')))->where('id', '=', $id); * $users = ORM::factory('user')->where(DB::expr("BINARY `hash`"), '=', $hash)->find(); * * @param string $string expression * @return Database_Expression */ public static function expr($string) { return new Database_Expression($string); } } // End DB libkohana3.1-php-3.1.5/modules/database/classes/kohana/model/0000755000000000000000000000000011771133710022370 5ustar rootrootlibkohana3.1-php-3.1.5/modules/database/classes/kohana/model/database.php0000644000000000000000000000253411772106746024663 0ustar rootroot_db = $db; } elseif ( ! $this->_db) { // Use the default name $this->_db = Database::$default; } if (is_string($this->_db)) { // Load the database $this->_db = Database::instance($this->_db); } } } // End Model libkohana3.1-php-3.1.5/modules/database/classes/kohana/session/0000755000000000000000000000000011771133710022753 5ustar rootrootlibkohana3.1-php-3.1.5/modules/database/classes/kohana/session/database.php0000644000000000000000000001161011772106746025241 0ustar rootroot 'session_id', 'last_active' => 'last_active', 'contents' => 'contents' ); // Garbage collection requests protected $_gc = 500; // The current session id protected $_session_id; // The old session id protected $_update_id; public function __construct(array $config = NULL, $id = NULL) { if ( ! isset($config['group'])) { // Use the default group $config['group'] = Database::$default; } // Load the database $this->_db = Database::instance($config['group']); if (isset($config['table'])) { // Set the table name $this->_table = (string) $config['table']; } if (isset($config['gc'])) { // Set the gc chance $this->_gc = (int) $config['gc']; } if (isset($config['columns'])) { // Overload column names $this->_columns = $config['columns']; } parent::__construct($config, $id); if (mt_rand(0, $this->_gc) === $this->_gc) { // Run garbage collection // This will average out to run once every X requests $this->_gc(); } } public function id() { return $this->_session_id; } protected function _read($id = NULL) { if ($id OR $id = Cookie::get($this->_name)) { $result = DB::select(array($this->_columns['contents'], 'contents')) ->from($this->_table) ->where($this->_columns['session_id'], '=', ':id') ->limit(1) ->param(':id', $id) ->execute($this->_db); if ($result->count()) { // Set the current session id $this->_session_id = $this->_update_id = $id; // Return the contents return $result->get('contents'); } } // Create a new session id $this->_regenerate(); return NULL; } protected function _regenerate() { // Create the query to find an ID $query = DB::select($this->_columns['session_id']) ->from($this->_table) ->where($this->_columns['session_id'], '=', ':id') ->limit(1) ->bind(':id', $id); do { // Create a new session id $id = str_replace('.', '-', uniqid(NULL, TRUE)); // Get the the id from the database $result = $query->execute($this->_db); } while ($result->count()); return $this->_session_id = $id; } protected function _write() { if ($this->_update_id === NULL) { // Insert a new row $query = DB::insert($this->_table, $this->_columns) ->values(array(':new_id', ':active', ':contents')); } else { // Update the row $query = DB::update($this->_table) ->value($this->_columns['last_active'], ':active') ->value($this->_columns['contents'], ':contents') ->where($this->_columns['session_id'], '=', ':old_id'); if ($this->_update_id !== $this->_session_id) { // Also update the session id $query->value($this->_columns['session_id'], ':new_id'); } } $query ->param(':new_id', $this->_session_id) ->param(':old_id', $this->_update_id) ->param(':active', $this->_data['last_active']) ->param(':contents', $this->__toString()); // Execute the query $query->execute($this->_db); // The update and the session id are now the same $this->_update_id = $this->_session_id; // Update the cookie with the new session id Cookie::set($this->_name, $this->_session_id, $this->_lifetime); return TRUE; } protected function _destroy() { if ($this->_update_id === NULL) { // Session has not been created yet return TRUE; } // Delete the current session $query = DB::delete($this->_table) ->where($this->_columns['session_id'], '=', ':id') ->param(':id', $this->_update_id); try { // Execute the query $query->execute($this->_db); // Delete the cookie Cookie::delete($this->_name); } catch (Exception $e) { // An error occurred, the session has not been deleted return FALSE; } return TRUE; } protected function _gc() { if ($this->_lifetime) { // Expire sessions when their lifetime is up $expires = $this->_lifetime; } else { // Expire sessions after one month $expires = Date::MONTH; } // Delete all sessions that have expired DB::delete($this->_table) ->where($this->_columns['last_active'], '<', ':time') ->param(':time', time() - $expires) ->execute($this->_db); } } // End Session_Database libkohana3.1-php-3.1.5/modules/database/classes/model/0000755000000000000000000000000011771133710021127 5ustar rootrootlibkohana3.1-php-3.1.5/modules/database/classes/model/database.php0000644000000000000000000000017511772106746023421 0ustar rootroot array ( 'type' => 'mysql', 'connection' => array( /** * The following options are available for MySQL: * * string hostname server hostname, or socket * string database database name * string username database username * string password database password * boolean persistent use persistent connections? * * Ports and sockets may be appended to the hostname. */ 'hostname' => 'localhost', 'database' => 'kohana', 'username' => FALSE, 'password' => FALSE, 'persistent' => FALSE, ), 'table_prefix' => '', 'charset' => 'utf8', 'caching' => FALSE, 'profiling' => TRUE, ), 'alternate' => array( 'type' => 'pdo', 'connection' => array( /** * The following options are available for PDO: * * string dsn Data Source Name * string username database username * string password database password * boolean persistent use persistent connections? */ 'dsn' => 'mysql:host=localhost;dbname=kohana', 'username' => 'root', 'password' => 'r00tdb', 'persistent' => FALSE, ), /** * The following extra options are available for PDO: * * string identifier set the escaping identifier */ 'table_prefix' => '', 'charset' => 'utf8', 'caching' => FALSE, 'profiling' => TRUE, ), );libkohana3.1-php-3.1.5/modules/database/config/session.php0000644000000000000000000000127211772106746022047 0ustar rootroot array( /** * Database settings for session storage. * * string group configuation group name * string table session table name * integer gc number of requests before gc is invoked * columns array custom column names */ 'group' => 'default', 'table' => 'sessions', 'gc' => 500, 'columns' => array( /** * session_id: session identifier * last_active: timestamp of the last activity * contents: serialized session data */ 'session_id' => 'session_id', 'last_active' => 'last_active', 'contents' => 'contents' ), ), ); libkohana3.1-php-3.1.5/modules/database/config/userguide.php0000644000000000000000000000131211772106746022353 0ustar rootroot array( // This should be the path to this modules userguide pages, without the 'guide/'. Ex: '/guide/modulename/' would be 'modulename' 'database' => array( // Whether this modules userguide pages should be shown 'enabled' => TRUE, // The name that should show up on the userguide index page 'name' => 'Database', // A short description of this module, shown on the index page 'description' => 'Database agnostic querying and result management.', // Copyright message, shown in the footer for this module 'copyright' => '© 2008–2012 Kohana Team', ) ) );libkohana3.1-php-3.1.5/modules/database/guide/0000755000000000000000000000000011771133710017467 5ustar rootrootlibkohana3.1-php-3.1.5/modules/database/guide/database/0000755000000000000000000000000011771133710021233 5ustar rootrootlibkohana3.1-php-3.1.5/modules/database/guide/database/config.md0000644000000000000000000001105611772106746023037 0ustar rootroot# Configuration The default config file is located in `MODPATH/database/config/database.php`. You should copy this file to `APPPATH/config/database.php` and make changes there, in keeping with the [cascading filesystem](../kohana/files). The database configuration file contains an array of configuration groups. The structure of each database configuration group, called an "instance", looks like this: string INSTANCE_NAME => array( 'type' => string DATABASE_TYPE, 'connection' => array CONNECTION_ARRAY, 'table_prefix' => string TABLE_PREFIX, 'charset' => string CHARACTER_SET, 'profiling' => boolean QUERY_PROFILING, ), Understanding each of these settings is important. INSTANCE_NAME : Connections can be named anything you want, but you should always have at least one connection called "default". DATABASE_TYPE : One of the installed database drivers. Kohana comes with "mysql" and "pdo" drivers. Drivers must extend the Database class. CONNECTION_ARRAY : Specific driver options for connecting to your database. (Driver options are explained [below](#connection-settings).) TABLE_PREFIX : Prefix that will be added to all table names by the [query builder](#query_building). QUERY_PROFILING : Enables [profiling](../kohana/profiling) of database queries. This is useful for seeing how many queries each page is using, and which are taking the longest. You must enable the profiler the view these stats. ## Example The example file below shows 2 MySQL connections, one local and one remote. return array ( 'default' => array ( 'type' => 'mysql', 'connection' => array( 'hostname' => 'localhost', 'username' => 'dbuser', 'password' => 'mypassword', 'persistent' => FALSE, 'database' => 'my_db_name', ), 'table_prefix' => '', 'charset' => 'utf8', 'profiling' => TRUE, ), 'remote' => array( 'type' => 'mysql', 'connection' => array( 'hostname' => '55.55.55.55', 'username' => 'remote_user', 'password' => 'mypassword', 'persistent' => FALSE, 'database' => 'my_remote_db_name', ), 'table_prefix' => '', 'charset' => 'utf8', 'profiling' => TRUE, ), ); ## Connections and Instances Each configuration group is referred to as a database instance. Each instance can be accessed by calling [Database::instance]. If you don't provide a parameter, the default instance is used. // This would connect to the database defined as 'default' $default = Database::instance(); // This would connect to the database defined as 'remote' $remote = Database::instance('remote'); To disconnect the database, simply destroy the object: unset($default) // Or unset(Database::$instances['default']); If you want to disconnect all of the database instances at once: Database::$instances = array(); ## Connection Settings Every database driver has different connection settings. ### MySQL A [MySQL database](http://www.php.net/manual/en/book.mysql.php) can accept the following options in the `connection` array: Type | Option | Description | Default value ----------|------------|----------------------------| ------------------------- `string` | hostname | Hostname of the database | `localhost` `integer` | port | Port number | `NULL` `string` | socket | UNIX socket | `NULL` `string` | username | Database username | `NULL` `string` | password | Database password | `NULL` `boolean` | persistent | Persistent connections | `FALSE` `string` | database | Database name | `kohana` ### PDO A [PDO database](http://php.net/manual/en/book.pdo.php) can accept these options in the `connection` array: Type | Option | Description | Default value ----------|------------|----------------------------| ------------------------- `string` | dsn | PDO data source identifier | `localhost` `string` | username | Database username | `NULL` `string` | password | Database password | `NULL` `boolean` | persistent | Persistent connections | `FALSE` [!!] If you are using PDO and are not sure what to use for the `dsn` option, review [PDO::__construct](http://php.net/pdo.construct).libkohana3.1-php-3.1.5/modules/database/guide/database/examples.md0000644000000000000000000000366011772106746023412 0ustar rootroot# Examples Here are some "real world" examples of using the database library to construct your queries and use the results. ## Examples of Parameterized Statements TODO: 4-6 examples of parameterized statements of varying complexity, including a good bind() example. ## Pagination and search/filter In this example, we loop through an array of whitelisted input fields and for each allowed non-empty value we add it to the search query. We make a clone of the query and then execute that query to count the total number of results. The count is then passed to the [Pagination](../pagination) class to determine the search offset. The last few lines search with Pagination's items_per_page and offset values to return a page of results based on the current page the user is on. $query = DB::select()->from('users'); //only search for these fields $form_inputs = array('first_name', 'last_name', 'email'); foreach ($form_inputs as $name) { $value = Arr::get($_GET, $name, FALSE); if ($value !== FALSE AND $value != '') { $query->where($name, 'like', '%'.$value.'%'); } } //copy the query & execute it $pagination_query = clone $query; $count = $pagination_query->select('COUNT("*") AS mycount')->execute()->get('mycount'); //pass the total item count to Pagination $config = Kohana::config('pagination'); $pagination = Pagination::factory(array( 'total_items' => $count, 'current_page' => array('source' => 'route', 'key' => 'page'), 'items_per_page' => 20, 'view' => 'pagination/pretty', 'auto_hide' => TRUE, )); $page_links = $pagination->render(); //search for results starting at the offset calculated by the Pagination class $query->order_by('last_name', 'asc') ->order_by('first_name', 'asc') ->limit($pagination->items_per_page) ->offset($pagination->offset); $results = $query->execute()->as_array(); ## Having TODO: example goes here [!!] We could use more examples on this page. libkohana3.1-php-3.1.5/modules/database/guide/database/index.md0000644000000000000000000000172111771113724022670 0ustar rootroot# Database Kohana 3.0 comes with a robust module for working with databases. By default, the database module supports drivers for [MySQL](http://php.net/mysql) and [PDO](http://php.net/pdo), but new drivers can be made for other database servers. The database module is included with the Kohana 3.0 install, but needs to be enabled before you can use it. To enable, open your `application/bootstrap.php` file and modify the call to [Kohana::modules] by including the database module like so: Kohana::modules(array( ... 'database' => MODPATH.'database', ... )); Next, you will then need to [configure](config) the database module to connect to your database. Once that is done then you can make [queries](query) and use the [results](results). The database module also provides a [config driver](../api/Kohana_Config_Database) (for storing [configuration](../kohana/files/config) in the database) and a [session driver](Session_Database). libkohana3.1-php-3.1.5/modules/database/guide/database/menu.md0000644000000000000000000000030211772106746022526 0ustar rootroot## [Database]() - [Configuration](config) - [Querying](query) - [Parameterized Statements](query/parameterized) - [Query Builder](query/builder) - [Results](results) - [Examples](examples)libkohana3.1-php-3.1.5/modules/database/guide/database/query/0000755000000000000000000000000011771133710022400 5ustar rootrootlibkohana3.1-php-3.1.5/modules/database/guide/database/query/builder.md0000644000000000000000000003164411772106746024372 0ustar rootroot# Query Builder Creating queries dynamically using objects and methods allows queries to be written very quickly in an agnostic way. Query building also adds identifier (table and column name) quoting, as well as value quoting. ## Select Each type of database query is represented by a different class, each with their own methods. For instance, to create a SELECT query, we use [DB::select] which is a shortcut to return a new [Database_Query_Builder_Select] object: $query = DB::select(); Query Builder methods return a reference to itself so that method chaining may be used. Select queries ussually require a table and they are referenced using the `from()` method. The `from()` method takes one parameter which can be the table name (string), an array of two strings (table name and alias), or an object (See Subqueries in the Advanced Queries section below). $query = DB::select()->from('users'); Limiting the results of queries is done using the `where()`, `and_where()` and `or_where()` methods. These methods take three parameters: a column, an operator, and a value. $query = DB::select()->from('users')->where('username', '=', 'john'); Multiple `where()` methods may be used to string together multiple clauses connected by the boolean operator in the method's prefix. The `where()` method is a wrapper that just calls `and_where()`. $query = DB::select()->from('users')->where('username', '=', 'john')->or_where('username', '=', 'jane'); You can use any operator you want. Examples include `IN`, `BETWEEN`, `>`, `=<`, `!=`, etc. Use an array for operators that require more than one value. $query = DB::select()->from('users')->where('logins', '<=', 1); $query = DB::select()->from('users')->where('logins', '>', 50); $query = DB::select()->from('users')->where('username', 'IN', array('john','mark','matt')); $query = DB::select()->from('users')->where('joindate', 'BETWEEN', array($then, $now)); By default, [DB::select] will select all columns (`SELECT * ...`), but you can also specify which columns you want returned by passing parameters to [DB::select]: $query = DB::select('username', 'password')->from('users')->where('username', '=', 'john'); Now take a minute to look at what this method chain is doing. First, we create a new selection object using the [DB::select] method. Next, we set table(s) using the `from()` method. Last, we search for a specific records using the `where()` method. We can display the SQL that will be executed by casting the query to a string: echo Kohana::debug((string) $query); // Should display: // SELECT `username`, `password` FROM `users` WHERE `username` = 'john' Notice how the column and table names are automatically escaped, as well as the values? This is one of the key benefits of using the query builder. ### Select - AS (column aliases) It is also possible to create `AS` aliases when selecting, by passing an array as each parameter to [DB::select]: $query = DB::select(array('username', 'u'), array('password', 'p'))->from('users'); This query would generate the following SQL: SELECT `username` AS `u`, `password` AS `p` FROM `users` ### Select - DISTINCT Unique column values may be turned on or off (default) by passing TRUE or FALSE, respectively, to the `distinct()` method. $query = DB::select('username')->distinct(TRUE)->from('posts'); This query would generate the following SQL: SELECT DISTINCT `username` FROM `posts` ### Select - LIMIT & OFFSET When querying large sets of data, it is often better to limit the results and page through the data one chunk at a time. This is done using the `limit()` and `offset()` methods. $query = DB::select()->from(`posts`)->limit(10)->offset(30); This query would generate the following SQL: SELECT * FROM `posts` LIMIT 10 OFFSET 30 ### Select - ORDER BY Often you will want the results in a particular order and rather than sorting the results, it's better to have the results returned to you in the correct order. You can do this by using the order_by() method. It takes the column name and an optional direction string as the parameters. Multiple `order_by()` methods can be used to add additional sorting capability. $query = DB::select()->from(`posts`)->order_by(`published`, `DESC`); This query would generate the following SQL: SELECT * FROM `posts` ORDER BY `published` DESC [!!] For a complete list of methods available while building a select query see [Database_Query_Builder_Select]. ## Insert To create records into the database, use [DB::insert] to create an INSERT query, using `values()` to pass in the data: $query = DB::insert('users', array('username', 'password'))->values(array('fred', 'p@5sW0Rd')); This query would generate the following SQL: INSERT INTO `users` (`username`, `password`) VALUES ('fred', 'p@5sW0Rd') [!!] For a complete list of methods available while building an insert query see [Database_Query_Builder_Insert]. ## Update To modify an existing record, use [DB::update] to create an UPDATE query: $query = DB::update('users')->set(array('username' => 'jane'))->where('username', '=', 'john'); This query would generate the following SQL: UPDATE `users` SET `username` = 'jane' WHERE `username` = 'john' [!!] For a complete list of methods available while building an update query see [Database_Query_Builder_Update]. ## Delete To remove an existing record, use [DB::delete] to create a DELETE query: $query = DB::delete('users')->where('username', 'IN', array('john', 'jane')); This query would generate the following SQL: DELETE FROM `users` WHERE `username` IN ('john', 'jane') [!!] For a complete list of methods available while building a delete query see [Database_Query_Builder_Delete]. ## Advanced Queries ### Joins Multiple tables can be joined using the `join()` and `on()` methods. The `join()` method takes two parameters. The first is either a table name, an array containing the table and alias, or an object (subquery or expression). The second parameter is the join type: LEFT, RIGHT, INNER, etc. The `on()` method sets the conditions for the previous `join()` method and is very similar to the `where()` method in that it takes three parameters; left column (name or object), an operator, and the right column (name or object). Multiple `on()` methods may be used to supply multiple conditions and they will be appended with an 'AND' operator. // This query will find all the posts related to "smith" with JOIN $query = DB::select('authors.name', 'posts.content')->from('authors')->join('posts')->on('authors.id', '=', 'posts.author_id')->where('authors.name', '=', 'smith'); This query would generate the following SQL: SELECT `authors`.`name`, `posts`.`content` FROM `authors` JOIN `posts` ON (`authors`.`id` = `posts`.`author_id`) WHERE `authors`.`name` = 'smith' If you want to do a LEFT, RIGHT or INNER JOIN you would do it like this `join('colum_name', 'type_of_join')`: // This query will find all the posts related to "smith" with LEFT JOIN $query = DB::select()->from('authors')->join('posts', 'LEFT')->on('authors.id', '=', 'posts.author_id')->where('authors.name', '=', 'smith'); This query would generate the following SQL: SELECT `authors`.`name`, `posts`.`content` FROM `authors` LEFT JOIN `posts` ON (`authors`.`id` = `posts`.`author_id`) WHERE `authors`.`name` = 'smith' [!!] When joining multiple tables with similar column names, it's best to prefix the columns with the table name or table alias to avoid errors. Ambiguous column names should also be aliased so that they can be referenced easier. ### Database Functions Eventually you will probably run into a situation where you need to call `COUNT` or some other database function within your query. The query builder supports these functions in two ways. The first is by using quotes within aliases: $query = DB::select(array('COUNT("username")', 'total_users'))->from('users'); This looks almost exactly the same as a standard `AS` alias, but note how the column name is wrapped in double quotes. Any time a double-quoted value appears inside of a column name, **only** the part inside the double quotes will be escaped. This query would generate the following SQL: SELECT COUNT(`username`) AS `total_users` FROM `users` [!!] When building complex queries and you need to get a count of the total rows that will be returned, build the expression with an empty column list first. Then clone the query and add the COUNT function to one copy and the columns list to the other. This will cut down on the total lines of code and make updating the query easier. $query = DB::select()->from('users') ->join('posts')->on('posts.username', '=', 'users.username') ->where('users.active', '=', TRUE) ->where('posts.created', '>=', $yesterday); $total = clone $query; $total->select(array('COUNT( DISTINCT "username")', 'unique_users')); $query->select('posts.username')->distinct(); ### Aggregate Functions Aggregate functions like `COUNT()`, `SUM()`, `AVG()`, etc. will most likely be used with the `group_by()` and possibly the `having()` methods in order to group and filter the results on a set of columns. $query = DB::select('username', array('COUNT("id")', 'total_posts') ->from('posts')->group_by('username')->having('total_posts', '>=', 10); This will generate the following query: SELECT `username`, COUNT(`id`) AS `total_posts` FROM `posts` GROUP BY `username` HAVING `total_posts` >= 10 ### Subqueries Query Builder objects can be passed as parameters to many of the methods to create subqueries. Let's take the previous example query and pass it to a new query. $sub = DB::select('username', array('COUNT("id")', 'total_posts') ->from('posts')->group_by('username')->having('total_posts', '>=', 10); $query = DB::select('profiles.*', 'posts.total_posts')->from('profiles') ->join(array($sub, 'posts'), 'INNER')->on('profiles.username', '=', 'posts.username'); This will generate the following query: SELECT `profiles`.*, `posts`.`total_posts` FROM `profiles` INNER JOIN ( SELECT `username`, COUNT(`id`) AS `total_posts` FROM `posts` GROUP BY `username` HAVING `total_posts` >= 10 ) AS posts ON `profiles`.`username` = `posts`.`username` Insert queries can also use a select query for the input values $sub = DB::select('username', array('COUNT("id")', 'total_posts') ->from('posts')->group_by('username')->having('total_posts', '>=', 10); $query = DB::insert('post_totals', array('username', 'posts'))->select($sub); This will generate the following query: INSERT INTO `post_totals` (`username`, `posts`) SELECT `username`, COUNT(`id`) AS `total_posts` FROM `posts` GROUP BY `username` HAVING `total_posts` >= 10 ### Boolean Operators and Nested Clauses Multiple Where and Having clauses are added to the query with Boolean operators connecting each expression. The default operator for both methods is AND which is the same as the and_ prefixed method. The OR operator can be specified by prefixing the methods with or_. Where and Having clauses can be nested or grouped by post fixing either method with _open and then followed by a method with a _close. $query = DB::select()->from('users') ->where_open() ->or_where('id', 'IN', $expired) ->and_where_open() ->where('last_login', '<=', $last_month) ->or_where('last_login', 'IS', NULL) ->and_where_close() ->where_close() ->and_where('removed','IS', NULL); This will generate the following query: SELECT * FROM `users` WHERE ( `id` IN (1, 2, 3, 5) OR ( `last_login` <= 1276020805 OR `last_login` IS NULL ) ) AND `removed` IS NULL ### Database Expressions There are cases were you need a complex expression or other database functions, which you don't want the Query Builder to try and escape. In these cases, you will need to use a database expression created with [DB::expr]. **A database expression is taken as direct input and no escaping is performed.** $query = DB::update('users')->set(array('login_count' => DB::expr('login_count + 1')))->where('id', '=', $id); This will generate the following query, assuming `$id = 45`: UPDATE `users` SET `login_count` = `login_count` + 1 WHERE `id` = 45 Another example to calculate the distance of two geographical points: $query = DB::select(array(DB::expr('degrees(acos(sin(radians('.$lat.')) * sin(radians(`latitude`)) + cos(radians('.$lat.')) * cos(radians(`latitude`)) * cos(radians(abs('.$lng.' - `longitude`))))) * 69.172'), 'distance'))->from('locations'); [!!] You must validate or escape any user input inside of DB::expr as it will obviously not be escaped it for you. ## Executing Once you are done building, you can execute the query using `execute()` and use [the results](results). $result = $query->execute(); To use a different database [config group](config) pass either the name or the config object to `execute()`. $result = $query->execute('config_name')libkohana3.1-php-3.1.5/modules/database/guide/database/query/parameterized.md0000644000000000000000000000672711772106746025604 0ustar rootroot# Parameterized Statements Using parameterized statements allows you to write SQL queries manually while still escaping the query values automatically to prevent [SQL injection](http://wikipedia.org/wiki/SQL_Injection). Creating a query is simple: $query = DB::query(Database::SELECT, 'SELECT * FROM users WHERE username = :user'); The [DB::query] method is just a shortcut that creates a new [Database_Query] class for us, to allow method chaining. The query contains a `:user` parameter, which we will get to in a second. The first parameter of [DB::query] is the type of query. It should be `Database::SELECT`, `Database::INSERT`, `Database::UPDATE`, or `Database::DELETE`. This is done for compatibility reasons for drivers, and to easily determine what `execute()` should return. The second parameter is the query itself. Rather than trying to concatenate your query and variables together, you should make use of [Database_Query::param]. This will make your queries much easier to mantain, and will escape the values to prevent [SQL injection](http://wikipedia.org/wiki/SQL_Injection). ## Parameters Our example query earlier contains a `:user` parameter, which we can assign to a value using [Database_Query::param] like so: $query->param(':user', 'john'); [!!] Parameter names can be any unique string, as they are replaced using [strtr](http://php.net/strtr). It is highly recommended to **not** use dollars signs as parameter names to prevent confusion. Colons are commonly used. You can also update the `:user` parameter by calling [Database_Query::param] again: $query->param(':user', $_GET['search']); If you want to set multiple parameters at once, you can use [Database_Query::parameters]. $query = DB::query(Database::SELECT, 'SELECT * FROM users WHERE username = :user AND status = :status'); $query->parameters(array( ':user' => 'john', ':status' => 'active', )); It is also possible to bind a parameter to a variable, using a [variable reference]((http://php.net/language.references.whatdo)). This can be extremely useful when running the same query many times: $query = DB::query(Database::INSERT, 'INSERT INTO users (username, password) VALUES (:user, :pass)') ->bind(':user', $username) ->bind(':pass', $password); foreach ($new_users as $username => $password) { $query->execute(); } In the above example, the variables `$username` and `$password` are changed for every loop of the `foreach` statement. When the parameter changes, it effectively changes the `:user` and `:pass` query parameters. Careful parameter binding can save a lot of code when it is used properly. The only difference between `param()` and `bind()` is that `bind()` passes the variable by reference rather than by assignment (copied), so future changes to the variable can be "seen" by the query. [!!] Although all parameters are escaped to prevent SQL injection, it is still a good idea to validate/sanitize your input. ## Display the raw query If you want to display the SQL that will be executed, you can simply echo the query: echo $query; // Will display: // SELECT * FROM users WHERE username = 'john' ## Executing Once you have assigned something to each of the parameters, you can execute the query using `execute()` and use [the results](results). $result = $query->execute(); To use a different database [config group](config) pass either the name or the config object to `execute()`. $result = $query->execute('config_name')libkohana3.1-php-3.1.5/modules/database/guide/database/query/prepared.md0000644000000000000000000000675411771131764024547 0ustar rootroot# Prepared Statements Using prepared statements allows you to write SQL queries manually while still escaping the query values automatically to prevent [SQL injection](http://wikipedia.org/wiki/SQL_Injection). Creating a query is simple: $query = DB::query(Database::SELECT, 'SELECT * FROM users WHERE username = :user'); The [DB::query] method is just a shortcut that creates a new [Database_Query] class for us, to allow method chaining. The query contains a `:user` parameter, which we will get to in a second. The first parameter of [DB::query] is the type of query. It should be `Database::SELECT`, `Database::INSERT`, `Database::UPDATE`, or `Database::DELETE`. This is done for compatibility reasons for drivers, and to easily determine what `execute()` should return. The second parameter is the query itself. Rather than trying to concatenate your query and variables together, you should make use of [Database_Query::param]. This will make your queries much easier to mantain, and will escape the values to prevent [SQL injection](http://wikipedia.org/wiki/SQL_Injection). ## Parameters Our example query earlier contains a `:user` parameter, which we can assign to a value using [Database_Query::param] like so: $query->param(':user', 'john'); [!!] Parameter names can be any unique string, as they are replaced using [strtr](http://php.net/strtr). It is highly recommended to **not** use dollars signs as parameter names to prevent confusion. Colons are commonly used. You can also update the `:user` parameter by calling [Database_Query::param] again: $query->param(':user', $_GET['search']); If you want to set multiple parameters at once, you can use [Database_Query::parameters]. $query = DB::query(Database::SELECT, 'SELECT * FROM users WHERE username = :user AND status = :status'); $query->parameters(array( ':user' => 'john', ':status' => 'active', )); It is also possible to bind a parameter to a variable, using a [variable reference]((http://php.net/language.references.whatdo)). This can be extremely useful when running the same query many times: $query = DB::query(Database::INSERT, 'INSERT INTO users (username, password) VALUES (:user, :pass)') ->bind(':user', $username) ->bind(':pass', $password); foreach ($new_users as $username => $password) { $query->execute(); } In the above example, the variables `$username` and `$password` are changed for every loop of the `foreach` statement. When the parameter changes, it effectively changes the `:user` and `:pass` query parameters. Careful parameter binding can save a lot of code when it is used properly. The only difference between `param()` and `bind()` is that `bind()` passes the variable by reference rather than by assignment (copied), so future changes to the variable can be "seen" by the query. [!!] Although all parameters are escaped to prevent SQL injection, it is still a good idea to validate/sanitize your input. ## Display the raw query If you want to display the SQL that will be executed, simply cast the object to a string: echo Kohana::debug((string) $query); // Should display: // SELECT * FROM users WHERE username = 'john' ## Executing Once you have assigned something to each of the parameters, you can execute the query using `execute()` and use [the results](results). $result = $query->execute(); To use a different database [config group](config) pass either the name or the config object to `execute()`. $result = $query->execute('config_name')libkohana3.1-php-3.1.5/modules/database/guide/database/query.md0000644000000000000000000000113211772106746022731 0ustar rootroot# Making Queries There are two different ways to make queries. The simplest way to make a query is to use [Database_Query], via [DB::query], to manually create queries. These queries are called [parameterized statements](query/parameterized) and allow you to set query parameters which are automatically escaped. The second way to make a query is by building the query using method calls. This is done using the [query builder](query/builder). [!!] All queries are run using the `execute` method, which accepts a [Database] object or instance name. See [Database_Query::execute] for more information.libkohana3.1-php-3.1.5/modules/database/guide/database/results.md0000644000000000000000000001174511772106746023300 0ustar rootroot# Results ## Execute Once you have a query object built, either through a parameterized statement or through the builder, you must then `execute()` the query and retrieve the results. Depending on the query type used, the results returned will vary. ## Select [DB::select] will return a [Database_Result] object which you can then iterate over. This example shows how you can iterate through the [Database_Result] using a foreach. $results = DB::select()->from('users')->where('verified', '=', 0)->execute(); foreach($results as $user) { // Send reminder email to $user['email'] echo $user['email']." needs to verify his/her account\n"; } ### Select - `as_object()` and `as_assoc()` When iterating over a result set, the default type will be an associative array with the column names or aliases as the keys. As an option, before calling `execute()`, you can specify to return the result rows as an object by using the `as_object()` method. The `as_object()` method takes one parameter, the name of the class of your choice, but will default to TRUE which uses the `stdClass`. Here is the example again using `stdClass`. $results = DB::select()->from('users')->where('verified', '=', 0)->as_object()->execute(); foreach($results as $user) { // Send reminder email to $user->email echo $user->email." needs to verify his/her account\n"; } [!!] The method `as_assoc()` will remove the object name and return the results set back to an associative array. Since this is the default, this method is seldom required. ### Select - `as_array()` Sometimes you will require the results as a pure array rather than as an object. The `Database_Result` method `as_array()` will return an array of all rows. $results = DB::select('id', 'email')->from('users')->execute(); $users = $results->as_array(); foreach($users as $user) { echo 'User ID: '.$user['id']; echo 'User Email: '.$user['email']; } It also accepts two parameters that can be very helpful: `$key` and `$value`. When passing a value to `$key` you will index the resulting array by the column specified. $results = DB::select('id', 'email')->from('users')->execute(); $users = $results->as_array('id'); foreach($users as $id => $user) { echo 'User ID: '.$id; echo 'User Email: '.$user['email']; } The second parameter, `$value`, will reference the column specified and return that value rather than the whole row. This is particularly useful when making `

~~~ View for `crop/do` action goes to `views/crop/do.php`. ~~~ Upload Profile Image Result

Upload success

Here is your uploaded and cropped avatar: " alt="Uploaded avatar" />

Something went wrong with the upload

~~~ ## Screenshots Below are screenshots for this example. ![Original image](crop_orig.jpg) _Original image to upload_ ![Upload image form](crop_form.jpg) _Upload image form_ ![Upload result page](crop_result.jpg) _Upload result form_libkohana3.1-php-3.1.5/modules/image/guide/image/examples/dynamic.md0000644000000000000000000000645311772106746023675 0ustar rootroot# Dynamic Image Controller In this example, we have images under `/uploads` under the webroot directory. We allow the user to render any image with dynamic dimension and is resized on the fly. It also caches the response for 1 hour to show basic caching mechanism. ## Route First, we need a [Route]. This [Route] is based on this URL pattern: `/imagefly/filename/width/height` - where filename is the name of the image without the extension. This assumes that all images are in `jpg` and all filenames uses numbers, letters, dash and underscores only. This is our [Route] definition: ~~~ /** * Set route for image fly */ Route::set('imagefly', 'imagefly///', array('image' => '[-09a-zA-Z_]+', 'width' => '[0-9]+', 'height' => '[0-9]+')) ->defaults(array( 'controller' => 'imagefly', 'action' => 'index' )); ~~~ We ensure that the filename is only composed of letters, numbers and underscores, width and height must be numeric. ## Controller Our controller simply accepts the request and capture the following parameters as defined by the [Route]: * `filename` - without the filename extension (and without dot) * `width` * `height` Then it finds the image file and when found, render it on the browser. Additional features added are browser caching. ~~~ request->param('image'); $width = (int) $this->request->param('width'); $height = (int) $this->request->param('height'); $rendered = FALSE; if ($file AND $width AND $height) { $filename = DOCROOT.'uploads/'.$file.'.jpg'; if (is_file($filename)) { $this->_render_image($filename, $width, $height); $rendered = TRUE; } } if ( ! $rendered) { $this->response->status(404); } } protected function _render_image($filename, $width, $height) { // Calculate ETag from original file padded with the dimension specs $etag_sum = md5(base64_encode(file_get_contents($filename)).$width.','.$height); // Render as image and cache for 1 hour $this->response->headers('Content-Type', 'image/jpeg') ->headers('Cache-Control', 'max-age='.Date::HOUR.', public, must-revalidate') ->headers('Expires', gmdate('D, d M Y H:i:s', time() + Date::HOUR).' GMT') ->headers('Last-Modified', date('r', filemtime($filename))) ->headers('ETag', $etag_sum); if ( $this->request->headers('if-none-match') AND (string) $this->request->headers('if-none-match') === $etag_sum) { $this->response->status(304) ->headers('Content-Length', '0'); } else { $result = Image::factory($filename) ->resize($width, $height) ->render('jpg'); $this->response->body($result); } } } ~~~ When the parameters are invalid or the filename does not exists, it simply returns 404 not found error. The rendering of image uses some caching mechanism. One by setting the max age and expire headers and second by using etags. ## Screenshots Visiting [http://localhost/kohana/imagefly/kitteh/400/400](http://localhost/kohana/imagefly/kitteh/400/400) yields: ![Kitten 400x400](dynamic-400.jpg) Visiting [http://localhost/kohana/imagefly/kitteh/600/500](http://localhost/kohana/imagefly/kitteh/600/500) yields: ![Kitten 400x400](dynamic-600.jpg)libkohana3.1-php-3.1.5/modules/image/guide/image/examples/upload.md0000644000000000000000000000774411772106746023541 0ustar rootroot# Upload Image The following example shows how to handle uploading of an image, resize it and save it to a file. Be sure you have enabled the [Image] module as discussed in getting started guide. Assuming you are creating a web application that allows your members to upload their profile picture (avatar), the steps below explains it how. ## Controller First we need to create a controller that handles the requests for uploading an image. We will name it `Controller_Avatar` and accessible through `/avatar` URL. Assuming that your project is located at [http://localhost/kohana](http://localhost/kohana), then our avatar controller is at [http://localhost/kohana/avatar](http://localhost/kohana/avatar). For simplicity, the upload form will be on `index` action and `upload` action will process the uploaded file. This is what our controller now looks like. Please note that we are not using [Controller_Template], just [Controller]. ~~~ response->body($view); } public function action_upload() { $view = View::factory('avatar/upload'); $error_message = NULL; $filename = NULL; if ($this->request->method() == Request::POST) { if (isset($_FILES['avatar'])) { $filename = $this->_save_image($_FILES['avatar']); } } if ( ! $filename) { $error_message = 'There was a problem while uploading the image. Make sure it is uploaded and must be JPG/PNG/GIF file.'; } $view->uploaded_file = $filename; $view->error_message = $error_message; $this->response->body($view); } protected function _save_image($image) { if ( ! Upload::valid($image) OR ! Upload::not_empty($image) OR ! Upload::type($image, array('jpg', 'jpeg', 'png', 'gif'))) { return FALSE; } $directory = DOCROOT.'uploads/'; if ($file = Upload::save($image, NULL, $directory)) { $filename = strtolower(Text::random('alnum', 20)).'.jpg'; Image::factory($file) ->resize(200, 200, Image::AUTO) ->save($directory.$filename); // Delete the temporary file unlink($file); return $filename; } return FALSE; } } ~~~ We have `index` and `upload` actions. `index` action will display the upload form and `upload` action will process the uploaded image and provides feedback to the user. In `upload` action, it checks if the request method was `POST`, then delegates the process to `_save_image()` method which in turn performs various checks and finally resize and save the image to the `uploads` directory. ## Views For the upload form (the `index` action), the view is located at `views/avatar/index.php`. ~~~ Upload Avatar

Upload your avatar

Choose file:

~~~ Take note of the action attribute. It points to our `avatar/upload` action whose view code goes to `views/avatar/upload.php`. ~~~ Upload Avatar Result

Upload success

Here is your uploaded avatar: " alt="Uploaded avatar" />

Something went wrong with the upload

~~~ When the upload is successfull, a success message is displayed with the uploaded image displayed. Otherwise, when it fails, it displays an error message. ## Screenshots Below are screenshots for this example. ![Upload image form](upload_form.jpg) _Upload image form_ ![Upload result page](upload_result.jpg) _Upload result form_libkohana3.1-php-3.1.5/modules/image/guide/image/examples.md0000644000000000000000000000057611772106746022251 0ustar rootroot# Examples The following are mini applications that uses the [Image] module. They are very straight forward and did not include additional code such as validations and the like. They are designed to be simple and should work out of the box. * [Uploading image](examples/upload) * [Cropping profile images](examples/crop) * [Serving images with dynamic dimension](examples/dynamic)libkohana3.1-php-3.1.5/modules/image/guide/image/index.md0000644000000000000000000000122511772106746021532 0ustar rootroot# Image Kohana 3.x provides a simple yet powerful image manipulation module. The [Image] module provides features that allows your application to resize images, crop, rotate, flip and many more. ## Drivers [Image] module ships with [Image_GD] driver which requires `GD` extension enabled in your PHP installation. This is the default driver. Additional drivers can be created by extending the [Image] class. ## Getting Started Before using the image module, we must enable it first on `APPPATH/bootstrap.php`: ~~~ Kohana::modules(array( ... 'image' => MODPATH.'image', // Image manipulation ... )); ~~~ Next: [Using the image module](using).libkohana3.1-php-3.1.5/modules/image/guide/image/menu.md0000644000000000000000000000025611772106746021372 0ustar rootroot## [Image]() - [Using](using) - [Examples](examples) - [Upload Image](examples/upload) - [Crop Profile Image](examples/crop) - [Dynamic Image Controller](examples/dynamic)libkohana3.1-php-3.1.5/modules/image/guide/image/using.md0000644000000000000000000000742411772106746021557 0ustar rootroot# Basic Usage Shown here are the basic usage of this module. For full documentation about the image module usage, visit the [Image] api browser. ## Creating Instance [Image::factory()] creates an instance of the image object and prepares it for manipulation. It accepts the `filename` as an arguement and an optional `driver` parameter. When `driver` is not specified, the default driver `GD` is used. ~~~ // Uses the image from upload directory $img = Image::factory(DOCROOT.'uploads/sample-image.jpg'); ~~~ Once an instance is created, you can now manipulate the image by using the following instance methods. ## Resize Resize the image to the given size. Either the width or the height can be omitted and the image will be resized proportionally. Using the image object above, we can resize our image to say 150x150 pixels with automatic scaling using the code below: ~~~ $img->resize(150, 150, Image::AUTO); ~~~ The parameters are `width`, `height` and `master` dimension respectively. With `AUTO` master dimension, the image is resized by either width or height depending on which is closer to the specified dimension. Other examples: ~~~ // Resize to 200 pixels on the shortest side $img->resize(200, 200); // Resize to 200x200 pixels, keeping aspect ratio $img->resize(200, 200, Image::INVERSE); // Resize to 500 pixel width, keeping aspect ratio $img->resize(500, NULL); // Resize to 500 pixel height, keeping aspect ratio $img->resize(NULL, 500); // Resize to 200x500 pixels, ignoring aspect ratio $img->resize(200, 500, Image::NONE); ~~~ ## Render You can render the image object directly to the browser using the [Image::render()] method. ~~~ $img = Image::factory(DOCROOT.'uploads/colorado-farm-1920x1200.jpg'); header('Content-Type: image/jpeg'); echo $img->resize(300, 300) ->render(); ~~~ What it did is resize a 1920x1200 wallpaper image into 300x300 proportionally and render it to the browser. If you are trying to render the image in a controller action, you can do instead: ~~~ $img = Image::factory(DOCROOT.'uploads/colorado-farm-1920x1200.jpg'); $this->response->headers('Content-Type', 'image/jpg'); $this->response->body( $img->resize(300, 300) ->render() ); ~~~ [Image::render()] method also allows you to specify the type and quality of the rendered image. ~~~ // Render the image at 50% quality $img->render(NULL, 50); // Render the image as a PNG $img->render('png'); ~~~ ## Save To File [Image::save()] let's you save the image object to a file. It has two parameters: `filename` and `quality`. If `filename` is omitted, the original file used will be overwritten instead. The `quality` parameter is an integer from 1-100 which indicates the quality of image to save which defaults to 100. On our example above, instead of rendering the file to the browser, you may want to save it somewhere instead. To do so, you may: ~~~ $img = Image::factory(DOCROOT.'uploads/colorado-farm-1920x1200.jpg'); $filename = DOCROOT.'uploads/img-'.uniqid().'.jpg'; $img->resize(300, 300) ->save($filename, 80); ~~~ What we do is resize the image and save it to file reducing quality to 80% and save it to the upload directory using a unique filename. ## Other Methods There are more methods available for the [Image] module which provides powerfull features that are best describe in the API documentation. Here are some of them: * [Image::background()] - Set the background color of an image. * [Image::crop()] - Crop an image to the given size. * [Image::flip()] - Flip the image along the horizontal or vertical axis. * [Image::reflection()] - Add a reflection to an image. * [Image::rotate()] - Rotate the image by a given amount. * [Image::sharpen()] - Sharpen the image by a given amount. * [Image::watermark()] - Add a watermark to an image with a specified opacity. Next: [Examples](examples)libkohana3.1-php-3.1.5/modules/image/media/0000755000000000000000000000000011772111000016754 5ustar rootrootlibkohana3.1-php-3.1.5/modules/image/media/guide/0000755000000000000000000000000011772111000020051 5ustar rootrootlibkohana3.1-php-3.1.5/modules/image/media/guide/image/0000755000000000000000000000000011772111000021133 5ustar rootrootlibkohana3.1-php-3.1.5/modules/image/media/guide/image/Thumbs.db0000644000000000000000000004700011772106746022732 0ustar rootrootࡱ>    !"#$%Root EntryЁ @?1 Catalog'j2)  !"#$%&(j*+,-./0123456789:;<=>?@BCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghilmnopqrstuvwxyz{|}~ } JFIF``C     C   9`" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?\߉O~39t6PžcspfˏnU_GBM}> wY%`bdv]Ncd3wk $!HWR0AU[("P |ڛM-#͖{-ngHCow"~oc:r3]>9j u Tk9'1iR9I7#J:(|?E/ Ii%:Zkx%NydUXi}n[ [ZmQR .ܫ rb']gA\!* %y3l>g?65}0W#8#֦ "O<یEI?r#fg/+/xG3HT 4oLpE/?{j_̾+{uq<0زr36/i?tV{yއVSRK?Ԣ+"Š(*@ /Mds; 䞕|3lj|{X-/*O|޾,#bQpUPNAA|%6xW]-9I qc':t\RvgMĶFHpw%'W8ȑ%ͯQ2gd֞kH:nG-qWicDpgx8V KckHI$wNg9gu?Z{ X? r]}"r:sYI$18@H&wX[s:EzCCm{[vg~ΘB cNico7}3ʵ( <3ʛJtvcU{v!KLHdK5n#_E=mQFscᧄO .~ErWDVt 5QKv?|&'1>|_M0|3M7hʻ!7Xqխ6Vo vF6Q(UQTSI-``6p upload_result.jpg2h upload_form.jpg. JFIF``C     C   0`" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?T]7○tgt=3L,ڷ1w\.;DI7PּGo{4kdIsa,v^9|@M}wK-[+3`uVF!Ciomg6Qh*Gzdx5os] N۝I4P:t 3ơrmOAV,1B#9!KWzLhۺUk/UΟnX~'#v2iw?6ڕxGU:\!_/"6ӎAхtZ<9I&t/x;bADGB(995<+Ⱆ x.ȧn7y# ^4F@[]<'(䕚4ko-rO8w@lt5w\" ޟ(TѢ(((|{wK[tBNRgFI[PbYJHsB*j֓]dhb \6Ja}[f "g4 [krA_Z|KV7,r8w^Cml`ȯ%*jy5."$D#8khTuuI#_m|UgZ)P`zC >IA 3`R:w^kT񭮏}scpd*R=c+ᅥMLj*&{AޫJPo|+kZ{V3t%B'9+4zVTXC %޽}MS=[D0$(X?gQԘˆ(_昨8.]uesqo*Y藷L7!FzStχ cYPHPpjtKp<Jލ%tm ǎ{WEA^8MYb[o#!QeڍAiTc[uQw{6 Kp8͜X0kQxUĮ'&A-dPP2k4~[tc&X.11ם|A+zhM`"К\^VvI6:nra N¿K+AreN|+'#z‰su;_h=+O MN] !C1\0zgһ?3ՙ׫&|7GOxPUتy1OnxV]Ms/~T}Gƽ Y3  }qƾ~I<3}kIsGu^+\Z*Qk.W_6|vQi^3ͳ9%r #'ל {UKiVYW%4gUt67kPG"t^(V9~],7VouuͼWӶ +*aԧdz՗k}0>fZQ^L7"> ,mMT]$@< C_e[iDK-v%:zR79˧2d¸Ӄ_C'NG17FKv+QF~oWxũqq|e|&)ik4V}XJjsL]48յ%*wglk|W:ͭrlT1x7DFv4jZ-hD)Ve'8n׉ȓF:g,U6Aml#6W"V*u3+ѿ WxsV徽5ACoY crop_orig.jpg.@G crop_ JFIF``C     C   ,`" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?$.<}OwZDR кydB?9}+J{}SPD7s*1 ֍~AFi%Lx'T6s:]VѥBWosCUn>'[Auĺ*]WO}@h@p=w,S$Vw|@!-0G?9l,oyem:)yFx3>@4_"h|QgV:e֟edD,L?3ە47 yz˖%"_;>+%mvOpZGv/{$M3N책 X䞇VX-ȒMn4.Jo+Ϧw})qzzdfIGþ"NHeve TI`Ӣb ( ( (*ivwryA3XdTZo?^ج#CAi -^kQHaEPEPEP JFIF``C     C   3`" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?>8oEۋ[t"-CN͹Ϋk+JP1Pc9]{z{~j:EͥY++Ӄ|O _DCyo$$H)T2HcXUHUQm~7A.H~Z j Ο;'vFNcH\/5|H| FkAw:4bhPJTR;嶏7$s^Exl[$֦Xj?\σ/ex`0H5(?ƝxP-IIz>@e!=A G>EQ^]O'΋~׶iظSv=Fo _]EHpV+hgV.S]GO!Lq5 6_{rTQ^#&#e}͆.GoE\7Ҝjo.ShQEQEQEWkRFqe 3_5K~R6H#ʅ~Ayzk;LOVhVDU 7p+ë;\ 5 }{C_g-gp;WS <3,+U*$ %-̟x$h0b}k/$f'\$fKbwL"# bL'9\u9E+|ek\Em-CLyS# O֤׼U|-v''k<ëkn"PxRY:Gz=Xxݬ^cV4ڪf82>Pz;-QG)Cľ5vP* sz #mntiwEz YƒuzXX_Xٖh9B(((((form.jpg2x crop_result.jpg2xS dynamic-400.jpg2  JFIF``C     C   8`" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?eKkN}*HY9柫xYtլ̠pqW֞ K9K+IFIM5 &؁c,s_ .$'o<'νOhPi&)յ tc4`8^xg:֙oB27KP!8Z [1k:ϝ@HWg/[/#_E,z[F缰]On|WWfW<lR@ztQ}zv=o|Io_>&ҵx% 9ԡPI'Y}'SEήS*t?AaOşRѐ*s{cޟm2)lq]vE,ʓ)}×W);d@-_+W=V}y$q7[W/cw(Ąwmz#kI#i>P'kOhWqq |rB2-Q'ucV6d#!yH֎q}T@bÂ8 WhںyԣaЯL 8[z:PK9AE*HcVMFVZ۷]u<:f12w*HH iXwXmywyM֫k6%J,.숨ζ«Q_nV,OMiKI4IB ~U׵xτIXx[%g0"C=A+4ofѹ\ f=l qK ~,g*k+be6k~̾D[4&Fؤpn9_]ZT<~5 }gTh\7E0ny5؃= #?OZR9}J՞V:wO?[i,`xӾH]O$,]51CV^ ‚#9V^jj CPI L#jfyJ/ݎjs+{џ_ҮUT8Q383_S>~-'̖Gf!TF}s}_pUi֥]Ϻ߇iXϺlNt/Atk+O W\<*# #=:MgK[7^(W!܌ c^~Z]:c ۖr8Unc9 k9%u.U)vna*2ƩjP^7q12 rZGÖu[]68,f-+KIZW2Xp8WWV3 ֺp-zֲQ^ne 4V3|)W].:ڽ'OtAqIk}/Rt嬲Eϖy?O־~?Zi :G獿}$dgиr>v u=NjSqW%W( :W"snCkq+? "9qӡp%yНMw$EIyE8.gIi h6{vRm4Hs7N̓:ǑyA޹Z(XzKhχ>6YlmlJ1WggmScj2.`/_S p(RR[E<j+ z>dcQZ>fJIQP>Q涨Il&  JFIF``C     C   5`" }!1A7 Qa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?K}FSmJʮK*ǽWjڒ3*0Z?>;H--k2Iq /ݑG{~?ٶ)X##|#?3Z^JވZM[Ex.rrg>Azj\&71F gڣjc("l-'Eejk ]ձ̉ձ㏮?sC>\%^ \oCYuW&;͎v, H½?b}B $i~U¯ë 5)VKUǶ3-6 ־dh3={_v%fcs|>7[ *[Ȥo(,C{{MCt]CI,pO9iNq{B,g}I ӽ;/n"< Fd+),UYy IK@NguO" 3j~:5ڂU=*!$vAFpBG5& :)ʎ(Ϧ>!ӜNjӢgԭ8UVg~՚E{ˏZ~ tFI&u6cdZH+gQ(f`rsEz?W;5HI?uLMyN5ImA*%5g⺌3BPѾWsH^𧉤Z a85Nѭl_m$r|yzӹ#L{-۟;pO8X ՠ}2&M=+4}$I sQcYsia O={W g@823k >݄vZG޽La][ơP8ۇ*r"QO[A=Wm}9"sk<p²GT>)¾ݩH㴊'UJb@N9knyEq+/7>Ev=x~':ݓmzMJgTY0]<03W?_jj:ьR2ycooׁk3 Yl0xoG\W ÑCLDKzu\1N:ZqizӿV:R;Km1}[~!mX@|]"4mFs,qMY!IlbCxV6?y!gqwZпe+4-6MVY/,u *U'>[dW|9KY''im#E-|7{skHYA,8?0R0zqֹK Tx_+eu(6Ĺ.FUH}s^컥 oD4/d=}]wO@¹6ɫZF.Xē$p}xDr,K'v7jIhw[)`I8潃Þ"ӌ4''jH^&tV1,2)#n뻭3P!'S!}R+>Nd}UcOEzv \g|uoSzv3 \1oMkj縮5DaVq<]WO eWw=H<=5}Zv5&GS:``A Gl^b8uƒEVٰyX솆6'gSI>"`dTD ˝zgoA׳|$hy'iJ9-Q<^#|h[xщ\;n@r /wg^$[0hA QN<<=b{2{oṵ+_;TwZlFzoր5単g\ƬY5}~oɨ ܪ^5ʯV;dW6\Õ|tW33^IM~NJetvr4c 4*<;4Pw72[R7ϹqQw6|[sq67vvR/@A='k:?f^oYuzd)D}ؒnܙ&ϭ] SsF~a.ne,vUrPg^ET'qי73u726-F]#)s$+b;'zlLPhno\ci\z_[n{;(KN: 8Ǘ Efl ٻ;;(ۃ$|.yYfS\nky1̲s,\ymDFlWSwC݂_5<ʃOm1O‘)[Kn-4u)ѱ`n7[-dvzkεPiʋfDn˿]ŲIF&1y~\ƻifU3ބm*IDy/)Oj(ͬhs?mu&Vz.71g.tUʖU)y*ےV :mKMͷbj3&4ѻmZm~fLiV :Kim t۵%{iXœ AndyZ-ЇĴ;5[m즑M +ȅqWSe="}n|ś!6;{4P6W|IT(ELAY\[} SV[ڛ?+Jaę"Aw*Td֔M&~KqOҗ|bbbbbxldžxldž%Mq2Z%{b\NO|)=\9Қ(o:1Nm$y.hy½2SqˬDmk''SfJurPː_7Je.&pf:>Њi8 9U zH@R=TU zH1DO(yÜ͒?2tBB_I|5VbG]PGf;8ݦ;.A7yI[h5Vɦ=#LcqH1i4k0{mxkǶ׏m^={mxkp4O⟻~9 x:R>Ux#Rg0z(¹PV ӡW#YѽK4FzU}:#DꟻEc|hf\¹‚-9hZW3_1#KSVT@9|.S>%######## U>wV2XV2ea$y]wӰa\O]Ӳg3Ļcccccccccccc?Ԙ>v (+Nȱ9pXV:gCz\Ȑ+PA:V<Uh|˂´h%w9zF;Ȼf:=;#<<<<cMqC&Y9I᪳G+=j1#WMj*ޜ%jFjXc;pHFaد)8xaAphۙiw?y*֥r|2׏)ۆjF3yI"a){RNmy,JF=J 6۴Đc1EϲPZ.+QZIvȲZVCej+!;~W,lnW_U> 0>n 1m.rM*mbMXkҠo8k #$0yVFȽbPVy#4oMyM2h^~qp-ތ v Uy_c^3UZfyq,%[ziJrcy8RrRsg#",(̶^0 .dۨF3;w\~Ӆ'*[+&o3 d4: n;(>Rm'Lj%*4# zːҸFnU8_gLh #nmLƠK vҸYFȥ-kS͂iXX#ZհH!A $sZf9P<%T%ݪŏcG1cL|X&>,t??qShmoY__QǸA$qƥ#l֬Z*卺md lS ҎdV\qb8q"Mnvu"Dy\~ŒډV 84%4ܿg"^ Dd1/;E4oQX0kKǯz"E*f؛l$ J5Uu\GwRV1rbrbrbrbrbrbrQ{9}|^>N_j/'/ڋ‰`xC5Tf:Ebh,q$|L5cRLH-#̴A eV +m9kdR4ܶZ^cg%v] kjxhFRq:Vz鞸I W yc d QsPᓪI{=D[76 $Z.Za?t裧|qcr=x f4+xۈ$XV[ge&]pB˝j}hO0oG dME-iˮ^osu)CJAģ#+7ƷGbݥNicxE%j;"cĩrJv؟͹0:h NaspĩØ[\{9xmU[ jP刔̱Px$K|Xcbя_F>V/a}Xcbя_F>V/a}Xcbя_F>V/a}8#F@??! ڶ #ވ)aXS4@`d`HI`vNHр%>DSeN@j3PG+K)BJЍܺgNp4,:B { 0f`(;}h5BI)5\s璥% mEQAhƤW0{UYA#|Rt8FD4<;MU $Owfa 'L" dwAAQqEԎ1R#CC*5dA+09˦>ֶR Q1`|6Fl9dA+т>y5Ya"4AE:oW;$8U#e H Jfx@7t!u fMpE 5e*HXke8Bh5ǐi9au9@ԖaAĞpS0M` bJiJ@bjcy!2 vA3Rۖ oKAL"bnoC4;@r } E' R l#%rTQxj$H$1X>@A,m7O H|  A9HlbS3 "F %vp=N@Nw -BfQ-DP!!( L(A"J&:dEG1ӹ '8Bۄ43Y6VKI ')\,]ʣ 䄌SbDk)5$ @+a@qHqHoAH4HvHQFpHc M{Si&h[M\)>",fZWb%J6Ml 8Xe7G ˙FB".d4a1]TF = !=Mx7S-< oLY6R(, #vQD%4noAwH%V|M ~<޹C %{e8c*Bu Խ ٬)`HhBs-$/岙|Rb'I3aI&O.\070LmA@A<@iipRUXԋB BE"#B֯50Z:S%A`ZTmEF& ɢ/H & ܩ* pPхtX0T"[$hjbڊ+RBKC/ HphdX+!sE6 D( x!BƁ] !y@PAk!&jIBF;б "X!^!"dduBHP Qxwl?ZeIևSX=>$mB #E| 0kP@BZ 2-qq"5]T b%b2腮ӪmrlI#_^zr˗ + T%8'yZM) ^#A( iR8P3֞q$'qa΀Qw'8C{Q$"lxyzLE h*:lHzt@ ā#h|lK(OyrJ=yRTXYLjL*HnC( $A BGN-o` \[cA.-4R7NI>cG4E@@BEp 4%c -I7Hni7` pĨ*ŋ,Xbŋ*pe?!2"5=h˞6_`$*fh>jd>Z_rXh^ߩְ㏚ȵL: E*A>jZvA7KLUpl+7Irm9b,i!+xo" 5(  ^7f)|Ȑ"4J@:i<0\fHe )٬ X1vP9WD hrdsѸ&9 A2 t۠Ԅ X TuFؠ P i%d[`[i aӬ+X hwN 8Vh&qA00Q$gEf 92g ; $̂ b,Q", ?AAv3@-=B_|̀,0t)ARnаI4$IJh&G)X`@"Px:䰣y )9;%" = b,[%<bzȋg-- RE i%Tn,NPY' @`R@ N" HoyyR$ dg@t'H2E= ](ݔe{Bc5UQ2iWF3Ȑp?hм4~s p!kH0X?|ANHP2]HhS)Wv&9@(u" B&5' ՛)L*C ɀ *ַI!h^J6BDDOa pymʷ( lܓ .F)P(HVaKT "EW$焯ȍ!ؤAHA"QC# ZNé( FcPĈ  ǜ*e *t" Mq$;.:BA#ӱ&Q @cV,Xb,XbŇcm5 AG<ԱC2BJ7"@1 !/r]5^HZ, 3*$jq'#TU m\Q`\oE8WZ $ TuENb!s6F#ñ7-l!(MCPQ ew3 M \S^E4H u83-965$V\&ZY$j>G`K:LX8R1x4n͇=?r˗.\rˇB@!?ᘰCCǎ8 D'W ߵ ['8 9<~![@ѼK,[-\s 8F^,;#hH(Z`EUBC!arPDtg_4ȤuF^1[Cӯzt$G|_JDEqqȾ%75ի{LA& 5FdÓQKj ]1H8n>FBQz|,nzPۋiBT#c7pF@[G z4%6@@[9jfHx/} `2])9DO%fHx/P{KQy7=0cdm\e=}ȥ ]ceaғ}I9ؽnúDre2_DoS}PnX@CRIԟD\;<%; jB!e.ؒ=y].1 񢙣pL9|u" F'lj4q#t_OF 0 iCeC1ЯB¾VJwq?dߥHV՟6jQ7ԃB$] cǙU(WHc _+ S6rA8kDBja?j@7uh1g䡿ߐ U0UwE*\oMm!N3E=ercK[*2\K 00x9oq ֽLw.|hH% 1p;\uP~rX/ $hƜn/C( +c8 ݛ OA9 |\'*s`@F@ʸK*,%<cOCy2PʠPnHyt:![h7DcoI,I%ԛFਂ< 2K1ZDDr ܸ,lEP KG?-vO=d^ 7\_q~*UW/\_f\A&a&O>J.Z|&3fԒ,"GV!.e$ &j6)X+g}JFEs`^>: C,EpI%(KCL:{LY @u`RH9< l F`țܬ9;Q/#jSB{!vgj(Q;IX v345O%l^M1VZa l-@A/tk ܝ:UtJ  z%sX 7 mG`!`#K 3NNaJ20&@$Óq3"m@.G. EۉbL4Bp#hRP%P%P%P%P%P%{ޔsNk>p5XHY9Im(Ho qFAa[GmX4XIn:T)R<3E#xM`3@b@ٱ&UXd$=OZ$0َ$Ph/}逐0D0@pе8Zj{fOr}BJXw *< `P "P~ऺ#I?Aql*n;w5kq㹭s[n;w5h2$?Q>4c8@,}P_4d:Џz1cJ0M IƯOlPeoCX8"SJ$5F【8](&,o4"2-sO2cE l`;|)>v/A:^4 6:Kӂ"z;@ NF<@bZp(x"(=RayhYaB 0=^GQ&wz{T=6^gq{1rkhw "c@^O >I՞jAK~҅@$VElNր1Nխ{`h ܮ.6>O+;{ ]*܏H';Df(,cڦևLZmtK=印l8҈ &=MZgڦmy" gAdby /k=>i7 䟡 >m(LYOUDeƹ!ϓZ~ =bZ>-zr7z_XbnG4htnLYOUDeƹ"Ovx MI)^@Ț9D|z!/gnȊR ڬ7^VAIM àxGoEߎ@7Alؚ` LĢb`[X*d#sN{S9> [0'r>vA:2nK (X =c @0n1h1 `73̤!jL q(oFAh6D}F2 ~BnKH@G$Qa73{ݙ3U.Kz|؛4~wwaW61$!/7=iZn#֛=iZn#֛=h $%@#!B O2x҈ -qcMQFmo05t L_XB@Da?b9&8([(('H+ !%u~ ^7ds6yyFd̋-2b$ 9V4@O55O@J]@!æ6'Nt7h/[IދXh[fx\L2Zky: ҋ#d_ J43 0SɏC˜K8caHTa.<h36WhMR=Sy ,SG6T{,a1C睕=Iг AN64ڼ|jM64ڼ|jM64ڼ|jM] B^Sm}]eG& '<"Z hj`3rDn(qncDDm{ [q&Kt(%> ֽOCCތ#dѝN\CDd^@F՗Es$a'H3dLHd-m=A 0{= " {={M [@29=Q􊳨 ۵mV۵mV۵mV۵mV۵mV۵mPR+libkohana3.1-php-3.1.5/modules/image/media/guide/image/crop_orig.jpg0000644000000000000000000007520011772106746023651 0ustar rootrootJFIFddDucky<&Adobed "V)oH-z~       !1 "@PA20#`3pBC!1A Qa0@q"2BRb3P#Cr P`0!@pa!1AQaq 0@P` 3TEjnΨ$iC 3:.u7>wC|=% Lf²Vzdl4U欙cJ"D>DtT8EZPUUtbjVbh"_;T~ܖtۥp(E*\dB='D*&YB|dz**!ZjHMTEaj]Ef_7(~_#Ӣ2Wf#K6 D,-*<٪Z_5;rVˊd^h2c o!Ӵt5Hrg[H$A(KcAa$'cͩ4`PΚp G,̲ I{ #Bൻ||A.5-t;N7VSփ4hJλQ.x+ܵhgmݖo*1y(hkӍ2Uq/L'7S4,4zysr0G#t-dt̮ݦ Ƥh-`29&bj5=_qXDh7INCr贚u-Unk27:JlBwF.AxES'c,͛WR[0PGj|1Ks{WEvA* cVeҋХX4zvsz:W)',)>h.% n7D,IC^36MlֿJ]_)kz x~Tٵc@:OT4С2lafy!vV2bVO$y2 &<^M&8Ef"nF_|"w >'ƌ<Ǻ ֖$7lԧKF렝W ai9 ,7 gOhZNYPj'+#N2 [io SEKx1rA̾͊9soҸ3Oiz?,_m6jk@"/S5s*Kƴ=wv_m{j`XMi0ҭVuaMrKfZk]A]ah8˽F]FC6'Y̩*P~VCGse.K֕/-vOԼY]:[]vjyVio~VMB-MaJyNL]koC:BTy_.#'('MB G=sߑ5m3F޷C" J]{ubU 4?iڛfzKK|8.6߬%󫽡nIkj6sw943bay-'F洗)Rwz_n|:R;w 9<@Fl\ jd%\s-^NK3đ仪c!(ھ'e6 *=lP0S<3t#jV))jB F*ۢClBBl`mNYh1RfԄ#'7N}3ٶMHzfzfӅSLS[S>R"xgI3+]$NE n1LSzS'4QlhiqP62nL]{q0HQ8=&Zf$0+y5]:Axiq+4Yq*f,u762r4 '~d^Juɵi= Ɯc54KP^6P* fi6֕#2!`iKn԰3RԂSKX@T (?9IKt;|e`tBZ,ݎ7lg:X _@Ej^:кiL &p`5ԤDɬP=&#LR1#P@]ܗȼq=85w<'6ZK=uB ☧[w`xTYZx`8a+*BkvфIڨ"0(\ 8i2K`cvΞGwX匷p\R:̠\)?Хj3!dQP^OF0lyDaZrZ;p*  PH@H@P3r^N#3Z $ ,)`X G2׀l8Ӫ:t R)ւlNThj P$*$pX8BcJ* a"p(R߇ Y3ETQ5D 31%Ta:gZ`|`0A\Mf%x0aLg 6PS/T`Lǯ躸) vQmC+Cxqq+dr{uݥgEyf%p g2i\qrv§*!I₦2laJ3vĤ;tB ,8̵ ',aLЅ 8b1hA,UdiMet0d [n,޶A` ]W9sZx=!Mm2剠3l #q, 8Ʊ݌¸J4keqbd^3Zѝ𦃳XDc>ʺ00 ؛!ZǣjysqO;2R祇PUYUj{m2xk(H-l tx" EW1jʴ;Q-$b# bM^<ͦ #`1pYzI-ڟ]KUwGP.=XZ7G,˵YIP!'+x1lXs+}oW3@mh,1ru#aGpʦWb2{zZ^oB5*0 8v"nM@\ۙNL[8[ ]K^ n e-gN3E툽+Nf9)YY,ĩ26,3Ľj+e3j}cD&p+F+jZ UQuR!j(¬YRLh3jLa;si5gQ[QDk7lj [l-Gq"ءoĥrm CbWR}^JC11dlpBۈVr4FmH H3b[~ؗ0] `mLr̮oP1IjEj7cjHR6tF-IbP":n {J;V #O4Kk )R&j N1;JΔXn G;Mm#>"6FviMXbm"=-UyeE^>=e2TѰ*C a,I+*C $54ҕfqĺY^띢7VZ 1 ( t^;Ç>]BW_5҂O[X.-dU~[+WiF}|%5m[/eK./lLuAq6 EZ.9`b"C,|J \: "؞K6yOʗ [\4?==a%L ㋷hPon~2ˏ⟺ 6M&ٶmlbzXeOJ[U*)dƫO'blBp._2Bba麺ll8u9i[‘$^w<|FqD\ǀ@@74s>&rvSG'8c6 g<-8CXlb;n jW[#8U{5TG\EM75{5Ô0E$57,>Ok.F0,)Ǚ߯ eVe)<`ګ@f觩RO\~ hK+~zmDS- '˔/Ke]|?"r0yUT»eק~ҧn~Mi[NJ{j˸r[~Roшltɻ[kPNaY* 7ӦffffffӗԜ[Gn+)e曗VVa$m1xībJטŘjYًvc sh\mv}7f|ydAB=1Fٞn܏ջ'Dx͜ \84g)ǠDU !`0cV!I.7jm,jXJ`Hsϧʻ亗Bw(`ө B14֖F]ӧBr_Mgm~#6@9L3Su?mF/]EMWʢ Bs5\d6KVѬ# ʮ"D}f6!j@ :fnD#kMi̶QK XLt@AedG;jNZp1\KR,›wfV%fezA0+LN`Dtltk2 5Y?ŨY䁔G`(gsB~ZJjmnp0;N/>W".,L7o.,Y_ز uCM<7mt32|y*si,[5@CQ0P2R#x} &I9䪋 h\<FִdrKJ;TruGbE-df&z'ד?~];Yߗw+|U޸%p}/^}J[Tx:7OEᛷ AaǤLLzӞ谎LLz ^:ßA:bt"$aLBH0Bb|QibƙQ"4yAi:g=xc*E0Й XcDlM3th!V1ZfRmC=bbBa+V&"f} tf#v=Ɉѿ3>311 iVb- t0@|q > HV,hW0)nxҵ3,Ae"]Jm|!9XwEb@g:D 0:ٍ1 s ]&'HfKҶc0j͙`041nA&)7NafvE<Kh{tecE',a3TAwz } C A,,1>]1A H3dV6͓]ibg=V+\/4Jf0H13v6Ӂ/Lh"WP31dc164O1\]1bW^c3tvfفSc4)7CfF>!S1x+Y&fՍЍH;rT3zW;37B b7fFy[AB.bM`}с%ReJDpAi>Mц';`!.cŘuq*81hgr S]e}xO8eŕb >!:5_13>}f. #M ۰IybԬhJ_[Svƨ!Y)ԙ E\b-=VbKf$a6Eĩ@cSK;#{ !5I鉉m&D+ИW eNsӸUY"Y-_iFF YertanGZj-IIm鉶c;aY=s33A3?~P=G'#ՉI=G?0 ~T>dz=D>?2bğ@A3|>/lcDq1鉶c\LLLLL|LLL|{? ƍ2ad|AoH; ??&~L62dv-iZ vRͳɹڣRU{L 3W6^ڄBe2Vz2Of{~5KxU2WuLc &e/vN\+ݥ̮hЯ tNQI灙pgYVm,ʬ\*fQ 1z{.up]zeOnPG] zޜ[n,ZgڻY]010ӚQ˃#Ӂa$qLD[iq,^E+ӻӇi eM̩ˑ ԛjuxRQ$ Ld&w v9yќsxW}3(s*儞bg\' X$t(C(C ۠a<|2_Тm̄̒ kp'LrFBx'vNN4_2'T6*@&SqJ[f(WfPZ J1ⰄF1vE5:1ܼ}"I_L+TC _q|),pxIRt7Fdo!m&9W!E.BPt)NGțI<W2I\3n k9\J!n!ɏ"mɋ-xs2RJHZX@ UnA6|ד\΄nQ;Jfltes\UYImy.Q7ED[%0Pu (u!Q1bGEd#uf;u+.sx̜L=,cBvMhA\S!n:,v2/Ri<СA1]&>L+q(("v|ms܋| 강\%<ȿqGya$hky|eu3I)f3ѾӨ!n'AFD`c}G[PM婹3ը'ݮA^vjS-c"u#R4}]ԛ.g2a<xeَr{tnUZQ#ou&_kgLёo=un";9r~ƹc7C({u7^;2YoChLMm΄(sȻnN"/B./ɓR/j܇ksm0W B5y:vun'wQ.U̥¤]~hl&eR6]3mFijȘжivjH i6zwQۡT;teݙfRMFFCԮkZ227@άWn7#2-ɟI1RdCU2 /~6ݙ^dU^myDZa ‡R0!r{Zr3ϩdV%F;nPҤvfVD+d:Q\7&mD#oV> U}+?oi=6q E=#1x-%Q#{[a1r'rw+T_nhW,=tg\'^,N 9e=(UvTf\kLq)Ȯl}x. L'DxȜ)×rNּ]bLD/iR]0%/|=ȣ_\j,jzjCNp<.R90B? /2oG?|:q\XQqawO?!' "ي(a*oԸЗ*RA? kĮ`NVo?Kys3B|COK @|,3R}t*4-j2.Y˝K,'5`LBda[: 24"x*ق>HhK,{75;?~bzq0f[ vwPbtmr6BoV H8xLPcҘL"Keq{%'8xiorhw~C l3-[*8A6O%7;hZm+İ0!|8dGQ,n MBZK32j+J3- LvQ 0}RӄƗ]KϘ3רtq> PoR7ɲ^@=LsŠ]% SOM}J+ Tե.)*U_~!MX*;p Bbj?q.,BR5RLдiqmXV.b>LzQ*HG>##(!ܬL2ylW'V k~< P~9!!P߉C_tX]{6"VK-{NȐ;*`K;)'+[b ~b(l-O*4]iXNiƦhw d?y8;ArKԿP VQf172>YWģ0 7mt?{[iDv&bqEψlyU5-d߸{ܡ5cbhp3 ߴƚ+`EI{4ƣr牤Aq&PX*V*/R3uqsQ՞x3L }txb2 Ÿ~suƣA! o`Zh D'Ghoz?&cK3SU(>J,SfEr~>!ϙj1J>t5JpY0ܶa npHu`3qb74-&ƫq"+pt?h_p,w,e: x6]0vlf^cE>qߩ`b]&"AT9WeȾjTK1#;!Pasg<҆)C1 /<]*Q vKY8Pt,%0=A$EIt3V-*;Ajx~y-|SK׏.4mE %k+|~-5;!bZ0z9qx(7>a`%>HX]o30?3ţufML4ֶ_x bgcjM"VY.SyLf]q/_[_Q噕cx3:&b㹂B{GmiEeԸYܯCwztCSSԉWdfa ;-4jwv`hje3 /P^`+׮( ]=,eX(b9`Òv 72mAHvNZb=K@ax7~#c>g̽CV tՇ9tP*YNYT fYLd_q-(HR3g/,Qw x'n SfFhRo%kRtrK狊{6B:ZNt m.'i)E,5's7%qFbG~&BrS Ӹ``"bS :)a[A],3//ı;߈zJj)#{1 ew, .1#+>%%h_yr-K+ pxeWQ.԰sGXV<7ԬcnPugr*E  \;&= X$U{FJv稥70YnȾ 2PnBZ-BԐsURp\g{KgS4+EA\k 4ETc_hi,ހSEn!Du+ "+yc@gO7ыٙt7+s#ߵD}qe~R9O"KܷºIw 마H]`Y[*_R$0Ci711-‘X^6瓟4" aJ{gw"FgR1hf 1n9Wg$Z#Ӑj;*M%G2)Z%"$O` ֓V n=O/e*%EW%A,\Z`?Xe~< 5#Nn(Jܡl]x}GebG#c/pe"*̷J-ud( 9tJʘֱ=}Xt+p 1-xB8)wGO=LUIxG9eF rJUXL|AX_qtS' "PTfmxf.|5N`UP>) x?>W)tJQC̉SLT:bM9@t{V!i]f5 A-/Z$z>xOf*kЎs%17=S/382(.f1é~}xK:Ӧg$ʾgeX눲k4?wAԱ1LA%'2LMF +*Kԧ/?̻x&Y,2䁔,ޒ4dkehf^dF^?WˤNzXj%̮o6@ܮV{F+іCZLZ6%Aj1ei1LP7R2__L~&T1j"SI98V/្ w㔳#bu*壎ߏQM\)~ɗ-0M>2s|ρ @+zFPv.EW x3@'=j!>qVXxY1zYA8)٧Ŀ_Pm=MΪSYUܼZS^Pjrn|x,.r9Kpdqw\O2G$Z&s= iџiiX%DFk2ݕbZz` f=Tc0Y>gI|υTdj:wl>.izĨ}`רcAy7Qv1 2Dm4T x8.#;)<"dK$bB B*13_ǧOe 5\Aw&T7%(|wK."vZ=KL,03Oaӿ<±h}8'D¿jdXq %5Dbf,zYX&L`e!JD F~dkM6|FEg8f@)t@yrz d 0; i֣SE[FJs<r_jcsWf0ATR`CQc̓2XưDE |K[ *˩Vq}uR0%kcopXcAjjUh`Mⷡmu7kP5^Ō*I3*wj"%?2u빚f>`z#F0YUJרLj/h R+&+t˘~;#~ާOB4|f{o]CH?M=usCMqo/?g?m6wŷ?=i?wӏ}Qn_?!m4oxʕB* *T_鿢9*TDrgPM^ !/~O沥D\qu 'qG˕`_/:)r,IbQ,c|<9qw:r a ጼFGaPkQ2g l8+(8bqE˗C*TNU/.&HA.-rCeġ) C"ԶT&>(,IL%1.Bs pY6fip}pLbúͥ\)'!\a}#7<XB3]̱4u\`«F p`&#@xQc_ m(MVKܣ% :9W3vPS+T\!JWFE2gQ7*ɁiO7"k-aC]1p);̢K8|g' q'nܽA YP 3 6xVKK57\!SH}d8cDC*XC,ܼ YD )noLUP& aRi) c 5%r/5 X(2\D6Q aiiI \lyKEV,GGe\11J)YTċ ddIjI)b:xcF'[8!?MJ A`cu es1Y<#]a4e`Ĥ+4T8*TD 1-m^T 8Tw "Aܹ$TK"eAK6`xynXÂk+a#h18!G'|@C[Ec %ELeBPLW ba CyQB\3,¥*xE-Tw1-Kܺ\C-u(e.QbCT@E%(_3P53OЯ.EKc%ĥQLKAra @&Jbjb+rŔQG0.U fK> \jW/%`q8Rп|)9{`Hu?\_$>*TL~~F?Y?A??!#jTa(}'5*1sA@\ .\%spjW!C|%3*T>aN.SR+ag#οRH}W+#/p~ ĉ/G鿬Dcc.(TaqyqQ]q\IX}ly9~HuD_CGx~>$?BI5~%JМ?J~// aC?AABr$`H`D%GO%C_\eʏR%p`p.\# \ӿH9/8/E}'x#pf$qqp %C\0|_*}$?c*0HG⹾*Wr?1~P7ʾ~>ʕ/!CrTFW\~+?$}-HU N//2W e$MR+YI^ +SȩRJ_ ݗKQk WHuUMI eSw:'۵HRLpcmiQ*(3 nTev>.l{JDFKC^zk#lICA~i8%"jX+FcBI!])I&``zQpgR?cp.TBa7]um,k/fP~Jr f ?g"S]@@)YnkHs$# {iݗWZ J :V[~}: Jz'oj_2|7.x.a;7vks'H"@%,k|qq_!">X57Q荫t3 ߈NQ&!}oLVl2̹:^'?L6D!zCl{pbRr *4oW0S;\S5QI$0EZ q`U?Pm"Wɣ9dnnk]a~A?olhFjL ^=cZ P.-Է,|U]stR_F~[P:RTAkӍW=VolxDp_w_Tavt^^ȺJ~jc87X'жusN΢G%QgW Cv<9EHeWw 8q|oR3"]MfL]p1SGQnxE0qYąZ: eb4mte\Jz/ ~ 3H S ;JXjޥMB=~&00g%6 #~{t|E-%e2/Ա\rǹq`2Bxln^[ej@~P>^$TQc8@) 1bYT/^EkLPe^!;zPlbQ/㐹/OSV32Qefe8]|D>e+Cqv*L PQY sU{SUq2i~F]ќgQ"W_i`߳sֿfTK_Y؋~Bx@}Cu:|Jm2_!2ٍ2!kmV1''vDÍo 4{@)v {.XjY+YK iSκK*Kj:0O/R@9K O܊-Xl@hFU0LM"_p{Fn%KQ伍ᔦru77р*T:򸖃EL~`5ɬrZmϳP nǺe_aطeS%t{ @4,)T>#7ܿ#F~XD^TKgPǁG hAc9V0 d? M[oS#ո nȐ0[{H(zḾ+^΢_Uz\0ed 2`)&e)jE DO'.+&*']X b\Td+棕: Hл7$YG^gϩrj {"]*8>FTJ%CTZA1x%S;Av4gk5(GݏlՒ( U]YDkȱ=LDz]w6|Y\ #HXhY1XMDrdkC{kQ0EN,SW9 ă/G /NY]=H"ohKVMѬ7>=zw2WԱdCa_ctP?(b#ۯa@(` )6@@"oQ}fc7NwnYРYQa;њ1k*>X>q1:c(s_ܸ6K q72N)΢X?$]7 kP|q8cS2.a*[LE8b] ։ֶC3e½?y*7ge`PR#݊W iK冩#R3ڥ^p|r/]8*5&@oTЀMlz#bg=I]L3,w ðp ׽ep'eu?1z~c|} K7fm= xvG\Q4YPOl y%$XQԲ;KGdTS$v0,/PhocSBP '^"!$,J`,:kDWˈJ{u >V%:QÖ,3_V ~ qŕ2ԩ@(^ҕl6bf0q~ur H 4c"Ps#o1~"ъ6?Fh6~ɞRA ާgΪ\#~# !dt΄01|0aQx[ؑ@ {(Qi%ވ.ה3,NƢR̡ڦba?؉ZVB'ht;H1Ћ ec|oKƧ _Y~_ ͐%0XQ.zg8.YBښxz5zb%?D t]Z+ x`|GLˤLzxz:䖋5=WDrzteI]8:b=Jn=̧)k,P3e=j$3C#6 M9 =I'fD1"<2䀥|Y1*Y0%SExug2Nĺݔڀ[ }E2m _Z7G%{5+?|Ww\t>Dv4eT+间X_YPDhdL"RiQ ^!3Hsr . T];[ksPOVLשJ r0c>"sz/\?S #߄°be7)L'Z=w3Gt f*T|y8)pƍa} եlw$ir#n%XmRi]3Xzb_ kZ']Mk[z$4ܼjOx혘`3ycQc]x=x4=.|ĠJHma*6eR (J_D>P O4^.6|6O=āEkq6Awy.Ï*fXiN{uPi@ >bBtU ZSஙYR-Bxel*7h)v} ɱ\ ǘ(7SRO٨|B/r>y%h"0TT{&lX~oF3zJk7+| ~! .%Vs 郞:P߈;=A}L[,6د_Ro_h1"`1lEԷ 셪1LG(b(ps1Ibk$~?0r|`u.gıdgMcg\GG!/+eвs؄bsF.yvO$y❌e[ 'Bt0bPx @,y2 Qꑿ Aob~q > VvyGx+""O>az۟` +F7,Ztyb3,w ZtF ;"mVOIm8 ! i,ԧV->j9m&(7(Jb/C|h?wxut%󆘣gJmm,- w4VwRH)Rj`0^r,hAKx\ns~$Wb\Vѣ/s[BUYJ+/ X!9kLBohj-weV_5e@5 )dטPK_/B cKZz$ȏj{l$c."t7La;z 6gGZ=6?@)t~bª&ĭ$r4-9WZ v [Hi2c'qhe) ~N 8{@.؟k]"~/ļ5r}(` Ʀ uH2̾&Z+׸KԠ_-ڻ3TR A'JL0*u4|dRD<2l2L>KXrQ2+i NdZSaMn˞1Ȧ1GE֯Më^X) .`ڷ^~w>a⬹[Yfoz8~aʪ5^ڀ/!oPAna |a*VBeP1nEZ稥K ӫ+;[R0- aWZ9BȳoAՖ}"1nWU׉m57P-2:$ su< ea"}nɣ#F #l>IM2FҶ?4E ㊫˷bLٰٔA]@nDk`Ѕɘ M" WSӷ֪jU@ձaà4~2`-mFC.v1IGlaR1T;x%%+>Ͽr6+ǸQ0w^i  {5 4\Y6&8ut?l\m0XR!|C(ĴVT,3yvϘ\~a4^t1.j[)ivx m!P >u4ØuiF|1mMX˺P` ]v]ZV&ᨀ(RW tېyYb2`ZƟ ~%΅-Q`E09xS9 ;iŸsho8+~!@O'y5xӸG  B1 +Y)QJb3^vҏ*x]2RT6xzinj[oѸ]J/jWL'|Oc*P;i1[?b⢯BT%U#nLp6h"Lah)(K&H 6S'"=e#Csf"tN5b{ U锩2Z~QL& F(f/W;iEX>P@莼SۿQfp*Y4DPIļX$\7\y/ '19}A:!Fէf^JWDW=w(L+F!oz11Z 5(ӯ$C0S|"ʠ"xH_ V:Zπ[|@b(n$d8)|-;Ħw1:7ܺ~b$pҬt\xջ3q+Q^>Τ3AB;v-ThC~{Z4oKx0 . =XՁU g0$aJcZ6<]WۭٙgjFv5 $f]M6*1s4geв&c5xiDyefnZctϘ Qq"ba`I.G˶/:#7l43/H&2 5r0UT58Vw?%h"^(2*:W% +r/Mv43? ff_ 05OLtZ섢K 4/^C4#&w"֖ߴ1B˟TʵKT_h￳rp:w ܢ(ubx>NkYelث+(d2dS\#ӧ_=4onlr9"㏥XψMD0'%sw V [(p#9:_0 PRA(+w(Op(1S׸|Ey hYmL,3p6V^bML*woC+S#dпby.A!DMt[&i>Ÿ wvepvƒuE~`mv :m@~"4>3֕@|"d{ To(kĪ tN0JE.7t;e)lˋg< ̪:: _&X`ޗez'D3mZz❓nX o:=+a2Ec`qؿK#TR&lGn6F" šwxRM"o]1_s`6B(iAkH\C:8\CdwvUw1-y"?2 P.A* ]׏P'*"[ug:QQj_`fZ3?ݼ>J3f:b'RnҞeiijq*U35Peb 'xۚsȃn8aYZRMKg$M; :OGn1s7+R#VAFUUڥyPNQCG$sh7DYGB/;>,+ 1.Eh1ɴUCi$$ơc2>Wh_exev% cx<1+lz VaS;|óߨ< ܦѫSe?CvmIzVQqVE=}Z쌟1B^JeQWw l~%B7 Pfٔ7Mt\<xN"./f] =ĸxSYJ'ehzf."kum{ zs4)>,՟z!hWWx1EPr7={-BpZcMј9軗W*2kez;T]8*qNx ?nT_{7v0*9ޠہ3,20,vO3O:=gs߃?,Sgo??4}uo?qM O엮o~?W|:ff~|>5{Wm֟nsM|3~&;ms޻׹o~~;i[:';zw7<ù'Ii??H\B2\ p%xx/ ^! c6!Qx`}J%g&q@`a..ap"JPAQ*200epGD YN5-pb yfcLC3`Dbq<&9Epx2U1p<W a%ܮ_p!w Tg.La i+n@$0X<1!]M"F)\$ sQgEQ)H Q%8aÔ1|\`eDžF pFQ\ ubLɼz , [&X53=2PFElpC\G VV Ec))ib0_>g1G[v\H(D8bnm@8!ITh "lS5dKPCPq p RFԮPbGȂBvJW #)YcN(8tnRJIR@WP'L[W:Ɣ9,DפHk@FK,V哸bo^b bY昋Fj"#E&F-KIRszEA*1 Dȹa,`T)Tb^’ 5r&Icf[`%̩,y஦ 7l@~d rٹR*yxA0\;\f1ZKl;Й.%|43ut'1|K/^Pj+XZP&(-FK"˗\a&ĭ8böT@SSm0\QrθVbQU#rE3 ?d0bYP(bˇ&p#/qpp;(=ƣpH {L:l b:+V FX CPzE~ȸfݲ{ytTBT.\Ê. !a\LMEua(f 1ZQ%/IJ/d7@,P27Z2]KiKq6APTI\KCY!(HYd$ƄB* 0ڂJjtb̶ C.;HtaS EDZfQIJ\ sQ9|\X-DH:J3=Am@V nm Zw;O q|8*G O/NJ a+S{e;Phfd0TE|KN҉ Q:S1eMC2XpK`Â!Q$+֥FaTj YXuJD\\X}!BEط_Us\T*TFy1crR*W鰄J/' +5*W\Ԯ*T9q| x`C7MTQ9/?@?D8eJ1fI<,\,Rʅ#*NI<8qq/"B qGE`pi/BT sRq|W "9_yr1y8!*_BCpxp/!c$T$ 'TL#M&IQ8r"PBkrTIP98H>(&c *5*+p(q|\~@@a*U@À#*<ĕ,c2\ܮ.w<\%Gp"Q&wtűM YrW F<\#JP.\7}qaQbpF hEri$B2B`"J%C"fǁKNXF'",03q/x#/(.0`F(19<.x<,p*].\\0!+hN"HET#xR1V%R"q0\ ^">p<<G(*R\Iqu E+21&!T .9 .rYyy<%ņcԸ0qp3 vʦjn <MGL9Nxx8  _#Qb2 1y6\ ਕ W(\>%abC1b2`L*\\U7* & 86%Pfm7 KR K"ʼn })`Fj!13AԢXD#6do.\rˌc¯ŕ4cC^LJԸ%.`QBG:x8K<libkohana3.1-php-3.1.5/modules/image/media/guide/image/crop_result.jpg0000644000000000000000000003663311772106746024236 0ustar rootrootJFIFddDucky<&Adobed !%=       P!1 A4@p"230B$5 !1A"PQaq2r BRb#3@p$s4C 0Pq`pAa!1AQaqP 0@p` P_@WX\oƀOynTJqδGv盇\=qywyfsSsy-g|?;~oژwsZ kuŜ:~ukr}?E:\{}<"T VtXl\]xՒf; ٿyľ1[Qms58[Džsll,u7/7zB+3Yo4k:VY֜kdKgf:177lDžuߗ20o9].k]Y'Zߩuu:&o^WkuƗ=fjkͳ:m6z[e뎧^` ٿ{UT#M5YiF53R!.;7dNKfX.˲mbN]n |?XDZ^\ t%ݼ`VMŻ ~n-Q7JMiSnf"YZ[̬QyN 0Q l/MZz%Z,6=@1~E OI˵/oԖ^=%Omfm.UY͚ cʉ4L{^D]fz cx4TȔEgecM_ ERFYTʕ]R%`5TU[f겹dJ'g;'Wj_dtWcmZWm<<[u+&YmhlgvxIm^_V,Vvf@fҋ,;s+z+y5HG1wYBoSӜʔgԲ9üS{ݦDҞj&[^c$5/t79QEi$j,LvwRΩjU,+I %eGLB9MSTV98נeXXAeN:9S5䶶*=c.NǸrج'}ŗ PyRguV+Gܓ?f,]tm;˛ylv#cU-.nY潽3Euҳܙ821>|m2 gyF3;m@O Wv1u7GeidͶ2{j;bx M3׫[#G5lZVFX&YOEG/nrp9~*9!^[rC3PrCo=Vն2ځ`g8%ߕܷl!ՖJ*nHnTmQkPV(}OcX#R)`i49jBTcF_]ZZТym4(7l <55Fh~ uV! 娵IJLдF* 6QQl+jhVb>\6^ZŔE-Qڐэ4 [[XmWUI|emO o&L#MbkV(n?MTNjti/ 6:" ABAnD#b @!àmVűUS9Q!f4B0UHP B 7@ZQQQo4N)4o𴪪wM{8,Aȹnh i˴Q(9n:j7pҜj Tk;U@SSz-sXݲRy:k໷[7KveK,ns2&JFrn^[{qq,1fMóY,˿\Iqrylcf[uquy%+$lJ; %q5-ѺB@نL28k.m}{v o,- L)1LǼalM,fɮVD!zyLo^SzRѼ{tgV}Os~#MM޽ !rܷ-rWo?8)1O ~ $#y]At(hꆣA;'t+A;Vյm[VEEEEEEEmTT]@GhptN䜻N ;+?oP: Pp'|ƢڨkmTTtT0x0irMO=OUUUUUUWZUUUU_? , sEC44f-.g1˭ߟ?=B$Qƌċ ?4p$d8/=6I| n+R+n-cp])b>9B wdǺ.J<* ށ09e/"µwnmŠej7*Kjt"I`a ZOKEGLUI$U 5C{L awP}ѵPx35♰6Di94t֜K79[V&xS,r U0;{Z%a/WŨ< l%!Qj PlFqCѝ@J;ZVkصո]*{I;U~HjลiP~#bŸvS8Mq,vok,.qp۶ нqKKF8ar/~jc=TC,Ǝ k]*~6Mi[#b֡ڂ4Ks;u0ƣnCfk“+$_+rhS+5|ky_?<5r{Ƽ{Ƽ˗$j^ύy_] [r{ƖHe;Ó ˏM*IƷ•NC̳zˉDtrtrnڠ V{9oC>U +r^Y`k,904=7owW7ԧ87KɎ'A ǓY}+t'^ڽjџa'@y:G re^\KX{MvrvLI]u])kAȜ'W] #$LβzO'}#x|9:+:٠2Z@%79.Xﵭr`O ǢCJэWZPo˽=2[^s1&$Q1d'r)BTaqTo,ĖpO(ROe;h gn 9-~9JmSmItM2F yDzގs~Z9JmSm: KH^Hs]"t7\n>'vbؙ]B2Av֪}ӵ+t*94L{M:Gi k_鹭 v!5|pKRB V6)ĦPT(dO{~G1^![ p!p,6% Nti4s,= 5.ű2^.Uq%༗B.xՍ ֿ s'rWҬO Ɖm65/@.nc$5\~ U%8Ѵ-F;QG~Q&/}7Zv_57"+>c]g*{y-gQ!:sdwT,d(]˭?Wiv0F{oM#e7rZޠ:?PD ݱi\bJ)' >Xm̌"buK$ JTi?fM kZn] J1 rLE$ҙZ].ۅZ-U!eK(M"yCY!Z ڨn9MP: ubBӡ2H1hiv):0ّR;$|fpk-G4*fR4f 9?3jT1&q^8%ذѭ<3ae4F J VK7V ږc\E<(d3:,[\bQ'U~nQ{5f.ES>4GV\B/D%4%l&h"tvOL!7ߒ ߽EmjѦ^ή[UؤW,Q襀_zl$ T]F٥jfq$sxPJV^4.{O?ӽ \mЧe6oHDucNetYp! BN7ڂ"wzQѥ4XCBq'vћzg-s3JhDү]`$ %vN &ƳέpnB74!q%NkRs݂M=mW113r h!* dNh{ZYnU'94f9TNG*e}3ue(֨_NOwr֝zcE]"m3P]Zps%+w[RNM/΄b lRl=t/_XS$~/Mt%p~zڗ;5*0,4ʬ4lU1U([nN^Kpb=b_.5gR.wU1[NU2k0kVF^+?ިIfzKjozKD Sڶ;ziryѴũW4&RڛދQ<$('T֤ δ6Pi/ڦޠ>OJcZhZ-ʈR L&N o~+*~MM:i˔D-⛢<2B+ie侩AuױLRqxK< y>|z&*ȠW^qY(xN57Ċ4y:څJ5=S=>%:?%p|qELy멡_ 46+%+PQIPǫEYخgꧢ Ӓ%ݱF)Ԫhh Hܼ~bi"7Ta]m }mFM aR|_R^KFf#wmd)RfBA0 @_: ,lGjr Q!06QV.ϻn4fPfؼC95*݄e"`қo7 0$ hRN2" j \3[EMHnH% KEN –`пaV1 M2!4h8OlVl>@s~Rҍ dGI.̺j:Hl9TU2@  A aqpSDBd:7|!lMTKh$HEهh"­W^ҠG!e0qbG-;Dj[T<B(a%aN?%jbL+ &jfl42TW, l39Xr ]Zōާ. zjmOXxγSb‚]j?7 u+vͮu4sJ12V2I| UeYW]@X%Z"2B ҊZmhdE5X r&b-h-{JM+w%PlɊk*Ŵٓ)h.CrH3DVk/BnCƮ=FXCYSݩi]a8dIp{rq Q`,,]+I;Z暯MXjFM4MNOJW?UW~~VPg?!TV־Q1\ΰhZz־B(8Uz80+ 3|;B=r^UH{U3~m-*oQ>8PFJ%Z3oj}w7/j=qJYX}Yq㶌_ZjTS8Zn8+Du5 BQP(y.G?3jji~g|?|*aSSSSxGpPB%qy+***?2<"%|"_%<? c\~/k5VW.=mK=|ÒTa]qޕ/j57|U:ϓ/zNՑwj4㏮R]_~)hD}⋱w(>~1OoƦV0jn&.2LnGtww__L1ZzEb`nzVSy֣ަEin BPT*>??!ѧ==|xZc~">iU~UljjU`ƇMղeMjݤT_kz:Tۍ[yr5mYzuU`W`3j/?u/WC#O}zʄ<(rHְSZ+u*NEJa?a?(^?OSG)sJo>J)oJ? PK?UNԘEGϧ'ZO BZxOjpT4^Ϛ.K#QN:O_c_YIs>j(8qw(um½z7^mi#(pN?i}Zϯ\1N>Ҵ3Mgiޟj?QMmMj|j I$I$I$I$I$I$I$/C$I$I$I$I$I$6Щo"I$I$I$I$IRI$I$I$I$0dI$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$FP$FK$I$I$I$I$|A$q+$I$I$I$I$Xl8m2m$I$I$I$I$ŕk$I$I$I$I$EC D$I$I$I$I$Nh$I$I$I$I$${ѣ\$I$I$I$I$ư$I$I$I$I$_Xc$I$I$I$I$9A$I$I$I$I$Hkߺ5$I$I$I$I$w_o$I$I$I$I$':CoP$I$I$I$I$01$I$I$I$I$l˨c$I$I$I$I$fU$I$I$I$I$Ү,$I$I$I$I$i\-1$ڎE NF $~TgM[SpB}k$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$?0dUbY01EΚcJ +rU܁"? BV؍Rm+˰C*bJA_rdnli"mKEV&0љڈn]<@H֭ Tq޵A0.Z;1 O(5L #@nj3k&KQ`YtUC[2V lʡ-dB$n¾i X'V7ꌀ=u#dFrĤ~o&1Ba ;oL 2AWѶ,Nd`q7$ RI )Y ^]F'0 J +v=o;&hV z"0'&)+#J쉥&rq,v)d@I%)Q R(rHCD`%ΖlH*EhTYb0IF<"j[F|W )0Vv `CD|fVha(7o2C,ږflaKN8F|DЩa2 P",؍CO!SXv@_I"7oJADର0sC n17,SBc1pGI0(g5}(ats&TXAfzQŨ1EFRmo0h=C#޳(ޛqH CE-qQ2?u@LjxD2VnV YgqB2I7u`*+eG̺*Q BKlRfRm؟M d^ڊB›eP#+[8Lru!z0 €"g9/d]1PdZI$8XN_L vAᅞ0m) '>u"_J-w $faڦ$嬆[Ou/biICOpq j;_zMa-0:9-~^0!by/IrDlY' jP _I`ϭ0"A[@_qR'W3D|Pr.[Q3ɮ}w4MlY+]`/ VXwJAD5gP65>TDL' "Y !hhzXC*|޺848Dl6b|PI*u*"#L&U!cFjTP!%>{сKHX3%(mRQ\]xy/nÓ*8V uiĖF5JvR.8kP#1C+IGxNH]қM>pIkq^1䷒$O*De3UZ,1V烂7֠4KcZ&b@AI\" ;tG[== YJvzvNi}\Tۉ,K|qnEE2Boޅn!f$kC;ԫdL40>V W!Z&*1 :iWlG".td ww64%piNԉp2q vLc'+/:QR0ڒȩ⛉ƴfֈ#yRJlVHJ u=xXQb Ha:B\We4b-бFT/N6 ݡb. ԓ*JZ0I HXh ԓ*EТe! &C@P[KS,{퍔%: 64Dqi,=j8(lH0 DW#fJ^ҋmBo JFcLK"/ 5kTCmZ47;-EHK-!BefWoS="%eJle^©i +1!1BJ`2 (CO m 5k|ꓒcG .*Ȍc6/Dim! X߽\tGffXYq~5Y>)9 ץIj]9RZ+s7:$pm]):ɛ'8p Lflbe_EQ/*;U$L&"0TRTd.JZ'Ҭ9}ژE+D@{g÷MRnABIWY QdDD3,N7"* Z=(XU51 33 DItX`؈.*+Nבs%-2VFRm hC(oC G5Ya+=rZ%4J0KÔ{sMG5Ho]bI$r8jC}"FiL S>ĐHT6 |[[X ! (]2$FTU*++nۊ(J F$Я$B@ŨT9â 7)l"AT+2>EWU* bg^JegW衒+'8 1/ͣF4_Sҿ֢vNHa "QP 7r~*GY?IԄWE3b}T38<7|ўjδeBcwgJb>OP,N].9u/riTmoZv߇ {ݙa3Zq(T6=%ӎvcoBoSlzũ fXo엟#@;y8_nQgt苞2L1z8[j&~-’<\fǤqҦ,@eMBѳ_Gh ^8Uz0QH蝎85Gh:C+ Iڠu9lBn օq}wk|b"ۀbhNSOϫL0PԎm:1fs]L=Uzc?<2ͳW닂Ӿ'au4JS<ͦ 5whʹ:~BƘ@ u ja så,N#BMiccjO`W:ΣĨjs+F\(AQQOM=KxQQBZZ('/<G (%| M4P~KSKJ9y.N^CSTTx |'*<SSSSKQ\)ISSST( <7/@-'xQQQQQ%L" (fQAQQQSSQ_ &bKSSCSE K<4QGG3TG3~GG7m/seVC;۝W,y9o{R-iL+߾tX2䙲*'ěwpc#zY5cޚIͷ e1R;$bcjRnÞ$X&d[uc(8focv&bmG[N=io.\ 0c p=iMc7w s9V @OI+\W:Ψ* B ??}QM ƽpc?u~0h8w0z-OTڕPD_oxA0ꞙCL|bؗU"2O3xPlm7S/]ΕwSdeZ~Z&WD d5Ngfjva2`.E}Z=4^OR1-&2NXΈtG&m8#n3bUإ`<]'Z  ?Ѓ>5bi!l߅kaӣNâoS7I.k6'7d}c'Δ /CwGQ@<6!)vҲ^tA,m7CȖ#k@.̼?XgKMj-Pl$L_6:bSnξBW"hgD ju:?u: %rtJ(Zh54> "X\JJhQs? AAAKCOX.gQRTPP~(<#(TxxMMMa#!Qiijjjji[rJͺQ%5!%K\ժlomC'Js­Ooz:W'f퓃s f*/]0X`m6NJZIԒ3PH=-ǨvEsNo8{zYݜY8 yu}KyRA/)׵$Ly@/Yb7 _`Cه-YsiS=$#:ǯ˵9C$߱wu@^RbLHqcv#E_.fo=s꼯 "HL OX"t>ۜUp뼴=#"[ '!h hR$xӱ2POa :PQs][w7&ugI 5n3Cmɵ-i>$ۼG[::z0GHFن SqD||z髏>xc&fH/'1JC=9Rαz1f 7'\i$MFtZSpcxmLvJ=6T<-;܆ 33%|rNU/o9q?o9=g,I3>\g<5\4lCj|T-/>S*kmmfM Ԫ57r1=Lv_ʧ_ܷ=r_,M:xuT?9'zSNp5yIt['jT{KH[^g)fw5^_5促)qK`]qzUa~#~]<__4EWM.4-췵_H1SlgJW~ Fכѽ3/%G/ϴS;W=Un2Gz=_g|q]ASK81e{[zy-]1Y]|KQgXbǓRܳyV҃=>3ޯ_+|h˥ފYw!+OjCպ==Djʲk9kC=<㚚;fOg<Ϋ>}相Wz)kϥar}?9(P] e>Ɨ}G7\+KTdӪkNx1~[Y,.ONj|]j~u|7ޭGE8^q0YN^=>YQ|[nX]_?1]<*ysѤWw/g^ԋ#ԾSsuĪܯr[.p{cgnayeu˲ZmGP|_a;g4*GQ{io[xG ޚj=춈d%(}ܾY+fyZW=g{dyz]&%I_hyu6u5feJ%jlJDc笩x^Vo&r_=i[Z=2͉RB R7RfapO9@o v;PjݳR+-gpcSTfs[-WYԶ,Z8JtXpTV^t .zcQGNT"U"iԗYٛ6L753=15%WRDNDi"67/W\VJੌY}i>NehLtk:n27MJ6tmή,.{eh8ttCY_U@HS,K1[/m'VHZչ,^Y%%8t^Xiiiiiyɦɦɦɦɥ&&&&&&&&&&&llll^iiiiiiiiiiii|ɣFEp_.]"*7'^iʵUU_m\9sqJ⿕O%%RVI{p9y 9J"b^M,m ]NH*l#''19הY=̮~HpcE%S/GW~-]8&rxO&ߍkV}$ip9xV=]m|[녵ߓǴu+mŦ*n=-_i\WX][Mʆ qimZl+ִ8ٳ](7ޛzoM7ޛzoM7ކBئoM7ޛzoM7ޛsy?#Ȟu_2yȞ;=&{D^ee{'QsbO{L߿;7h =-%UGNt/EVE6}]=_==='{羾{؛blE{Gx hb- '',EXK`anT׉7qe +K4/'{eתkST!W|>&'I@`]1vb20kdVJBQ,ј1(1'NcU:5@ɜeAE?6W<0>]@s9m嫬QZn̽900'Y; 1(sJ5Zԫi,zΩeKa30۩LX#˕mD/psfb[|//nֲX m!s5E+H{a$NT&"OV86<)nrEoWx;\ ^ٖӸr 0S+vEvfV ^Lsvlskf'o3f剙WCWli{QeY?@*)İJ'u1( c4Vpks+@'!w%ŒNV{_UZ*E/%-Yb` .WfJ#KxR4. 4@ų'8/fwtغ9N8,Pk =9 7C",V  z6㨏I ce k 4* Sm5Bp\ޢ\!ݸ Ă f<,lY˫BœJ;+Q7}GV.W QBS_sn֖,%B#\pÒ)QǬc2QdWxL vcHf#mkn? ;TDXևh\,wr[d5h7aTTPj'Ev]UMQ_`=A/QݍZ'm zRYX(אEͷM|\+M49jZQo[ 3XoHeX[#TC4V"nDLb9գ'&3D᱌K>ѷjY 6QimuUSu:Y[wF}ʗM;n{KQj *d5CJ9f%|!0y7,)0 o3qgwe;6u.՘bEٮfMMbrh"7N:ZVX֤ͨ eꔯ-wva**aW9W1OV14u2 m>RIHWGɫq|; Φ|}[j,iJ)Y Zp~Fp#^ VWzEoW{V04SSS3330::#+ropo<_ K\Tbꘁfm''R"VrΙZFf05a@eM{٘E= -Ӵ0l}]{LTӟ |O&Lۚq1t1F>ojl (YiM/dn ̾#'1LgY&I EY 9 flT +6;Lf;kϚp~7Kh-Ia?R;7f{,8 Z䨌#fTp4MDa aA4<[^ r&a}E܋t5շl㼫Y&w04=Q,~d c@>/!Rg{*O|O4., @O"i2'OX?p?ณN,''SxRn<*lefͥK6m if؛bm&ͥ4OGၙg:>|t73x73x73x71l=333333333335MST5@fᛦnfofnf雦nGQ2<,#~?AL0GQ1 ř`~LB:cLxǃFL=gn LPwfBbj'Ls2LO8!|#8,a&z * ,1J`n #0@'ψfE>%aiQw, Й=P:3EnqUđ#LŖL1cFA1DՂ kD3T(3B#ьH>ƀь{k2N21+BW0՘D1ӵF3?TGs̈~{)`Lj3%'2D0}W̞gЏVEf(s*t#A F|g-3ˢƆ5LD0L{iWfc.&&v dG˦c#y$&k<#!b1_ʞ X 9.B1Xa1FO8F"4=DNWŘEV?V|ٷ-g{_~l|"~{i ~`3ԉa\%#vo1yX_= |LM3L)6 +g#f Glz t#3by& 4M3L5yH,bbianUbbbbbbbbbbbbbbc&if&&&&iAψ\ρ|ǗLS3T335LP鉉hfff|yaM>AGzf|D/!? f|x?Gq1111C>abcbcؘYgfj?XdqtRL7-?d?K0^k0^k0^k0^k0^k0^k0^k0^k0^k0^k0^k0^k0^k0^k0^k0^k0^k0^k0^k0#6jV;^$v%ߍ/n:cum cj^N2ݪ046.xІ''-*Su1 byg.iz=i z]288G /?qWPBS1 c#C@6" +mWKHi}/t9Li?r2j?^:QD2aoRiyP v-Xq-".ݫL댢X[I1P3-#Ga12@ݕaDYoaDh'Χ8+{ {Pk([g >ljb"jjPW֩Bg,qDXqf(OL> e2~L魆 @$-< h Us CS$^ š ?Ӕ#xq\X0 i@FqK& Cp*{*1i]oPt9va@ܵte1q7i`C(wCZ#eÒ bsZ&g:@C '-CPOXSToH x_`#b#ېԨb}XVӈ-S# [Zï8D4{lԜtu# =<5qKSkCI=3Pi2{%,-r1]Xk%/9𽑜t&w?22G}a!R*d:d?m"No*rDRKbcI4"/*Z!P'Ǫ D'~e>ԅHiJ3Y KTjec:E!!K)@ T`DZJGN~DH'mwPt>fդӑӈ9o^h.4j-!0zqhܴF2YV"1Ԕ/{Njf=ε' Poe> )겟)겟)겟)겟)겟)겟)겟)겟)겟)겟 z4UuAQ|:ê|:ê|:ê|:ê|:ê|:ê|:ê|:G%w%r+BZVUS澎HO%oo~1Y|WkK䫇ZVUjZVhVhYgE^yxtY$ժ(/]ZX*ީv41E6mt˒#0(~i]56Stb+4t }ʧd*r ǎ¨w)j-@u>|Y>6 =ihUlJn m(ܰj7(v7?JOxO&L{bOl+j-^* HM ַjbmUܦ|*mkنKMh .Z\qVP*ELʻ0ݪq6D]ݘTU '6/Bm,S~+Y6ΝB~ڰތM*EzJ< 8 pݱZ Bi#e}]Eڝ:d?4RПjFЗSJL EHoTWsf%:r$6r;P!VEP 肙{~Ї1 (:kLa;!aTڣ!j ]b2 b7#J'e>bZB}8g ℣BFLgv*|Q[(P7AZw-JܨوXm 0uEvw/v~wBU]ɭ^IUS*abRö{,C ߩ%˫JJC^Vb7Ms:*j:&3<15U'?s}NqOsysE5yW_sWg:,:,d4WJ?!r˗.\r˗)^YY?sc1?cc1?cc1?cc1?ccJᗾ/ PҀef.ϼ/rΣ;RQd+W.ˠ+f9=eͦ<Cۈq,ln#nSk;98B8 W-N9raS* [j\{f1h`;}ĎSUPXX l;iǼ況=Mvw)zp_-C@\]ӯ&IPLڌ }BEYɸm+j@Ve7 рG(R.X7+X5v]^c0Ȣ˨۝g,v<64Uܚ<0JЖVaMU't XR1KS$ݰ'f +;eŔsf=ʧ& ʴ)p !ckybezE v󓂫!KK*2`i/eNQ D;Us\YdhGp7Д(FG1u[w*00QiSlx@r/Y] gE>r֠lwyT|{IS'!)_?em_1K- -JZ>Py \UJSNސu7.Gf%U05{E.fbx6C|U76a$p{d? D!,-0ބ 1p3l!.sa5L^!RP-?iƿ* #Xq˗.\r˗.\[=;/;/;/;/;/ %~ H.̖e|v_e|v_e|v_e*h`%芪'>|>A|n`?D!c=m6p!Bs#y}?HOħQrpr_jPN".Xsj >;OA8}'a''#}ݧ!$2VaVaV#{r7P^A& fpnN#gwQ8q.0 ";bPa̱Uy]lۖ_LT,=0]zhyț¸qR_8jôgy&?i~ycHW HÈD8615-+}o5Kl@jb,"]Tu\04ǩU=ML+%'H@ 4Ǝ!w .|=42X vnnX0,M̻\~ɜ!8<0KN X65A^Tf^=1Oqԩ姌]!ҌnL&xj-,aظ)dUaLE#] prfY6HҴOLA٠EqP[ 2Tڧ"*֮i T[b+^Z{6.{?oǙYcpK&`j aUU(_n Xy4#0K59u6 勼Z{L0t&6ELkpL@B|LD㈾Zow0,b|c=G외%(d*PJZ[X9GÆaQ%'⠝YP*P 5Qq#bL̽$3>IijEK2omx^ٙjc7጗P|̫j<$yld&ߟМƼp5~;5 j197-5!*kr/!y]ݑQ9]l"?X|б Ƀ;`=9!)~a,B<ϩl7Zح 7 ؏P\`Hx} ߴ>[4Bo1|3IB @W˾gka]2F,RxP4F"[2ygs%RD)xj#j*5$G/9^/I*qj[35(-'΃UbYo,*%Fɫd jj@B ¬M*fjfHX rShF+Rh`hy}MHm:b\4;쀭pAt2bwR b(ņo.4ll0;n OdE nfY!ٲ `rZ/RP" ULA)Ti˕W/RǻRTUKhsvS|c ?왣5 2ͳ3c;v^G Pz36S1Kz,B5bTp[ xj(L@E%TUE\#aD}5y"E7{@"K0buħ(#*'Nd ޡ60ʾb5 Q P*aU# ͌-eձaҒ Khe Cc7c\(Aᩳx`so&f &*7,]׈,7IvWeC.Vn]M7rb l.5^%0acռT[3l+ԝ{A**&]\VR\ֻx{pA~dkM /EMG~X L!{ j/i(28(Dנ1_fa5`8=0ϼ{^LGf X_1u1 .C-u*YG;ŧp ݣL~PS5<Ʌw[~KWm9t^>;N1u6WwQL;OG[\U]6@9qhIhHvKx0 4xHm@q+_6*^c wN 5?h G1_$}`4)81+,h_ 6¶)b|䘧3rR5"p; EU@l=ȼp鹖bˡD{$pSq3rqs0Ko0}}Jr%Lfo>26AbC*ǘdq.'r`#HMJq6I% FuyY.†\tWf*8z<+`nF'G6TƂ62 r|=sGy&Ei 3dİ{y۴Wg+Ef;T. kP5۱aB4L݈LzB1̮C0R5i%T;j'x_$%jaE3{ՌU̦|F3/J6 AQ1/ohM;&y0-1o;Tt  ZJcs)v e%0+X&1rB<,~h qYI:g7rY@Xrf"1p{*qOq:?c1;E.I2|҆Za;G2aO0ݒ 9}%7 o4eq>j-/t4%L\̘ R9\v~.r-Y˙Z/3\PPK JML%j. g/*y81 +?RZU)_ڿQ[ js{ zBk?QPgYI˫K}ՋxjB S7ox^WZ/5v0;yO)M}}c<~O7<3ygFy;<1~?!ٿ_ᯏ3#=_x^6}jgUɅlX_c'n==sDߎaχy};X/E`A 1KLJy+uI3ɚP! q9e ̶&9=,1̱1H|=.!ҥJ^ztaQP0ڠz#- :w 1z>S,̝; t$%Z !2kՎxy1ɖ"171A^Ժ5\V'b_S !⃙UTpJKĺ#Qy}/NXHI\Pi8\p ӽK~TrqХ]4:Uqk3$E0?BES,sb4#kZRL0aJ" w.%J:0\#s3,0p2g8$j@oQ??~ާԯw/ǣ=OJJ餤)))J*TRJ֥O?!/_zpM?a-ϩu?CƒNyşX|̿P|cO>8Ǽ4|q?Lc?1~'|saP״bs^Z_/-//-Hz/AчS/Ժ\rcRΗ5OKt*TRt_k@5ҽ"9}Dc$Nֿ窺_::jH!辊0R=M=!zJ=DzܸGFSet=OGz+  u: }pKD+GC+^z=}O:#?>a*_Mu?ŗ}:e!ҡ֠*(0:}n\!a+].Ta:KJ1ueG==H:uS]+C u?:J'c}'+sЩ_Ws%u WM˗.\}.\r}[KKt\Kzr˗.\rr $I$I$I$I$I$fF'w> X b\ $iy۔߂'H@FeC`E-v;֗v{4cBڻ$vƓ{6" 37nm9$\Т Ae}A W$^"6[꿚b~|dK^I#a‹#} ?i\I.׽;A[C'*,ՒHYMMa٩.>?.OU4\,TmL7Hl+,#Q`< +j֥$fف|B74y⎔I$*+-0UiI$X&vaC5N)uoI!Z9g"q +r2I {xȹ/?MIx BS0HQsb'`X[D7Vai{DzDVȀ "P#Bb<!"$Q% 5ؘ@3С)i{t&ڊ|,,ٶW~>)|2-dxUphǶ`v5,26RvP L!0/q 8<n% _vYB@4-6&BV=DYFd軂ӂ;՛)FB| AyTAUǑoQn>H `rnœ21͇jaČq5]:)İ2-հű)+{<\C}#n#VN+h7nqCx*.e~ufql0H5+ D @vĬ \S|f#c!r8PR_Z/{ ĄsIň^m;ڌQw{.l Dv?\~H_̆%͂ZJINt$'I:INt$'I:INt:0ETAro%q 0sI:INt$'I:INt$!.UDOcP_p> p_S ?9_7! YЀ+q!^>A.x$s ,BUO!q[|H>d/L6))uB]ږoT1G_,esboE wjS)*0g)_g//;qY^ }cFv>4?GՆap1pabfe/FNial X_ܨVϞ;>KSE _%k7ܢWn*KvvK`J-aSOiU %sC|5.hbv=1p^l6Q c2Pq t%3Z%\04!oK=v堾Llg8.yGWaW,s_6/)>3NC [#c!*iPbcc& =:(FWN>-Eu w#n'yb S&Θ-ë iV!Zxa9 KVG>Mp1nǏP0a%.W eM&Y ם6|] P!WǙdZI, 92{K EZ\%@X%z {PVP)z9Q?] ` FVܘC_=!AS!J(%1ʻ}teͰ+P2غM4D{~̬E#(6œpw٬8f (M#0=jqxR1,^`aQ7.\S湇X$V Ql*ux_0 _fd ,6-;>5FfCK5i+CnWax 84>L}&I9E8xҶ`G+JkH eM`*{)E\=%RٕU_nh*Cƻɚc0sehݹOpP*SrdididF<lLd"S$JRcFϲB {ZHq8lh6_+<#b]Kr.2N..dM S) b&&Qjߎ"TT!)f1Wu2#2(Bl!bi%0FW'̲ݢ}"ZBCQezXvCVG`= q)kw]6-fT\ f.jNwu71_idZ`k`yU!lAXJG_RJ= L,5pbP U`۱$29N˳ {NWWδ:\\Y+Ai6B}*3-VN%Y #yMk@hDA+PsPgEO'5םgh!UMM$/$V,cyl זS _ٴ`l07]+%uņ22#s;`(>_q Q}0Boc}*=G!*FЅZEhBc.}G{^0"fdxx(]Ĵ*!j7=6 5@/u]ذ>;2C|d-9غ0}^=`Hf%b7"/3VP?Qwi[h6Jx5S_H^y5ъkǸJG'ps̳5IFFe|V{5ƅrقV1#+uOe"K۸aݚ"ueت^R ʲ1PPcuS".%ùX^*iW#) fCXUXw:'oiB[xGjVm?_% { P[mKÏcP0M>J4q4kRLanB`/gDT,#M h"o쩲Uկ~HWȬ`w*U۸&„r ȲiWzqFFJl*Yv`ni1PЦvŃ]?W|fz1fDF>bTyK]j)TܗN˫XlAW|oK!w\jnNosmAH=j_̯C'al͇"Lm9|$W]'`-!zcn̙#}0䡡ݚI /whY؎Y*qE!2/B+|eXL(bX.;,#A*gnwOh8Ꮏ@}%>eylfΙ↮Z¼c .ωH:j 阔*Vc[Wzw"6[.mof{3Rٸrاptбb:W(&#8GH.8jet]ѿxAB7LbeDx1 "[V eTE iZr$Xg[FޘrdmB %jġDLv|lِZyx6؆f!oc\K7 ] Dw>G2WUwKߴX(;T qcl)~pj c&/3D]+L~(3^hpXKJ;TERi@#􈦞 T/T2 6!shυ#}Eo'%6;>aq1`Vqw_7?)~TEiO$. s_:{9@ 1 vO cwAfYv_.;Ao^qa+eK`$nZ+0 g9+Um& v8 LXc"֥UWr]+x%D1"3NĤZ*׫rʜUxwl}~#A ʧ*vF,̾yX9V3 .ϖY\~io+LR O?8*VXcޤLhA+V|+7k q7kz;P`h67Ŷ }nm`vuݭ ]ϼG1l+ݯx?POC??S~PEi7O'?YQ@-/]*"|ؽ{oHG//:xE_VdIJ+>v`]W/W^oEq|5-!/̹?oJ p ϥƸS )^Ҭ?e< 1нCJ||C?|ǺEbxj<nywr\W繩Ş 1zG$@eMN3>A|vƣuSֱVn ݼ c 0,4<=,ڜ]qA[Gsf^Y& ~i fq`gX>t Ӿ;jxp6-M?WS}~>;D)˿|6;g]i3鿷N<ıG ߪ(CSA`A Dt$eˋ *T# 0!:Xa^"z0abN$aR\01s}LbTM&ҡ:nZ59$`RUGLTJXQ@\HcQNIltleıL!n*\M\1I`˗.2-%0`0*1x"› )dQArc TK"U6q0T ttFfj_FUK0B%j"E>9Hn%e11JYhJ+$9c`S&Ex"J\[b190]0,Ak6̷*Ǩ4,3 "-*F`/q=B=!ѬTAR9H#z(Z-DXJ!I Bl̯-q"1B  ALuMaרGÌ0"/ .6fFI af x0}\].)Fϫ:nU3\l%NAz h l2/Pwf]J7),`rJR2@ N? S-Ch`ݡL"JTרMYa`bDr*0tqR0O1\`pj{A2ЦEMD# ġVШB(A`GA7:t ]*WJz' Ōa: YrT]n\T˖KgE \ Rt0TX2+J.1b2جS 2R\XK辗/zUe%eeee%%e%%%e%`:z{|ޞ))+)) +?QԭcU=9.E7 rw;Rx\|JyiÙ\ ׅF7_zg~X^gOClK+Fx/m.y?$<+.VTKU_Kj_+5LدWk~>k:n B9'}և`X˶h+q+``톱e^55dyC|y,^*.h}CjG.Cdz|?h>x4' ?ܟf~f3| IY~cV'7E[V~Wli=ߩk^_;p~_ _ Oyo~mƥz҅-`%e`ee%ee:CįX00Z* eEKQ+".`uBRuM&i}'LAr:V#etn3:@%JZ az2˃B'@J]ʄ]^%JT`bd0A 1e K}U6ct#]tG0:CfE= EEtK=8+jr:FR-DX]^+nZ8qqcCaҥMBҥĕ.+ bc)˗LB䀌]z\ 30BTq*H@cbEi_YrW NESrarR_Fq @Eäbs=KE0X0zԣӎ#2C s0b.Ը\ 1[2}nq} =DZ]0&:\D̹*1c.0]'A rrˌzTW43w'40LOQA0b\i/Chԗ^1A} P-k3} :t=*TbXT16AdZY  CqeG R =,HYP)Q`t%ԥt 2:WZ2TQK0ԥFPJn. >_QǢu:DV&*\ WJgP=q7*Q:1%Dz"U/B:`%e %z@Ռ[G~aֺ=B R1c䨈rQ"tFWb'JRt#QYQ"=^*WGZ=+D-///-////鋄^_tB^AL libkohana3.1-php-3.1.5/modules/image/media/guide/image/dynamic-600.jpg0000644000000000000000000013071511772106746023620 0ustar rootrootJFIFddDucky<&Adobed 2?zk       Pd! 1"@0PA2#`Bp3C$4!1A Qa20@q"3BPRb#rC4`A !1AQ aq0@P` h*X+6%&Ɔe(Ar8 8$*XN N8N8888888T}q3 Xx:K#g{s ?auֽ5Ӛzuќ[o g'J:02Ηf3R䖲ٴ888888El$8skJ37Q<"CSk,AphER ah+,UB8H%ds888888b%x 䃎8x_V*I$!cq$\I Jb fqqĜtq\BI'1q7t9YؒD[R1UETk~髫{FH^i0TGY(XaM0h>O=fFi%[6qh/F-KY2X_#J#SB7ReV*ҔEc:_|u+gtyw|`ִ{vNRA֡jTF3DZΛG5gy%-\Z t+ҝ0=f5gUc'ۼм9Yc:vi6THLvayo oHx_\ћYƔf·J) RmG6Ϣ欱u h_:c=dz7MrtN/~<ۀKGǛjh]!S1F}yrSfCѯ1=@Xf`8G:$2R. KV m#}z^|>iʕ|rKRIؚ4 a#T_k{qG zwKQ,H*L֫?jOE5]rЛ{|/WQmk!I\UTͼH\VYUW/'%)a*bΞKtWT٢Ag=ѧҞxnzϯۏ|> x]OU #mDw+_MuF7QX^[ޚ"gÊWB 6:ze&}v5S΀c*sLM/Ki+kk-J^7]x_ncǥ1Z}̿&E):;%|}W.5׾=jʬ0iEVod4#5:?a< |VEy%4T|n=F`:͸YONJU95]m˝LW:gKb/ۘ}o. k! ;ߙ7x[+_Pvm)̯vޑ<Dzn+^Cq^y2 ίDƷР_nԾE6N{lh_=M+qQ]JT [ߗǭkn>#7?fxv,jo:|бmÎoߩ%|<}?1ld|cM+5ιG;Pe.z_ۏ WEs3zƺ頓h}Jm3SYez2/TҖlZ:/76o=5nqzq\zewy6*O:u4صJyz^jM \'ss]]x?/<49w>JXC[ ӱVO͉?Un^oi}ٻ]vRUXUKw8+֦t][,jѩfO!#_ϒi}bPuk[K G#:o}7 {<8_N'#WKy+}܉yT5n'f}tVJX MUαܵķ筼rjsQGwٓė9|C͕N{燱yRl-uW'E(5LPNjs[%?I{ʧ9ѹȯH]kiS2gi }-?>Z/b5E ּIfjK[Rik4.zLL?><iz)9[ʯ$QYzS'JηҜyzc]j,_e_$v;W[wWZnfލfr½.Tϑ=pAZ=ۧM]%h+hͺ^šgzfNӻ0}dR܋][u+~>ZA]r ~une0-=9豢H˗ZolOh˳t49wnv;SQiT\T*\ZhβBX}1k*Xb0wW1 6rB<2eSqNCXG|z3[ůi-_w?-z$n5^ٯFQU j &ԝzѱ,4Ɲ|J#zɪ j=β>LO[n77ݎi3V3az.秦4~c6_i|f{nN+>2Ƌ!{4{[Wˍz/zSe.Esk\V{j(,ht{t73\5ckK]3`ͼk@#}W8M3ndy2t<=+XhV,uJ(Wgn]ܭfϒ7| {ÜzݴCܵ?nv}{g ?.;z$LM˯E\$W{r<=nbR!T1Bщ RX}f6Ds;Sa&Mhzuy#x|AzF_^]>|x׼zfcck{NjNhptFGnE9jBQ LZDA3˟;,u ry=W~_377[Ö.߳kydφf{0 w[>[|=LgC˪zLy-ըw}SZ`5X,2ԽZ$. ek*xcy/,/q[Ԙƿk؟h;k_T[g埬~>/7WY؀GWU]&I֑5=G{rw Z_GYI-7)eR RK&%-KW@LCkq)Wj|בn!i&czWgySߏ~c·֧bt̮UuNȯGw zKobv)ߥ5J W(5RVrYj l-gP3peg5e[/;4=;7^=jG(RTCU (j6q`Kyq| οq:Gٌk,Gß~7my߻~oυzRri[6i~P_szƬtm׺tVA, 5R+/,&h[fͪ .+9cʱL[hNYo-<dKԎQ}aGԂO}|3q}y1Qo4^0fo:wFσz0GWֺ莴QKC7d/Z85%԰Z#鏧~#O?;;;.)8>z>z7>]0 u heY4hX4JJ˛Z25ΤF5%-Tb"R3g/cwTu?O^>+8:[4 4Ya4C_Y֞ڟ`)0v>MˀRN7e*aYWj D$8 J<霏E5W?9?u>>%4<힗^G'8SqcbdFj2V ,7ZXZ)rsU]zm8-=dcP7* z胊 ~lLқgپn>k -ƎnWKIjFJ\JbLХ2ƒPkB=3eγ3t~GoQK{.Z*qE8fI@(\p;UHJ-ZN$Ūb8qkf_X)p܆Jn)`Si5h92)gQF5 e)8h(0CGITQ7N<-\\rū-RIMIY|9p6{|ߢo줖H`F*k.2!I ?k8=Q j$2) 3ǟ箌\]-ckh$UƏ:LkK糥X ʚbP3K:vhojUE  8B8$JP-q BQ\B] V=Aa%lĻٺY曕pH/y7[:ug'M.,S4JȲB b*I,U9xx;=K} FJJS)>6M6&մ' IV[KUzL&N\JT'~J{gyغt}&v#"ޚマ˺qUKseuQvWuJW"JB^."Vۨw| *;!L2ur$U6;J$zkbu~:+Vӵѣn ?mǫעzYmQ/t]qoNV/ՋPkwNRzh׶rޚi>5"RGŤMq٭OثbkJWu+֥?N#MquQzFQ+p*|>_XT/|>W\T*|>_\}suϕ>W\T*|OA>M#E?#>Uȧ K=v܉99]b|/|ީ>Mɠ4&|Ai>E'Ȥ"R|Aʠ4*P|AeD#OOבrxu18ꥺfE!3&IKnnedF5zsDɓx+HQ&~EZY112OL m݆lFMquؕЖLRLOtG!1OID%('(.k9BbfD̙cGLEc%L9dzcD5jB#vPb|P,}k2ٜ[HߖZ!}x^]÷0)[dzȤd_A,'8dpdȐZK!GiyG i֐5ȢBjut",7=,Lc0_rSdrLW/4Dǂ>$`l`=i\}Pg08p8a։!~h`$(cZeHy.w^hGL[S4rE>5Olm'Ly$ۺRF;㽋/dG{88y8a'0BбTZ#7{,ؘyA%:pDG$`{5<61-r : c9eW`=5ɥ+ 1=0:^N,,`7DDy8IaH#;'+)|[֬"0Xp$qާrN '2jXfpYBx2Jؒycrpl&+rʃcBԈB,ԕ,UD.&F;Y= JB?<3"цc(9n83̃$$gr5r$<2dL2o)K+(|~Gv>X;YŪ^_8Ξ]!b!<8meIхu Y$ !#ٖDxCSxkxm$H~s r#i)5*䚑+c!<5\V'3EzXD3S..}>K4.AD]'e,})rVf+yڷ;"Kx$Q%JXS$0KlvLiVG䱙B^ PQuצpÒ0*ԣ%;|2Dc5&|x:cl|͞RfTuxPU"ACQb߂dOY&`{:7qdVOsyJ4)ɭ"\K=ZW[zַ.By7 <X2r#4[3]6LyB W dK;u%4#2cޭ^;#)4<-H,**{EV6RXda`eN\ӈOjWGVM cY;)9Rˑ8g/g~U| {<D %s6iC!FJ5xcH[`ό\me\Ty*Y[uoInj1L]'8+9Cj ʶEbsp\ՑKk ږD HrP"#8epjbO"q$&&Gr/+[̥#tYo%_a;E,'n)"!8$ʓ m%j%c'MؤMRsH{껔YĢ;UFHA %=RFu^GUXqYyCf=ji3R+&YǖnÊ&ʧMܛPr/TdWsY̮%=_g.UJ[&5$J3Pga+(\lHy9*1Y\D"m$ʓlB(O.dI̲ĉI/SI4W,l/^8(K2Ff%USSYC^N1 gr:]gDάHQJUN88+K'dVp8#i"͋X(#O(>JsHHqʔ<<*\a\YG-4ĥ5&+,e^UE9r'||pẆ֝Mr&UgSYG¶u;ʢQB'p#Y d,v"lu.zKD6k.^iVl'Ϸo'q,)[6Vt} :!N6O*-w=f<"(B4M"ǯC|&i߉F4q9,P/I¯H !9e^i8[kW3,2I׈oٶ\kG\~:siFp8"qN"BF gbt܋\.$%qFQ增!&*ϳ^ >;8YSYvUW%,2̥w[ث (hr!81A ….1)vr5JsY.7zEmIQ8[Vc}8<g}/x]~q&c(WQ#?W PjgkISc0Ƚ2⩹Ig3VyQYdY0cL xЅdvkyc92vc>U5&5rhRRQӉw(J];يzWg Ojr*Qt+c u܇cr'@_ ': m؟ {7QH?L_7]j5YI{U.4issj;CӏDZ(6s?s*JWU)YBL-O\7O꿆2EV+e #2D/t2,b",^).̽W$J_ӳ:,]%)78=u*Lpƙ[د׻M]W"+ןmU_)juoi:mfٜ{/vVNu{ӦF]9>ϱlx_ρ b{ EcȆ0K?L|ZSE՝ܑ R$_WlP$I{M&?e>_؄%i/j Ya.UII5׿ڶNS[P:վ6khSgzDl\39R12295FL1xYVW (!駰(}>GZm80i_d`G)zD083JV9ST5]TmI5U5EK1||GjƫX}\u2;åd,jMTQ5"{8ȑHY#-ukBK4j\(򮮇iIw.v? d% sdI6Sr)YVۦ0(T[ħ:͖yԫrSj^RSdcyFZ#${6r̋31x04&6DZ}mxaaWh n'>x%UiMD2~ܠRLJzt݂>um.tOҪuەqTlr^g){)MP*Qm( [%I&sLnxUOz1 4$ܞ#!յ[>juλ-cgChk=e=*VS8>\V18MEOCe;(qee2K EJ^_LRװёdx)9F-DBΛ3ei'o햳]UUkiuW]lP.TTDQFQ`1G$v\,'[vkSj$W E'-ljU/􈌗ݗVm9 S#/EoL&(B5;=[ ungDlIFVN( -(}ҧ?w}i,՗@3[y@sL,.(! ΒCFCcq2Kuq3L2s9DLĻMaFqf$$q/L}Y3_q' T+3/7D'eRÉ 3FpdΎD.lah̙2dvVrd 8D(Dൌ$˫7T#ei(LBzvʬpC+=y0Y8βY̫~J)1Z6h3s{hb*{hU=}3-߽v]ϔ({l39"3ҮZU)'Gj|by9gĬUu> #u,+D/u/u$A?h#һkIG [_$C?tPG=?_?W[}Q}լĺ1}.H՟ƁD{Q==Oj'j'ډGm={q=uρcz"C1ǣ'ι/#9#9#9#9#9#9#9#9#9#9#9#9/ɓ9#9#9#9#9#9#9#9#9#9#9/D~%G9I~65 TFDX$ lK&!k4Dfw{BfD$u_ɑGR,!2Bz=1L(fGzc9iN$F)i2 b+2LLBzccg#'(Z%" Du$1LlLoEj!CbF2Kbz@{ Z/Z2Z(q"H,:2؈#8(`Ceq&b& _U%_9)`{h4Dg2 {9X'ќ3#c"&`qoDAD?dȱ12DAe[^ ؠL1n8$erBѭ0q%q$ 2c81dXHd"`d8Ē g"#ZD"er0`{ L d",aɈe*,[-K^#CDPс!j\-H,$cG !"LH*-3i"3X9HD"ZG$I!ă8'1dF$7;!!k$F$"rȐ23'Y\t!mākH4-ȼ9hB%28!"~n9U=قkh}[",rqC2@L0!"khohGHёDc+zgFDH90D'!"& ,E~s"{y%ε$LQd0GDg!ocg_ֱVq'$c& Oί,2X!#ə85) h4##ֲBk#-&9<e"OIK h5!D)E,"b׫,g J'%"^Ӊ$`x9%[ ,`#25CD52ID&%&M [FDD"ou(9SXd1uC׈2=`ƙYȓ'\8N#C آAZ&N%EJ"'b&VdFH0cÃd+%ȕdd=T#BN9+dЧfFdO'Lȫ34Abz2,8!,@hF'Q(zOb/:Y<NY PF?6qg=1cG&q 0` 0` 0cq8L0`q0` cyjtɟL2dΙ}\2_T?W?W?W?P?F1_&LF9z ~60_B2) v+`?c~ }n 0`l~s<0`& ~LL9G#&|2r9FL2dɓ?S?_(s?4vj?GSRYb1Bp/TXY/(j]M ɮ2GRPw-|@9bs/~ D7vu4Z/ݷRq9a`/)!!U9ި^zU-Q]<i :\p<0 5Z z@u5N~wYtQ.pZsR;T%9w_Z076SSi8)́?B19Z:5-=]O:y4ǑR8IKue8inw5{~KCWԟjk!&%DA\ _{'4`[)=[K6ei^:F_iFy C=]=9jK+.|ij|o=ViĭXf͜Pzy--)NZrQ{(R PA=9 pe=CZd 3]N*qy13zɿ-#H#6 WJO0]yzPH(QɌ(JQ2lqsOL@6M<4;Ry>Y>vYtVSG4-شg'g UKfm-o OVzq3ջUNC_osS3Ěz{Ě0jfi\cqPƘ27+gl6GnZ7yGZCQӏ,Z%'yu#BW̽ӕ%3sGJ7=.hSEYcބ#Ha٥8/+wS2YD ֌|0oq$MH5_ "re)G,ѩ$ЩNqyOLH7(>X0e Zye(He"S1-RbKݘ$yÇC;$un^`(Зg$qpj\M3go6(k⼱y1Iñf .\w3򑻴)9}UY5+9㰻]@`+K. 0p#p}aV$pdj_/M8^\sι .u8\s|>/s>/ss/s%}R\9&?pg>+ϊ>+9n*Ls+%|䯗|ךdv/|q^W?|y^WE{Q^%|Ϝ?K{9/s%s.s%{|9>r^KϜ>r^KϜ԰{<^p\0EYGRCVڦLWiEbezK*29Qp+>*Ȧ_]Ԫzˀ8BGlvt$\p[zsSכ7Cz.l9\ʇ :;kY#ۊGh=I!Rgz PrnSd|,actU<:fO11 4!.(P!QҖa+~P~/0,'bF7ܸў7Wtmacc'F ӞWj{Q6+xDafUNUtSan[3t,TV5zk 诱N 5T^k*knW|W >lMk*]o(7D̉p2rX Ss*aMeelfIzkhMXꉊcr9|lxK2Il덝p7IrmkT>l0k8L :{(tc2̆tcZV*#rU(E:k+c68^[Lea.LWc  ,$&7LQ8Y1YJ۹nꮝQ=K@/xٚ/N|8Lv+c&*SU*񲪈X$x)At ڡ=>n $BeTRȦYJRrdۺpЅYu/O}dd=,[UKޛ;9 "q빂d1E: K N&bp1̯-̫h6NST[Е*^J#c&TULWbh[p@Y1N?fThNg̸&7X'P6p(I=z_(]S k,f Yb'TM$nN.):ಋGY5n^Cz $cBïpTS8>+.WK8;M;* $B"I>!4{AQk >o.?e+&L~SatۖkjQ6 :/̌wYV̑4F<̒D~`eL  DRI(/,J+r[>)Y1O$a֍9UCJrEb2.YZDYUP*DTC> )*R TZ-"IġkiTOӋ,o)_eʡR*훶Yrˆ TOcm#kt*<|ym\qBQWg؁=8!㣠^k*^W|EFxܮ1T'omlctY^WlsY^/ $O}BׅK,G}t5BT|U^Y_cٻk)p6s/5Tzc޼*ˑ(l>eIdKac&Ԭp(ϖUq+&-_og9IsIsI^V*|^ +ɕ¤;ǂ;ǂ Sx/sWx/{Wx/rgSx.}^kws[?s}N/sSx/sS> x\sjwϩ<4ǂOjs[?{+\\sϼx.i/W\W_%o?!tCrDԹp"ON-w/\O'iF><(AhA( I գ 5H0]/m{^2臌Z`"YΠ2ȞL:` R7g=zLGtFDZzRyt= E{i.1OG・ʮm?Xb۶?,HђR'mL) FO-<(?`J븟ѽ>=WI07io @B%,6>ΥiO% TeB SMm, %ba@|p8ܹ4ɒȍ6&(:Oj6J̡1}/cpJh9;|d t%%ql|d j3U4ڑ U.QV|̌h$MOB?@!s]w$VeN5Z3cu+͈80jOxGT8Y=Υ8b@5^byÍ:btVD<n;"a<˗Ar<З.wijjK\jqMY'GF]f9 A?w&遠4 P!N@AS,N-l({M8B檷ĨPS#,+ƞ˶;p}lY¹anaxãJ} |tE Pp/o i+tg T e@!G;%RƲ BSK޹=[]5Ezj:aɋ6ed*y Vz1YP4|XIU]}ǎx-[ʰ-qh6QE#7ݕ 2Qtfr?R|핿3w){Wʹ_w#z$GeOeOiJf?៯{=y{u=^ HY$Q lDo?xMiWCwsc_I7~g~{M{={y^~yW~~/~^~yG_{={u=^X`cR/2 eJI k;G~%yϏ%5( T\c1.4!c<\GY5 <7L'u;|".Xeè |*pQ #sDJ8x 5+̯2,qzٳ_nLy|#C[p} ϵ,fT^~ ԰!@4pØR++%.1'H l̪XG*(PGBj$9 f z7*aO̶6ܤ27nir )2/6fV9/T[JCQPUQdq(Uё2n(Z*e\Yqcqy'P9{8i C Tkd sS*ܾU`f#YyVw^b&d%6YX]<:,ToRՙ7Ű; |}nn52K ͣ/*u/ǀLX8<9pUL !Rx bR/Z,!VxqDuB^ 6v4G%\],f\J}$޲*{pZYDkX:PNK pSsF{ʣT,&Ҳ|{*'5DAf&酂{ |P@Exlx8P`3<|J37+T/;# qQd*1|K} ;5F7oƫF1C&ObT .`."QqMͪc퇤 qaDcRjIIpϘPU6^ eZPiB̉)+3Q>%a-n.jaQ+|@'qjPCfg U 7WE گhp/v> I+,lZaXG4X>">%2bL٬ͪ"TĄx)EKªpB K%5}hptS=2lK @jPJ,'U`EPm5 XbfZ䯈u\Ks#ճ2J/K,rR>ҏK5y!vJ%ޢ K1a(P,fbnEbAV%Jgf*AVO$b`uCrRc*X/שj!i;OTHlK>X^*DshjaM܄V#3|_G&1pB7Nmʕ{1* v~ujyԺNU eu(21oryGu [f-\Nw(-h-}rĪuN0F V*h3) r.i˲2۹\*[IS J:.*_ElFo^l3 0c¦nw؁O-fܰl %m3aGPq*eƵ(){{Կ eψPL(*0v`D4^|W嶚FPϒnG̡(zb xo>׹4lj(u MQ)t6V'ȗWcόU$07 #Cf- &ȅ55Hxx 6F8aW,u"ESx?P=n{<,+]Ek qGLSq2FE HW6S)cPb*FwxNeT \-p?R3%M"B*zW~hܖeO*5-WpゖS 2&**s+xna)ߔu,y\E&j2/qnQyOgLLjP.G)*^A—#{]M0ԗXIe1PVwm t0vE'pDn;QG 6'5O-e::EާVbA} bƨ(J{APP3 N#av5}A ͷq6YQC*Es5 Ӄ e=a/)\r3IR!3v%Y٘ZcsQ$ΗJ{iϳ- DLO&(6eZ'dZ)hg{K c_!;ƪfh[YBl'`4 J'J^2T8DMA W+q]M )J-Q>y^K֐cWeK}KS )%L3ޢyl6!0"P-q㊋ 6tӖ~axIt WCBP<Í"T$+ 3ԩi%r4BR}M$,"j{_7u\URi(C #Snp=Ԟ#IS9`NDGb#@ʭm lvB`$.nPc{vh%%fE3Y$p;!Dwi FʏlEC s7@ [A7nrj0n\%fd/.0ژxc"+5k3(ub0H{Ī,XH3F_l¯F|S*h7JRtIڃ"g2apM࣬vSi ˽2y 1H7XV]鸗̡ [HCqapp<+R?1 kޙ]X1\ XS+fA L2*e-:y(`وP4ًtN⅔"n]Kř? EzPVBr_Q&M~Eϴ)E[0k]DLKHc/(zJcڗd:UHxSʥjLY1aSݘ/hFg(8|zU 0 MqʆS$HeeaHRe%CTsiPQ鎊ig!zn?dœ[Ǽr:1Ac+;%73;A> ud8/ͫ RKñ+8ؘ =`No&QzzQڞ8 VJYBi'1#H;5 W5{ qKlE[dOXI@fy/m#rE T)?D"p2?0HX. vkJ'sSX J<Ӽ>b)dYBT){;LH[(-5 7 f;3M=ˊ MTTy~2 &jz=F45*/kJJ-g^xLEk̿Y].'c+8:1'.Hm?vWgeId+",ɚ :HVs=sqqvcCo.,LjEs$Hwi4wc c*ۇNcSSps9`~w) h:`"jdb"sbb.4' ";;ZOj>(ޣ(ukk&1\f>GrЕ cl}y:tnuWs^rO!)\KrxX(nk7p[ ԹHy _ t[rWVCNi7¾5)&kgۀВ.$*T.›K[##jPQޠPp052ɹs7O 1eSlQC>' cAp9v.;G. $HY ez]gԑ0F1f`,͋l:BB*sԥR 7QMaH9^>$ogr*$g1L Cq&&~3yx1lz'w5.PyDN]w0NfxefN:`Zu+C*k |ȶm`F9R!X֫CRƻDa94`־X-sVZ }20Fh4G4nS,xm{/dbbRVVv|9YXzy`Tq TOd;a -#p8WnT d~X-j?=6^ò%_XlBpҥ}%VygWQ/f%Iijt;"I^arKIk(`h:P0}&Uw-ޥ%\Oq\ْRoA\]5/qRn#rᢈn\'zBτ-ḮQ溍=(L4 {LhwH_# F&:Đyubvg?yLYTE~nI`+/sW6kG̎Ɉk}#i)N̻쉠L@n<2u23|P0>YI7ɔu[ke0i m&@,l9od @O Lw2~ 0o\k7*d*k3OlC*y6}a (6l+_1 ny+&T;]"{a52{xu{ֽMTj>BA@ \q0iy+ܣj1 *f@3r=E2>&Ϙ?t/7 p,hHZu\bLI#jTK0c?d&dc3>Φ ێaRxU@ DG|Ki;ܨ J8΄0m W(_jBg.>h/4kqf2Ŗp儎©s70OiM?/{={7ݞ{'q=|g}y4W??!*? WPKm_0qa?0_5X(y?vg?Oه?ٺ^Yx,I͙&L10׆iD3?5X/ R}ʟ1+\wp={ '}}}}}}Op={'Ox=_˗.\~.\rr 'Op={'}Ox={ 'Op_ѿeGB$QǢJdȋe*TJ*T.T^QA}7~Tbn Ǒc*<ցjE.p|z'|¸G0A7P \+2y\N 9 q42* IyG9l(aI^#+ ˗pbJW'+<%G\ip5p,PpCsaG>p0} / 1Fa0LXsЏ.Vx>V%)+Po3s"G\̥y JLҔ~ L i^b2~p8pDpQ1EU\%us7=1nbˈ|>0A]ȢE90F!\m,l 8S8!(DePFҸ@Ezc, /Aa11q1lsn8 މfpQ-S9Pe1_O+ JƇ @Sw~`|WGgP< tX:!2[i&,5 Bn-~p8+1GqL ,,4~%x%N*N-ƃc\'~JBSY`EO7L-.N^PB9eLM!?eĤ]̒FNRW.W q "JO$K.S ȼ?1q=T_(o?J \r_'BTRJ_z_I #RrRRRRRS %%e?bJ*TRJ*TRR?!'Og?ԫ~,|rN ~fAjxF?~h͏?Ǎ}o=}#~y}C?xg~w? s^1>~)˖JK%%%r˔JK,KTjTRJԩRJKqdrrW.Y,.Y,rGJ>EJ/~Ug</˗.\~?/!ܾok\ MJq'_Q⟓MJ5? \; _T/}C/?pIrp2GzN/7.(x_=u£8\~tܸ1=G?#ÿ>C_pBG0=GgC<^Qz_g HB:x \רxzMpzNK_yaOY~._?6G%E_=dIR?<^ rzjW~pzk%rGG_Up\Į J*T^?# *TRpR5~W+UT?z_˗.\rr˗.\r|rZZ[g.\r˗.\r˗._._?  8 dI$I@$I$I$I$Hd*a0|ނI$I$I$]"hG-VI$I$I >ɬBm}7uvoe-Uɤ=kS 78H%T$'l[A;$k+®gZ,j%! $im! E)>t r|@R>LP2~:K)- gըLh$BN5fEtf>X P[f#;.ɻe#'{)=B$;8.KMC徭-Ø;$Ç{1Q0#8dbP]O/t|nAf?V>X{8Kv֢V{EUG[t   ӽmБ^%]<Q1!gH`hCv"-|'Ybs.϶s:1٨d* < (g]{)CV4(1';Kʡ뜓&\C> 8J }&B"ē  xMLn1Bڲ@쭷/SzC42.X,U쇺[>.rg=s&UThz~iJ6a}Y9_4/m 55ڒ*nRU{I ]am5;qA$Ĺ5̕ŷ[eE ?ḕɓ`g|^)ZS%-+8̬p%JTQ@"#aL{";|5'e}}-k? JJ'u,XI!hY[/y5AM6r#ɹJ ƦV|K返i ؈e5QIamd&)h!^w{o%ҶUkуu2x (0>P|C8-6O'Ie,x ^hL?fLkʒ3 l%m5^B&d6̷M Lm3YIkǂD,{ o9ɲwi~viI\Ҋ=Dh] i5\Q% IdF˵BI}[oO2t"59JA)`2 0K{oԲ1 Df7՟4{xG^9ЛlGI $&?>s ` DyhQ[{e#a"֎ Q <3hȉ LU0mpqo%UUJ pCD-Uȏ*Z 0"`"vE ZM0 pE@JJvr;(KZڔs =`@ Y6]5ݣ0`x54]iK㋔1ѧBe1ٷU-EGhpV&k7_xpN%va}:7&1N3`F|D9T-#, 4bD-CbNXd]Yk$}2Fڍ6Iuzhs+≋2 C}ހf0OC4@5T>E,-}ٖ~ݥ>Ⱙ` 7a>^fc#Eć GwJv-U]ĺSX]s(; "c ^LmX%]xۺCKFS1x%p"3L]3Ŕ^n YEx[/*AԲ57V['Oھ8j%WFCv4`?bs:l7Z1k"[Jk_^dHXpx@mE6Z@t _@N'iʪ[_fUVejU[Wq &J2tcy6TAҦqe 8,QUu#PxMҒ!oO9V&G^)Pm ǂ l.̱x hqwx NNs5j)2F/HHs1[ ͵@!6`b>BLb^ J 1MS@ fkeR5*1Z>B]5zUe`d!8<DpRUe1CZ+C`-EےcmF!DlP%j*F?hyaPVR+ Dej5Đ"0wQИP|@ ⮠(DolnT|.Qھr^dW5.~f&<[ W/#/nJ>=E* HlɟkPSeAV .ހ;җs()N]fѫf!bipV㢁N.۠ks}crݥ *PKq .-z>T=T.a[Vk|E Y@/xظ>Hdd&e7"[(d&)=X&h=?yJ0GZjYA=i"6Q Vľ 78ʛ EƳ2>&d)l*6N - 7c܏8X1yZZC ?DZA@)6BXheL-m k +D<]EX9撙 BZP&PKQJ%XĨ̲r>̡?x-!]rWt Mϩ[#eIEKc-S!ԹT{& wHe&9[ >`mo-_jf]|"U2-%-qᮥɜb/Ƅ jLM\ep̯K,R)sXb|kXىL {Z\) udNq@2ȤWP0A-|Ai rA-@b30+N%3 jZ:QņH+{5YL3b`2 puN۟ .SmJ|ƒs9=NvHu2w~сZa:!~|/ye{0]@߱eQ3}2v! 7:_c7Բ[.ZIuriL3%YbP0@b] RϼtVS[J hÚ 3qČǫmJmSw יiA~bZe,LwxX(w[V$)1CJm&X{]CBbJk`a.\`)Y2bִ:M*4 #<Ŏ2DrqH>=JaE$+3L@ҨUP}ch'Zw,n#_>`3 -F(n跚Vɢ4^V43VTdx%0Papi#BJf3XefS|h8căBw hi,l uЇ))1aP]$=xaEt ƥ{ @[`2`jEDžN @0Z+D \ ªcZʅ=5X]KN0r%s6P1a/<@lnX͝1g3V[p k;sa#Љ/ Pk-Ma\8$n>۔Ijl-^ HGJaDccSaS嘖ge0h_>`x"lHb _\vY \S(:Yi(PMAz ՗^0ʲu.ԩ̟R=یy:0zs3T64m֌SV=,́.WJ{tmR|) qܖ(NWX)ɜ>026jn(4Uj55;_JB!JUjjp 20Z-y^[,虜 V@]z[Tۊ˴>jҖOp-/dw.e !1R$n*3&@T}|yMR4bF/韈bk+Cf߶MB9%bJ7O*$@G'qMl|^ǩَ00Jm(vJCQdG]cG0!|3‡e7̌1FY<Ի.x\QڅWRO9&wQM0X)QhӵňZXL|Jrך-Nm6ty^ Db qlr*Q^IsPw\ztz4uZV~z}b3&9Ln,ĩlB1 mh &rr ]B}V @5口r(o/ՋjU=ýJ~ӰS#U>9KG,{o&MOe=l:0$E  o`:uW(dJ 3S2@0 J< -:n`*4n,1Z>Ъ;1  H&Tϳdll &9e}!O`5YfħT*SOkO6K1{_q$1_B@,Q`gQغF_7vn]qx9Yj3zb 0=γԭ||2u[BѺf(P3N!PiGD'z +W. ֲц0IM󘠣fh-E%˼ .ėwf2/I w2ŏ#1n,%1(cperfB^B1|\\PYc lCB 5}˫4adb~%`b;B3Ӽ},=y o*i PY[UUaJqe[b٨ŁCiQ`2}"끼HuE ?Ty-iEtg 13N_+@naNk6RBi PcLV/d&ei;ڍ @㋸%k^Ҡ弶mBO$9bLje#:bmU+~6X,G0%E/w^Z~!፿rf -)hǙC]hNbνUFK>Â-q*Np+/іf g˒?|J tu0\SۢUFuSC~}uFR0T: ħbc]O\+Z75KRFi ˖6>; n.^̹wBV%ˁw<>& y 2. SӍj05;VXT2\,hq% B ڬv!&Ax+IJ`R,tRq%Le0UF|!2(̦L)w㸋";֌w0@*..HWox" a8 w k`_x/ IZ~`y`s 0 QAfV AJ0Z<15mL@0`O#uBn\)G]Ԡǻ$?S`T\ru/j#jrZ=B<+/h e{;q5+FK (UE%s*[rbBQ} \ @eAy#>BW  lH,2ur*."TrltF& Xb;aVOȕ\ {CkoQ ˊߘ``|״9AU"cGıxۗ#!垡g:#VvQ㹝6<;~0ke|HWUz՜-3dlHHq)gb_F;.؆dJ֏ Y\R8ڦzr/a&TGK[ o_0bESq; &Z\<шq47p+^HreJan,{Y TFが01*C%Y:by7x%+ģfC/¨`"ԫ{"(7RVWD{b"0d0k%(Y0>X`N8A|P>gǐ`. I F-,H,!S[ B,Et6KbftuӨ;{ K]_NI>=lǥ*gaX{{ı!heVS*,pս6;)A^`XYf<=Ʈ'u /g"/:T]vcľs Ƹ9|FW_?Ŀ /P(tyb<'0ݐD{ ۃϠ̓U+.9՝KZOb*2֯ぼjX.%FQn "qC=DWٝ\* aʃ NvF@UF^wja̠%,J "ˤSW-/Pܣ<Pb\9.ihaE|*"^M62] J1Ha/S-؀Tݠ}b5DZe(T%ψijsU@m=Dj6ǰ,RVQܭ6LKYj)!LDurf4[\\0s*Nӱ( Oc% udHBYlhTXz]ի8Nn{x ,-VaViH38GLed5ǰB=GRPqAʀ)2E(S(K0Ksfm`(4$)+`9u W=iJ sPkc2dS.n]]nda]EV >Npˀl=f\ a0ApSS!k 2ԧ:9u8e\F/0PeKCEU,iI wɨ7THa߼_X#+$!z2})D?XϤ+{)Pr/a,W"Y:[ ybqr%<2hj[29pmJ48Gic ]G|Ieya-i֭ͅWU8<:P[aM/X/G ?)^Ӻ` *bn F󄉲2tAO -W` !a@H9w2X psdc hͬ{P, \Fvb c6+4L}T62W}{'9qlVY9=H1_bblش׼e*0HBcAkq.c `O12=Mb yUv#_ɬ{ ئ$U5a=YyR73iZ3n"4P{0e~`?Jvdo0 6Lja;"f$(3 n"*!= Ȍ̭J[>1JV' q)v ܺ0%3P8MJTvg ţ@,fS$mT 6 Lj7Py{X_qE>l dZ /gm܁#H !o) /7,a|%D,x;"*ѸaΫN_I_Hj,em4?4XiJ!D"sވ1T, 9+=xfo@(bRܲi.Ƌas]/ytԾnSpS0Ĵs*e)p"M 5){m,__̅:Bey ;=BBQ#2 (2ȱ0|kp 1li:ō,25r e%vVl5YJ @Ŷ'IgOq(o  @.q@Yh_ܸ}t|\NSCv|@->"CFR>a]FSyKpj SSAT3>aOQN*xp/1oS>>!bQ*|zH4Rm!ps,hflw;ǂV \ԭ W#;z6 ԵCF]PXd34rU}WϘnT.p/Y:Pg`䔥6>'ؘ}vjgV  L7  ܞLfm:|2^mಢ.UՖĽEC o%{0x#},2x/Ut4ej ƒ{jO3,BwmI(- -vX(R+XԩqHFM$vG~ϼObRteu<>҆ c$[dT_Xg"3JE1w$ gS,+]gs3er h!Z ptYFA(jwZ:8 f]ʜb(: sD`Q9@$H! 1/feOd5¾cf'iW&UT;qHvt"co$}ݧPGNKVĻjip7M0@"j9LѸ"`4p!OiM[ ]'"i(X(S\<3Lm,Z#z1pƘi6 N_x劳;l.(;B6qZFi0B[VhFnXtDN_hzX)_j0!;>`0xa9bLuc#M>bbtgS->c߾ ]= ۀ7, )OqUG/+{ %ZĩccbX(>f~l0 #|彾%]Uk5i0+V6EKec?yGp!"SP+ YĤ$h\89 M 7)>'LYC鳥T2 ʩR2XLjP*!KgGd&(S .Pr:\.G _0j5tM " ր9,43i{bh ܪ藾0773?Dz8 ^_ږN_E҅tߴ3 ̴K D1 cʱCIߍGPH uӶ4gW/b:׼-d7*Ō!s)1XJ 2Vkz**5qA" QM6( P+]dA5-P~$Gf b׼-GI,=1i*!?T(. Pr_q@T0.{,{JT)a!V+\Y?D(pe1/Pr%yKt^T[ vlWXcJ9,6Up0^,~bLj>H28oĤ֫b7yt~*^rM%h·5MSEK1oY"KLA-AJ.lxH) 1 v.j<<4XϜRQ/%|Q¾^!4F"kr=vۣŸ$>7 } 1S@](E秈wLͽ.*x~|B!;%hk8 W,ޚ)GEGJy-DXj:Ж_qo~.XPBb)x`aA2>YWWӸE6BǕk wSy!2>`dq*^Aec (m[ /0ȼWGN Y9M-uISE\fΘP %N]ͱ&4'xw \G( j10ogQ(;yHBP~^ib6=<˜1)ar*RP,mscl jE+т: XmS2lO @FmKw >UbZx6 xJT/"^TG +HA( .4aD,{P; })7ZOlZQrWFL>8+ |Q+bo?ܤFЍ 0 R>̶yє8wSvlT_< 4G4 }W` |*sB2^ePHWV=&֥iiegQ ٦WQ_H t,pZ6 =eX%Š7D3FG#e)lf\\say)j.6 D9b.c1 Yd_˕te2G{oрQBXy׾UfU򄶗wB#qv^g-UYFەU)0QX1UQٿa R1U-z{Lm k4ԧjX2ma!_[3gwꕡPM[REıf% 0@m 6MKp=2*s H]u컉e:CMVeR#8n%\yF;1$CUc `cZÙY!KG;m#e Gc1]HJؗU"nLcy?WKW4:>F{G ا Ni,OξnMˑ}ozlwו _ s~YpYd6+oO/ɘ_ʎC{j*Z>jZHUu@+uY;vwLx"܄+s>`7> Z7sLτj~ !X;E-[<H`N]!Yf6a* 耲Y/@.4'wyJnrJHm݊/ IaG(|]sD$Cb<~N1Tvd Io鎆Zw9dm={P SG2G<,⬲c b\UO٦YUuR+`:o>H+> %c E7℡,iyfΥWCBne~@O8" ~@'+O?p'?P ?vO ?gsZ9?q=H(upS^?XJ,8*yGܔ!g*|43w8(2+ѿ> TatZ}0fg=ߣr:#^{R5=/Oڼ5jQݫ_!5B}aŃ~W C}d0_NX6˧O!g 6~@;k~w ~RϵE?2#G6 Y&D4b?w }O~ɐ_p=_O"[yTqwr+kd%Wu~WgO>'L+Y%u,{>K#}٦SQڣ1m_? }J ~ƿYW_MGOr7~n/w~_(Wew[n9C!#j}>Whט_ϙOOGp?'d2''B'b'b'!@Ir˖K)Jy%Pf K5cdƸ_7<2Jpx! NE$/LQpxy𖥄,Ge(yÌCr&)EQ\’$T.(10ZX.(GW%E*R2!7&QIpfY,cFyayPuʨ8pasĢnAXbǚ9r2a\WqDKnVŒAD$ȁ XT'&&\ɜ1#*;*:Gh TCLH1+b.!p&&fL̘k1 %̩fTbS 69B$B3(.K M8 MZEZzk%8ʅ`Fl*`*^Iz T#^a0T"aq05rqqe`b\2nEQ3¢ \%dUL(QnZL r808T1XJZ(F5q(&hQ , cbl#}^_NVU ;[4ydj 9,Yp \ 3JI7j=fr.2BlވxX f 8lN,pԲ iucy0J)J16D".,^+#ĭDR@ BTډQHl1FQmMu(+sB708Ĥ# &`WBܳ3j \1EA*y* j" DXpKbHM2h%l LXxD`0݈֌E׈;D>0[8KXJ8bJ- me$Sg^EcSBW5OD(FQKp0g2ĺp7dLdZ,p C@qe˗| \Ta*1.V6LK EKcw,lE9/dS@fic]Lіi-T&pª`%nR5yD.\^k%JF1cd"D'tRYbW$&jyYnkd1E0R 21w G0&l+0 'k.Yh SOK/D"tG %^G eLrudG92LL,$UJ5qBk1LT~k +<6dR# 9lEAwQhJsbDppRj2 ]L(U,lBG%D䨒JpCK0"Za2EL#Yz*8c,pX#%@,YXVx"ZJ 30\un[5p!J@+/!r)E8YV2CAqֱCXErJ*RBDX@-+%pH# B8 RCB2/aCҒd0"n Q `TC7Q[>Tp**gYP#s !jjTǁŋ.!88R?HHTB\q}JdB!(Fd*W+!aT_R+B.^8 p}c1RD r˗/rx~+SP2˃F< x8YXVVVVVVVRVVVRRRVVVVVVW>/|g>iO`TOI?ZPo *_IXٚ@baNh^M֯3gZ:?x2]SI=g_7/'Cb֜yOue  \ >f;?FZu?Km +UK=l?%q +ˇ|>'YLPV}k& 1k> i*N{Vf__X4`W}Ŀ׋cׂnK>fq~J)_x_PWP1T?_BWk{ܞ'=J=Or{ܞ+R{ޔLiL[ķo%Je2LZ_-_/={ qJJJJKK%J|B1*$*bWJBG\" R<\Q B˕ME(cC=*WJ*_+P/._\ p<TTH`9!+W ˋv\|ŌHJB$bpp!AxCB/5J*TUA.,Pr!1brT@ĕFT`A.'\T!^`TH8+!rHFW,qr p%z PJxuƳ$HWYp>er8px P<#beD8qyK./8.\pCAQ qԪ2T.>ajTK˄Rȱx'$W,}*"Ae!pVc,$ԸLJq \1 r@!W,1FWo\G\bŋ 8.DDž|_ a*`eqq%JC("K \2$H @* X\TIP"28 F\B.(Y*1tL 3EoTp#(+AAră|8Rpo>Iq9F<Ö(bȐ`T%ŗrF$ PXV9PfP)n\ˋ.(^#0J<. ^zE8D*1QJ8LPb} p98ˊ\.( rXF\"pPFaŽF0}Bǁ!I rqq P@qLUŌ7+!F,!e=HC+YB\xqK1[B%DpyP488<"s X=1ro' PK ,G+ q_( 8*\yE*(=n\r˗O@B2\ QcWS+qT'|,+/x%FT`Ұ.b%PxFK\HzOEǚciPcBFD;/FncC\HzkG'/a.g|ᚋk3#yK 0P~ Kx}$b1\-2bJ&P@Ge-_pz*' xc+~E+z88H+cQ?-\?#~!_XŋJ~fRJᐄ!>y?'\YRJzHCzL Jԯ+KKKKr^ >(RG^^Z ZZ^Z_@ libkohana3.1-php-3.1.5/modules/image/media/guide/image/upload_form.jpg0000644000000000000000000003027611772106746024201 0ustar rootrootJFIFddDucky<&Adobed o0       1AP!@2 0p"5`$4B3!1Q"Aa24qRPr#s@B3pbS `C  0PpQ@`aq!1AQaq𑡱P@ `p0 @f9oTq2P/y3j6|<~=xA7LBT"w5tk6s7eyQ=Uv翖cί{;ٍ4&ٝ9ξnwz_rNsXns.;yc8u羳 g*t_^Vlҙˍ%d楙\A'E}f` 4Դp5$ҟ%TdP99KZkVtQF2afؑPntM λl3Ukqȳde nܲdg[QX˦Wtu,),7-;$򖙵}nwWRw"܀HLrd9Xߝ]|'@Tk4}2nls \Ǭ >ޖ"o~NĊZ:|l_Yȯin J hne.EDI$ 7e.EGј&EhًM]bO[IK-v0҇gh62Ug{r sjlww[qEqӎ\:fL5t=2Tg{QqvWj@j@j@j@j@D77B"zRaڲI]iuX!ʒ<`; ƚqa)S; ֔ؼW4xĨ^Wיa.*&97yB ? ٍm*Og) 6-đD&Xa׎^:mx׎^:mxᨱ?naSNu:YWNI2q(8{tT&ye=楒bJ`IKkj`[t:qB~ަ@F[UfA-{K`0 af;ڬ-4ccccccccc01 at {OVjޤ,>cBM Y\AиA P_2 $i Wd[ $z[  $́dW3{b1F#ct  ~?`0 F#cuw_?~Y`,LZ yЭ9Vį?BY`And1Ne?R]"zev ebEv´Đ>bskE҈a"ɚ0OonOj-JLmpzGm/oHƟэ_2J/EA6[q9 ycI[0d~9r|R9cQEԈ`xԡl,[W(q /[@6hKvIIhoDpIB6bYIҁM.Y fWnO}~cxe eu ,A-5-juqekf nG,_!΋ttW/k6;QKT 2Xډˍ,rmHYUf߄iV7+ÃQ]&`oL=ִ[$]\&Z4U9ҦNVMp+#Sfڠx˄y"ܟݸ4|](.Uf,6֡ݞ 9r!EibONT!PV^Ij jSW=J(5*+uW;H 7Q0q<+Yުn chzƏMn]_F4u#MXvݝGn4AJI@:A4zQ4NrӍ760#/9kGݍ&8+c,Omw#* ŔRSv$/ b#/*WL )nE ^]$!`L1ā-pn lujJ N{+gyp 5C 6Aׄ[_BQT+ׁ dpתJ$x/VvRRڬ=<@r,YRĊӮ1qⰋv*aJK$bYЅz9Պ-l0yNT,୬ {):bR"J+F Xh *7!Q8-Ӱu4n#Zwz[,e#G^'*jܭD4XE̸-\.M\Ʊe>oGRV cǏ/l_<|xrǏ/Ǐ/Ǐ/Ǐ/DžH^˭5-LI9L+fPjqv Qc*qe2;QNX@Cz:1eP2)j:՗] gH[/DJ:'M0s4` -ZbH"Kw NX3Ikvh4|Cj5m=;q*& 1D4QUcR+fzq΂Q[Ԏˈݦ22#.AV͘ǨV kP+ ;nuJlj cMV;d5bQV)W:'~,4]ʟ=mq\(sjݞiy>n[ ɐ^: L$mT b((kEn[ѺR6/dN?6FN @Zq 8gJB(k@]iTMϥ ^^Gh.w/ЦVQx7{~mE]mzy4~7:?E^=~꺏_=~꺏_=~I}Qo%3Qx(iJ/ZM-G??Dxhݧ{֑MoR*Fh:=oR71~1>:@lєx}V$ZӿJJJJ?k&j{~ZwִҊ>瞧;(b5ON:tc_B/5j&=4Z|ЉVVo__s׿֔ߴ׿ֽ7]=kְv3-FE_l~5ooEɢMޣXjqk:՚Y{xqi?EtWEtWEtWEtWG暏z+NڒӧzӳME8O#koEG֦jq"wi=z}iҟO _ck/vOss>;x j;TvQڣGj;PGz;w4Okt⮿s<}tg޹?X_ I$I$I$I$I$I$C̅I%I$I$I$I$I$5ܱc(͒I$I$;#LЄxI$I$H8ÓI$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$fnI$I$I$I$I$Ft$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$ImI$I$I$I$I$eWI$I$I$I$\z==2I$I$I$I'rI$I$I$I$I$I$I$I$I$I$I$HI$I$I$I$I$I'2ԒI$I$I$I$I$I$I$I$I$I$H$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I??&TCOxVm Yc ,DثI&pWNSET>V0CpJdgJkeڐ(!xl ă+4Mif MRV==LXt pGr"gr|@$fm'K"j"?O,R"a3zn.鄳hSP$NhaƱDJCBڅ.ج}" "sU-D[Z~l \V\Q  *n 7 Xm"U΄ SDfveҦaV FAT(4cVZL7D)0 f2 ΎH`TL^C6I7]i%SnT&%1I0d0 40 X0ȓZ*fo q 4GO ^ $嵀Q0 ;oQ $tozS<,6 85(XNJʇI K"~9erqف` lJq5T!˪fҨF2oWj!P 7K+CXhp4,mEH ڦDRCjK,+)7,^j+%(q'̶B#vb<DCe$ N )1HL{E,LQfm .=WP|]+2(%іɷ}g nTͮ@ ,#@\r˗.\Sb4j_dIKPDҗ}S]bۄ0C "EUpT !j!IӖ%ZSs L֮&O'!hbD#&m zlI4%;?H܄v^6~lpmO4;ڀ:7{r]٘ѹTjmJ+?b{ޜg_NJqFLP| m҄3;=5h#j4ϟج=~9 >ɠ @f`k2y t[d_3w]wMZ!$T1ܗNP[A)$C$4(JH?2-@+X*:оZAJGSko@r]qM-.oqX r)(S1:Sv\ҫ.hR).k>S֥O*nCd%4N h ޥKF 3f;\|VLͣ)j;vqOG~*wjb }ށ6)'f 7IN1z"KPKrcʈë>*é6_aT6q%8s#o5MźM# !i}i&oq%Zd^ bKSHd=Rzlt$#KkwI:DsZ>9ҭ#Y}A5NNzY+z)LChp}>U:}:O[ojSu">3t@ MC.7[ڔgFȏc}!}*g#Ӟ85kV^5D:\ H$4b"iҮkPA3RRA T!7?y퇬<n,A (( S{Х b1PLԾZd OS5ճr*7͵kW^qC/mʇ{Pq;NQ;/icZp ݍqVvX\~oEʈs:_ae=ƣDm7 f/$ܮґ#U/),zsK zwBڡi{Էz}Էz}Էz}Էz}Էz}Էz}Էz}Էz}Էz}t"y|?3QH'$1:N<₂f?\ug}x! d \cF-F^Ȩ;FzMkx]ٳz˫bcP89Ȉt>iq><4-=D2B1`gޭl1穴ZԕDb6}͋y~_3tuz)ꄗ{ߍ/h "&utA3%<ɤ 4{ϯǯ{S75+ti9=nt{rKNͰVOx쵝@=c/]ӛul'cZ} ,]1SϠ#z}rBJ-\2F0B%V7NXBdɓpd$b v5~YVS&A26+_4C61} }?={+>Nst˛2Jf."FMڵ:ybƽ/u8t x׈Ϙ+6d];wt&5 M-ߞog6vJ_O=vƥN3->wïu:rFKlU,J>ou(ԕY}lF盵nf5nuMJIޔ.Qgơ^$,ܝ[wnz<<楚U܏]y\sz'8y'*^ח{9חf5~uƯ>onbd5qFU5EemZR55"lTOMambZ$=7 ^EVO6c[Y\,[x+7;(ʳ1griB9e 53m67ɼ2WhI<ѩM-#$ljg/r$m\NהZK !dgG7ˁbɕ,RtAl,g:milo)r[[Iuc. p/XN/h?Afy\3-wiryCy-y滏3&>.6cgm3;m@w Wv1e#2 fz;j;bwp]cyw㇗cGGcG㇖㇖cc㇖c㇖㇖㇖c㇖㇖cwV<Ǖcǖ㇕5㇗EqY]y) @ Sևnh"_};dj9:eH@A4@M &" @h`"?&(\ F %)̈́N3~6BoEP`b#G{~ zPj_!Ë7~1VEMRXb2yXͤ֋[Yg+Uf6чDQPȬm+˲2DXS62'Q2h#s1{`I e7r#&8Sc^2۝0iu눝+ݢ Y tZu\Oȕ& (و3Y9+wX yՂh&3$f\ڼyr-]˙q`vΠBBPT* BPT* * |=o%;qMeo Sh @7&c4';"H2PZě z(_{j oUwߗmvT !=BBZ @- g>.n{\?, wB=C44~f-/?}B$Qȼƌ?cG"FJ ondїQ,RL8mkAuLmD 15ɡ幏v kUJwtpZ?rQYsN<&-F2*mW=0Da bYO﨨I1C/ p`!5oβ}@T5*;`7^1pnh󬸖nx/sAë+)> "묻}։XpEx.XZnBŘ)$`:/Sl%p7[\FKYLہ *Sue'jTb\AmJbEuS9MqlxJ7ݚee6Pu마1:iih1"*lf9=d klhSl%p7[\FKYff.es*]xf9㖜e;%_,b(m^ۛNmW6ӛs|͹j5zsno97^ۛNmW6ӛs|͹j5zsno97^ۛN]W.ӛs|͹jS5zsno97Lި\v{6*¾m4w|cEi >7w4*Q&48t( c]]\ok۲>ňwOK 4)ω5)livxhƚX]5Š%+vUZ|n uivu+xEڅQRW JJ ZT9ajR=⃘VO A]XZ5Fm uPkY &\&ʿCZZPU҇VVPO&nXԳEWmV}.1oҜԠnZi E-p~^j,'mw|CY5C `.J%6vOMФkZK8Wjf%tY]Nq1xOH:BB}++Vvz(Mkբ WURV$/M?*SUج(_Q~3Q /5y]Nei,ׯE\JWUXqai]5e-ն |nٿMjoіwO񊾬HazNIej*it@혼'Ƌgضlu]P'&]KVh*ѢjڴjІVBZA]'GF>7w4ދ[jRle[VhZE~u^-t ZW mTW صxVhzoe"鲲u 4RUwJ)KkN 4Hwտ*!>qkjЕgЫPw6d t]D`o N8`,ֹ,E' ۲C*эWa@@q[(7؞L ӽYr2(aiqىu*0`~6iJhhـIg.$Sv,`Hv8l(ilvD於])j0d:0?Ίs~׋8mTNhR$7 kM\P>g(ˋq`\ v.> 9 U?k]ғScDӤ|fX0&MhS 拕0x@"hn3Ǝ)~SY(*vZ%)qBKxQ4i TڽXt^b-` ..;,&/{8Ԧ@T4ҭf(iEYgM.5:kN-}jOX:JZ)4j|9 N'GP9x1a?;1{˘#s]׶ހ:M?0đ 4XI܂?uFv"ZY,L9f&N:@7WfTcIќM>״(Mܶb1KEdҙZ]/.og G?r`zܷڊ'5Y\jo3rv( tZBӡ2H4RAk\vnR;$|rk0/TR_,LRv i rn#Qf%Xrpny^]0 G-++'ʽYw]w]w^R]ש.i9oIw^ܻ]7¬]n]ש.KZ%-)]ש6kԗ{|K.)A#/ޖKv›]w-&cդ2E#CJZhhriQ $n rpbe E3ac}q,]`<[Yl!&=ZCnPeHr0*Ci3SU! jGSbA7.7&4`o1Y%-ȧL 4<瘆EoG#Ik6 ה/'Q |@騍 \m/ia;ūGQ*ᏼJe]"5|9E[*)f4@aarLN+ј?Բ=#\ ΓSn^X6Sk(@o0eaduF<6/A]a`$适{U\@ qF>x-A&rmϜrHb/T%[k 4;Lt7*# \vb EbZP|s ${D7V_e:ZT1CV#`&2i{Vfuxf|x -u*5V^R?h{LBѼ7y)jk1;[BL J`YXYB!Ux-`Υ' Dvai3fD ^ӟ*?Ļ;3ʄ-㛅(ǜ'yyu@t8Ãrt7 ğ`Xwfh5)h,8RT99v@Vaw,p9=·Xqr1g3rl8*9ĴsBN;ʷlynh]#YW>r t?gi?uƦ侤t{ ב9/N(*ŒPo:qz(n5|Y 1UXD2H&U9ϓ]Wg hznڕ]<9/fJKFfY[K _dќX3^q55c2z[TҌcG~)c8T^C eR/z Twg# -yT/>VpiVe^|wqX\I`~ {xe4f|c%5B)jhŚ}%fB3=O\dv@{&Nj*ƼKRfUYIG #H A αeG1E +&5[T RגQe3QDz %pCT-YDQZwBpCTk5LԫV棜ȉY`pf%- hXvmQa*z`4(WMD~f 93#oB, j5g\+ c?5n#5X^um%_J"t'ZBm%ߜ>t`0큠>,'o!]_[ мtn=7q%a/L++~MVZ˼Ia׷ejMl Yw˙ҋz=(*/P`3/kʟ{(\yە_]c˵>a_n]J zj,)=e5y80GN%AW {Tܲbk, c5bFhm ,r؊&AQ*,tBs}ЖEe|l;@eaKGWv@c)\F- SzL+27ԸPT%bdZ \UO+!腒N@.G?!l9V9}/yS_M4`fJ~੏;S4wПZ:N>:=ftz }&}yJf}qŸ[zu@?,7v5׼7y}+nS7#}aZQ? N)՟w#+yiiyyoG*T_|U+¿E|5R*TйYI_WiWYixT[ j"_W+p*TOfx'_+.UJWe*WKx.?2̳J%FnT>t._| )a+rrr˗/_E@=_5 T~7/ v_+~;e;|撥J+^*TRJ?9_f &.\~r6%J~ +rkߗ?x?s0]>EOϔO0]v5_׈C2*_u.yDs/7X.^|=5y~ezG^9VVVVVVWIX IXWY?!lvxcONJbE_M>Dw~cLߩ\˖6 gLmCyV&Gz]g+^;kΙ+(oeo _޾?tL1x&2qJ?|̿x68 {A}gMJi>}>?d)+ᬤƲ._._7P%|~7_JBRJ*T *TRxԯo2+_G\"gxR|7r˗.\Ys2F*T<i5ksߍ˗r% I$I$I$I$I$I$I1_$I$I$I$I$I$CxB$I$I$I$/M:BI$I$I$I#I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$I$16$I$I$I&vAI$I$I$@/S640]^dI$I$I$8U۲I$I$I$I#ŋC19RbI$I$I$ զiK$I$I$I$o#E32I$I$I$H|F~%ZY! ʘ2TY^ r?7V9U$l=(HCK6]k `wƉCz7lY.h;  $Uk10qq^Y3*E2TiEW%=Լay 8shP1{Tpv!7F]=HPA⌷WuӼک@mxlUn'X=E( cq1)Ymуի .q/c6oQfJ_HHt !.)a0mfK( ws .-} Pf/!pj1,VW(:-.ދIDoìl)pP Jk1r0ư{@J\nuS1n?9Dʒ!o5[0.5j9 [P=-T d wr)@AV}6W ZF'VQ{ze]EPT :YeǷnKJ路u󚐶6 (kskn,w:. tN<wPל=ǣԢ% 1pĜG8VAα r)O;bLJ<ԯMMDgtۦ"HX@c0(!Kx9}@+_9Bk"PTX 0;+svj 7VGPD-r9ts1'_6.DyDi á0wO2J͋@`&9a D2$ȧpfx3zVһ99]\5 (S]Uv m,dBh;47 P֚Rތ}*`;3؄'G2FŃ`;nF`n064qƫtNR3U($6hb=6>We;qo9ܻZ=Be=Il× e~>]WTЇ>Sy@Ud` ZRiZhY4V)[egfYyG2J"׊.g;7ll#k^? 4H :(Vpq5oTIqnX!D8yK<DKkvrfymT]J} c ?+UBᆪO/c.=gG-<Q+cUQdz,̀h&.jMGhY+M 5JdմY9y2p2J6F\v}r^d BR\2w{K͡߹亵lJBRp4^fw4~stknjlU% bsЬ^NkU,6̫kmb0B A#UZ`.cp:D%`@cMn} M/ p40Q  g`rtWhjɴZZ0YEJ@9b&WB|4* Qm-J|l:+ Ku,6դz1gX{gzA -fA[''̯8@]Q!Mj^ϷK ]8mvy8IB`BppSttk)zǓ}Ƞ./mi ˺ ]Skgh<)n!eoe5VwXprl]X1S/ Q۩pAEa*(߬ZXtJƎmj=mzrKVy^hSW%AQngAh^@irZ%RUd.b.OYj=Yp+Y2!>N꼠݂oa]l& 7"($ឹm"(ŮSⶅg.!ffa(x2V8*ˊo~la ˙FŸu4&àXJw&Hx5P%q ^mtrힸ/]0; `|6D]r1ڷXq5@ @;"2U;p*ܣ !o/ sA%k&(7ս\!zU`ќպBE%܆)Fs d/?l[ĺEx_a|1b~7;W ~>C{JW^b?']kzd W~#G5볎;KucbsC_v05Tjm:-W nohSٯB?Neĵ{z^b ^:9Ƿ1s[`U<{5waJcH[wKxĺ'HZ}mW)saVsۈW\Wл*E;2֞{?zVDCXhi: %ʽ`UNdat w+Wы e6kPŘ7yUkqxU55ỳUNvy8=n})Q<';}zN;פ^}zN;N;N<'|jy~sOST^*T 5 J _J਑%x*< T`l~K_RDԙ -Oafi!0X\Wqk(nl>sW҉~Jk]%!#tq FR$(R7<~"A ~P%J/)DC2fep<2>B2IFr@DQaF u`HU/<G$jμa-L1px씥H_yU ~r0%XYh< ŦԤfRY(1$\)9 H:xb:KÔ| ECž" 7ixK#0`R Xq`6|HWEL%e7<˨lLȗ.+?9&&62@*RUD#RO/:pQǂȊw@ah?"Gd` >1P" J/o@e.cm]AD4뵎=}44pP+|:]{BK׽̡7yاմ=q+j㗅 Z*&u9fw0KZDx|MUWQW}޵} ߫tP~>s-^W}٣\OZ``Erݼso˯wE[Ge5o/NBczWOLŬ;y[WuƊ{{.UDw,2ی|]ّlS= Mi~_D/}E:BE=Կ3^@u-y̞d Ro <ނ?l4ڿT s+wP=OtŽ_F#"UYUx+߱ON;j[+>/˾8sӦ>~g9=I~;tϧ?^;;rxiD2hY~ˆމg}/7G-@}^OٜzUsQ[|Y۔xFO^NrC>pvGTik=2KJ(W㖠 78F?o#yK/Z90 J}c}Wl3뎔4mƣ;rq~|\WЫ *pU:y< Vs|" ,r˗.\~%˗26_˗. r˗ 60r˗r/*^ ~VͰh|w ?3j@>{|+<\LDJFTb\Y~ 0\ \~s˗. .(q+x* KXrH-$*eVkq`8Wbii2bR !ac.Qſ ԿEJaq̩h7)&eA#R%xT/ƒ%x_J///TMrf`0PSk3JIS("P_-Q)//A2Y2p`L@J— 9u; 02E0jZ ".0WR[[TD2Ef`xZ1e5ΆR,,V2X$G O?Ͽ=yu|E-JbYt6#zޱbWZb3So1u<:A~~Lq~~L:tzgg& Lp]h޴hG~^3W tzbr+~_Q5zEV6߹Ǜh)U}ou 3( ཷקLm Pc\R^%Tdqb,͸r (D%K-~^~w{UljQ ѫukYW*ezB]w*U.^Ybl~}FG\>zj1Î?Xjq͝3 Qk3X(YLajG=>,,>?ֻE˿~g[ Rdd[Wklibkohana3.1-php-3.1.5/modules/image/tests/0000755000000000000000000000000011771133710017052 5ustar rootrootlibkohana3.1-php-3.1.5/modules/image/tests/kohana/0000755000000000000000000000000011771133710020313 5ustar rootrootlibkohana3.1-php-3.1.5/modules/image/tests/kohana/ImageTest.php0000644000000000000000000000127711771131766022726 0ustar rootrootassertTrue($image->save(Kohana::$cache_dir.'/test_image')); unlink(Kohana::$cache_dir.'/test_image'); } } // End Kohana_ImageTestlibkohana3.1-php-3.1.5/modules/image/tests/test_data/0000755000000000000000000000000011771133710021022 5ustar rootrootlibkohana3.1-php-3.1.5/modules/image/tests/test_data/test_image0000644000000000000000000000657711771131766023116 0ustar rootrootGIF89af3̙f3ff3̙3̙̙f̙3ff3fff3fffff33333333̭333!+,pH,Ȥrl:ШtJZجvzxL.zn|N~tgV ! R  P ˸y֬Q َҾã o4i BTX#j] C4lI}6 Q͛=*$&r 1XьOft wqXɡVJkB BL3U'|c@,l.R]i[/JFBbԺQ!@pvzt_)n岤6 ύ3k+#Ʒ0?x5&k+;`1- I<'IYBr%4)i2ט)52[\1T,Y *3w(N5%'tl;ztq)nQ $@08RAVw,:"(S\xلfxheg0lwAB|T [v®)abI9?xu9O`> Ƹ&(&n/b-^8*h@+5B۱1Zx-ZU ynuKυ˕zzT?ZOh7z#x뿿Ab,lxDQ$_ "Zn۪~s` @#?h9 L `<ÕΧE wB !tL02 A`קN:Pmht`O9=$@pqP j{`ŁL\s1ZJP`t pO_hݝG_6GnamQs"JAqUA䒌@IDNɈD"{ω1W>5̔ G>Ra[[/qEg˳49A$3Lr42U1p| ajIU3&ta0LX2>̉bpwySD7M*fUY擊hx#lEvNL+@2=GB`el+83# |ࣸr*bk.#6IC~Êb:n8OA}Cܙ@">la>li VR8XD)$@UXIk"Jx U-Q9qْXUy Qpꁳ2UT9Fjj*>`5yhfi%*´EwZ8S6v’@lV`x3<uѵ% )9LrfԵVN@ ɼ9j ^ j|E*Uq^Le^|ln:\)&󥹷PE;40% u3Z=4{İ茛\F&mkK๚BRU*9Ls@ٗ%I?Tr\ ^ ,E|l[*SW3)@"-Jc[H9j& @HUaۣü[}Mcׅ2]wOD&Ә91RT ׽2Zg:}W 8.}U8{Uu+i4܇1q*/IŞ4c[YfǾh$f2<MkhRqX5pZQ" dB+DZx^^"y|zI(P,5(ͽ(ݙaB\ԉ9Cj '8uTړ+a=4t8OdШ~f)r{zw=^^{ZƼ \@\{~cryrsRd~7=V` $tH@&5IgLoG)ZvlOUgExeH˷{Gn7QDYBsbkOsEGi=lKSaorxqMI9Ɩrqqw8 $sV>NTYv_g;C{'!^Not 7;&(@oaDZ$2#dmlHnR{R:2Uxy6|籆]6v"V6nb)}gGWFwCjQO= ,Vl@ZDxRlOswm|hh@hk"~7"M{tkv^^x3uL8x&d^@L3{nH$"n=WѨH'sPK dNJj`q9'‰(#5xldK fq Coz툎 )t`1f?"h`6uR!),i [v-y Br H 8A\80gBCI%qGKILY8Iq`VW8M`,I ~DE`j9I b ]rIViY N(k}B9Y9ɔ{w ypIǙ# yY[pjŁ 隴i/m[(–iʹٜ;libkohana3.1-php-3.1.5/modules/orm/0000755000000000000000000000000011771133710015423 5ustar rootrootlibkohana3.1-php-3.1.5/modules/orm/auth-schema-mysql.sql0000644000000000000000000000371611772106746021527 0ustar rootrootCREATE TABLE IF NOT EXISTS `roles` ( `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, `name` varchar(32) NOT NULL, `description` varchar(255) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `uniq_name` (`name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; INSERT INTO `roles` (`id`, `name`, `description`) VALUES(1, 'login', 'Login privileges, granted after account confirmation'); INSERT INTO `roles` (`id`, `name`, `description`) VALUES(2, 'admin', 'Administrative user, has access to everything.'); CREATE TABLE IF NOT EXISTS `roles_users` ( `user_id` int(10) UNSIGNED NOT NULL, `role_id` int(10) UNSIGNED NOT NULL, PRIMARY KEY (`user_id`,`role_id`), KEY `fk_role_id` (`role_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE IF NOT EXISTS `users` ( `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, `email` varchar(127) NOT NULL, `username` varchar(32) NOT NULL DEFAULT '', `password` varchar(64) NOT NULL, `logins` int(10) UNSIGNED NOT NULL DEFAULT '0', `last_login` int(10) UNSIGNED, PRIMARY KEY (`id`), UNIQUE KEY `uniq_username` (`username`), UNIQUE KEY `uniq_email` (`email`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE IF NOT EXISTS `user_tokens` ( `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, `user_id` int(11) UNSIGNED NOT NULL, `user_agent` varchar(40) NOT NULL, `token` varchar(40) NOT NULL, `type` varchar(100) NOT NULL, `created` int(10) UNSIGNED NOT NULL, `expires` int(10) UNSIGNED NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `uniq_token` (`token`), KEY `fk_user_id` (`user_id`), KEY `expires` (`expires`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; ALTER TABLE `roles_users` ADD CONSTRAINT `roles_users_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE, ADD CONSTRAINT `roles_users_ibfk_2` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`) ON DELETE CASCADE; ALTER TABLE `user_tokens` ADD CONSTRAINT `user_tokens_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE; libkohana3.1-php-3.1.5/modules/orm/auth-schema-postgresql.sql0000644000000000000000000000307111771117100022540 0ustar rootrootCREATE TABLE roles ( id serial, "name" varchar(32) NOT NULL, description text NOT NULL, CONSTRAINT roles_id_pkey PRIMARY KEY (id), CONSTRAINT roles_name_key UNIQUE (name) ); CREATE TABLE roles_users ( user_id integer, role_id integer ); CREATE TABLE users ( id serial, email varchar(318) NOT NULL, username varchar(32) NOT NULL, "password" varchar(64) NOT NULL, logins integer NOT NULL DEFAULT 0, last_login integer, CONSTRAINT users_id_pkey PRIMARY KEY (id), CONSTRAINT users_username_key UNIQUE (username), CONSTRAINT users_email_key UNIQUE (email), CONSTRAINT users_logins_check CHECK (logins >= 0) ); CREATE TABLE user_tokens ( id serial, user_id integer NOT NULL, user_agent varchar(40) NOT NULL, token character varying(32) NOT NULL, created integer NOT NULL, expires integer NOT NULL, CONSTRAINT user_tokens_id_pkey PRIMARY KEY (id), CONSTRAINT user_tokens_token_key UNIQUE (token) ); CREATE INDEX user_id_idx ON roles_users (user_id); CREATE INDEX role_id_idx ON roles_users (role_id); ALTER TABLE roles_users ADD CONSTRAINT user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, ADD CONSTRAINT role_id_fkey FOREIGN KEY (role_id) REFERENCES roles(id) ON DELETE CASCADE; ALTER TABLE user_tokens ADD CONSTRAINT user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE; INSERT INTO roles (name, description) VALUES ('login', 'Login privileges, granted after account confirmation'); INSERT INTO roles (name, description) VALUES ('admin', 'Administrative user, has access to everything.'); libkohana3.1-php-3.1.5/modules/orm/classes/0000755000000000000000000000000011771133710017060 5ustar rootrootlibkohana3.1-php-3.1.5/modules/orm/classes/auth/0000755000000000000000000000000011771133710020021 5ustar rootrootlibkohana3.1-php-3.1.5/modules/orm/classes/auth/orm.php0000644000000000000000000000015111772106746021336 0ustar rootrootget_user(); if ( ! $user) return FALSE; if ($user instanceof Model_User AND $user->loaded()) { // If we don't have a roll no further checking is needed if ( ! $role) return TRUE; if (is_array($role)) { // Get all the roles $roles = ORM::factory('role') ->where('name', 'IN', $role) ->find_all() ->as_array(NULL, 'id'); // Make sure all the roles are valid ones if (count($roles) !== count($role)) return FALSE; } else { if ( ! is_object($role)) { // Load the role $roles = ORM::factory('role', array('name' => $role)); if ( ! $roles->loaded()) return FALSE; } } return $user->has('roles', $roles); } } /** * Logs a user in. * * @param string $username * @param string $password * @param boolean $remember enable autologin * @return boolean */ protected function _login($user, $password, $remember) { if ( ! is_object($user)) { $username = $user; // Load the user $user = ORM::factory('user'); $user->where($user->unique_key($username), '=', $username)->find(); } // If the passwords match, perform a login if ($user->has('roles', ORM::factory('role', array('name' => 'login'))) AND $user->password === $password) { if ($remember === TRUE) { // Token data $data = array( 'user_id' => $user->id, 'expires' => time() + $this->_config['lifetime'], 'user_agent' => sha1(Request::$user_agent), ); // Create a new autologin token $token = ORM::factory('user_token') ->values($data) ->create(); // Set the autologin cookie Cookie::set('authautologin', $token->token, $this->_config['lifetime']); } // Finish the login $this->complete_login($user); return TRUE; } // Login failed return FALSE; } /** * Forces a user to be logged in, without specifying a password. * * @param mixed $user username string, or user ORM object * @param boolean $mark_session_as_forced mark the session as forced * @return boolean */ public function force_login($user, $mark_session_as_forced = FALSE) { if ( ! is_object($user)) { $username = $user; // Load the user $user = ORM::factory('user'); $user->where($user->unique_key($username), '=', $username)->find(); } if ($mark_session_as_forced === TRUE) { // Mark the session as forced, to prevent users from changing account information $this->_session->set('auth_forced', TRUE); } // Run the standard completion $this->complete_login($user); } /** * Logs a user in, based on the authautologin cookie. * * @return mixed */ public function auto_login() { if ($token = Cookie::get('authautologin')) { // Load the token and user $token = ORM::factory('user_token', array('token' => $token)); if ($token->loaded() AND $token->user->loaded()) { if ($token->user_agent === sha1(Request::$user_agent)) { // Save the token to create a new unique token $token->save(); // Set the new token Cookie::set('authautologin', $token->token, $token->expires - time()); // Complete the login with the found data $this->complete_login($token->user); // Automatic login was successful return $token->user; } // Token is invalid $token->delete(); } } return FALSE; } /** * Gets the currently logged in user from the session (with auto_login check). * Returns $default if no user is currently logged in. * * @param mixed $default to return in case user isn't logged in * @return mixed */ public function get_user($default = NULL) { $user = parent::get_user($default); if ($user === $default) { // check for "remembered" login if (($user = $this->auto_login()) === FALSE) return $default; } return $user; } /** * Log a user out and remove any autologin cookies. * * @param boolean $destroy completely destroy the session * @param boolean $logout_all remove all tokens for user * @return boolean */ public function logout($destroy = FALSE, $logout_all = FALSE) { // Set by force_login() $this->_session->delete('auth_forced'); if ($token = Cookie::get('authautologin')) { // Delete the autologin cookie to prevent re-login Cookie::delete('authautologin'); // Clear the autologin token from the database $token = ORM::factory('user_token', array('token' => $token)); if ($token->loaded() AND $logout_all) { // Delete all user tokens. This isn't the most elegant solution but does the job $tokens = ORM::factory('user_token')->where('user_id','=',$token->user_id)->find_all(); foreach ($tokens as $_token) { $_token->delete(); } } elseif ($token->loaded()) { $token->delete(); } } return parent::logout($destroy); } /** * Get the stored password for a username. * * @param mixed $user username string, or user ORM object * @return string */ public function password($user) { if ( ! is_object($user)) { $username = $user; // Load the user $user = ORM::factory('user'); $user->where($user->unique_key($username), '=', $username)->find(); } return $user->password; } /** * Complete the login for a user by incrementing the logins and setting * session data: user_id, username, roles. * * @param object $user user ORM object * @return void */ protected function complete_login($user) { $user->complete_login(); return parent::complete_login($user); } /** * Compare password with original (hashed). Works for current (logged in) user * * @param string $password * @return boolean */ public function check_password($password) { $user = $this->get_user(); if ( ! $user) return FALSE; return ($this->hash($password) === $user->password); } } // End Auth ORMlibkohana3.1-php-3.1.5/modules/orm/classes/kohana/orm/0000755000000000000000000000000011771133710021116 5ustar rootrootlibkohana3.1-php-3.1.5/modules/orm/classes/kohana/orm/validation/0000755000000000000000000000000011771133710023250 5ustar rootrootlibkohana3.1-php-3.1.5/modules/orm/classes/kohana/orm/validation/exception.php0000644000000000000000000001201011772106746025763 0ustar rootroot_object_name = $object_name; $this->_objects['_object'] = $object; parent::__construct($message, $values, $code); } /** * Adds a Validation object to this exception * * // The following will add a validation object for a profile model * // inside the exception for a user model. * $e->add_object('profile', $validation); * // The errors array will now look something like this * // array * // ( * // 'username' => 'This field is required', * // 'profile' => array * // ( * // 'first_name' => 'This field is required', * // ), * // ); * * @param string $alias The relationship alias from the model * @param Validation $object The Validation object to merge * @param mixed $has_many The array key to use if this exception can be merged multiple times * @return ORM_Validation_Exception */ public function add_object($alias, Validation $object, $has_many = FALSE) { if ($has_many === TRUE) { // This is most likely a has_many relationship $this->_objects[$alias][]['_object'] = $object; } elseif ($has_many) { // This is most likely a has_many relationship $this->_objects[$alias][$has_many]['_object'] = $object; } else { $this->_objects[$alias]['_object'] = $object; } return $this; } /** * Merges an ORM_Validation_Exception object into the current exception * Useful when you want to combine errors into one array * * @param string $alias The relationship alias from the model * @param ORM_Validation_Exception $object The exception to merge * @param mixed $has_many The array key to use if this exception can be merged multiple times * @return ORM_Validation_Exception */ public function merge($alias, ORM_Validation_Exception $object, $has_many = FALSE) { if ($has_many === TRUE) { // This is most likely a has_many relationship $this->_objects[$alias][] = $object->objects(); } elseif ($has_many) { // This is most likely a has_many relationship $this->_objects[$alias][$has_many] = $object->objects(); } else { $this->_objects[$alias] = $object->objects(); } return $this; } /** * Returns a merged array of the errors from all the Validation objects in this exception * * // Will load Model_User errors from messages/orm-validation/user.php * $e->errors('orm-validation'); * * @param string $directory Directory to load error messages from * @param mixed $translate Translate the message * @return array * @see generate_errors() */ public function errors($directory = NULL, $translate = TRUE) { if ($directory !== NULL) { // Everything starts at $directory/$object_name $directory .= '/'.$this->_object_name; } return $this->generate_errors($this->_objects, $directory, $translate); } /** * Recursive method to fetch all the errors in this exception * * @param array $array Array of Validation objects to get errors from * @param string $directory Directory to load error messages from * @param mixed $translate Translate the message * @return array */ protected function generate_errors(array $array, $directory, $translate) { $errors = array(); foreach ($array as $alias => $object) { if ($directory === NULL) { // Return the raw errors $file = NULL; } else { $file = trim($directory.'/'.$alias, '/'); } if (is_array($object)) { // Recursively fill the errors array $errors[$alias] = $this->generate_errors($object, $file, $translate); } else { // Merge in this array of errors $errors += $object->errors($directory, $translate); } } return $errors; } /** * Returns the protected _objects property from this exception * * @return array */ public function objects() { return $this->_objects; } } // End Kohana_ORM_Validation_Exception libkohana3.1-php-3.1.5/modules/orm/classes/kohana/orm.php0000644000000000000000000011434111772106746021645 0ustar rootroot_initialize(); if ($id !== NULL) { if (is_array($id)) { foreach ($id as $column => $value) { // Passing an array of column => values $this->where($column, '=', $value); } $this->find(); } else { // Passing the primary key $this->where($this->_table_name.'.'.$this->_primary_key, '=', $id)->find(); } } elseif ( ! empty($this->_cast_data)) { // Load preloaded data from a database call cast $this->_load_values($this->_cast_data); $this->_cast_data = array(); } } /** * Prepares the model database connection, determines the table name, * and loads column information. * * @return void */ protected function _initialize() { // Set the object name and plural name $this->_object_name = strtolower(substr(get_class($this), 6)); $this->_object_plural = Inflector::plural($this->_object_name); if ( ! is_object($this->_db)) { // Get database instance $this->_db = Database::instance($this->_db_group); } if (empty($this->_table_name)) { // Table name is the same as the object name $this->_table_name = $this->_object_name; if ($this->_table_names_plural === TRUE) { // Make the table name plural $this->_table_name = Inflector::plural($this->_table_name); } } foreach ($this->_belongs_to as $alias => $details) { $defaults['model'] = $alias; $defaults['foreign_key'] = $alias.$this->_foreign_key_suffix; $this->_belongs_to[$alias] = array_merge($defaults, $details); } foreach ($this->_has_one as $alias => $details) { $defaults['model'] = $alias; $defaults['foreign_key'] = $this->_object_name.$this->_foreign_key_suffix; $this->_has_one[$alias] = array_merge($defaults, $details); } foreach ($this->_has_many as $alias => $details) { $defaults['model'] = Inflector::singular($alias); $defaults['foreign_key'] = $this->_object_name.$this->_foreign_key_suffix; $defaults['through'] = NULL; $defaults['far_key'] = Inflector::singular($alias).$this->_foreign_key_suffix; $this->_has_many[$alias] = array_merge($defaults, $details); } // Load column information $this->reload_columns(); // Clear initial model state $this->clear(); } /** * Initializes validation rules, and labels * * @return void */ protected function _validation() { // Build the validation object with its rules $this->_validation = Validation::factory($this->_object) ->bind(':model', $this); foreach ($this->rules() as $field => $rules) { $this->_validation->rules($field, $rules); } // Use column names by default for labels $columns = array_keys($this->_table_columns); // Merge user-defined labels $labels = array_merge(array_combine($columns, $columns), $this->labels()); foreach ($labels as $field => $label) { $this->_validation->label($field, $label); } } /** * Reload column definitions. * * @chainable * @param boolean $force Force reloading * @return ORM */ public function reload_columns($force = FALSE) { if ($force === TRUE OR empty($this->_table_columns)) { if (isset(ORM::$_column_cache[$this->_object_name])) { // Use cached column information $this->_table_columns = ORM::$_column_cache[$this->_object_name]; } else { // Grab column information from database $this->_table_columns = $this->list_columns(); // Load column cache ORM::$_column_cache[$this->_object_name] = $this->_table_columns; } } return $this; } /** * Unloads the current object and clears the status. * * @chainable * @return ORM */ public function clear() { // Create an array with all the columns set to NULL $values = array_combine(array_keys($this->_table_columns), array_fill(0, count($this->_table_columns), NULL)); // Replace the object and reset the object status $this->_object = $this->_changed = $this->_related = array(); // Replace the current object with an empty one $this->_load_values($values); // Reset primary key $this->_primary_key_value = NULL; // Reset the loaded state $this->_loaded = FALSE; $this->reset(); return $this; } /** * Reloads the current object from the database. * * @chainable * @return ORM */ public function reload() { $primary_key = $this->pk(); // Replace the object and reset the object status $this->_object = $this->_changed = $this->_related = array(); // Only reload the object if we have one to reload if ($this->_loaded) return $this->clear() ->where($this->_table_name.'.'.$this->_primary_key, '=', $primary_key) ->find(); else return $this->clear(); } /** * Checks if object data is set. * * @param string $column Column name * @return boolean */ public function __isset($column) { return (isset($this->_object[$column]) OR isset($this->_related[$column]) OR isset($this->_has_one[$column]) OR isset($this->_belongs_to[$column]) OR isset($this->_has_many[$column])); } /** * Unsets object data. * * @param string $column Column name * @return void */ public function __unset($column) { unset($this->_object[$column], $this->_changed[$column], $this->_related[$column]); } /** * Displays the primary key of a model when it is converted to a string. * * @return string */ public function __toString() { return (string) $this->pk(); } /** * Allows serialization of only the object data and state, to prevent * "stale" objects being unserialized, which also requires less memory. * * @return string */ public function serialize() { // Store only information about the object foreach (array('_primary_key_value', '_object', '_changed', '_loaded', '_saved', '_sorting') as $var) { $data[$var] = $this->{$var}; } return serialize($data); } /** * Prepares the database connection and reloads the object. * * @param string $data String for unserialization * @return void */ public function unserialize($data) { // Initialize model $this->_initialize(); foreach (unserialize($data) as $name => $var) { $this->{$name} = $var; } if ($this->_reload_on_wakeup === TRUE) { // Reload the object $this->reload(); } } /** * Handles pass-through to database methods. Calls to query methods * (query, get, insert, update) are not allowed. Query builder methods * are chainable. * * @param string $method Method name * @param array $args Method arguments * @return mixed */ public function __call($method, array $args) { if (in_array($method, ORM::$_properties)) { if ($method === 'validation') { if ( ! isset($this->_validation)) { // Initialize the validation object $this->_validation(); } } // Return the property return $this->{'_'.$method}; } elseif (in_array($method, ORM::$_db_methods)) { // Add pending database call which is executed after query type is determined $this->_db_pending[] = array('name' => $method, 'args' => $args); return $this; } else { throw new Kohana_Exception('Invalid method :method called in :class', array(':method' => $method, ':class' => get_class($this))); } } /** * Handles retrieval of all model values, relationships, and metadata. * * @param string $column Column name * @return mixed */ public function __get($column) { if (array_key_exists($column, $this->_object)) { return $this->_object[$column]; } elseif (isset($this->_related[$column])) { // Return related model that has already been fetched return $this->_related[$column]; } elseif (isset($this->_belongs_to[$column])) { $model = $this->_related($column); // Use this model's column and foreign model's primary key $col = $model->_table_name.'.'.$model->_primary_key; $val = $this->_object[$this->_belongs_to[$column]['foreign_key']]; // Make sure we don't run WHERE "AUTO_INCREMENT column" = NULL queries. This would // return the last inserted record instead of an empty result. // See: http://mysql.localhost.net.ar/doc/refman/5.1/en/server-session-variables.html#sysvar_sql_auto_is_null if ($val !== NULL) { $model->where($col, '=', $val)->find(); } return $this->_related[$column] = $model; } elseif (isset($this->_has_one[$column])) { $model = $this->_related($column); // Use this model's primary key value and foreign model's column $col = $model->_table_name.'.'.$this->_has_one[$column]['foreign_key']; $val = $this->pk(); $model->where($col, '=', $val)->find(); return $this->_related[$column] = $model; } elseif (isset($this->_has_many[$column])) { $model = ORM::factory($this->_has_many[$column]['model']); if (isset($this->_has_many[$column]['through'])) { // Grab has_many "through" relationship table $through = $this->_has_many[$column]['through']; // Join on through model's target foreign key (far_key) and target model's primary key $join_col1 = $through.'.'.$this->_has_many[$column]['far_key']; $join_col2 = $model->_table_name.'.'.$model->_primary_key; $model->join($through)->on($join_col1, '=', $join_col2); // Through table's source foreign key (foreign_key) should be this model's primary key $col = $through.'.'.$this->_has_many[$column]['foreign_key']; $val = $this->pk(); } else { // Simple has_many relationship, search where target model's foreign key is this model's primary key $col = $model->_table_name.'.'.$this->_has_many[$column]['foreign_key']; $val = $this->pk(); } return $model->where($col, '=', $val); } else { throw new Kohana_Exception('The :property property does not exist in the :class class', array(':property' => $column, ':class' => get_class($this))); } } /** * Base set method - this should not be overridden. * * @param string $column Column name * @param mixed $value Column value * @return void */ public function __set($column, $value) { if ( ! isset($this->_object_name)) { // Object not yet constructed, so we're loading data from a database call cast $this->_cast_data[$column] = $value; } else { // Set the model's column to given value $this->set($column, $value); } } /** * Handles setting of column * * @param string $column Column name * @param mixed $value Column value * @return void */ public function set($column, $value) { if (array_key_exists($column, $this->_object)) { // Filter the data $value = $this->run_filter($column, $value); // See if the data really changed if ($value !== $this->_object[$column]) { $this->_object[$column] = $value; // Data has changed $this->_changed[$column] = $column; // Object is no longer saved or valid $this->_saved = $this->_valid = FALSE; } } elseif (isset($this->_belongs_to[$column])) { // Update related object itself $this->_related[$column] = $value; // Update the foreign key of this model $this->_object[$this->_belongs_to[$column]['foreign_key']] = $value->pk(); $this->_changed[$column] = $this->_belongs_to[$column]['foreign_key']; } else { throw new Kohana_Exception('The :property: property does not exist in the :class: class', array(':property:' => $column, ':class:' => get_class($this))); } return $this; } /** * Set values from an array with support for one-one relationships. This method should be used * for loading in post data, etc. * * @param array $values Array of column => val * @param array $expected Array of keys to take from $values * @return ORM */ public function values(array $values, array $expected = NULL) { // Default to expecting everything except the primary key if ($expected === NULL) { $expected = array_keys($this->_table_columns); // Don't set the primary key by default unset($values[$this->_primary_key]); } foreach ($expected as $key => $column) { if (is_string($key)) { // isset() fails when the value is NULL (we want it to pass) if ( ! array_key_exists($key, $values)) continue; // Try to set values to a related model $this->{$key}->values($values[$key], $column); } else { // isset() fails when the value is NULL (we want it to pass) if ( ! array_key_exists($column, $values)) continue; // Update the column, respects __set() $this->$column = $values[$column]; } } return $this; } /** * Returns the values of this object as an array, including any related one-one * models that have already been loaded using with() * * @return array */ public function as_array() { $object = array(); foreach ($this->_object as $column => $value) { // Call __get for any user processing $object[$column] = $this->__get($column); } foreach ($this->_related as $column => $model) { // Include any related objects that are already loaded $object[$column] = $model->as_array(); } return $object; } /** * Binds another one-to-one object to this model. One-to-one objects * can be nested using 'object1:object2' syntax * * @param string $target_path Target model to bind to * @return void */ public function with($target_path) { if (isset($this->_with_applied[$target_path])) { // Don't join anything already joined return $this; } // Split object parts $aliases = explode(':', $target_path); $target = $this; foreach ($aliases as $alias) { // Go down the line of objects to find the given target $parent = $target; $target = $parent->_related($alias); if ( ! $target) { // Can't find related object return $this; } } // Target alias is at the end $target_alias = $alias; // Pop-off top alias to get the parent path (user:photo:tag becomes user:photo - the parent table prefix) array_pop($aliases); $parent_path = implode(':', $aliases); if (empty($parent_path)) { // Use this table name itself for the parent path $parent_path = $this->_table_name; } else { if ( ! isset($this->_with_applied[$parent_path])) { // If the parent path hasn't been joined yet, do it first (otherwise LEFT JOINs fail) $this->with($parent_path); } } // Add to with_applied to prevent duplicate joins $this->_with_applied[$target_path] = TRUE; // Use the keys of the empty object to determine the columns foreach (array_keys($target->_object) as $column) { $name = $target_path.'.'.$column; $alias = $target_path.':'.$column; // Add the prefix so that load_result can determine the relationship $this->select(array($name, $alias)); } if (isset($parent->_belongs_to[$target_alias])) { // Parent belongs_to target, use target's primary key and parent's foreign key $join_col1 = $target_path.'.'.$target->_primary_key; $join_col2 = $parent_path.'.'.$parent->_belongs_to[$target_alias]['foreign_key']; } else { // Parent has_one target, use parent's primary key as target's foreign key $join_col1 = $parent_path.'.'.$parent->_primary_key; $join_col2 = $target_path.'.'.$parent->_has_one[$target_alias]['foreign_key']; } // Join the related object into the result $this->join(array($target->_table_name, $target_path), 'LEFT')->on($join_col1, '=', $join_col2); return $this; } /** * Initializes the Database Builder to given query type * * @param integer $type Type of Database query * @return ORM */ protected function _build($type) { // Construct new builder object based on query type switch ($type) { case Database::SELECT: $this->_db_builder = DB::select(); break; case Database::UPDATE: $this->_db_builder = DB::update($this->_table_name); break; case Database::DELETE: $this->_db_builder = DB::delete($this->_table_name); } // Process pending database method calls foreach ($this->_db_pending as $method) { $name = $method['name']; $args = $method['args']; $this->_db_applied[$name] = $name; call_user_func_array(array($this->_db_builder, $name), $args); } return $this; } /** * Finds and loads a single database row into the object. * * @chainable * @return ORM */ public function find() { if ($this->_loaded) throw new Kohana_Exception('Method find() cannot be called on loaded objects'); if ( ! empty($this->_load_with)) { foreach ($this->_load_with as $alias) { // Bind auto relationships $this->with($alias); } } $this->_build(Database::SELECT); return $this->_load_result(FALSE); } /** * Finds multiple database rows and returns an iterator of the rows found. * * @return Database_Result */ public function find_all() { if ($this->_loaded) throw new Kohana_Exception('Method find_all() cannot be called on loaded objects'); if ( ! empty($this->_load_with)) { foreach ($this->_load_with as $alias) { // Bind auto relationships $this->with($alias); } } $this->_build(Database::SELECT); return $this->_load_result(TRUE); } /** * Returns an array of columns to include in the select query. This method * can be overridden to change the default select behavior. * * @return array Columns to select */ protected function _build_select() { $columns = array(); foreach ($this->_table_columns as $column => $_) { $columns[] = array($this->_table_name.'.'.$column, $column); } return $columns; } /** * Loads a database result, either as a new record for this model, or as * an iterator for multiple rows. * * @chainable * @param bool $multiple Return an iterator or load a single row * @return ORM|Database_Result */ protected function _load_result($multiple = FALSE) { $this->_db_builder->from($this->_table_name); if ($multiple === FALSE) { // Only fetch 1 record $this->_db_builder->limit(1); } // Select all columns by default $this->_db_builder->select_array($this->_build_select()); if ( ! isset($this->_db_applied['order_by']) AND ! empty($this->_sorting)) { foreach ($this->_sorting as $column => $direction) { if (strpos($column, '.') === FALSE) { // Sorting column for use in JOINs $column = $this->_table_name.'.'.$column; } $this->_db_builder->order_by($column, $direction); } } if ($multiple === TRUE) { // Return database iterator casting to this object type $result = $this->_db_builder->as_object(get_class($this))->execute($this->_db); $this->reset(); return $result; } else { // Load the result as an associative array $result = $this->_db_builder->as_assoc()->execute($this->_db); $this->reset(); if ($result->count() === 1) { // Load object values $this->_load_values($result->current()); } else { // Clear the object, nothing was found $this->clear(); } return $this; } } /** * Loads an array of values into into the current object. * * @chainable * @param array $values Values to load * @return ORM */ protected function _load_values(array $values) { if (array_key_exists($this->_primary_key, $values)) { if ($values[$this->_primary_key] !== NULL) { // Flag as loaded, saved, and valid $this->_loaded = $this->_saved = $this->_valid = TRUE; // Store primary key $this->_primary_key_value = $values[$this->_primary_key]; } else { // Not loaded, saved, or valid $this->_loaded = $this->_saved = $this->_valid = FALSE; } } // Related objects $related = array(); foreach ($values as $column => $value) { if (strpos($column, ':') === FALSE) { // Load the value to this model $this->_object[$column] = $value; } else { // Column belongs to a related model list ($prefix, $column) = explode(':', $column, 2); $related[$prefix][$column] = $value; } } if ( ! empty($related)) { foreach ($related as $object => $values) { // Load the related objects with the values in the result $this->_related($object)->_load_values($values); } } return $this; } /** * Rule definitions for validation * * @return array */ public function rules() { return array(); } /** * Filters a value for a specific column * * @param string $field The column name * @param string $value The value to filter * @return string */ protected function run_filter($field, $value) { $filters = $this->filters(); // Get the filters for this column $wildcards = empty($filters[TRUE]) ? array() : $filters[TRUE]; // Merge in the wildcards $filters = empty($filters[$field]) ? $wildcards : array_merge($wildcards, $filters[$field]); // Bind the field name and model so they can be used in the filter method $_bound = array ( ':field' => $field, ':model' => $this, ); foreach ($filters as $array) { // Value needs to be bound inside the loop so we are always using the // version that was modified by the filters that already ran $_bound[':value'] = $value; // Filters are defined as array($filter, $params) $filter = $array[0]; $params = Arr::get($array, 1, array(':value')); foreach ($params as $key => $param) { if (is_string($param) AND array_key_exists($param, $_bound)) { // Replace with bound value $params[$key] = $_bound[$param]; } } if (is_array($filter) OR ! is_string($filter)) { // This is either a callback as an array or a lambda $value = call_user_func_array($filter, $params); } elseif (strpos($filter, '::') === FALSE) { // Use a function call $function = new ReflectionFunction($filter); // Call $function($this[$field], $param, ...) with Reflection $value = $function->invokeArgs($params); } else { // Split the class and method of the rule list($class, $method) = explode('::', $filter, 2); // Use a static method call $method = new ReflectionMethod($class, $method); // Call $Class::$method($this[$field], $param, ...) with Reflection $value = $method->invokeArgs(NULL, $params); } } return $value; } /** * Filter definitions for validation * * @return array */ public function filters() { return array(); } /** * Label definitions for validation * * @return array */ public function labels() { return array(); } /** * Validates the current model's data * * @param Validation $extra_validation Validation object * @return ORM */ public function check(Validation $extra_validation = NULL) { // Determine if any external validation failed $extra_errors = ($extra_validation AND ! $extra_validation->check()); // Always build a new validation object $this->_validation(); $array = $this->_validation; if (($this->_valid = $array->check()) === FALSE OR $extra_errors) { $exception = new ORM_Validation_Exception($this->_object_name, $array); if ($extra_errors) { // Merge any possible errors from the external object $exception->add_object('_external', $extra_validation); } throw $exception; } return $this; } /** * Insert a new object to the database * @param Validation $validation Validation object * @return ORM */ public function create(Validation $validation = NULL) { if ($this->_loaded) throw new Kohana_Exception('Cannot create :model model because it is already loaded.', array(':model' => $this->_object_name)); // Require model validation before saving if ( ! $this->_valid OR $validation) { $this->check($validation); } $data = array(); foreach ($this->_changed as $column) { // Generate list of column => values $data[$column] = $this->_object[$column]; } if (is_array($this->_created_column)) { // Fill the created column $column = $this->_created_column['column']; $format = $this->_created_column['format']; $data[$column] = $this->_object[$column] = ($format === TRUE) ? time() : date($format); } $result = DB::insert($this->_table_name) ->columns(array_keys($data)) ->values(array_values($data)) ->execute($this->_db); if ( ! array_key_exists($this->_primary_key, $data)) { // Load the insert id as the primary key if it was left out $this->_object[$this->_primary_key] = $this->_primary_key_value = $result[0]; } if (empty($this->_primary_key_value)) { // Set the primary key value if it was manually chosen by the user $this->_primary_key_value = $this->_object[$this->_primary_key]; } // Object is now loaded and saved $this->_loaded = $this->_saved = TRUE; // All changes have been saved $this->_changed = array(); return $this; } /** * Updates a single record or multiple records * * @chainable * @param Validation $validation Validation object * @return ORM */ public function update(Validation $validation = NULL) { if ( ! $this->_loaded) throw new Kohana_Exception('Cannot update :model model because it is not loaded.', array(':model' => $this->_object_name)); // Run validation if the model isn't valid or we have additional validation rules. if ( ! $this->_valid OR $validation) { $this->check($validation); } if (empty($this->_changed)) { // Nothing to update return $this; } $data = array(); foreach ($this->_changed as $column) { // Compile changed data $data[$column] = $this->_object[$column]; } if (is_array($this->_updated_column)) { // Fill the updated column $column = $this->_updated_column['column']; $format = $this->_updated_column['format']; $data[$column] = $this->_object[$column] = ($format === TRUE) ? time() : date($format); } // Use primary key value $id = $this->pk(); // Update a single record DB::update($this->_table_name) ->set($data) ->where($this->_primary_key, '=', $id) ->execute($this->_db); if (isset($data[$this->_primary_key])) { // Primary key was changed, reflect it $this->_primary_key_value = $data[$this->_primary_key]; } // Object has been saved $this->_saved = TRUE; // All changes have been saved $this->_changed = array(); return $this; } /** * Updates or Creates the record depending on loaded() * * @chainable * @param Validation $validation Validation object * @return ORM */ public function save(Validation $validation = NULL) { return $this->loaded() ? $this->update($validation) : $this->create($validation); } /** * Deletes a single record while ignoring relationships. * * @chainable * @return ORM */ public function delete() { if ( ! $this->_loaded) throw new Kohana_Exception('Cannot delete :model model because it is not loaded.', array(':model' => $this->_object_name)); // Use primary key value $id = $this->pk(); // Delete the object DB::delete($this->_table_name) ->where($this->_primary_key, '=', $id) ->execute($this->_db); return $this->clear(); } /** * Tests if this object has a relationship to a different model, * or an array of different models. * * // Check if $model has the login role * $model->has('roles', ORM::factory('role', array('name' => 'login'))); * // Check for the login role if you know the roles.id is 5 * $model->has('roles', 5); * // Check for all of the following roles * $model->has('roles', array(1, 2, 3, 4)); * @param string $alias Alias of the has_many "through" relationship * @param mixed $far_keys Related model, primary key, or an array of primary keys * @return Database_Result */ public function has($alias, $far_keys) { $far_keys = ($far_keys instanceof ORM) ? $far_keys->pk() : $far_keys; // We need an array to simplify the logic $far_keys = (array) $far_keys; // Nothing to check if the model isn't loaded or we don't have any far_keys if ( ! $far_keys OR ! $this->_loaded) return FALSE; $count = (int) DB::select(array('COUNT("*")', 'records_found')) ->from($this->_has_many[$alias]['through']) ->where($this->_has_many[$alias]['foreign_key'], '=', $this->pk()) ->where($this->_has_many[$alias]['far_key'], 'IN', $far_keys) ->execute($this->_db)->get('records_found'); // Rows found need to match the rows searched return $count === count($far_keys); } /** * Adds a new relationship to between this model and another. * * // Add the login role using a model instance * $model->add('roles', ORM::factory('role', array('name' => 'login'))); * // Add the login role if you know the roles.id is 5 * $model->add('roles', 5); * // Add multiple roles (for example, from checkboxes on a form) * $model->add('roles', array(1, 2, 3, 4)); * * @param string $alias Alias of the has_many "through" relationship * @param mixed $far_keys Related model, primary key, or an array of primary keys * @return ORM */ public function add($alias, $far_keys) { $far_keys = ($far_keys instanceof ORM) ? $far_keys->pk() : $far_keys; $columns = array($this->_has_many[$alias]['foreign_key'], $this->_has_many[$alias]['far_key']); $foreign_key = $this->pk(); $query = DB::insert($this->_has_many[$alias]['through'], $columns); foreach ( (array) $far_keys as $key) { $query->values(array($foreign_key, $key)); } $query->execute($this->_db); return $this; } /** * Removes a relationship between this model and another. * * // Remove a role using a model instance * $model->remove('roles', ORM::factory('role', array('name' => 'login'))); * // Remove the role knowing the primary key * $model->remove('roles', 5); * // Remove multiple roles (for example, from checkboxes on a form) * $model->remove('roles', array(1, 2, 3, 4)); * // Remove all related roles * $model->remove('roles'); * * @param string $alias Alias of the has_many "through" relationship * @param mixed $far_keys Related model, primary key, or an array of primary keys * @return ORM */ public function remove($alias, $far_keys = NULL) { $far_keys = ($far_keys instanceof ORM) ? $far_keys->pk() : $far_keys; $query = DB::delete($this->_has_many[$alias]['through']) ->where($this->_has_many[$alias]['foreign_key'], '=', $this->pk()); if ($far_keys !== NULL) { // Remove all the relationships in the array $query->where($this->_has_many[$alias]['far_key'], 'IN', (array) $far_keys); } $query->execute($this->_db); return $this; } /** * Count the number of records in the table. * * @return integer */ public function count_all() { $selects = array(); foreach ($this->_db_pending as $key => $method) { if ($method['name'] == 'select') { // Ignore any selected columns for now $selects[] = $method; unset($this->_db_pending[$key]); } } if ( ! empty($this->_load_with)) { foreach ($this->_load_with as $alias) { // Bind relationship $this->with($alias); } } $this->_build(Database::SELECT); $records = $this->_db_builder->from($this->_table_name) ->select(array('COUNT("*")', 'records_found')) ->execute($this->_db) ->get('records_found'); // Add back in selected columns $this->_db_pending += $selects; $this->reset(); // Return the total number of records in a table return $records; } /** * Proxy method to Database list_columns. * * @return array */ public function list_columns() { // Proxy to database return $this->_db->list_columns($this->_table_name); } /** * Returns an ORM model for the given one-one related alias * * @param string $alias Alias name * @return ORM */ protected function _related($alias) { if (isset($this->_related[$alias])) { return $this->_related[$alias]; } elseif (isset($this->_has_one[$alias])) { return $this->_related[$alias] = ORM::factory($this->_has_one[$alias]['model']); } elseif (isset($this->_belongs_to[$alias])) { return $this->_related[$alias] = ORM::factory($this->_belongs_to[$alias]['model']); } else { return FALSE; } } /** * Returns the value of the primary key * * @return mixed Primary key */ public function pk() { return $this->_primary_key_value; } /** * Returns last executed query * * @return string */ public function last_query() { return $this->_db->last_query; } /** * Clears query builder. Passing FALSE is useful to keep the existing * query conditions for another query. * * @param bool $next Pass FALSE to avoid resetting on the next call * @return ORM */ public function reset($next = TRUE) { if ($next AND $this->_db_reset) { $this->_db_pending = array(); $this->_db_applied = array(); $this->_db_builder = NULL; $this->_with_applied = array(); } // Reset on the next call? $this->_db_reset = $next; return $this; } } // End ORMlibkohana3.1-php-3.1.5/modules/orm/classes/model/0000755000000000000000000000000011771133710020160 5ustar rootrootlibkohana3.1-php-3.1.5/modules/orm/classes/model/auth/0000755000000000000000000000000011771133710021121 5ustar rootrootlibkohana3.1-php-3.1.5/modules/orm/classes/model/auth/role.php0000644000000000000000000000123411772106746022605 0ustar rootroot array('through' => 'roles_users')); public function rules() { return array( 'name' => array( array('not_empty'), array('min_length', array(':value', 4)), array('max_length', array(':value', 32)), ), 'description' => array( array('max_length', array(':value', 255)), ) ); } } // End Auth Role Modellibkohana3.1-php-3.1.5/modules/orm/classes/model/auth/user/0000755000000000000000000000000011771133710022077 5ustar rootrootlibkohana3.1-php-3.1.5/modules/orm/classes/model/auth/user/token.php0000644000000000000000000000264311772106746023747 0ustar rootroot array()); protected $_created_column = array( 'column' => 'created', 'format' => TRUE, ); /** * Handles garbage collection and deleting of expired objects. * * @return void */ public function __construct($id = NULL) { parent::__construct($id); if (mt_rand(1, 100) === 1) { // Do garbage collection $this->delete_expired(); } if ($this->expires < time() AND $this->_loaded) { // This object has expired $this->delete(); } } /** * Deletes all expired tokens. * * @return ORM */ public function delete_expired() { // Delete all expired tokens DB::delete($this->_table_name) ->where('expires', '<', time()) ->execute($this->_db); return $this; } public function create(Validation $validation = NULL) { $this->token = $this->create_token(); return parent::create($validation); } protected function create_token() { do { $token = sha1(uniqid(Text::random('alnum', 32), TRUE)); } while(ORM::factory('user_token', array('token' => $token))->loaded()); return $token; } } // End Auth User Token Modellibkohana3.1-php-3.1.5/modules/orm/classes/model/auth/user.php0000644000000000000000000001334011772106746022623 0ustar rootroot array('model' => 'user_token'), 'roles' => array('model' => 'role', 'through' => 'roles_users'), ); /** * Rules for the user model. Because the password is _always_ a hash * when it's set,you need to run an additional not_empty rule in your controller * to make sure you didn't hash an empty string. The password rules * should be enforced outside the model or with a model helper method. * * @return array Rules */ public function rules() { return array( 'username' => array( array('not_empty'), array('min_length', array(':value', 4)), array('max_length', array(':value', 32)), array('regex', array(':value', '/^[-\pL\pN_.]++$/uD')), array(array($this, 'username_available'), array(':validation', ':field')), ), 'password' => array( array('not_empty'), ), 'email' => array( array('not_empty'), array('min_length', array(':value', 4)), array('max_length', array(':value', 127)), array('email'), array(array($this, 'email_available'), array(':validation', ':field')), ), ); } /** * Filters to run when data is set in this model. The password filter * automatically hashes the password when it's set in the model. * * @return array Filters */ public function filters() { return array( 'password' => array( array(array(Auth::instance(), 'hash')) ) ); } /** * Labels for fields in this model * * @return array Labels */ public function labels() { return array( 'username' => 'username', 'email' => 'email address', 'password' => 'password', ); } /** * Complete the login for a user by incrementing the logins and saving login timestamp * * @return void */ public function complete_login() { if ($this->_loaded) { // Update the number of logins $this->logins = new Database_Expression('logins + 1'); // Set the last login date $this->last_login = time(); // Save the user $this->update(); } } /** * Does the reverse of unique_key_exists() by triggering error if username exists. * Validation callback. * * @param Validation Validation object * @param string Field name * @return void */ public function username_available(Validation $validation, $field) { if ($this->unique_key_exists($validation[$field], 'username')) { $validation->error($field, 'username_available', array($validation[$field])); } } /** * Does the reverse of unique_key_exists() by triggering error if email exists. * Validation callback. * * @param Validation Validation object * @param string Field name * @return void */ public function email_available(Validation $validation, $field) { if ($this->unique_key_exists($validation[$field], 'email')) { $validation->error($field, 'email_available', array($validation[$field])); } } /** * Tests if a unique key value exists in the database. * * @param mixed the value to test * @param string field name * @return boolean */ public function unique_key_exists($value, $field = NULL) { if ($field === NULL) { // Automatically determine field by looking at the value $field = $this->unique_key($value); } return (bool) DB::select(array('COUNT("*")', 'total_count')) ->from($this->_table_name) ->where($field, '=', $value) ->where($this->_primary_key, '!=', $this->pk()) ->execute($this->_db) ->get('total_count'); } /** * Allows a model use both email and username as unique identifiers for login * * @param string unique value * @return string field name */ public function unique_key($value) { return Valid::email($value) ? 'email' : 'username'; } /** * Password validation for plain passwords. * * @param array $values * @return Validation */ public static function get_password_validation($values) { return Validation::factory($values) ->rule('password', 'min_length', array(':value', 8)) ->rule('password_confirm', 'matches', array(':validation', ':field', 'password')); } /** * Create a new user * * Example usage: * ~~~ * $user = ORM::factory('user')->create_user($_POST, array( * 'username', * 'password', * 'email', * ); * ~~~ * * @param array $values * @param array $expected * @throws ORM_Validation_Exception */ public function create_user($values, $expected) { // Validation for passwords $extra_validation = Model_User::get_password_validation($values) ->rule('password', 'not_empty'); return $this->values($values, $expected)->create($extra_validation); } /** * Update an existing user * * [!!] We make the assumption that if a user does not supply a password, that they do not wish to update their password. * * Example usage: * ~~~ * $user = ORM::factory('user') * ->where('username', '=', 'kiall') * ->find() * ->update_user($_POST, array( * 'username', * 'password', * 'email', * ); * ~~~ * * @param array $values * @param array $expected * @throws ORM_Validation_Exception */ public function update_user($values, $expected = NULL) { if (empty($values['password'])) { unset($values['password'], $values['password_confirm']); } // Validation for passwords $extra_validation = Model_User::get_password_validation($values); return $this->values($values, $expected)->update($extra_validation); } } // End Auth User Modellibkohana3.1-php-3.1.5/modules/orm/classes/model/role.php0000644000000000000000000000025211772106746021643 0ustar rootroot array( // This should be the path to this modules userguide pages, without the 'guide/'. Ex: '/guide/modulename/' would be 'modulename' 'orm' => array( // Whether this modules userguide pages should be shown 'enabled' => TRUE, // The name that should show up on the userguide index page 'name' => 'ORM', // A short description of this module, shown on the index page 'description' => 'Official ORM module, a modeling library for object relational mapping.', // Copyright message, shown in the footer for this module 'copyright' => '© 2008–2012 Kohana Team', ) ) ); libkohana3.1-php-3.1.5/modules/orm/guide/0000755000000000000000000000000011771133710016520 5ustar rootrootlibkohana3.1-php-3.1.5/modules/orm/guide/orm/0000755000000000000000000000000011771133710017315 5ustar rootrootlibkohana3.1-php-3.1.5/modules/orm/guide/orm/examples/0000755000000000000000000000000011771133710021133 5ustar rootrootlibkohana3.1-php-3.1.5/modules/orm/guide/orm/examples/simple.md0000644000000000000000000000622511772106746022765 0ustar rootroot# Simple Examples This is a simple example of a single ORM model, that has no relationships, but uses validation on the fields. ## SQL schema CREATE TABLE IF NOT EXISTS `members` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `username` varchar(32) NOT NULL, `first_name` varchar(32) NOT NULL, `last_name` varchar(32) NOT NULL, `email` varchar(127) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1; ## Model array( array('not_empty'), array('min_length', array(':value', 4)), array('max_length', array(':value', 32)), array('regex', array(':value', '/^[-\pL\pN_.]++$/uD')), ), 'first_name' => array( array('not_empty'), array('min_length', array(':value', 4)), array('max_length', array(':value', 32)), array('regex', array(':value', '/^[-\pL\pN_.]++$/uD')), ), 'last_name' => array( array('not_empty'), array('min_length', array(':value', 4)), array('max_length', array(':value', 32)), array('regex', array(':value', '/^[-\pL\pN_.]++$/uD')), ), 'email' => array( array('not_empty'), array('min_length', array(':value', 4)), array('max_length', array(':value', 127)), array('email'), ), ); } } [!!] The array returned by `ORM::rules()` will be passed to a [Validation] object and tested when you call `ORM::save()`. [!!] Please notice that defining the primary key "id" in the model is not necessary. Also the table name in the database is plural and the model name is singular. ## Controller where('first_name', '=', 'Peter')->find_all(); // Count records in the $members object $members->count_all(); /** * Example 2 */ // Create an instance of a model $member = ORM::factory('member'); // Get a member with the user name "bongo" find() means // we only want the first record matching the query. $member->where('username', '=', 'bongo')->find(); /** * Example 3 */ // Create an instance of a model $member = ORM::factory('member'); // Do an INSERT query $member->username = 'bongo'; $member->first_name = 'Peter'; $member->last_name = 'Smith'; $member->save(); /** * Example 4 */ // Create an instance of a model where the // table field "id" is "1" $member = ORM::factory('member', 1); // Do an UPDATE query $member->username = 'bongo'; $member->first_name = 'Peter'; $member->last_name = 'Smith'; $member->save(); } } [!!] $member will be a PHP object where you can access the values from the query e.g. echo $member->first_name libkohana3.1-php-3.1.5/modules/orm/guide/orm/examples/validation.md0000644000000000000000000001107311772106746023623 0ustar rootroot# Validation Example This example will create user accounts and demonstrate how to handle model and controller validation. We will create a form, process it, and display any errors to the user. We will be assuming that the Model_User class contains a method called `hash_password` that is used to turn the plaintext passwords into some kind of hash. The implementation of the hashing methods are beyond the scope of this example and should be provided with the Authentication library you decide to use. ## SQL schema CREATE TABLE IF NOT EXISTS `members` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `username` varchar(32) NOT NULL, `password` varchar(100) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1; ## Model array( array('not_empty'), array('min_length', array(':value', 4)), array('max_length', array(':value', 32)), array(array($this, 'username_available')), ), 'password' => array( array('not_empty'), ), ); } public function filters() { return array( 'password' => array( array(array($this, 'hash_password')), ), ); } public function username_available($username) { // There are simpler ways to do this, but I will use ORM for the sake of the example return ORM::factory('member', array('username' => $username))->loaded(); } public function hash_password($password) { // Do something to hash the password } } ## HTML Form Please forgive my slightly ugly form. I am trying not to use any modules or unrelated magic. :)