diff --git a/application/bootstrap.php b/application/bootstrap.php
index d12c416c..e49c28d1 100644
--- a/application/bootstrap.php
+++ b/application/bootstrap.php
@@ -1,6 +1,20 @@
"
+ */
+if (isset($_SERVER['KOHANA_ENV']))
+{
+ Kohana::$environment = constant('Kohana::'.strtoupper($_SERVER['KOHANA_ENV']));
+}
/**
* Initialize Kohana, setting the default options.
@@ -57,12 +87,12 @@ Kohana::init(array(
/**
* Attach the file write to logging. Multiple writers are supported.
*/
-Kohana::$log->attach(new Kohana_Log_File(APPPATH.'logs'));
+Kohana::$log->attach(new Log_File(APPPATH.'logs'));
/**
* Attach a file reader to config. Multiple readers are supported.
*/
-Kohana::$config->attach(new Kohana_Config_File);
+Kohana::$config->attach(new Config_File);
/**
* Enable modules. Modules are referenced by a relative or absolute path.
@@ -74,13 +104,11 @@ Kohana::modules(array(
'database' => SMDPATH.'database', // Database access
// 'image' => SMDPATH.'image', // Image manipulation
'orm' => SMDPATH.'orm', // Object Relationship Mapping
- // 'oauth' => SMDPATH.'oauth', // OAuth authentication
- // 'pagination' => SMDPATH.'pagination', // Paging of results
// 'unittest' => SMDPATH.'unittest', // Unit testing
- // 'userguide' => SMDPATH.'userguide', // User guide and API documentation
- 'xml' => SMDPATH.'xml', // XML module for Kohana 3 PHP Framework
- 'email' => SMDPATH.'email', // Email module for Kohana 3 PHP Framework
- 'gchart' => MODPATH.'gchart', // Google Chart Module
+ 'userguide' => SMDPATH.'userguide', // User guide and API documentation
+ 'email' => SMDPATH.'email', // Email module for Kohana 3 PHP Framework
+ 'gchart' => MODPATH.'gchart', // Google Chart Module
+ 'xml' => SMDPATH.'xml', // XML module for Kohana 3 PHP Framework
));
/**
@@ -113,21 +141,3 @@ Route::set('default/media', 'media(/
PECL HTTP Enabled | + +Pass | + +Kohana can use the http extension for the Request_Client_External class. | + +|
---|---|---|---|
cURL Enabled | Pass | -Kohana requires cURL for the Remote class. | +Kohana can use the cURL extension for the Request_Client_External class. |
Kohana requires GD v2 for the Image class. | |||
MySQL Enabled | + +Pass | + +Kohana can use the MySQL extension to support MySQL databases. | + +|
PDO Enabled | diff --git a/includes/kohana/modules/auth/README.md b/includes/kohana/modules/auth/README.md new file mode 100644 index 00000000..27ac9cd7 --- /dev/null +++ b/includes/kohana/modules/auth/README.md @@ -0,0 +1,13 @@ +New 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. \ No newline at end of file diff --git a/includes/kohana/modules/auth/classes/kohana/auth.php b/includes/kohana/modules/auth/classes/kohana/auth.php index f9989c7d..c5030e8a 100644 --- a/includes/kohana/modules/auth/classes/kohana/auth.php +++ b/includes/kohana/modules/auth/classes/kohana/auth.php @@ -5,8 +5,8 @@ * * @package Kohana/Auth * @author Kohana Team - * @copyright (c) 2007-2009 Kohana Team - * @license http://kohanaphp.com/license.html + * @copyright (c) 2007-2010 Kohana Team + * @license http://kohanaframework.org/license */ abstract class Kohana_Auth { @@ -27,7 +27,7 @@ abstract class Kohana_Auth { if ( ! $type = $config->get('driver')) { - $type = 'ORM'; + $type = 'file'; } // Set the session class name @@ -40,16 +40,6 @@ abstract class Kohana_Auth { return Auth::$_instance; } - /** - * Create an instance of Auth. - * - * @return Auth - */ - public static function factory($config = array()) - { - return new Auth($config); - } - protected $_session; protected $_config; @@ -61,9 +51,6 @@ abstract class Kohana_Auth { */ public function __construct($config = array()) { - // Clean up the salt pattern and split it into an array - $config['salt_pattern'] = preg_split('/,\s*/', Kohana::config('auth')->get('salt_pattern')); - // Save the config in the object $this->_config = $config; @@ -78,13 +65,13 @@ abstract class Kohana_Auth { /** * Gets the currently logged in user from the session. - * Returns FALSE if no user is currently logged in. + * Returns NULL if no user is currently logged in. * * @return mixed */ - public function get_user() + public function get_user($default = NULL) { - return $this->_session->get($this->_config['session_key'], FALSE); + return $this->_session->get($this->_config['session_key'], $default); } /** @@ -102,11 +89,8 @@ abstract class Kohana_Auth { if (is_string($password)) { - // Get the salt from the stored password - $salt = $this->find_salt($this->password($username)); - - // Create a hashed password using the salt from the stored password - $password = $this->hash_password($password, $salt); + // Create a hashed password + $password = $this->hash($password); } return $this->_login($username, $password, $remember); @@ -141,90 +125,40 @@ abstract class Kohana_Auth { /** * Check if there is an active session. Optionally allows checking for a - * specific role. + * specific role. * * @param string role name * @return mixed */ public function logged_in($role = NULL) { - return FALSE !== $this->get_user(); + return ($this->get_user() !== NULL); } /** - * Creates a hashed password from a plaintext password, inserting salt - * based on the configured salt pattern. + * Creates a hashed hmac password from a plaintext password. This + * method is deprecated, [Auth::hash] should be used instead. * + * @deprecated * @param string plaintext password - * @return string hashed password string */ - public function hash_password($password, $salt = FALSE) + public function hash_password($password) { - if ($salt === FALSE) - { - // Create a salt seed, same length as the number of offsets in the pattern - $salt = substr($this->hash(uniqid(NULL, TRUE)), 0, count($this->_config['salt_pattern'])); - } - - // Password hash that the salt will be inserted into - $hash = $this->hash($salt.$password); - - // Change salt to an array - $salt = str_split($salt, 1); - - // Returned password - $password = ''; - - // Used to calculate the length of splits - $last_offset = 0; - - foreach ($this->_config['salt_pattern'] as $offset) - { - // Split a new part of the hash off - $part = substr($hash, 0, $offset - $last_offset); - - // Cut the current part out of the hash - $hash = substr($hash, $offset - $last_offset); - - // Add the part to the password, appending the salt character - $password .= $part.array_shift($salt); - - // Set the last offset to the current offset - $last_offset = $offset; - } - - // Return the password, with the remaining hash appended - return $password.$hash; + return $this->hash($password); } /** - * Perform a hash, using the configured method. + * Perform a hmac hash, using the configured method. * * @param string string to hash * @return string */ public function hash($str) { - return hash($this->_config['hash_method'], $str); - } + if ( ! $this->_config['hash_key']) + throw new Kohana_Exception('A valid hash key must be set in your auth config.'); - /** - * Finds the salt from a password, based on the configured salt pattern. - * - * @param string hashed password - * @return string - */ - public function find_salt($password) - { - $salt = ''; - - foreach ($this->_config['salt_pattern'] as $i => $offset) - { - // Find salt characters, take a good long look... - $salt .= substr($password, $offset + $i, 1); - } - - return $salt; + return hash_hmac($this->_config['hash_method'], $str, $this->_config['hash_key']); } protected function complete_login($user) @@ -238,4 +172,4 @@ abstract class Kohana_Auth { return TRUE; } -} // End Auth \ No newline at end of file +} // End Auth diff --git a/includes/kohana/modules/auth/classes/kohana/auth/file.php b/includes/kohana/modules/auth/classes/kohana/auth/file.php index 439f17fa..31ca0c3b 100644 --- a/includes/kohana/modules/auth/classes/kohana/auth/file.php +++ b/includes/kohana/modules/auth/classes/kohana/auth/file.php @@ -5,8 +5,8 @@ * * @package Kohana/Auth * @author Kohana Team - * @copyright (c) 2007-2008 Kohana Team - * @license http://kohanaphp.com/license.html + * @copyright (c) 2007-2010 Kohana Team + * @license http://kohanaframework.org/license */ class Kohana_Auth_File extends Auth { diff --git a/includes/kohana/modules/auth/classes/model/auth/user.php b/includes/kohana/modules/auth/classes/model/auth/user.php deleted file mode 100644 index 179391ba..00000000 --- a/includes/kohana/modules/auth/classes/model/auth/user.php +++ /dev/null @@ -1,244 +0,0 @@ - array('model' => 'user_token'), - 'roles' => array('model' => 'role', 'through' => 'roles_users'), - ); - - // Validation rules - protected $_rules = array( - 'username' => array( - 'not_empty' => NULL, - 'min_length' => array(4), - 'max_length' => array(32), - 'regex' => array('/^[-\pL\pN_.]++$/uD'), - ), - 'password' => array( - 'not_empty' => NULL, - 'min_length' => array(5), - 'max_length' => array(42), - ), - 'password_confirm' => array( - 'matches' => array('password'), - ), - 'email' => array( - 'not_empty' => NULL, - 'min_length' => array(4), - 'max_length' => array(127), - 'email' => NULL, - ), - ); - - // Validation callbacks - protected $_callbacks = array( - 'username' => array('username_available'), - 'email' => array('email_available'), - ); - - // Field labels - protected $_labels = array( - 'username' => 'username', - 'email' => 'email address', - 'password' => 'password', - 'password_confirm' => 'password confirmation', - ); - - // Columns to ignore - protected $_ignored_columns = array('password_confirm'); - - /** - * Validates login information from an array, and optionally redirects - * after a successful login. - * - * @param array values to check - * @param string URI or URL to redirect to - * @return boolean - */ - public function login(array & $array, $redirect = FALSE) - { - $fieldname = $this->unique_key($array['username']); - $array = Validate::factory($array) - ->label('username', $this->_labels[$fieldname]) - ->label('password', $this->_labels['password']) - ->filter(TRUE, 'trim') - ->rules('username', $this->_rules[$fieldname]) - ->rules('password', $this->_rules['password']); - - // Get the remember login option - $remember = isset($array['remember']); - - // Login starts out invalid - $status = FALSE; - - if ($array->check()) - { - // Attempt to load the user - $this->where($fieldname, '=', $array['username'])->find(); - - if ($this->loaded() AND Auth::instance()->login($this, $array['password'], $remember)) - { - if (is_string($redirect)) - { - // Redirect after a successful login - Request::instance()->redirect($redirect); - } - - // Login is successful - $status = TRUE; - } - else - { - $array->error('username', 'invalid'); - } - } - - return $status; - } - - /** - * Validates an array for a matching password and password_confirm field, - * and optionally redirects after a successful save. - * - * @param array values to check - * @param string URI or URL to redirect to - * @return boolean - */ - public function change_password(array & $array, $redirect = FALSE) - { - $array = Validate::factory($array) - ->label('password', $this->_labels['password']) - ->label('password_confirm', $this->_labels['password_confirm']) - ->filter(TRUE, 'trim') - ->rules('password', $this->_rules['password']) - ->rules('password_confirm', $this->_rules['password_confirm']); - - if ($status = $array->check()) - { - // Change the password - $this->password = $array['password']; - - if ($status = $this->save() AND is_string($redirect)) - { - // Redirect to the success page - Request::instance()->redirect($redirect); - } - } - - return $status; - } - - /** - * Complete the login for a user by incrementing the logins and saving login timestamp - * - * @return void - */ - public function complete_login() - { - if ( ! $this->_loaded) - { - // nothing to do - return; - } - - // 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->save(); - } - - /** - * Does the reverse of unique_key_exists() by triggering error if username exists. - * Validation callback. - * - * @param Validate Validate object - * @param string field name - * @return void - */ - public function username_available(Validate $array, $field) - { - if ($this->unique_key_exists($array[$field], 'username')) - { - $array->error($field, 'username_available', array($array[$field])); - } - } - - /** - * Does the reverse of unique_key_exists() by triggering error if email exists. - * Validation callback. - * - * @param Validate Validate object - * @param string field name - * @return void - */ - public function email_available(Validate $array, $field) - { - if ($this->unique_key_exists($array[$field], 'email')) - { - $array->error($field, 'email_available', array($array[$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 Validate::email($value) ? 'email' : 'username'; - } - - /** - * Saves the current object. Will hash password if it was changed. - * - * @return ORM - */ - public function save() - { - if (array_key_exists('password', $this->_changed)) - { - $this->_object['password'] = Auth::instance()->hash_password($this->_object['password']); - } - - return parent::save(); - } - -} // End Auth User Model \ No newline at end of file diff --git a/includes/kohana/modules/auth/classes/model/auth/user/token.php b/includes/kohana/modules/auth/classes/model/auth/user/token.php deleted file mode 100644 index e7d820c0..00000000 --- a/includes/kohana/modules/auth/classes/model/auth/user/token.php +++ /dev/null @@ -1,101 +0,0 @@ - array()); - - // Current timestamp - protected $_now; - - /** - * Handles garbage collection and deleting of expired objects. - * - * @return void - */ - public function __construct($id = NULL) - { - parent::__construct($id); - - // Set the now, we use this a lot - $this->_now = time(); - - if (mt_rand(1, 100) === 1) - { - // Do garbage collection - $this->delete_expired(); - } - - if ($this->expires < $this->_now) - { - // This object has expired - $this->delete(); - } - } - - /** - * Overload saving to set the created time and to create a new token - * when the object is saved. - * - * @return ORM - */ - public function save() - { - if ($this->loaded() === FALSE) - { - // Set the created time, token, and hash of the user agent - $this->created = $this->_now; - $this->user_agent = sha1(Request::$user_agent); - } - - while (TRUE) - { - // Generate a new token - $this->token = $this->create_token(); - - try - { - return parent::save(); - } - catch (Kohana_Database_Exception $e) - { - // Collision occurred, token is not unique - } - } - } - - /** - * Deletes all expired tokens. - * - * @return ORM - */ - public function delete_expired() - { - // Delete all expired tokens - DB::delete($this->_table_name) - ->where('expires', '<', $this->_now) - ->execute($this->_db); - - return $this; - } - - /** - * Generate a new unique token. - * - * @return string - * @uses Text::random - */ - protected function create_token() - { - // Create a random token - return Text::random('alnum', 32); - } - -} // End Auth User Token Model \ No newline at end of file diff --git a/includes/kohana/modules/auth/config/auth.php b/includes/kohana/modules/auth/config/auth.php index 3d18e7b7..0d802087 100644 --- a/includes/kohana/modules/auth/config/auth.php +++ b/includes/kohana/modules/auth/config/auth.php @@ -2,9 +2,9 @@ return array( - 'driver' => 'ORM', - 'hash_method' => 'sha1', - 'salt_pattern' => '1, 3, 5, 9, 14, 15, 20, 21, 28, 30', + 'driver' => 'file', + 'hash_method' => 'sha256', + 'hash_key' => NULL, 'lifetime' => 1209600, 'session_key' => 'auth_user', @@ -13,4 +13,4 @@ return array( // 'admin' => 'b3154acf3a344170077d11bdb5fff31532f679a1919e716a02', ), -); \ No newline at end of file +); diff --git a/includes/kohana/modules/auth/guide/auth/config.md b/includes/kohana/modules/auth/guide/auth/config.md new file mode 100644 index 00000000..e69de29b diff --git a/includes/kohana/modules/auth/guide/auth/edit.md b/includes/kohana/modules/auth/guide/auth/edit.md new file mode 100644 index 00000000..e69de29b diff --git a/includes/kohana/modules/auth/guide/auth/index.md b/includes/kohana/modules/auth/guide/auth/index.md new file mode 100644 index 00000000..e69de29b diff --git a/includes/kohana/modules/auth/guide/auth/login.md b/includes/kohana/modules/auth/guide/auth/login.md new file mode 100644 index 00000000..e69de29b diff --git a/includes/kohana/modules/auth/guide/auth/menu.md b/includes/kohana/modules/auth/guide/auth/menu.md new file mode 100644 index 00000000..1708caa9 --- /dev/null +++ b/includes/kohana/modules/auth/guide/auth/menu.md @@ -0,0 +1,7 @@ +## [Auth]() +- [Config](config) +- [User Model](user) +- [Register Users](register) +- [Log in and out](login) +- [Edit User](edit) +- [Using Roles](roles) diff --git a/includes/kohana/modules/auth/guide/auth/register.md b/includes/kohana/modules/auth/guide/auth/register.md new file mode 100644 index 00000000..e69de29b diff --git a/includes/kohana/modules/auth/guide/auth/roles.md b/includes/kohana/modules/auth/guide/auth/roles.md new file mode 100644 index 00000000..e69de29b diff --git a/includes/kohana/modules/auth/guide/auth/user.md b/includes/kohana/modules/auth/guide/auth/user.md new file mode 100644 index 00000000..e69de29b diff --git a/includes/kohana/modules/cache/README.md b/includes/kohana/modules/cache/README.md index 8c486cb7..cd04fbde 100644 --- a/includes/kohana/modules/cache/README.md +++ b/includes/kohana/modules/cache/README.md @@ -15,6 +15,7 @@ Currently this module supports the following cache methods. 5. SQLite (Supports tags) 6. File 7. Xcache +8. Wincache Planned support --------------- diff --git a/includes/kohana/modules/cache/classes/cache/wincache.php b/includes/kohana/modules/cache/classes/cache/wincache.php new file mode 100644 index 00000000..603e446a --- /dev/null +++ b/includes/kohana/modules/cache/classes/cache/wincache.php @@ -0,0 +1,3 @@ +_sanitize_id($id))) === FALSE) ? $default : $data; + $data = apc_fetch($this->_sanitize_id($id), $success); + + return $success ? $data : $default; } /** @@ -130,4 +132,4 @@ class Kohana_Cache_Apc extends Cache { { return apc_clear_cache('user'); } -} \ No newline at end of file +} diff --git a/includes/kohana/modules/cache/classes/kohana/cache/eaccelerator.php b/includes/kohana/modules/cache/classes/kohana/cache/eaccelerator.php index 7abcd775..96c97f50 100644 --- a/includes/kohana/modules/cache/classes/kohana/cache/eaccelerator.php +++ b/includes/kohana/modules/cache/classes/kohana/cache/eaccelerator.php @@ -30,8 +30,8 @@ * * PHP 5.2.4 or greater * * Eaccelerator PHP extension * - * @package Kohana - * @category Cache + * @package Kohana/Cache + * @category Base * @author Kohana Team * @copyright (c) 2009-2010 Kohana Team * @license http://kohanaphp.com/license @@ -130,4 +130,4 @@ class Kohana_Cache_Eaccelerator extends Cache { { return eaccelerator_clean(); } -} \ No newline at end of file +} diff --git a/includes/kohana/modules/cache/classes/kohana/cache/exception.php b/includes/kohana/modules/cache/classes/kohana/cache/exception.php index f8677e78..d89d25eb 100644 --- a/includes/kohana/modules/cache/classes/kohana/cache/exception.php +++ b/includes/kohana/modules/cache/classes/kohana/cache/exception.php @@ -1,3 +1,11 @@ _config, 'cache_dir', APPPATH.Cache_File::CACHE_DIR); - $this->_cache_dir = new RecursiveDirectoryIterator($directory); + $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) { - if ( ! mkdir($directory, 0777, TRUE)) - { - throw new Kohana_Cache_Exception('Failed to create the defined cache directory : :directory', array(':directory' => $directory)); - } - chmod($directory, 0777); - $this->_cache_dir = new RecursiveDirectoryIterator($directory); + $this->_cache_dir = $this->_make_directory($directory, 0777, TRUE); } // If the defined directory is a file, get outta here @@ -138,7 +133,7 @@ class Kohana_Cache_File extends Cache implements Kohana_Cache_GarbageCollect { $file = new SplFileInfo($directory.$filename); // If file does not exist - if ( ! $file->getRealPath()) + if ( ! $file->isFile()) { // Return default value return $default; @@ -233,7 +228,7 @@ class Kohana_Cache_File extends Cache implements Kohana_Cache_GarbageCollect { $type = gettype($data); // Serialize the data - $data = json_encode((object) array( + $data = json_encode( (object) array( 'payload' => ($type === 'string') ? $data : serialize($data), 'expiry' => time() + $lifetime, 'type' => $type @@ -366,7 +361,7 @@ class Kohana_Cache_File extends Cache implements Kohana_Cache_GarbageCollect { } } // Else, is directory - else if ($file->isDir()) + elseif ($file->isDir()) { // Create new DirectoryIterator $files = new DirectoryIterator($file->getPathname()); @@ -378,7 +373,7 @@ class Kohana_Cache_File extends Cache implements Kohana_Cache_GarbageCollect { $name = $files->getFilename(); // If the name is not a dot - if ($name != '.' and $name != '..') + if ($name != '.' AND $name != '..' AND substr($file->getFilename(), 0, 1) == '.') { // Create new file resource $fp = new SplFileInfo($files->getRealPath()); @@ -442,4 +437,27 @@ class Kohana_Cache_File extends Cache implements Kohana_Cache_GarbageCollect { { 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 string mode + * @param string recursive + * @param string 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);; + } } diff --git a/includes/kohana/modules/cache/classes/kohana/cache/garbagecollect.php b/includes/kohana/modules/cache/classes/kohana/cache/garbagecollect.php index 228dd610..62c3148e 100644 --- a/includes/kohana/modules/cache/classes/kohana/cache/garbagecollect.php +++ b/includes/kohana/modules/cache/classes/kohana/cache/garbagecollect.php @@ -4,8 +4,8 @@ * of their own, such as [Cache_File] and [Cache_Sqlite]. Memory based * cache systems clean their own caches periodically. * - * @package Kohana - * @category Cache + * @package Kohana/Cache + * @category Base * @version 2.0 * @author Kohana Team * @copyright (c) 2009-2010 Kohana Team @@ -20,4 +20,4 @@ interface Kohana_Cache_GarbageCollect { * @return void */ public function garbage_collect(); -} \ No newline at end of file +} diff --git a/includes/kohana/modules/cache/classes/kohana/cache/memcache.php b/includes/kohana/modules/cache/classes/kohana/cache/memcache.php index e67c2496..c377dc46 100644 --- a/includes/kohana/modules/cache/classes/kohana/cache/memcache.php +++ b/includes/kohana/modules/cache/classes/kohana/cache/memcache.php @@ -24,6 +24,7 @@ * 'timeout' => 1, * 'retry_interval' => 15, * 'status' => TRUE, + * 'instant_death' => TRUE, * 'failure_callback' => array('className', 'classMethod') * ), * // Second memcache server @@ -72,8 +73,8 @@ * * Memcache (plus Memcached-tags for native tagging support) * * Zlib * - * @package Kohana - * @category Cache + * @package Kohana/Cache + * @category Base * @version 2.0 * @author Kohana Team * @copyright (c) 2009-2010 Kohana Team @@ -98,6 +99,13 @@ class Kohana_Cache_Memcache extends Cache { */ protected $_flags; + /** + * The default configuration for the memcached server + * + * @var array + */ + protected $_default_config = array(); + /** * Constructs the memcache Kohana_Cache object * @@ -127,22 +135,23 @@ class Kohana_Cache_Memcache extends Cache { } // Setup default server configuration - $config = array( - 'host' => 'localhost', - 'port' => 11211, - 'persistent' => FALSE, - 'weight' => 1, - 'timeout' => 1, - 'retry_interval' => 15, - 'status' => TRUE, - 'failure_callback' => array($this, '_failed_request'), + $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 += $config; + $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'])) { @@ -276,7 +285,7 @@ class Kohana_Cache_Memcache extends Cache { * @return void|boolean * @since 3.0.8 */ - protected function _failed_request($hostname, $port) + public function _failed_request($hostname, $port) { if ( ! $this->_config['instant_death']) return; @@ -287,8 +296,12 @@ class Kohana_Cache_Memcache extends Cache { // 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; } @@ -303,7 +316,7 @@ class Kohana_Cache_Memcache extends Cache { $host['port'], $host['timeout'], $host['retry_interval'], - FALSE, + FALSE, // Server is offline array($this, '_failed_request' )); } diff --git a/includes/kohana/modules/cache/classes/kohana/cache/memcachetag.php b/includes/kohana/modules/cache/classes/kohana/cache/memcachetag.php index cdb0db73..866ab9bf 100644 --- a/includes/kohana/modules/cache/classes/kohana/cache/memcachetag.php +++ b/includes/kohana/modules/cache/classes/kohana/cache/memcachetag.php @@ -2,8 +2,8 @@ /** * See [Kohana_Cache_Memcache] * -* @package Kohana -* @category Cache +* @package Kohana/Cache +* @category Base * @version 2.0 * @author Kohana Team * @copyright (c) 2009-2010 Kohana Team @@ -11,7 +11,7 @@ */ class Kohana_Cache_MemcacheTag extends Cache_Memcache implements Kohana_Cache_Tagging { - /** + /** * Constructs the memcache object * * @param array configuration @@ -19,12 +19,12 @@ class Kohana_Cache_MemcacheTag extends Cache_Memcache implements Kohana_Cache_Ta */ protected function __construct(array $config) { + parent::__construct($config); + if ( ! method_exists($this->_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'); } - - parent::__construct($config); } /** @@ -73,4 +73,4 @@ class Kohana_Cache_MemcacheTag extends Cache_Memcache implements Kohana_Cache_Ta { throw new Kohana_Cache_Exception('Memcached-tags does not support finding by tag'); } -} \ No newline at end of file +} diff --git a/includes/kohana/modules/cache/classes/kohana/cache/sqlite.php b/includes/kohana/modules/cache/classes/kohana/cache/sqlite.php index 32619771..70045e53 100644 --- a/includes/kohana/modules/cache/classes/kohana/cache/sqlite.php +++ b/includes/kohana/modules/cache/classes/kohana/cache/sqlite.php @@ -4,8 +4,8 @@ * * Requires SQLite3 and PDO * - * @package Kohana - * @category Cache + * @package Kohana/Cache + * @category Base * @author Kohana Team * @copyright (c) 2009-2010 Kohana Team * @license http://kohanaphp.com/license @@ -195,16 +195,16 @@ class Kohana_Cache_Sqlite extends Cache implements Kohana_Cache_Tagging, Kohana_ $data = serialize($data); // Normalise tags - $tags = (NULL === $tags) ? NULL : '<'.implode('>,<', $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(); + $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(); + $lifetime = (0 === $lifetime) ? 0 : ($lifetime + time()); } // Prepare statement @@ -333,4 +333,4 @@ class Kohana_Cache_Sqlite extends Cache implements Kohana_Cache_Tagging, Kohana_ return (bool) $statement->fetchAll(); } -} \ No newline at end of file +} diff --git a/includes/kohana/modules/cache/classes/kohana/cache/tagging.php b/includes/kohana/modules/cache/classes/kohana/cache/tagging.php index b7fe1ab7..001a2246 100644 --- a/includes/kohana/modules/cache/classes/kohana/cache/tagging.php +++ b/includes/kohana/modules/cache/classes/kohana/cache/tagging.php @@ -2,8 +2,8 @@ /** * Kohana Cache Tagging Interface * - * @package Kohana - * @category Cache + * @package Kohana/Cache + * @category Base * @author Kohana Team * @copyright (c) 2009-2010 Kohana Team * @license http://kohanaphp.com/license @@ -39,4 +39,4 @@ interface Kohana_Cache_Tagging { * @return array */ public function find($tag); -} \ No newline at end of file +} diff --git a/includes/kohana/modules/cache/classes/kohana/cache/wincache.php b/includes/kohana/modules/cache/classes/kohana/cache/wincache.php new file mode 100644 index 00000000..51484f44 --- /dev/null +++ b/includes/kohana/modules/cache/classes/kohana/cache/wincache.php @@ -0,0 +1,140 @@ + 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-2010 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 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 of cache to entry + * @param string 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 of cache entry + * @param string data to set to cache + * @param integer 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 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(); + } +} diff --git a/includes/kohana/modules/cache/config/cache.php b/includes/kohana/modules/cache/config/cache.php index c509c704..9ec311c0 100644 --- a/includes/kohana/modules/cache/config/cache.php +++ b/includes/kohana/modules/cache/config/cache.php @@ -46,6 +46,11 @@ return array 'driver' => 'apc', 'default_expire' => 3600, ), + 'wincache' => array + ( + 'driver' => 'wincache', + 'default_expire' => 3600, + ), 'sqlite' => array ( 'driver' => 'sqlite', diff --git a/includes/kohana/modules/oauth/config/userguide.php b/includes/kohana/modules/cache/config/userguide.php similarity index 82% rename from includes/kohana/modules/oauth/config/userguide.php rename to includes/kohana/modules/cache/config/userguide.php index 828837ff..524afc41 100644 --- a/includes/kohana/modules/oauth/config/userguide.php +++ b/includes/kohana/modules/cache/config/userguide.php @@ -5,16 +5,16 @@ return array( 'modules' => array( // This should be the path to this modules userguide pages, without the 'guide/'. Ex: '/guide/modulename/' would be 'modulename' - 'oauth' => array( + 'cache' => array( // Whether this modules userguide pages should be shown 'enabled' => TRUE, // The name that should show up on the userguide index page - 'name' => 'OAuth', + 'name' => 'Cache', // A short description of this module, shown on the index page - 'description' => 'Official OAuth module, used for open protocol authorization.', + 'description' => 'Common interface for caching engines.', // Copyright message, shown in the footer for this module 'copyright' => '© 2008–2010 Kohana Team', diff --git a/includes/kohana/modules/cache/guide/cache.config.md b/includes/kohana/modules/cache/guide/cache/config.md similarity index 100% rename from includes/kohana/modules/cache/guide/cache.config.md rename to includes/kohana/modules/cache/guide/cache/config.md diff --git a/includes/kohana/modules/cache/guide/cache/examples.md b/includes/kohana/modules/cache/guide/cache/examples.md new file mode 100644 index 00000000..e69de29b diff --git a/includes/kohana/modules/cache/guide/cache.about.md b/includes/kohana/modules/cache/guide/cache/index.md similarity index 100% rename from includes/kohana/modules/cache/guide/cache.about.md rename to includes/kohana/modules/cache/guide/cache/index.md diff --git a/includes/kohana/modules/cache/guide/cache/menu.md b/includes/kohana/modules/cache/guide/cache/menu.md new file mode 100644 index 00000000..5218558f --- /dev/null +++ b/includes/kohana/modules/cache/guide/cache/menu.md @@ -0,0 +1,3 @@ +## [Cache]() +- [Configuration](config) +- [Usage](usage) \ No newline at end of file diff --git a/includes/kohana/modules/cache/guide/cache/usage.md b/includes/kohana/modules/cache/guide/cache/usage.md new file mode 100644 index 00000000..8a8239bc --- /dev/null +++ b/includes/kohana/modules/cache/guide/cache/usage.md @@ -0,0 +1,219 @@ +# 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(); + } \ No newline at end of file diff --git a/includes/kohana/modules/cache/guide/menu.cache.md b/includes/kohana/modules/cache/guide/menu.cache.md deleted file mode 100644 index 3f516e5c..00000000 --- a/includes/kohana/modules/cache/guide/menu.cache.md +++ /dev/null @@ -1,4 +0,0 @@ -1. **Cache** - - [About](cache.about) - - [Configuration](cache.config) - - [Usage](cache.usage) \ No newline at end of file diff --git a/includes/kohana/modules/codebench/classes/bench/datespan.php b/includes/kohana/modules/codebench/classes/bench/datespan.php index 6ea1f508..ff35479e 100644 --- a/includes/kohana/modules/codebench/classes/bench/datespan.php +++ b/includes/kohana/modules/codebench/classes/bench/datespan.php @@ -29,7 +29,7 @@ class Bench_DateSpan extends Codebench { 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)); + $output = preg_split('/[^a-z]+/', strtolower( (string) $output)); // Invalid output if (empty($output)) @@ -116,7 +116,7 @@ class Bench_DateSpan extends Codebench { 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)); + $output = preg_split('/[^a-z]+/', strtolower( (string) $output)); // Invalid output if (empty($output)) diff --git a/includes/kohana/modules/codebench/classes/controller/codebench.php b/includes/kohana/modules/codebench/classes/controller/codebench.php index 52e84045..cdd9a705 100644 --- a/includes/kohana/modules/codebench/classes/controller/codebench.php +++ b/includes/kohana/modules/codebench/classes/controller/codebench.php @@ -17,7 +17,9 @@ class Controller_Codebench extends Kohana_Controller_Template { { // Convert submitted class name to URI segment if (isset($_POST['class'])) + { $this->request->redirect('codebench/'.trim($_POST['class'])); + } // Pass the class name on to the view $this->template->class = (string) $class; diff --git a/includes/kohana/modules/codebench/classes/kohana/codebench.php b/includes/kohana/modules/codebench/classes/kohana/codebench.php index d8a71c17..68601bd1 100644 --- a/includes/kohana/modules/codebench/classes/kohana/codebench.php +++ b/includes/kohana/modules/codebench/classes/kohana/codebench.php @@ -155,10 +155,10 @@ abstract class Kohana_Codebench { 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; + $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']); @@ -168,10 +168,10 @@ abstract class Kohana_Codebench { 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; + $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']); diff --git a/includes/kohana/modules/codebench/config/userguide.php b/includes/kohana/modules/codebench/config/userguide.php new file mode 100644 index 00000000..f9e6a14d --- /dev/null +++ b/includes/kohana/modules/codebench/config/userguide.php @@ -0,0 +1,23 @@ + 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–2010 Kohana Team', + ) + ) +); \ No newline at end of file diff --git a/includes/kohana/modules/codebench/guide/codebench/index.md b/includes/kohana/modules/codebench/guide/codebench/index.md new file mode 100644 index 00000000..78b6f217 --- /dev/null +++ b/includes/kohana/modules/codebench/guide/codebench/index.md @@ -0,0 +1,76 @@ +# Using Codebench + +[!!] The contents of this page are taken (with some minor changes) from