Upgrade to KH 3.1.3.1

This commit is contained in:
Deon George 2011-05-13 16:00:25 +10:00
parent 8013aadc4c
commit 6d256839fc
675 changed files with 22771 additions and 24111 deletions

View File

@ -1,6 +1,20 @@
<?php defined('SYSPATH') or die('No direct script access.');
//-- Environment setup --------------------------------------------------------
// -- Environment setup --------------------------------------------------------
// Load the core Kohana class
require SYSPATH.'classes/kohana/core'.EXT;
if (is_file(APPPATH.'classes/kohana'.EXT))
{
// Application extends the core
require APPPATH.'classes/kohana'.EXT;
}
else
{
// Load empty core extension
require SYSPATH.'classes/kohana'.EXT;
}
/**
* Set the default time zone.
@ -34,7 +48,23 @@ spl_autoload_register(array('Kohana', 'auto_load'));
*/
ini_set('unserialize_callback_func', 'spl_autoload_call');
//-- Configuration and initialization -----------------------------------------
// -- Configuration and initialization -----------------------------------------
/**
* Set the default language
*/
I18n::lang('en-us');
/**
* Set Kohana::$environment if a 'KOHANA_ENV' environment variable has been supplied.
*
* Note: If you supply an invalid environment name, a PHP warning will be thrown
* saying "Couldn't find constant Kohana::<INVALID_ENV_NAME>"
*/
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(/<file>)', array('file' => '.+'))
'action' => 'media',
'file' => NULL,
));
// Make sure their PHP version is current enough
if (strcmp(phpversion(),'5.3') < 0) {
echo 'This application requires PHP 5.3 or newer to run';
die();
}
if ( ! defined('SUPPRESS_REQUEST'))
{
/**
* 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::instance()
->execute()
->send_headers()
->response;
}

View File

@ -1,3 +1,3 @@
# Kohana PHP Framework, version 3.0 (dev)
# Kohana PHP Framework, version 3.1 (release)
This is the current development version of [Kohana](http://kohanaframework.org/).
This is the current release version of [Kohana](http://kohanaframework.org/).

View File

@ -1,12 +1,4 @@
<?php
// Sanity check, install should only be checked from index.php
defined('SYSPATH') or exit('Install tests must be loaded from within index.php!');
// Clear out the cache to prevent errors. This typically happens on Windows/FastCGI.
clearstatcache(TRUE);
?>
<?php defined('SYSPATH') or exit('Install tests must be loaded from within index.php!'); ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
@ -171,12 +163,20 @@ clearstatcache(TRUE);
</p>
<table cellspacing="0">
<tr>
<th>PECL HTTP Enabled</th>
<?php if (extension_loaded('http')): ?>
<td class="pass">Pass</td>
<?php else: ?>
<td class="fail">Kohana can use the <a href="http://php.net/http">http</a> extension for the Request_Client_External class.</td>
<?php endif ?>
</tr>
<tr>
<th>cURL Enabled</th>
<?php if (extension_loaded('curl')): ?>
<td class="pass">Pass</td>
<?php else: ?>
<td class="fail">Kohana requires <a href="http://php.net/curl">cURL</a> for the Remote class.</td>
<td class="fail">Kohana can use the <a href="http://php.net/curl">cURL</a> extension for the Request_Client_External class.</td>
<?php endif ?>
</tr>
<tr>
@ -195,6 +195,14 @@ clearstatcache(TRUE);
<td class="fail">Kohana requires <a href="http://php.net/gd">GD</a> v2 for the Image class.</td>
<?php endif ?>
</tr>
<tr>
<th>MySQL Enabled</th>
<?php if (function_exists('mysql_connect')): ?>
<td class="pass">Pass</td>
<?php else: ?>
<td class="fail">Kohana can use the <a href="http://php.net/mysql">MySQL</a> extension to support MySQL databases.</td>
<?php endif ?>
</tr>
<tr>
<th>PDO Enabled</th>
<?php if (class_exists('PDO')): ?>

View File

@ -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.

View File

@ -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
} // End Auth

View File

@ -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 {

View File

@ -1,244 +0,0 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* Default auth user
*
* @package Kohana/Auth
* @author Kohana Team
* @copyright (c) 2007-2009 Kohana Team
* @license http://kohanaphp.com/license.html
*/
class Model_Auth_User extends ORM {
// Relationships
protected $_has_many = array(
'user_tokens' => 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

View File

@ -1,101 +0,0 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* Default auth user toke
*
* @package Kohana/Auth
* @author Kohana Team
* @copyright (c) 2007-2009 Kohana Team
* @license http://kohanaphp.com/license.html
*/
class Model_Auth_User_Token extends ORM {
// Relationships
protected $_belongs_to = array('user' => 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

View File

@ -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',
),
);
);

View File

@ -0,0 +1,7 @@
## [Auth]()
- [Config](config)
- [User Model](user)
- [Register Users](register)
- [Log in and out](login)
- [Edit User](edit)
- [Using Roles](roles)

View File

@ -15,6 +15,7 @@ Currently this module supports the following cache methods.
5. SQLite (Supports tags)
6. File
7. Xcache
8. Wincache
Planned support
---------------

View File

@ -0,0 +1,3 @@
<?php defined('SYSPATH') or die('No direct script access.');
class Cache_Wincache extends Kohana_Cache_Wincache {}

View File

@ -67,8 +67,8 @@
* * Kohana 3.0.x
* * PHP 5.2.4 or greater
*
* @package Kohana
* @category Cache
* @package Kohana/Cache
* @category Base
* @version 2.0
* @author Kohana Team
* @copyright (c) 2009-2010 Kohana Team
@ -138,7 +138,7 @@ abstract class Kohana_Cache {
}
/**
* @var Kohana_Config
* @var Config
*/
protected $_config;
@ -253,4 +253,4 @@ abstract class Kohana_Cache {
return str_replace(array('/', '\\', ' '), '_', $id);
}
}
// End Kohana_Cache
// End Kohana_Cache

View File

@ -30,8 +30,8 @@
* * PHP 5.2.4 or greater
* * APC 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
@ -71,7 +71,9 @@ class Kohana_Cache_Apc extends Cache {
*/
public function get($id, $default = NULL)
{
return (($data = apc_fetch($this->_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');
}
}
}

View File

@ -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();
}
}
}

View File

@ -1,3 +1,11 @@
<?php defined('SYSPATH') or die('No direct script access.');
class Kohana_Cache_Exception extends Kohana_Exception {}
/**
* Kohana Cache Exception
*
* @package Kohana/Cache
* @category Base
* @author Kohana Team
* @copyright (c) 2009-2010 Kohana Team
* @license http://kohanaphp.com/license
*/
class Kohana_Cache_Exception extends Kohana_Exception {}

View File

@ -32,20 +32,14 @@
* * Kohana 3.0.x
* * PHP 5.2.4 or greater
*
* @package Kohana
* @category Cache
* @package Kohana/Cache
* @category Base
* @author Kohana Team
* @copyright (c) 2009-2010 Kohana Team
* @license http://kohanaphp.com/license
*/
class Kohana_Cache_File extends Cache implements Kohana_Cache_GarbageCollect {
// !!! NOTICE !!!
// THIS CONSTANT IS USED BY THE FILE CACHE CLASS
// INTERNALLY. USE THE CONFIGURATION FILE TO
// REDEFINE THE CACHE DIRECTORY.
const CACHE_DIR = 'cache/.kohana_cache';
/**
* Creates a hashed filename based on the string. This is used
* to create shorter unique IDs for each cache filename.
@ -80,17 +74,18 @@ class Kohana_Cache_File extends Cache implements Kohana_Cache_GarbageCollect {
try
{
$directory = Arr::get($this->_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);;
}
}

View File

@ -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();
}
}

View File

@ -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'
));
}

View File

@ -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');
}
}
}

View File

@ -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();
}
}
}

View File

@ -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);
}
}

View File

@ -0,0 +1,140 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* [Kohana Cache](api/Kohana_Cache) Wincache driver. Provides an opcode based
* driver for the Kohana Cache library.
*
* ### Configuration example
*
* Below is an example of an _wincache_ server configuration.
*
* return array(
* 'wincache' => 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();
}
}

View File

@ -46,6 +46,11 @@ return array
'driver' => 'apc',
'default_expire' => 3600,
),
'wincache' => array
(
'driver' => 'wincache',
'default_expire' => 3600,
),
'sqlite' => array
(
'driver' => 'sqlite',

View File

@ -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' => '&copy; 20082010 Kohana Team',

View File

View File

@ -0,0 +1,3 @@
## [Cache]()
- [Configuration](config)
- [Usage](usage)

View File

@ -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();
}

View File

@ -1,4 +0,0 @@
1. **Cache**
- [About](cache.about)
- [Configuration](cache.config)
- [Usage](cache.usage)

View File

@ -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))

View File

@ -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;

View File

@ -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']);

View File

@ -0,0 +1,23 @@
<?php defined('SYSPATH') or die('No direct script access.');
return array(
// Leave this alone
'modules' => 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' => '&copy; 20082010 Kohana Team',
)
)
);

View File

@ -0,0 +1,76 @@
# Using Codebench
[!!] The contents of this page are taken (with some minor changes) from <http://www.geertdedeckere.be/article/introducing-codebench> 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!

View File

@ -0,0 +1 @@
## [Codebench]()

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -2,7 +2,7 @@
/**
* Codebench A benchmarking module.
*
* @package Kohana
* @package Kohana/Codebench
* @author Kohana Team
* @copyright (c) 2009 Kohana Team
* @license http://kohanaphp.com/license.html
@ -14,7 +14,9 @@
<head>
<meta charset="utf-8" />
<title><?php if ($class !== '') echo $class, ' · ' ?>Codebench</title>
<title><?php if ($class !== ''): ?>
<?php echo $class, ' · ' ?>
<?php endif; ?>Codebench</title>
<style>
/* General styles*/
@ -255,4 +257,4 @@
</p>
</body>
</html>
</html>

View File

@ -1,3 +1,3 @@
<?php defined('SYSPATH') or die('No direct script access.');
class OAuth_Consumer extends Kohana_OAuth_Consumer { }
class Config_Database extends Kohana_Config_Database {}

View File

@ -15,7 +15,7 @@
* @copyright (c) 2009 Kohana Team
* @license http://kohanaphp.com/license
*/
class Kohana_Config_Database extends Kohana_Config_Reader {
class Kohana_Config_Database extends Config_Reader {
protected $_database_instance = 'default';

View File

@ -1,8 +1,14 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* Database connection wrapper. All database object instances are referenced
* by a name. Queries are typically handled by [Database_Query], rather than
* using the database object directly.
* Database connection wrapper/helper.
*
* You may get a database instance using `Database::instance('name')` where
* name is the [config](database/config) group.
*
* This class provides connection instance management via Database Drivers, as
* well as quoting, escaping and other related functions. Querys are done using
* [Database_Query] and [Database_Query_Builder] objects, which can be easily
* created using the [DB] helper class.
*
* @package Kohana/Database
* @category Base
@ -152,12 +158,18 @@ abstract class Kohana_Database {
/**
* Disconnect from the database. This is called automatically by [Database::__destruct].
* Clears the database instance from [Database::$instances].
*
* $db->disconnect();
*
* @return boolean
*/
abstract public function disconnect();
public function disconnect()
{
unset(Database::$instances[$this->_instance]);
return TRUE;
}
/**
* Set the connection character set. This is called automatically by [Database::connect].
@ -181,58 +193,56 @@ abstract class Kohana_Database {
*
* @param integer Database::SELECT, Database::INSERT, etc
* @param string SQL query
* @param mixed result object class, TRUE for stdClass, FALSE for assoc array
* @param mixed result object class string, TRUE for stdClass, FALSE for assoc array
* @param array 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);
abstract public function query($type, $sql, $as_object = FALSE, array $params = NULL);
/**
* Count the number of records in the last query, without LIMIT or OFFSET applied.
* Start a SQL transaction
*
* // Get the total number of records that match the last query
* $count = $db->count_last_query();
* // Start the transactions
* $db->begin();
*
* @return integer
* 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 transaction mode
* @return boolean
*/
public function count_last_query()
{
if ($sql = $this->last_query)
{
$sql = trim($sql);
if (stripos($sql, 'SELECT') !== 0)
{
return FALSE;
}
abstract public function begin($mode = NULL);
if (stripos($sql, 'LIMIT') !== FALSE)
{
// Remove LIMIT from the SQL
$sql = preg_replace('/\sLIMIT\s+[^a-z]+/i', ' ', $sql);
}
/**
* Commit the current transaction
*
* // Commit the database changes
* $db->commit();
*
* @return boolean
*/
abstract public function commit();
if (stripos($sql, 'OFFSET') !== FALSE)
{
// Remove OFFSET from the SQL
$sql = preg_replace('/\sOFFSET\s+\d+/i', '', $sql);
}
// Get the total rows from the last query executed
$result = $this->query
(
Database::SELECT,
'SELECT COUNT(*) AS '.$this->quote_identifier('total_rows').' '.
'FROM ('.$sql.') AS '.$this->quote_table('counted_results'),
TRUE
);
// Return the total number of rows from the query
return (int) $result->current()->total_rows;
}
return FALSE;
}
/**
* Abort the current transaction
*
* // Undo the changes
* $db->rollback();
*
* @return boolean
*/
abstract public function rollback();
/**
* Count the number of records in a table.
@ -246,7 +256,7 @@ abstract class Kohana_Database {
public function count_records($table)
{
// Quote the table name
$table = $this->quote_identifier($table);
$table = $this->quote_table($table);
return $this->query(Database::SELECT, 'SELECT COUNT(*) AS total_row_count FROM '.$table, FALSE)
->get('total_row_count');
@ -347,11 +357,15 @@ abstract class Kohana_Database {
* // 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 to get columns from
* @param string column to search for
* @param boolean whether to add the table prefix automatically or not
* @return array
*/
abstract public function list_columns($table, $like = NULL);
abstract public function list_columns($table, $like = NULL, $add_prefix = TRUE);
/**
* Extracts the text between parentheses, if any.
@ -439,7 +453,7 @@ abstract class Kohana_Database {
else
{
// Convert the object to a string
return $this->quote((string) $value);
return $this->quote( (string) $value);
}
}
elseif (is_array($value))
@ -459,6 +473,90 @@ abstract class Kohana_Database {
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 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.
*
@ -469,40 +567,67 @@ abstract class Kohana_Database {
* @uses Database::quote_identifier
* @uses Database::table_prefix
*/
public function quote_table($value)
public function quote_table($table)
{
// Assign the table by reference from the value
if (is_array($value))
if (is_array($table))
{
$table =& $value[0];
list($table, $alias) = $table;
}
// Attach table prefix to alias
$value[1] = $this->table_prefix().$value[1];
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
{
$table =& $value;
// 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 (is_string($table) AND strpos($table, '.') === FALSE)
if (isset($alias))
{
// Add the table prefix for tables
$table = $this->table_prefix().$table;
// Attach table prefix to alias
$table .= ' AS '.$this->_identifier.$this->table_prefix().$alias.$this->_identifier;
}
return $this->quote_identifier($value);
return $table;
}
/**
* Quote a database identifier, such as a column name. Adds the
* table prefix to the identifier if a table name is present.
*
* $column = $db->quote_identifier($column);
*
* You can also use SQL methods within identifiers.
*
* // The value of "column" will be quoted
* $column = $db->quote_identifier('COUNT("column")');
* Quote a database identifier
*
* Objects passed to this function will be converted to strings.
* [Database_Expression] objects will use the value of the expression.
@ -511,67 +636,53 @@ abstract class Kohana_Database {
*
* @param mixed any identifier
* @return string
* @uses Database::table_prefix
*/
public function quote_identifier($value)
{
if ($value === '*')
if (is_array($value))
{
return $value;
}
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_identifier((string) $value);
}
}
elseif (is_array($value))
{
// Separate the column and alias
list ($value, $alias) = $value;
return $this->quote_identifier($value).' AS '.$this->quote_identifier($alias);
list($value, $alias) = $value;
}
if (strpos($value, '"') !== FALSE)
if ($value instanceof Database_Query)
{
// Quote the column in FUNC("ident") identifiers
return preg_replace('/"(.+?)"/e', '$this->quote_identifier("$1")', $value);
// Create a sub-query
$value = '('.$value->compile($this).')';
}
elseif (strpos($value, '.') !== FALSE)
elseif ($value instanceof Database_Expression)
{
// Split the identifier into the individual parts
$parts = explode('.', $value);
if ($prefix = $this->table_prefix())
{
// Get the offset of the table name, 2nd-to-last part
// This works for databases that can have 3 identifiers (Postgre)
$offset = count($parts) - 2;
// Add the table prefix to the table name
$parts[$offset] = $prefix.$parts[$offset];
}
// Quote each of the parts
return implode('.', array_map(array($this, __FUNCTION__), $parts));
// Use a raw expression
$value = $value->value();
}
else
{
return $this->_identifier.$value.$this->_identifier;
// 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;
}
/**

View File

@ -8,6 +8,8 @@
* // SELECT CONCAT(first_name, last_name) AS full_name
* $query = DB::select(array(DB::expr('CONCAT(first_name, last_name)'), 'full_name')));
*
* More examples are available on the [Query Builder](database/query/builder#database-expressions) page
*
* @package Kohana/Database
* @category Base
* @author Kohana Team

View File

@ -64,9 +64,8 @@ class Kohana_Database_MySQL extends Database {
// No connection exists
$this->_connection = NULL;
throw new Database_Exception(':error', array(
':error' => mysql_error(),
),
throw new Database_Exception(':error',
array(':error' => mysql_error()),
mysql_errno());
}
@ -114,6 +113,9 @@ class Kohana_Database_MySQL extends Database {
{
// Clear the connection
$this->_connection = NULL;
// Clear the instance
parent::disconnect();
}
}
}
@ -150,7 +152,7 @@ class Kohana_Database_MySQL extends Database {
}
}
public function query($type, $sql, $as_object)
public function query($type, $sql, $as_object = FALSE, array $params = NULL)
{
// Make sure the database is connected
$this->_connection or $this->connect();
@ -192,7 +194,7 @@ class Kohana_Database_MySQL extends Database {
if ($type === Database::SELECT)
{
// Return an iterator of results
return new Database_MySQL_Result($result, $sql, $as_object);
return new Database_MySQL_Result($result, $sql, $as_object, $params);
}
elseif ($type === Database::INSERT)
{
@ -256,6 +258,57 @@ class Kohana_Database_MySQL extends Database {
return parent::datatype($type);
}
/**
* Start a SQL transaction
*
* @link http://dev.mysql.com/doc/refman/5.0/en/set-transaction.html
*
* @param string 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
*
* @param string Isolation level
* @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
*
* @param string Isolation level
* @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))
@ -278,10 +331,10 @@ class Kohana_Database_MySQL extends Database {
return $tables;
}
public function list_columns($table, $like = NULL)
public function list_columns($table, $like = NULL, $add_prefix = TRUE)
{
// Quote the table name
$table = $this->quote_table($table);
$table = ($add_prefix === TRUE) ? $this->quote_table($table) : $table;
if (is_string($like))
{
@ -330,7 +383,6 @@ class Kohana_Database_MySQL extends Database {
case 'varbinary':
$column['character_maximum_length'] = $length;
break;
case 'char':
case 'varchar':
$column['character_maximum_length'] = $length;
@ -340,7 +392,6 @@ class Kohana_Database_MySQL extends Database {
case 'longtext':
$column['collation_name'] = $row['Collation'];
break;
case 'enum':
case 'set':
$column['collation_name'] = $row['Collation'];
@ -367,11 +418,11 @@ class Kohana_Database_MySQL extends Database {
// Make sure the database is connected
$this->_connection or $this->connect();
if (($value = mysql_real_escape_string((string) $value, $this->_connection)) === FALSE)
if (($value = mysql_real_escape_string( (string) $value, $this->_connection)) === FALSE)
{
throw new Database_Exception(':error',
array(':error' => mysql_errno($this->_connection)),
mysql_error($this->_connection));
array(':error' => mysql_error($this->_connection)),
mysql_errno($this->_connection));
}
// SQL standard is to use single-quotes for all values

View File

@ -1,6 +1,6 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* MySQL database result.
* MySQL database result. See [Results](/database/results) for usage and examples.
*
* @package Kohana/Database
* @category Query/Result
@ -12,9 +12,9 @@ class Kohana_Database_MySQL_Result extends Database_Result {
protected $_internal_row = 0;
public function __construct($result, $sql, $as_object)
public function __construct($result, $sql, $as_object = FALSE, array $params = NULL)
{
parent::__construct($result, $sql, $as_object);
parent::__construct($result, $sql, $as_object, $params);
// Find the number of rows in the result
$this->_total_rows = mysql_num_rows($result);
@ -46,7 +46,7 @@ class Kohana_Database_MySQL_Result extends Database_Result {
public function current()
{
if ($this->_current_row !== $this->_internal_row AND ! $this->seek($this->_current_row))
return FALSE;
return NULL;
// Increment internal row for optimization assuming rows are fetched in order
$this->_internal_row++;
@ -59,7 +59,7 @@ class Kohana_Database_MySQL_Result extends Database_Result {
elseif (is_string($this->_as_object))
{
// Return an object of given class name
return mysql_fetch_object($this->_result, $this->_as_object);
return mysql_fetch_object($this->_result, $this->_as_object, $this->_object_params);
}
else
{

View File

@ -56,11 +56,9 @@ class Kohana_Database_PDO extends Database {
}
catch (PDOException $e)
{
throw new Database_Exception(':error', array(
':error' => $e->getMessage(),
),
$e->getCode(),
$e);
throw new Database_Exception(':error',
array(':error' => $e->getMessage()),
$e->getCode());
}
if ( ! empty($this->_config['charset']))
@ -75,7 +73,7 @@ class Kohana_Database_PDO extends Database {
// Destroy the PDO object
$this->_connection = NULL;
return TRUE;
return parent::disconnect();
}
public function set_charset($charset)
@ -87,7 +85,7 @@ class Kohana_Database_PDO extends Database {
$this->_connection->exec('SET NAMES '.$this->quote($charset));
}
public function query($type, $sql, $as_object)
public function query($type, $sql, $as_object = FALSE, array $params = NULL)
{
// Make sure the database is connected
$this->_connection or $this->connect();
@ -111,12 +109,12 @@ class Kohana_Database_PDO extends Database {
}
// Convert the exception in a database exception
throw new Database_Exception(':error [ :query ]', array(
throw new Database_Exception(':error [ :query ]',
array(
':error' => $e->getMessage(),
':query' => $sql
),
$e->getCode(),
$e);
$e->getCode());
}
if (isset($benchmark))
@ -136,7 +134,7 @@ class Kohana_Database_PDO extends Database {
}
elseif (is_string($as_object))
{
$result->setFetchMode(PDO::FETCH_CLASS, $as_object);
$result->setFetchMode(PDO::FETCH_CLASS, $as_object, $params);
}
else
{
@ -146,7 +144,7 @@ class Kohana_Database_PDO extends Database {
$result = $result->fetchAll();
// Return an iterator of results
return new Database_Result_Cached($result, $sql, $as_object);
return new Database_Result_Cached($result, $sql, $as_object, $params);
}
elseif ($type === Database::INSERT)
{
@ -163,13 +161,37 @@ class Kohana_Database_PDO extends Database {
}
}
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)
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__));

View File

@ -1,6 +1,6 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* Database query wrapper.
* Database query wrapper. See [Prepared Statements](database/query/prepared) for usage and examples.
*
* @package Kohana/Database
* @category Query
@ -14,7 +14,7 @@ class Kohana_Database_Query {
protected $_type;
// Cache lifetime
protected $_lifetime;
protected $_lifetime = NULL;
// SQL statement
protected $_sql;
@ -25,6 +25,9 @@ class Kohana_Database_Query {
// Return results as associative arrays or objects
protected $_as_object = FALSE;
// Parameters for __construct when using object results
protected $_object_params = array();
/**
* Creates a new SQL query of the specified type.
*
@ -52,7 +55,7 @@ class Kohana_Database_Query {
}
catch (Exception $e)
{
return Kohana::exception_text($e);
return Kohana_Exception::text($e);
}
}
@ -69,11 +72,18 @@ class Kohana_Database_Query {
/**
* Enables the query to be cached for a specified amount of time.
*
* @param integer number of seconds to cache or null for default
* @param integer 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;
@ -88,6 +98,8 @@ class Kohana_Database_Query {
{
$this->_as_object = FALSE;
$this->_object_params = array();
return $this;
}
@ -97,10 +109,16 @@ class Kohana_Database_Query {
* @param string classname or TRUE for stdClass
* @return $this
*/
public function as_object($class = TRUE)
public function as_object($class = TRUE, array $params = NULL)
{
$this->_as_object = $class;
if ($params)
{
// Add object parameters
$this->_object_params = $params;
}
return $this;
}
@ -191,7 +209,7 @@ class Kohana_Database_Query {
// Compile the SQL query
$sql = $this->compile($db);
if ( ! empty($this->_lifetime) AND $this->_type === Database::SELECT)
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.'")';
@ -199,12 +217,12 @@ class Kohana_Database_Query {
if ($result = Kohana::cache($cache_key, NULL, $this->_lifetime))
{
// Return a cached result
return new Database_Result_Cached($result, $sql, $this->_as_object);
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);
$result = $db->query($this->_type, $sql, $this->_as_object, $this->_object_params);
if (isset($cache_key))
{

View File

@ -1,6 +1,6 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* Database query builder.
* Database query builder. See [Query Builder](/database/query/builder) for usage and examples.
*
* @package Kohana/Database
* @category Query
@ -95,35 +95,35 @@ abstract class Kohana_Database_Query_Builder extends Database_Query {
// BETWEEN always has exactly two arguments
list($min, $max) = $value;
if (is_string($min) AND array_key_exists($min, $this->_parameters))
if ((is_string($min) AND array_key_exists($min, $this->_parameters)) === FALSE)
{
// Set the parameter as the minimum
$min = $this->_parameters[$min];
// Quote the value, it is not a parameter
$min = $db->quote($min);
}
if (is_string($max) AND array_key_exists($max, $this->_parameters))
if ((is_string($max) AND array_key_exists($max, $this->_parameters)) === FALSE)
{
// Set the parameter as the maximum
$max = $this->_parameters[$max];
// Quote the value, it is not a parameter
$max = $db->quote($max);
}
// Quote the min and max value
$value = $db->quote($min).' AND '.$db->quote($max);
$value = $min.' AND '.$max;
}
else
elseif ((is_string($value) AND array_key_exists($value, $this->_parameters)) === FALSE)
{
if (is_string($value) AND array_key_exists($value, $this->_parameters))
{
// Set the parameter as the value
$value = $this->_parameters[$value];
}
// Quote the entire value normally
// Quote the value, it is not a parameter
$value = $db->quote($value);
}
if ($column)
{
// Apply proper quoting to the column
$column = $db->quote_column($column);
}
// Append the statement to the query
$sql .= $db->quote_identifier($column).' '.$op.' '.$value;
$sql .= trim($column.' '.$op.' '.$value);
}
$last_condition = $condition;
@ -149,15 +149,15 @@ abstract class Kohana_Database_Query_Builder extends Database_Query {
list ($column, $value) = $group;
// Quote the column name
$column = $db->quote_identifier($column);
$column = $db->quote_column($column);
if (is_string($value) AND array_key_exists($value, $this->_parameters))
if ((is_string($value) AND array_key_exists($value, $this->_parameters)) === FALSE)
{
// Use the parameter value
$value = $this->_parameters[$value];
// Quote the value, it is not a parameter
$value = $db->quote($value);
}
$set[$column] = $column.' = '.$db->quote($value);
$set[$column] = $column.' = '.$value;
}
return implode(', ', $set);
@ -177,13 +177,19 @@ abstract class Kohana_Database_Query_Builder extends Database_Query {
{
list ($column, $direction) = $group;
if ( ! empty($direction))
if ($direction)
{
// Make the direction uppercase
$direction = ' '.strtoupper($direction);
$direction = strtoupper($direction);
}
$sort[] = $db->quote_identifier($column).$direction;
if ($column)
{
// Quote the column, if it has a value
$column = $db->quote_column($column);
}
$sort[] = trim($column.' '.$direction);
}
return 'ORDER BY '.implode(', ', $sort);

View File

@ -1,6 +1,6 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* Database query builder for DELETE statements.
* Database query builder for DELETE statements. See [Query Builder](/database/query/builder) for usage and examples.
*
* @package Kohana/Database
* @category Query
@ -73,7 +73,9 @@ class Kohana_Database_Query_Builder_Delete extends Database_Query_Builder_Where
$query .= ' LIMIT '.$this->_limit;
}
return $query;
$this->_sql = $query;
return parent::compile($db);
}
public function reset()
@ -83,6 +85,8 @@ class Kohana_Database_Query_Builder_Delete extends Database_Query_Builder_Where
$this->_parameters = array();
$this->_sql = NULL;
return $this;
}

View File

@ -1,6 +1,6 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* Database query builder for INSERT statements.
* Database query builder for INSERT statements. See [Query Builder](/database/query/builder) for usage and examples.
*
* @package Kohana/Database
* @category Query
@ -122,7 +122,7 @@ class Kohana_Database_Query_Builder_Insert extends Database_Query_Builder {
$query = 'INSERT INTO '.$db->quote_table($this->_table);
// Add the column names
$query .= ' ('.implode(', ', array_map(array($db, 'quote_identifier'), $this->_columns)).') ';
$query .= ' ('.implode(', ', array_map(array($db, 'quote_column'), $this->_columns)).') ';
if (is_array($this->_values))
{
@ -132,16 +132,16 @@ class Kohana_Database_Query_Builder_Insert extends Database_Query_Builder {
$groups = array();
foreach ($this->_values as $group)
{
foreach ($group as $i => $value)
foreach ($group as $offset => $value)
{
if (is_string($value) AND isset($this->_parameters[$value]))
if ((is_string($value) AND array_key_exists($value, $this->_parameters)) === FALSE)
{
// Use the parameter value
$group[$i] = $this->_parameters[$value];
// Quote the value, it is not a parameter
$group[$offset] = $db->quote($value);
}
}
$groups[] = '('.implode(', ', array_map($quote, $group)).')';
$groups[] = '('.implode(', ', $group).')';
}
// Add the values
@ -153,7 +153,9 @@ class Kohana_Database_Query_Builder_Insert extends Database_Query_Builder {
$query .= (string) $this->_values;
}
return $query;
$this->_sql = $query;
return parent::compile($db);;
}
public function reset()
@ -165,6 +167,8 @@ class Kohana_Database_Query_Builder_Insert extends Database_Query_Builder {
$this->_parameters = array();
$this->_sql = NULL;
return $this;
}

View File

@ -1,6 +1,6 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* Database query builder for JOIN statements.
* Database query builder for JOIN statements. See [Query Builder](/database/query/builder) for usage and examples.
*
* @package Kohana/Database
* @category Query
@ -19,6 +19,9 @@ class Kohana_Database_Query_Builder_Join extends Database_Query_Builder {
// ON ...
protected $_on = array();
// USING ...
protected $_using = array();
/**
* Creates a new JOIN statement for a table. Optionally, the type of JOIN
* can be specified as the second parameter.
@ -49,11 +52,37 @@ class Kohana_Database_Query_Builder_Join extends Database_Query_Builder {
*/
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 column name
* @param ...
* @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.
*
@ -72,27 +101,35 @@ class Kohana_Database_Query_Builder_Join extends Database_Query_Builder {
}
// Quote the table name that is being joined
$sql .= ' '.$db->quote_table($this->_table).' ON ';
$sql .= ' '.$db->quote_table($this->_table);
$conditions = array();
foreach ($this->_on as $condition)
if ( ! empty($this->_using))
{
// Split the condition
list($c1, $op, $c2) = $condition;
if ($op)
// Quote and concat the columns
$sql .= ' USING ('.implode(', ', array_map(array($db, 'quote_column'), $this->_using)).')';
}
else
{
$conditions = array();
foreach ($this->_on as $condition)
{
// Make the operator uppercase and spaced
$op = ' '.strtoupper($op);
// 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);
}
// Quote each of the identifiers used for the condition
$conditions[] = $db->quote_identifier($c1).$op.' '.$db->quote_identifier($c2);
// Concat the conditions "... AND ..."
$sql .= ' ON ('.implode(' AND ', $conditions).')';
}
// Concat the conditions "... AND ..."
$sql .= '('.implode(' AND ', $conditions).')';
return $sql;
}

View File

@ -1,6 +1,6 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* Database query builder for SELECT statements.
* Database query builder for SELECT statements. See [Query Builder](/database/query/builder) for usage and examples.
*
* @package Kohana/Database
* @category Query
@ -31,6 +31,9 @@ class Kohana_Database_Query_Builder_Select extends Database_Query_Builder_Where
// OFFSET ...
protected $_offset = NULL;
// UNION ...
protected $_union = array();
// The last JOIN statement created
protected $_last_join;
@ -76,10 +79,6 @@ class Kohana_Database_Query_Builder_Select extends Database_Query_Builder_Where
{
$columns = func_get_args();
// Ignore our default select of .* if we already have some selected columns.
if (count($columns) == 1 AND is_string($columns[0]) AND preg_match('/\.\*$/',$columns[0]) AND $this->_select)
return $this;
$this->_select = array_merge($this->_select, $columns);
return $this;
@ -143,6 +142,22 @@ class Kohana_Database_Query_Builder_Select extends Database_Query_Builder_Where
return $this;
}
/**
* Adds "USING ..." conditions for the last created JOIN statement.
*
* @param string column name
* @param ...
* @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.
*
@ -270,6 +285,26 @@ class Kohana_Database_Query_Builder_Select extends Database_Query_Builder_Where
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 ..."
*
@ -291,8 +326,8 @@ class Kohana_Database_Query_Builder_Select extends Database_Query_Builder_Where
*/
public function compile(Database $db)
{
// Callback to quote identifiers
$quote_ident = array($db, 'quote_identifier');
// Callback to quote columns
$quote_column = array($db, 'quote_column');
// Callback to quote tables
$quote_table = array($db, 'quote_table');
@ -314,7 +349,7 @@ class Kohana_Database_Query_Builder_Select extends Database_Query_Builder_Where
else
{
// Select all columns
$query .= implode(', ', array_unique(array_map($quote_ident, $this->_select)));
$query .= implode(', ', array_unique(array_map($quote_column, $this->_select)));
}
if ( ! empty($this->_from))
@ -338,7 +373,7 @@ class Kohana_Database_Query_Builder_Select extends Database_Query_Builder_Where
if ( ! empty($this->_group_by))
{
// Add sorting
$query .= ' GROUP BY '.implode(', ', array_map($quote_ident, $this->_group_by));
$query .= ' GROUP BY '.implode(', ', array_map($quote_column, $this->_group_by));
}
if ( ! empty($this->_having))
@ -364,8 +399,22 @@ class Kohana_Database_Query_Builder_Select extends Database_Query_Builder_Where
// 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);
}
}
return $query;
$this->_sql = $query;
return parent::compile($db);
}
public function reset()
@ -376,7 +425,8 @@ class Kohana_Database_Query_Builder_Select extends Database_Query_Builder_Where
$this->_where =
$this->_group_by =
$this->_having =
$this->_order_by = array();
$this->_order_by =
$this->_union = array();
$this->_distinct = FALSE;
@ -386,7 +436,10 @@ class Kohana_Database_Query_Builder_Select extends Database_Query_Builder_Where
$this->_parameters = array();
$this->_sql = NULL;
return $this;
}
} // End Database_Query_Select

View File

@ -1,6 +1,6 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* Database query builder for UPDATE statements.
* Database query builder for UPDATE statements. See [Query Builder](/database/query/builder) for usage and examples.
*
* @package Kohana/Database
* @category Query
@ -97,13 +97,21 @@ class Kohana_Database_Query_Builder_Update extends Database_Query_Builder_Where
$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;
}
return $query;
$this->_sql = $query;
return parent::compile($db);
}
public function reset()
@ -117,6 +125,8 @@ class Kohana_Database_Query_Builder_Update extends Database_Query_Builder_Where
$this->_parameters = array();
$this->_sql = NULL;
return $this;
}

View File

@ -1,6 +1,6 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* Database query builder for WHERE statements.
* Database query builder for WHERE statements. See [Query Builder](/database/query/builder) for usage and examples.
*
* @package Kohana/Database
* @category Query

View File

@ -1,6 +1,6 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* Database result wrapper.
* Database result wrapper. See [Results](/database/results) for usage and examples.
*
* @package Kohana/Database
* @category Query/Result
@ -23,6 +23,9 @@ abstract class Kohana_Database_Result implements Countable, Iterator, SeekableIt
// Return rows as an object or associative array
protected $_as_object;
// Parameters for __construct when using object results
protected $_object_params = NULL;
/**
* Sets the total number of rows and stores the result locally.
*
@ -30,7 +33,7 @@ abstract class Kohana_Database_Result implements Countable, Iterator, SeekableIt
* @param string SQL query
* @return void
*/
public function __construct($result, $sql, $as_object)
public function __construct($result, $sql, $as_object = FALSE, array $params = NULL)
{
// Store the result locally
$this->_result = $result;
@ -46,6 +49,12 @@ abstract class Kohana_Database_Result implements Countable, Iterator, SeekableIt
// Results as objects or associative arrays
$this->_as_object = $as_object;
if ($params)
{
// Object constructor params
$this->_object_params = $params;
}
}
/**

View File

@ -1,6 +1,6 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* Cached database result.
* Object used for caching the results of select queries. See [Results](/database/results#select-cached) for usage and examples.
*
* @package Kohana/Database
* @category Query/Result
@ -45,7 +45,7 @@ class Kohana_Database_Result_Cached extends Database_Result {
public function current()
{
// Return an array of the row
return $this->_result[$this->_current_row];
return $this->valid() ? $this->_result[$this->_current_row] : NULL;
}
} // End Database_Result_Cached

View File

@ -1,6 +1,17 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* Database object creation helper methods.
* Provides a shortcut to get Database related objects for [making queries](../database/query).
*
* Shortcut | Returned Object
* -------------|---------------
* [`DB::query()`](#query) | [Database_Query]
* [`DB::insert()`](#insert) | [Database_Query_Builder_Insert]
* [`DB::select()`](#select),<br />[`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
@ -114,6 +125,8 @@ class Kohana_DB {
* 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 expression
* @return Database_Expression

View File

@ -0,0 +1,58 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* Database Model base class.
*
* @package Kohana/Database
* @category Models
* @author Kohana Team
* @copyright (c) 2008-2010 Kohana Team
* @license http://kohanaframework.org/license
*/
abstract class Kohana_Model_Database extends Model {
/**
* Create a new model instance. A [Database] instance or configuration
* group name can be passed to the model. If no database is defined, the
* "default" database group will be used.
*
* $model = Model::factory($name);
*
* @param string model name
* @param mixed Database instance object or string
* @return Model
*/
public static function factory($name, $db = NULL)
{
// Add the model prefix
$class = 'Model_'.$name;
return new $class($db);
}
// Database instance
protected $_db = 'default';
/**
* Loads the database.
*
* $model = new Foo_Model($db);
*
* @param mixed Database instance object or string
* @return void
*/
public function __construct($db = NULL)
{
if ($db !== NULL)
{
// Set the database instance name
$this->_db = $db;
}
if (is_string($this->_db))
{
// Load the database
$this->_db = Database::instance($this->_db);
}
}
} // End Model

View File

@ -0,0 +1,3 @@
<?php defined('SYSPATH') or die('No direct script access.');
abstract class Model_Database extends Kohana_Model_Database {}

View File

@ -0,0 +1,23 @@
<?php defined('SYSPATH') or die('No direct script access.');
return array(
// Leave this alone
'modules' => 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' => '&copy; 20082010 Kohana Team',
)
)
);

View File

@ -0,0 +1,118 @@
# 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). Prepared statements will **not** use the table prefix.
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).

View File

@ -0,0 +1,52 @@
# Examples
Here are some "real world" examples of using the database library to construct your queries and use the results.
## Examples of Prepared Statements
TODO: 4-6 examples of prepared 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.

View File

@ -0,0 +1,17 @@
# 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).

View File

@ -0,0 +1,7 @@
## [Database]()
- [Configuration](config)
- [Querying](query)
- [Prepared Statements](query/prepared)
- [Query Builder](query/builder)
- [Results](results)
- [Examples](examples)

View File

@ -0,0 +1,5 @@
# 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 [prepared statements](query/prepared) 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.

View File

@ -0,0 +1,253 @@
# 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.
[!!] At this time, it is not possible to combine query building with prepared statements.
## 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')

View File

@ -0,0 +1,67 @@
# 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')

View File

@ -0,0 +1,105 @@
# Results
## Execute
Once you have a query object built, either through a prepared 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 `<select>` dropdowns.
$results = DB::select('id', 'name')->from('users')->execute();
$users = $results->as_array('id','name');
// Show a dropdown with all users in it.
echo Form::select('author', $users)
To return a non-associative array, leave `$key` as NULL and just pass a `$value`.
$results = DB::select('email')->from('users')->execute();
$users = $results->as_array(NULL, 'email');
foreach($users as $email)
{
echo 'User Email: '.$email;
}
### Select - `get()`
Sometime you only want a single value from a query. The `get()` method returns the value of the named column from the current row. The second parameter, `$default`, is used to supply a default value when the result is NULL.
$total_users = DB::select(array('COUNT("username")', 'total_users'))->from('users')->execute()->get('total_users', 0);
### Select - `cached()`
The mysql database driver returns a `Database_Result` that works with a MySQL Resource data type. Since this resource lives outside of PHP environment, it can't be serialized which means it also can't be cached. To get around this the `Database_Result` object has the `cached()` method that returns a `Database_Result_Cached` object of the result set. The `Database_Result_Cached` can be serialized and cached, but can take up more memory.
[!!] NOTE: Currently, the PDO diver always returns a class of `Database_Result_Cached`, so `cached()` just returns itself.
The `cached()` function doesn't actually do any caching, it simply returns the result in a way that can be serialized and cached. You will need to use the [Cache Module](../cache) or some other caching method.
### Select - `count()`
The `Database_Result` object implements the `Countable` Interface. The method `count()` returns the total row count in the result set.
[!!] NOTE: This is the count of the current result set, not a count of how many records are in the database. This is important to point out especially when using `limit()` and `offset()` in your query.
[!!] For a complete list of methods available when working with a result set see [Database_Result].
## Insert
[DB::insert] returns an array of two values: the last insert id and the number of affected rows.
$insert = DB::insert('tools')
->columns(array('name', 'model', 'description'))
->values(array('Skil 3400 10" Table Saw', '3400', 'Powerful 15 amp motor; weighs just 54-pounds'));
list($insert_id, $affected_rows) = $insert->execute();
## Update & Delete
[DB::update] and [DB::delete] both return the number of affected rows as an integer.
$rows_deleted = DB::delete('tools')->where('model', 'like', '3400')->execute();

View File

@ -10,7 +10,7 @@
*/
abstract class Kohana_Image {
// Resizing contraints
// Resizing constraints
const NONE = 0x01;
const WIDTH = 0x02;
const HEIGHT = 0x03;
@ -73,6 +73,11 @@ abstract class Kohana_Image {
*/
public $type;
/**
* @var string mime type of the image
*/
public $mime;
/**
* Loads information about the image. Will throw an exception if the image
* does not exist or is not an image.
@ -99,7 +104,7 @@ abstract class Kohana_Image {
if (empty($file) OR empty($info))
{
throw new Kohana_Exception('Not an image or invalid image: :file',
array(':file' => Kohana::debug_path($file)));
array(':file' => Debug::path($file)));
}
// Store the image information
@ -132,10 +137,10 @@ abstract class Kohana_Image {
if (is_object(Kohana::$log))
{
// Get the text of the exception
$error = Kohana::exception_text($e);
$error = Kohana_Exception::text($e);
// Add this exception to the log
Kohana::$log->add(Kohana::ERROR, $error);
Kohana::$log->add(Log::ERROR, $error);
}
// Showing any kind of error will be "inside" image data
@ -175,20 +180,20 @@ abstract class Kohana_Image {
// Choose the master dimension automatically
$master = Image::AUTO;
}
// Image::WIDTH and Image::HEIGHT depricated. You can use it in old projects,
// Image::WIDTH and Image::HEIGHT deprecated. You can use it in old projects,
// but in new you must pass empty value for non-master dimension
elseif ($master == Image::WIDTH AND ! empty($width))
{
$master = Image::AUTO;
// Set empty height for backvard compatibility
// Set empty height for backward compatibility
$height = NULL;
}
elseif ($master == Image::HEIGHT AND ! empty($height))
{
$master = Image::AUTO;
// Set empty width for backvard compatibility
// Set empty width for backward compatibility
$width = NULL;
}
@ -599,7 +604,7 @@ abstract class Kohana_Image {
if ( ! is_writable($file))
{
throw new Kohana_Exception('File must be writable: :file',
array(':file' => Kohana::debug_path($file)));
array(':file' => Debug::path($file)));
}
}
else
@ -610,7 +615,7 @@ abstract class Kohana_Image {
if ( ! is_dir($directory) OR ! is_writable($directory))
{
throw new Kohana_Exception('Directory must be writable: :directory',
array(':directory' => Kohana::debug_path($directory)));
array(':directory' => Debug::path($directory)));
}
}

View File

@ -408,6 +408,8 @@ class Kohana_Image_GD extends Image {
// Create the watermark image resource
$overlay = imagecreatefromstring($watermark->render());
imagesavealpha($overlay, TRUE);
// Get the width and height of the watermark
$width = imagesx($overlay);
$height = imagesy($overlay);
@ -417,13 +419,13 @@ class Kohana_Image_GD extends Image {
// Convert an opacity range of 0-100 to 127-0
$opacity = round(abs(($opacity * 127 / 100) - 127));
// Allocate transparent white
$color = imagecolorallocatealpha($overlay, 255, 255, 255, $opacity);
// Allocate transparent gray
$color = imagecolorallocatealpha($overlay, 127, 127, 127, $opacity);
// The transparent image will overlay the watermark
imagelayereffect($overlay, IMG_EFFECT_OVERLAY);
// Fill the background with transparent white
// Fill the background with the transparent color
imagefilledrectangle($overlay, 0, 0, $width, $height, $color);
}

View File

@ -0,0 +1,23 @@
<?php defined('SYSPATH') or die('No direct script access.');
return array(
// Leave this alone
'modules' => array(
// This should be the path to this modules userguide pages, without the 'guide/'. Ex: '/guide/modulename/' would be 'modulename'
'image' => array(
// Whether this modules userguide pages should be shown
'enabled' => TRUE,
// The name that should show up on the userguide index page
'name' => 'Image',
// A short description of this module, shown on the index page
'description' => 'Image manipulation.',
// Copyright message, shown in the footer for this module
'copyright' => '&copy; 20082010 Kohana Team',
)
)
);

View File

@ -0,0 +1,3 @@
## [Image]()
- [Using](using)
- [Examples](examples)

View File

@ -1,5 +0,0 @@
# OAuth for Kohana
An implementation of the [OAuth](http://oauth.net/) protocol.
*Does not provide server capabilities!*

View File

@ -1,217 +0,0 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* OAuth Library
*
* @package Kohana/OAuth
* @category Base
* @author Kohana Team
* @copyright (c) 2010 Kohana Team
* @license http://kohanaframework.org/license
* @since 3.0.7
*/
abstract class Kohana_OAuth {
/**
* @var string OAuth complaince version
*/
public static $version = '1.0';
/**
* RFC3986 compatible version of urlencode. Passing an array will encode
* all of the values in the array. Array keys will not be encoded.
*
* $input = OAuth::urlencode($input);
*
* Multi-dimensional arrays are not allowed!
*
* [!!] This method implements [OAuth 1.0 Spec 5.1](http://oauth.net/core/1.0/#rfc.section.5.1).
*
* @param mixed input string or array
* @return mixed
*/
public static function urlencode($input)
{
if (is_array($input))
{
// Encode the values of the array
return array_map(array('OAuth', 'urlencode'), $input);
}
// Encode the input
$input = rawurlencode($input);
if (version_compare(PHP_VERSION, '<', '5.3'))
{
// rawurlencode() is RFC3986 compliant in PHP 5.3
// the only difference is the encoding of tilde
$input = str_replace('%7E', '~', $input);
}
return $input;
}
/**
* RFC3986 complaint version of urldecode. Passing an array will decode
* all of the values in the array. Array keys will not be encoded.
*
* $input = OAuth::urldecode($input);
*
* Multi-dimensional arrays are not allowed!
*
* [!!] This method implements [OAuth 1.0 Spec 5.1](http://oauth.net/core/1.0/#rfc.section.5.1).
*
* @param mixed input string or array
* @return mixed
*/
public static function urldecode($input)
{
if (is_array($input))
{
// Decode the values of the array
return array_map(array('OAuth', 'urldecode'), $input);
}
// Decode the input
return rawurldecode($input);
}
/**
* Normalize all request parameters into a string.
*
* $query = OAuth::normalize_params($params);
*
* [!!] This method implements [OAuth 1.0 Spec 9.1.1](http://oauth.net/core/1.0/#rfc.section.9.1.1).
*
* @param array request parameters
* @return string
* @uses OAuth::urlencode
*/
public static function normalize_params(array $params = NULL)
{
if ( ! $params)
{
// Nothing to do
return '';
}
// Encode the parameter keys and values
$keys = OAuth::urlencode(array_keys($params));
$values = OAuth::urlencode(array_values($params));
// Recombine the parameters
$params = array_combine($keys, $values);
// OAuth Spec 9.1.1 (1)
// "Parameters are sorted by name, using lexicographical byte value ordering."
uksort($params, 'strcmp');
// Create a new query string
$query = array();
foreach ($params as $name => $value)
{
if (is_array($value))
{
// OAuth Spec 9.1.1 (1)
// "If two or more parameters share the same name, they are sorted by their value."
$value = natsort($value);
foreach ($value as $duplicate)
{
$query[] = $name.'='.$duplicate;
}
}
else
{
$query[] = $name.'='.$value;
}
}
return implode('&', $query);
}
/**
* Parse the query string out of the URL and return it as parameters.
* All GET parameters must be removed from the request URL when building
* the base string and added to the request parameters.
*
* // parsed parameters: array('oauth_key' => 'abcdef123456789')
* list($url, $params) = OAuth::parse_url('http://example.com/oauth/access?oauth_key=abcdef123456789');
*
* [!!] This implements [OAuth Spec 9.1.1](http://oauth.net/core/1.0/#rfc.section.9.1.1).
*
* @param string URL to parse
* @return array (clean_url, params)
* @uses OAuth::parse_params
*/
public static function parse_url($url)
{
if ($query = parse_url($url, PHP_URL_QUERY))
{
// Remove the query string from the URL
list($url) = explode('?', $url, 2);
// Parse the query string as request parameters
$params = OAuth::parse_params($query);
}
else
{
// No parameters are present
$params = array();
}
return array($url, $params);
}
/**
* Parse the parameters in a string and return an array. Duplicates are
* converted into indexed arrays.
*
* // Parsed: array('a' => '1', 'b' => '2', 'c' => '3')
* $params = OAuth::parse_params('a=1,b=2,c=3');
*
* // Parsed: array('a' => array('1', '2'), 'c' => '3')
* $params = OAuth::parse_params('a=1,a=2,c=3');
*
* @param string parameter string
* @return array
*/
public static function parse_params($params)
{
// Split the parameters by &
$params = explode('&', trim($params));
// Create an array of parsed parameters
$parsed = array();
foreach ($params as $param)
{
// Split the parameter into name and value
list($name, $value) = explode('=', $param, 2);
// Decode the name and value
$name = OAuth::urldecode($name);
$value = OAuth::urldecode($value);
if (isset($parsed[$name]))
{
if ( ! is_array($parsed[$name]))
{
// Convert the parameter to an array
$parsed[$name] = array($parsed[$name]);
}
// Add a new duplicate parameter
$parsed[$name][] = $value;
}
else
{
// Add a new parameter
$parsed[$name] = $value;
}
}
return $parsed;
}
} // End OAuth

View File

@ -1,99 +0,0 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* OAuth Consumer
*
* @package Kohana/OAuth
* @category Base
* @author Kohana Team
* @copyright (c) 2010 Kohana Team
* @license http://kohanaframework.org/license
* @since 3.0.7
*/
class Kohana_OAuth_Consumer {
/**
* Create a new consumer object.
*
* $consumer = OAuth_Consumer::factory($options);
*
* @param array consumer options, key and secret are required
* @return OAuth_Consumer
*/
public static function factory(array $options = NULL)
{
return new OAuth_Consumer($options);
}
/**
* @var string consumer key
*/
protected $key;
/**
* @var string consumer secret
*/
protected $secret;
/**
* @var string callback URL for OAuth authorization completion
*/
protected $callback;
/**
* Sets the consumer key and secret.
*
* @param array consumer options, key and secret are required
* @return void
*/
public function __construct(array $options = NULL)
{
if ( ! isset($options['key']))
{
throw new Kohana_OAuth_Exception('Required option not passed: :option',
array(':option' => 'key'));
}
if ( ! isset($options['secret']))
{
throw new Kohana_OAuth_Exception('Required option not passed: :option',
array(':option' => 'secret'));
}
$this->key = $options['key'];
$this->secret = $options['secret'];
if (isset($options['callback']))
{
$this->callback = $options['callback'];
}
}
/**
* Return the value of any protected class variable.
*
* // Get the consumer key
* $key = $consumer->key;
*
* @param string variable name
* @return mixed
*/
public function __get($key)
{
return $this->$key;
}
/**
* Change the consumer callback.
*
* @param string new consumer callback
* @return $this
*/
public function callback($callback)
{
$this->callback = $callback;
return $this;
}
} // End OAuth_Consumer

View File

@ -1,12 +0,0 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* OAuth Exception
*
* @package Kohana/OAuth
* @category Exceptions
* @author Kohana Team
* @copyright (c) 2010 Kohana Team
* @license http://kohanaframework.org/license
* @since 3.0.7
*/
class Kohana_OAuth_Exception extends Kohana_Exception { }

View File

@ -1,215 +0,0 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* OAuth Provider
*
* @package Kohana/OAuth
* @category Provider
* @author Kohana Team
* @copyright (c) 2010 Kohana Team
* @license http://kohanaframework.org/license
* @since 3.0.7
*/
abstract class Kohana_OAuth_Provider {
/**
* Create a new provider.
*
* // Load the Twitter provider
* $provider = OAuth_Provider::factory('twitter');
*
* @param string provider name
* @param array provider options
* @return OAuth_Provider
*/
public static function factory($name, array $options = NULL)
{
$class = 'OAuth_Provider_'.$name;
return new $class($options);
}
/**
* @var string provider name
*/
public $name;
/**
* @var array additional request parameters to be used for remote requests
*/
protected $params = array();
/**
* Overloads default class properties from the options.
*
* Any of the provider options can be set here:
*
* Type | Option | Description | Default Value
* ----------|---------------|------------------------------------------------|-----------------
* mixed | signature | Signature method name or object | provider default
*
* @param array provider options
* @return void
*/
public function __construct(array $options = NULL)
{
if (isset($options['signature']))
{
// Set the signature method name or object
$this->signature = $options['signature'];
}
if ( ! is_object($this->signature))
{
// Convert the signature name into an object
$this->signature = OAuth_Signature::factory($this->signature);
}
if ( ! $this->name)
{
// Attempt to guess the name from the class name
$this->name = strtolower(substr(get_class($this), strlen('OAuth_Provider_')));
}
}
/**
* Return the value of any protected class variable.
*
* // Get the provider signature
* $signature = $provider->signature;
*
* @param string variable name
* @return mixed
*/
public function __get($key)
{
return $this->$key;
}
/**
* Returns the request token URL for the provider.
*
* $url = $provider->url_request_token();
*
* @return string
*/
abstract public function url_request_token();
/**
* Returns the authorization URL for the provider.
*
* $url = $provider->url_authorize();
*
* @return string
*/
abstract public function url_authorize();
/**
* Returns the access token endpoint for the provider.
*
* $url = $provider->url_access_token();
*
* @return string
*/
abstract public function url_access_token();
/**
* Ask for a request token from the OAuth provider.
*
* $token = $provider->request_token($consumer);
*
* @param OAuth_Consumer consumer
* @param array additional request parameters
* @return OAuth_Token_Request
* @uses OAuth_Request_Token
*/
public function request_token(OAuth_Consumer $consumer, array $params = NULL)
{
// Create a new GET request for a request token with the required parameters
$request = OAuth_Request::factory('token', 'GET', $this->url_request_token(), array(
'oauth_consumer_key' => $consumer->key,
'oauth_callback' => $consumer->callback,
));
if ($params)
{
// Load user parameters
$request->params($params);
}
// Sign the request using only the consumer, no token is available yet
$request->sign($this->signature, $consumer);
// Create a response from the request
$response = $request->execute();
// Store this token somewhere useful
return OAuth_Token::factory('request', array(
'token' => $response->param('oauth_token'),
'secret' => $response->param('oauth_token_secret'),
));
}
/**
* Get the authorization URL for the request token.
*
* $this->request->redirect($provider->authorize_url($token));
*
* @param OAuth_Token_Request token
* @param array additional request parameters
* @return string
*/
public function authorize_url(OAuth_Token_Request $token, array $params = NULL)
{
// Create a new GET request for a request token with the required parameters
$request = OAuth_Request::factory('authorize', 'GET', $this->url_authorize(), array(
'oauth_token' => $token->token,
));
if ($params)
{
// Load user parameters
$request->params($params);
}
return $request->as_url();
}
/**
* Exchange the request token for an access token.
*
* $token = $provider->access_token($consumer, $token);
*
* @param OAuth_Consumer consumer
* @param OAuth_Token_Request token
* @param array additional request parameters
* @return OAuth_Token_Access
*/
public function access_token(OAuth_Consumer $consumer, OAuth_Token_Request $token, array $params = NULL)
{
// Create a new GET request for a request token with the required parameters
$request = OAuth_Request::factory('access', 'GET', $this->url_access_token(), array(
'oauth_consumer_key' => $consumer->key,
'oauth_token' => $token->token,
'oauth_verifier' => $token->verifier,
));
if ($params)
{
// Load user parameters
$request->params($params);
}
// Sign the request using only the consumer, no token is available yet
$request->sign($this->signature, $consumer, $token);
// Create a response from the request
$response = $request->execute();
// Store this token somewhere useful
return OAuth_Token::factory('access', array(
'token' => $response->param('oauth_token'),
'secret' => $response->param('oauth_token_secret'),
));
}
} // End OAuth_Signature

View File

@ -1,55 +0,0 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* OAuth Google Provider
*
* Documents for implementing Google OAuth can be found at
* <http://code.google.com/apis/accounts/docs/OAuth.html>.
* Individual Google APIs have separate documentation. A complete list is
* available at <http://code.google.com/more/>.
*
* [!!] This class does not implement any Google API. It is only an
* implementation of standard OAuth with Google as the service provider.
*
* @package Kohana/OAuth
* @category Provider
* @author Kohana Team
* @copyright (c) 2010 Kohana Team
* @license http://kohanaframework.org/license
* @since 3.0.7
*/
class Kohana_OAuth_Provider_Google extends OAuth_Provider {
public $name = 'google';
protected $signature = 'HMAC-SHA1';
public function url_request_token()
{
return 'https://www.google.com/accounts/OAuthGetRequestToken';
}
public function url_authorize()
{
return 'https://www.google.com/accounts/OAuthAuthorizeToken';
}
public function url_access_token()
{
return 'https://www.google.com/accounts/OAuthGetAccessToken';
}
public function request_token(OAuth_Consumer $consumer, array $params = NULL)
{
if ( ! isset($params['scope']))
{
// All request tokens must specify the data scope to access
// http://code.google.com/apis/accounts/docs/OAuth.html#prepScope
throw new Kohana_OAuth_Exception('Required parameter to not passed: :param', array(
':param' => 'scope',
));
}
return parent::request_token($consumer, $params);
}
} // End OAuth_Provider_Google

View File

@ -1,39 +0,0 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* OAuth Twitter Provider
*
* Documents for implementing Twitter OAuth can be found at
* <http://dev.twitter.com/pages/auth>.
*
* [!!] This class does not implement the Twitter API. It is only an
* implementation of standard OAuth with Twitter as the service provider.
*
* @package Kohana/OAuth
* @category Provider
* @author Kohana Team
* @copyright (c) 2010 Kohana Team
* @license http://kohanaframework.org/license
* @since 3.0.7
*/
class Kohana_OAuth_Provider_Twitter extends OAuth_Provider {
public $name = 'twitter';
protected $signature = 'HMAC-SHA1';
public function url_request_token()
{
return 'https://api.twitter.com/oauth/request_token';
}
public function url_authorize()
{
return 'https://api.twitter.com/oauth/authenticate';
}
public function url_access_token()
{
return 'https://api.twitter.com/oauth/access_token';
}
} // End OAuth_Provider_Twitter

View File

@ -1,478 +0,0 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* OAuth Request
*
* @package Kohana/OAuth
* @category Request
* @author Kohana Team
* @copyright (c) 2010 Kohana Team
* @license http://kohanaframework.org/license
* @since 3.0.7
*/
class Kohana_OAuth_Request {
/**
* Create a new request object.
*
* $request = OAuth_Request::factory('token', 'http://example.com/oauth/request_token');
*
* @param string request type
* @param string request URL
* @param string request method
* @param array request parameters
* @return OAuth_Request
*/
public static function factory($type, $method, $url = NULL, array $params = NULL)
{
$class = 'OAuth_Request_'.$type;
return new $class($method, $url, $params);
}
/**
* @var integer connection timeout
*/
public $timeout = 10;
/**
* @var boolean send Authorization header?
*/
public $send_header = TRUE;
/**
* @var string request type name: token, authorize, access, resource
*/
protected $name;
/**
* @var string request method: GET, POST, etc
*/
protected $method = 'GET';
/**
* @var string request URL
*/
protected $url;
/**
* @var array request parameters
*/
protected $params = array();
/**
* @var array required parameters
*/
protected $required = array();
/**
* Set the request URL, method, and parameters.
*
* @param string request method
* @param string request URL
* @param array request parameters
* @uses OAuth::parse_url
*/
public function __construct($method, $url, array $params = NULL)
{
if ($method)
{
// Set the request method
$this->method = strtoupper($method);
}
// Separate the URL and query string, which will be used as additional
// default parameters
list ($url, $default) = OAuth::parse_url($url);
// Set the request URL
$this->url = $url;
if ($default)
{
// Set the default parameters
$this->params($default);
}
if ($params)
{
// Set the request parameters
$this->params($params);
}
if ($this->required('oauth_version') AND ! isset($this->params['oauth_version']))
{
// Set the version of this request
$this->params['oauth_version'] = OAuth::$version;
}
if ($this->required('oauth_timestamp') AND ! isset($this->params['oauth_timestamp']))
{
// Set the timestamp of this request
$this->params['oauth_timestamp'] = $this->timestamp();
}
if ($this->required('oauth_nonce') AND ! isset($this->params['oauth_nonce']))
{
// Set the unique nonce of this request
$this->params['oauth_nonce'] = $this->nonce();
}
}
/**
* Return the value of any protected class variable.
*
* // Get the request parameters
* $params = $request->params;
*
* // Get the request URL
* $url = $request->url;
*
* @param string variable name
* @return mixed
*/
public function __get($key)
{
return $this->$key;
}
/**
* Generates the UNIX timestamp for a request.
*
* $time = $request->timestamp();
*
* [!!] This method implements [OAuth 1.0 Spec 8](http://oauth.net/core/1.0/#rfc.section.8).
*
* @return integer
*/
public function timestamp()
{
return time();
}
/**
* Generates the nonce for a request.
*
* $nonce = $request->nonce();
*
* [!!] This method implements [OAuth 1.0 Spec 8](http://oauth.net/core/1.0/#rfc.section.8).
*
* @return string
* @uses Text::random
*/
public function nonce()
{
return Text::random('alnum', 40);
}
/**
* Get the base signature string for a request.
*
* $base = $request->base_string();
*
* [!!] This method implements [OAuth 1.0 Spec A5.1](http://oauth.net/core/1.0/#rfc.section.A.5.1).
*
* @param OAuth_Request request to sign
* @return string
* @uses OAuth::urlencode
* @uses OAuth::normalize_params
*/
public function base_string()
{
$url = $this->url;
// Get the request parameters
$params = $this->params;
// "oauth_signature" is never included in the base string!
unset($params['oauth_signature']);
// method & url & sorted-parameters
return implode('&', array(
$this->method,
OAuth::urlencode($url),
OAuth::urlencode(OAuth::normalize_params($params)),
));
}
/**
* Parameter getter and setter. Setting the value to `NULL` will remove it.
*
* // Set the "oauth_consumer_key" to a new value
* $request->param('oauth_consumer_key', $key);
*
* // Get the "oauth_consumer_key" value
* $key = $request->param('oauth_consumer_key');
*
* // Remove "oauth_consumer_key"
* $request->param('oauth_consumer_key', NULL);
*
* @param string parameter name
* @param mixed parameter value
* @param boolean allow duplicates?
* @return mixed when getting
* @return $this when setting
* @uses Arr::get
*/
public function param($name, $value = NULL, $duplicate = FALSE)
{
if (func_num_args() < 2)
{
// Get the parameter
return Arr::get($this->params, $name);
}
if ($value === NULL)
{
// Remove the parameter
unset($this->params[$name]);
}
else
{
if (isset($this->params[$name]) AND $duplicate)
{
if ( ! is_array($this->params[$name]))
{
// Convert the parameter into an array
$this->params[$name] = array($this->params[$name]);
}
// Add the duplicate value
$this->params[$name][] = $value;
}
else
{
// Set the parameter value
$this->params[$name] = $value;
}
}
return $this;
}
/**
* Set multiple parameters.
*
* $request->params($params);
*
* @param array parameters
* @param boolean allow duplicates?
* @return $this
* @uses OAuth_Request::param
*/
public function params(array $params, $duplicate = FALSE)
{
foreach ($params as $name => $value)
{
$this->param($name, $value, $duplicate);
}
return $this;
}
/**
* Get and set required parameters.
*
* $request->required($field, $value);
*
* @param string parameter name
* @param boolean field value
* @return boolean when getting
* @return $this when setting
*/
public function required($param, $value = NULL)
{
if ($value === NULL)
{
return ! empty($this->required[$param]);
}
// Change the requirement value
$this->required[$param] = (boolean) $value;
return $this;
}
/**
* Convert the request parameters into an `Authorization` header.
*
* $header = $request->as_header();
*
* [!!] This method implements [OAuth 1.0 Spec 5.4.1](http://oauth.net/core/1.0/#rfc.section.5.4.1).
*
* @return string
*/
public function as_header()
{
$header = array();
foreach ($this->params as $name => $value)
{
if (strpos($name, 'oauth_') === 0)
{
// OAuth Spec 5.4.1
// "Parameter names and values are encoded per Parameter Encoding [RFC 3986]."
$header[] = OAuth::urlencode($name).'="'.OAuth::urlencode($value).'"';
}
}
return 'OAuth '.implode(', ', $header);
}
/**
* Convert the request parameters into a query string, suitable for GET and
* POST requests.
*
* $query = $request->as_query();
*
* [!!] This method implements [OAuth 1.0 Spec 5.2 (2,3)](http://oauth.net/core/1.0/#rfc.section.5.2).
*
* @param boolean include oauth parameters
* @return string
*/
public function as_query($include_oauth = NULL)
{
if ($include_oauth !== TRUE AND $this->send_header)
{
// If we are sending a header, OAuth parameters should not be
// included in the query string.
$params = array();
foreach ($this->params as $name => $value)
{
if (strpos($name, 'oauth_') !== 0)
{
// This is not an OAuth parameter
$params[$name] = $value;
}
}
}
else
{
$params = $this->params;
}
return OAuth::normalize_params($params);
}
/**
* Return the entire request URL with the parameters as a GET string.
*
* $url = $request->as_url();
*
* @return string
* @uses OAuth_Request::as_query
*/
public function as_url()
{
return $this->url.'?'.$this->as_query(TRUE);
}
/**
* Sign the request, setting the `oauth_signature_method` and `oauth_signature`.
*
* @param OAuth_Signature signature
* @param OAuth_Consumer consumer
* @param OAuth_Token token
* @return $this
* @uses OAuth_Signature::sign
*/
public function sign(OAuth_Signature $signature, OAuth_Consumer $consumer, OAuth_Token $token = NULL)
{
// Create a new signature class from the method
$this->param('oauth_signature_method', $signature->name);
// Sign the request using the consumer and token
$this->param('oauth_signature', $signature->sign($this, $consumer, $token));
return $this;
}
/**
* Checks that all required request parameters have been set. Throws an
* exception if any parameters are missing.
*
* try
* {
* $request->check();
* }
* catch (OAuth_Exception $e)
* {
* // Request has missing parameters
* }
*
* @return TRUE
* @throws Kohana_OAuth_Exception
*/
public function check()
{
foreach ($this->required as $param => $required)
{
if ($required AND ! isset($this->params[$param]))
{
throw new Kohana_OAuth_Exception('Request to :url requires missing parameter ":param"', array(
':url' => $this->url,
':param' => $param,
));
}
}
return TRUE;
}
/**
* Execute the request and return a response.
*
* @param string request type: GET, POST, etc (NULL for header)
* @param array additional cURL options
* @return string request response body
* @uses OAuth_Request::check
* @uses Arr::get
* @uses Remote::get
*/
public function execute(array $options = NULL)
{
// Check that all required fields are set
$this->check();
// Get the URL of the request
$url = $this->url;
if ( ! isset($options[CURLOPT_CONNECTTIMEOUT]))
{
// Use the request default timeout
$options[CURLOPT_CONNECTTIMEOUT] = $this->timeout;
}
if ($this->send_header)
{
// Get the the current headers
$headers = Arr::get($options, CURLOPT_HTTPHEADER, array());
// Add the Authorization header
$headers[] = 'Authorization: '.$this->as_header();
// Store the new headers
$options[CURLOPT_HTTPHEADER] = $headers;
}
if ($this->method === 'POST')
{
// Send the request as a POST
$options[CURLOPT_POST] = TRUE;
if ($post = $this->as_query())
{
// Attach the post fields to the request
$options[CURLOPT_POSTFIELDS] = $post;
}
}
elseif ($query = $this->as_query())
{
// Append the parameters to the query string
$url = "{$url}?{$query}";
}
return Remote::get($url, $options);
}
} // End OAuth_Request

View File

@ -1,32 +0,0 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* OAuth Access Request
*
* @package Kohana/OAuth
* @category Request
* @author Kohana Team
* @copyright (c) 2010 Kohana Team
* @license http://kohanaframework.org/license
* @since 3.0.7
*/
class Kohana_OAuth_Request_Access extends OAuth_Request {
protected $name = 'access';
protected $required = array(
'oauth_consumer_key' => TRUE,
'oauth_token' => TRUE,
'oauth_signature_method' => TRUE,
'oauth_signature' => TRUE,
'oauth_timestamp' => TRUE,
'oauth_nonce' => TRUE,
'oauth_verifier' => TRUE,
'oauth_version' => TRUE,
);
public function execute(array $options = NULL)
{
return OAuth_Response::factory(parent::execute($options));
}
} // End OAuth_Request_Access

View File

@ -1,26 +0,0 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* OAuth Authorization Request
*
* @package Kohana/OAuth
* @category Request
* @author Kohana Team
* @copyright (c) 2010 Kohana Team
* @license http://kohanaframework.org/license
* @since 3.0.7
*/
class Kohana_OAuth_Request_Authorize extends OAuth_Request {
protected $name = 'request';
// http://oauth.net/core/1.0/#rfc.section.6.2.1
protected $required = array(
'oauth_token' => TRUE,
);
public function execute(array $options = NULL)
{
return Request::instance()->redirect($this->as_url());
}
} // End OAuth_Request_Authorize

View File

@ -1,16 +0,0 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* OAuth Credentials Request
*
* @package Kohana/OAuth
* @category Request
* @author Kohana Team
* @copyright (c) 2010 Kohana Team
* @license http://kohanaframework.org/license
* @since 3.0.7
*/
class Kohana_OAuth_Request_Credentials extends OAuth_Request {
} // End OAuth_Request_Credentials

View File

@ -1,27 +0,0 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* OAuth Resource Request
*
* @package Kohana/OAuth
* @category Request
* @author Kohana Team
* @copyright (c) 2010 Kohana Team
* @license http://kohanaframework.org/license
* @since 3.0.7
*/
class Kohana_OAuth_Request_Resource extends OAuth_Request {
protected $name = 'resource';
// http://oauth.net/core/1.0/#rfc.section.7
protected $required = array(
'oauth_consumer_key' => TRUE,
'oauth_token' => TRUE,
'oauth_signature_method' => TRUE,
'oauth_signature' => TRUE,
'oauth_timestamp' => TRUE,
'oauth_nonce' => TRUE,
'oauth_version' => TRUE,
);
} // End OAuth_Request_Resource

View File

@ -1,32 +0,0 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* OAuth Token Request
*
* @package Kohana/OAuth
* @category Request
* @author Kohana Team
* @copyright (c) 2010 Kohana Team
* @license http://kohanaframework.org/license
* @since 3.0.7
*/
class Kohana_OAuth_Request_Token extends OAuth_Request {
protected $name = 'request';
// http://oauth.net/core/1.0/#rfc.section.6.3.1
protected $required = array(
'oauth_callback' => TRUE,
'oauth_consumer_key' => TRUE,
'oauth_signature_method' => TRUE,
'oauth_signature' => TRUE,
'oauth_timestamp' => TRUE,
'oauth_nonce' => TRUE,
'oauth_version' => TRUE,
);
public function execute(array $options = NULL)
{
return OAuth_Response::factory(parent::execute($options));
}
} // End OAuth_Request_Token

View File

@ -1,51 +0,0 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* OAuth Response
*
* @package Kohana/OAuth
* @category Base
* @author Kohana Team
* @copyright (c) 2010 Kohana Team
* @license http://kohanaframework.org/license
* @since 3.0.7
*/
class Kohana_OAuth_Response {
public static function factory($body)
{
return new OAuth_Response($body);
}
/**
* @var array response parameters
*/
protected $params = array();
public function __construct($body = NULL)
{
if ($body)
{
$this->params = OAuth::parse_params($body);
}
}
/**
* Return the value of any protected class variable.
*
* // Get the response parameters
* $params = $response->params;
*
* @param string variable name
* @return mixed
*/
public function __get($key)
{
return $this->$key;
}
public function param($name, $default = NULL)
{
return Arr::get($this->params, $name, $default);
}
} // End OAuth_Response

View File

@ -1,14 +0,0 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* OAuth Server
*
* @package Kohana/OAuth
* @category Server
* @author Kohana Team
* @copyright (c) 2010 Kohana Team
* @license http://kohanaframework.org/license
* @since 3.0.7
*/
abstract class Kohana_OAuth_Server {
} // End OAuth_Server

View File

@ -1,77 +0,0 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* OAuth Signature
*
* @package Kohana/OAuth
* @category Signature
* @author Kohana Team
* @copyright (c) 2010 Kohana Team
* @license http://kohanaframework.org/license
* @since 3.0.7
*/
abstract class Kohana_OAuth_Signature {
/**
* Create a new signature object by name.
*
* $signature = OAuth_Signature::factory('HMAC-SHA1');
*
* @param string signature name: HMAC-SHA1, PLAINTEXT, etc
* @param array signature options
* @return OAuth_Signature
*/
public static function factory($name, array $options = NULL)
{
// Create the class name as a base of this class
$class = 'OAuth_Signature_'.str_replace('-', '_', $name);
return new $class($options);
}
/**
* @var string signature name: HMAC-SHA1, PLAINTEXT, etc
*/
protected $name;
/**
* Return the value of any protected class variables.
*
* $name = $signature->name;
*
* @param string variable name
* @return mixed
*/
public function __get($key)
{
return $this->$key;
}
/**
* Get a signing key from a consumer and token.
*
* $key = $signature->key($consumer, $token);
*
* [!!] This method implements the signing key of [OAuth 1.0 Spec 9](http://oauth.net/core/1.0/#rfc.section.9).
*
* @param OAuth_Consumer consumer
* @param OAuth_Token token
* @return string
* @uses OAuth::urlencode
*/
public function key(OAuth_Consumer $consumer, OAuth_Token $token = NULL)
{
$key = OAuth::urlencode($consumer->secret).'&';
if ($token)
{
$key .= OAuth::urlencode($token->secret);
}
return $key;
}
abstract public function sign(OAuth_Request $request, OAuth_Consumer $consumer, OAuth_Token $token = NULL);
abstract public function verify($signature, OAuth_Request $request, OAuth_Consumer $consumer, OAuth_Token $token = NULL);
} // End OAuth_Signature

View File

@ -1,68 +0,0 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* The HMAC-SHA1 signature provides secure signing using the HMAC-SHA1
* algorithm as defined by [RFC2104](http://tools.ietf.org/html/rfc2104).
* It uses [OAuth_Request::base_string] as the text and [OAuth_Signature::key]
* as the signing key.
*
* @package Kohana/OAuth
* @category Signature
* @author Kohana Team
* @copyright (c) 2010 Kohana Team
* @license http://kohanaframework.org/license
* @since 3.0.7
*/
class Kohana_OAuth_Signature_HMAC_SHA1 extends OAuth_Signature {
protected $name = 'HMAC-SHA1';
/**
* Generate a signed hash of the base string using the consumer and token
* as the signing key.
*
* $sig = $signature->sign($request, $consumer, $token);
*
* [!!] This method implements [OAuth 1.0 Spec 9.2.1](http://oauth.net/core/1.0/#rfc.section.9.2.1).
*
* @param OAuth_Request request
* @param OAuth_Consumer consumer
* @param OAuth_Token token
* @return string
* @uses OAuth_Signature::key
* @uses OAuth_Request::base_string
*/
public function sign(OAuth_Request $request, OAuth_Consumer $consumer, OAuth_Token $token = NULL)
{
// Get the signing key
$key = $this->key($consumer, $token);
// Get the base string for the signature
$base_string = $request->base_string();
// Sign the base string using the key
return base64_encode(hash_hmac('sha1', $base_string, $key, TRUE));
}
/**
* Verify a HMAC-SHA1 signature.
*
* if ( ! $signature->verify($signature, $request, $consumer, $token))
* {
* throw new Kohana_OAuth_Exception('Failed to verify signature');
* }
*
* [!!] This method implements [OAuth 1.0 Spec 9.2.2](http://oauth.net/core/1.0/#rfc.section.9.2.2).
*
* @param string signature to verify
* @param OAuth_Request request
* @param OAuth_Consumer consumer
* @param OAuth_Token token
* @return boolean
* @uses OAuth_Signature_HMAC_SHA1::sign
*/
public function verify($signature, OAuth_Request $request, OAuth_Consumer $consumer, OAuth_Token $token = NULL)
{
return $signature === $this->sign($request, $consumer, $token);
}
} // End OAuth_Signature_HMAC_SHA1

Some files were not shown because too many files have changed in this diff Show More