Update Kohana to 3.1.3.1

This commit is contained in:
Deon George 2011-05-16 22:47:16 +10:00
parent 8b658b497a
commit ff2370c368
401 changed files with 14070 additions and 10213 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,27 +87,33 @@ 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.
*/
Kohana::modules(array(
'auth' => MODPATH.'auth', // Basic authentication
// 'cache' => MODPATH.'cache', // Caching with multiple backends
// 'codebench' => MODPATH.'codebench', // Benchmarking tool
'database' => MODPATH.'database', // Database access
// 'image' => MODPATH.'image', // Image manipulation
'orm' => MODPATH.'orm', // Object Relationship Mapping
// 'oauth' => MODPATH.'oauth', // OAuth authentication
// 'pagination' => MODPATH.'pagination', // Paging of results
// 'unittest' => MODPATH.'unittest', // Unit testing
'userguide' => MODPATH.'userguide', // User guide and API documentation
'auth' => SMDPATH.'auth', // Basic authentication
'cache' => SMDPATH.'cache', // Caching with multiple backends
// 'codebench' => SMDPATH.'codebench', // Benchmarking tool
'database' => SMDPATH.'database', // Database access
// 'image' => SMDPATH.'image', // Image manipulation
'orm' => SMDPATH.'orm', // Object Relationship Mapping
// 'unittest' => SMDPATH.'unittest', // Unit testing
// 'userguide' => SMDPATH.'userguide', // User guide and API documentation
));
/**
* Enable specalised interfaces
*/
Route::set('sections', '<directory>/<controller>(/<action>(/<id>(/<sid>)))',
array(
'directory' => '('.implode('|',Kohana::config('config.method_directory')).')'
));
/**
@ -97,15 +133,4 @@ Route::set('default/media', 'media(/<file>)', array('file' => '.+'))
'action' => 'media',
'file' => NULL,
));
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

@ -0,0 +1,118 @@
<?php defined('SYSPATH') or die('No direct script access.');
// -- 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.
*
* @see http://kohanaframework.org/guide/using.configuration
* @see http://php.net/timezones
*/
date_default_timezone_set('America/Chicago');
/**
* Set the default locale.
*
* @see http://kohanaframework.org/guide/using.configuration
* @see http://php.net/setlocale
*/
setlocale(LC_ALL, 'en_US.utf-8');
/**
* Enable the Kohana auto-loader.
*
* @see http://kohanaframework.org/guide/using.autoloading
* @see http://php.net/spl_autoload_register
*/
spl_autoload_register(array('Kohana', 'auto_load'));
/**
* Enable the Kohana auto-loader for unserialization.
*
* @see http://php.net/spl_autoload_call
* @see http://php.net/manual/var.configuration.php#unserialize-callback-func
*/
ini_set('unserialize_callback_func', 'spl_autoload_call');
// -- 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.
*
* The following options are available:
*
* - string base_url path, and optionally domain, of your application NULL
* - string index_file name of your index file, usually "index.php" index.php
* - string charset internal character set used for input and output utf-8
* - string cache_dir set the internal cache directory APPPATH/cache
* - boolean errors enable or disable error handling TRUE
* - boolean profile enable or disable internal profiling TRUE
* - boolean caching enable or disable internal caching FALSE
*/
Kohana::init(array(
'base_url' => '/',
));
/**
* Attach the file write to logging. Multiple writers are supported.
*/
Kohana::$log->attach(new Log_File(APPPATH.'logs'));
/**
* Attach a file reader to config. Multiple readers are supported.
*/
Kohana::$config->attach(new Config_File);
/**
* Enable modules. Modules are referenced by a relative or absolute path.
*/
Kohana::modules(array(
// 'auth' => MODPATH.'auth', // Basic authentication
// 'cache' => MODPATH.'cache', // Caching with multiple backends
// 'codebench' => MODPATH.'codebench', // Benchmarking tool
// 'database' => MODPATH.'database', // Database access
// 'image' => MODPATH.'image', // Image manipulation
// 'orm' => MODPATH.'orm', // Object Relationship Mapping
// 'unittest' => MODPATH.'unittest', // Unit testing
// 'userguide' => MODPATH.'userguide', // User guide and API documentation
));
/**
* Set the routes. Each route must have a minimum of a name, a URI and a set of
* defaults for the URI.
*/
Route::set('default', '(<controller>(/<action>(/<id>)))')
->defaults(array(
'controller' => 'welcome',
'action' => 'index',
));

View File

@ -0,0 +1,10 @@
<?php defined('SYSPATH') or die('No direct script access.');
class Controller_Welcome extends Controller {
public function action_index()
{
$this->response->body('hello, world!');
}
} // End Welcome

View File

@ -1,233 +0,0 @@
<project name="kohana" default="help" basedir=".">
<property environment="env"/>
<property name="basedir" value="${project.basedir}"/>
<property name="builddir" value="${basedir}/build"/>
<property name="repo" value="git@github.com:kohana/kohana.git"/>
<property name="branch" value="3.1.x"/>
<property name="tag" value="${env.TAG}"/>
<property name="submodules" value="system,modules/auth,modules/cache,modules/codebench,modules/database,modules/image,modules/oauth,modules/orm,modules/pagination,modules/unittest,modules/userguide"/>
<property name="release-excludes" value="**/.git,**/.git*,build.xml,phpunit.xml,DEVELOPERS.md,phpunitcc,code_coverage.xml,release-tag,TESTING.md,**/tests"/>
<!-- Shows the help message -->
<target name="help">
<echo message="General Targets"/>
<echo message="==============="/>
<echo message="phing test Run unit tests."/>
<echo message="phing test-log Run unit tests with logging enabled."/>
<echo message="phing phpcs Run phpcs."/>
<echo message="phing phpcs-log Run phpcs with logging enabled."/>
<echo message="phing phpmd Run phpmd."/>
<echo message="phing phpmd-log Run phpmd with logging enabled."/>
<echo message="phing phpcpd-log Run phpcpd with logging enabled."/>
<echo message="phing pdepend-log Run pdepend with logging enabled."/>
<echo message="phing phpcb-log Run phpcb with logging enabled."/>
<echo message=""/>
<echo message="Kohana Developer Targets"/>
<echo message="========================"/>
<echo message="phing dev-setup Setup for development on Kohana itself."/>
<echo message="phing git-status Show the git status of each submodule."/>
<echo message="phing git-checkout Checkout a branch accross all submodules."/>
<echo message="phing git-pull Perform a pull for each submodule."/>
<echo message="phing git-push Perform a push for each submodule."/>
<echo message=""/>
<echo message="Misc Targets"/>
<echo message="============"/>
<echo message="phing ci Alias task for continuous integration servers"/>
</target>
<!-- Clean up -->
<target name="clean">
<delete dir="${builddir}"/>
<!-- Create build directories -->
<mkdir dir="${builddir}/coverage"/>
<mkdir dir="${builddir}/logs"/>
<mkdir dir="${builddir}/release"/>
<mkdir dir="${builddir}/code-browser"/>
</target>
<target name="dev-setup">
<property name="git-checkout-branch" value="${branch}"/> <!-- Prevents git-checkout asking for a branch name -->
<exec command="git submodule update --init --recursive" dir="${basedir}" />
<phingcall target="_dev-setup-remotes" />
<phingcall target="git-pull" />
<phingcall target="git-checkout" />
</target>
<target name="git-pull">
<phingcall target="_git-pull">
<property name="dir" value="." />
</phingcall>
<foreach list="${submodules}" param="dir" target="_git-pull"/>
</target>
<target name="_git-pull">
<exec command="git pull dev" dir="${dir}"/>
</target>
<target name="git-checkout">
<if>
<not>
<isset property="git-checkout-branch"/>
</not>
<then>
<propertyprompt propertyName="git-checkout-branch" defaultValue="${branch}" promptText="Branch name:" />
</then>
</if>
<phingcall target="_git-checkout">
<property name="dir" value="." />
</phingcall>
<foreach list="${submodules}" param="dir" target="_git-checkout"/>
</target>
<target name="_git-checkout">
<exec returnProperty="git-checkout-branch-exists" command="git show-ref --quiet --verify -- 'refs/remotes/dev/${git-checkout-branch}'" dir="${dir}" passthru="true"/>
<if>
<equals arg1="${git-checkout-branch-exists}" arg2="0"/>
<then>
<exec command="git checkout --track -b ${git-checkout-branch} dev/${git-checkout-branch}" dir="${dir}" passthru="true"/>
</then>
<else>
<exec command="git checkout -b ${git-checkout-branch}" dir="${dir}" passthru="true"/>
</else>
</if>
</target>
<target name="git-push">
<foreach list="${submodules}" param="dir" target="_git-push"/>
<phingcall target="_git-push">
<property name="dir" value="." />
</phingcall>
</target>
<target name="_git-push">
<exec command="git push dev" dir="${dir}" passthru="true"/>
</target>
<target name="git-status">
<foreach list="${submodules}" param="dir" target="_git-status"/>
<phingcall target="_git-status">
<property name="dir" value="." />
</phingcall>
</target>
<target name="_git-status">
<exec command="git status" dir="${dir}" passthru="true"/>
</target>
<target name="_dev-setup-remotes">
<!-- TODO: Clean up... -->
<phingcall target="_dev-setup-remote">
<property name="repository" value="git@github.com:kohana/kohana.git" />
<property name="dir" value="${basedir}" />
</phingcall>
<phingcall target="_dev-setup-remote">
<property name="repository" value="git@github.com:kohana/core.git" />
<property name="dir" value="${basedir}/system" />
</phingcall>
<phingcall target="_dev-setup-remote">
<property name="repository" value="git@github.com:kohana/auth.git" />
<property name="dir" value="${basedir}/modules/auth" />
</phingcall>
<phingcall target="_dev-setup-remote">
<property name="repository" value="git@github.com:kohana/cache.git" />
<property name="dir" value="${basedir}/modules/cache" />
</phingcall>
<phingcall target="_dev-setup-remote">
<property name="repository" value="git@github.com:kohana/codebench.git" />
<property name="dir" value="${basedir}/modules/codebench" />
</phingcall>
<phingcall target="_dev-setup-remote">
<property name="repository" value="git@github.com:kohana/database.git" />
<property name="dir" value="${basedir}/modules/database" />
</phingcall>
<phingcall target="_dev-setup-remote">
<property name="repository" value="git@github.com:kohana/image.git" />
<property name="dir" value="${basedir}/modules/image" />
</phingcall>
<phingcall target="_dev-setup-remote">
<property name="repository" value="git@github.com:kohana/oauth.git" />
<property name="dir" value="${basedir}/modules/oauth" />
</phingcall>
<phingcall target="_dev-setup-remote">
<property name="repository" value="git@github.com:kohana/orm.git" />
<property name="dir" value="${basedir}/modules/orm" />
</phingcall>
<phingcall target="_dev-setup-remote">
<property name="repository" value="git@github.com:kohana/pagination.git" />
<property name="dir" value="${basedir}/modules/pagination" />
</phingcall>
<phingcall target="_dev-setup-remote">
<property name="repository" value="git@github.com:kohana/unittest.git" />
<property name="dir" value="${basedir}/modules/unittest" />
</phingcall>
<phingcall target="_dev-setup-remote">
<property name="repository" value="git@github.com:kohana/userguide.git" />
<property name="dir" value="${basedir}/modules/userguide" />
</phingcall>
</target>
<target name="_dev-setup-remote">
<exec command="git remote rm dev" dir="${dir}"/>
<exec command="git remote add dev ${repository}" dir="${dir}"/>
</target>
<!-- Run unit tests -->
<target name="test">
<exec command="phpunit --bootstrap=application/test_bootstrap.php modules/unittest/tests.php" checkreturn="true" passthru="true"/>
</target>
<!-- Run unit tests and generate junit.xml and clover.xml -->
<target name="test-log">
<exec command="phpunit --bootstrap=application/test_bootstrap.php --coverage-html=${builddir}/coverage --log-junit=${builddir}/logs/junit.xml --coverage-clover=${builddir}/logs/clover.xml modules/unittest/tests.php" checkreturn="true" passthru="true"/>
</target>
<!-- Run PHP Code Sniffer -->
<target name="phpcs">
<exec command="phpcs --standard=Kohana --ignore=*.js,*.css,**/vendor/**,**/tests/** ${basedir}" passthru="true"/>
</target>
<!-- Run PHP Code Sniffer and generate checkstyle.xml -->
<target name="phpcs-log">
<exec command="phpcs --standard=Kohana --ignore=*.js,*.css,**/vendor/**,**/tests/** --report=checkstyle --report-file=${builddir}/logs/checkstyle.xml ${basedir}" passthru="false"/>
</target>
<!-- Run PHP Mess Detector -->
<target name="phpmd">
<exec command="phpmd ${basedir} text codesize,unusedcode --exclude=**/vendor/**" passthru="true"/>
</target>
<!-- Run PHP Mess Detector and generate pmd.xml -->
<target name="phpmd-log">
<exec command="phpmd ${basedir} xml codesize,unusedcode --exclude=**/vendor/** --reportfile ${builddir}/logs/pmd.xml" passthru="true"/>
</target>
<!-- Run PHP Copy/Paste Detector and generate pmd.xml -->
<target name="phpcpd-log">
<exec command="phpcpd --log-pmd ${builddir}/logs/pmd-cpd.xml ${basedir}" passthru="true"/>
</target>
<!-- Run PHP Depend and generate jdepend.xml -->
<target name="pdepend-log">
<exec command="pdepend --jdepend-xml=${builddir}/logs/jdepend.xml ${basedir}" passthru="true"/>
</target>
<!-- Run PHP CodeBrowser and generate output -->
<target name="phpcb-log">
<exec command="phpcb --log ${builddir}/logs --source ${basedir} --output ${builddir}/code-browser" passthru="true"/>
</target>
<!-- Hudson CI target -->
<target name="ci" depends="clean">
<phingcall target="test-log"/>
<phingcall target="pdepend-log"/>
<phingcall target="phpmd-log"/>
<phingcall target="phpcpd-log"/>
<phingcall target="phpcs-log"/>
<phingcall target="phpcb-log"/>
</target>
</project>

View File

@ -1,21 +0,0 @@
# Turn on URL rewriting
RewriteEngine On
# Installation directory
RewriteBase /
# Protect hidden files from being viewed
<Files .*>
Order Deny,Allow
Deny From All
</Files>
# Protect application and system files from being viewed
RewriteRule ^(?:application|modules|system)\b.* index.php/$0 [L]
# Allow any files or directories that exist to be displayed directly
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
# Rewrite all other URLs to index.php/URL
RewriteRule .* index.php/$0 [PT]

View File

@ -1,20 +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!');
if (version_compare(PHP_VERSION, '5.3', '<'))
{
// Clear out the cache to prevent errors. This typically happens on Windows/FastCGI.
clearstatcache();
}
else
{
// Clearing the realpath() cache is only possible PHP 5.3+
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">
@ -179,12 +163,20 @@ else
</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>
@ -203,6 +195,14 @@ else
<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

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

@ -1,27 +0,0 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* Default auth role
*
* @package Kohana/Auth
* @author Kohana Team
* @copyright (c) 2007-2010 Kohana Team
* @license http://kohanaframework.org/license
*/
class Model_Auth_Role extends ORM {
// Relationships
protected $_has_many = array('users' => array('through' => 'roles_users'));
// Validation rules
protected $_rules = array(
'name' => array(
'not_empty' => NULL,
'min_length' => array(4),
'max_length' => array(32),
),
'description' => array(
'max_length' => array(255),
),
);
} // End Auth Role Model

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-2010 Kohana Team
* @license http://kohanaframework.org/license
*/
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

@ -2,17 +2,15 @@
return array(
'driver' => 'ORM',
'hash_method' => 'sha1',
'salt_pattern' => '1, 3, 5, 9, 14, 15, 20, 21, 28, 30',
'lifetime' => 1209600,
'session_key' => 'auth_user',
'autologin_key' => 'auth_autologin',
'forced_key' => 'auth_forced',
'driver' => 'file',
'hash_method' => 'sha256',
'hash_key' => NULL,
'lifetime' => 1209600,
'session_key' => 'auth_user',
// Username/password combinations for the Auth File driver
'users' => array(
// 'admin' => 'b3154acf3a344170077d11bdb5fff31532f679a1919e716a02',
),
);
);

View File

@ -1,23 +0,0 @@
<?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'
'auth' => array(
// Whether this modules userguide pages should be shown
'enabled' => TRUE,
// The name that should show up on the userguide index page
'name' => 'Auth',
// A short description of this module, shown on the index page
'description' => 'User authentication and authorization.',
// Copyright message, shown in the footer for this module
'copyright' => '&copy; 20082010 Kohana Team',
)
)
);

View File

@ -138,7 +138,7 @@ abstract class Kohana_Cache {
}
/**
* @var Kohana_Config
* @var Config
*/
protected $_config;

View File

@ -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;
}
/**

View File

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

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

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

Before

Width:  |  Height:  |  Size: 13 KiB

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

@ -158,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].
@ -196,51 +202,47 @@ abstract class Kohana_Database {
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();
*
* @deprecated since v3.0.9
* @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.
@ -254,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');
@ -471,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.
*
@ -481,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.
@ -523,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

@ -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();
}
}
}
@ -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))
@ -368,8 +421,8 @@ class Kohana_Database_MySQL extends Database {
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

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

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)
@ -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))
@ -163,6 +161,30 @@ 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',

View File

@ -14,7 +14,7 @@ class Kohana_Database_Query {
protected $_type;
// Cache lifetime
protected $_lifetime;
protected $_lifetime = NULL;
// SQL statement
protected $_sql;
@ -55,7 +55,7 @@ class Kohana_Database_Query {
}
catch (Exception $e)
{
return Kohana::exception_text($e);
return Kohana_Exception::text($e);
}
}
@ -72,7 +72,7 @@ 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
*/
@ -209,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.'")';

View File

@ -95,37 +95,31 @@ 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_identifier($column);
$column = $db->quote_column($column);
}
// Append the statement to the query
@ -155,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);
@ -192,7 +186,7 @@ abstract class Kohana_Database_Query_Builder extends Database_Query {
if ($column)
{
// Quote the column, if it has a value
$column = $db->quote_identifier($column);
$column = $db->quote_column($column);
}
$sort[] = trim($column.' '.$direction);

View File

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

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

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

@ -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;
@ -139,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.
*
@ -266,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 ..."
*
@ -287,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');
@ -310,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))
@ -334,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))
@ -360,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()
@ -372,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;
@ -382,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

@ -109,7 +109,9 @@ class Kohana_Database_Query_Builder_Update extends Database_Query_Builder_Where
$query .= ' LIMIT '.$this->_limit;
}
return $query;
$this->_sql = $query;
return parent::compile($db);
}
public function reset()
@ -123,6 +125,8 @@ class Kohana_Database_Query_Builder_Update extends Database_Query_Builder_Where
$this->_parameters = array();
$this->_sql = NULL;
return $this;
}

View File

@ -45,7 +45,7 @@ class Kohana_Database_Result_Cached extends Database_Result {
public function current()
{
// Return an array of the row
return $this->valid() ? $this->_result[$this->_current_row] : FALSE;
return $this->valid() ? $this->_result[$this->_current_row] : NULL;
}
} // End Database_Result_Cached

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

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

@ -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,509 +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 upload parameters
*/
protected $upload = 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 = array_diff_key($this->params, $this->upload);
// "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');
*
* @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 ($value === NULL)
{
// Get the parameter
return Arr::get($this->params, $name);
}
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;
}
/**
* Upload getter and setter. Setting the value to `NULL` will remove it.
*
* // Set the "image" file path for uploading
* $request->upload('image', $file_path);
*
* // Get the "image" file path
* $key = $request->param('oauth_consumer_key');
*
* @param string upload name
* @param mixed upload file path
* @return mixed when getting
* @return $this when setting
* @uses OAuth_Request::param
*/
public function upload($name, $value = NULL)
{
if ($value !== NULL)
{
// This is an upload parameter
$this->upload[$name] = TRUE;
// Get the mime type of the image
$mime = File::mime($value);
// Format the image path for CURL
$value = "@{$value};type={$mime}";
}
return $this->param($name, $value, FALSE);
}
/**
* 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)
{
// Get the current status
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?
* @param boolean return a normalized string?
* @return string
*/
public function as_query($include_oauth = NULL, $as_string = TRUE)
{
if ($include_oauth === NULL)
{
// If we are sending a header, OAuth parameters should not be
// included in the query string.
$include_oauth = ! $this->send_header;
}
if ($include_oauth)
{
$params = $this->params;
}
else
{
$params = array();
foreach ($this->params as $name => $value)
{
if (strpos($name, 'oauth_') !== 0)
{
// This is not an OAuth parameter
$params[$name] = $value;
}
}
}
return $as_string ? OAuth::normalize_params($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 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(NULL, empty($this->upload)))
{
// 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

View File

@ -1,57 +0,0 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* The PLAINTEXT signature does not provide any security protection and should
* only be used over a secure channel such as HTTPS.
*
* @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_PLAINTEXT extends OAuth_Signature {
protected $name = 'PLAINTEXT';
/**
* Generate a plaintext signature for the request _without_ the base string.
*
* $sig = $signature->sign($request, $consumer, $token);
*
* [!!] This method implements [OAuth 1.0 Spec 9.4.1](http://oauth.net/core/1.0/#rfc.section.9.4.1).
*
* @param OAuth_Request request
* @param OAuth_Consumer consumer
* @param OAuth_Token token
* @return $this
*/
public function sign(OAuth_Request $request, OAuth_Consumer $consumer, OAuth_Token $token = NULL)
{
// Use the signing key as the signature
return $this->key($consumer, $token);
}
/**
* Verify a plaintext 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.4.2](http://oauth.net/core/1.0/#rfc.section.9.4.2).
*
* @param string signature to verify
* @param OAuth_Request request
* @param OAuth_Consumer consumer
* @param OAuth_Token token
* @return boolean
* @uses OAuth_Signature_PLAINTEXT::sign
*/
public function verify($signature, OAuth_Request $request, OAuth_Consumer $consumer, OAuth_Token $token = NULL)
{
return $signature === $this->key($consumer, $token);
}
} // End OAuth_Signature_PLAINTEXT

View File

@ -1,94 +0,0 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* OAuth Token
*
* @package Kohana/OAuth
* @category Token
* @author Kohana Team
* @copyright (c) 2010 Kohana Team
* @license http://kohanaframework.org/license
* @since 3.0.7
*/
abstract class Kohana_OAuth_Token {
/**
* Create a new token object.
*
* $token = OAuth_Token::factory($name);
*
* @param string token type
* @param array token options
* @return OAuth_Token
*/
public static function factory($name, array $options = NULL)
{
$class = 'OAuth_Token_'.$name;
return new $class($options);
}
/**
* @var string token type name: request, access
*/
protected $name;
/**
* @var string token key
*/
protected $token;
/**
* @var string token secret
*/
protected $secret;
/**
* Sets the token and secret values.
*
* @param array token options
* @return void
*/
public function __construct(array $options = NULL)
{
if ( ! isset($options['token']))
{
throw new Kohana_OAuth_Exception('Required option not passed: :option',
array(':option' => 'token'));
}
if ( ! isset($options['secret']))
{
throw new Kohana_OAuth_Exception('Required option not passed: :option',
array(':option' => 'secret'));
}
$this->token = $options['token'];
$this->secret = $options['secret'];
}
/**
* Return the value of any protected class variable.
*
* // Get the token secret
* $secret = $token->secret;
*
* @param string variable name
* @return mixed
*/
public function __get($key)
{
return $this->$key;
}
/**
* Returns the token key.
*
* @return string
*/
public function __toString()
{
return (string) $this->token;
}
} // End OAuth_Token

View File

@ -1,16 +0,0 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* OAuth Access Token
*
* @package Kohana/OAuth
* @category Token
* @author Kohana Team
* @copyright (c) 2010 Kohana Team
* @license http://kohanaframework.org/license
* @since 3.0.7
*/
class Kohana_OAuth_Token_Access extends OAuth_Token {
protected $name = 'access';
} // End OAuth_Token_Access

View File

@ -1,36 +0,0 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* OAuth Request Token
*
* @package Kohana/OAuth
* @category Token
* @author Kohana Team
* @copyright (c) 2010 Kohana Team
* @license http://kohanaframework.org/license
* @since 3.0.7
*/
class Kohana_OAuth_Token_Request extends OAuth_Token {
protected $name = 'request';
/**
* @var string request token verifier
*/
protected $verifier;
/**
* Change the token verifier.
*
* $token->verifier($key);
*
* @param string new verifier
* @return $this
*/
public function verifier($verifier)
{
$this->verifier = $verifier;
return $this;
}
} // End OAuth_Token_Request

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,15 +0,0 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* Configuration for OAuth providers.
*/
return array(
/**
* Twitter applications can be registered at https://twitter.com/apps.
* You will be given a "consumer key" and "consumer secret", which must
* be provided when making OAuth requests.
*/
'twitter' => array(
'key' => 'your consumer key',
'secret' => 'your consumer secret'
),
);

View File

@ -1,23 +0,0 @@
<?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'
'oauth' => array(
// Whether this modules userguide pages should be shown
'enabled' => TRUE,
// The name that should show up on the userguide index page
'name' => 'OAuth',
// A short description of this module, shown on the index page
'description' => 'Open protocol authorization.',
// Copyright message, shown in the footer for this module
'copyright' => '&copy; 20082010 Kohana Team',
)
)
);

View File

@ -1,24 +0,0 @@
# OAuth Configuration
All configuration for OAuth is done in the `config/oauth.php` file. The configuration file is organized by the provider name.
## Example Configuration File
return array(
/**
* Twitter application registration: https://twitter.com/apps
*/
'twitter' => array(
'key' => 'your consumer key',
'secret' => 'your consumer secret'
),
/**
* Google application registration: https://www.google.com/accounts/ManageDomains
*/
'google' => array(
'key' => 'your domain name',
'secret' => 'your consumer secret'
),
);
[!!] The consumer key and secret **must** be defined for all providers.

View File

@ -1,14 +0,0 @@
# About OAuth
[OAuth](http://oauth.net/) is an open protocol to allow secure API authorization in a simple and standard method from desktop and web applications. This module provides a pure PHP implementation of the OAuth v1.0 protocol, with support for PLAINTEXT and HMAC-SHA1 signatures.
## Supported Providers
The following providers are available by default:
* [Twitter](http://twitter.com/) using [OAuth_Provider_Twitter]
* [Google](http://www.google.com/) using [OAuth_Provider_Google]
Additional providers can be created by creating an extension of [OAuth_Provider].

View File

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

View File

@ -1,3 +0,0 @@
# OAuth Usage
[!!] This is a stub. Please see [controller/oauth source](http://github.com/kohana/oauth/tree/206c20033e209f233c9e7dc72836bfb786bd7e34/classes/controller) for now. The methods [OAuth_Provider_Google::user_profile] and [OAuth_Provider_Twitter::update_status] no longer exist in the current git master branch.

View File

@ -1,15 +0,0 @@
<?php echo Form::open() ?>
<dl>
<dt><?php echo Form::label('tweet', 'Update Twitter Status?') ?></dt>
<dd><?php echo Form::textarea('tweet', $tweet) ?></dd>
</dl>
<?php echo Form::submit(NULL, 'Send') ?>
<?php echo Form::close() ?>
<?php if ($response): ?>
<p>Response from Twitter:</p>
<?php echo $response ?>
<?php endif ?>

View File

@ -20,7 +20,7 @@ CREATE TABLE IF NOT EXISTS `users` (
`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`email` varchar(127) NOT NULL,
`username` varchar(32) NOT NULL DEFAULT '',
`password` char(50) NOT NULL,
`password` varchar(64) NOT NULL,
`logins` int(10) UNSIGNED NOT NULL DEFAULT '0',
`last_login` int(10) UNSIGNED,
PRIMARY KEY (`id`),
@ -32,7 +32,8 @@ CREATE TABLE IF NOT EXISTS `user_tokens` (
`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`user_id` int(11) UNSIGNED NOT NULL,
`user_agent` varchar(40) NOT NULL,
`token` varchar(32) NOT NULL,
`token` varchar(40) NOT NULL,
`type` varchar(100) NOT NULL,
`created` int(10) UNSIGNED NOT NULL,
`expires` int(10) UNSIGNED NOT NULL,
PRIMARY KEY (`id`),
@ -45,4 +46,4 @@ ALTER TABLE `roles_users`
ADD CONSTRAINT `roles_users_ibfk_2` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`) ON DELETE CASCADE;
ALTER TABLE `user_tokens`
ADD CONSTRAINT `user_tokens_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE;
ADD CONSTRAINT `user_tokens_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE;

View File

@ -18,7 +18,7 @@ CREATE TABLE users
id serial,
email varchar(318) NOT NULL,
username varchar(32) NOT NULL,
"password" varchar(50) NOT NULL,
"password" varchar(64) NOT NULL,
logins integer NOT NULL DEFAULT 0,
last_login integer,
CONSTRAINT users_id_pkey PRIMARY KEY (id),
@ -50,4 +50,4 @@ ALTER TABLE user_tokens
ADD CONSTRAINT user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
INSERT INTO roles (name, description) VALUES ('login', 'Login privileges, granted after account confirmation');
INSERT INTO roles (name, description) VALUES ('admin', 'Administrative user, has access to everything.');
INSERT INTO roles (name, description) VALUES ('admin', 'Administrative user, has access to everything.');

View File

@ -4,7 +4,7 @@
*
* @package Kohana/Auth
* @author Kohana Team
* @copyright (c) 2007-2010 Kohana Team
* @copyright (c) 2007-2011 Kohana Team
* @license http://kohanaframework.org/license
*/
class Kohana_Auth_ORM extends Auth {
@ -12,71 +12,49 @@ class Kohana_Auth_ORM extends Auth {
/**
* Checks if a session is active.
*
* @param mixed role name string, role ORM object, or array with role names
* @param boolean check user for every role applied (TRUE, by default) or if any?
* @param mixed $role Role name string, role ORM object, or array with role names
* @return boolean
*/
public function logged_in($role = NULL, $all_required = TRUE)
public function logged_in($role = NULL)
{
$status = FALSE;
// Get the user from the session
$user = $this->get_user();
if (is_object($user) AND $user instanceof Model_User AND $user->loaded())
if ( ! $user)
return FALSE;
if ($user instanceof Model_User AND $user->loaded())
{
// Everything is okay so far
$status = TRUE;
// If we don't have a roll no further checking is needed
if ( ! $role)
return TRUE;
if ( ! empty($role))
if (is_array($role))
{
// Multiple roles to check
if (is_array($role))
// Get all the roles
$roles = ORM::factory('role')
->where('name', 'IN', $role)
->find_all()
->as_array(NULL, 'id');
// Make sure all the roles are valid ones
if (count($roles) !== count($role))
return FALSE;
}
else
{
if ( ! is_object($role))
{
// set initial status
$status = (bool) $all_required;
// Load the role
$roles = ORM::factory('role', array('name' => $role));
// Check each role
foreach ($role as $_role)
{
if ( ! is_object($_role))
{
$_role = ORM::factory('role', array('name' => $_role));
}
// If the user doesn't have the role
if ( ! $user->has('roles', $_role))
{
// Set the status false and get outta here
$status = FALSE;
if ($all_required)
{
break;
}
}
elseif ( ! $all_required )
{
$status = TRUE;
break;
}
}
}
// Single role to check
else
{
if ( ! is_object($role))
{
// Load the role
$role = ORM::factory('role', array('name' => $role));
}
// Check that the user has the given role
$status = $user->has('roles', $role);
if ( ! $roles->loaded())
return FALSE;
}
}
}
return $status;
return $user->has('roles', $roles);
}
}
/**
@ -103,7 +81,20 @@ class Kohana_Auth_ORM extends Auth {
{
if ($remember === TRUE)
{
$this->remember($user);
// Token data
$data = array(
'user_id' => $user->id,
'expires' => time() + $this->_config['lifetime'],
'user_agent' => sha1(Request::$user_agent),
);
// Create a new autologin token
$token = ORM::factory('user_token')
->values($data)
->create();
// Set the autologin cookie
Cookie::set('authautologin', $token->token, $this->_config['lifetime']);
}
// Finish the login
@ -137,7 +128,7 @@ class Kohana_Auth_ORM extends Auth {
if ($mark_session_as_forced === TRUE)
{
// Mark the session as forced, to prevent users from changing account information
$this->_session->set($this->_config['forced_key'], TRUE);
$this->_session->set('auth_forced', TRUE);
}
// Run the standard completion
@ -151,7 +142,7 @@ class Kohana_Auth_ORM extends Auth {
*/
public function auto_login()
{
if ($token = Cookie::get($this->_config['autologin_key']))
if ($token = Cookie::get('authautologin'))
{
// Load the token and user
$token = ORM::factory('user_token', array('token' => $token));
@ -164,7 +155,7 @@ class Kohana_Auth_ORM extends Auth {
$token->save();
// Set the new token
Cookie::set($this->_config['autologin_key'], $token->token, $token->expires - time());
Cookie::set('authautologin', $token->token, $token->expires - time());
// Complete the login with the found data
$this->complete_login($token->user);
@ -187,11 +178,11 @@ class Kohana_Auth_ORM extends Auth {
*
* @return mixed
*/
public function get_user()
public function get_user($default = NULL)
{
$user = parent::get_user();
$user = parent::get_user($default);
if ($user === FALSE)
if ( ! $user)
{
// check for "remembered" login
$user = $this->auto_login();
@ -210,12 +201,12 @@ class Kohana_Auth_ORM extends Auth {
public function logout($destroy = FALSE, $logout_all = FALSE)
{
// Set by force_login()
$this->_session->delete($this->_config['forced_key']);
$this->_session->delete('auth_forced');
if ($token = Cookie::get($this->_config['autologin_key']))
if ($token = Cookie::get('authautologin'))
{
// Delete the autologin cookie to prevent re-login
Cookie::delete($this->_config['autologin_key']);
Cookie::delete('authautologin');
// Clear the autologin token from the database
$token = ORM::factory('user_token', array('token' => $token));
@ -277,46 +268,10 @@ class Kohana_Auth_ORM extends Auth {
{
$user = $this->get_user();
if ($user === FALSE)
{
// nothing to compare
return FALSE;
}
$hash = $this->hash_password($password, $this->find_salt($user->password));
return $hash == $user->password;
}
/**
* Remember user (create token and save it in cookie)
*
* @param Model_User $user
* @return boolean
*/
public function remember($user = NULL)
{
if (is_null($user))
{
$user = $this->get_user();
}
if ( ! $user)
{
return FALSE;
}
// Create a new autologin token
$token = ORM::factory('user_token');
// Set token data
$token->user_id = $user->id;
$token->expires = time() + $this->_config['lifetime'];
$token->save();
// Set the autologin cookie
Cookie::set($this->_config['autologin_key'], $token->token, $this->_config['lifetime']);
return TRUE;
return ($this->hash($password) === $user->password);
}
} // End Auth ORM

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,182 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* ORM Validation exceptions.
*
* @package Kohana/ORM
* @author Kohana Team
* @copyright (c) 2007-2010 Kohana Team
* @license http://kohanaframework.org/license
*/
class Kohana_ORM_Validation_Exception extends Kohana_Exception {
/**
* Array of validation objects
* @var array
*/
protected $_objects = array();
/**
* The _object_name property of the main ORM model this exception was created for
* @var string
*/
protected $_object_name = NULL;
/**
* Constructs a new exception for the specified model
*
* @param string $object_name The _object_name of the model this exception is for
* @param Validation $object The Validation object of the model
* @param string $message The error message
* @param array $values The array of values for the error message
* @param integer $code The error code for the exception
* @return void
*/
public function __construct($object_name, Validation $object, $message = 'Failed to validate array', array $values = NULL, $code = 0)
{
$this->_object_name = $object_name;
$this->_objects['_object'] = $object;
parent::__construct($message, $values, $code);
}
/**
* Adds a Validation object to this exception
*
* // The following will add a validation object for a profile model
* // inside the exception for a user model.
* $e->add_object('profile', $validation);
* // The errors array will now look something like this
* // array
* // (
* // 'username' => 'This field is required',
* // 'profile' => array
* // (
* // 'first_name' => 'This field is required',
* // ),
* // );
*
* @param string $alias The relationship alias from the model
* @param Validation $object The Validation object to merge
* @param mixed $has_many The array key to use if this exception can be merged multiple times
* @return ORM_Validation_Exception
*/
public function add_object($alias, Validation $object, $has_many = FALSE)
{
if ($has_many === TRUE)
{
// This is most likely a has_many relationship
$this->_objects[$alias][]['_object'] = $object;
}
elseif ($has_many)
{
// This is most likely a has_many relationship
$this->_objects[$alias][$has_many]['_object'] = $object;
}
else
{
$this->_objects[$alias]['_object'] = $object;
}
return $this;
}
/**
* Merges an ORM_Validation_Exception object into the current exception
* Useful when you want to combine errors into one array
*
* @param string $alias The relationship alias from the model
* @param ORM_Validation_Exception $object The exception to merge
* @param mixed $has_many The array key to use if this exception can be merged multiple times
* @return ORM_Validation_Exception
*/
public function merge($alias, ORM_Validation_Exception $object, $has_many = FALSE)
{
if ($has_many === TRUE)
{
// This is most likely a has_many relationship
$this->_objects[$alias][] = $object->objects();
}
elseif ($has_many)
{
// This is most likely a has_many relationship
$this->_objects[$alias][$has_many] = $object->objects();
}
else
{
$this->_objects[$alias] = $object->objects();
}
return $this;
}
/**
* Returns a merged array of the errors from all the Validation objects in this exception
*
* // Will load Model_User errors from messages/orm-validation/user.php
* $e->errors('orm-validation');
*
* @param string $directory Directory to load error messages from
* @param mixed $translate Translate the message
* @return array
* @see generate_errors()
*/
public function errors($directory = NULL, $translate = TRUE)
{
if ($directory !== NULL)
{
// Everything starts at $directory/$object_name
$directory .= '/'.$this->_object_name;
}
return $this->generate_errors($this->_objects, $directory, $translate);
}
/**
* Recursive method to fetch all the errors in this exception
*
* @param array $array Array of Validation objects to get errors from
* @param string $directory Directory to load error messages from
* @param mixed $translate Translate the message
* @return array
*/
protected function generate_errors(array $array, $directory, $translate)
{
$errors = array();
foreach ($array as $alias => $object)
{
if ($directory === NULL)
{
// Return the raw errors
$file = NULL;
}
else
{
$file = trim($directory.'/'.$alias, '/');
}
if (is_array($object))
{
// Recursively fill the errors array
$errors[$alias] = $this->generate_errors($object, $file, $translate);
}
else
{
// Merge in this array of errors
$errors += $object->errors($directory, $translate);
}
}
return $errors;
}
/**
* Returns the protected _objects property from this exception
*
* @return array
*/
public function objects()
{
return $this->_objects;
}
} // End Kohana_ORM_Validation_Exception

View File

@ -0,0 +1,29 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* Default auth role
*
* @package Kohana/Auth
* @author Kohana Team
* @copyright (c) 2007-2009 Kohana Team
* @license http://kohanaphp.com/license.html
*/
class Model_Auth_Role extends ORM {
// Relationships
protected $_has_many = array('users' => array('through' => 'roles_users'));
public function rules()
{
return array(
'name' => array(
array('not_empty'),
array('min_length', array(':value', 4)),
array('max_length', array(':value', 32)),
),
'description' => array(
array('max_length', array(':value', 255)),
)
);
}
} // End Auth Role Model

View File

@ -0,0 +1,240 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* Default auth user
*
* @package Kohana/Auth
* @author Kohana Team
* @copyright (c) 2007-2011 Kohana Team
* @license http://kohanaframework.org/license
*/
class Model_Auth_User extends ORM {
/**
* A user has many tokens and roles
*
* @var array Relationhips
*/
protected $_has_many = array(
'user_tokens' => array('model' => 'user_token'),
'roles' => array('model' => 'role', 'through' => 'roles_users'),
);
/**
* Rules for the user model. Because the password is _always_ a hash
* when it's set,you need to run an additional not_empty rule in your controller
* to make sure you didn't hash an empty string. The password rules
* should be enforced outside the model or with a model helper method.
*
* @return array Rules
*/
public function rules()
{
return array(
'username' => array(
array('not_empty'),
array('min_length', array(':value', 4)),
array('max_length', array(':value', 32)),
array('regex', array(':value', '/^[-\pL\pN_.]++$/uD')),
array(array($this, 'username_available'), array(':validation', ':field')),
),
'password' => array(
array('not_empty'),
),
'email' => array(
array('not_empty'),
array('min_length', array(':value', 4)),
array('max_length', array(':value', 127)),
array('email'),
array(array($this, 'email_available'), array(':validation', ':field')),
),
);
}
/**
* Filters to run when data is set in this model. The password filter
* automatically hashes the password when it's set in the model.
*
* @return array Filters
*/
public function filters()
{
return array(
'password' => array(
array(array(Auth::instance(), 'hash'))
)
);
}
/**
* Labels for fields in this model
*
* @return array Labels
*/
public function labels()
{
return array(
'username' => 'username',
'email' => 'email address',
'password' => 'password',
);
}
/**
* Complete the login for a user by incrementing the logins and saving login timestamp
*
* @return void
*/
public function complete_login()
{
if ($this->_loaded)
{
// Update the number of logins
$this->logins = new Database_Expression('logins + 1');
// Set the last login date
$this->last_login = time();
// Save the user
$this->update();
}
}
/**
* Does the reverse of unique_key_exists() by triggering error if username exists.
* Validation callback.
*
* @param Validation Validation object
* @param string Field name
* @return void
*/
public function username_available(Validation $validation, $field)
{
if ($this->unique_key_exists($validation[$field], 'username'))
{
$validation->error($field, 'username_available', array($validation[$field]));
}
}
/**
* Does the reverse of unique_key_exists() by triggering error if email exists.
* Validation callback.
*
* @param Validation Validation object
* @param string Field name
* @return void
*/
public function email_available(Validation $validation, $field)
{
if ($this->unique_key_exists($validation[$field], 'email'))
{
$validation->error($field, 'email_available', array($validation[$field]));
}
}
/**
* Tests if a unique key value exists in the database.
*
* @param mixed the value to test
* @param string field name
* @return boolean
*/
public function unique_key_exists($value, $field = NULL)
{
if ($field === NULL)
{
// Automatically determine field by looking at the value
$field = $this->unique_key($value);
}
return (bool) DB::select(array('COUNT("*")', 'total_count'))
->from($this->_table_name)
->where($field, '=', $value)
->where($this->_primary_key, '!=', $this->pk())
->execute($this->_db)
->get('total_count');
}
/**
* Allows a model use both email and username as unique identifiers for login
*
* @param string unique value
* @return string field name
*/
public function unique_key($value)
{
return Valid::email($value) ? 'email' : 'username';
}
/**
* Password validation for plain passwords.
*
* @param array $values
* @return Validation
*/
public static function get_password_validation($values)
{
return Validation::factory($values)
->rule('password', 'min_length', array(':value', 8))
->rule('password_confirm', 'matches', array(':validation', ':field', 'password'));
}
/**
* Create a new user
*
* Example usage:
* ~~~
* $user = ORM::factory('user')->create_user($_POST, array(
* 'username',
* 'password',
* 'email',
* );
* ~~~
*
* @param array $values
* @param array $expected
* @throws ORM_Validation_Exception
*/
public function create_user($values, $expected)
{
// Validation for passwords
$extra_validation = Model_User::get_password_validation($values)
->rule('password', 'not_empty');
return $this->values($values, $expected)->create($extra_validation);
}
/**
* Update an existing user
*
* [!!] We make the assumption that if a user does not supply a password, that they do not wish to update their password.
*
* Example usage:
* ~~~
* $user = ORM::factory('user')
* ->where('username', '=', 'kiall')
* ->find()
* ->update_user($_POST, array(
* 'username',
* 'password',
* 'email',
* );
* ~~~
*
* @param array $values
* @param array $expected
* @throws ORM_Validation_Exception
*/
public function update_user($values, $expected = NULL)
{
if (empty($values['password']))
{
unset($values['password'], $values['password_confirm']);
}
// Validation for passwords
$extra_validation = Model_User::get_password_validation($values);
return $this->values($values, $expected)->update($extra_validation);
}
} // End Auth User Model

View File

@ -4,7 +4,7 @@
*
* @package Kohana/Auth
* @author Kohana Team
* @copyright (c) 2007-2010 Kohana Team
* @copyright (c) 2007-2011 Kohana Team
* @license http://kohanaframework.org/license
*/
class Model_Auth_User_Token extends ORM {
@ -12,9 +12,6 @@ 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.
*
@ -24,53 +21,19 @@ class Model_Auth_User_Token extends ORM {
{
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)
if ($this->expires < time() AND $this->_loaded)
{
// 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.
*
@ -80,22 +43,28 @@ class Model_Auth_User_Token extends ORM {
{
// Delete all expired tokens
DB::delete($this->_table_name)
->where('expires', '<', $this->_now)
->where('expires', '<', time())
->execute($this->_db);
return $this;
}
/**
* Generate a new unique token.
*
* @return string
* @uses Text::random
*/
public function create(Validation $validation = NULL)
{
$this->token = $this->create_token();
return parent::create($validation);
}
protected function create_token()
{
// Create a random token
return Text::random('alnum', 32);
do
{
$token = sha1(uniqid(Text::random('alnum', 32), TRUE));
}
while(ORM::factory('user_token', array('token' => $token))->loaded());
return $token;
}
} // End Auth User Token Model

View File

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

View File

@ -0,0 +1,10 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* ORM Validation exceptions.
*
* @package Kohana/ORM
* @author Kohana Team
* @copyright (c) 2008-2009 Kohana Team
* @license http://kohanaphp.com/license
*/
class ORM_Validation_Exception extends Kohana_ORM_Validation_Exception {}

View File

@ -14,10 +14,10 @@ return array(
'name' => 'ORM',
// A short description of this module, shown on the index page
'description' => 'Object modeling and relation mapping.',
'description' => 'Official ORM module, a modeling library for object relational mapping.',
// Copyright message, shown in the footer for this module
'copyright' => '&copy; 20082010 Kohana Team',
'copyright' => '&copy; 20082011 Kohana Team',
)
)
);
);

View File

@ -1,8 +1,13 @@
There will be subpages to this page that have examples. The following is a sample list, don't feel limited by his, or that these are required. Items on the list can be combined, split up, removed or added to.
# Examples
- [Simple](examples/simple): Basic, one table model examples.
- [Validation](examples/validation): Full example of creating a user account and handling validation errors.
## @TODO:
The following is a sample list of examples that might be useful. Don't feel limited by this list, or consider these required. Items on the list can be combined, split up, removed or added to. All contribution are appreciated.
- Simple one table, one class model. Show CRUD examples.
- Examples of changing things like $_table_name, $_labels, with, etc.
- Example of using validation
- Example of a one to one relationship.
- Example of one to many
- Example of many to many.
- Example of many to many.

View File

@ -1,4 +1,4 @@
# Simple example
# Simple Examples
This is a simple example of a single ORM model, that has no relationships, but uses validation on the fields.
@ -7,8 +7,8 @@ This is a simple example of a single ORM model, that has no relationships, but u
CREATE TABLE IF NOT EXISTS `members` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(32) NOT NULL,
`firstname` varchar(32) NOT NULL,
`lastname` varchar(32) NOT NULL,
`first_name` varchar(32) NOT NULL,
`last_name` varchar(32) NOT NULL,
`email` varchar(127) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
@ -19,35 +19,38 @@ This is a simple example of a single ORM model, that has no relationships, but u
class Model_Member extends ORM {
protected $_rules = array(
'username' => array(
'not_empty' => NULL,
'min_length' => array(4),
'max_length' => array(32),
'regex' => array('/^[-\pL\pN_.]++$/uD'),
),
'firstname' => array(
'not_empty' => NULL,
'min_length' => array(4),
'max_length' => array(32),
'regex' => array('/^[-\pL\pN_.]++$/uD'),
),
'lastname' => array(
'not_empty' => NULL,
'min_length' => array(4),
'max_length' => array(32),
'regex' => array('/^[-\pL\pN_.]++$/uD'),
),
'email' => array(
'not_empty' => NULL,
'min_length' => array(4),
'max_length' => array(127),
'email' => NULL,
),
);
public function rules()
{
return array(
'username' => array(
array('not_empty'),
array('min_length', array(':value', 4)),
array('max_length', array(':value', 32)),
array('regex', array(':value', '/^[-\pL\pN_.]++$/uD')),
),
'first_name' => array(
array('not_empty'),
array('min_length', array(':value', 4)),
array('max_length', array(':value', 32)),
array('regex', array(':value', '/^[-\pL\pN_.]++$/uD')),
),
'last_name' => array(
array('not_empty'),
array('min_length', array(':value', 4)),
array('max_length', array(':value', 32)),
array('regex', array(':value', '/^[-\pL\pN_.]++$/uD')),
),
'email' => array(
array('not_empty'),
array('min_length', array(':value', 4)),
array('max_length', array(':value', 127)),
array('email'),
),
);
}
}
[!!] The `$_rules` array will be passed to a [Validate] object and tested when you call `check()`.
[!!] The array returned by `ORM::rules()` will be passed to a [Validation] object and tested when you call `ORM::save()`.
[!!] Please notice that defining the primary key "id" in the model is not necessary. Also the table name in the database is plural and the model name is singular.
@ -59,23 +62,23 @@ This is a simple example of a single ORM model, that has no relationships, but u
public function action_index()
{
// -------------
// - Example 1 -
// -------------
/**
* Example 1
*/
// Create an instance of a model
$member = ORM::factory('member');
$members = ORM::factory('member');
// Get all members with the First name "Peter" find_all()
// Get all members with the first name "Peter" find_all()
// means we get all records matching the query.
$member->where('firstname', '=', 'Peter')->find_all();
$members->where('first_name', '=', 'Peter')->find_all();
// Count records in the $member object
$member->count_all();
// Count records in the $members object
$members->count_all();
// -------------
// - Example 2 -
// -------------
/**
* Example 2
*/
// Create an instance of a model
$member = ORM::factory('member');
@ -84,37 +87,33 @@ This is a simple example of a single ORM model, that has no relationships, but u
// we only want the first record matching the query.
$member->where('username', '=', 'bongo')->find();
// -------------
// - Example 3 -
// -------------
/**
* Example 3
*/
// Create an instance of a model
$member = ORM::factory('member');
// Do a INSERT query
// Do an INSERT query
$member->username = 'bongo';
$member->firstname = 'Peter';
$member->lastname = 'Smith';
$member->first_name = 'Peter';
$member->last_name = 'Smith';
$member->save();
// -------------
// - Example 4 -
// -------------
/**
* Example 4
*/
// Create an instance of a model where the
// table field "id" is "1"
$member = ORM::factory('member', 1);
// You can create the instance like below
// If you do not want to use the "id" field
$member = ORM::factory('member')->where('username', '=', 'bongo')->find();
// Do a UPDATE query
// Do an UPDATE query
$member->username = 'bongo';
$member->firstname = 'Peter';
$member->lastname = 'Smith';
$member->first_name = 'Peter';
$member->last_name = 'Smith';
$member->save();
}
}
[!!] $member will be a PHP object where you can access the values from the query e.g. echo $member->firstname
[!!] $member will be a PHP object where you can access the values from the query e.g. echo $member->first_name

View File

@ -0,0 +1,137 @@
# Validation Example
This example will create user accounts and demonstrate how to handle model and controller validation. We will create a form, process it, and display any errors to the user. We will be assuming that the Model_User class contains a method called `hash_password` that is used to turn the plaintext passwords into some kind of hash. The implementation of the hashing methods are beyond the scope of this example and should be provided with the Authentication library you decide to use.
## SQL schema
CREATE TABLE IF NOT EXISTS `members` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(32) NOT NULL,
`password` varchar(100) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
## Model
<?php defined('SYSPATH') or die('No direct access allowed.');
class Model_Member extends ORM {
public function rules()
{
return array(
'username' => array(
array('not_empty'),
array('min_length', array(':value', 4)),
array('max_length', array(':value', 32)),
array(array($this, 'username_available')),
),
'password' => array(
array('not_empty'),
),
);
}
public function filters()
{
return array(
'password' => array(
array(array($this, 'hash_password')),
),
);
}
public function username_available($username)
{
// There are simpler ways to do this, but I will use ORM for the sake of the example
return ORM::factory('member', array('username' => $username))->loaded();
}
public function hash_password($password)
{
// Do something to hash the password
}
}
## HTML Form
Please forgive my slightly ugly form. I am trying not to use any modules or unrelated magic. :)
<form action="<?= URL::site('/members'); ?>" method="post" accept-charset="utf-8">
<label for="username">Username:</label>
<input id="username" type="text" name="username" value="<?= Arr::get($values, 'username'); ?>" />
<label for="username" class="error"><?= Arr::get($errors, 'username'); ?>
<label for="password">Password:</label>
<input id="password" type="password" name="password" value="<?= Arr::get($values, 'password'); ?>" />
<label for="password" class="error"><?= Arr::get($errors, 'password'); ?>
<label for="password_confirm">Repeat Password:</label>
<input id="password_confirm" type="password" name="_external[password_confirm]" value="<?= Arr::path($values, '_external.password_confirm'); ?>" />
<label for="password_confirm" class="error"><?= Arr::path($errors, '_external.password_confirm'); ?>
<button type="submit">Create</button>
</form>
## Controller
[!!] Remember that the `password` will be hashed as soon as it is set in the model, for this reason, it is impossible to validate it's length or the fact that it matches the `password_confirm` field. The model should not care about validating the `password_confirm` field, so we add that logic to the controller and simply ask the model to bundle the errors into one tidy array. Read the [filters](filters) section to understand how those work.
public function action_create()
{
$view = View::factory('members/create')
->set('values', $_POST)
->bind('errors', $errors);
if ($_POST)
{
$member = ORM::factory('member')
// The ORM::values() method is a shortcut to assign many values at once
->values($_POST, array('username', 'password'));
$external_values = array(
// The unhashed password is needed for comparing to the password_confirm field
'password' => Arr::get($_POST, 'password'),
// Add all external values
) + Arr::get($_POST, '_external', array());
$extra = Validation::factory($external_values)
->rule('password_confirm', 'matches', array(':validation', ':field', 'password'));
try
{
$member->save($extra);
// Redirect the user to his page
$this->request->redirect('members/'.$member->id);
}
catch (ORM_Validation_Exception $e)
{
$errors = $e->errors('models');
}
}
$this->response->body($view);
}
## Messages
**application/messages/models/member.php**
return array(
'username' => array(
'not_empty' => 'You must provide a username.',
'min_length' => 'The username must be at least :param2 characters long.',
'max_length' => 'The username must be less than :param2 characters long.',
'username_available' => 'This username is not available.',
),
'password' => array(
'not_empty' => 'You must provide a password.',
),
);
**application/messages/models/member/_external.php**
return array(
'password_confirm' => array(
'matches' => 'The password fields did not match.',
),
);

View File

@ -0,0 +1,22 @@
# Filters
Filters in ORM work much like they used to when they were part of the Validate class in 3.0.x however they have been modified to match the flexible syntax of [Validation] rules in 3.1.x. Filters run as soon as the field is set in your model and should be used to format the data before it is inserted into the Database.
Define your filters the same way you define rules, as an array returned by the `ORM::filters()` method like the following:
public function filters()
{
return array(
'username' => array(
array('trim'),
),
'password' => array(
array(array($this, 'hash_password')),
),
'created_on' => array(
array('Format::date', array(':value', 'Y-m-d H:i:s')),
),
);
}
[!!] When defining filters, you may use the parameters `:value`, `:field`, and `:model` to refer to the field value, field name, and the model instance respectively.

View File

@ -1,10 +1,10 @@
# ORM
Kohana 3.X includes a powerful Object Relational Mapping (ORM) module that uses the active record pattern and database introspection to determine a model's column information. ORM is integrated tightly with the [Validate] library.
Kohana 3.x includes a powerful Object Relational Mapping (ORM) module that uses the active record pattern and database introspection to determine a model's column information. ORM is integrated tightly with the [Validation] library.
The ORM allows for manipulation and control of data within a database as though it was a PHP object. Once you define the relationships ORM allows you to pull data from your database, manipulate the data in any way you like and then save the result back to the database without the use of SQL. By creating relationships between models that follow convention over configuration, much of the repetition of writing queries to create, read, update and delete information from the database can be reduced or entirely removed. All of the relationships can be handled automatically by the ORM library and you can access related data as standard object properties.
The ORM allows for manipulation and control of data within a database as though it was a PHP object. Once you define the relationships ORM allows you to pull data from your database, manipulate the data in any way you like, and then save the result back to the database without the use of SQL. By creating relationships between models that follow convention over configuration, much of the repetition of writing queries to create, read, update, and delete information from the database can be reduced or entirely removed. All of the relationships can be handled automatically by the ORM library and you can access related data as standard object properties.
ORM is included with the Kohana 3.X install but needs to be enabled before you can use it. In your `application/bootstrap.php` file modify the call to Kohana::modules and include the ORM modules.
ORM is included with the Kohana 3.x install but needs to be enabled before you can use it. In your `application/bootstrap.php` file modify the call to Kohana::modules and include the ORM modules.
## Getting started
@ -17,6 +17,6 @@ Before we use ORM, we must enable the modules required
...
));
[!!] The database module is requried for the ORM module to work. Of course the database module has to be configured to use a existing database.
[!!] The database module is requried for the ORM module to work. Of course the database module has to be configured to use an existing database.
You can now created your [models](models) and [use ORM](using).
You can now create your [models](models) and [use ORM](using).

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