Kohana v3.3.0
This commit is contained in:
commit
f96694b18f
24
.travis.yml
Normal file
24
.travis.yml
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
language: php
|
||||||
|
|
||||||
|
php:
|
||||||
|
- 5.3
|
||||||
|
|
||||||
|
before_install:
|
||||||
|
- "git submodule update --init --recursive"
|
||||||
|
|
||||||
|
before_script:
|
||||||
|
- "pear channel-discover pear.phing.info"
|
||||||
|
- "pear install phing/phing"
|
||||||
|
- "phpenv rehash"
|
||||||
|
- "composer install"
|
||||||
|
|
||||||
|
script: "phing test"
|
||||||
|
|
||||||
|
notifications:
|
||||||
|
irc:
|
||||||
|
channels:
|
||||||
|
- "irc.freenode.org#kohana"
|
||||||
|
template:
|
||||||
|
- "%{repository}/%{branch} (%{commit}) - %{author}: %{message}"
|
||||||
|
- "Build details: %{build_url}"
|
||||||
|
email: false
|
14
LICENSE.md
Normal file
14
LICENSE.md
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
# Kohana License Agreement
|
||||||
|
|
||||||
|
This license is a legal agreement between you and the Kohana Team for the use of Kohana Framework (the "Software"). By obtaining the Software you agree to comply with the terms and conditions of this license.
|
||||||
|
|
||||||
|
Copyright (c) 2007-2011 Kohana Team
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||||
|
* Neither the name of the Kohana nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
19
README.md
Normal file
19
README.md
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# Kohana PHP Framework
|
||||||
|
|
||||||
|
[Kohana](http://kohanaframework.org/) is an elegant, open source, and object oriented HMVC framework built using PHP5, by a team of volunteers. It aims to be swift, secure, and small.
|
||||||
|
|
||||||
|
Released under a [BSD license](http://kohanaframework.org/license), Kohana can be used legally for any open source, commercial, or personal project.
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
Kohana's documentation can be found at <http://kohanaframework.org/documentation> which also contains an API browser.
|
||||||
|
|
||||||
|
The `userguide` module included in all Kohana releases also allows you to view the documentation locally. Once the `userguide` module is enabled in the bootstrap, it is accessible from your site via `/index.php/guide` (or just `/guide` if you are rewriting your URLs).
|
||||||
|
|
||||||
|
## Reporting bugs
|
||||||
|
If you've stumbled across a bug, please help us out by [reporting the bug](http://dev.kohanaframework.org/projects/kohana3/) you have found. Simply log in or register and submit a new issue, leaving as much information about the bug as possible, e.g.
|
||||||
|
|
||||||
|
* Steps to reproduce
|
||||||
|
* Expected result
|
||||||
|
* Actual result
|
||||||
|
|
||||||
|
This will help us to fix the bug as quickly as possible, and if you'd like to fix it yourself feel free to [fork us on GitHub](https://github.com/kohana) and submit a pull request!
|
129
application/bootstrap.php
Normal file
129
application/bootstrap.php
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
<?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.
|
||||||
|
*
|
||||||
|
* @link http://kohanaframework.org/guide/using.configuration
|
||||||
|
* @link http://www.php.net/manual/timezones
|
||||||
|
*/
|
||||||
|
date_default_timezone_set('America/Chicago');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the default locale.
|
||||||
|
*
|
||||||
|
* @link http://kohanaframework.org/guide/using.configuration
|
||||||
|
* @link http://www.php.net/manual/function.setlocale
|
||||||
|
*/
|
||||||
|
setlocale(LC_ALL, 'en_US.utf-8');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable the Kohana auto-loader.
|
||||||
|
*
|
||||||
|
* @link http://kohanaframework.org/guide/using.autoloading
|
||||||
|
* @link http://www.php.net/manual/function.spl-autoload-register
|
||||||
|
*/
|
||||||
|
spl_autoload_register(array('Kohana', 'auto_load'));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optionally, you can enable a compatibility auto-loader for use with
|
||||||
|
* older modules that have not been updated for PSR-0.
|
||||||
|
*
|
||||||
|
* It is recommended to not enable this unless absolutely necessary.
|
||||||
|
*/
|
||||||
|
//spl_autoload_register(array('Kohana', 'auto_load_lowercase'));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable the Kohana auto-loader for unserialization.
|
||||||
|
*
|
||||||
|
* @link http://www.php.net/manual/function.spl-autoload-call
|
||||||
|
* @link http://www.php.net/manual/var.configuration#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
|
||||||
|
* - integer cache_life lifetime, in seconds, of items cached 60
|
||||||
|
* - boolean errors enable or disable error handling TRUE
|
||||||
|
* - boolean profile enable or disable internal profiling TRUE
|
||||||
|
* - boolean caching enable or disable internal caching FALSE
|
||||||
|
* - boolean expose set the X-Powered-By header FALSE
|
||||||
|
*/
|
||||||
|
Kohana::init(array(
|
||||||
|
'base_url' => '/kohana/',
|
||||||
|
));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
// 'minion' => MODPATH.'minion', // CLI Tasks
|
||||||
|
// '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',
|
||||||
|
));
|
10
application/classes/Controller/Welcome.php
Normal file
10
application/classes/Controller/Welcome.php
Normal 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
|
5
composer.json
Normal file
5
composer.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"require": {
|
||||||
|
"phpunit/phpunit": "3.7.*"
|
||||||
|
}
|
||||||
|
}
|
413
composer.lock
generated
Normal file
413
composer.lock
generated
Normal file
@ -0,0 +1,413 @@
|
|||||||
|
{
|
||||||
|
"hash": "11774bed2716724738d66bb56a8a1508",
|
||||||
|
"packages": [
|
||||||
|
{
|
||||||
|
"name": "phpunit/php-code-coverage",
|
||||||
|
"version": "1.2.6",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git://github.com/sebastianbergmann/php-code-coverage.git",
|
||||||
|
"reference": "1.2.6"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://github.com/sebastianbergmann/php-code-coverage/zipball/1.2.6",
|
||||||
|
"reference": "1.2.6",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=5.3.3",
|
||||||
|
"phpunit/php-file-iterator": ">=1.3.0@stable",
|
||||||
|
"phpunit/php-token-stream": ">=1.1.3@stable",
|
||||||
|
"phpunit/php-text-template": ">=1.1.1@stable"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"ext-dom": "*",
|
||||||
|
"ext-xdebug": ">=2.0.5"
|
||||||
|
},
|
||||||
|
"time": "2012-10-16 22:34:13",
|
||||||
|
"type": "library",
|
||||||
|
"installation-source": "dist",
|
||||||
|
"autoload": {
|
||||||
|
"classmap": [
|
||||||
|
"PHP/"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"include-path": [
|
||||||
|
""
|
||||||
|
],
|
||||||
|
"license": [
|
||||||
|
"BSD-3-Clause"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Sebastian Bergmann",
|
||||||
|
"email": "sb@sebastian-bergmann.de",
|
||||||
|
"role": "lead"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
|
||||||
|
"homepage": "https://github.com/sebastianbergmann/php-code-coverage",
|
||||||
|
"keywords": [
|
||||||
|
"testing",
|
||||||
|
"coverage",
|
||||||
|
"xunit"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "phpunit/php-file-iterator",
|
||||||
|
"version": "1.3.3",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git://github.com/sebastianbergmann/php-file-iterator.git",
|
||||||
|
"reference": "1.3.3"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://github.com/sebastianbergmann/php-file-iterator/zipball/1.3.3",
|
||||||
|
"reference": "1.3.3",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=5.3.3"
|
||||||
|
},
|
||||||
|
"time": "2012-10-11 04:44:38",
|
||||||
|
"type": "library",
|
||||||
|
"installation-source": "dist",
|
||||||
|
"autoload": {
|
||||||
|
"classmap": [
|
||||||
|
"File/"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"include-path": [
|
||||||
|
""
|
||||||
|
],
|
||||||
|
"license": [
|
||||||
|
"BSD-3-Clause"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Sebastian Bergmann",
|
||||||
|
"email": "sb@sebastian-bergmann.de",
|
||||||
|
"role": "lead"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "FilterIterator implementation that filters files based on a list of suffixes.",
|
||||||
|
"homepage": "http://www.phpunit.de/",
|
||||||
|
"keywords": [
|
||||||
|
"filesystem",
|
||||||
|
"iterator"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "phpunit/php-text-template",
|
||||||
|
"version": "1.1.3",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git://github.com/sebastianbergmann/php-text-template.git",
|
||||||
|
"reference": "1.1.3"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://github.com/sebastianbergmann/php-text-template/zipball/1.1.3",
|
||||||
|
"reference": "1.1.3",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=5.3.3"
|
||||||
|
},
|
||||||
|
"time": "2012-10-11 04:48:39",
|
||||||
|
"type": "library",
|
||||||
|
"installation-source": "dist",
|
||||||
|
"autoload": {
|
||||||
|
"classmap": [
|
||||||
|
"Text/"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"include-path": [
|
||||||
|
""
|
||||||
|
],
|
||||||
|
"license": [
|
||||||
|
"BSD-3-Clause"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Sebastian Bergmann",
|
||||||
|
"email": "sb@sebastian-bergmann.de",
|
||||||
|
"role": "lead"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Simple template engine.",
|
||||||
|
"homepage": "http://www.phpunit.de/",
|
||||||
|
"keywords": [
|
||||||
|
"template"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "phpunit/php-timer",
|
||||||
|
"version": "1.0.4",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git://github.com/sebastianbergmann/php-timer.git",
|
||||||
|
"reference": "1.0.4"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://github.com/sebastianbergmann/php-timer/zipball/1.0.4",
|
||||||
|
"reference": "1.0.4",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=5.3.3"
|
||||||
|
},
|
||||||
|
"time": "2012-10-11 04:45:58",
|
||||||
|
"type": "library",
|
||||||
|
"installation-source": "dist",
|
||||||
|
"autoload": {
|
||||||
|
"classmap": [
|
||||||
|
"PHP/"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"include-path": [
|
||||||
|
""
|
||||||
|
],
|
||||||
|
"license": [
|
||||||
|
"BSD-3-Clause"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Sebastian Bergmann",
|
||||||
|
"email": "sb@sebastian-bergmann.de",
|
||||||
|
"role": "lead"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Utility class for timing",
|
||||||
|
"homepage": "http://www.phpunit.de/",
|
||||||
|
"keywords": [
|
||||||
|
"timer"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "phpunit/php-token-stream",
|
||||||
|
"version": "1.1.5",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git://github.com/sebastianbergmann/php-token-stream.git",
|
||||||
|
"reference": "1.1.5"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://github.com/sebastianbergmann/php-token-stream/zipball/1.1.5",
|
||||||
|
"reference": "1.1.5",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"ext-tokenizer": "*",
|
||||||
|
"php": ">=5.3.3"
|
||||||
|
},
|
||||||
|
"time": "2012-10-11 04:47:14",
|
||||||
|
"type": "library",
|
||||||
|
"installation-source": "dist",
|
||||||
|
"autoload": {
|
||||||
|
"classmap": [
|
||||||
|
"PHP/"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"include-path": [
|
||||||
|
""
|
||||||
|
],
|
||||||
|
"license": [
|
||||||
|
"BSD-3-Clause"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Sebastian Bergmann",
|
||||||
|
"email": "sb@sebastian-bergmann.de",
|
||||||
|
"role": "lead"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Wrapper around PHP's tokenizer extension.",
|
||||||
|
"homepage": "http://www.phpunit.de/",
|
||||||
|
"keywords": [
|
||||||
|
"tokenizer"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "phpunit/phpunit",
|
||||||
|
"version": "3.7.8",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git://github.com/sebastianbergmann/phpunit.git",
|
||||||
|
"reference": "3.7.8"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://github.com/sebastianbergmann/phpunit/zipball/3.7.8",
|
||||||
|
"reference": "3.7.8",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=5.3.3",
|
||||||
|
"phpunit/php-file-iterator": ">=1.3.1",
|
||||||
|
"phpunit/php-text-template": ">=1.1.1",
|
||||||
|
"phpunit/php-code-coverage": ">=1.2.1",
|
||||||
|
"phpunit/php-timer": ">=1.0.2",
|
||||||
|
"phpunit/phpunit-mock-objects": ">=1.2.0",
|
||||||
|
"symfony/yaml": ">=2.1.0",
|
||||||
|
"ext-dom": "*",
|
||||||
|
"ext-pcre": "*",
|
||||||
|
"ext-reflection": "*",
|
||||||
|
"ext-spl": "*"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"phpunit/php-invoker": ">=1.1.0",
|
||||||
|
"ext-json": "*",
|
||||||
|
"ext-simplexml": "*",
|
||||||
|
"ext-tokenizer": "*"
|
||||||
|
},
|
||||||
|
"time": "2012-10-16 22:37:08",
|
||||||
|
"bin": [
|
||||||
|
"composer/bin/phpunit"
|
||||||
|
],
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "3.7.x-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"installation-source": "dist",
|
||||||
|
"autoload": {
|
||||||
|
"classmap": [
|
||||||
|
"PHPUnit/"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"include-path": [
|
||||||
|
"",
|
||||||
|
"../../symfony/yaml/"
|
||||||
|
],
|
||||||
|
"license": [
|
||||||
|
"BSD-3-Clause"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Sebastian Bergmann",
|
||||||
|
"email": "sebastian@phpunit.de",
|
||||||
|
"role": "lead"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "The PHP Unit Testing framework.",
|
||||||
|
"homepage": "http://www.phpunit.de/",
|
||||||
|
"keywords": [
|
||||||
|
"testing",
|
||||||
|
"phpunit",
|
||||||
|
"xunit"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "phpunit/phpunit-mock-objects",
|
||||||
|
"version": "1.2.1",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git://github.com/sebastianbergmann/phpunit-mock-objects.git",
|
||||||
|
"reference": "1.2.1"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://github.com/sebastianbergmann/phpunit-mock-objects/zipball/1.2.1",
|
||||||
|
"reference": "1.2.1",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=5.3.3",
|
||||||
|
"phpunit/php-text-template": ">=1.1.1@stable",
|
||||||
|
"ext-reflection": "*",
|
||||||
|
"ext-spl": "*"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"ext-soap": "*"
|
||||||
|
},
|
||||||
|
"time": "2012-10-05 00:00:00",
|
||||||
|
"type": "library",
|
||||||
|
"installation-source": "dist",
|
||||||
|
"autoload": {
|
||||||
|
"classmap": [
|
||||||
|
"PHPUnit/"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"include-path": [
|
||||||
|
""
|
||||||
|
],
|
||||||
|
"license": [
|
||||||
|
"BSD-3-Clause"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Sebastian Bergmann",
|
||||||
|
"email": "sb@sebastian-bergmann.de",
|
||||||
|
"role": "lead"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Mock Object library for PHPUnit",
|
||||||
|
"homepage": "http://www.phpunit.de/",
|
||||||
|
"keywords": [
|
||||||
|
"mock",
|
||||||
|
"xunit"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "symfony/yaml",
|
||||||
|
"version": "v2.1.2",
|
||||||
|
"target-dir": "Symfony/Component/Yaml",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/symfony/Yaml",
|
||||||
|
"reference": "v2.1.0-RC2"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://github.com/symfony/Yaml/zipball/v2.1.0-RC2",
|
||||||
|
"reference": "v2.1.0-RC2",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=5.3.3"
|
||||||
|
},
|
||||||
|
"time": "2012-08-22 06:48:41",
|
||||||
|
"type": "library",
|
||||||
|
"extra": {
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "2.1-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"installation-source": "dist",
|
||||||
|
"autoload": {
|
||||||
|
"psr-0": {
|
||||||
|
"Symfony\\Component\\Yaml": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Fabien Potencier",
|
||||||
|
"email": "fabien@symfony.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Symfony Community",
|
||||||
|
"homepage": "http://symfony.com/contributors"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Symfony Yaml Component",
|
||||||
|
"homepage": "http://symfony.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"packages-dev": null,
|
||||||
|
"aliases": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"minimum-stability": "stable",
|
||||||
|
"stability-flags": [
|
||||||
|
|
||||||
|
]
|
||||||
|
}
|
BIN
composer.phar
Normal file
BIN
composer.phar
Normal file
Binary file not shown.
21
example.htaccess
Normal file
21
example.htaccess
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# 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]
|
121
index.php
Normal file
121
index.php
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The directory in which your application specific resources are located.
|
||||||
|
* The application directory must contain the bootstrap.php file.
|
||||||
|
*
|
||||||
|
* @link http://kohanaframework.org/guide/about.install#application
|
||||||
|
*/
|
||||||
|
$application = 'application';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The directory in which your modules are located.
|
||||||
|
*
|
||||||
|
* @link http://kohanaframework.org/guide/about.install#modules
|
||||||
|
*/
|
||||||
|
$modules = 'modules';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The directory in which the Kohana resources are located. The system
|
||||||
|
* directory must contain the classes/kohana.php file.
|
||||||
|
*
|
||||||
|
* @link http://kohanaframework.org/guide/about.install#system
|
||||||
|
*/
|
||||||
|
$system = 'system';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default extension of resource files. If you change this, all resources
|
||||||
|
* must be renamed to use the new extension.
|
||||||
|
*
|
||||||
|
* @link http://kohanaframework.org/guide/about.install#ext
|
||||||
|
*/
|
||||||
|
define('EXT', '.php');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the PHP error reporting level. If you set this in php.ini, you remove this.
|
||||||
|
* @link http://www.php.net/manual/errorfunc.configuration#ini.error-reporting
|
||||||
|
*
|
||||||
|
* When developing your application, it is highly recommended to enable notices
|
||||||
|
* and strict warnings. Enable them by using: E_ALL | E_STRICT
|
||||||
|
*
|
||||||
|
* In a production environment, it is safe to ignore notices and strict warnings.
|
||||||
|
* Disable them by using: E_ALL ^ E_NOTICE
|
||||||
|
*
|
||||||
|
* When using a legacy application with PHP >= 5.3, it is recommended to disable
|
||||||
|
* deprecated notices. Disable with: E_ALL & ~E_DEPRECATED
|
||||||
|
*/
|
||||||
|
error_reporting(E_ALL | E_STRICT);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* End of standard configuration! Changing any of the code below should only be
|
||||||
|
* attempted by those with a working knowledge of Kohana internals.
|
||||||
|
*
|
||||||
|
* @link http://kohanaframework.org/guide/using.configuration
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Set the full path to the docroot
|
||||||
|
define('DOCROOT', realpath(dirname(__FILE__)).DIRECTORY_SEPARATOR);
|
||||||
|
|
||||||
|
// Make the application relative to the docroot, for symlink'd index.php
|
||||||
|
if ( ! is_dir($application) AND is_dir(DOCROOT.$application))
|
||||||
|
$application = DOCROOT.$application;
|
||||||
|
|
||||||
|
// Make the modules relative to the docroot, for symlink'd index.php
|
||||||
|
if ( ! is_dir($modules) AND is_dir(DOCROOT.$modules))
|
||||||
|
$modules = DOCROOT.$modules;
|
||||||
|
|
||||||
|
// Make the system relative to the docroot, for symlink'd index.php
|
||||||
|
if ( ! is_dir($system) AND is_dir(DOCROOT.$system))
|
||||||
|
$system = DOCROOT.$system;
|
||||||
|
|
||||||
|
// Define the absolute paths for configured directories
|
||||||
|
define('APPPATH', realpath($application).DIRECTORY_SEPARATOR);
|
||||||
|
define('MODPATH', realpath($modules).DIRECTORY_SEPARATOR);
|
||||||
|
define('SYSPATH', realpath($system).DIRECTORY_SEPARATOR);
|
||||||
|
|
||||||
|
// Clean up the configuration vars
|
||||||
|
unset($application, $modules, $system);
|
||||||
|
|
||||||
|
if (file_exists('install'.EXT))
|
||||||
|
{
|
||||||
|
// Load the installation check
|
||||||
|
return include 'install'.EXT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define the start time of the application, used for profiling.
|
||||||
|
*/
|
||||||
|
if ( ! defined('KOHANA_START_TIME'))
|
||||||
|
{
|
||||||
|
define('KOHANA_START_TIME', microtime(TRUE));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define the memory usage at the start of the application, used for profiling.
|
||||||
|
*/
|
||||||
|
if ( ! defined('KOHANA_START_MEMORY'))
|
||||||
|
{
|
||||||
|
define('KOHANA_START_MEMORY', memory_get_usage());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bootstrap the application
|
||||||
|
require APPPATH.'bootstrap'.EXT;
|
||||||
|
|
||||||
|
if (PHP_SAPI == 'cli') // Try and load minion
|
||||||
|
{
|
||||||
|
class_exists('Minion_Task') OR die('Please enable the Minion module for CLI support.');
|
||||||
|
set_exception_handler(array('Minion_Exception', 'handler'));
|
||||||
|
|
||||||
|
Minion_Task::factory(Minion_CLI::options())->execute();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Execute the main request. A source of the URI can be passed, eg: $_SERVER['PATH_INFO'].
|
||||||
|
* If no source is specified, the URI will be automatically detected.
|
||||||
|
*/
|
||||||
|
echo Request::factory(TRUE, array(), FALSE)
|
||||||
|
->execute()
|
||||||
|
->send_headers(TRUE)
|
||||||
|
->body();
|
||||||
|
}
|
233
install.php
Normal file
233
install.php
Normal file
@ -0,0 +1,233 @@
|
|||||||
|
<?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);
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||||
|
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||||
|
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||||
|
<head>
|
||||||
|
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||||
|
<title>Kohana Installation</title>
|
||||||
|
|
||||||
|
<style type="text/css">
|
||||||
|
body { width: 42em; margin: 0 auto; font-family: sans-serif; background: #fff; font-size: 1em; }
|
||||||
|
h1 { letter-spacing: -0.04em; }
|
||||||
|
h1 + p { margin: 0 0 2em; color: #333; font-size: 90%; font-style: italic; }
|
||||||
|
code { font-family: monaco, monospace; }
|
||||||
|
table { border-collapse: collapse; width: 100%; }
|
||||||
|
table th,
|
||||||
|
table td { padding: 0.4em; text-align: left; vertical-align: top; }
|
||||||
|
table th { width: 12em; font-weight: normal; }
|
||||||
|
table tr:nth-child(odd) { background: #eee; }
|
||||||
|
table td.pass { color: #191; }
|
||||||
|
table td.fail { color: #911; }
|
||||||
|
#results { padding: 0.8em; color: #fff; font-size: 1.5em; }
|
||||||
|
#results.pass { background: #191; }
|
||||||
|
#results.fail { background: #911; }
|
||||||
|
</style>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<h1>Environment Tests</h1>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
The following tests have been run to determine if <a href="http://kohanaframework.org/">Kohana</a> will work in your environment.
|
||||||
|
If any of the tests have failed, consult the <a href="http://kohanaframework.org/guide/about.install">documentation</a>
|
||||||
|
for more information on how to correct the problem.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<?php $failed = FALSE ?>
|
||||||
|
|
||||||
|
<table cellspacing="0">
|
||||||
|
<tr>
|
||||||
|
<th>PHP Version</th>
|
||||||
|
<?php if (version_compare(PHP_VERSION, '5.3.3', '>=')): ?>
|
||||||
|
<td class="pass"><?php echo PHP_VERSION ?></td>
|
||||||
|
<?php else: $failed = TRUE ?>
|
||||||
|
<td class="fail">Kohana requires PHP 5.3.3 or newer, this version is <?php echo PHP_VERSION ?>.</td>
|
||||||
|
<?php endif ?>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>System Directory</th>
|
||||||
|
<?php if (is_dir(SYSPATH) AND is_file(SYSPATH.'classes/Kohana'.EXT)): ?>
|
||||||
|
<td class="pass"><?php echo SYSPATH ?></td>
|
||||||
|
<?php else: $failed = TRUE ?>
|
||||||
|
<td class="fail">The configured <code>system</code> directory does not exist or does not contain required files.</td>
|
||||||
|
<?php endif ?>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Application Directory</th>
|
||||||
|
<?php if (is_dir(APPPATH) AND is_file(APPPATH.'bootstrap'.EXT)): ?>
|
||||||
|
<td class="pass"><?php echo APPPATH ?></td>
|
||||||
|
<?php else: $failed = TRUE ?>
|
||||||
|
<td class="fail">The configured <code>application</code> directory does not exist or does not contain required files.</td>
|
||||||
|
<?php endif ?>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Cache Directory</th>
|
||||||
|
<?php if (is_dir(APPPATH) AND is_dir(APPPATH.'cache') AND is_writable(APPPATH.'cache')): ?>
|
||||||
|
<td class="pass"><?php echo APPPATH.'cache/' ?></td>
|
||||||
|
<?php else: $failed = TRUE ?>
|
||||||
|
<td class="fail">The <code><?php echo APPPATH.'cache/' ?></code> directory is not writable.</td>
|
||||||
|
<?php endif ?>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Logs Directory</th>
|
||||||
|
<?php if (is_dir(APPPATH) AND is_dir(APPPATH.'logs') AND is_writable(APPPATH.'logs')): ?>
|
||||||
|
<td class="pass"><?php echo APPPATH.'logs/' ?></td>
|
||||||
|
<?php else: $failed = TRUE ?>
|
||||||
|
<td class="fail">The <code><?php echo APPPATH.'logs/' ?></code> directory is not writable.</td>
|
||||||
|
<?php endif ?>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>PCRE UTF-8</th>
|
||||||
|
<?php if ( ! @preg_match('/^.$/u', 'ñ')): $failed = TRUE ?>
|
||||||
|
<td class="fail"><a href="http://php.net/pcre">PCRE</a> has not been compiled with UTF-8 support.</td>
|
||||||
|
<?php elseif ( ! @preg_match('/^\pL$/u', 'ñ')): $failed = TRUE ?>
|
||||||
|
<td class="fail"><a href="http://php.net/pcre">PCRE</a> has not been compiled with Unicode property support.</td>
|
||||||
|
<?php else: ?>
|
||||||
|
<td class="pass">Pass</td>
|
||||||
|
<?php endif ?>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>SPL Enabled</th>
|
||||||
|
<?php if (function_exists('spl_autoload_register')): ?>
|
||||||
|
<td class="pass">Pass</td>
|
||||||
|
<?php else: $failed = TRUE ?>
|
||||||
|
<td class="fail">PHP <a href="http://www.php.net/spl">SPL</a> is either not loaded or not compiled in.</td>
|
||||||
|
<?php endif ?>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Reflection Enabled</th>
|
||||||
|
<?php if (class_exists('ReflectionClass')): ?>
|
||||||
|
<td class="pass">Pass</td>
|
||||||
|
<?php else: $failed = TRUE ?>
|
||||||
|
<td class="fail">PHP <a href="http://www.php.net/reflection">reflection</a> is either not loaded or not compiled in.</td>
|
||||||
|
<?php endif ?>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Filters Enabled</th>
|
||||||
|
<?php if (function_exists('filter_list')): ?>
|
||||||
|
<td class="pass">Pass</td>
|
||||||
|
<?php else: $failed = TRUE ?>
|
||||||
|
<td class="fail">The <a href="http://www.php.net/filter">filter</a> extension is either not loaded or not compiled in.</td>
|
||||||
|
<?php endif ?>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Iconv Extension Loaded</th>
|
||||||
|
<?php if (extension_loaded('iconv')): ?>
|
||||||
|
<td class="pass">Pass</td>
|
||||||
|
<?php else: $failed = TRUE ?>
|
||||||
|
<td class="fail">The <a href="http://php.net/iconv">iconv</a> extension is not loaded.</td>
|
||||||
|
<?php endif ?>
|
||||||
|
</tr>
|
||||||
|
<?php if (extension_loaded('mbstring')): ?>
|
||||||
|
<tr>
|
||||||
|
<th>Mbstring Not Overloaded</th>
|
||||||
|
<?php if (ini_get('mbstring.func_overload') & MB_OVERLOAD_STRING): $failed = TRUE ?>
|
||||||
|
<td class="fail">The <a href="http://php.net/mbstring">mbstring</a> extension is overloading PHP's native string functions.</td>
|
||||||
|
<?php else: ?>
|
||||||
|
<td class="pass">Pass</td>
|
||||||
|
<?php endif ?>
|
||||||
|
</tr>
|
||||||
|
<?php endif ?>
|
||||||
|
<tr>
|
||||||
|
<th>Character Type (CTYPE) Extension</th>
|
||||||
|
<?php if ( ! function_exists('ctype_digit')): $failed = TRUE ?>
|
||||||
|
<td class="fail">The <a href="http://php.net/ctype">ctype</a> extension is not enabled.</td>
|
||||||
|
<?php else: ?>
|
||||||
|
<td class="pass">Pass</td>
|
||||||
|
<?php endif ?>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>URI Determination</th>
|
||||||
|
<?php if (isset($_SERVER['REQUEST_URI']) OR isset($_SERVER['PHP_SELF']) OR isset($_SERVER['PATH_INFO'])): ?>
|
||||||
|
<td class="pass">Pass</td>
|
||||||
|
<?php else: $failed = TRUE ?>
|
||||||
|
<td class="fail">Neither <code>$_SERVER['REQUEST_URI']</code>, <code>$_SERVER['PHP_SELF']</code>, or <code>$_SERVER['PATH_INFO']</code> is available.</td>
|
||||||
|
<?php endif ?>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<?php if ($failed === TRUE): ?>
|
||||||
|
<p id="results" class="fail">✘ Kohana may not work correctly with your environment.</p>
|
||||||
|
<?php else: ?>
|
||||||
|
<p id="results" class="pass">✔ Your environment passed all requirements.<br />
|
||||||
|
Remove or rename the <code>install<?php echo EXT ?></code> file now.</p>
|
||||||
|
<?php endif ?>
|
||||||
|
|
||||||
|
<h1>Optional Tests</h1>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
The following extensions are not required to run the Kohana core, but if enabled can provide access to additional classes.
|
||||||
|
</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 can use the <a href="http://php.net/curl">cURL</a> extension for the Request_Client_External class.</td>
|
||||||
|
<?php endif ?>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>mcrypt Enabled</th>
|
||||||
|
<?php if (extension_loaded('mcrypt')): ?>
|
||||||
|
<td class="pass">Pass</td>
|
||||||
|
<?php else: ?>
|
||||||
|
<td class="fail">Kohana requires <a href="http://php.net/mcrypt">mcrypt</a> for the Encrypt class.</td>
|
||||||
|
<?php endif ?>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>GD Enabled</th>
|
||||||
|
<?php if (function_exists('gd_info')): ?>
|
||||||
|
<td class="pass">Pass</td>
|
||||||
|
<?php 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')): ?>
|
||||||
|
<td class="pass">Pass</td>
|
||||||
|
<?php else: ?>
|
||||||
|
<td class="fail">Kohana can use <a href="http://php.net/pdo">PDO</a> to support additional databases.</td>
|
||||||
|
<?php endif ?>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
13
modules/auth/README.md
Normal file
13
modules/auth/README.md
Normal 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.
|
3
modules/auth/classes/Auth.php
Normal file
3
modules/auth/classes/Auth.php
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<?php defined('SYSPATH') OR die('No direct access allowed.');
|
||||||
|
|
||||||
|
abstract class Auth extends Kohana_Auth { }
|
3
modules/auth/classes/Auth/File.php
Normal file
3
modules/auth/classes/Auth/File.php
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<?php defined('SYSPATH') OR die('No direct access allowed.');
|
||||||
|
|
||||||
|
class Auth_File extends Kohana_Auth_File { }
|
171
modules/auth/classes/Kohana/Auth.php
Normal file
171
modules/auth/classes/Kohana/Auth.php
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
<?php defined('SYSPATH') OR die('No direct access allowed.');
|
||||||
|
/**
|
||||||
|
* User authorization library. Handles user login and logout, as well as secure
|
||||||
|
* password hashing.
|
||||||
|
*
|
||||||
|
* @package Kohana/Auth
|
||||||
|
* @author Kohana Team
|
||||||
|
* @copyright (c) 2007-2012 Kohana Team
|
||||||
|
* @license http://kohanaframework.org/license
|
||||||
|
*/
|
||||||
|
abstract class Kohana_Auth {
|
||||||
|
|
||||||
|
// Auth instances
|
||||||
|
protected static $_instance;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Singleton pattern
|
||||||
|
*
|
||||||
|
* @return Auth
|
||||||
|
*/
|
||||||
|
public static function instance()
|
||||||
|
{
|
||||||
|
if ( ! isset(Auth::$_instance))
|
||||||
|
{
|
||||||
|
// Load the configuration for this type
|
||||||
|
$config = Kohana::$config->load('auth');
|
||||||
|
|
||||||
|
if ( ! $type = $config->get('driver'))
|
||||||
|
{
|
||||||
|
$type = 'file';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the session class name
|
||||||
|
$class = 'Auth_'.ucfirst($type);
|
||||||
|
|
||||||
|
// Create a new session instance
|
||||||
|
Auth::$_instance = new $class($config);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Auth::$_instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected $_session;
|
||||||
|
|
||||||
|
protected $_config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads Session and configuration options.
|
||||||
|
*
|
||||||
|
* @param array $config Config Options
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct($config = array())
|
||||||
|
{
|
||||||
|
// Save the config in the object
|
||||||
|
$this->_config = $config;
|
||||||
|
|
||||||
|
$this->_session = Session::instance($this->_config['session_type']);
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract protected function _login($username, $password, $remember);
|
||||||
|
|
||||||
|
abstract public function password($username);
|
||||||
|
|
||||||
|
abstract public function check_password($password);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the currently logged in user from the session.
|
||||||
|
* Returns NULL if no user is currently logged in.
|
||||||
|
*
|
||||||
|
* @param mixed $default Default value to return if the user is currently not logged in.
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function get_user($default = NULL)
|
||||||
|
{
|
||||||
|
return $this->_session->get($this->_config['session_key'], $default);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempt to log in a user by using an ORM object and plain-text password.
|
||||||
|
*
|
||||||
|
* @param string $username Username to log in
|
||||||
|
* @param string $password Password to check against
|
||||||
|
* @param boolean $remember Enable autologin
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function login($username, $password, $remember = FALSE)
|
||||||
|
{
|
||||||
|
if (empty($password))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
return $this->_login($username, $password, $remember);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log out a user by removing the related session variables.
|
||||||
|
*
|
||||||
|
* @param boolean $destroy Completely destroy the session
|
||||||
|
* @param boolean $logout_all Remove all tokens for user
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function logout($destroy = FALSE, $logout_all = FALSE)
|
||||||
|
{
|
||||||
|
if ($destroy === TRUE)
|
||||||
|
{
|
||||||
|
// Destroy the session completely
|
||||||
|
$this->_session->destroy();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Remove the user from the session
|
||||||
|
$this->_session->delete($this->_config['session_key']);
|
||||||
|
|
||||||
|
// Regenerate session_id
|
||||||
|
$this->_session->regenerate();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Double check
|
||||||
|
return ! $this->logged_in();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if there is an active session. Optionally allows checking for a
|
||||||
|
* specific role.
|
||||||
|
*
|
||||||
|
* @param string $role role name
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function logged_in($role = NULL)
|
||||||
|
{
|
||||||
|
return ($this->get_user() !== NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a hashed hmac password from a plaintext password. This
|
||||||
|
* method is deprecated, [Auth::hash] should be used instead.
|
||||||
|
*
|
||||||
|
* @deprecated
|
||||||
|
* @param string $password Plaintext password
|
||||||
|
*/
|
||||||
|
public function hash_password($password)
|
||||||
|
{
|
||||||
|
return $this->hash($password);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform a hmac hash, using the configured method.
|
||||||
|
*
|
||||||
|
* @param string $str string to hash
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function hash($str)
|
||||||
|
{
|
||||||
|
if ( ! $this->_config['hash_key'])
|
||||||
|
throw new Kohana_Exception('A valid hash key must be set in your auth config.');
|
||||||
|
|
||||||
|
return hash_hmac($this->_config['hash_method'], $str, $this->_config['hash_key']);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function complete_login($user)
|
||||||
|
{
|
||||||
|
// Regenerate session_id
|
||||||
|
$this->_session->regenerate();
|
||||||
|
|
||||||
|
// Store username in session
|
||||||
|
$this->_session->set($this->_config['session_key'], $user);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // End Auth
|
94
modules/auth/classes/Kohana/Auth/File.php
Normal file
94
modules/auth/classes/Kohana/Auth/File.php
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
<?php defined('SYSPATH') OR die('No direct access allowed.');
|
||||||
|
/**
|
||||||
|
* File Auth driver.
|
||||||
|
* [!!] this Auth driver does not support roles nor autologin.
|
||||||
|
*
|
||||||
|
* @package Kohana/Auth
|
||||||
|
* @author Kohana Team
|
||||||
|
* @copyright (c) 2007-2012 Kohana Team
|
||||||
|
* @license http://kohanaframework.org/license
|
||||||
|
*/
|
||||||
|
class Kohana_Auth_File extends Auth {
|
||||||
|
|
||||||
|
// User list
|
||||||
|
protected $_users;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor loads the user list into the class.
|
||||||
|
*/
|
||||||
|
public function __construct($config = array())
|
||||||
|
{
|
||||||
|
parent::__construct($config);
|
||||||
|
|
||||||
|
// Load user list
|
||||||
|
$this->_users = Arr::get($config, 'users', array());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logs a user in.
|
||||||
|
*
|
||||||
|
* @param string $username Username
|
||||||
|
* @param string $password Password
|
||||||
|
* @param boolean $remember Enable autologin (not supported)
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
protected function _login($username, $password, $remember)
|
||||||
|
{
|
||||||
|
if (is_string($password))
|
||||||
|
{
|
||||||
|
// Create a hashed password
|
||||||
|
$password = $this->hash($password);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($this->_users[$username]) AND $this->_users[$username] === $password)
|
||||||
|
{
|
||||||
|
// Complete the login
|
||||||
|
return $this->complete_login($username);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Login failed
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forces a user to be logged in, without specifying a password.
|
||||||
|
*
|
||||||
|
* @param mixed $username Username
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function force_login($username)
|
||||||
|
{
|
||||||
|
// Complete the login
|
||||||
|
return $this->complete_login($username);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the stored password for a username.
|
||||||
|
*
|
||||||
|
* @param mixed $username Username
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function password($username)
|
||||||
|
{
|
||||||
|
return Arr::get($this->_users, $username, FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compare password with original (plain text). Works for current (logged in) user
|
||||||
|
*
|
||||||
|
* @param string $password Password
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function check_password($password)
|
||||||
|
{
|
||||||
|
$username = $this->get_user();
|
||||||
|
|
||||||
|
if ($username === FALSE)
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ($password === $this->password($username));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // End Auth File
|
17
modules/auth/config/auth.php
Normal file
17
modules/auth/config/auth.php
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?php defined('SYSPATH') OR die('No direct access allowed.');
|
||||||
|
|
||||||
|
return array(
|
||||||
|
|
||||||
|
'driver' => 'File',
|
||||||
|
'hash_method' => 'sha256',
|
||||||
|
'hash_key' => NULL,
|
||||||
|
'lifetime' => 1209600,
|
||||||
|
'session_type' => Session::$default,
|
||||||
|
'session_key' => 'auth_user',
|
||||||
|
|
||||||
|
// Username/password combinations for the Auth File driver
|
||||||
|
'users' => array(
|
||||||
|
// 'admin' => 'b3154acf3a344170077d11bdb5fff31532f679a1919e716a02',
|
||||||
|
),
|
||||||
|
|
||||||
|
);
|
23
modules/auth/config/userguide.php
Normal file
23
modules/auth/config/userguide.php
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?php defined('SYSPATH') OR die('No direct script access.');
|
||||||
|
|
||||||
|
return array(
|
||||||
|
// Leave this alone
|
||||||
|
'modules' => array(
|
||||||
|
|
||||||
|
// This should be the path to this modules userguide pages, without the 'guide/'. Ex: '/guide/modulename/' would be 'modulename'
|
||||||
|
'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' => '© 2008–2012 Kohana Team',
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
13
modules/auth/guide/auth/config.md
Normal file
13
modules/auth/guide/auth/config.md
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# Configuration
|
||||||
|
|
||||||
|
The default configuration file is located in `MODPATH/auth/config/auth.php`. You should copy this file to `APPPATH/config/auth.php` and make changes there, in keeping with the [cascading filesystem](../kohana/files).
|
||||||
|
|
||||||
|
[Config merging](../kohana/config#config-merging) allows these default configuration settings to apply if you don't overwrite them in your application configuration file.
|
||||||
|
|
||||||
|
Name | Type | Default | Description
|
||||||
|
-----|------|---------|------------
|
||||||
|
driver | `string` | file | The name of the auth driver to use.
|
||||||
|
hash_method | `string` | sha256 | The hashing function to use on the passwords.
|
||||||
|
hash_key | `string` | NULL | The key to use when hashing the password.
|
||||||
|
session_type | `string` | [Session::$default] | The type of session to use when storing the auth user.
|
||||||
|
session_key | `string` | auth_user | The name of the session variable used to save the user.
|
79
modules/auth/guide/auth/driver/develop.md
Normal file
79
modules/auth/guide/auth/driver/develop.md
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
# Developing Drivers
|
||||||
|
|
||||||
|
## Real World Example
|
||||||
|
|
||||||
|
Sometimes the best way to learn is to jump right in and read the code from another module. The [ORM](https://github.com/kohana/orm/blob/3.2/develop/classes/kohana/auth/orm.php) module comes with an auth driver you can learn from.
|
||||||
|
|
||||||
|
[!!] We will be developing an `example` driver. In your own driver you will substitute `example` with your driver name.
|
||||||
|
|
||||||
|
This example file would be saved at `APPPATH/classes/auth/example.php` (or `MODPATH` if you are creating a module).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Quick Example
|
||||||
|
|
||||||
|
First we will show you a quick example and then break down what is going on.
|
||||||
|
|
||||||
|
~~~
|
||||||
|
class Auth_Example extends Auth
|
||||||
|
{
|
||||||
|
protected function _login($username, $password, $remember)
|
||||||
|
{
|
||||||
|
// Do username/password check here
|
||||||
|
}
|
||||||
|
|
||||||
|
public function password($username)
|
||||||
|
{
|
||||||
|
// Return the password for the username
|
||||||
|
}
|
||||||
|
|
||||||
|
public function check_password($password)
|
||||||
|
{
|
||||||
|
// Check to see if the logged in user has the given password
|
||||||
|
}
|
||||||
|
|
||||||
|
public function logged_in($role = NULL)
|
||||||
|
{
|
||||||
|
// Check to see if the user is logged in, and if $role is set, has all roles
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_user($default = NULL)
|
||||||
|
{
|
||||||
|
// Get the logged in user, or return the $default if a user is not found
|
||||||
|
}
|
||||||
|
}
|
||||||
|
~~~
|
||||||
|
|
||||||
|
## Extending Auth
|
||||||
|
|
||||||
|
All drivers must extend the [Auth] class.
|
||||||
|
|
||||||
|
class Auth_Example extends Auth
|
||||||
|
|
||||||
|
## Abstract Methods
|
||||||
|
|
||||||
|
The `Auth` class has 3 abstract methods that must be defined in your new driver.
|
||||||
|
|
||||||
|
~~~
|
||||||
|
abstract protected function _login($username, $password, $remember);
|
||||||
|
|
||||||
|
abstract public function password($username);
|
||||||
|
|
||||||
|
abstract public function check_password($user);
|
||||||
|
~~~
|
||||||
|
|
||||||
|
## Extending Functionality
|
||||||
|
|
||||||
|
Given that every auth system is going to check if users exist and if they have roles or not you will more than likely have to change some default functionality.
|
||||||
|
|
||||||
|
Here are a few functions that you should pay attention to.
|
||||||
|
|
||||||
|
~~~
|
||||||
|
public function logged_in($role = NULL)
|
||||||
|
|
||||||
|
public function get_user($default = NULL)
|
||||||
|
~~~
|
||||||
|
|
||||||
|
## Activating the Driver
|
||||||
|
|
||||||
|
After you create your driver you will want to use it. It is a easy as setting the `driver` [configuration](config) option to the name of your driver (in our case `example`).
|
19
modules/auth/guide/auth/driver/file.md
Normal file
19
modules/auth/guide/auth/driver/file.md
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# File Driver
|
||||||
|
|
||||||
|
The [Auth::File] driver is included with the auth module.
|
||||||
|
|
||||||
|
Below are additional configuration options that can be set for this driver.
|
||||||
|
|
||||||
|
Name | Type | Default | Description
|
||||||
|
-----|------|---------|-------------
|
||||||
|
users | `array` | array() | A user => password (_hashed_) array of all the users in your application
|
||||||
|
|
||||||
|
## Forcing Login
|
||||||
|
|
||||||
|
[Auth_File::force_login] allows you to force a user login without a password.
|
||||||
|
|
||||||
|
~~~
|
||||||
|
// Force the user with a username of admin to be logged into your application
|
||||||
|
Auth::instance()->force_login('admin');
|
||||||
|
$user = Auth::instance()->get_user();
|
||||||
|
~~~
|
19
modules/auth/guide/auth/index.md
Normal file
19
modules/auth/guide/auth/index.md
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# Auth
|
||||||
|
|
||||||
|
User authentication and authorization is provided by the auth module.
|
||||||
|
|
||||||
|
The auth module is included with Kohana, but needs to be enabled before you can use it. To enable, open your `application/bootstrap.php` file and modify the call to [Kohana::modules] by including the auth module like so:
|
||||||
|
|
||||||
|
~~~
|
||||||
|
Kohana::modules(array(
|
||||||
|
...
|
||||||
|
'auth' => MODPATH.'auth',
|
||||||
|
...
|
||||||
|
));
|
||||||
|
~~~
|
||||||
|
|
||||||
|
Next, you will then need to [configure](config) the auth module.
|
||||||
|
|
||||||
|
The auth module provides the [Auth::File] driver for you. There is also an auth driver included with the ORM module.
|
||||||
|
|
||||||
|
As your application needs change you may need to find another driver or [develop](driver/develop) your own.
|
62
modules/auth/guide/auth/login.md
Normal file
62
modules/auth/guide/auth/login.md
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
# Log in and out
|
||||||
|
|
||||||
|
The auth module provides methods to help you log users in and out of your application.
|
||||||
|
|
||||||
|
## Log in
|
||||||
|
|
||||||
|
The [Auth::login] method handles the login.
|
||||||
|
|
||||||
|
~~~
|
||||||
|
// Handled from a form with inputs with names email / password
|
||||||
|
$post = $this->request->post();
|
||||||
|
$success = Auth::instance()->login($post['email'], $post['password']);
|
||||||
|
|
||||||
|
if ($success)
|
||||||
|
{
|
||||||
|
// Login successful, send to app
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Login failed, send back to form with error message
|
||||||
|
}
|
||||||
|
~~~
|
||||||
|
|
||||||
|
## Logged in User
|
||||||
|
|
||||||
|
There are two ways to check if a user is logged in. If you just need to check if the user is logged in use [Auth::logged_in].
|
||||||
|
|
||||||
|
~~~
|
||||||
|
if (Auth::instance()->logged_in())
|
||||||
|
{
|
||||||
|
// User is logged in, continue on
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// User isn't logged in, redirect to the login form.
|
||||||
|
}
|
||||||
|
~~~
|
||||||
|
|
||||||
|
You can also get the logged in user object by using [Auth::get_user]. If the user is null, then no user was found.
|
||||||
|
|
||||||
|
~~~
|
||||||
|
$user = Auth::instance()->get_user();
|
||||||
|
|
||||||
|
// Check for a user (NULL if not user is found)
|
||||||
|
if ($user !== null)
|
||||||
|
{
|
||||||
|
// User is found, continue on
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// User was not found, redirect to the login form
|
||||||
|
}
|
||||||
|
~~~
|
||||||
|
|
||||||
|
## Log out
|
||||||
|
|
||||||
|
The [Auth::logout] method will take care of logging out a user.
|
||||||
|
|
||||||
|
~~~
|
||||||
|
Auth::instance()->logout();
|
||||||
|
// Redirect the user back to login page
|
||||||
|
~~~
|
6
modules/auth/guide/auth/menu.md
Normal file
6
modules/auth/guide/auth/menu.md
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
## [Auth]()
|
||||||
|
- [Configuration](config)
|
||||||
|
- [Log in and out](login)
|
||||||
|
- Drivers
|
||||||
|
- [File](driver/file)
|
||||||
|
- [Developing](driver/develop)
|
59
modules/cache/README.md
vendored
Normal file
59
modules/cache/README.md
vendored
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
Kohana Cache library
|
||||||
|
====================
|
||||||
|
|
||||||
|
The cache library for Kohana 3 provides a simple interface to the most common cache solutions. Developers are free to add their own caching solutions that follow the cache design pattern defined within this module.
|
||||||
|
|
||||||
|
Supported cache solutions
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
Currently this module supports the following cache methods.
|
||||||
|
|
||||||
|
1. APC
|
||||||
|
2. Memcache
|
||||||
|
3. Memcached-tags (Supports tags)
|
||||||
|
4. SQLite (Supports tags)
|
||||||
|
5. File
|
||||||
|
6. Wincache
|
||||||
|
|
||||||
|
Planned support
|
||||||
|
---------------
|
||||||
|
|
||||||
|
In the near future, additional support for the following methods will be included.
|
||||||
|
|
||||||
|
1. Memcached
|
||||||
|
|
||||||
|
Introduction to caching
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
To use caching to the maximum potential, your application should be designed with caching in mind from the outset. In general, the most effective caches contain lots of small collections of data that are the result of expensive computational operations, such as searching through a large data set.
|
||||||
|
|
||||||
|
There are many different caching methods available for PHP, from the very basic file based caching to opcode caching in eAccelerator and APC. Caching engines that use physical memory over disk based storage are always faster, however many do not support more advanced features such as tagging.
|
||||||
|
|
||||||
|
Using Cache
|
||||||
|
-----------
|
||||||
|
|
||||||
|
To use Kohana Cache, download and extract the latest stable release of Kohana Cache from [Github](http://github.com/samsoir/kohana-cache). Place the module into your Kohana instances modules folder. Finally enable the module within the application bootstrap within the section entitled _modules_.
|
||||||
|
|
||||||
|
Quick example
|
||||||
|
-------------
|
||||||
|
|
||||||
|
The following is a quick example of how to use Kohana Cache. The example is using the SQLite driver.
|
||||||
|
|
||||||
|
<?php
|
||||||
|
// Get a Sqlite Cache instance
|
||||||
|
$mycache = Cache::instance('sqlite');
|
||||||
|
|
||||||
|
// Create some data
|
||||||
|
$data = array('foo' => 'bar', 'apples' => 'pear', 'BDFL' => 'Shadowhand');
|
||||||
|
|
||||||
|
// Save the data to cache, with an id of test_id and a lifetime of 10 minutes
|
||||||
|
$mycache->set('test_id', $data, 600);
|
||||||
|
|
||||||
|
// Retrieve the data from cache
|
||||||
|
$retrieved_data = $mycache->get('test_id');
|
||||||
|
|
||||||
|
// Remove the cache item
|
||||||
|
$mycache->delete('test_id');
|
||||||
|
|
||||||
|
// Clear the cache of all stored items
|
||||||
|
$mycache->delete_all();
|
3
modules/cache/classes/Cache.php
vendored
Normal file
3
modules/cache/classes/Cache.php
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<?php defined('SYSPATH') or die('No direct script access.');
|
||||||
|
|
||||||
|
abstract class Cache extends Kohana_Cache {}
|
3
modules/cache/classes/Cache/Apc.php
vendored
Normal file
3
modules/cache/classes/Cache/Apc.php
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<?php defined('SYSPATH') or die('No direct script access.');
|
||||||
|
|
||||||
|
class Cache_Apc extends Kohana_Cache_Apc {}
|
3
modules/cache/classes/Cache/Arithmetic.php
vendored
Normal file
3
modules/cache/classes/Cache/Arithmetic.php
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<?php defined('SYSPATH') or die('No direct script access.');
|
||||||
|
|
||||||
|
interface Cache_Arithmetic extends Kohana_Cache_Arithmetic {}
|
3
modules/cache/classes/Cache/Exception.php
vendored
Normal file
3
modules/cache/classes/Cache/Exception.php
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<?php defined('SYSPATH') or die('No direct script access.');
|
||||||
|
|
||||||
|
class Cache_Exception extends Kohana_Cache_Exception {}
|
3
modules/cache/classes/Cache/File.php
vendored
Normal file
3
modules/cache/classes/Cache/File.php
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<?php defined('SYSPATH') or die('No direct script access.');
|
||||||
|
|
||||||
|
class Cache_File extends Kohana_Cache_File {}
|
3
modules/cache/classes/Cache/GarbageCollect.php
vendored
Normal file
3
modules/cache/classes/Cache/GarbageCollect.php
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<?php defined('SYSPATH') or die('No direct script access.');
|
||||||
|
|
||||||
|
interface Cache_GarbageCollect extends Kohana_Cache_GarbageCollect {}
|
3
modules/cache/classes/Cache/Memcache.php
vendored
Normal file
3
modules/cache/classes/Cache/Memcache.php
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<?php defined('SYSPATH') or die('No direct script access.');
|
||||||
|
|
||||||
|
class Cache_Memcache extends Kohana_Cache_Memcache {}
|
3
modules/cache/classes/Cache/MemcacheTag.php
vendored
Normal file
3
modules/cache/classes/Cache/MemcacheTag.php
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<?php defined('SYSPATH') or die('No direct script access.');
|
||||||
|
|
||||||
|
class Cache_MemcacheTag extends Kohana_Cache_MemcacheTag {}
|
3
modules/cache/classes/Cache/Sqlite.php
vendored
Normal file
3
modules/cache/classes/Cache/Sqlite.php
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<?php defined('SYSPATH') or die('No direct script access.');
|
||||||
|
|
||||||
|
class Cache_Sqlite extends Kohana_Cache_Sqlite {}
|
3
modules/cache/classes/Cache/Tagging.php
vendored
Normal file
3
modules/cache/classes/Cache/Tagging.php
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<?php defined('SYSPATH') or die('No direct script access.');
|
||||||
|
|
||||||
|
interface Cache_Tagging extends Kohana_Cache_Tagging {}
|
3
modules/cache/classes/Cache/Wincache.php
vendored
Normal file
3
modules/cache/classes/Cache/Wincache.php
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<?php defined('SYSPATH') or die('No direct script access.');
|
||||||
|
|
||||||
|
class Cache_Wincache extends Kohana_Cache_Wincache {}
|
3
modules/cache/classes/HTTP/Cache.php
vendored
Normal file
3
modules/cache/classes/HTTP/Cache.php
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<?php defined('SYSPATH') or die('No direct script access.');
|
||||||
|
|
||||||
|
class HTTP_Cache extends Kohana_HTTP_Cache {}
|
300
modules/cache/classes/Kohana/Cache.php
vendored
Normal file
300
modules/cache/classes/Kohana/Cache.php
vendored
Normal file
@ -0,0 +1,300 @@
|
|||||||
|
<?php defined('SYSPATH') or die('No direct script access.');
|
||||||
|
/**
|
||||||
|
* Kohana Cache provides a common interface to a variety of caching engines. Tags are
|
||||||
|
* supported where available natively to the cache system. Kohana Cache supports multiple
|
||||||
|
* instances of cache engines through a grouped singleton pattern.
|
||||||
|
*
|
||||||
|
* ### Supported cache engines
|
||||||
|
*
|
||||||
|
* * [APC](http://php.net/manual/en/book.apc.php)
|
||||||
|
* * [eAccelerator](http://eaccelerator.net/)
|
||||||
|
* * File
|
||||||
|
* * [Memcache](http://memcached.org/)
|
||||||
|
* * [Memcached-tags](http://code.google.com/p/memcached-tags/)
|
||||||
|
* * [SQLite](http://www.sqlite.org/)
|
||||||
|
* * [Xcache](http://xcache.lighttpd.net/)
|
||||||
|
*
|
||||||
|
* ### Introduction to caching
|
||||||
|
*
|
||||||
|
* Caching should be implemented with consideration. Generally, caching the result of resources
|
||||||
|
* is faster than reprocessing them. Choosing what, how and when to cache is vital. PHP APC is
|
||||||
|
* presently one of the fastest caching systems available, closely followed by Memcache. SQLite
|
||||||
|
* and File caching are two of the slowest cache methods, however usually faster than reprocessing
|
||||||
|
* a complex set of instructions.
|
||||||
|
*
|
||||||
|
* Caching engines that use memory are considerably faster than the file based alternatives. But
|
||||||
|
* memory is limited whereas disk space is plentiful. If caching large datasets it is best to use
|
||||||
|
* file caching.
|
||||||
|
*
|
||||||
|
* ### Configuration settings
|
||||||
|
*
|
||||||
|
* Kohana Cache uses configuration groups to create cache instances. A configuration group can
|
||||||
|
* use any supported driver, with successive groups using the same driver type if required.
|
||||||
|
*
|
||||||
|
* #### Configuration example
|
||||||
|
*
|
||||||
|
* Below is an example of a _memcache_ server configuration.
|
||||||
|
*
|
||||||
|
* return array(
|
||||||
|
* 'default' => array( // Default group
|
||||||
|
* 'driver' => 'memcache', // using Memcache driver
|
||||||
|
* 'servers' => array( // Available server definitions
|
||||||
|
* array(
|
||||||
|
* 'host' => 'localhost',
|
||||||
|
* 'port' => 11211,
|
||||||
|
* 'persistent' => FALSE
|
||||||
|
* )
|
||||||
|
* ),
|
||||||
|
* 'compression' => FALSE, // Use compression?
|
||||||
|
* ),
|
||||||
|
* )
|
||||||
|
*
|
||||||
|
* In cases where only one cache group is required, if the group is named `default` there is
|
||||||
|
* no need to pass the group name when instantiating a cache instance.
|
||||||
|
*
|
||||||
|
* #### General cache group configuration settings
|
||||||
|
*
|
||||||
|
* Below are the settings available to all types of cache driver.
|
||||||
|
*
|
||||||
|
* Name | Required | Description
|
||||||
|
* -------------- | -------- | ---------------------------------------------------------------
|
||||||
|
* driver | __YES__ | (_string_) The driver type to use
|
||||||
|
*
|
||||||
|
* Details of the settings specific to each driver are available within the drivers documentation.
|
||||||
|
*
|
||||||
|
* ### System requirements
|
||||||
|
*
|
||||||
|
* * Kohana 3.0.x
|
||||||
|
* * PHP 5.2.4 or greater
|
||||||
|
*
|
||||||
|
* @package Kohana/Cache
|
||||||
|
* @category Base
|
||||||
|
* @version 2.0
|
||||||
|
* @author Kohana Team
|
||||||
|
* @copyright (c) 2009-2012 Kohana Team
|
||||||
|
* @license http://kohanaphp.com/license
|
||||||
|
*/
|
||||||
|
abstract class Kohana_Cache {
|
||||||
|
|
||||||
|
const DEFAULT_EXPIRE = 3600;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string default driver to use
|
||||||
|
*/
|
||||||
|
public static $default = 'file';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Kohana_Cache instances
|
||||||
|
*/
|
||||||
|
public static $instances = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a singleton of a Kohana Cache group. If no group is supplied
|
||||||
|
* the __default__ cache group is used.
|
||||||
|
*
|
||||||
|
* // Create an instance of the default group
|
||||||
|
* $default_group = Cache::instance();
|
||||||
|
*
|
||||||
|
* // Create an instance of a group
|
||||||
|
* $foo_group = Cache::instance('foo');
|
||||||
|
*
|
||||||
|
* // Access an instantiated group directly
|
||||||
|
* $foo_group = Cache::$instances['default'];
|
||||||
|
*
|
||||||
|
* @param string $group the name of the cache group to use [Optional]
|
||||||
|
* @return Cache
|
||||||
|
* @throws Cache_Exception
|
||||||
|
*/
|
||||||
|
public static function instance($group = NULL)
|
||||||
|
{
|
||||||
|
// If there is no group supplied
|
||||||
|
if ($group === NULL)
|
||||||
|
{
|
||||||
|
// Use the default setting
|
||||||
|
$group = Cache::$default;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset(Cache::$instances[$group]))
|
||||||
|
{
|
||||||
|
// Return the current group if initiated already
|
||||||
|
return Cache::$instances[$group];
|
||||||
|
}
|
||||||
|
|
||||||
|
$config = Kohana::$config->load('cache');
|
||||||
|
|
||||||
|
if ( ! $config->offsetExists($group))
|
||||||
|
{
|
||||||
|
throw new Cache_Exception(
|
||||||
|
'Failed to load Kohana Cache group: :group',
|
||||||
|
array(':group' => $group)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$config = $config->get($group);
|
||||||
|
|
||||||
|
// Create a new cache type instance
|
||||||
|
$cache_class = 'Cache_'.ucfirst($config['driver']);
|
||||||
|
Cache::$instances[$group] = new $cache_class($config);
|
||||||
|
|
||||||
|
// Return the instance
|
||||||
|
return Cache::$instances[$group];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Config
|
||||||
|
*/
|
||||||
|
protected $_config = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures singleton pattern is observed, loads the default expiry
|
||||||
|
*
|
||||||
|
* @param array $config configuration
|
||||||
|
*/
|
||||||
|
protected function __construct(array $config)
|
||||||
|
{
|
||||||
|
$this->config($config);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Getter and setter for the configuration. If no argument provided, the
|
||||||
|
* current configuration is returned. Otherwise the configuration is set
|
||||||
|
* to this class.
|
||||||
|
*
|
||||||
|
* // Overwrite all configuration
|
||||||
|
* $cache->config(array('driver' => 'memcache', '...'));
|
||||||
|
*
|
||||||
|
* // Set a new configuration setting
|
||||||
|
* $cache->config('servers', array(
|
||||||
|
* 'foo' => 'bar',
|
||||||
|
* '...'
|
||||||
|
* ));
|
||||||
|
*
|
||||||
|
* // Get a configuration setting
|
||||||
|
* $servers = $cache->config('servers);
|
||||||
|
*
|
||||||
|
* @param mixed key to set to array, either array or config path
|
||||||
|
* @param mixed value to associate with key
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function config($key = NULL, $value = NULL)
|
||||||
|
{
|
||||||
|
if ($key === NULL)
|
||||||
|
return $this->_config;
|
||||||
|
|
||||||
|
if (is_array($key))
|
||||||
|
{
|
||||||
|
$this->_config = $key;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ($value === NULL)
|
||||||
|
return Arr::get($this->_config, $key);
|
||||||
|
|
||||||
|
$this->_config[$key] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overload the __clone() method to prevent cloning
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
* @throws Cache_Exception
|
||||||
|
*/
|
||||||
|
final public function __clone()
|
||||||
|
{
|
||||||
|
throw new Cache_Exception('Cloning of Kohana_Cache objects is forbidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a cached value entry by id.
|
||||||
|
*
|
||||||
|
* // Retrieve cache entry from default group
|
||||||
|
* $data = Cache::instance()->get('foo');
|
||||||
|
*
|
||||||
|
* // Retrieve cache entry from default group and return 'bar' if miss
|
||||||
|
* $data = Cache::instance()->get('foo', 'bar');
|
||||||
|
*
|
||||||
|
* // Retrieve cache entry from memcache group
|
||||||
|
* $data = Cache::instance('memcache')->get('foo');
|
||||||
|
*
|
||||||
|
* @param string $id id of cache to entry
|
||||||
|
* @param string $default default value to return if cache miss
|
||||||
|
* @return mixed
|
||||||
|
* @throws Cache_Exception
|
||||||
|
*/
|
||||||
|
abstract public function get($id, $default = NULL);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a value to cache with id and lifetime
|
||||||
|
*
|
||||||
|
* $data = 'bar';
|
||||||
|
*
|
||||||
|
* // Set 'bar' to 'foo' in default group, using default expiry
|
||||||
|
* Cache::instance()->set('foo', $data);
|
||||||
|
*
|
||||||
|
* // Set 'bar' to 'foo' in default group for 30 seconds
|
||||||
|
* Cache::instance()->set('foo', $data, 30);
|
||||||
|
*
|
||||||
|
* // Set 'bar' to 'foo' in memcache group for 10 minutes
|
||||||
|
* if (Cache::instance('memcache')->set('foo', $data, 600))
|
||||||
|
* {
|
||||||
|
* // Cache was set successfully
|
||||||
|
* return
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* @param string $id id of cache entry
|
||||||
|
* @param string $data data to set to cache
|
||||||
|
* @param integer $lifetime lifetime in seconds
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
abstract public function set($id, $data, $lifetime = 3600);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a cache entry based on id
|
||||||
|
*
|
||||||
|
* // Delete 'foo' entry from the default group
|
||||||
|
* Cache::instance()->delete('foo');
|
||||||
|
*
|
||||||
|
* // Delete 'foo' entry from the memcache group
|
||||||
|
* Cache::instance('memcache')->delete('foo')
|
||||||
|
*
|
||||||
|
* @param string $id id to remove from cache
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
abstract public function delete($id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete all cache entries.
|
||||||
|
*
|
||||||
|
* Beware of using this method when
|
||||||
|
* using shared memory cache systems, as it will wipe every
|
||||||
|
* entry within the system for all clients.
|
||||||
|
*
|
||||||
|
* // Delete all cache entries in the default group
|
||||||
|
* Cache::instance()->delete_all();
|
||||||
|
*
|
||||||
|
* // Delete all cache entries in the memcache group
|
||||||
|
* Cache::instance('memcache')->delete_all();
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
abstract public function delete_all();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces troublesome characters with underscores.
|
||||||
|
*
|
||||||
|
* // Sanitize a cache id
|
||||||
|
* $id = $this->_sanitize_id($id);
|
||||||
|
*
|
||||||
|
* @param string $id id of cache to sanitize
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function _sanitize_id($id)
|
||||||
|
{
|
||||||
|
// Change slashes and spaces to underscores
|
||||||
|
return str_replace(array('/', '\\', ' '), '_', $id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// End Kohana_Cache
|
166
modules/cache/classes/Kohana/Cache/Apc.php
vendored
Normal file
166
modules/cache/classes/Kohana/Cache/Apc.php
vendored
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
<?php defined('SYSPATH') or die('No direct script access.');
|
||||||
|
/**
|
||||||
|
* [Kohana Cache](api/Kohana_Cache) APC driver. Provides an opcode based
|
||||||
|
* driver for the Kohana Cache library.
|
||||||
|
*
|
||||||
|
* ### Configuration example
|
||||||
|
*
|
||||||
|
* Below is an example of an _apc_ server configuration.
|
||||||
|
*
|
||||||
|
* return array(
|
||||||
|
* 'apc' => array( // Driver group
|
||||||
|
* 'driver' => 'apc', // using APC driver
|
||||||
|
* ),
|
||||||
|
* )
|
||||||
|
*
|
||||||
|
* In cases where only one cache group is required, if the group is named `default` there is
|
||||||
|
* no need to pass the group name when instantiating a cache instance.
|
||||||
|
*
|
||||||
|
* #### General cache group configuration settings
|
||||||
|
*
|
||||||
|
* Below are the settings available to all types of cache driver.
|
||||||
|
*
|
||||||
|
* Name | Required | Description
|
||||||
|
* -------------- | -------- | ---------------------------------------------------------------
|
||||||
|
* driver | __YES__ | (_string_) The driver type to use
|
||||||
|
*
|
||||||
|
* ### System requirements
|
||||||
|
*
|
||||||
|
* * Kohana 3.0.x
|
||||||
|
* * PHP 5.2.4 or greater
|
||||||
|
* * APC PHP extension
|
||||||
|
*
|
||||||
|
* @package Kohana/Cache
|
||||||
|
* @category Base
|
||||||
|
* @author Kohana Team
|
||||||
|
* @copyright (c) 2009-2012 Kohana Team
|
||||||
|
* @license http://kohanaphp.com/license
|
||||||
|
*/
|
||||||
|
class Kohana_Cache_Apc extends Cache implements Cache_Arithmetic {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check for existence of the APC extension This method cannot be invoked externally. The driver must
|
||||||
|
* be instantiated using the `Cache::instance()` method.
|
||||||
|
*
|
||||||
|
* @param array $config configuration
|
||||||
|
* @throws Cache_Exception
|
||||||
|
*/
|
||||||
|
protected function __construct(array $config)
|
||||||
|
{
|
||||||
|
if ( ! extension_loaded('apc'))
|
||||||
|
{
|
||||||
|
throw new Cache_Exception('PHP APC extension is not available.');
|
||||||
|
}
|
||||||
|
|
||||||
|
parent::__construct($config);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a cached value entry by id.
|
||||||
|
*
|
||||||
|
* // Retrieve cache entry from apc group
|
||||||
|
* $data = Cache::instance('apc')->get('foo');
|
||||||
|
*
|
||||||
|
* // Retrieve cache entry from apc group and return 'bar' if miss
|
||||||
|
* $data = Cache::instance('apc')->get('foo', 'bar');
|
||||||
|
*
|
||||||
|
* @param string $id id of cache to entry
|
||||||
|
* @param string $default default value to return if cache miss
|
||||||
|
* @return mixed
|
||||||
|
* @throws Cache_Exception
|
||||||
|
*/
|
||||||
|
public function get($id, $default = NULL)
|
||||||
|
{
|
||||||
|
$data = apc_fetch($this->_sanitize_id($id), $success);
|
||||||
|
|
||||||
|
return $success ? $data : $default;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a value to cache with id and lifetime
|
||||||
|
*
|
||||||
|
* $data = 'bar';
|
||||||
|
*
|
||||||
|
* // Set 'bar' to 'foo' in apc group, using default expiry
|
||||||
|
* Cache::instance('apc')->set('foo', $data);
|
||||||
|
*
|
||||||
|
* // Set 'bar' to 'foo' in apc group for 30 seconds
|
||||||
|
* Cache::instance('apc')->set('foo', $data, 30);
|
||||||
|
*
|
||||||
|
* @param string $id id of cache entry
|
||||||
|
* @param string $data data to set to cache
|
||||||
|
* @param integer $lifetime lifetime in seconds
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function set($id, $data, $lifetime = NULL)
|
||||||
|
{
|
||||||
|
if ($lifetime === NULL)
|
||||||
|
{
|
||||||
|
$lifetime = Arr::get($this->_config, 'default_expire', Cache::DEFAULT_EXPIRE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return apc_store($this->_sanitize_id($id), $data, $lifetime);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a cache entry based on id
|
||||||
|
*
|
||||||
|
* // Delete 'foo' entry from the apc group
|
||||||
|
* Cache::instance('apc')->delete('foo');
|
||||||
|
*
|
||||||
|
* @param string $id id to remove from cache
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function delete($id)
|
||||||
|
{
|
||||||
|
return apc_delete($this->_sanitize_id($id));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete all cache entries.
|
||||||
|
*
|
||||||
|
* Beware of using this method when
|
||||||
|
* using shared memory cache systems, as it will wipe every
|
||||||
|
* entry within the system for all clients.
|
||||||
|
*
|
||||||
|
* // Delete all cache entries in the apc group
|
||||||
|
* Cache::instance('apc')->delete_all();
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function delete_all()
|
||||||
|
{
|
||||||
|
return apc_clear_cache('user');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increments a given value by the step value supplied.
|
||||||
|
* Useful for shared counters and other persistent integer based
|
||||||
|
* tracking.
|
||||||
|
*
|
||||||
|
* @param string id of cache entry to increment
|
||||||
|
* @param int step value to increment by
|
||||||
|
* @return integer
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function increment($id, $step = 1)
|
||||||
|
{
|
||||||
|
return apc_inc($id, $step);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrements a given value by the step value supplied.
|
||||||
|
* Useful for shared counters and other persistent integer based
|
||||||
|
* tracking.
|
||||||
|
*
|
||||||
|
* @param string id of cache entry to decrement
|
||||||
|
* @param int step value to decrement by
|
||||||
|
* @return integer
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function decrement($id, $step = 1)
|
||||||
|
{
|
||||||
|
return apc_dec($id, $step);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // End Kohana_Cache_Apc
|
39
modules/cache/classes/Kohana/Cache/Arithmetic.php
vendored
Normal file
39
modules/cache/classes/Kohana/Cache/Arithmetic.php
vendored
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<?php defined('SYSPATH') or die('No direct script access.');
|
||||||
|
/**
|
||||||
|
* Kohana Cache Arithmetic Interface, for basic cache integer based
|
||||||
|
* arithmetic, addition and subtraction
|
||||||
|
*
|
||||||
|
* @package Kohana/Cache
|
||||||
|
* @category Base
|
||||||
|
* @author Kohana Team
|
||||||
|
* @copyright (c) 2009-2012 Kohana Team
|
||||||
|
* @license http://kohanaphp.com/license
|
||||||
|
* @since 3.2.0
|
||||||
|
*/
|
||||||
|
interface Kohana_Cache_Arithmetic {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increments a given value by the step value supplied.
|
||||||
|
* Useful for shared counters and other persistent integer based
|
||||||
|
* tracking.
|
||||||
|
*
|
||||||
|
* @param string id of cache entry to increment
|
||||||
|
* @param int step value to increment by
|
||||||
|
* @return integer
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function increment($id, $step = 1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrements a given value by the step value supplied.
|
||||||
|
* Useful for shared counters and other persistent integer based
|
||||||
|
* tracking.
|
||||||
|
*
|
||||||
|
* @param string id of cache entry to decrement
|
||||||
|
* @param int step value to decrement by
|
||||||
|
* @return integer
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function decrement($id, $step = 1);
|
||||||
|
|
||||||
|
} // End Kohana_Cache_Arithmetic
|
11
modules/cache/classes/Kohana/Cache/Exception.php
vendored
Normal file
11
modules/cache/classes/Kohana/Cache/Exception.php
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?php defined('SYSPATH') or die('No direct script access.');
|
||||||
|
/**
|
||||||
|
* Kohana Cache Exception
|
||||||
|
*
|
||||||
|
* @package Kohana/Cache
|
||||||
|
* @category Base
|
||||||
|
* @author Kohana Team
|
||||||
|
* @copyright (c) 2009-2012 Kohana Team
|
||||||
|
* @license http://kohanaphp.com/license
|
||||||
|
*/
|
||||||
|
class Kohana_Cache_Exception extends Kohana_Exception {}
|
466
modules/cache/classes/Kohana/Cache/File.php
vendored
Normal file
466
modules/cache/classes/Kohana/Cache/File.php
vendored
Normal file
@ -0,0 +1,466 @@
|
|||||||
|
<?php defined('SYSPATH') or die('No direct script access.');
|
||||||
|
/**
|
||||||
|
* [Kohana Cache](api/Kohana_Cache) File driver. Provides a file based
|
||||||
|
* driver for the Kohana Cache library. This is one of the slowest
|
||||||
|
* caching methods.
|
||||||
|
*
|
||||||
|
* ### Configuration example
|
||||||
|
*
|
||||||
|
* Below is an example of a _file_ server configuration.
|
||||||
|
*
|
||||||
|
* return array(
|
||||||
|
* 'file' => array( // File driver group
|
||||||
|
* 'driver' => 'file', // using File driver
|
||||||
|
* 'cache_dir' => APPPATH.'cache/.kohana_cache', // Cache location
|
||||||
|
* ),
|
||||||
|
* )
|
||||||
|
*
|
||||||
|
* In cases where only one cache group is required, if the group is named `default` there is
|
||||||
|
* no need to pass the group name when instantiating a cache instance.
|
||||||
|
*
|
||||||
|
* #### General cache group configuration settings
|
||||||
|
*
|
||||||
|
* Below are the settings available to all types of cache driver.
|
||||||
|
*
|
||||||
|
* Name | Required | Description
|
||||||
|
* -------------- | -------- | ---------------------------------------------------------------
|
||||||
|
* driver | __YES__ | (_string_) The driver type to use
|
||||||
|
* cache_dir | __NO__ | (_string_) The cache directory to use for this cache instance
|
||||||
|
*
|
||||||
|
* ### System requirements
|
||||||
|
*
|
||||||
|
* * Kohana 3.0.x
|
||||||
|
* * PHP 5.2.4 or greater
|
||||||
|
*
|
||||||
|
* @package Kohana/Cache
|
||||||
|
* @category Base
|
||||||
|
* @author Kohana Team
|
||||||
|
* @copyright (c) 2009-2012 Kohana Team
|
||||||
|
* @license http://kohanaphp.com/license
|
||||||
|
*/
|
||||||
|
class Kohana_Cache_File extends Cache implements Cache_GarbageCollect {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a hashed filename based on the string. This is used
|
||||||
|
* to create shorter unique IDs for each cache filename.
|
||||||
|
*
|
||||||
|
* // Create the cache filename
|
||||||
|
* $filename = Cache_File::filename($this->_sanitize_id($id));
|
||||||
|
*
|
||||||
|
* @param string $string string to hash into filename
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected static function filename($string)
|
||||||
|
{
|
||||||
|
return sha1($string).'.cache';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string the caching directory
|
||||||
|
*/
|
||||||
|
protected $_cache_dir;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs the file cache driver. This method cannot be invoked externally. The file cache driver must
|
||||||
|
* be instantiated using the `Cache::instance()` method.
|
||||||
|
*
|
||||||
|
* @param array $config config
|
||||||
|
* @throws Cache_Exception
|
||||||
|
*/
|
||||||
|
protected function __construct(array $config)
|
||||||
|
{
|
||||||
|
// Setup parent
|
||||||
|
parent::__construct($config);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$directory = Arr::get($this->_config, 'cache_dir', Kohana::$cache_dir);
|
||||||
|
$this->_cache_dir = new SplFileInfo($directory);
|
||||||
|
}
|
||||||
|
// PHP < 5.3 exception handle
|
||||||
|
catch (ErrorException $e)
|
||||||
|
{
|
||||||
|
$this->_cache_dir = $this->_make_directory($directory, 0777, TRUE);
|
||||||
|
}
|
||||||
|
// PHP >= 5.3 exception handle
|
||||||
|
catch (UnexpectedValueException $e)
|
||||||
|
{
|
||||||
|
$this->_cache_dir = $this->_make_directory($directory, 0777, TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the defined directory is a file, get outta here
|
||||||
|
if ($this->_cache_dir->isFile())
|
||||||
|
{
|
||||||
|
throw new Cache_Exception('Unable to create cache directory as a file already exists : :resource', array(':resource' => $this->_cache_dir->getRealPath()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the read status of the directory
|
||||||
|
if ( ! $this->_cache_dir->isReadable())
|
||||||
|
{
|
||||||
|
throw new Cache_Exception('Unable to read from the cache directory :resource', array(':resource' => $this->_cache_dir->getRealPath()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the write status of the directory
|
||||||
|
if ( ! $this->_cache_dir->isWritable())
|
||||||
|
{
|
||||||
|
throw new Cache_Exception('Unable to write to the cache directory :resource', array(':resource' => $this->_cache_dir->getRealPath()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a cached value entry by id.
|
||||||
|
*
|
||||||
|
* // Retrieve cache entry from file group
|
||||||
|
* $data = Cache::instance('file')->get('foo');
|
||||||
|
*
|
||||||
|
* // Retrieve cache entry from file group and return 'bar' if miss
|
||||||
|
* $data = Cache::instance('file')->get('foo', 'bar');
|
||||||
|
*
|
||||||
|
* @param string $id id of cache to entry
|
||||||
|
* @param string $default default value to return if cache miss
|
||||||
|
* @return mixed
|
||||||
|
* @throws Cache_Exception
|
||||||
|
*/
|
||||||
|
public function get($id, $default = NULL)
|
||||||
|
{
|
||||||
|
$filename = Cache_File::filename($this->_sanitize_id($id));
|
||||||
|
$directory = $this->_resolve_directory($filename);
|
||||||
|
|
||||||
|
// Wrap operations in try/catch to handle notices
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Open file
|
||||||
|
$file = new SplFileInfo($directory.$filename);
|
||||||
|
|
||||||
|
// If file does not exist
|
||||||
|
if ( ! $file->isFile())
|
||||||
|
{
|
||||||
|
// Return default value
|
||||||
|
return $default;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Open the file and parse data
|
||||||
|
$created = $file->getMTime();
|
||||||
|
$data = $file->openFile();
|
||||||
|
$lifetime = $data->fgets();
|
||||||
|
|
||||||
|
// If we're at the EOF at this point, corrupted!
|
||||||
|
if ($data->eof())
|
||||||
|
{
|
||||||
|
throw new Cache_Exception(__METHOD__.' corrupted cache file!');
|
||||||
|
}
|
||||||
|
|
||||||
|
$cache = '';
|
||||||
|
|
||||||
|
while ($data->eof() === FALSE)
|
||||||
|
{
|
||||||
|
$cache .= $data->fgets();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test the expiry
|
||||||
|
if (($created + (int) $lifetime) < time())
|
||||||
|
{
|
||||||
|
// Delete the file
|
||||||
|
$this->_delete_file($file, NULL, TRUE);
|
||||||
|
return $default;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return unserialize($cache);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (ErrorException $e)
|
||||||
|
{
|
||||||
|
// Handle ErrorException caused by failed unserialization
|
||||||
|
if ($e->getCode() === E_NOTICE)
|
||||||
|
{
|
||||||
|
throw new Cache_Exception(__METHOD__.' failed to unserialize cached object with message : '.$e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise throw the exception
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a value to cache with id and lifetime
|
||||||
|
*
|
||||||
|
* $data = 'bar';
|
||||||
|
*
|
||||||
|
* // Set 'bar' to 'foo' in file group, using default expiry
|
||||||
|
* Cache::instance('file')->set('foo', $data);
|
||||||
|
*
|
||||||
|
* // Set 'bar' to 'foo' in file group for 30 seconds
|
||||||
|
* Cache::instance('file')->set('foo', $data, 30);
|
||||||
|
*
|
||||||
|
* @param string $id id of cache entry
|
||||||
|
* @param string $data data to set to cache
|
||||||
|
* @param integer $lifetime lifetime in seconds
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function set($id, $data, $lifetime = NULL)
|
||||||
|
{
|
||||||
|
$filename = Cache_File::filename($this->_sanitize_id($id));
|
||||||
|
$directory = $this->_resolve_directory($filename);
|
||||||
|
|
||||||
|
// If lifetime is NULL
|
||||||
|
if ($lifetime === NULL)
|
||||||
|
{
|
||||||
|
// Set to the default expiry
|
||||||
|
$lifetime = Arr::get($this->_config, 'default_expire', Cache::DEFAULT_EXPIRE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open directory
|
||||||
|
$dir = new SplFileInfo($directory);
|
||||||
|
|
||||||
|
// If the directory path is not a directory
|
||||||
|
if ( ! $dir->isDir())
|
||||||
|
{
|
||||||
|
// Create the directory
|
||||||
|
if ( ! mkdir($directory, 0777, TRUE))
|
||||||
|
{
|
||||||
|
throw new Cache_Exception(__METHOD__.' unable to create directory : :directory', array(':directory' => $directory));
|
||||||
|
}
|
||||||
|
|
||||||
|
// chmod to solve potential umask issues
|
||||||
|
chmod($directory, 0777);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open file to inspect
|
||||||
|
$resouce = new SplFileInfo($directory.$filename);
|
||||||
|
$file = $resouce->openFile('w');
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$data = $lifetime."\n".serialize($data);
|
||||||
|
$file->fwrite($data, strlen($data));
|
||||||
|
return (bool) $file->fflush();
|
||||||
|
}
|
||||||
|
catch (ErrorException $e)
|
||||||
|
{
|
||||||
|
// If serialize through an error exception
|
||||||
|
if ($e->getCode() === E_NOTICE)
|
||||||
|
{
|
||||||
|
// Throw a caching error
|
||||||
|
throw new Cache_Exception(__METHOD__.' failed to serialize data for caching with message : '.$e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Else rethrow the error exception
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a cache entry based on id
|
||||||
|
*
|
||||||
|
* // Delete 'foo' entry from the file group
|
||||||
|
* Cache::instance('file')->delete('foo');
|
||||||
|
*
|
||||||
|
* @param string $id id to remove from cache
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function delete($id)
|
||||||
|
{
|
||||||
|
$filename = Cache_File::filename($this->_sanitize_id($id));
|
||||||
|
$directory = $this->_resolve_directory($filename);
|
||||||
|
|
||||||
|
return $this->_delete_file(new SplFileInfo($directory.$filename), NULL, TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete all cache entries.
|
||||||
|
*
|
||||||
|
* Beware of using this method when
|
||||||
|
* using shared memory cache systems, as it will wipe every
|
||||||
|
* entry within the system for all clients.
|
||||||
|
*
|
||||||
|
* // Delete all cache entries in the file group
|
||||||
|
* Cache::instance('file')->delete_all();
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function delete_all()
|
||||||
|
{
|
||||||
|
return $this->_delete_file($this->_cache_dir, TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Garbage collection method that cleans any expired
|
||||||
|
* cache entries from the cache.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function garbage_collect()
|
||||||
|
{
|
||||||
|
$this->_delete_file($this->_cache_dir, TRUE, FALSE, TRUE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes files recursively and returns FALSE on any errors
|
||||||
|
*
|
||||||
|
* // Delete a file or folder whilst retaining parent directory and ignore all errors
|
||||||
|
* $this->_delete_file($folder, TRUE, TRUE);
|
||||||
|
*
|
||||||
|
* @param SplFileInfo $file file
|
||||||
|
* @param boolean $retain_parent_directory retain the parent directory
|
||||||
|
* @param boolean $ignore_errors ignore_errors to prevent all exceptions interrupting exec
|
||||||
|
* @param boolean $only_expired only expired files
|
||||||
|
* @return boolean
|
||||||
|
* @throws Cache_Exception
|
||||||
|
*/
|
||||||
|
protected function _delete_file(SplFileInfo $file, $retain_parent_directory = FALSE, $ignore_errors = FALSE, $only_expired = FALSE)
|
||||||
|
{
|
||||||
|
// Allow graceful error handling
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// If is file
|
||||||
|
if ($file->isFile())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Handle ignore files
|
||||||
|
if (in_array($file->getFilename(), $this->config('ignore_on_delete')))
|
||||||
|
{
|
||||||
|
$delete = FALSE;
|
||||||
|
}
|
||||||
|
// If only expired is not set
|
||||||
|
elseif ($only_expired === FALSE)
|
||||||
|
{
|
||||||
|
// We want to delete the file
|
||||||
|
$delete = TRUE;
|
||||||
|
}
|
||||||
|
// Otherwise...
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Assess the file expiry to flag it for deletion
|
||||||
|
$json = $file->openFile('r')->current();
|
||||||
|
$data = json_decode($json);
|
||||||
|
$delete = $data->expiry < time();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the delete flag is set delete file
|
||||||
|
if ($delete === TRUE)
|
||||||
|
return unlink($file->getRealPath());
|
||||||
|
else
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
catch (ErrorException $e)
|
||||||
|
{
|
||||||
|
// Catch any delete file warnings
|
||||||
|
if ($e->getCode() === E_WARNING)
|
||||||
|
{
|
||||||
|
throw new Cache_Exception(__METHOD__.' failed to delete file : :file', array(':file' => $file->getRealPath()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Else, is directory
|
||||||
|
elseif ($file->isDir())
|
||||||
|
{
|
||||||
|
// Create new DirectoryIterator
|
||||||
|
$files = new DirectoryIterator($file->getPathname());
|
||||||
|
|
||||||
|
// Iterate over each entry
|
||||||
|
while ($files->valid())
|
||||||
|
{
|
||||||
|
// Extract the entry name
|
||||||
|
$name = $files->getFilename();
|
||||||
|
|
||||||
|
// If the name is not a dot
|
||||||
|
if ($name != '.' AND $name != '..')
|
||||||
|
{
|
||||||
|
// Create new file resource
|
||||||
|
$fp = new SplFileInfo($files->getRealPath());
|
||||||
|
// Delete the file
|
||||||
|
$this->_delete_file($fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move the file pointer on
|
||||||
|
$files->next();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If set to retain parent directory, return now
|
||||||
|
if ($retain_parent_directory)
|
||||||
|
{
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Remove the files iterator
|
||||||
|
// (fixes Windows PHP which has permission issues with open iterators)
|
||||||
|
unset($files);
|
||||||
|
|
||||||
|
// Try to remove the parent directory
|
||||||
|
return rmdir($file->getRealPath());
|
||||||
|
}
|
||||||
|
catch (ErrorException $e)
|
||||||
|
{
|
||||||
|
// Catch any delete directory warnings
|
||||||
|
if ($e->getCode() === E_WARNING)
|
||||||
|
{
|
||||||
|
throw new Cache_Exception(__METHOD__.' failed to delete directory : :directory', array(':directory' => $file->getRealPath()));
|
||||||
|
}
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// We get here if a file has already been deleted
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Catch all exceptions
|
||||||
|
catch (Exception $e)
|
||||||
|
{
|
||||||
|
// If ignore_errors is on
|
||||||
|
if ($ignore_errors === TRUE)
|
||||||
|
{
|
||||||
|
// Return
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
// Throw exception
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves the cache directory real path from the filename
|
||||||
|
*
|
||||||
|
* // Get the realpath of the cache folder
|
||||||
|
* $realpath = $this->_resolve_directory($filename);
|
||||||
|
*
|
||||||
|
* @param string $filename filename to resolve
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function _resolve_directory($filename)
|
||||||
|
{
|
||||||
|
return $this->_cache_dir->getRealPath().DIRECTORY_SEPARATOR.$filename[0].$filename[1].DIRECTORY_SEPARATOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes the cache directory if it doesn't exist. Simply a wrapper for
|
||||||
|
* `mkdir` to ensure DRY principles
|
||||||
|
*
|
||||||
|
* @link http://php.net/manual/en/function.mkdir.php
|
||||||
|
* @param string $directory
|
||||||
|
* @param integer $mode
|
||||||
|
* @param boolean $recursive
|
||||||
|
* @param resource $context
|
||||||
|
* @return SplFileInfo
|
||||||
|
* @throws Cache_Exception
|
||||||
|
*/
|
||||||
|
protected function _make_directory($directory, $mode = 0777, $recursive = FALSE, $context = NULL)
|
||||||
|
{
|
||||||
|
if ( ! mkdir($directory, $mode, $recursive, $context))
|
||||||
|
{
|
||||||
|
throw new Cache_Exception('Failed to create the defined cache directory : :directory', array(':directory' => $directory));
|
||||||
|
}
|
||||||
|
chmod($directory, $mode);
|
||||||
|
|
||||||
|
return new SplFileInfo($directory);
|
||||||
|
}
|
||||||
|
}
|
23
modules/cache/classes/Kohana/Cache/GarbageCollect.php
vendored
Normal file
23
modules/cache/classes/Kohana/Cache/GarbageCollect.php
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?php defined('SYSPATH') or die('No direct script access.');
|
||||||
|
/**
|
||||||
|
* Garbage Collection interface for caches that have no GC methods
|
||||||
|
* of their own, such as [Cache_File] and [Cache_Sqlite]. Memory based
|
||||||
|
* cache systems clean their own caches periodically.
|
||||||
|
*
|
||||||
|
* @package Kohana/Cache
|
||||||
|
* @category Base
|
||||||
|
* @version 2.0
|
||||||
|
* @author Kohana Team
|
||||||
|
* @copyright (c) 2009-2012 Kohana Team
|
||||||
|
* @license http://kohanaphp.com/license
|
||||||
|
* @since 3.0.8
|
||||||
|
*/
|
||||||
|
interface Kohana_Cache_GarbageCollect {
|
||||||
|
/**
|
||||||
|
* Garbage collection method that cleans any expired
|
||||||
|
* cache entries from the cache.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function garbage_collect();
|
||||||
|
}
|
354
modules/cache/classes/Kohana/Cache/Memcache.php
vendored
Normal file
354
modules/cache/classes/Kohana/Cache/Memcache.php
vendored
Normal file
@ -0,0 +1,354 @@
|
|||||||
|
<?php defined('SYSPATH') or die('No direct script access.');
|
||||||
|
/**
|
||||||
|
* [Kohana Cache](api/Kohana_Cache) Memcache driver,
|
||||||
|
*
|
||||||
|
* ### Supported cache engines
|
||||||
|
*
|
||||||
|
* * [Memcache](http://www.php.net/manual/en/book.memcache.php)
|
||||||
|
* * [Memcached-tags](http://code.google.com/p/memcached-tags/)
|
||||||
|
*
|
||||||
|
* ### Configuration example
|
||||||
|
*
|
||||||
|
* Below is an example of a _memcache_ server configuration.
|
||||||
|
*
|
||||||
|
* return array(
|
||||||
|
* 'default' => array( // Default group
|
||||||
|
* 'driver' => 'memcache', // using Memcache driver
|
||||||
|
* 'servers' => array( // Available server definitions
|
||||||
|
* // First memcache server server
|
||||||
|
* array(
|
||||||
|
* 'host' => 'localhost',
|
||||||
|
* 'port' => 11211,
|
||||||
|
* 'persistent' => FALSE
|
||||||
|
* 'weight' => 1,
|
||||||
|
* 'timeout' => 1,
|
||||||
|
* 'retry_interval' => 15,
|
||||||
|
* 'status' => TRUE,
|
||||||
|
* 'instant_death' => TRUE,
|
||||||
|
* 'failure_callback' => array('className', 'classMethod')
|
||||||
|
* ),
|
||||||
|
* // Second memcache server
|
||||||
|
* array(
|
||||||
|
* 'host' => '192.168.1.5',
|
||||||
|
* 'port' => 22122,
|
||||||
|
* 'persistent' => TRUE
|
||||||
|
* )
|
||||||
|
* ),
|
||||||
|
* 'compression' => FALSE, // Use compression?
|
||||||
|
* ),
|
||||||
|
* )
|
||||||
|
*
|
||||||
|
* In cases where only one cache group is required, if the group is named `default` there is
|
||||||
|
* no need to pass the group name when instantiating a cache instance.
|
||||||
|
*
|
||||||
|
* #### General cache group configuration settings
|
||||||
|
*
|
||||||
|
* Below are the settings available to all types of cache driver.
|
||||||
|
*
|
||||||
|
* Name | Required | Description
|
||||||
|
* -------------- | -------- | ---------------------------------------------------------------
|
||||||
|
* driver | __YES__ | (_string_) The driver type to use
|
||||||
|
* servers | __YES__ | (_array_) Associative array of server details, must include a __host__ key. (see _Memcache server configuration_ below)
|
||||||
|
* compression | __NO__ | (_boolean_) Use data compression when caching
|
||||||
|
*
|
||||||
|
* #### Memcache server configuration
|
||||||
|
*
|
||||||
|
* The following settings should be used when defining each memcache server
|
||||||
|
*
|
||||||
|
* Name | Required | Description
|
||||||
|
* ---------------- | -------- | ---------------------------------------------------------------
|
||||||
|
* host | __YES__ | (_string_) The host of the memcache server, i.e. __localhost__; or __127.0.0.1__; or __memcache.domain.tld__
|
||||||
|
* port | __NO__ | (_integer_) Point to the port where memcached is listening for connections. Set this parameter to 0 when using UNIX domain sockets. Default __11211__
|
||||||
|
* persistent | __NO__ | (_boolean_) Controls the use of a persistent connection. Default __TRUE__
|
||||||
|
* weight | __NO__ | (_integer_) Number of buckets to create for this server which in turn control its probability of it being selected. The probability is relative to the total weight of all servers. Default __1__
|
||||||
|
* timeout | __NO__ | (_integer_) Value in seconds which will be used for connecting to the daemon. Think twice before changing the default value of 1 second - you can lose all the advantages of caching if your connection is too slow. Default __1__
|
||||||
|
* retry_interval | __NO__ | (_integer_) Controls how often a failed server will be retried, the default value is 15 seconds. Setting this parameter to -1 disables automatic retry. Default __15__
|
||||||
|
* status | __NO__ | (_boolean_) Controls if the server should be flagged as online. Default __TRUE__
|
||||||
|
* failure_callback | __NO__ | (_[callback](http://www.php.net/manual/en/language.pseudo-types.php#language.types.callback)_) Allows the user to specify a callback function to run upon encountering an error. The callback is run before failover is attempted. The function takes two parameters, the hostname and port of the failed server. Default __NULL__
|
||||||
|
*
|
||||||
|
* ### System requirements
|
||||||
|
*
|
||||||
|
* * Kohana 3.0.x
|
||||||
|
* * PHP 5.2.4 or greater
|
||||||
|
* * Memcache (plus Memcached-tags for native tagging support)
|
||||||
|
* * Zlib
|
||||||
|
*
|
||||||
|
* @package Kohana/Cache
|
||||||
|
* @category Base
|
||||||
|
* @version 2.0
|
||||||
|
* @author Kohana Team
|
||||||
|
* @copyright (c) 2009-2012 Kohana Team
|
||||||
|
* @license http://kohanaphp.com/license
|
||||||
|
*/
|
||||||
|
class Kohana_Cache_Memcache extends Cache implements Cache_Arithmetic {
|
||||||
|
|
||||||
|
// Memcache has a maximum cache lifetime of 30 days
|
||||||
|
const CACHE_CEILING = 2592000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Memcache resource
|
||||||
|
*
|
||||||
|
* @var Memcache
|
||||||
|
*/
|
||||||
|
protected $_memcache;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flags to use when storing values
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $_flags;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default configuration for the memcached server
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $_default_config = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs the memcache Kohana_Cache object
|
||||||
|
*
|
||||||
|
* @param array $config configuration
|
||||||
|
* @throws Cache_Exception
|
||||||
|
*/
|
||||||
|
protected function __construct(array $config)
|
||||||
|
{
|
||||||
|
// Check for the memcache extention
|
||||||
|
if ( ! extension_loaded('memcache'))
|
||||||
|
{
|
||||||
|
throw new Cache_Exception('Memcache PHP extention not loaded');
|
||||||
|
}
|
||||||
|
|
||||||
|
parent::__construct($config);
|
||||||
|
|
||||||
|
// Setup Memcache
|
||||||
|
$this->_memcache = new Memcache;
|
||||||
|
|
||||||
|
// Load servers from configuration
|
||||||
|
$servers = Arr::get($this->_config, 'servers', NULL);
|
||||||
|
|
||||||
|
if ( ! $servers)
|
||||||
|
{
|
||||||
|
// Throw an exception if no server found
|
||||||
|
throw new Cache_Exception('No Memcache servers defined in configuration');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup default server configuration
|
||||||
|
$this->_default_config = array(
|
||||||
|
'host' => 'localhost',
|
||||||
|
'port' => 11211,
|
||||||
|
'persistent' => FALSE,
|
||||||
|
'weight' => 1,
|
||||||
|
'timeout' => 1,
|
||||||
|
'retry_interval' => 15,
|
||||||
|
'status' => TRUE,
|
||||||
|
'instant_death' => TRUE,
|
||||||
|
'failure_callback' => array($this, '_failed_request'),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add the memcache servers to the pool
|
||||||
|
foreach ($servers as $server)
|
||||||
|
{
|
||||||
|
// Merge the defined config with defaults
|
||||||
|
$server += $this->_default_config;
|
||||||
|
|
||||||
|
if ( ! $this->_memcache->addServer($server['host'], $server['port'], $server['persistent'], $server['weight'], $server['timeout'], $server['retry_interval'], $server['status'], $server['failure_callback']))
|
||||||
|
{
|
||||||
|
throw new Cache_Exception('Memcache could not connect to host \':host\' using port \':port\'', array(':host' => $server['host'], ':port' => $server['port']));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup the flags
|
||||||
|
$this->_flags = Arr::get($this->_config, 'compression', FALSE) ? MEMCACHE_COMPRESSED : FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a cached value entry by id.
|
||||||
|
*
|
||||||
|
* // Retrieve cache entry from memcache group
|
||||||
|
* $data = Cache::instance('memcache')->get('foo');
|
||||||
|
*
|
||||||
|
* // Retrieve cache entry from memcache group and return 'bar' if miss
|
||||||
|
* $data = Cache::instance('memcache')->get('foo', 'bar');
|
||||||
|
*
|
||||||
|
* @param string $id id of cache to entry
|
||||||
|
* @param string $default default value to return if cache miss
|
||||||
|
* @return mixed
|
||||||
|
* @throws Cache_Exception
|
||||||
|
*/
|
||||||
|
public function get($id, $default = NULL)
|
||||||
|
{
|
||||||
|
// Get the value from Memcache
|
||||||
|
$value = $this->_memcache->get($this->_sanitize_id($id));
|
||||||
|
|
||||||
|
// If the value wasn't found, normalise it
|
||||||
|
if ($value === FALSE)
|
||||||
|
{
|
||||||
|
$value = (NULL === $default) ? NULL : $default;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the value
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a value to cache with id and lifetime
|
||||||
|
*
|
||||||
|
* $data = 'bar';
|
||||||
|
*
|
||||||
|
* // Set 'bar' to 'foo' in memcache group for 10 minutes
|
||||||
|
* if (Cache::instance('memcache')->set('foo', $data, 600))
|
||||||
|
* {
|
||||||
|
* // Cache was set successfully
|
||||||
|
* return
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* @param string $id id of cache entry
|
||||||
|
* @param mixed $data data to set to cache
|
||||||
|
* @param integer $lifetime lifetime in seconds, maximum value 2592000
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function set($id, $data, $lifetime = 3600)
|
||||||
|
{
|
||||||
|
// If the lifetime is greater than the ceiling
|
||||||
|
if ($lifetime > Cache_Memcache::CACHE_CEILING)
|
||||||
|
{
|
||||||
|
// Set the lifetime to maximum cache time
|
||||||
|
$lifetime = Cache_Memcache::CACHE_CEILING + time();
|
||||||
|
}
|
||||||
|
// Else if the lifetime is greater than zero
|
||||||
|
elseif ($lifetime > 0)
|
||||||
|
{
|
||||||
|
$lifetime += time();
|
||||||
|
}
|
||||||
|
// Else
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Normalise the lifetime
|
||||||
|
$lifetime = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the data to memcache
|
||||||
|
return $this->_memcache->set($this->_sanitize_id($id), $data, $this->_flags, $lifetime);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a cache entry based on id
|
||||||
|
*
|
||||||
|
* // Delete the 'foo' cache entry immediately
|
||||||
|
* Cache::instance('memcache')->delete('foo');
|
||||||
|
*
|
||||||
|
* // Delete the 'bar' cache entry after 30 seconds
|
||||||
|
* Cache::instance('memcache')->delete('bar', 30);
|
||||||
|
*
|
||||||
|
* @param string $id id of entry to delete
|
||||||
|
* @param integer $timeout timeout of entry, if zero item is deleted immediately, otherwise the item will delete after the specified value in seconds
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function delete($id, $timeout = 0)
|
||||||
|
{
|
||||||
|
// Delete the id
|
||||||
|
return $this->_memcache->delete($this->_sanitize_id($id), $timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete all cache entries.
|
||||||
|
*
|
||||||
|
* Beware of using this method when
|
||||||
|
* using shared memory cache systems, as it will wipe every
|
||||||
|
* entry within the system for all clients.
|
||||||
|
*
|
||||||
|
* // Delete all cache entries in the default group
|
||||||
|
* Cache::instance('memcache')->delete_all();
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function delete_all()
|
||||||
|
{
|
||||||
|
$result = $this->_memcache->flush();
|
||||||
|
|
||||||
|
// We must sleep after flushing, or overwriting will not work!
|
||||||
|
// @see http://php.net/manual/en/function.memcache-flush.php#81420
|
||||||
|
sleep(1);
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback method for Memcache::failure_callback to use if any Memcache call
|
||||||
|
* on a particular server fails. This method switches off that instance of the
|
||||||
|
* server if the configuration setting `instant_death` is set to `TRUE`.
|
||||||
|
*
|
||||||
|
* @param string $hostname
|
||||||
|
* @param integer $port
|
||||||
|
* @return void|boolean
|
||||||
|
* @since 3.0.8
|
||||||
|
*/
|
||||||
|
public function _failed_request($hostname, $port)
|
||||||
|
{
|
||||||
|
if ( ! $this->_config['instant_death'])
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Setup non-existent host
|
||||||
|
$host = FALSE;
|
||||||
|
|
||||||
|
// Get host settings from configuration
|
||||||
|
foreach ($this->_config['servers'] as $server)
|
||||||
|
{
|
||||||
|
// Merge the defaults, since they won't always be set
|
||||||
|
$server += $this->_default_config;
|
||||||
|
// We're looking at the failed server
|
||||||
|
if ($hostname == $server['host'] and $port == $server['port'])
|
||||||
|
{
|
||||||
|
// Server to disable, since it failed
|
||||||
|
$host = $server;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! $host)
|
||||||
|
return;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return $this->_memcache->setServerParams(
|
||||||
|
$host['host'],
|
||||||
|
$host['port'],
|
||||||
|
$host['timeout'],
|
||||||
|
$host['retry_interval'],
|
||||||
|
FALSE, // Server is offline
|
||||||
|
array($this, '_failed_request'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increments a given value by the step value supplied.
|
||||||
|
* Useful for shared counters and other persistent integer based
|
||||||
|
* tracking.
|
||||||
|
*
|
||||||
|
* @param string id of cache entry to increment
|
||||||
|
* @param int step value to increment by
|
||||||
|
* @return integer
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function increment($id, $step = 1)
|
||||||
|
{
|
||||||
|
return $this->_memcache->increment($id, $step);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrements a given value by the step value supplied.
|
||||||
|
* Useful for shared counters and other persistent integer based
|
||||||
|
* tracking.
|
||||||
|
*
|
||||||
|
* @param string id of cache entry to decrement
|
||||||
|
* @param int step value to decrement by
|
||||||
|
* @return integer
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function decrement($id, $step = 1)
|
||||||
|
{
|
||||||
|
return $this->_memcache->decrement($id, $step);
|
||||||
|
}
|
||||||
|
}
|
78
modules/cache/classes/Kohana/Cache/MemcacheTag.php
vendored
Normal file
78
modules/cache/classes/Kohana/Cache/MemcacheTag.php
vendored
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
<?php defined('SYSPATH') or die('No direct script access.');
|
||||||
|
/**
|
||||||
|
* See [Kohana_Cache_Memcache]
|
||||||
|
*
|
||||||
|
* @package Kohana/Cache
|
||||||
|
* @category Base
|
||||||
|
* @version 2.0
|
||||||
|
* @author Kohana Team
|
||||||
|
* @copyright (c) 2009-2012 Kohana Team
|
||||||
|
* @license http://kohanaphp.com/license
|
||||||
|
*/
|
||||||
|
class Kohana_Cache_MemcacheTag extends Cache_Memcache implements Cache_Tagging {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs the memcache object
|
||||||
|
*
|
||||||
|
* @param array $config configuration
|
||||||
|
* @throws Cache_Exception
|
||||||
|
*/
|
||||||
|
protected function __construct(array $config)
|
||||||
|
{
|
||||||
|
parent::__construct($config);
|
||||||
|
|
||||||
|
if ( ! method_exists($this->_memcache, 'tag_add'))
|
||||||
|
{
|
||||||
|
throw new Cache_Exception('Memcached-tags PHP plugin not present. Please see http://code.google.com/p/memcached-tags/ for more information');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a value based on an id with tags
|
||||||
|
*
|
||||||
|
* @param string $id id
|
||||||
|
* @param mixed $data data
|
||||||
|
* @param integer $lifetime lifetime [Optional]
|
||||||
|
* @param array $tags tags [Optional]
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function set_with_tags($id, $data, $lifetime = NULL, array $tags = NULL)
|
||||||
|
{
|
||||||
|
$id = $this->_sanitize_id($id);
|
||||||
|
|
||||||
|
$result = $this->set($id, $data, $lifetime);
|
||||||
|
|
||||||
|
if ($result and $tags)
|
||||||
|
{
|
||||||
|
foreach ($tags as $tag)
|
||||||
|
{
|
||||||
|
$this->_memcache->tag_add($tag, $id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete cache entries based on a tag
|
||||||
|
*
|
||||||
|
* @param string $tag tag
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function delete_tag($tag)
|
||||||
|
{
|
||||||
|
return $this->_memcache->tag_delete($tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find cache entries based on a tag
|
||||||
|
*
|
||||||
|
* @param string $tag tag
|
||||||
|
* @return void
|
||||||
|
* @throws Cache_Exception
|
||||||
|
*/
|
||||||
|
public function find($tag)
|
||||||
|
{
|
||||||
|
throw new Cache_Exception('Memcached-tags does not support finding by tag');
|
||||||
|
}
|
||||||
|
}
|
334
modules/cache/classes/Kohana/Cache/Sqlite.php
vendored
Normal file
334
modules/cache/classes/Kohana/Cache/Sqlite.php
vendored
Normal file
@ -0,0 +1,334 @@
|
|||||||
|
<?php defined('SYSPATH') or die('No direct script access.');
|
||||||
|
/**
|
||||||
|
* Kohana Cache Sqlite Driver
|
||||||
|
*
|
||||||
|
* Requires SQLite3 and PDO
|
||||||
|
*
|
||||||
|
* @package Kohana/Cache
|
||||||
|
* @category Base
|
||||||
|
* @author Kohana Team
|
||||||
|
* @copyright (c) 2009-2012 Kohana Team
|
||||||
|
* @license http://kohanaphp.com/license
|
||||||
|
*/
|
||||||
|
class Kohana_Cache_Sqlite extends Cache implements Cache_Tagging, Cache_GarbageCollect {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Database resource
|
||||||
|
*
|
||||||
|
* @var PDO
|
||||||
|
*/
|
||||||
|
protected $_db;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets up the PDO SQLite table and
|
||||||
|
* initialises the PDO connection
|
||||||
|
*
|
||||||
|
* @param array $config configuration
|
||||||
|
* @throws Cache_Exception
|
||||||
|
*/
|
||||||
|
protected function __construct(array $config)
|
||||||
|
{
|
||||||
|
parent::__construct($config);
|
||||||
|
|
||||||
|
$database = Arr::get($this->_config, 'database', NULL);
|
||||||
|
|
||||||
|
if ($database === NULL)
|
||||||
|
{
|
||||||
|
throw new Cache_Exception('Database path not available in Kohana Cache configuration');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load new Sqlite DB
|
||||||
|
$this->_db = new PDO('sqlite:'.$database);
|
||||||
|
|
||||||
|
// Test for existing DB
|
||||||
|
$result = $this->_db->query("SELECT * FROM sqlite_master WHERE name = 'caches' AND type = 'table'")->fetchAll();
|
||||||
|
|
||||||
|
// If there is no table, create a new one
|
||||||
|
if (0 == count($result))
|
||||||
|
{
|
||||||
|
$database_schema = Arr::get($this->_config, 'schema', NULL);
|
||||||
|
|
||||||
|
if ($database_schema === NULL)
|
||||||
|
{
|
||||||
|
throw new Cache_Exception('Database schema not found in Kohana Cache configuration');
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Create the caches table
|
||||||
|
$this->_db->query(Arr::get($this->_config, 'schema', NULL));
|
||||||
|
}
|
||||||
|
catch (PDOException $e)
|
||||||
|
{
|
||||||
|
throw new Cache_Exception('Failed to create new SQLite caches table with the following error : :error', array(':error' => $e->getMessage()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a value based on an id
|
||||||
|
*
|
||||||
|
* @param string $id id
|
||||||
|
* @param string $default default [Optional] Default value to return if id not found
|
||||||
|
* @return mixed
|
||||||
|
* @throws Cache_Exception
|
||||||
|
*/
|
||||||
|
public function get($id, $default = NULL)
|
||||||
|
{
|
||||||
|
// Prepare statement
|
||||||
|
$statement = $this->_db->prepare('SELECT id, expiration, cache FROM caches WHERE id = :id LIMIT 0, 1');
|
||||||
|
|
||||||
|
// Try and load the cache based on id
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$statement->execute(array(':id' => $this->_sanitize_id($id)));
|
||||||
|
}
|
||||||
|
catch (PDOException $e)
|
||||||
|
{
|
||||||
|
throw new Cache_Exception('There was a problem querying the local SQLite3 cache. :error', array(':error' => $e->getMessage()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! $result = $statement->fetch(PDO::FETCH_OBJ))
|
||||||
|
{
|
||||||
|
return $default;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the cache has expired
|
||||||
|
if ($result->expiration != 0 and $result->expiration <= time())
|
||||||
|
{
|
||||||
|
// Delete it and return default value
|
||||||
|
$this->delete($id);
|
||||||
|
return $default;
|
||||||
|
}
|
||||||
|
// Otherwise return cached object
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Disable notices for unserializing
|
||||||
|
$ER = error_reporting(~E_NOTICE);
|
||||||
|
|
||||||
|
// Return the valid cache data
|
||||||
|
$data = unserialize($result->cache);
|
||||||
|
|
||||||
|
// Turn notices back on
|
||||||
|
error_reporting($ER);
|
||||||
|
|
||||||
|
// Return the resulting data
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a value based on an id. Optionally add tags.
|
||||||
|
*
|
||||||
|
* @param string $id id
|
||||||
|
* @param mixed $data data
|
||||||
|
* @param integer $lifetime lifetime [Optional]
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function set($id, $data, $lifetime = NULL)
|
||||||
|
{
|
||||||
|
return (bool) $this->set_with_tags($id, $data, $lifetime);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a cache entry based on id
|
||||||
|
*
|
||||||
|
* @param string $id id
|
||||||
|
* @return boolean
|
||||||
|
* @throws Cache_Exception
|
||||||
|
*/
|
||||||
|
public function delete($id)
|
||||||
|
{
|
||||||
|
// Prepare statement
|
||||||
|
$statement = $this->_db->prepare('DELETE FROM caches WHERE id = :id');
|
||||||
|
|
||||||
|
// Remove the entry
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$statement->execute(array(':id' => $this->_sanitize_id($id)));
|
||||||
|
}
|
||||||
|
catch (PDOException $e)
|
||||||
|
{
|
||||||
|
throw new Cache_Exception('There was a problem querying the local SQLite3 cache. :error', array(':error' => $e->getMessage()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return (bool) $statement->rowCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete all cache entries
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function delete_all()
|
||||||
|
{
|
||||||
|
// Prepare statement
|
||||||
|
$statement = $this->_db->prepare('DELETE FROM caches');
|
||||||
|
|
||||||
|
// Remove the entry
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$statement->execute();
|
||||||
|
}
|
||||||
|
catch (PDOException $e)
|
||||||
|
{
|
||||||
|
throw new Cache_Exception('There was a problem querying the local SQLite3 cache. :error', array(':error' => $e->getMessage()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return (bool) $statement->rowCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a value based on an id. Optionally add tags.
|
||||||
|
*
|
||||||
|
* @param string $id id
|
||||||
|
* @param mixed $data data
|
||||||
|
* @param integer $lifetime lifetime [Optional]
|
||||||
|
* @param array $tags tags [Optional]
|
||||||
|
* @return boolean
|
||||||
|
* @throws Cache_Exception
|
||||||
|
*/
|
||||||
|
public function set_with_tags($id, $data, $lifetime = NULL, array $tags = NULL)
|
||||||
|
{
|
||||||
|
// Serialize the data
|
||||||
|
$data = serialize($data);
|
||||||
|
|
||||||
|
// Normalise tags
|
||||||
|
$tags = (NULL === $tags) ? NULL : ('<'.implode('>,<', $tags).'>');
|
||||||
|
|
||||||
|
// Setup lifetime
|
||||||
|
if ($lifetime === NULL)
|
||||||
|
{
|
||||||
|
$lifetime = (0 === Arr::get($this->_config, 'default_expire', NULL)) ? 0 : (Arr::get($this->_config, 'default_expire', Cache::DEFAULT_EXPIRE) + time());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$lifetime = (0 === $lifetime) ? 0 : ($lifetime + time());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare statement
|
||||||
|
// $this->exists() may throw Cache_Exception, no need to catch/rethrow
|
||||||
|
$statement = $this->exists($id) ? $this->_db->prepare('UPDATE caches SET expiration = :expiration, cache = :cache, tags = :tags WHERE id = :id') : $this->_db->prepare('INSERT INTO caches (id, cache, expiration, tags) VALUES (:id, :cache, :expiration, :tags)');
|
||||||
|
|
||||||
|
// Try to insert
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$statement->execute(array(':id' => $this->_sanitize_id($id), ':cache' => $data, ':expiration' => $lifetime, ':tags' => $tags));
|
||||||
|
}
|
||||||
|
catch (PDOException $e)
|
||||||
|
{
|
||||||
|
throw new Cache_Exception('There was a problem querying the local SQLite3 cache. :error', array(':error' => $e->getMessage()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return (bool) $statement->rowCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete cache entries based on a tag
|
||||||
|
*
|
||||||
|
* @param string $tag tag
|
||||||
|
* @return boolean
|
||||||
|
* @throws Cache_Exception
|
||||||
|
*/
|
||||||
|
public function delete_tag($tag)
|
||||||
|
{
|
||||||
|
// Prepare the statement
|
||||||
|
$statement = $this->_db->prepare('DELETE FROM caches WHERE tags LIKE :tag');
|
||||||
|
|
||||||
|
// Try to delete
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$statement->execute(array(':tag' => "%<{$tag}>%"));
|
||||||
|
}
|
||||||
|
catch (PDOException $e)
|
||||||
|
{
|
||||||
|
throw new Cache_Exception('There was a problem querying the local SQLite3 cache. :error', array(':error' => $e->getMessage()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return (bool) $statement->rowCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find cache entries based on a tag
|
||||||
|
*
|
||||||
|
* @param string $tag tag
|
||||||
|
* @return array
|
||||||
|
* @throws Cache_Exception
|
||||||
|
*/
|
||||||
|
public function find($tag)
|
||||||
|
{
|
||||||
|
// Prepare the statement
|
||||||
|
$statement = $this->_db->prepare('SELECT id, cache FROM caches WHERE tags LIKE :tag');
|
||||||
|
|
||||||
|
// Try to find
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if ( ! $statement->execute(array(':tag' => "%<{$tag}>%")))
|
||||||
|
{
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (PDOException $e)
|
||||||
|
{
|
||||||
|
throw new Cache_Exception('There was a problem querying the local SQLite3 cache. :error', array(':error' => $e->getMessage()));
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = array();
|
||||||
|
|
||||||
|
while ($row = $statement->fetchObject())
|
||||||
|
{
|
||||||
|
// Disable notices for unserializing
|
||||||
|
$ER = error_reporting(~E_NOTICE);
|
||||||
|
|
||||||
|
$result[$row->id] = unserialize($row->cache);
|
||||||
|
|
||||||
|
// Turn notices back on
|
||||||
|
error_reporting($ER);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Garbage collection method that cleans any expired
|
||||||
|
* cache entries from the cache.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function garbage_collect()
|
||||||
|
{
|
||||||
|
// Create the sequel statement
|
||||||
|
$statement = $this->_db->prepare('DELETE FROM caches WHERE expiration < :expiration');
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$statement->execute(array(':expiration' => time()));
|
||||||
|
}
|
||||||
|
catch (PDOException $e)
|
||||||
|
{
|
||||||
|
throw new Cache_Exception('There was a problem querying the local SQLite3 cache. :error', array(':error' => $e->getMessage()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests whether an id exists or not
|
||||||
|
*
|
||||||
|
* @param string $id id
|
||||||
|
* @return boolean
|
||||||
|
* @throws Cache_Exception
|
||||||
|
*/
|
||||||
|
protected function exists($id)
|
||||||
|
{
|
||||||
|
$statement = $this->_db->prepare('SELECT id FROM caches WHERE id = :id');
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$statement->execute(array(':id' => $this->_sanitize_id($id)));
|
||||||
|
}
|
||||||
|
catch (PDOExeption $e)
|
||||||
|
{
|
||||||
|
throw new Cache_Exception('There was a problem querying the local SQLite3 cache. :error', array(':error' => $e->getMessage()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return (bool) $statement->fetchAll();
|
||||||
|
}
|
||||||
|
}
|
41
modules/cache/classes/Kohana/Cache/Tagging.php
vendored
Normal file
41
modules/cache/classes/Kohana/Cache/Tagging.php
vendored
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<?php defined('SYSPATH') or die('No direct script access.');
|
||||||
|
/**
|
||||||
|
* Kohana Cache Tagging Interface
|
||||||
|
*
|
||||||
|
* @package Kohana/Cache
|
||||||
|
* @category Base
|
||||||
|
* @author Kohana Team
|
||||||
|
* @copyright (c) 2009-2012 Kohana Team
|
||||||
|
* @license http://kohanaphp.com/license
|
||||||
|
*/
|
||||||
|
interface Kohana_Cache_Tagging {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a value based on an id. Optionally add tags.
|
||||||
|
*
|
||||||
|
* Note : Some caching engines do not support
|
||||||
|
* tagging
|
||||||
|
*
|
||||||
|
* @param string $id id
|
||||||
|
* @param mixed $data data
|
||||||
|
* @param integer $lifetime lifetime [Optional]
|
||||||
|
* @param array $tags tags [Optional]
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function set_with_tags($id, $data, $lifetime = NULL, array $tags = NULL);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete cache entries based on a tag
|
||||||
|
*
|
||||||
|
* @param string $tag tag
|
||||||
|
*/
|
||||||
|
public function delete_tag($tag);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find cache entries based on a tag
|
||||||
|
*
|
||||||
|
* @param string $tag tag
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function find($tag);
|
||||||
|
}
|
140
modules/cache/classes/Kohana/Cache/Wincache.php
vendored
Normal file
140
modules/cache/classes/Kohana/Cache/Wincache.php
vendored
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
<?php defined('SYSPATH') or die('No direct script access.');
|
||||||
|
/**
|
||||||
|
* [Kohana Cache](api/Kohana_Cache) Wincache driver. Provides an opcode based
|
||||||
|
* driver for the Kohana Cache library.
|
||||||
|
*
|
||||||
|
* ### Configuration example
|
||||||
|
*
|
||||||
|
* Below is an example of an _wincache_ server configuration.
|
||||||
|
*
|
||||||
|
* return array(
|
||||||
|
* 'wincache' => array( // Driver group
|
||||||
|
* 'driver' => 'wincache', // using wincache driver
|
||||||
|
* ),
|
||||||
|
* )
|
||||||
|
*
|
||||||
|
* In cases where only one cache group is required, if the group is named `default` there is
|
||||||
|
* no need to pass the group name when instantiating a cache instance.
|
||||||
|
*
|
||||||
|
* #### General cache group configuration settings
|
||||||
|
*
|
||||||
|
* Below are the settings available to all types of cache driver.
|
||||||
|
*
|
||||||
|
* Name | Required | Description
|
||||||
|
* -------------- | -------- | ---------------------------------------------------------------
|
||||||
|
* driver | __YES__ | (_string_) The driver type to use
|
||||||
|
*
|
||||||
|
* ### System requirements
|
||||||
|
*
|
||||||
|
* * Windows XP SP3 with IIS 5.1 and » FastCGI Extension
|
||||||
|
* * Windows Server 2003 with IIS 6.0 and » FastCGI Extension
|
||||||
|
* * Windows Vista SP1 with IIS 7.0 and FastCGI Module
|
||||||
|
* * Windows Server 2008 with IIS 7.0 and FastCGI Module
|
||||||
|
* * Windows 7 with IIS 7.5 and FastCGI Module
|
||||||
|
* * Windows Server 2008 R2 with IIS 7.5 and FastCGI Module
|
||||||
|
* * PHP 5.2.X, Non-thread-safe build
|
||||||
|
* * PHP 5.3 X86, Non-thread-safe VC9 build
|
||||||
|
*
|
||||||
|
* @package Kohana/Cache
|
||||||
|
* @category Base
|
||||||
|
* @author Kohana Team
|
||||||
|
* @copyright (c) 2009-2012 Kohana Team
|
||||||
|
* @license http://kohanaphp.com/license
|
||||||
|
*/
|
||||||
|
class Kohana_Cache_Wincache extends Cache {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check for existence of the wincache extension This method cannot be invoked externally. The driver must
|
||||||
|
* be instantiated using the `Cache::instance()` method.
|
||||||
|
*
|
||||||
|
* @param array $config configuration
|
||||||
|
* @throws Cache_Exception
|
||||||
|
*/
|
||||||
|
protected function __construct(array $config)
|
||||||
|
{
|
||||||
|
if ( ! extension_loaded('wincache'))
|
||||||
|
{
|
||||||
|
throw new Cache_Exception('PHP wincache extension is not available.');
|
||||||
|
}
|
||||||
|
|
||||||
|
parent::__construct($config);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a cached value entry by id.
|
||||||
|
*
|
||||||
|
* // Retrieve cache entry from wincache group
|
||||||
|
* $data = Cache::instance('wincache')->get('foo');
|
||||||
|
*
|
||||||
|
* // Retrieve cache entry from wincache group and return 'bar' if miss
|
||||||
|
* $data = Cache::instance('wincache')->get('foo', 'bar');
|
||||||
|
*
|
||||||
|
* @param string $id id of cache to entry
|
||||||
|
* @param string $default default value to return if cache miss
|
||||||
|
* @return mixed
|
||||||
|
* @throws Cache_Exception
|
||||||
|
*/
|
||||||
|
public function get($id, $default = NULL)
|
||||||
|
{
|
||||||
|
$data = wincache_ucache_get($this->_sanitize_id($id), $success);
|
||||||
|
|
||||||
|
return $success ? $data : $default;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a value to cache with id and lifetime
|
||||||
|
*
|
||||||
|
* $data = 'bar';
|
||||||
|
*
|
||||||
|
* // Set 'bar' to 'foo' in wincache group, using default expiry
|
||||||
|
* Cache::instance('wincache')->set('foo', $data);
|
||||||
|
*
|
||||||
|
* // Set 'bar' to 'foo' in wincache group for 30 seconds
|
||||||
|
* Cache::instance('wincache')->set('foo', $data, 30);
|
||||||
|
*
|
||||||
|
* @param string $id id of cache entry
|
||||||
|
* @param string $data data to set to cache
|
||||||
|
* @param integer $lifetime lifetime in seconds
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function set($id, $data, $lifetime = NULL)
|
||||||
|
{
|
||||||
|
if ($lifetime === NULL)
|
||||||
|
{
|
||||||
|
$lifetime = Arr::get($this->_config, 'default_expire', Cache::DEFAULT_EXPIRE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return wincache_ucache_set($this->_sanitize_id($id), $data, $lifetime);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a cache entry based on id
|
||||||
|
*
|
||||||
|
* // Delete 'foo' entry from the wincache group
|
||||||
|
* Cache::instance('wincache')->delete('foo');
|
||||||
|
*
|
||||||
|
* @param string $id id to remove from cache
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function delete($id)
|
||||||
|
{
|
||||||
|
return wincache_ucache_delete($this->_sanitize_id($id));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete all cache entries.
|
||||||
|
*
|
||||||
|
* Beware of using this method when
|
||||||
|
* using shared memory cache systems, as it will wipe every
|
||||||
|
* entry within the system for all clients.
|
||||||
|
*
|
||||||
|
* // Delete all cache entries in the wincache group
|
||||||
|
* Cache::instance('wincache')->delete_all();
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function delete_all()
|
||||||
|
{
|
||||||
|
return wincache_ucache_clear();
|
||||||
|
}
|
||||||
|
}
|
503
modules/cache/classes/Kohana/HTTP/Cache.php
vendored
Normal file
503
modules/cache/classes/Kohana/HTTP/Cache.php
vendored
Normal file
@ -0,0 +1,503 @@
|
|||||||
|
<?php defined('SYSPATH') or die('No direct script access.');
|
||||||
|
/**
|
||||||
|
* HTTT Caching adaptor class that provides caching services to the
|
||||||
|
* [Request_Client] class, using HTTP cache control logic as defined in
|
||||||
|
* RFC 2616.
|
||||||
|
*
|
||||||
|
* @package Kohana
|
||||||
|
* @category Base
|
||||||
|
* @author Kohana Team
|
||||||
|
* @copyright (c) 2008-2012 Kohana Team
|
||||||
|
* @license http://kohanaframework.org/license
|
||||||
|
* @since 3.2.0
|
||||||
|
*/
|
||||||
|
class Kohana_HTTP_Cache {
|
||||||
|
|
||||||
|
const CACHE_STATUS_KEY = 'x-cache-status';
|
||||||
|
const CACHE_STATUS_SAVED = 'SAVED';
|
||||||
|
const CACHE_STATUS_HIT = 'HIT';
|
||||||
|
const CACHE_STATUS_MISS = 'MISS';
|
||||||
|
const CACHE_HIT_KEY = 'x-cache-hits';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factory method for HTTP_Cache that provides a convenient dependency
|
||||||
|
* injector for the Cache library.
|
||||||
|
*
|
||||||
|
* // Create HTTP_Cache with named cache engine
|
||||||
|
* $http_cache = HTTP_Cache::factory('memcache', array(
|
||||||
|
* 'allow_private_cache' => FALSE
|
||||||
|
* )
|
||||||
|
* );
|
||||||
|
*
|
||||||
|
* // Create HTTP_Cache with supplied cache engine
|
||||||
|
* $http_cache = HTTP_Cache::factory(Cache::instance('memcache'),
|
||||||
|
* array(
|
||||||
|
* 'allow_private_cache' => FALSE
|
||||||
|
* )
|
||||||
|
* );
|
||||||
|
*
|
||||||
|
* @uses [Cache]
|
||||||
|
* @param mixed $cache cache engine to use
|
||||||
|
* @param array $options options to set to this class
|
||||||
|
* @return HTTP_Cache
|
||||||
|
*/
|
||||||
|
public static function factory($cache, array $options = array())
|
||||||
|
{
|
||||||
|
if ( ! $cache instanceof Cache)
|
||||||
|
{
|
||||||
|
$cache = Cache::instance($cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
$options['cache'] = $cache;
|
||||||
|
|
||||||
|
return new HTTP_Cache($options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Basic cache key generator that hashes the entire request and returns
|
||||||
|
* it. This is fine for static content, or dynamic content where user
|
||||||
|
* specific information is encoded into the request.
|
||||||
|
*
|
||||||
|
* // Generate cache key
|
||||||
|
* $cache_key = HTTP_Cache::basic_cache_key_generator($request);
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function basic_cache_key_generator(Request $request)
|
||||||
|
{
|
||||||
|
$uri = $request->uri();
|
||||||
|
$query = $request->query();
|
||||||
|
$headers = $request->headers()->getArrayCopy();
|
||||||
|
$body = $request->body();
|
||||||
|
|
||||||
|
return sha1($uri.'?'.http_build_query($query, NULL, '&').'~'.implode('~', $headers).'~'.$body);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Cache cache driver to use for HTTP caching
|
||||||
|
*/
|
||||||
|
protected $_cache;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var callback Cache key generator callback
|
||||||
|
*/
|
||||||
|
protected $_cache_key_callback;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var boolean Defines whether this client should cache `private` cache directives
|
||||||
|
* @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9
|
||||||
|
*/
|
||||||
|
protected $_allow_private_cache = FALSE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int The timestamp of the request
|
||||||
|
*/
|
||||||
|
protected $_request_time;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int The timestamp of the response
|
||||||
|
*/
|
||||||
|
protected $_response_time;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor method for this class. Allows dependency injection of the
|
||||||
|
* required components such as `Cache` and the cache key generator.
|
||||||
|
*
|
||||||
|
* @param array $options
|
||||||
|
*/
|
||||||
|
public function __construct(array $options = array())
|
||||||
|
{
|
||||||
|
foreach ($options as $key => $value)
|
||||||
|
{
|
||||||
|
if (method_exists($this, $key))
|
||||||
|
{
|
||||||
|
$this->$key($value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->_cache_key_callback === NULL)
|
||||||
|
{
|
||||||
|
$this->cache_key_callback('HTTP_Cache::basic_cache_key_generator');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes the supplied [Request] with the supplied [Request_Client].
|
||||||
|
* Before execution, the HTTP_Cache adapter checks the request type,
|
||||||
|
* destructive requests such as `POST`, `PUT` and `DELETE` will bypass
|
||||||
|
* cache completely and ensure the response is not cached. All other
|
||||||
|
* Request methods will allow caching, if the rules are met.
|
||||||
|
*
|
||||||
|
* @param Request_Client $client client to execute with Cache-Control
|
||||||
|
* @param Request $request request to execute with client
|
||||||
|
* @return [Response]
|
||||||
|
*/
|
||||||
|
public function execute(Request_Client $client, Request $request, Response $response)
|
||||||
|
{
|
||||||
|
if ( ! $this->_cache instanceof Cache)
|
||||||
|
return $client->execute_request($request, $response);
|
||||||
|
|
||||||
|
// If this is a destructive request, by-pass cache completely
|
||||||
|
if (in_array($request->method(), array(
|
||||||
|
HTTP_Request::POST,
|
||||||
|
HTTP_Request::PUT,
|
||||||
|
HTTP_Request::DELETE)))
|
||||||
|
{
|
||||||
|
// Kill existing caches for this request
|
||||||
|
$this->invalidate_cache($request);
|
||||||
|
|
||||||
|
$response = $client->execute_request($request, $response);
|
||||||
|
|
||||||
|
$cache_control = HTTP_Header::create_cache_control(array(
|
||||||
|
'no-cache',
|
||||||
|
'must-revalidate'
|
||||||
|
));
|
||||||
|
|
||||||
|
// Ensure client respects destructive action
|
||||||
|
return $response->headers('cache-control', $cache_control);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the cache key
|
||||||
|
$cache_key = $this->create_cache_key($request, $this->_cache_key_callback);
|
||||||
|
|
||||||
|
// Try and return cached version
|
||||||
|
if (($cached_response = $this->cache_response($cache_key, $request)) instanceof Response)
|
||||||
|
return $cached_response;
|
||||||
|
|
||||||
|
// Start request time
|
||||||
|
$this->_request_time = time();
|
||||||
|
|
||||||
|
// Execute the request with the Request client
|
||||||
|
$response = $client->execute_request($request, $response);
|
||||||
|
|
||||||
|
// Stop response time
|
||||||
|
$this->_response_time = (time() - $this->_request_time);
|
||||||
|
|
||||||
|
// Cache the response
|
||||||
|
$this->cache_response($cache_key, $request, $response);
|
||||||
|
|
||||||
|
$response->headers(HTTP_Cache::CACHE_STATUS_KEY,
|
||||||
|
HTTP_Cache::CACHE_STATUS_MISS);
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalidate a cached response for the [Request] supplied.
|
||||||
|
* This has the effect of deleting the response from the
|
||||||
|
* [Cache] entry.
|
||||||
|
*
|
||||||
|
* @param Request $request Response to remove from cache
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function invalidate_cache(Request $request)
|
||||||
|
{
|
||||||
|
if (($cache = $this->cache()) instanceof Cache)
|
||||||
|
{
|
||||||
|
$cache->delete($this->create_cache_key($request, $this->_cache_key_callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Getter and setter for the internal caching engine,
|
||||||
|
* used to cache responses if available and valid.
|
||||||
|
*
|
||||||
|
* @param Kohana_Cache $cache engine to use for caching
|
||||||
|
* @return Kohana_Cache
|
||||||
|
* @return Kohana_Request_Client
|
||||||
|
*/
|
||||||
|
public function cache(Cache $cache = NULL)
|
||||||
|
{
|
||||||
|
if ($cache === NULL)
|
||||||
|
return $this->_cache;
|
||||||
|
|
||||||
|
$this->_cache = $cache;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets or sets the [Request_Client::allow_private_cache] setting.
|
||||||
|
* If set to `TRUE`, the client will also cache cache-control directives
|
||||||
|
* that have the `private` setting.
|
||||||
|
*
|
||||||
|
* @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9
|
||||||
|
* @param boolean $setting allow caching of privately marked responses
|
||||||
|
* @return boolean
|
||||||
|
* @return [Request_Client]
|
||||||
|
*/
|
||||||
|
public function allow_private_cache($setting = NULL)
|
||||||
|
{
|
||||||
|
if ($setting === NULL)
|
||||||
|
return $this->_allow_private_cache;
|
||||||
|
|
||||||
|
$this->_allow_private_cache = (bool) $setting;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets or gets the cache key generator callback for this caching
|
||||||
|
* class. The cache key generator provides a unique hash based on the
|
||||||
|
* `Request` object passed to it.
|
||||||
|
*
|
||||||
|
* The default generator is [HTTP_Cache::basic_cache_key_generator()], which
|
||||||
|
* serializes the entire `HTTP_Request` into a unique sha1 hash. This will
|
||||||
|
* provide basic caching for static and simple dynamic pages. More complex
|
||||||
|
* algorithms can be defined and then passed into `HTTP_Cache` using this
|
||||||
|
* method.
|
||||||
|
*
|
||||||
|
* // Get the cache key callback
|
||||||
|
* $callback = $http_cache->cache_key_callback();
|
||||||
|
*
|
||||||
|
* // Set the cache key callback
|
||||||
|
* $http_cache->cache_key_callback('Foo::cache_key');
|
||||||
|
*
|
||||||
|
* // Alternatively, in PHP 5.3 use a closure
|
||||||
|
* $http_cache->cache_key_callback(function (Request $request) {
|
||||||
|
* return sha1($request->render());
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* @param callback $callback
|
||||||
|
* @return mixed
|
||||||
|
* @throws HTTP_Exception
|
||||||
|
*/
|
||||||
|
public function cache_key_callback($callback = NULL)
|
||||||
|
{
|
||||||
|
if ($callback === NULL)
|
||||||
|
return $this->_cache_key_callback;
|
||||||
|
|
||||||
|
if ( ! is_callable($callback))
|
||||||
|
throw new Kohana_Exception('cache_key_callback must be callable!');
|
||||||
|
|
||||||
|
$this->_cache_key_callback = $callback;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a cache key for the request to use for caching
|
||||||
|
* [Kohana_Response] returned by [Request::execute].
|
||||||
|
*
|
||||||
|
* This is the default cache key generating logic, but can be overridden
|
||||||
|
* by setting [HTTP_Cache::cache_key_callback()].
|
||||||
|
*
|
||||||
|
* @param Request $request request to create key for
|
||||||
|
* @param callback $callback optional callback to use instead of built-in method
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function create_cache_key(Request $request, $callback = FALSE)
|
||||||
|
{
|
||||||
|
if (is_callable($callback))
|
||||||
|
return call_user_func($callback, $request);
|
||||||
|
else
|
||||||
|
return HTTP_Cache::basic_cache_key_generator($request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controls whether the response can be cached. Uses HTTP
|
||||||
|
* protocol to determine whether the response can be cached.
|
||||||
|
*
|
||||||
|
* @link RFC 2616 http://www.w3.org/Protocols/rfc2616/
|
||||||
|
* @param Response $response The Response
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function set_cache(Response $response)
|
||||||
|
{
|
||||||
|
$headers = $response->headers()->getArrayCopy();
|
||||||
|
|
||||||
|
if ($cache_control = Arr::get($headers, 'cache-control'))
|
||||||
|
{
|
||||||
|
// Parse the cache control
|
||||||
|
$cache_control = HTTP_Header::parse_cache_control($cache_control);
|
||||||
|
|
||||||
|
// If the no-cache or no-store directive is set, return
|
||||||
|
if (array_intersect($cache_control, array('no-cache', 'no-store')))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
// Check for private cache and get out of here if invalid
|
||||||
|
if ( ! $this->_allow_private_cache AND in_array('private', $cache_control))
|
||||||
|
{
|
||||||
|
if ( ! isset($cache_control['s-maxage']))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
// If there is a s-maxage directive we can use that
|
||||||
|
$cache_control['max-age'] = $cache_control['s-maxage'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that max-age has been set and if it is valid for caching
|
||||||
|
if (isset($cache_control['max-age']) AND $cache_control['max-age'] < 1)
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($expires = Arr::get($headers, 'expires') AND ! isset($cache_control['max-age']))
|
||||||
|
{
|
||||||
|
// Can't cache things that have expired already
|
||||||
|
if (strtotime($expires) <= time())
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Caches a [Response] using the supplied [Cache]
|
||||||
|
* and the key generated by [Request_Client::_create_cache_key].
|
||||||
|
*
|
||||||
|
* If not response is supplied, the cache will be checked for an existing
|
||||||
|
* one that is available.
|
||||||
|
*
|
||||||
|
* @param string $key the cache key to use
|
||||||
|
* @param Request $request the HTTP Request
|
||||||
|
* @param Response $response the HTTP Response
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function cache_response($key, Request $request, Response $response = NULL)
|
||||||
|
{
|
||||||
|
if ( ! $this->_cache instanceof Cache)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
// Check for Pragma: no-cache
|
||||||
|
if ($pragma = $request->headers('pragma'))
|
||||||
|
{
|
||||||
|
if ($pragma == 'no-cache')
|
||||||
|
return FALSE;
|
||||||
|
elseif (is_array($pragma) AND in_array('no-cache', $pragma))
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there is no response, lookup an existing cached response
|
||||||
|
if ($response === NULL)
|
||||||
|
{
|
||||||
|
$response = $this->_cache->get($key);
|
||||||
|
|
||||||
|
if ( ! $response instanceof Response)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
// Do cache hit arithmetic, using fast arithmetic if available
|
||||||
|
if ($this->_cache instanceof Cache_Arithmetic)
|
||||||
|
{
|
||||||
|
$hit_count = $this->_cache->increment(HTTP_Cache::CACHE_HIT_KEY.$key);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$hit_count = $this->_cache->get(HTTP_Cache::CACHE_HIT_KEY.$key);
|
||||||
|
$this->_cache->set(HTTP_Cache::CACHE_HIT_KEY.$key, ++$hit_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the header to have correct HIT status and count
|
||||||
|
$response->headers(HTTP_Cache::CACHE_STATUS_KEY,
|
||||||
|
HTTP_Cache::CACHE_STATUS_HIT)
|
||||||
|
->headers(HTTP_Cache::CACHE_HIT_KEY, $hit_count);
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (($ttl = $this->cache_lifetime($response)) === FALSE)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
$response->headers(HTTP_Cache::CACHE_STATUS_KEY,
|
||||||
|
HTTP_Cache::CACHE_STATUS_SAVED);
|
||||||
|
|
||||||
|
// Set the hit count to zero
|
||||||
|
$this->_cache->set(HTTP_Cache::CACHE_HIT_KEY.$key, 0);
|
||||||
|
|
||||||
|
return $this->_cache->set($key, $response, $ttl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the total Time To Live based on the specification
|
||||||
|
* RFC 2616 cache lifetime rules.
|
||||||
|
*
|
||||||
|
* @param Response $response Response to evaluate
|
||||||
|
* @return mixed TTL value or false if the response should not be cached
|
||||||
|
*/
|
||||||
|
public function cache_lifetime(Response $response)
|
||||||
|
{
|
||||||
|
// Get out of here if this cannot be cached
|
||||||
|
if ( ! $this->set_cache($response))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
// Calculate apparent age
|
||||||
|
if ($date = $response->headers('date'))
|
||||||
|
{
|
||||||
|
$apparent_age = max(0, $this->_response_time - strtotime($date));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$apparent_age = max(0, $this->_response_time);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate corrected received age
|
||||||
|
if ($age = $response->headers('age'))
|
||||||
|
{
|
||||||
|
$corrected_received_age = max($apparent_age, intval($age));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$corrected_received_age = $apparent_age;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Corrected initial age
|
||||||
|
$corrected_initial_age = $corrected_received_age + $this->request_execution_time();
|
||||||
|
|
||||||
|
// Resident time
|
||||||
|
$resident_time = time() - $this->_response_time;
|
||||||
|
|
||||||
|
// Current age
|
||||||
|
$current_age = $corrected_initial_age + $resident_time;
|
||||||
|
|
||||||
|
// Prepare the cache freshness lifetime
|
||||||
|
$ttl = NULL;
|
||||||
|
|
||||||
|
// Cache control overrides
|
||||||
|
if ($cache_control = $response->headers('cache-control'))
|
||||||
|
{
|
||||||
|
// Parse the cache control header
|
||||||
|
$cache_control = HTTP_Header::parse_cache_control($cache_control);
|
||||||
|
|
||||||
|
if (isset($cache_control['max-age']))
|
||||||
|
{
|
||||||
|
$ttl = $cache_control['max-age'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($cache_control['s-maxage']) AND isset($cache_control['private']) AND $this->_allow_private_cache)
|
||||||
|
{
|
||||||
|
$ttl = $cache_control['s-maxage'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($cache_control['max-stale']) AND ! isset($cache_control['must-revalidate']))
|
||||||
|
{
|
||||||
|
$ttl = $current_age + $cache_control['max-stale'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we have a TTL at this point, return
|
||||||
|
if ($ttl !== NULL)
|
||||||
|
return $ttl;
|
||||||
|
|
||||||
|
if ($expires = $response->headers('expires'))
|
||||||
|
return strtotime($expires) - $current_age;
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the duration of the last request execution.
|
||||||
|
* Either returns the time of completed requests or
|
||||||
|
* `FALSE` if the request hasn't finished executing, or
|
||||||
|
* is yet to be run.
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function request_execution_time()
|
||||||
|
{
|
||||||
|
if ($this->_request_time === NULL OR $this->_response_time === NULL)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
return $this->_response_time - $this->_request_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // End Kohana_HTTP_Cache
|
70
modules/cache/config/cache.php
vendored
Normal file
70
modules/cache/config/cache.php
vendored
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
<?php defined('SYSPATH') or die('No direct script access.');
|
||||||
|
return array
|
||||||
|
(
|
||||||
|
/* 'memcache' => array(
|
||||||
|
'driver' => 'memcache',
|
||||||
|
'default_expire' => 3600,
|
||||||
|
'compression' => FALSE, // Use Zlib compression (can cause issues with integers)
|
||||||
|
'servers' => array(
|
||||||
|
'local' => array(
|
||||||
|
'host' => 'localhost', // Memcache Server
|
||||||
|
'port' => 11211, // Memcache port number
|
||||||
|
'persistent' => FALSE, // Persistent connection
|
||||||
|
'weight' => 1,
|
||||||
|
'timeout' => 1,
|
||||||
|
'retry_interval' => 15,
|
||||||
|
'status' => TRUE,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'instant_death' => TRUE, // Take server offline immediately on first fail (no retry)
|
||||||
|
),
|
||||||
|
'memcachetag' => array(
|
||||||
|
'driver' => 'memcachetag',
|
||||||
|
'default_expire' => 3600,
|
||||||
|
'compression' => FALSE, // Use Zlib compression (can cause issues with integers)
|
||||||
|
'servers' => array(
|
||||||
|
'local' => array(
|
||||||
|
'host' => 'localhost', // Memcache Server
|
||||||
|
'port' => 11211, // Memcache port number
|
||||||
|
'persistent' => FALSE, // Persistent connection
|
||||||
|
'weight' => 1,
|
||||||
|
'timeout' => 1,
|
||||||
|
'retry_interval' => 15,
|
||||||
|
'status' => TRUE,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'instant_death' => TRUE,
|
||||||
|
),
|
||||||
|
'apc' => array(
|
||||||
|
'driver' => 'apc',
|
||||||
|
'default_expire' => 3600,
|
||||||
|
),
|
||||||
|
'wincache' => array(
|
||||||
|
'driver' => 'wincache',
|
||||||
|
'default_expire' => 3600,
|
||||||
|
),
|
||||||
|
'sqlite' => array(
|
||||||
|
'driver' => 'sqlite',
|
||||||
|
'default_expire' => 3600,
|
||||||
|
'database' => APPPATH.'cache/kohana-cache.sql3',
|
||||||
|
'schema' => 'CREATE TABLE caches(id VARCHAR(127) PRIMARY KEY, tags VARCHAR(255), expiration INTEGER, cache TEXT)',
|
||||||
|
),
|
||||||
|
'eaccelerator' => array(
|
||||||
|
'driver' => 'eaccelerator',
|
||||||
|
),
|
||||||
|
'xcache' => array(
|
||||||
|
'driver' => 'xcache',
|
||||||
|
'default_expire' => 3600,
|
||||||
|
),
|
||||||
|
'file' => array(
|
||||||
|
'driver' => 'file',
|
||||||
|
'cache_dir' => APPPATH.'cache',
|
||||||
|
'default_expire' => 3600,
|
||||||
|
'ignore_on_delete' => array(
|
||||||
|
'.gitignore',
|
||||||
|
'.git',
|
||||||
|
'.svn'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
*/
|
||||||
|
);
|
23
modules/cache/config/userguide.php
vendored
Normal file
23
modules/cache/config/userguide.php
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?php defined('SYSPATH') or die('No direct script access.');
|
||||||
|
|
||||||
|
return array(
|
||||||
|
// Leave this alone
|
||||||
|
'modules' => array(
|
||||||
|
|
||||||
|
// This should be the path to this modules userguide pages, without the 'guide/'. Ex: '/guide/modulename/' would be 'modulename'
|
||||||
|
'cache' => array(
|
||||||
|
|
||||||
|
// Whether this modules userguide pages should be shown
|
||||||
|
'enabled' => TRUE,
|
||||||
|
|
||||||
|
// The name that should show up on the userguide index page
|
||||||
|
'name' => 'Cache',
|
||||||
|
|
||||||
|
// A short description of this module, shown on the index page
|
||||||
|
'description' => 'Common interface for caching engines.',
|
||||||
|
|
||||||
|
// Copyright message, shown in the footer for this module
|
||||||
|
'copyright' => '© 2008–2012 Kohana Team',
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
219
modules/cache/guide/cache.usage.md
vendored
Normal file
219
modules/cache/guide/cache.usage.md
vendored
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
# Kohana Cache usage
|
||||||
|
|
||||||
|
[Kohana_Cache] provides a simple interface allowing getting, setting and deleting of cached values. Two interfaces included in _Kohana Cache_ additionally provide _tagging_ and _garbage collection_ where they are supported by the respective drivers.
|
||||||
|
|
||||||
|
## Getting a new cache instance
|
||||||
|
|
||||||
|
Creating a new _Kohana Cache_ instance is simple, however it must be done using the [Cache::instance] method, rather than the traditional `new` constructor.
|
||||||
|
|
||||||
|
// Create a new instance of cache using the default group
|
||||||
|
$cache = Cache::instance();
|
||||||
|
|
||||||
|
The default group will use whatever is set to [Cache::$default] and must have a corresponding [configuration](cache.config) definition for that group.
|
||||||
|
|
||||||
|
To create a cache instance using a group other than the _default_, simply provide the group name as an argument.
|
||||||
|
|
||||||
|
// Create a new instance of the memcache group
|
||||||
|
$memcache = Cache::instance('memcache');
|
||||||
|
|
||||||
|
If there is a cache instance already instantiated then you can get it directly from the class member.
|
||||||
|
|
||||||
|
[!!] Beware that this can cause issues if you do not test for the instance before trying to access it.
|
||||||
|
|
||||||
|
// Check for the existance of the cache driver
|
||||||
|
if (isset(Cache::$instances['memcache']))
|
||||||
|
{
|
||||||
|
// Get the existing cache instance directly (faster)
|
||||||
|
$memcache = Cache::$instances['memcache'];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Get the cache driver instance (slower)
|
||||||
|
$memcache = Cache::instance('memcache');
|
||||||
|
}
|
||||||
|
|
||||||
|
## Setting and getting variables to and from cache
|
||||||
|
|
||||||
|
The cache library supports scalar and object values, utilising object serialization where required (or not supported by the caching engine). This means that the majority or objects can be cached without any modification.
|
||||||
|
|
||||||
|
[!!] Serialisation does not work with resource handles, such as filesystem, curl or socket resources.
|
||||||
|
|
||||||
|
### Setting a value to cache
|
||||||
|
|
||||||
|
Setting a value to cache using the [Cache::set] method can be done in one of two ways; either using the Cache instance interface, which is good for atomic operations; or getting an instance and using that for multiple operations.
|
||||||
|
|
||||||
|
The first example demonstrates how to quickly load and set a value to the default cache instance.
|
||||||
|
|
||||||
|
// Create a cachable object
|
||||||
|
$object = new stdClass;
|
||||||
|
|
||||||
|
// Set a property
|
||||||
|
$object->foo = 'bar';
|
||||||
|
|
||||||
|
// Cache the object using default group (quick interface) with default time (3600 seconds)
|
||||||
|
Cache::instance()->set('foo', $object);
|
||||||
|
|
||||||
|
If multiple cache operations are required, it is best to assign an instance of Cache to a variable and use that as below.
|
||||||
|
|
||||||
|
// Set the object using a defined group for a defined time period (30 seconds)
|
||||||
|
$memcache = Cache::instance('memcache');
|
||||||
|
$memcache->set('foo', $object, 30);
|
||||||
|
|
||||||
|
#### Setting a value with tags
|
||||||
|
|
||||||
|
Certain cache drivers support setting values with tags. To set a value to cache with tags using the following interface.
|
||||||
|
|
||||||
|
// Get a cache instance that supports tags
|
||||||
|
$memcache = Cache::instance('memcachetag');
|
||||||
|
|
||||||
|
// Test for tagging interface
|
||||||
|
if ($memcache instanceof Cache_Tagging)
|
||||||
|
{
|
||||||
|
// Set a value with some tags for 30 seconds
|
||||||
|
$memcache->set('foo', $object, 30, array('snafu', 'stfu', 'fubar'));
|
||||||
|
}
|
||||||
|
// Otherwise set without tags
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Set a value for 30 seconds
|
||||||
|
$memcache->set('foo', $object, 30);
|
||||||
|
}
|
||||||
|
|
||||||
|
It is possible to implement custom tagging solutions onto existing or new cache drivers by implementing the [Cache_Tagging] interface. Kohana_Cache only applies the interface to drivers that support tagging natively as standard.
|
||||||
|
|
||||||
|
### Getting a value from cache
|
||||||
|
|
||||||
|
Getting variables back from cache is achieved using the [Cache::get] method using a single key to identify the cache entry.
|
||||||
|
|
||||||
|
// Retrieve a value from cache (quickly)
|
||||||
|
$object = Cache::instance()->get('foo');
|
||||||
|
|
||||||
|
In cases where the requested key is not available or the entry has expired, a default value will be returned (__NULL__ by default). It is possible to define the default value as the key is requested.
|
||||||
|
|
||||||
|
// If the cache key is available (with default value set to FALSE)
|
||||||
|
if ($object = Cache::instance()->get('foo', FALSE))
|
||||||
|
{
|
||||||
|
// Do something
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Do something else
|
||||||
|
}
|
||||||
|
|
||||||
|
#### Getting values from cache using tags
|
||||||
|
|
||||||
|
It is possible to retrieve values from cache grouped by tag, using the [Cache::find] method with drivers that support tagging.
|
||||||
|
|
||||||
|
[!!] The __Memcachetag__ driver does not support the `Cache::find($tag)` interface and will throw an exception.
|
||||||
|
|
||||||
|
// Get an instance of cache
|
||||||
|
$cache = Cache::instance('memcachetag');
|
||||||
|
|
||||||
|
// Wrap in a try/catch statement to gracefully handle memcachetag
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Find values based on tag
|
||||||
|
return $cache->find('snafu');
|
||||||
|
}
|
||||||
|
catch (Cache_Exception $e)
|
||||||
|
{
|
||||||
|
// Handle gracefully
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
### Deleting values from cache
|
||||||
|
|
||||||
|
Deleting variables is very similar to the getting and setting methods already described. Deleting operations are split into three categories:
|
||||||
|
|
||||||
|
- __Delete value by key__. Deletes a cached value by the associated key.
|
||||||
|
- __Delete all values__. Deletes all caches values stored in the cache instance.
|
||||||
|
- __Delete values by tag__. Deletes all values that have the supplied tag. This is only supported by Memcached-Tag and Sqlite.
|
||||||
|
|
||||||
|
#### Delete value by key
|
||||||
|
|
||||||
|
To delete a specific value by its associated key:
|
||||||
|
|
||||||
|
// If the cache entry for 'foo' is deleted
|
||||||
|
if (Cache::instance()->delete('foo'))
|
||||||
|
{
|
||||||
|
// Cache entry successfully deleted, do something
|
||||||
|
}
|
||||||
|
|
||||||
|
By default a `TRUE` value will be returned. However a `FALSE` value will be returned in instances where the key did not exist in the cache.
|
||||||
|
|
||||||
|
#### Delete all values
|
||||||
|
|
||||||
|
To delete all values in a specific instance:
|
||||||
|
|
||||||
|
// If all cache items where deleted successfully
|
||||||
|
if (Cache::instance()->delete_all())
|
||||||
|
{
|
||||||
|
// Do something
|
||||||
|
}
|
||||||
|
|
||||||
|
It is also possible to delete all cache items in every instance:
|
||||||
|
|
||||||
|
// For each cache instance
|
||||||
|
foreach (Cache::$instances as $group => $instance)
|
||||||
|
{
|
||||||
|
if ($instance->delete_all())
|
||||||
|
{
|
||||||
|
var_dump('instance : '.$group.' has been flushed!');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#### Delete values by tag
|
||||||
|
|
||||||
|
Some of the caching drivers support deleting by tag. This will remove all the cached values that are associated with a specific tag. Below is an example of how to robustly handle deletion by tag.
|
||||||
|
|
||||||
|
// Get cache instance
|
||||||
|
$cache = Cache::instance();
|
||||||
|
|
||||||
|
// Check for tagging interface
|
||||||
|
if ($cache instanceof Cache_Tagging)
|
||||||
|
{
|
||||||
|
// Delete all entries by the tag 'snafu'
|
||||||
|
$cache->delete_tag('snafu');
|
||||||
|
}
|
||||||
|
|
||||||
|
#### Garbage Collection
|
||||||
|
|
||||||
|
Garbage Collection (GC) is the cleaning of expired cache entries. For the most part, caching engines will take care of garbage collection internally. However a few of the file based systems do not handle this task and in these circumstances it would be prudent to garbage collect at a predetermined frequency. If no garbage collection is executed, the resource storing the cache entries will eventually fill and become unusable.
|
||||||
|
|
||||||
|
When not automated, garbage collection is the responsibility of the developer. It is prudent to have a GC probability value that dictates how likely the garbage collection routing will be run. An example of such a system is demonstrated below.
|
||||||
|
|
||||||
|
// Get a cache instance
|
||||||
|
$cache_file = Cache::instance('file');
|
||||||
|
|
||||||
|
// Set a GC probability of 10%
|
||||||
|
$gc = 10;
|
||||||
|
|
||||||
|
// If the GC probability is a hit
|
||||||
|
if (rand(0,99) <= $gc and $cache_file instanceof Cache_GarbageCollect)
|
||||||
|
{
|
||||||
|
// Garbage Collect
|
||||||
|
$cache_file->garbage_collect();
|
||||||
|
}
|
||||||
|
|
||||||
|
# Interfaces
|
||||||
|
|
||||||
|
Kohana Cache comes with two interfaces that are implemented where the drivers support them:
|
||||||
|
|
||||||
|
- __[Cache_Tagging] for tagging support on cache entries__
|
||||||
|
- [Cache_MemcacheTag]
|
||||||
|
- [Cache_Sqlite]
|
||||||
|
- __[Cache_GarbageCollect] for garbage collection with drivers without native support__
|
||||||
|
- [Cache_File]
|
||||||
|
- [Cache_Sqlite]
|
||||||
|
|
||||||
|
When using interface specific caching features, ensure that code checks for the required interface before using the methods supplied. The following example checks whether the garbage collection interface is available before calling the `garbage_collect` method.
|
||||||
|
|
||||||
|
// Create a cache instance
|
||||||
|
$cache = Cache::instance();
|
||||||
|
|
||||||
|
// Test for Garbage Collection
|
||||||
|
if ($cache instanceof Cache_GarbageCollect)
|
||||||
|
{
|
||||||
|
// Collect garbage
|
||||||
|
$cache->garbage_collect();
|
||||||
|
}
|
162
modules/cache/guide/cache/config.md
vendored
Normal file
162
modules/cache/guide/cache/config.md
vendored
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
# Kohana Cache configuration
|
||||||
|
|
||||||
|
Kohana Cache uses configuration groups to create cache instances. A configuration group can
|
||||||
|
use any supported driver, with successive groups using multiple instances of the same driver type.
|
||||||
|
|
||||||
|
The default cache group is loaded based on the `Cache::$default` setting. It is set to the `file` driver as standard, however this can be changed within the `/application/boostrap.php` file.
|
||||||
|
|
||||||
|
// Change the default cache driver to memcache
|
||||||
|
Cache::$default = 'memcache';
|
||||||
|
|
||||||
|
// Load the memcache cache driver using default setting
|
||||||
|
$memcache = Cache::instance();
|
||||||
|
|
||||||
|
## Group settings
|
||||||
|
|
||||||
|
Below are the default cache configuration groups for each supported driver. Add to- or override these settings
|
||||||
|
within the `application/config/cache.php` file.
|
||||||
|
|
||||||
|
Name | Required | Description
|
||||||
|
-------------- | -------- | ---------------------------------------------------------------
|
||||||
|
driver | __YES__ | (_string_) The driver type to use
|
||||||
|
default_expire | __NO__ | (_string_) The driver type to use
|
||||||
|
|
||||||
|
|
||||||
|
'file' => array
|
||||||
|
(
|
||||||
|
'driver' => 'file',
|
||||||
|
'cache_dir' => APPPATH.'cache/.kohana_cache',
|
||||||
|
'default_expire' => 3600,
|
||||||
|
),
|
||||||
|
|
||||||
|
## Memcache & Memcached-tag settings
|
||||||
|
|
||||||
|
Name | Required | Description
|
||||||
|
-------------- | -------- | ---------------------------------------------------------------
|
||||||
|
driver | __YES__ | (_string_) The driver type to use
|
||||||
|
servers | __YES__ | (_array_) Associative array of server details, must include a __host__ key. (see _Memcache server configuration_ below)
|
||||||
|
compression | __NO__ | (_boolean_) Use data compression when caching
|
||||||
|
|
||||||
|
### Memcache server configuration
|
||||||
|
|
||||||
|
Name | Required | Description
|
||||||
|
---------------- | -------- | ---------------------------------------------------------------
|
||||||
|
host | __YES__ | (_string_) The host of the memcache server, i.e. __localhost__; or __127.0.0.1__; or __memcache.domain.tld__
|
||||||
|
port | __NO__ | (_integer_) Point to the port where memcached is listening for connections. Set this parameter to 0 when using UNIX domain sockets. Default __11211__
|
||||||
|
persistent | __NO__ | (_boolean_) Controls the use of a persistent connection. Default __TRUE__
|
||||||
|
weight | __NO__ | (_integer_) Number of buckets to create for this server which in turn control its probability of it being selected. The probability is relative to the total weight of all servers. Default __1__
|
||||||
|
timeout | __NO__ | (_integer_) Value in seconds which will be used for connecting to the daemon. Think twice before changing the default value of 1 second - you can lose all the advantages of caching if your connection is too slow. Default __1__
|
||||||
|
retry_interval | __NO__ | (_integer_) Controls how often a failed server will be retried, the default value is 15 seconds. Setting this parameter to -1 disables automatic retry. Default __15__
|
||||||
|
status | __NO__ | (_boolean_) Controls if the server should be flagged as online. Default __TRUE__
|
||||||
|
failure_callback | __NO__ | (_[callback](http://www.php.net/manual/en/language.pseudo-types.php#language.types.callback)_) Allows the user to specify a callback function to run upon encountering an error. The callback is run before failover is attempted. The function takes two parameters, the hostname and port of the failed server. Default __NULL__
|
||||||
|
|
||||||
|
'memcache' => array
|
||||||
|
(
|
||||||
|
'driver' => 'memcache',
|
||||||
|
'default_expire' => 3600,
|
||||||
|
'compression' => FALSE, // Use Zlib compression
|
||||||
|
(can cause issues with integers)
|
||||||
|
'servers' => array
|
||||||
|
(
|
||||||
|
'local' => array
|
||||||
|
(
|
||||||
|
'host' => 'localhost', // Memcache Server
|
||||||
|
'port' => 11211, // Memcache port number
|
||||||
|
'persistent' => FALSE, // Persistent connection
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'memcachetag' => array
|
||||||
|
(
|
||||||
|
'driver' => 'memcachetag',
|
||||||
|
'default_expire' => 3600,
|
||||||
|
'compression' => FALSE, // Use Zlib compression
|
||||||
|
(can cause issues with integers)
|
||||||
|
'servers' => array
|
||||||
|
(
|
||||||
|
'local' => array
|
||||||
|
(
|
||||||
|
'host' => 'localhost', // Memcache Server
|
||||||
|
'port' => 11211, // Memcache port number
|
||||||
|
'persistent' => FALSE, // Persistent connection
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
## APC settings
|
||||||
|
|
||||||
|
'apc' => array
|
||||||
|
(
|
||||||
|
'driver' => 'apc',
|
||||||
|
'default_expire' => 3600,
|
||||||
|
),
|
||||||
|
|
||||||
|
## SQLite settings
|
||||||
|
|
||||||
|
'sqlite' => array
|
||||||
|
(
|
||||||
|
'driver' => 'sqlite',
|
||||||
|
'default_expire' => 3600,
|
||||||
|
'database' => APPPATH.'cache/kohana-cache.sql3',
|
||||||
|
'schema' => 'CREATE TABLE caches(id VARCHAR(127) PRIMARY KEY,
|
||||||
|
tags VARCHAR(255), expiration INTEGER, cache TEXT)',
|
||||||
|
),
|
||||||
|
|
||||||
|
## File settings
|
||||||
|
|
||||||
|
'file' => array
|
||||||
|
(
|
||||||
|
'driver' => 'file',
|
||||||
|
'cache_dir' => 'cache/.kohana_cache',
|
||||||
|
'default_expire' => 3600,
|
||||||
|
)
|
||||||
|
|
||||||
|
## Wincache settings
|
||||||
|
|
||||||
|
'wincache' => array
|
||||||
|
(
|
||||||
|
'driver' => 'wincache',
|
||||||
|
'default_expire' => 3600,
|
||||||
|
),
|
||||||
|
|
||||||
|
|
||||||
|
## Override existing configuration group
|
||||||
|
|
||||||
|
The following example demonstrates how to override an existing configuration setting, using the config file in `/application/config/cache.php`.
|
||||||
|
|
||||||
|
<?php defined('SYSPATH') or die('No direct script access.');
|
||||||
|
return array
|
||||||
|
(
|
||||||
|
// Override the default configuration
|
||||||
|
'memcache' => array
|
||||||
|
(
|
||||||
|
'driver' => 'memcache', // Use Memcached as the default driver
|
||||||
|
'default_expire' => 8000, // Overide default expiry
|
||||||
|
'servers' => array
|
||||||
|
(
|
||||||
|
// Add a new server
|
||||||
|
array
|
||||||
|
(
|
||||||
|
'host' => 'cache.domain.tld',
|
||||||
|
'port' => 11211,
|
||||||
|
'persistent' => FALSE
|
||||||
|
)
|
||||||
|
),
|
||||||
|
'compression' => FALSE
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
## Add new configuration group
|
||||||
|
|
||||||
|
The following example demonstrates how to add a new configuration setting, using the config file in `/application/config/cache.php`.
|
||||||
|
|
||||||
|
<?php defined('SYSPATH') or die('No direct script access.');
|
||||||
|
return array
|
||||||
|
(
|
||||||
|
// Override the default configuration
|
||||||
|
'fastkv' => array
|
||||||
|
(
|
||||||
|
'driver' => 'apc', // Use Memcached as the default driver
|
||||||
|
'default_expire' => 1000, // Overide default expiry
|
||||||
|
)
|
||||||
|
);
|
0
modules/cache/guide/cache/examples.md
vendored
Normal file
0
modules/cache/guide/cache/examples.md
vendored
Normal file
57
modules/cache/guide/cache/index.md
vendored
Normal file
57
modules/cache/guide/cache/index.md
vendored
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
# About Kohana Cache
|
||||||
|
|
||||||
|
[Kohana_Cache] provides a common interface to a variety of caching engines. [Cache_Tagging] is
|
||||||
|
supported where available natively to the cache system. Kohana Cache supports multiple
|
||||||
|
instances of cache engines through a grouped singleton pattern.
|
||||||
|
|
||||||
|
## Supported cache engines
|
||||||
|
|
||||||
|
* APC ([Cache_Apc])
|
||||||
|
* File ([Cache_File])
|
||||||
|
* Memcached ([Cache_Memcache])
|
||||||
|
* Memcached-tags ([Cache_Memcachetag])
|
||||||
|
* SQLite ([Cache_Sqlite])
|
||||||
|
* Wincache
|
||||||
|
|
||||||
|
## Introduction to caching
|
||||||
|
|
||||||
|
Caching should be implemented with consideration. Generally, caching the result of resources
|
||||||
|
is faster than reprocessing them. Choosing what, how and when to cache is vital. [PHP APC](http://php.net/manual/en/book.apc.php) is one of the fastest caching systems available, closely followed by [Memcached](http://memcached.org/). [SQLite](http://www.sqlite.org/) and File caching are two of the slowest cache methods, however usually faster than reprocessing
|
||||||
|
a complex set of instructions.
|
||||||
|
|
||||||
|
Caching engines that use memory are considerably faster than file based alternatives. But
|
||||||
|
memory is limited whereas disk space is plentiful. If caching large datasets, such as large database result sets, it is best to use file caching.
|
||||||
|
|
||||||
|
[!!] Cache drivers require the relevant PHP extensions to be installed. APC, eAccelerator, Memecached and Xcache all require non-standard PHP extensions.
|
||||||
|
|
||||||
|
## What the Kohana Cache module does (and does not do)
|
||||||
|
|
||||||
|
This module provides a simple abstracted interface to a wide selection of popular PHP cache engines. The caching API provides the basic caching methods implemented across all solutions, memory, network or disk based. Basic key / value storing is supported by all drivers, with additional tagging and garbage collection support where implemented or required.
|
||||||
|
|
||||||
|
_Kohana Cache_ does not provide HTTP style caching for clients (web browsers) and/or proxies (_Varnish_, _Squid_). There are other Kohana modules that provide this functionality.
|
||||||
|
|
||||||
|
## Choosing a cache provider
|
||||||
|
|
||||||
|
Getting and setting values to cache is very simple when using the _Kohana Cache_ interface. The hardest choice is choosing which cache engine to use. When choosing a caching engine, the following criteria must be considered:
|
||||||
|
|
||||||
|
1. __Does the cache need to be distributed?__
|
||||||
|
This is an important consideration as it will severely limit the options available to solutions such as Memcache when a distributed solution is required.
|
||||||
|
2. __Does the cache need to be fast?__
|
||||||
|
In almost all cases retrieving data from a cache is faster than execution. However generally memory based caching is considerably faster than disk based caching (see table below).
|
||||||
|
3. __How much cache is required?__
|
||||||
|
Cache is not endless, and memory based caches are subject to a considerably more limited storage resource.
|
||||||
|
|
||||||
|
Driver | Storage | Speed | Tags | Distributed | Automatic Garbage Collection | Notes
|
||||||
|
---------------- | ------------ | --------- | -------- | ----------- | ---------------------------- | -----------------------
|
||||||
|
APC | __Memory__ | Excellent | No | No | Yes | Widely available PHP opcode caching solution, improves php execution performance
|
||||||
|
Wincache | __Memory__ | Excellent | No | No | Yes | Windows variant of APC
|
||||||
|
File | __Disk__ | Poor | No | No | No | Marginally faster than execution
|
||||||
|
Memcache (tag) | __Memory__ | Good | No (yes) | Yes | Yes | Generally fast distributed solution, but has a speed hit due to variable network latency and serialization
|
||||||
|
Sqlite | __Disk__ | Poor | Yes | No | No | Marginally faster than execution
|
||||||
|
|
||||||
|
It is possible to have hybrid cache solutions that use a combination of the engines above in different contexts. This is supported with _Kohana Cache_ as well
|
||||||
|
|
||||||
|
## Minimum requirements
|
||||||
|
|
||||||
|
* Kohana 3.0.4
|
||||||
|
* PHP 5.2.4 or greater
|
3
modules/cache/guide/cache/menu.md
vendored
Normal file
3
modules/cache/guide/cache/menu.md
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
## [Cache]()
|
||||||
|
- [Configuration](config)
|
||||||
|
- [Usage](usage)
|
219
modules/cache/guide/cache/usage.md
vendored
Normal file
219
modules/cache/guide/cache/usage.md
vendored
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
# Kohana Cache usage
|
||||||
|
|
||||||
|
[Kohana_Cache] provides a simple interface allowing getting, setting and deleting of cached values. Two interfaces included in _Kohana Cache_ additionally provide _tagging_ and _garbage collection_ where they are supported by the respective drivers.
|
||||||
|
|
||||||
|
## Getting a new cache instance
|
||||||
|
|
||||||
|
Creating a new _Kohana Cache_ instance is simple, however it must be done using the [Cache::instance] method, rather than the traditional `new` constructor.
|
||||||
|
|
||||||
|
// Create a new instance of cache using the default group
|
||||||
|
$cache = Cache::instance();
|
||||||
|
|
||||||
|
The default group will use whatever is set to [Cache::$default] and must have a corresponding [configuration](cache.config) definition for that group.
|
||||||
|
|
||||||
|
To create a cache instance using a group other than the _default_, simply provide the group name as an argument.
|
||||||
|
|
||||||
|
// Create a new instance of the memcache group
|
||||||
|
$memcache = Cache::instance('memcache');
|
||||||
|
|
||||||
|
If there is a cache instance already instantiated then you can get it directly from the class member.
|
||||||
|
|
||||||
|
[!!] Beware that this can cause issues if you do not test for the instance before trying to access it.
|
||||||
|
|
||||||
|
// Check for the existance of the cache driver
|
||||||
|
if (isset(Cache::$instances['memcache']))
|
||||||
|
{
|
||||||
|
// Get the existing cache instance directly (faster)
|
||||||
|
$memcache = Cache::$instances['memcache'];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Get the cache driver instance (slower)
|
||||||
|
$memcache = Cache::instance('memcache');
|
||||||
|
}
|
||||||
|
|
||||||
|
## Setting and getting variables to and from cache
|
||||||
|
|
||||||
|
The cache library supports scalar and object values, utilising object serialization where required (or not supported by the caching engine). This means that the majority or objects can be cached without any modification.
|
||||||
|
|
||||||
|
[!!] Serialisation does not work with resource handles, such as filesystem, curl or socket resources.
|
||||||
|
|
||||||
|
### Setting a value to cache
|
||||||
|
|
||||||
|
Setting a value to cache using the [Cache::set] method can be done in one of two ways; either using the Cache instance interface, which is good for atomic operations; or getting an instance and using that for multiple operations.
|
||||||
|
|
||||||
|
The first example demonstrates how to quickly load and set a value to the default cache instance.
|
||||||
|
|
||||||
|
// Create a cachable object
|
||||||
|
$object = new stdClass;
|
||||||
|
|
||||||
|
// Set a property
|
||||||
|
$object->foo = 'bar';
|
||||||
|
|
||||||
|
// Cache the object using default group (quick interface) with default time (3600 seconds)
|
||||||
|
Cache::instance()->set('foo', $object);
|
||||||
|
|
||||||
|
If multiple cache operations are required, it is best to assign an instance of Cache to a variable and use that as below.
|
||||||
|
|
||||||
|
// Set the object using a defined group for a defined time period (30 seconds)
|
||||||
|
$memcache = Cache::instance('memcache');
|
||||||
|
$memcache->set('foo', $object, 30);
|
||||||
|
|
||||||
|
#### Setting a value with tags
|
||||||
|
|
||||||
|
Certain cache drivers support setting values with tags. To set a value to cache with tags using the following interface.
|
||||||
|
|
||||||
|
// Get a cache instance that supports tags
|
||||||
|
$memcache = Cache::instance('memcachetag');
|
||||||
|
|
||||||
|
// Test for tagging interface
|
||||||
|
if ($memcache instanceof Cache_Tagging)
|
||||||
|
{
|
||||||
|
// Set a value with some tags for 30 seconds
|
||||||
|
$memcache->set('foo', $object, 30, array('snafu', 'stfu', 'fubar'));
|
||||||
|
}
|
||||||
|
// Otherwise set without tags
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Set a value for 30 seconds
|
||||||
|
$memcache->set('foo', $object, 30);
|
||||||
|
}
|
||||||
|
|
||||||
|
It is possible to implement custom tagging solutions onto existing or new cache drivers by implementing the [Cache_Tagging] interface. Kohana_Cache only applies the interface to drivers that support tagging natively as standard.
|
||||||
|
|
||||||
|
### Getting a value from cache
|
||||||
|
|
||||||
|
Getting variables back from cache is achieved using the [Cache::get] method using a single key to identify the cache entry.
|
||||||
|
|
||||||
|
// Retrieve a value from cache (quickly)
|
||||||
|
$object = Cache::instance()->get('foo');
|
||||||
|
|
||||||
|
In cases where the requested key is not available or the entry has expired, a default value will be returned (__NULL__ by default). It is possible to define the default value as the key is requested.
|
||||||
|
|
||||||
|
// If the cache key is available (with default value set to FALSE)
|
||||||
|
if ($object = Cache::instance()->get('foo', FALSE))
|
||||||
|
{
|
||||||
|
// Do something
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Do something else
|
||||||
|
}
|
||||||
|
|
||||||
|
#### Getting values from cache using tags
|
||||||
|
|
||||||
|
It is possible to retrieve values from cache grouped by tag, using the [Cache::find] method with drivers that support tagging.
|
||||||
|
|
||||||
|
[!!] The __Memcachetag__ driver does not support the `Cache::find($tag)` interface and will throw an exception.
|
||||||
|
|
||||||
|
// Get an instance of cache
|
||||||
|
$cache = Cache::instance('memcachetag');
|
||||||
|
|
||||||
|
// Wrap in a try/catch statement to gracefully handle memcachetag
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Find values based on tag
|
||||||
|
return $cache->find('snafu');
|
||||||
|
}
|
||||||
|
catch (Cache_Exception $e)
|
||||||
|
{
|
||||||
|
// Handle gracefully
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
### Deleting values from cache
|
||||||
|
|
||||||
|
Deleting variables is very similar to the getting and setting methods already described. Deleting operations are split into three categories:
|
||||||
|
|
||||||
|
- __Delete value by key__. Deletes a cached value by the associated key.
|
||||||
|
- __Delete all values__. Deletes all caches values stored in the cache instance.
|
||||||
|
- __Delete values by tag__. Deletes all values that have the supplied tag. This is only supported by Memcached-Tag and Sqlite.
|
||||||
|
|
||||||
|
#### Delete value by key
|
||||||
|
|
||||||
|
To delete a specific value by its associated key:
|
||||||
|
|
||||||
|
// If the cache entry for 'foo' is deleted
|
||||||
|
if (Cache::instance()->delete('foo'))
|
||||||
|
{
|
||||||
|
// Cache entry successfully deleted, do something
|
||||||
|
}
|
||||||
|
|
||||||
|
By default a `TRUE` value will be returned. However a `FALSE` value will be returned in instances where the key did not exist in the cache.
|
||||||
|
|
||||||
|
#### Delete all values
|
||||||
|
|
||||||
|
To delete all values in a specific instance:
|
||||||
|
|
||||||
|
// If all cache items where deleted successfully
|
||||||
|
if (Cache::instance()->delete_all())
|
||||||
|
{
|
||||||
|
// Do something
|
||||||
|
}
|
||||||
|
|
||||||
|
It is also possible to delete all cache items in every instance:
|
||||||
|
|
||||||
|
// For each cache instance
|
||||||
|
foreach (Cache::$instances as $group => $instance)
|
||||||
|
{
|
||||||
|
if ($instance->delete_all())
|
||||||
|
{
|
||||||
|
var_dump('instance : '.$group.' has been flushed!');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#### Delete values by tag
|
||||||
|
|
||||||
|
Some of the caching drivers support deleting by tag. This will remove all the cached values that are associated with a specific tag. Below is an example of how to robustly handle deletion by tag.
|
||||||
|
|
||||||
|
// Get cache instance
|
||||||
|
$cache = Cache::instance();
|
||||||
|
|
||||||
|
// Check for tagging interface
|
||||||
|
if ($cache instanceof Cache_Tagging)
|
||||||
|
{
|
||||||
|
// Delete all entries by the tag 'snafu'
|
||||||
|
$cache->delete_tag('snafu');
|
||||||
|
}
|
||||||
|
|
||||||
|
#### Garbage Collection
|
||||||
|
|
||||||
|
Garbage Collection (GC) is the cleaning of expired cache entries. For the most part, caching engines will take care of garbage collection internally. However a few of the file based systems do not handle this task and in these circumstances it would be prudent to garbage collect at a predetermined frequency. If no garbage collection is executed, the resource storing the cache entries will eventually fill and become unusable.
|
||||||
|
|
||||||
|
When not automated, garbage collection is the responsibility of the developer. It is prudent to have a GC probability value that dictates how likely the garbage collection routing will be run. An example of such a system is demonstrated below.
|
||||||
|
|
||||||
|
// Get a cache instance
|
||||||
|
$cache_file = Cache::instance('file');
|
||||||
|
|
||||||
|
// Set a GC probability of 10%
|
||||||
|
$gc = 10;
|
||||||
|
|
||||||
|
// If the GC probability is a hit
|
||||||
|
if (rand(0,99) <= $gc and $cache_file instanceof Cache_GarbageCollect)
|
||||||
|
{
|
||||||
|
// Garbage Collect
|
||||||
|
$cache_file->garbage_collect();
|
||||||
|
}
|
||||||
|
|
||||||
|
# Interfaces
|
||||||
|
|
||||||
|
Kohana Cache comes with two interfaces that are implemented where the drivers support them:
|
||||||
|
|
||||||
|
- __[Cache_Tagging] for tagging support on cache entries__
|
||||||
|
- [Cache_MemcacheTag]
|
||||||
|
- [Cache_Sqlite]
|
||||||
|
- __[Cache_GarbageCollect] for garbage collection with drivers without native support__
|
||||||
|
- [Cache_File]
|
||||||
|
- [Cache_Sqlite]
|
||||||
|
|
||||||
|
When using interface specific caching features, ensure that code checks for the required interface before using the methods supplied. The following example checks whether the garbage collection interface is available before calling the `garbage_collect` method.
|
||||||
|
|
||||||
|
// Create a cache instance
|
||||||
|
$cache = Cache::instance();
|
||||||
|
|
||||||
|
// Test for Garbage Collection
|
||||||
|
if ($cache instanceof Cache_GarbageCollect)
|
||||||
|
{
|
||||||
|
// Collect garbage
|
||||||
|
$cache->garbage_collect();
|
||||||
|
}
|
299
modules/cache/tests/cache/CacheBasicMethodsTest.php
vendored
Normal file
299
modules/cache/tests/cache/CacheBasicMethodsTest.php
vendored
Normal file
@ -0,0 +1,299 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @package Kohana/Cache
|
||||||
|
* @group kohana
|
||||||
|
* @group kohana.cache
|
||||||
|
* @category Test
|
||||||
|
* @author Kohana Team
|
||||||
|
* @copyright (c) 2009-2012 Kohana Team
|
||||||
|
* @license http://kohanaphp.com/license
|
||||||
|
*/
|
||||||
|
abstract class Kohana_CacheBasicMethodsTest extends PHPUnit_Framework_TestCase {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Cache driver for this test
|
||||||
|
*/
|
||||||
|
protected $_cache_driver;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method MUST be implemented by each driver to setup the `Cache`
|
||||||
|
* instance for each test.
|
||||||
|
*
|
||||||
|
* This method should do the following tasks for each driver test:
|
||||||
|
*
|
||||||
|
* - Test the Cache instance driver is available, skip test otherwise
|
||||||
|
* - Setup the Cache instance
|
||||||
|
* - Call the parent setup method, `parent::setUp()`
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accessor method to `$_cache_driver`.
|
||||||
|
*
|
||||||
|
* @return Cache
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
public function cache(Cache $cache = NULL)
|
||||||
|
{
|
||||||
|
if ($cache === NULL)
|
||||||
|
return $this->_cache_driver;
|
||||||
|
|
||||||
|
$this->_cache_driver = $cache;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data provider for test_set_get()
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function provider_set_get()
|
||||||
|
{
|
||||||
|
$object = new StdClass;
|
||||||
|
$object->foo = 'foo';
|
||||||
|
$object->bar = 'bar';
|
||||||
|
|
||||||
|
$html_text = <<<TESTTEXT
|
||||||
|
<!doctype html>
|
||||||
|
<head>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
TESTTEXT;
|
||||||
|
|
||||||
|
return array(
|
||||||
|
array(
|
||||||
|
array(
|
||||||
|
'id' => 'string', // Key to set to cache
|
||||||
|
'value' => 'foobar', // Value to set to key
|
||||||
|
'ttl' => 0, // Time to live
|
||||||
|
'wait' => FALSE, // Test wait time to let cache expire
|
||||||
|
'type' => 'string', // Type test
|
||||||
|
'default' => NULL // Default value get should return
|
||||||
|
),
|
||||||
|
'foobar'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
array(
|
||||||
|
'id' => 'integer',
|
||||||
|
'value' => 101010,
|
||||||
|
'ttl' => 0,
|
||||||
|
'wait' => FALSE,
|
||||||
|
'type' => 'integer',
|
||||||
|
'default' => NULL
|
||||||
|
),
|
||||||
|
101010
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
array(
|
||||||
|
'id' => 'float',
|
||||||
|
'value' => 10.00,
|
||||||
|
'ttl' => 0,
|
||||||
|
'wait' => FALSE,
|
||||||
|
'type' => 'float',
|
||||||
|
'default' => NULL
|
||||||
|
),
|
||||||
|
10.00
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
array(
|
||||||
|
'id' => 'array',
|
||||||
|
'value' => array(
|
||||||
|
'key' => 'foo',
|
||||||
|
'value' => 'bar'
|
||||||
|
),
|
||||||
|
'ttl' => 0,
|
||||||
|
'wait' => FALSE,
|
||||||
|
'type' => 'array',
|
||||||
|
'default' => NULL
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'key' => 'foo',
|
||||||
|
'value' => 'bar'
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
array(
|
||||||
|
'id' => 'boolean',
|
||||||
|
'value' => TRUE,
|
||||||
|
'ttl' => 0,
|
||||||
|
'wait' => FALSE,
|
||||||
|
'type' => 'boolean',
|
||||||
|
'default' => NULL
|
||||||
|
),
|
||||||
|
TRUE
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
array(
|
||||||
|
'id' => 'null',
|
||||||
|
'value' => NULL,
|
||||||
|
'ttl' => 0,
|
||||||
|
'wait' => FALSE,
|
||||||
|
'type' => 'null',
|
||||||
|
'default' => NULL
|
||||||
|
),
|
||||||
|
NULL
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
array(
|
||||||
|
'id' => 'object',
|
||||||
|
'value' => $object,
|
||||||
|
'ttl' => 0,
|
||||||
|
'wait' => FALSE,
|
||||||
|
'type' => 'object',
|
||||||
|
'default' => NULL
|
||||||
|
),
|
||||||
|
$object
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
array(
|
||||||
|
'id' => 'bar\\ with / troublesome key',
|
||||||
|
'value' => 'foo bar snafu',
|
||||||
|
'ttl' => 0,
|
||||||
|
'wait' => FALSE,
|
||||||
|
'type' => 'string',
|
||||||
|
'default' => NULL
|
||||||
|
),
|
||||||
|
'foo bar snafu'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
array(
|
||||||
|
'id' => 'bar',
|
||||||
|
'value' => 'foo',
|
||||||
|
'ttl' => 3,
|
||||||
|
'wait' => 5,
|
||||||
|
'type' => 'null',
|
||||||
|
'default' => NULL
|
||||||
|
),
|
||||||
|
NULL
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
array(
|
||||||
|
'id' => 'snafu',
|
||||||
|
'value' => 'fubar',
|
||||||
|
'ttl' => 3,
|
||||||
|
'wait' => 5,
|
||||||
|
'type' => 'string',
|
||||||
|
'default' => 'something completely different!'
|
||||||
|
),
|
||||||
|
'something completely different!'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
array(
|
||||||
|
'id' => 'new line test with HTML',
|
||||||
|
'value' => $html_text,
|
||||||
|
'ttl' => 10,
|
||||||
|
'wait' => FALSE,
|
||||||
|
'type' => 'string',
|
||||||
|
'default' => NULL,
|
||||||
|
),
|
||||||
|
$html_text
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the [Cache::set()] method, testing;
|
||||||
|
*
|
||||||
|
* - The value is cached
|
||||||
|
* - The lifetime is respected
|
||||||
|
* - The returned value type is as expected
|
||||||
|
* - The default not-found value is respected
|
||||||
|
*
|
||||||
|
* @dataProvider provider_set_get
|
||||||
|
*
|
||||||
|
* @param array data
|
||||||
|
* @param mixed expected
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function test_set_get(array $data, $expected)
|
||||||
|
{
|
||||||
|
$cache = $this->cache();
|
||||||
|
extract($data);
|
||||||
|
|
||||||
|
$this->assertTrue($cache->set($id, $value, $ttl));
|
||||||
|
|
||||||
|
if ($wait !== FALSE)
|
||||||
|
{
|
||||||
|
// Lets let the cache expire
|
||||||
|
sleep($wait);
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = $cache->get($id, $default);
|
||||||
|
$this->assertEquals($expected, $result);
|
||||||
|
$this->assertInternalType($type, $result);
|
||||||
|
|
||||||
|
unset($id, $value, $ttl, $wait, $type, $default);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the [Cache::delete()] method, testing;
|
||||||
|
*
|
||||||
|
* - The a cached value is deleted from cache
|
||||||
|
* - The cache returns a TRUE value upon deletion
|
||||||
|
* - The cache returns a FALSE value if no value exists to delete
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function test_delete()
|
||||||
|
{
|
||||||
|
// Init
|
||||||
|
$cache = $this->cache();
|
||||||
|
$cache->delete_all();
|
||||||
|
|
||||||
|
// Test deletion if real cached value
|
||||||
|
if ( ! $cache->set('test_delete_1', 'This should not be here!', 0))
|
||||||
|
{
|
||||||
|
$this->fail('Unable to set cache value to delete!');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test delete returns TRUE and check the value is gone
|
||||||
|
$this->assertTrue($cache->delete('test_delete_1'));
|
||||||
|
$this->assertNull($cache->get('test_delete_1'));
|
||||||
|
|
||||||
|
// Test non-existant cache value returns FALSE if no error
|
||||||
|
$this->assertFalse($cache->delete('test_delete_1'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests [Cache::delete_all()] works as specified
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
* @uses Kohana_CacheBasicMethodsTest::provider_set_get()
|
||||||
|
*/
|
||||||
|
public function test_delete_all()
|
||||||
|
{
|
||||||
|
// Init
|
||||||
|
$cache = $this->cache();
|
||||||
|
$data = $this->provider_set_get();
|
||||||
|
|
||||||
|
foreach ($data as $key => $values)
|
||||||
|
{
|
||||||
|
extract($values[0]);
|
||||||
|
if ( ! $cache->set($id, $value))
|
||||||
|
{
|
||||||
|
$this->fail('Unable to set: '.$key.' => '.$value.' to cache');
|
||||||
|
}
|
||||||
|
unset($id, $value, $ttl, $wait, $type, $default);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test delete_all is successful
|
||||||
|
$this->assertTrue($cache->delete_all());
|
||||||
|
|
||||||
|
foreach ($data as $key => $values)
|
||||||
|
{
|
||||||
|
// Verify data has been purged
|
||||||
|
$this->assertSame('Cache Deleted!', $cache->get($values[0]['id'],
|
||||||
|
'Cache Deleted!'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // End Kohana_CacheBasicMethodsTest
|
242
modules/cache/tests/cache/CacheTest.php
vendored
Normal file
242
modules/cache/tests/cache/CacheTest.php
vendored
Normal file
@ -0,0 +1,242 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @package Kohana/Cache
|
||||||
|
* @group kohana
|
||||||
|
* @group kohana.cache
|
||||||
|
* @category Test
|
||||||
|
* @author Kohana Team
|
||||||
|
* @copyright (c) 2009-2012 Kohana Team
|
||||||
|
* @license http://kohanaphp.com/license
|
||||||
|
*/
|
||||||
|
class Kohana_CacheTest extends PHPUnit_Framework_TestCase {
|
||||||
|
|
||||||
|
const BAD_GROUP_DEFINITION = 1010;
|
||||||
|
const EXPECT_SELF = 1001;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data provider for test_instance
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function provider_instance()
|
||||||
|
{
|
||||||
|
$tmp = realpath(sys_get_temp_dir());
|
||||||
|
|
||||||
|
$base = array();
|
||||||
|
|
||||||
|
if (Kohana::$config->load('cache.file'))
|
||||||
|
{
|
||||||
|
$base = array(
|
||||||
|
// Test default group
|
||||||
|
array(
|
||||||
|
NULL,
|
||||||
|
Cache::instance('file')
|
||||||
|
),
|
||||||
|
// Test defined group
|
||||||
|
array(
|
||||||
|
'file',
|
||||||
|
Cache::instance('file')
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return array(
|
||||||
|
// Test bad group definition
|
||||||
|
$base+array(
|
||||||
|
Kohana_CacheTest::BAD_GROUP_DEFINITION,
|
||||||
|
'Failed to load Kohana Cache group: 1010'
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the [Cache::factory()] method behaves as expected
|
||||||
|
*
|
||||||
|
* @dataProvider provider_instance
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function test_instance($group, $expected)
|
||||||
|
{
|
||||||
|
if (in_array($group, array(
|
||||||
|
Kohana_CacheTest::BAD_GROUP_DEFINITION,
|
||||||
|
)
|
||||||
|
))
|
||||||
|
{
|
||||||
|
$this->setExpectedException('Cache_Exception');
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$cache = Cache::instance($group);
|
||||||
|
}
|
||||||
|
catch (Cache_Exception $e)
|
||||||
|
{
|
||||||
|
$this->assertSame($expected, $e->getMessage());
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->assertInstanceOf(get_class($expected), $cache);
|
||||||
|
$this->assertSame($expected->config(), $cache->config());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that `clone($cache)` will be prevented to maintain singleton
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
* @expectedException Cache_Exception
|
||||||
|
*/
|
||||||
|
public function test_cloning_fails()
|
||||||
|
{
|
||||||
|
if ( ! Kohana::$config->load('cache.file'))
|
||||||
|
{
|
||||||
|
$this->markTestSkipped('Unable to load File configuration');
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$cache_clone = clone(Cache::instance('file'));
|
||||||
|
}
|
||||||
|
catch (Cache_Exception $e)
|
||||||
|
{
|
||||||
|
$this->assertSame('Cloning of Kohana_Cache objects is forbidden',
|
||||||
|
$e->getMessage());
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data provider for test_config
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function provider_config()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
array(
|
||||||
|
array(
|
||||||
|
'server' => 'otherhost',
|
||||||
|
'port' => 5555,
|
||||||
|
'persistent' => TRUE,
|
||||||
|
),
|
||||||
|
NULL,
|
||||||
|
Kohana_CacheTest::EXPECT_SELF,
|
||||||
|
array(
|
||||||
|
'server' => 'otherhost',
|
||||||
|
'port' => 5555,
|
||||||
|
'persistent' => TRUE,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'foo',
|
||||||
|
'bar',
|
||||||
|
Kohana_CacheTest::EXPECT_SELF,
|
||||||
|
array(
|
||||||
|
'foo' => 'bar'
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'server',
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
array()
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
array(),
|
||||||
|
array()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the config method behaviour
|
||||||
|
*
|
||||||
|
* @dataProvider provider_config
|
||||||
|
*
|
||||||
|
* @param mixed key value to set or get
|
||||||
|
* @param mixed value to set to key
|
||||||
|
* @param mixed expected result from [Cache::config()]
|
||||||
|
* @param array expected config within cache
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function test_config($key, $value, $expected_result, array $expected_config)
|
||||||
|
{
|
||||||
|
$cache = $this->getMock('Cache_File', NULL, array(), '', FALSE);
|
||||||
|
|
||||||
|
if ($expected_result === Kohana_CacheTest::EXPECT_SELF)
|
||||||
|
{
|
||||||
|
$expected_result = $cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->assertSame($expected_result, $cache->config($key, $value));
|
||||||
|
$this->assertSame($expected_config, $cache->config());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data provider for test_sanitize_id
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function provider_sanitize_id()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
array(
|
||||||
|
'foo',
|
||||||
|
'foo'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'foo+-!@',
|
||||||
|
'foo+-!@'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'foo/bar',
|
||||||
|
'foo_bar',
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'foo\\bar',
|
||||||
|
'foo_bar'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'foo bar',
|
||||||
|
'foo_bar'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'foo\\bar snafu/stfu',
|
||||||
|
'foo_bar_snafu_stfu'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the [Cache::_sanitize_id()] method works as expected.
|
||||||
|
* This uses some nasty reflection techniques to access a protected
|
||||||
|
* method.
|
||||||
|
*
|
||||||
|
* @dataProvider provider_sanitize_id
|
||||||
|
*
|
||||||
|
* @param string id
|
||||||
|
* @param string expected
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function test_sanitize_id($id, $expected)
|
||||||
|
{
|
||||||
|
$cache = $this->getMock('Cache', array(
|
||||||
|
'get',
|
||||||
|
'set',
|
||||||
|
'delete',
|
||||||
|
'delete_all'
|
||||||
|
), array(array()),
|
||||||
|
'', FALSE
|
||||||
|
);
|
||||||
|
|
||||||
|
$cache_reflection = new ReflectionClass($cache);
|
||||||
|
$sanitize_id = $cache_reflection->getMethod('_sanitize_id');
|
||||||
|
$sanitize_id->setAccessible(TRUE);
|
||||||
|
|
||||||
|
$this->assertSame($expected, $sanitize_id->invoke($cache, $id));
|
||||||
|
}
|
||||||
|
} // End Kohana_CacheTest
|
98
modules/cache/tests/cache/FileTest.php
vendored
Normal file
98
modules/cache/tests/cache/FileTest.php
vendored
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
<?php
|
||||||
|
include_once(Kohana::find_file('tests/cache', 'CacheBasicMethodsTest'));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @package Kohana/Cache
|
||||||
|
* @group kohana
|
||||||
|
* @group kohana.cache
|
||||||
|
* @category Test
|
||||||
|
* @author Kohana Team
|
||||||
|
* @copyright (c) 2009-2012 Kohana Team
|
||||||
|
* @license http://kohanaphp.com/license
|
||||||
|
*/
|
||||||
|
class Kohana_Cache_FileTest extends Kohana_CacheBasicMethodsTest {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method MUST be implemented by each driver to setup the `Cache`
|
||||||
|
* instance for each test.
|
||||||
|
*
|
||||||
|
* This method should do the following tasks for each driver test:
|
||||||
|
*
|
||||||
|
* - Test the Cache instance driver is available, skip test otherwise
|
||||||
|
* - Setup the Cache instance
|
||||||
|
* - Call the parent setup method, `parent::setUp()`
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
if ( ! Kohana::$config->load('cache.file'))
|
||||||
|
{
|
||||||
|
$this->markTestSkipped('Unable to load File configuration');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->cache(Cache::instance('file'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that ignored files are not removed from file cache
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function test_ignore_delete_file()
|
||||||
|
{
|
||||||
|
$cache = $this->cache();
|
||||||
|
$config = Kohana::$config->load('cache')->file;
|
||||||
|
$file = $config['cache_dir'].'/.gitignore';
|
||||||
|
|
||||||
|
// Lets pollute the cache folder
|
||||||
|
file_put_contents($file, 'foobar');
|
||||||
|
|
||||||
|
$this->assertTrue($cache->delete_all());
|
||||||
|
$this->assertTrue(file_exists($file));
|
||||||
|
$this->assertEquals('foobar', file_get_contents($file));
|
||||||
|
|
||||||
|
unlink($file);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provider for test_utf8
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function provider_utf8()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
array(
|
||||||
|
'This is â ütf-8 Ӝ☃ string',
|
||||||
|
'This is â ütf-8 Ӝ☃ string'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'㆓㆕㆙㆛',
|
||||||
|
'㆓㆕㆙㆛'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'அஆஇஈஊ',
|
||||||
|
'அஆஇஈஊ'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the file driver supports utf-8 strings
|
||||||
|
*
|
||||||
|
* @dataProvider provider_utf8
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function test_utf8($input, $expected)
|
||||||
|
{
|
||||||
|
$cache = $this->cache();
|
||||||
|
$cache->set('utf8', $input);
|
||||||
|
|
||||||
|
$this->assertSame($expected, $cache->get('utf8'));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // End Kohana_SqliteTest
|
44
modules/cache/tests/cache/SqliteTest.php
vendored
Normal file
44
modules/cache/tests/cache/SqliteTest.php
vendored
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<?php
|
||||||
|
include_once(Kohana::find_file('tests/cache', 'CacheBasicMethodsTest'));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @package Kohana/Cache
|
||||||
|
* @group kohana
|
||||||
|
* @group kohana.cache
|
||||||
|
* @category Test
|
||||||
|
* @author Kohana Team
|
||||||
|
* @copyright (c) 2009-2012 Kohana Team
|
||||||
|
* @license http://kohanaphp.com/license
|
||||||
|
*/
|
||||||
|
class Kohana_SqliteTest extends Kohana_CacheBasicMethodsTest {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method MUST be implemented by each driver to setup the `Cache`
|
||||||
|
* instance for each test.
|
||||||
|
*
|
||||||
|
* This method should do the following tasks for each driver test:
|
||||||
|
*
|
||||||
|
* - Test the Cache instance driver is available, skip test otherwise
|
||||||
|
* - Setup the Cache instance
|
||||||
|
* - Call the parent setup method, `parent::setUp()`
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
if ( ! extension_loaded('pdo_sqlite'))
|
||||||
|
{
|
||||||
|
$this->markTestSkipped('SQLite PDO PHP Extension is not available');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! Kohana::$config->load('cache.sqlite'))
|
||||||
|
{
|
||||||
|
$this->markTestIncomplete('Unable to load sqlite configuration');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->cache(Cache::instance('sqlite'));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // End Kohana_SqliteTest
|
39
modules/cache/tests/cache/WincacheTest.php
vendored
Normal file
39
modules/cache/tests/cache/WincacheTest.php
vendored
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
include_once(Kohana::find_file('tests/cache', 'CacheBasicMethodsTest'));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @package Kohana/Cache
|
||||||
|
* @group kohana
|
||||||
|
* @group kohana.cache
|
||||||
|
* @category Test
|
||||||
|
* @author Kohana Team
|
||||||
|
* @copyright (c) 2009-2012 Kohana Team
|
||||||
|
* @license http://kohanaphp.com/license
|
||||||
|
*/
|
||||||
|
class Kohana_WincacheTest extends Kohana_CacheBasicMethodsTest {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method MUST be implemented by each driver to setup the `Cache`
|
||||||
|
* instance for each test.
|
||||||
|
*
|
||||||
|
* This method should do the following tasks for each driver test:
|
||||||
|
*
|
||||||
|
* - Test the Cache instance driver is available, skip test otherwise
|
||||||
|
* - Setup the Cache instance
|
||||||
|
* - Call the parent setup method, `parent::setUp()`
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
if ( ! extension_loaded('wincache'))
|
||||||
|
{
|
||||||
|
$this->markTestSkipped('Wincache PHP Extension is not available');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->cache(Cache::instance('wincache'));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // End Kohana_WincacheTest
|
75
modules/cache/tests/cache/arithmetic/ApcTest.php
vendored
Normal file
75
modules/cache/tests/cache/arithmetic/ApcTest.php
vendored
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
<?php
|
||||||
|
include_once(Kohana::find_file('tests/cache/arithmetic', 'CacheArithmeticMethods'));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @package Kohana/Cache
|
||||||
|
* @group kohana
|
||||||
|
* @group kohana.cache
|
||||||
|
* @category Test
|
||||||
|
* @author Kohana Team
|
||||||
|
* @copyright (c) 2009-2012 Kohana Team
|
||||||
|
* @license http://kohanaphp.com/license
|
||||||
|
*/
|
||||||
|
class Kohana_ApcTest extends Kohana_CacheArithmeticMethodsTest {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method MUST be implemented by each driver to setup the `Cache`
|
||||||
|
* instance for each test.
|
||||||
|
*
|
||||||
|
* This method should do the following tasks for each driver test:
|
||||||
|
*
|
||||||
|
* - Test the Cache instance driver is available, skip test otherwise
|
||||||
|
* - Setup the Cache instance
|
||||||
|
* - Call the parent setup method, `parent::setUp()`
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
if ( ! extension_loaded('apc'))
|
||||||
|
{
|
||||||
|
$this->markTestSkipped('APC PHP Extension is not available');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ini_get('apc.enable_cli') != '1')
|
||||||
|
{
|
||||||
|
$this->markTestSkipped('Unable to test APC in CLI mode. To fix '.
|
||||||
|
'place "apc.enable_cli=1" in your php.ini file');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->cache(Cache::instance('apc'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the [Cache::set()] method, testing;
|
||||||
|
*
|
||||||
|
* - The value is cached
|
||||||
|
* - The lifetime is respected
|
||||||
|
* - The returned value type is as expected
|
||||||
|
* - The default not-found value is respected
|
||||||
|
*
|
||||||
|
* This test doesn't test the TTL as there is a known bug/feature
|
||||||
|
* in APC that prevents the same request from killing cache on timeout.
|
||||||
|
*
|
||||||
|
* @link http://pecl.php.net/bugs/bug.php?id=16814
|
||||||
|
*
|
||||||
|
* @dataProvider provider_set_get
|
||||||
|
*
|
||||||
|
* @param array data
|
||||||
|
* @param mixed expected
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function test_set_get(array $data, $expected)
|
||||||
|
{
|
||||||
|
if ($data['wait'] !== FALSE)
|
||||||
|
{
|
||||||
|
$this->markTestSkipped('Unable to perform TTL test in CLI, see: '.
|
||||||
|
'http://pecl.php.net/bugs/bug.php?id=16814 for more info!');
|
||||||
|
}
|
||||||
|
|
||||||
|
parent::test_set_get($data, $expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // End Kohana_ApcTest
|
173
modules/cache/tests/cache/arithmetic/CacheArithmeticMethods.php
vendored
Normal file
173
modules/cache/tests/cache/arithmetic/CacheArithmeticMethods.php
vendored
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
<?php
|
||||||
|
include_once(Kohana::find_file('tests/cache', 'CacheBasicMethodsTest'));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @package Kohana/Cache/Memcache
|
||||||
|
* @group kohana
|
||||||
|
* @group kohana.cache
|
||||||
|
* @category Test
|
||||||
|
* @author Kohana Team
|
||||||
|
* @copyright (c) 2009-2012 Kohana Team
|
||||||
|
* @license http://kohanaphp.com/license
|
||||||
|
*/
|
||||||
|
abstract class Kohana_CacheArithmeticMethodsTest extends Kohana_CacheBasicMethodsTest {
|
||||||
|
|
||||||
|
public function tearDown()
|
||||||
|
{
|
||||||
|
parent::tearDown();
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
$cache = $this->cache();
|
||||||
|
|
||||||
|
if ($cache instanceof Cache)
|
||||||
|
{
|
||||||
|
$cache->delete_all();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provider for test_increment
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function provider_increment()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
array(
|
||||||
|
0,
|
||||||
|
array(
|
||||||
|
'id' => 'increment_test_1',
|
||||||
|
'step' => 1
|
||||||
|
),
|
||||||
|
1
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
1,
|
||||||
|
array(
|
||||||
|
'id' => 'increment_test_2',
|
||||||
|
'step' => 1
|
||||||
|
),
|
||||||
|
2
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
5,
|
||||||
|
array(
|
||||||
|
'id' => 'increment_test_3',
|
||||||
|
'step' => 5
|
||||||
|
),
|
||||||
|
10
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
NULL,
|
||||||
|
array(
|
||||||
|
'id' => 'increment_test_4',
|
||||||
|
'step' => 1
|
||||||
|
),
|
||||||
|
FALSE
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test for [Cache_Arithmetic::increment()]
|
||||||
|
*
|
||||||
|
* @dataProvider provider_increment
|
||||||
|
*
|
||||||
|
* @param integer start state
|
||||||
|
* @param array increment arguments
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function test_increment(
|
||||||
|
$start_state = NULL,
|
||||||
|
array $inc_args,
|
||||||
|
$expected)
|
||||||
|
{
|
||||||
|
$cache = $this->cache();
|
||||||
|
|
||||||
|
if ($start_state !== NULL)
|
||||||
|
{
|
||||||
|
$cache->set($inc_args['id'], $start_state, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->assertSame(
|
||||||
|
$expected,
|
||||||
|
$cache->increment(
|
||||||
|
$inc_args['id'],
|
||||||
|
$inc_args['step']
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provider for test_decrement
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function provider_decrement()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
array(
|
||||||
|
10,
|
||||||
|
array(
|
||||||
|
'id' => 'decrement_test_1',
|
||||||
|
'step' => 1
|
||||||
|
),
|
||||||
|
9
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
10,
|
||||||
|
array(
|
||||||
|
'id' => 'decrement_test_2',
|
||||||
|
'step' => 2
|
||||||
|
),
|
||||||
|
8
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
50,
|
||||||
|
array(
|
||||||
|
'id' => 'decrement_test_3',
|
||||||
|
'step' => 5
|
||||||
|
),
|
||||||
|
45
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
NULL,
|
||||||
|
array(
|
||||||
|
'id' => 'decrement_test_4',
|
||||||
|
'step' => 1
|
||||||
|
),
|
||||||
|
FALSE
|
||||||
|
),
|
||||||
|
); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test for [Cache_Arithmetic::decrement()]
|
||||||
|
*
|
||||||
|
* @dataProvider provider_decrement
|
||||||
|
*
|
||||||
|
* @param integer start state
|
||||||
|
* @param array decrement arguments
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function test_decrement(
|
||||||
|
$start_state = NULL,
|
||||||
|
array $dec_args,
|
||||||
|
$expected)
|
||||||
|
{
|
||||||
|
$cache = $this->cache();
|
||||||
|
|
||||||
|
if ($start_state !== NULL)
|
||||||
|
{
|
||||||
|
$cache->set($dec_args['id'], $start_state, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->assertSame(
|
||||||
|
$expected,
|
||||||
|
$cache->decrement(
|
||||||
|
$dec_args['id'],
|
||||||
|
$dec_args['step']
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // End Kohana_CacheArithmeticMethodsTest
|
103
modules/cache/tests/cache/arithmetic/MemcacheTest.php
vendored
Normal file
103
modules/cache/tests/cache/arithmetic/MemcacheTest.php
vendored
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
<?php
|
||||||
|
include_once(Kohana::find_file('tests/cache/arithmetic', 'CacheArithmeticMethods'));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @package Kohana/Cache/Memcache
|
||||||
|
* @group kohana
|
||||||
|
* @group kohana.cache
|
||||||
|
* @category Test
|
||||||
|
* @author Kohana Team
|
||||||
|
* @copyright (c) 2009-2012 Kohana Team
|
||||||
|
* @license http://kohanaphp.com/license
|
||||||
|
*/
|
||||||
|
class Kohana_CacheArithmeticMemcacheTest extends Kohana_CacheArithmeticMethodsTest {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method MUST be implemented by each driver to setup the `Cache`
|
||||||
|
* instance for each test.
|
||||||
|
*
|
||||||
|
* This method should do the following tasks for each driver test:
|
||||||
|
*
|
||||||
|
* - Test the Cache instance driver is available, skip test otherwise
|
||||||
|
* - Setup the Cache instance
|
||||||
|
* - Call the parent setup method, `parent::setUp()`
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
if ( ! extension_loaded('memcache'))
|
||||||
|
{
|
||||||
|
$this->markTestSkipped('Memcache PHP Extension is not available');
|
||||||
|
}
|
||||||
|
if ( ! $config = Kohana::$config->load('cache.memcache'))
|
||||||
|
{
|
||||||
|
$this->markTestSkipped('Unable to load Memcache configuration');
|
||||||
|
}
|
||||||
|
|
||||||
|
$memcache = new Memcache;
|
||||||
|
if ( ! $memcache->connect($config['servers']['local']['host'],
|
||||||
|
$config['servers']['local']['port']))
|
||||||
|
{
|
||||||
|
$this->markTestSkipped('Unable to connect to memcache server @ '.
|
||||||
|
$config['servers']['local']['host'].':'.
|
||||||
|
$config['servers']['local']['port']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($memcache->getVersion() === FALSE)
|
||||||
|
{
|
||||||
|
$this->markTestSkipped('Memcache server @ '.
|
||||||
|
$config['servers']['local']['host'].':'.
|
||||||
|
$config['servers']['local']['port'].
|
||||||
|
' not responding!');
|
||||||
|
}
|
||||||
|
|
||||||
|
unset($memcache);
|
||||||
|
|
||||||
|
$this->cache(Cache::instance('memcache'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that multiple values set with Memcache do not cause unexpected
|
||||||
|
* results. For accurate results, this should be run with a memcache
|
||||||
|
* configuration that includes multiple servers.
|
||||||
|
*
|
||||||
|
* This is to test #4110
|
||||||
|
*
|
||||||
|
* @link http://dev.kohanaframework.org/issues/4110
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function test_multiple_set()
|
||||||
|
{
|
||||||
|
$cache = $this->cache();
|
||||||
|
$id_set = 'set_id';
|
||||||
|
$ttl = 300;
|
||||||
|
|
||||||
|
$data = array(
|
||||||
|
'foobar',
|
||||||
|
0,
|
||||||
|
1.0,
|
||||||
|
new stdClass,
|
||||||
|
array('foo', 'bar' => 1),
|
||||||
|
TRUE,
|
||||||
|
NULL,
|
||||||
|
FALSE
|
||||||
|
);
|
||||||
|
|
||||||
|
$previous_set = $cache->get($id_set, NULL);
|
||||||
|
|
||||||
|
foreach ($data as $value)
|
||||||
|
{
|
||||||
|
// Use Equals over Sames as Objects will not be equal
|
||||||
|
$this->assertEquals($previous_set, $cache->get($id_set, NULL));
|
||||||
|
$cache->set($id_set, $value, $ttl);
|
||||||
|
|
||||||
|
$previous_set = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // End Kohana_CacheArithmeticMemcacheTest
|
265
modules/cache/tests/cache/request/client/CacheTest.php
vendored
Normal file
265
modules/cache/tests/cache/request/client/CacheTest.php
vendored
Normal file
@ -0,0 +1,265 @@
|
|||||||
|
<?php defined('SYSPATH') OR die('Kohana bootstrap needs to be included before tests run');
|
||||||
|
/**
|
||||||
|
* Unit tests for request client cache logic
|
||||||
|
*
|
||||||
|
* @group kohana
|
||||||
|
* @group kohana.request
|
||||||
|
* @group kohana.request.client
|
||||||
|
* @group kohana.request.client.cache
|
||||||
|
*
|
||||||
|
* @package Kohana
|
||||||
|
* @category Tests
|
||||||
|
* @author Kohana Team
|
||||||
|
* @copyright (c) 2008-2012 Kohana Team
|
||||||
|
* @license http://kohanaframework.org/license
|
||||||
|
*/
|
||||||
|
class Kohana_Request_Client_CacheTest extends Unittest_TestCase {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets up a test route for caching
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
Route::set('welcome', 'welcome/index')
|
||||||
|
->defaults(array(
|
||||||
|
'controller' => 'welcome',
|
||||||
|
'action' => 'index'
|
||||||
|
));
|
||||||
|
|
||||||
|
parent::setUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the Client does not attempt to load cache if no Cache library
|
||||||
|
* is present
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function test_cache_not_called_with_no_cache()
|
||||||
|
{
|
||||||
|
$request = new Request('welcome/index');
|
||||||
|
$response = new Response;
|
||||||
|
|
||||||
|
$client_mock = $this->getMock('Request_Client_Internal');
|
||||||
|
|
||||||
|
$request->client($client_mock);
|
||||||
|
$client_mock->expects($this->exactly(0))
|
||||||
|
->method('execute_request');
|
||||||
|
$client_mock->expects($this->once())
|
||||||
|
->method('execute')
|
||||||
|
->will($this->returnValue($response));
|
||||||
|
|
||||||
|
$this->assertSame($response, $request->execute());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that the client attempts to load a cached response from the
|
||||||
|
* cache library, but fails.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function test_cache_miss()
|
||||||
|
{
|
||||||
|
$route = new Route('welcome/index');
|
||||||
|
$route->defaults(array(
|
||||||
|
'controller' => 'Kohana_Request_CacheTest_Dummy',
|
||||||
|
'action' => 'index',
|
||||||
|
));
|
||||||
|
|
||||||
|
$request = new Request('welcome/index', NULL, array($route));
|
||||||
|
$cache_mock = $this->_get_cache_mock();
|
||||||
|
|
||||||
|
$request->client()->cache(HTTP_Cache::factory($cache_mock));
|
||||||
|
|
||||||
|
$cache_mock->expects($this->once())
|
||||||
|
->method('get')
|
||||||
|
->with($request->client()->cache()->create_cache_key($request))
|
||||||
|
->will($this->returnValue(FALSE));
|
||||||
|
|
||||||
|
$response = $request->client()->execute($request);
|
||||||
|
|
||||||
|
$this->assertSame(HTTP_Cache::CACHE_STATUS_MISS,
|
||||||
|
$response->headers(HTTP_Cache::CACHE_STATUS_KEY));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the client saves a response if the correct headers are set
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function test_cache_save()
|
||||||
|
{
|
||||||
|
$lifetime = 800;
|
||||||
|
$request = new Request('welcome/index');
|
||||||
|
$cache_mock = $this->_get_cache_mock();
|
||||||
|
$response = Response::factory();
|
||||||
|
|
||||||
|
$request->client()->cache(new HTTP_Cache(array(
|
||||||
|
'cache' => $cache_mock
|
||||||
|
)
|
||||||
|
));
|
||||||
|
|
||||||
|
$response->headers('cache-control', 'max-age='.$lifetime);
|
||||||
|
|
||||||
|
$key = $request->client()->cache()->create_cache_key($request);
|
||||||
|
|
||||||
|
$cache_mock->expects($this->at(0))
|
||||||
|
->method('set')
|
||||||
|
->with($this->stringEndsWith($key), $this->identicalTo(0));
|
||||||
|
|
||||||
|
$cache_mock->expects($this->at(1))
|
||||||
|
->method('set')
|
||||||
|
->with($this->identicalTo($key), $this->anything(), $this->identicalTo($lifetime))
|
||||||
|
->will($this->returnValue(TRUE));
|
||||||
|
|
||||||
|
$this->assertTrue(
|
||||||
|
$request->client()->cache()
|
||||||
|
->cache_response($key, $request, $response)
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertSame(HTTP_Cache::CACHE_STATUS_SAVED,
|
||||||
|
$response->headers(HTTP_Cache::CACHE_STATUS_KEY));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the client handles a cache HIT event correctly
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function test_cache_hit()
|
||||||
|
{
|
||||||
|
$lifetime = 800;
|
||||||
|
$request = new Request('welcome/index');
|
||||||
|
$cache_mock = $this->_get_cache_mock();
|
||||||
|
|
||||||
|
$request->client()->cache(new HTTP_Cache(array(
|
||||||
|
'cache' => $cache_mock
|
||||||
|
)
|
||||||
|
));
|
||||||
|
|
||||||
|
$response = Response::factory();
|
||||||
|
|
||||||
|
$response->headers(array(
|
||||||
|
'cache-control' => 'max-age='.$lifetime,
|
||||||
|
HTTP_Cache::CACHE_STATUS_KEY =>
|
||||||
|
HTTP_Cache::CACHE_STATUS_HIT
|
||||||
|
));
|
||||||
|
|
||||||
|
$key = $request->client()->cache()->create_cache_key($request);
|
||||||
|
|
||||||
|
$cache_mock->expects($this->exactly(2))
|
||||||
|
->method('get')
|
||||||
|
->with($this->stringContains($key))
|
||||||
|
->will($this->returnValue($response));
|
||||||
|
|
||||||
|
$request->client()->cache()->cache_response($key, $request);
|
||||||
|
|
||||||
|
$this->assertSame(HTTP_Cache::CACHE_STATUS_HIT,
|
||||||
|
$response->headers(HTTP_Cache::CACHE_STATUS_KEY));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data provider for test_set_cache
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function provider_set_cache()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
array(
|
||||||
|
new HTTP_Header(array('cache-control' => 'no-cache')),
|
||||||
|
array('no-cache' => NULL),
|
||||||
|
FALSE,
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
new HTTP_Header(array('cache-control' => 'no-store')),
|
||||||
|
array('no-store' => NULL),
|
||||||
|
FALSE,
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
new HTTP_Header(array('cache-control' => 'max-age=100')),
|
||||||
|
array('max-age' => '100'),
|
||||||
|
TRUE
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
new HTTP_Header(array('cache-control' => 'private')),
|
||||||
|
array('private' => NULL),
|
||||||
|
FALSE
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
new HTTP_Header(array('cache-control' => 'private, max-age=100')),
|
||||||
|
array('private' => NULL, 'max-age' => '100'),
|
||||||
|
FALSE
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
new HTTP_Header(array('cache-control' => 'private, s-maxage=100')),
|
||||||
|
array('private' => NULL, 's-maxage' => '100'),
|
||||||
|
TRUE
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
new HTTP_Header(array(
|
||||||
|
'expires' => date('m/d/Y', strtotime('-1 day')),
|
||||||
|
)),
|
||||||
|
array(),
|
||||||
|
FALSE
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
new HTTP_Header(array(
|
||||||
|
'expires' => date('m/d/Y', strtotime('+1 day')),
|
||||||
|
)),
|
||||||
|
array(),
|
||||||
|
TRUE
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
new HTTP_Header(array()),
|
||||||
|
array(),
|
||||||
|
TRUE
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the set_cache() method
|
||||||
|
*
|
||||||
|
* @test
|
||||||
|
* @dataProvider provider_set_cache
|
||||||
|
*
|
||||||
|
* @return null
|
||||||
|
*/
|
||||||
|
public function test_set_cache($headers, $cache_control, $expected)
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Set up a mock response object to test with
|
||||||
|
*/
|
||||||
|
$response = $this->getMock('Response');
|
||||||
|
|
||||||
|
$response->expects($this->any())
|
||||||
|
->method('headers')
|
||||||
|
->will($this->returnValue($headers));
|
||||||
|
|
||||||
|
$request = new Request_Client_Internal;
|
||||||
|
$request->cache(new HTTP_Cache);
|
||||||
|
$this->assertEquals($request->cache()->set_cache($response), $expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a mock object for Cache
|
||||||
|
*
|
||||||
|
* @return Cache
|
||||||
|
*/
|
||||||
|
protected function _get_cache_mock()
|
||||||
|
{
|
||||||
|
return $this->getMock('Cache_File', array(), array(), '', FALSE);
|
||||||
|
}
|
||||||
|
} // End Kohana_Request_Client_CacheTest
|
||||||
|
|
||||||
|
class Controller_Kohana_Request_CacheTest_Dummy extends Controller
|
||||||
|
{
|
||||||
|
public function action_index()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
19
modules/cache/tests/phpunit.xml
vendored
Normal file
19
modules/cache/tests/phpunit.xml
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<!--
|
||||||
|
This is an example phpunit.xml file to get you started
|
||||||
|
Copy it to a directory, update the relative paths and rename to phpunit.xml
|
||||||
|
Then to run tests cd into it's directory and just run
|
||||||
|
phpunit
|
||||||
|
(it'll automatically use any phpunit.xml file in the current directory)
|
||||||
|
|
||||||
|
Any options you specify when calling phpunit will override the ones in here
|
||||||
|
-->
|
||||||
|
<php>
|
||||||
|
<includePath>./cache</includePath>
|
||||||
|
</php>
|
||||||
|
<phpunit colors="true" bootstrap="/path/to/your/unittest/bootstrap.php">
|
||||||
|
<testsuites>
|
||||||
|
<testsuite name="Kohana Cache Tests">
|
||||||
|
<directory suffix=".php">cache/</directory>
|
||||||
|
</testsuite>
|
||||||
|
</testsuites>
|
||||||
|
</phpunit>
|
57
modules/codebench/classes/Bench/ArrCallback.php
Normal file
57
modules/codebench/classes/Bench/ArrCallback.php
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
<?php defined('SYSPATH') or die('No direct access allowed.');
|
||||||
|
/**
|
||||||
|
* @package Kohana/Codebench
|
||||||
|
* @category Tests
|
||||||
|
* @author Geert De Deckere <geert@idoe.be>
|
||||||
|
*/
|
||||||
|
class Bench_ArrCallback extends Codebench {
|
||||||
|
|
||||||
|
public $description =
|
||||||
|
'Parsing <em>command[param,param]</em> strings in <code>Arr::callback()</code>:
|
||||||
|
http://github.com/shadowhand/kohana/commit/c3aaae849164bf92a486e29e736a265b350cb4da#L0R127';
|
||||||
|
|
||||||
|
public $loops = 10000;
|
||||||
|
|
||||||
|
public $subjects = array
|
||||||
|
(
|
||||||
|
// Valid callback strings
|
||||||
|
'foo',
|
||||||
|
'foo::bar',
|
||||||
|
'foo[apple,orange]',
|
||||||
|
'foo::bar[apple,orange]',
|
||||||
|
'[apple,orange]', // no command, only params
|
||||||
|
'foo[[apple],[orange]]', // params with brackets inside
|
||||||
|
|
||||||
|
// Invalid callback strings
|
||||||
|
'foo[apple,orange', // no closing bracket
|
||||||
|
);
|
||||||
|
|
||||||
|
public function bench_shadowhand($subject)
|
||||||
|
{
|
||||||
|
// The original regex we're trying to optimize
|
||||||
|
if (preg_match('/([^\[]*+)\[(.*)\]/', $subject, $match))
|
||||||
|
return $match;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function bench_geert_regex_1($subject)
|
||||||
|
{
|
||||||
|
// Added ^ and $ around the whole pattern
|
||||||
|
if (preg_match('/^([^\[]*+)\[(.*)\]$/', $subject, $matches))
|
||||||
|
return $matches;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function bench_geert_regex_2($subject)
|
||||||
|
{
|
||||||
|
// A rather experimental approach using \K which requires PCRE 7.2 ~ PHP 5.2.4
|
||||||
|
// Note: $matches[0] = params, $matches[1] = command
|
||||||
|
if (preg_match('/^([^\[]*+)\[\K.*(?=\]$)/', $subject, $matches))
|
||||||
|
return $matches;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function bench_geert_str($subject)
|
||||||
|
{
|
||||||
|
// A native string function approach which beats all the regexes
|
||||||
|
if (strpos($subject, '[') !== FALSE AND substr($subject, -1) === ']')
|
||||||
|
return explode('[', substr($subject, 0, -1), 2);
|
||||||
|
}
|
||||||
|
}
|
70
modules/codebench/classes/Bench/AutoLinkEmails.php
Normal file
70
modules/codebench/classes/Bench/AutoLinkEmails.php
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
<?php defined('SYSPATH') or die('No direct access allowed.');
|
||||||
|
/**
|
||||||
|
* @package Kohana/Codebench
|
||||||
|
* @category Tests
|
||||||
|
* @author Geert De Deckere <geert@idoe.be>
|
||||||
|
*/
|
||||||
|
class Bench_AutoLinkEmails extends Codebench {
|
||||||
|
|
||||||
|
public $description =
|
||||||
|
'Fixing <a href="http://dev.kohanaphp.com/issues/2772">#2772</a>, and comparing some possibilities.';
|
||||||
|
|
||||||
|
public $loops = 1000;
|
||||||
|
|
||||||
|
public $subjects = array
|
||||||
|
(
|
||||||
|
'<ul>
|
||||||
|
<li>voorzitter@xxxx.com</li>
|
||||||
|
<li>vicevoorzitter@xxxx.com</li>
|
||||||
|
</ul>',
|
||||||
|
);
|
||||||
|
|
||||||
|
// The original function, with str_replace replaced by preg_replace. Looks clean.
|
||||||
|
public function bench_match_all_loop($subject)
|
||||||
|
{
|
||||||
|
if (preg_match_all('~\b(?<!href="mailto:|">|58;)(?!\.)[-+_a-z0-9.]++(?<!\.)@(?![-.])[-a-z0-9.]+(?<!\.)\.[a-z]{2,6}\b~i', $subject, $matches))
|
||||||
|
{
|
||||||
|
foreach ($matches[0] as $match)
|
||||||
|
{
|
||||||
|
$subject = preg_replace('!\b'.preg_quote($match).'\b!', HTML::mailto($match), $subject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $subject;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The "e" stands for "eval", hmm... Ugly and slow because it needs to reinterpret the PHP code upon each match.
|
||||||
|
public function bench_replace_e($subject)
|
||||||
|
{
|
||||||
|
return preg_replace(
|
||||||
|
'~\b(?<!href="mailto:|">|58;)(?!\.)[-+_a-z0-9.]++(?<!\.)@(?![-.])[-a-z0-9.]+(?<!\.)\.[a-z]{2,6}\b~ie',
|
||||||
|
'HTML::mailto("$0")', // Yuck!
|
||||||
|
$subject
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This one should be quite okay, it just requires an otherwise useless single-purpose callback.
|
||||||
|
public function bench_replace_callback_external($subject)
|
||||||
|
{
|
||||||
|
return preg_replace_callback(
|
||||||
|
'~\b(?<!href="mailto:|">|58;)(?!\.)[-+_a-z0-9.]++(?<!\.)@(?![-.])[-a-z0-9.]+(?<!\.)\.[a-z]{2,6}\b~i',
|
||||||
|
array($this, '_callback_external'),
|
||||||
|
$subject
|
||||||
|
);
|
||||||
|
}
|
||||||
|
protected function _callback_external($matches)
|
||||||
|
{
|
||||||
|
return HTML::mailto($matches[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This one clearly is the ugliest, the slowest and consumes a lot of memory!
|
||||||
|
public function bench_replace_callback_internal($subject)
|
||||||
|
{
|
||||||
|
return preg_replace_callback(
|
||||||
|
'~\b(?<!href="mailto:|">|58;)(?!\.)[-+_a-z0-9.]++(?<!\.)@(?![-.])[-a-z0-9.]+(?<!\.)\.[a-z]{2,6}\b~i',
|
||||||
|
create_function('$matches', 'return HTML::mailto($matches[0]);'), // Yuck!
|
||||||
|
$subject
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
186
modules/codebench/classes/Bench/DateSpan.php
Normal file
186
modules/codebench/classes/Bench/DateSpan.php
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
<?php defined('SYSPATH') or die('No direct access allowed.');
|
||||||
|
/**
|
||||||
|
* @package Kohana/Codebench
|
||||||
|
* @category Tests
|
||||||
|
* @author Woody Gilk <woody.gilk@kohanaphp.com>
|
||||||
|
*/
|
||||||
|
class Bench_DateSpan extends Codebench {
|
||||||
|
|
||||||
|
public $description =
|
||||||
|
'Optimization for <code>Date::span()</code>.';
|
||||||
|
|
||||||
|
public $loops = 1000;
|
||||||
|
|
||||||
|
public $subjects = array();
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
|
||||||
|
$this->subjects = array(
|
||||||
|
time(),
|
||||||
|
time() - Date::MONTH,
|
||||||
|
time() - Date::YEAR,
|
||||||
|
time() - Date::YEAR * 10,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Original method
|
||||||
|
public static function bench_span_original($remote, $local = NULL, $output = 'years,months,weeks,days,hours,minutes,seconds')
|
||||||
|
{
|
||||||
|
// Array with the output formats
|
||||||
|
$output = preg_split('/[^a-z]+/', strtolower( (string) $output));
|
||||||
|
|
||||||
|
// Invalid output
|
||||||
|
if (empty($output))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
// Make the output values into keys
|
||||||
|
extract(array_flip($output), EXTR_SKIP);
|
||||||
|
|
||||||
|
if ($local === NULL)
|
||||||
|
{
|
||||||
|
// Calculate the span from the current time
|
||||||
|
$local = time();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate timespan (seconds)
|
||||||
|
$timespan = abs($remote - $local);
|
||||||
|
|
||||||
|
if (isset($years))
|
||||||
|
{
|
||||||
|
$timespan -= Date::YEAR * ($years = (int) floor($timespan / Date::YEAR));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($months))
|
||||||
|
{
|
||||||
|
$timespan -= Date::MONTH * ($months = (int) floor($timespan / Date::MONTH));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($weeks))
|
||||||
|
{
|
||||||
|
$timespan -= Date::WEEK * ($weeks = (int) floor($timespan / Date::WEEK));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($days))
|
||||||
|
{
|
||||||
|
$timespan -= Date::DAY * ($days = (int) floor($timespan / Date::DAY));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($hours))
|
||||||
|
{
|
||||||
|
$timespan -= Date::HOUR * ($hours = (int) floor($timespan / Date::HOUR));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($minutes))
|
||||||
|
{
|
||||||
|
$timespan -= Date::MINUTE * ($minutes = (int) floor($timespan / Date::MINUTE));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Seconds ago, 1
|
||||||
|
if (isset($seconds))
|
||||||
|
{
|
||||||
|
$seconds = $timespan;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the variables that cannot be accessed
|
||||||
|
unset($timespan, $remote, $local);
|
||||||
|
|
||||||
|
// Deny access to these variables
|
||||||
|
$deny = array_flip(array('deny', 'key', 'difference', 'output'));
|
||||||
|
|
||||||
|
// Return the difference
|
||||||
|
$difference = array();
|
||||||
|
foreach ($output as $key)
|
||||||
|
{
|
||||||
|
if (isset($$key) AND ! isset($deny[$key]))
|
||||||
|
{
|
||||||
|
// Add requested key to the output
|
||||||
|
$difference[$key] = $$key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invalid output formats string
|
||||||
|
if (empty($difference))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
// If only one output format was asked, don't put it in an array
|
||||||
|
if (count($difference) === 1)
|
||||||
|
return current($difference);
|
||||||
|
|
||||||
|
// Return array
|
||||||
|
return $difference;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Using an array for the output
|
||||||
|
public static function bench_span_use_array($remote, $local = NULL, $output = 'years,months,weeks,days,hours,minutes,seconds')
|
||||||
|
{
|
||||||
|
// Array with the output formats
|
||||||
|
$output = preg_split('/[^a-z]+/', strtolower( (string) $output));
|
||||||
|
|
||||||
|
// Invalid output
|
||||||
|
if (empty($output))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
// Convert the list of outputs to an associative array
|
||||||
|
$output = array_combine($output, array_fill(0, count($output), 0));
|
||||||
|
|
||||||
|
// Make the output values into keys
|
||||||
|
extract(array_flip($output), EXTR_SKIP);
|
||||||
|
|
||||||
|
if ($local === NULL)
|
||||||
|
{
|
||||||
|
// Calculate the span from the current time
|
||||||
|
$local = time();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate timespan (seconds)
|
||||||
|
$timespan = abs($remote - $local);
|
||||||
|
|
||||||
|
if (isset($output['years']))
|
||||||
|
{
|
||||||
|
$timespan -= Date::YEAR * ($output['years'] = (int) floor($timespan / Date::YEAR));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($output['months']))
|
||||||
|
{
|
||||||
|
$timespan -= Date::MONTH * ($output['months'] = (int) floor($timespan / Date::MONTH));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($output['weeks']))
|
||||||
|
{
|
||||||
|
$timespan -= Date::WEEK * ($output['weeks'] = (int) floor($timespan / Date::WEEK));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($output['days']))
|
||||||
|
{
|
||||||
|
$timespan -= Date::DAY * ($output['days'] = (int) floor($timespan / Date::DAY));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($output['hours']))
|
||||||
|
{
|
||||||
|
$timespan -= Date::HOUR * ($output['hours'] = (int) floor($timespan / Date::HOUR));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($output['minutes']))
|
||||||
|
{
|
||||||
|
$timespan -= Date::MINUTE * ($output['minutes'] = (int) floor($timespan / Date::MINUTE));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Seconds ago, 1
|
||||||
|
if (isset($output['seconds']))
|
||||||
|
{
|
||||||
|
$output['seconds'] = $timespan;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count($output) === 1)
|
||||||
|
{
|
||||||
|
// Only a single output was requested, return it
|
||||||
|
return array_pop($output);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return array
|
||||||
|
return $output;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
34
modules/codebench/classes/Bench/ExplodeLimit.php
Normal file
34
modules/codebench/classes/Bench/ExplodeLimit.php
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<?php defined('SYSPATH') or die('No direct script access.');
|
||||||
|
/**
|
||||||
|
* @package Kohana/Codebench
|
||||||
|
* @category Tests
|
||||||
|
* @author Geert De Deckere <geert@idoe.be>
|
||||||
|
*/
|
||||||
|
class Bench_ExplodeLimit extends Codebench {
|
||||||
|
|
||||||
|
public $description =
|
||||||
|
'Having a look at the effect of adding a limit to the <a href="http://php.net/explode">explode</a> function.<br />
|
||||||
|
http://stackoverflow.com/questions/1308149/how-to-get-a-part-of-url-between-4th-and-5th-slashes';
|
||||||
|
|
||||||
|
public $loops = 10000;
|
||||||
|
|
||||||
|
public $subjects = array
|
||||||
|
(
|
||||||
|
'http://example.com/articles/123a/view',
|
||||||
|
'http://example.com/articles/123a/view/x/x/x/x/x',
|
||||||
|
'http://example.com/articles/123a/view/x/x/x/x/x/x/x/x/x/x/x/x/x/x/x/x/x/x',
|
||||||
|
);
|
||||||
|
|
||||||
|
public function bench_explode_without_limit($subject)
|
||||||
|
{
|
||||||
|
$parts = explode('/', $subject);
|
||||||
|
return $parts[4];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function bench_explode_with_limit($subject)
|
||||||
|
{
|
||||||
|
$parts = explode('/', $subject, 6);
|
||||||
|
return $parts[4];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
61
modules/codebench/classes/Bench/GruberURL.php
Normal file
61
modules/codebench/classes/Bench/GruberURL.php
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
<?php defined('SYSPATH') or die('No direct access allowed.');
|
||||||
|
/**
|
||||||
|
* @package Kohana/Codebench
|
||||||
|
* @category Tests
|
||||||
|
* @author Geert De Deckere <geert@idoe.be>
|
||||||
|
*/
|
||||||
|
class Bench_GruberURL extends Codebench {
|
||||||
|
|
||||||
|
public $description =
|
||||||
|
'Optimization for http://daringfireball.net/2009/11/liberal_regex_for_matching_urls';
|
||||||
|
|
||||||
|
public $loops = 10000;
|
||||||
|
|
||||||
|
public $subjects = array
|
||||||
|
(
|
||||||
|
'http://foo.com/blah_blah',
|
||||||
|
'http://foo.com/blah_blah/',
|
||||||
|
'(Something like http://foo.com/blah_blah)',
|
||||||
|
'http://foo.com/blah_blah_(wikipedia)',
|
||||||
|
'(Something like http://foo.com/blah_blah_(wikipedia))',
|
||||||
|
'http://foo.com/blah_blah.',
|
||||||
|
'http://foo.com/blah_blah/.',
|
||||||
|
'<http://foo.com/blah_blah>',
|
||||||
|
'<http://foo.com/blah_blah/>',
|
||||||
|
'http://foo.com/blah_blah,',
|
||||||
|
'http://www.example.com/wpstyle/?p=364.',
|
||||||
|
'http://✪df.ws/e7l',
|
||||||
|
'rdar://1234',
|
||||||
|
'rdar:/1234',
|
||||||
|
'x-yojimbo-item://6303E4C1-xxxx-45A6-AB9D-3A908F59AE0E',
|
||||||
|
'message://%3c330e7f8409726r6a4ba78dkf1fd71420c1bf6ff@mail.gmail.com%3e',
|
||||||
|
'http://➡.ws/䨹',
|
||||||
|
'www.➡.ws/䨹',
|
||||||
|
'<tag>http://example.com</tag>',
|
||||||
|
'Just a www.example.com link.',
|
||||||
|
// To test the use of possessive quatifiers:
|
||||||
|
'httpppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp',
|
||||||
|
);
|
||||||
|
|
||||||
|
public function bench_daringfireball($subject)
|
||||||
|
{
|
||||||
|
// Original regex by John Gruber
|
||||||
|
preg_match('~\b(([\w-]+://?|www[.])[^\s()<>]+(?:\([\w\d]+\)|([^[:punct:]\s]|/)))~', $subject, $matches);
|
||||||
|
return (empty($matches)) ? FALSE : $matches[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function bench_daringfireball_v2($subject)
|
||||||
|
{
|
||||||
|
// Removed outer capturing parentheses, made another pair non-capturing
|
||||||
|
preg_match('~\b(?:[\w-]+://?|www[.])[^\s()<>]+(?:\([\w\d]+\)|(?:[^[:punct:]\s]|/))~', $subject, $matches);
|
||||||
|
return (empty($matches)) ? FALSE : $matches[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function bench_daringfireball_v3($subject)
|
||||||
|
{
|
||||||
|
// Made quantifiers possessive where possible
|
||||||
|
preg_match('~\b(?:[\w-]++://?+|www[.])[^\s()<>]+(?:\([\w\d]++\)|(?:[^[:punct:]\s]|/))~', $subject, $matches);
|
||||||
|
return (empty($matches)) ? FALSE : $matches[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
28
modules/codebench/classes/Bench/LtrimDigits.php
Normal file
28
modules/codebench/classes/Bench/LtrimDigits.php
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<?php defined('SYSPATH') or die('No direct access allowed.');
|
||||||
|
/**
|
||||||
|
* @package Kohana/Codebench
|
||||||
|
* @category Tests
|
||||||
|
* @author Geert De Deckere <geert@idoe.be>
|
||||||
|
*/
|
||||||
|
class Bench_LtrimDigits extends Codebench {
|
||||||
|
|
||||||
|
public $description = 'Chopping off leading digits: regex vs ltrim.';
|
||||||
|
|
||||||
|
public $loops = 100000;
|
||||||
|
|
||||||
|
public $subjects = array
|
||||||
|
(
|
||||||
|
'123digits',
|
||||||
|
'no-digits',
|
||||||
|
);
|
||||||
|
|
||||||
|
public function bench_regex($subject)
|
||||||
|
{
|
||||||
|
return preg_replace('/^\d+/', '', $subject);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function bench_ltrim($subject)
|
||||||
|
{
|
||||||
|
return ltrim($subject, '0..9');
|
||||||
|
}
|
||||||
|
}
|
66
modules/codebench/classes/Bench/MDDoBaseURL.php
Normal file
66
modules/codebench/classes/Bench/MDDoBaseURL.php
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
<?php defined('SYSPATH') or die('No direct script access.');
|
||||||
|
/**
|
||||||
|
* @package Kohana/Codebench
|
||||||
|
* @category Tests
|
||||||
|
* @author Geert De Deckere <geert@idoe.be>
|
||||||
|
*/
|
||||||
|
class Bench_MDDoBaseURL extends Codebench {
|
||||||
|
|
||||||
|
public $description =
|
||||||
|
'Optimization for the <code>doBaseURL()</code> method of <code>Kohana_Kodoc_Markdown</code>
|
||||||
|
for the Kohana Userguide.';
|
||||||
|
|
||||||
|
public $loops = 10000;
|
||||||
|
|
||||||
|
public $subjects = array
|
||||||
|
(
|
||||||
|
// Valid matches
|
||||||
|
'[filesystem](about.filesystem)',
|
||||||
|
'[filesystem](about.filesystem "Optional title")',
|
||||||
|
'[same page link](#id)',
|
||||||
|
'[object oriented](http://wikipedia.org/wiki/Object-Oriented_Programming)',
|
||||||
|
|
||||||
|
// Invalid matches
|
||||||
|
'![this is image syntax](about.filesystem)',
|
||||||
|
'[filesystem](about.filesystem',
|
||||||
|
);
|
||||||
|
|
||||||
|
public function bench_original($subject)
|
||||||
|
{
|
||||||
|
// The original regex contained a bug, which is fixed here for benchmarking purposes.
|
||||||
|
// At the very start of the regex, (?!!) has been replace by (?<!!)
|
||||||
|
return preg_replace_callback('~(?<!!)\[(.+?)\]\(([^#]\S*(?:\s*".+?")?)\)~', array($this, '_add_base_url_original'), $subject);
|
||||||
|
}
|
||||||
|
public function _add_base_url_original($matches)
|
||||||
|
{
|
||||||
|
if ($matches[2] AND strpos($matches[2], '://') === FALSE)
|
||||||
|
{
|
||||||
|
// Add the base url to the link URL
|
||||||
|
$matches[2] = 'http://BASE/'.$matches[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recreate the link
|
||||||
|
return "[{$matches[1]}]({$matches[2]})";
|
||||||
|
}
|
||||||
|
|
||||||
|
public function bench_optimized_callback($subject)
|
||||||
|
{
|
||||||
|
return preg_replace_callback('~(?<!!)\[(.+?)\]\((?!\w++://)([^#]\S*(?:\s*+".+?")?)\)~', array($this, '_add_base_url_optimized'), $subject);
|
||||||
|
}
|
||||||
|
public function _add_base_url_optimized($matches)
|
||||||
|
{
|
||||||
|
// Add the base url to the link URL
|
||||||
|
$matches[2] = 'http://BASE/'.$matches[2];
|
||||||
|
|
||||||
|
// Recreate the link
|
||||||
|
return "[{$matches[1]}]({$matches[2]})";
|
||||||
|
}
|
||||||
|
|
||||||
|
public function bench_callback_gone($subject)
|
||||||
|
{
|
||||||
|
// All the optimized callback was doing now, is prepend some text to the URL.
|
||||||
|
// We don't need a callback for that, and that should be clearly faster.
|
||||||
|
return preg_replace('~(?<!!)(\[.+?\]\()(?!\w++://)([^#]\S*(?:\s*+".+?")?\))~', '$1http://BASE/$2', $subject);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
66
modules/codebench/classes/Bench/MDDoImageURL.php
Normal file
66
modules/codebench/classes/Bench/MDDoImageURL.php
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
<?php defined('SYSPATH') or die('No direct script access.');
|
||||||
|
/**
|
||||||
|
* @package Kohana/Codebench
|
||||||
|
* @category Tests
|
||||||
|
* @author Geert De Deckere <geert@idoe.be>
|
||||||
|
*/
|
||||||
|
class Bench_MDDoImageURL extends Codebench {
|
||||||
|
|
||||||
|
public $description =
|
||||||
|
'Optimization for the <code>doImageURL()</code> method of <code>Kohana_Kodoc_Markdown</code>
|
||||||
|
for the Kohana Userguide.';
|
||||||
|
|
||||||
|
public $loops = 10000;
|
||||||
|
|
||||||
|
public $subjects = array
|
||||||
|
(
|
||||||
|
// Valid matches
|
||||||
|
'![Alt text](http://img.skitch.com/20091019-rud5mmqbf776jwua6hx9nm1n.png)',
|
||||||
|
'![Alt text](https://img.skitch.com/20091019-rud5mmqbf776jwua6hx9nm1n.png)',
|
||||||
|
'![Alt text](otherprotocol://image.png "Optional title")',
|
||||||
|
'![Alt text](img/install.png "Optional title")',
|
||||||
|
'![Alt text containing [square] brackets](img/install.png)',
|
||||||
|
'![Empty src]()',
|
||||||
|
|
||||||
|
// Invalid matches
|
||||||
|
'![Alt text](img/install.png "No closing parenthesis"',
|
||||||
|
);
|
||||||
|
|
||||||
|
public function bench_original($subject)
|
||||||
|
{
|
||||||
|
return preg_replace_callback('~!\[(.+?)\]\((\S*(?:\s*".+?")?)\)~', array($this, '_add_image_url_original'), $subject);
|
||||||
|
}
|
||||||
|
protected function _add_image_url_original($matches)
|
||||||
|
{
|
||||||
|
if ($matches[2] AND strpos($matches[2], '://') === FALSE)
|
||||||
|
{
|
||||||
|
// Add the base url to the link URL
|
||||||
|
$matches[2] = 'http://BASE/'.$matches[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recreate the link
|
||||||
|
return "![{$matches[1]}]({$matches[2]})";
|
||||||
|
}
|
||||||
|
|
||||||
|
public function bench_optimized_callback($subject)
|
||||||
|
{
|
||||||
|
// Moved the check for "://" to the regex, simplifying the callback function
|
||||||
|
return preg_replace_callback('~!\[(.+?)\]\((?!\w++://)(\S*(?:\s*+".+?")?)\)~', array($this, '_add_image_url_optimized'), $subject);
|
||||||
|
}
|
||||||
|
protected function _add_image_url_optimized($matches)
|
||||||
|
{
|
||||||
|
// Add the base url to the link URL
|
||||||
|
$matches[2] = 'http://BASE/'.$matches[2];
|
||||||
|
|
||||||
|
// Recreate the link
|
||||||
|
return "![{$matches[1]}]({$matches[2]})";
|
||||||
|
}
|
||||||
|
|
||||||
|
public function bench_callback_gone($subject)
|
||||||
|
{
|
||||||
|
// All the optimized callback was doing now, is prepend some text to the URL.
|
||||||
|
// We don't need a callback for that, and that should be clearly faster.
|
||||||
|
return preg_replace('~(!\[.+?\]\()(?!\w++://)(\S*(?:\s*+".+?")?\))~', '$1http://BASE/$2', $subject);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
50
modules/codebench/classes/Bench/MDDoIncludeViews.php
Normal file
50
modules/codebench/classes/Bench/MDDoIncludeViews.php
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
<?php defined('SYSPATH') or die('No direct script access.');
|
||||||
|
/**
|
||||||
|
* @package Kohana/Codebench
|
||||||
|
* @category Tests
|
||||||
|
* @author Geert De Deckere <geert@idoe.be>
|
||||||
|
*/
|
||||||
|
class Bench_MDDoIncludeViews extends Codebench {
|
||||||
|
|
||||||
|
public $description =
|
||||||
|
'Optimization for the <code>doIncludeViews()</code> method of <code>Kohana_Kodoc_Markdown</code>
|
||||||
|
for the Kohana Userguide.';
|
||||||
|
|
||||||
|
public $loops = 10000;
|
||||||
|
|
||||||
|
public $subjects = array
|
||||||
|
(
|
||||||
|
// Valid matches
|
||||||
|
'{{one}} two {{three}}',
|
||||||
|
'{{userguide/examples/hello_world_error}}',
|
||||||
|
|
||||||
|
// Invalid matches
|
||||||
|
'{}',
|
||||||
|
'{{}}',
|
||||||
|
'{{userguide/examples/hello_world_error}',
|
||||||
|
'{{userguide/examples/hello_world_error }}',
|
||||||
|
'{{userguide/examples/{{hello_world_error }}',
|
||||||
|
);
|
||||||
|
|
||||||
|
public function bench_original($subject)
|
||||||
|
{
|
||||||
|
preg_match_all('/{{(\S+?)}}/m', $subject, $matches, PREG_SET_ORDER);
|
||||||
|
return $matches;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function bench_possessive($subject)
|
||||||
|
{
|
||||||
|
// Using a possessive character class
|
||||||
|
// Removed useless /m modifier
|
||||||
|
preg_match_all('/{{([^\s{}]++)}}/', $subject, $matches, PREG_SET_ORDER);
|
||||||
|
return $matches;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function bench_lookaround($subject)
|
||||||
|
{
|
||||||
|
// Using lookaround to move $mathes[1] into $matches[0]
|
||||||
|
preg_match_all('/(?<={{)[^\s{}]++(?=}})/', $subject, $matches, PREG_SET_ORDER);
|
||||||
|
return $matches;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
37
modules/codebench/classes/Bench/StripNullBytes.php
Normal file
37
modules/codebench/classes/Bench/StripNullBytes.php
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<?php defined('SYSPATH') or die('No direct access allowed.');
|
||||||
|
/**
|
||||||
|
* @package Kohana/Codebench
|
||||||
|
* @category Tests
|
||||||
|
* @author Geert De Deckere <geert@idoe.be>
|
||||||
|
*/
|
||||||
|
class Bench_StripNullBytes extends Codebench {
|
||||||
|
|
||||||
|
public $description =
|
||||||
|
'String replacement comparisons related to <a href="http://dev.kohanaphp.com/issues/2676">#2676</a>.';
|
||||||
|
|
||||||
|
public $loops = 1000;
|
||||||
|
|
||||||
|
public $subjects = array
|
||||||
|
(
|
||||||
|
"\0",
|
||||||
|
"\0\0\0\0\0\0\0\0\0\0",
|
||||||
|
"bla\0bla\0bla\0bla\0bla\0bla\0bla\0bla\0bla\0bla",
|
||||||
|
"blablablablablablablablablablablablablablablabla",
|
||||||
|
);
|
||||||
|
|
||||||
|
public function bench_str_replace($subject)
|
||||||
|
{
|
||||||
|
return str_replace("\0", '', $subject);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function bench_strtr($subject)
|
||||||
|
{
|
||||||
|
return strtr($subject, array("\0" => ''));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function bench_preg_replace($subject)
|
||||||
|
{
|
||||||
|
return preg_replace('~\0+~', '', $subject);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
65
modules/codebench/classes/Bench/Transliterate.php
Normal file
65
modules/codebench/classes/Bench/Transliterate.php
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
<?php defined('SYSPATH') or die('No direct access allowed.');
|
||||||
|
/**
|
||||||
|
* @package Kohana/Codebench
|
||||||
|
* @category Tests
|
||||||
|
* @author Geert De Deckere <geert@idoe.be>
|
||||||
|
*/
|
||||||
|
class Bench_Transliterate extends Codebench {
|
||||||
|
|
||||||
|
public $description =
|
||||||
|
'Inspired by:
|
||||||
|
http://forum.kohanaframework.org/comments.php?DiscussionID=6113';
|
||||||
|
|
||||||
|
public $loops = 10;
|
||||||
|
|
||||||
|
public $subjects = array
|
||||||
|
(
|
||||||
|
// ASCII
|
||||||
|
'a', 'b', 'c', 'd', '1', '2', '3',
|
||||||
|
|
||||||
|
// Non-ASCII
|
||||||
|
'à', 'ô', 'ď', 'ḟ', 'ë', 'š', 'ơ',
|
||||||
|
'ß', 'ă', 'ř', 'ț', 'ň', 'ā', 'ķ',
|
||||||
|
'ŝ', 'ỳ', 'ņ', 'ĺ', 'ħ', 'ṗ', 'ó',
|
||||||
|
'ú', 'ě', 'é', 'ç', 'ẁ', 'ċ', 'õ',
|
||||||
|
'ṡ', 'ø', 'ģ', 'ŧ', 'ș', 'ė', 'ĉ',
|
||||||
|
'ś', 'î', 'ű', 'ć', 'ę', 'ŵ', 'ṫ',
|
||||||
|
'ū', 'č', 'ö', 'è', 'ŷ', 'ą', 'ł',
|
||||||
|
'ų', 'ů', 'ş', 'ğ', 'ļ', 'ƒ', 'ž',
|
||||||
|
'ẃ', 'ḃ', 'å', 'ì', 'ï', 'ḋ', 'ť',
|
||||||
|
'ŗ', 'ä', 'í', 'ŕ', 'ê', 'ü', 'ò',
|
||||||
|
'ē', 'ñ', 'ń', 'ĥ', 'ĝ', 'đ', 'ĵ',
|
||||||
|
'ÿ', 'ũ', 'ŭ', 'ư', 'ţ', 'ý', 'ő',
|
||||||
|
'â', 'ľ', 'ẅ', 'ż', 'ī', 'ã', 'ġ',
|
||||||
|
'ṁ', 'ō', 'ĩ', 'ù', 'į', 'ź', 'á',
|
||||||
|
'û', 'þ', 'ð', 'æ', 'µ', 'ĕ', 'ı',
|
||||||
|
'À', 'Ô', 'Ď', 'Ḟ', 'Ë', 'Š', 'Ơ',
|
||||||
|
'Ă', 'Ř', 'Ț', 'Ň', 'Ā', 'Ķ', 'Ĕ',
|
||||||
|
'Ŝ', 'Ỳ', 'Ņ', 'Ĺ', 'Ħ', 'Ṗ', 'Ó',
|
||||||
|
'Ú', 'Ě', 'É', 'Ç', 'Ẁ', 'Ċ', 'Õ',
|
||||||
|
'Ṡ', 'Ø', 'Ģ', 'Ŧ', 'Ș', 'Ė', 'Ĉ',
|
||||||
|
'Ś', 'Î', 'Ű', 'Ć', 'Ę', 'Ŵ', 'Ṫ',
|
||||||
|
'Ū', 'Č', 'Ö', 'È', 'Ŷ', 'Ą', 'Ł',
|
||||||
|
'Ų', 'Ů', 'Ş', 'Ğ', 'Ļ', 'Ƒ', 'Ž',
|
||||||
|
'Ẃ', 'Ḃ', 'Å', 'Ì', 'Ï', 'Ḋ', 'Ť',
|
||||||
|
'Ŗ', 'Ä', 'Í', 'Ŕ', 'Ê', 'Ü', 'Ò',
|
||||||
|
'Ē', 'Ñ', 'Ń', 'Ĥ', 'Ĝ', 'Đ', 'Ĵ',
|
||||||
|
'Ÿ', 'Ũ', 'Ŭ', 'Ư', 'Ţ', 'Ý', 'Ő',
|
||||||
|
'Â', 'Ľ', 'Ẅ', 'Ż', 'Ī', 'Ã', 'Ġ',
|
||||||
|
'Ṁ', 'Ō', 'Ĩ', 'Ù', 'Į', 'Ź', 'Á',
|
||||||
|
'Û', 'Þ', 'Ð', 'Æ', 'İ',
|
||||||
|
);
|
||||||
|
|
||||||
|
public function bench_utf8($subject)
|
||||||
|
{
|
||||||
|
return UTF8::transliterate_to_ascii($subject);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function bench_iconv($subject)
|
||||||
|
{
|
||||||
|
// Note: need to suppress errors on iconv because some chars trigger the following notice:
|
||||||
|
// "Detected an illegal character in input string"
|
||||||
|
return preg_replace('~[^-a-z0-9]+~i', '', @iconv('UTF-8', 'ASCII//TRANSLIT', $subject));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
123
modules/codebench/classes/Bench/URLSite.php
Normal file
123
modules/codebench/classes/Bench/URLSite.php
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
<?php defined('SYSPATH') or die('No direct access allowed.');
|
||||||
|
/**
|
||||||
|
* @package Kohana/Codebench
|
||||||
|
* @category Tests
|
||||||
|
* @author Geert De Deckere <geert@idoe.be>
|
||||||
|
*/
|
||||||
|
class Bench_URLSite extends Codebench {
|
||||||
|
|
||||||
|
public $description = 'http://dev.kohanaframework.org/issues/3110';
|
||||||
|
|
||||||
|
public $loops = 1000;
|
||||||
|
|
||||||
|
public $subjects = array
|
||||||
|
(
|
||||||
|
'',
|
||||||
|
'news',
|
||||||
|
'news/',
|
||||||
|
'/news/',
|
||||||
|
'news/page/5',
|
||||||
|
'news/page:5',
|
||||||
|
'http://example.com/',
|
||||||
|
'http://example.com/hello',
|
||||||
|
'http://example.com:80/',
|
||||||
|
'http://user:pass@example.com/',
|
||||||
|
);
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
foreach ($this->subjects as $subject)
|
||||||
|
{
|
||||||
|
// Automatically create URIs with query string and/or fragment part appended
|
||||||
|
$this->subjects[] = $subject.'?query=string';
|
||||||
|
$this->subjects[] = $subject.'#fragment';
|
||||||
|
$this->subjects[] = $subject.'?query=string#fragment';
|
||||||
|
}
|
||||||
|
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function bench_original($uri)
|
||||||
|
{
|
||||||
|
// Get the path from the URI
|
||||||
|
$path = trim(parse_url($uri, PHP_URL_PATH), '/');
|
||||||
|
|
||||||
|
if ($query = parse_url($uri, PHP_URL_QUERY))
|
||||||
|
{
|
||||||
|
$query = '?'.$query;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($fragment = parse_url($uri, PHP_URL_FRAGMENT))
|
||||||
|
{
|
||||||
|
$fragment = '#'.$fragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $path.$query.$fragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function bench_explode($uri)
|
||||||
|
{
|
||||||
|
// Chop off possible scheme, host, port, user and pass parts
|
||||||
|
$path = preg_replace('~^[-a-z0-9+.]++://[^/]++/?~', '', trim($uri, '/'));
|
||||||
|
|
||||||
|
$fragment = '';
|
||||||
|
$explode = explode('#', $path, 2);
|
||||||
|
if (isset($explode[1]))
|
||||||
|
{
|
||||||
|
$path = $explode[0];
|
||||||
|
$fragment = '#'.$explode[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
$query = '';
|
||||||
|
$explode = explode('?', $path, 2);
|
||||||
|
if (isset($explode[1]))
|
||||||
|
{
|
||||||
|
$path = $explode[0];
|
||||||
|
$query = '?'.$explode[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $path.$query.$fragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function bench_regex($uri)
|
||||||
|
{
|
||||||
|
preg_match('~^(?:[-a-z0-9+.]++://[^/]++/?)?([^?#]++)?(\?[^#]*+)?(#.*)?~', trim($uri, '/'), $matches);
|
||||||
|
$path = Arr::get($matches, 1, '');
|
||||||
|
$query = Arr::get($matches, 2, '');
|
||||||
|
$fragment = Arr::get($matches, 3, '');
|
||||||
|
|
||||||
|
return $path.$query.$fragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function bench_regex_without_arrget($uri)
|
||||||
|
{
|
||||||
|
preg_match('~^(?:[-a-z0-9+.]++://[^/]++/?)?([^?#]++)?(\?[^#]*+)?(#.*)?~', trim($uri, '/'), $matches);
|
||||||
|
$path = isset($matches[1]) ? $matches[1] : '';
|
||||||
|
$query = isset($matches[2]) ? $matches[2] : '';
|
||||||
|
$fragment = isset($matches[3]) ? $matches[3] : '';
|
||||||
|
|
||||||
|
return $path.$query.$fragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
// And then I thought, why do all the work of extracting the query and fragment parts and then reappending them?
|
||||||
|
// Just leaving them alone should be fine, right? As a bonus we get a very nice speed boost.
|
||||||
|
public function bench_less_is_more($uri)
|
||||||
|
{
|
||||||
|
// Chop off possible scheme, host, port, user and pass parts
|
||||||
|
$path = preg_replace('~^[-a-z0-9+.]++://[^/]++/?~', '', trim($uri, '/'));
|
||||||
|
|
||||||
|
return $path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function bench_less_is_more_with_strpos_optimization($uri)
|
||||||
|
{
|
||||||
|
if (strpos($uri, '://') !== FALSE)
|
||||||
|
{
|
||||||
|
// Chop off possible scheme, host, port, user and pass parts
|
||||||
|
$uri = preg_replace('~^[-a-z0-9+.]++://[^/]++/?~', '', trim($uri, '/'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
58
modules/codebench/classes/Bench/UserFuncArray.php
Normal file
58
modules/codebench/classes/Bench/UserFuncArray.php
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
<?php defined('SYSPATH') or die('No direct access allowed.');
|
||||||
|
/**
|
||||||
|
* @package Kohana/Codebench
|
||||||
|
* @category Tests
|
||||||
|
* @author Woody Gilk <woody.gilk@kohanaphp.com>
|
||||||
|
*/
|
||||||
|
class Bench_UserFuncArray extends Codebench {
|
||||||
|
|
||||||
|
public $description =
|
||||||
|
'Testing the speed difference of using <code>call_user_func_array</code>
|
||||||
|
compared to counting args and doing manual calls.';
|
||||||
|
|
||||||
|
public $loops = 100000;
|
||||||
|
|
||||||
|
public $subjects = array
|
||||||
|
(
|
||||||
|
// Argument sets
|
||||||
|
array(),
|
||||||
|
array('one'),
|
||||||
|
array('one', 'two'),
|
||||||
|
array('one', 'two', 'three'),
|
||||||
|
);
|
||||||
|
|
||||||
|
public function bench_count_args($args)
|
||||||
|
{
|
||||||
|
$name = 'callme';
|
||||||
|
switch (count($args))
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
$this->$name($args[0]);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
$this->$name($args[0], $args[1]);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
$this->$name($args[0], $args[1], $args[2]);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
$this->$name($args[0], $args[1], $args[2], $args[3]);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
call_user_func_array(array($this, $name), $args);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function bench_direct_call($args)
|
||||||
|
{
|
||||||
|
$name = 'callme';
|
||||||
|
call_user_func_array(array($this, $name), $args);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function callme()
|
||||||
|
{
|
||||||
|
return count(func_get_args());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
116
modules/codebench/classes/Bench/ValidColor.php
Normal file
116
modules/codebench/classes/Bench/ValidColor.php
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
<?php defined('SYSPATH') or die('No direct access allowed.');
|
||||||
|
/**
|
||||||
|
* @package Kohana/Codebench
|
||||||
|
* @category Tests
|
||||||
|
* @author Geert De Deckere <geert@idoe.be>
|
||||||
|
*/
|
||||||
|
class Bench_ValidColor extends Codebench {
|
||||||
|
|
||||||
|
public $description =
|
||||||
|
'Optimization for <code>Validate::color()</code>.
|
||||||
|
See: http://forum.kohanaphp.com/comments.php?DiscussionID=2192.
|
||||||
|
|
||||||
|
Note that the methods with an <em>_invalid</em> suffix contain flawed regexes and should be
|
||||||
|
completely discarded. I left them in here for educational purposes, and to remind myself
|
||||||
|
to think harder and test more thoroughly. It can\'t be that I only found out so late in
|
||||||
|
the game. For the regex explanation have a look at the forum topic mentioned earlier.';
|
||||||
|
|
||||||
|
public $loops = 10000;
|
||||||
|
|
||||||
|
public $subjects = array
|
||||||
|
(
|
||||||
|
// Valid colors
|
||||||
|
'aaA',
|
||||||
|
'123',
|
||||||
|
'000000',
|
||||||
|
'#123456',
|
||||||
|
'#abcdef',
|
||||||
|
|
||||||
|
// Invalid colors
|
||||||
|
'ggg',
|
||||||
|
'1234',
|
||||||
|
'#1234567',
|
||||||
|
"#000\n",
|
||||||
|
'}§è!çà%$z',
|
||||||
|
);
|
||||||
|
|
||||||
|
// Note that I added the D modifier to corey's regexes. We need to match exactly
|
||||||
|
// the same if we want the benchmarks to be of any value.
|
||||||
|
public function bench_corey_regex_1_invalid($subject)
|
||||||
|
{
|
||||||
|
return (bool) preg_match('/^#?([0-9a-f]{1,2}){3}$/iD', $subject);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function bench_corey_regex_2($subject)
|
||||||
|
{
|
||||||
|
return (bool) preg_match('/^#?([0-9a-f]){3}(([0-9a-f]){3})?$/iD', $subject);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optimized corey_regex_1
|
||||||
|
// Using non-capturing parentheses and a possessive interval
|
||||||
|
public function bench_geert_regex_1a_invalid($subject)
|
||||||
|
{
|
||||||
|
return (bool) preg_match('/^#?(?:[0-9a-f]{1,2}+){3}$/iD', $subject);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optimized corey_regex_2
|
||||||
|
// Removed useless parentheses, made the remaining ones non-capturing
|
||||||
|
public function bench_geert_regex_2a($subject)
|
||||||
|
{
|
||||||
|
return (bool) preg_match('/^#?[0-9a-f]{3}(?:[0-9a-f]{3})?$/iD', $subject);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optimized geert_regex_1a
|
||||||
|
// Possessive "#"
|
||||||
|
public function bench_geert_regex_1b_invalid($subject)
|
||||||
|
{
|
||||||
|
return (bool) preg_match('/^#?+(?:[0-9a-f]{1,2}+){3}$/iD', $subject);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optimized geert_regex_2a
|
||||||
|
// Possessive "#"
|
||||||
|
public function bench_geert_regex_2b($subject)
|
||||||
|
{
|
||||||
|
return (bool) preg_match('/^#?+[0-9a-f]{3}(?:[0-9a-f]{3})?$/iD', $subject);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Using \z instead of $
|
||||||
|
public function bench_salathe_regex_1($subject)
|
||||||
|
{
|
||||||
|
return (bool) preg_match('/^#?+[0-9a-f]{3}(?:[0-9a-f]{3})?\z/i', $subject);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Using \A instead of ^
|
||||||
|
public function bench_salathe_regex_2($subject)
|
||||||
|
{
|
||||||
|
return (bool) preg_match('/\A#?+[0-9a-f]{3}(?:[0-9a-f]{3})?\z/i', $subject);
|
||||||
|
}
|
||||||
|
|
||||||
|
// A solution without regex
|
||||||
|
public function bench_geert_str($subject)
|
||||||
|
{
|
||||||
|
if ($subject[0] === '#')
|
||||||
|
{
|
||||||
|
$subject = substr($subject, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
$strlen = strlen($subject);
|
||||||
|
return (($strlen === 3 OR $strlen === 6) AND ctype_xdigit($subject));
|
||||||
|
}
|
||||||
|
|
||||||
|
// An ugly, but fast, solution without regex
|
||||||
|
public function bench_salathe_str($subject)
|
||||||
|
{
|
||||||
|
if ($subject[0] === '#')
|
||||||
|
{
|
||||||
|
$subject = substr($subject, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TRUE if:
|
||||||
|
// 1. $subject is 6 or 3 chars long
|
||||||
|
// 2. $subject contains only hexadecimal digits
|
||||||
|
return (((isset($subject[5]) AND ! isset($subject[6])) OR
|
||||||
|
(isset($subject[2]) AND ! isset($subject[3])))
|
||||||
|
AND ctype_xdigit($subject));
|
||||||
|
}
|
||||||
|
}
|
105
modules/codebench/classes/Bench/ValidURL.php
Normal file
105
modules/codebench/classes/Bench/ValidURL.php
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
<?php defined('SYSPATH') or die('No direct access allowed.');
|
||||||
|
/**
|
||||||
|
* @package Kohana/Codebench
|
||||||
|
* @category Tests
|
||||||
|
* @author Geert De Deckere <geert@idoe.be>
|
||||||
|
*/
|
||||||
|
class Bench_ValidURL extends Codebench {
|
||||||
|
|
||||||
|
public $description =
|
||||||
|
'filter_var vs regex:
|
||||||
|
http://dev.kohanaframework.org/issues/2847';
|
||||||
|
|
||||||
|
public $loops = 1000;
|
||||||
|
|
||||||
|
public $subjects = array
|
||||||
|
(
|
||||||
|
// Valid
|
||||||
|
'http://google.com',
|
||||||
|
'http://google.com/',
|
||||||
|
'http://google.com/?q=abc',
|
||||||
|
'http://google.com/#hash',
|
||||||
|
'http://localhost',
|
||||||
|
'http://hello-world.pl',
|
||||||
|
'http://hello--world.pl',
|
||||||
|
'http://h.e.l.l.0.pl',
|
||||||
|
'http://server.tld/get/info',
|
||||||
|
'http://127.0.0.1',
|
||||||
|
'http://127.0.0.1:80',
|
||||||
|
'http://user@127.0.0.1',
|
||||||
|
'http://user:pass@127.0.0.1',
|
||||||
|
'ftp://my.server.com',
|
||||||
|
'rss+xml://rss.example.com',
|
||||||
|
|
||||||
|
// Invalid
|
||||||
|
'http://google.2com',
|
||||||
|
'http://google.com?q=abc',
|
||||||
|
'http://google.com#hash',
|
||||||
|
'http://hello-.pl',
|
||||||
|
'http://hel.-lo.world.pl',
|
||||||
|
'http://ww£.google.com',
|
||||||
|
'http://127.0.0.1234',
|
||||||
|
'http://127.0.0.1.1',
|
||||||
|
'http://user:@127.0.0.1',
|
||||||
|
"http://finalnewline.com\n",
|
||||||
|
);
|
||||||
|
|
||||||
|
public function bench_filter_var($url)
|
||||||
|
{
|
||||||
|
return (bool) filter_var($url, FILTER_VALIDATE_URL, FILTER_FLAG_HOST_REQUIRED);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function bench_regex($url)
|
||||||
|
{
|
||||||
|
// Based on http://www.apps.ietf.org/rfc/rfc1738.html#sec-5
|
||||||
|
if ( ! preg_match(
|
||||||
|
'~^
|
||||||
|
|
||||||
|
# scheme
|
||||||
|
[-a-z0-9+.]++://
|
||||||
|
|
||||||
|
# username:password (optional)
|
||||||
|
(?:
|
||||||
|
[-a-z0-9$_.+!*\'(),;?&=%]++ # username
|
||||||
|
(?::[-a-z0-9$_.+!*\'(),;?&=%]++)? # password (optional)
|
||||||
|
@
|
||||||
|
)?
|
||||||
|
|
||||||
|
(?:
|
||||||
|
# ip address
|
||||||
|
\d{1,3}+(?:\.\d{1,3}+){3}+
|
||||||
|
|
||||||
|
| # or
|
||||||
|
|
||||||
|
# hostname (captured)
|
||||||
|
(
|
||||||
|
(?!-)[-a-z0-9]{1,63}+(?<!-)
|
||||||
|
(?:\.(?!-)[-a-z0-9]{1,63}+(?<!-)){0,126}+
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# port (optional)
|
||||||
|
(?::\d{1,5}+)?
|
||||||
|
|
||||||
|
# path (optional)
|
||||||
|
(?:/.*)?
|
||||||
|
|
||||||
|
$~iDx', $url, $matches))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
// We matched an IP address
|
||||||
|
if ( ! isset($matches[1]))
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
// Check maximum length of the whole hostname
|
||||||
|
// http://en.wikipedia.org/wiki/Domain_name#cite_note-0
|
||||||
|
if (strlen($matches[1]) > 253)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
// An extra check for the top level domain
|
||||||
|
// It must start with a letter
|
||||||
|
$tld = ltrim(substr($matches[1], (int) strrpos($matches[1], '.')), '.');
|
||||||
|
return ctype_alpha($tld[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
3
modules/codebench/classes/Codebench.php
Normal file
3
modules/codebench/classes/Codebench.php
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<?php defined('SYSPATH') or die('No direct script access.');
|
||||||
|
|
||||||
|
class Codebench extends Kohana_Codebench {}
|
36
modules/codebench/classes/Controller/Codebench.php
Normal file
36
modules/codebench/classes/Controller/Codebench.php
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<?php defined('SYSPATH') or die('No direct access allowed.');
|
||||||
|
/**
|
||||||
|
* Codebench — A benchmarking module.
|
||||||
|
*
|
||||||
|
* @package Kohana/Codebench
|
||||||
|
* @category Controllers
|
||||||
|
* @author Kohana Team
|
||||||
|
* @copyright (c) 2009 Kohana Team
|
||||||
|
* @license http://kohanaphp.com/license.html
|
||||||
|
*/
|
||||||
|
class Controller_Codebench extends Kohana_Controller_Template {
|
||||||
|
|
||||||
|
// The codebench view
|
||||||
|
public $template = 'codebench';
|
||||||
|
|
||||||
|
public function action_index()
|
||||||
|
{
|
||||||
|
$class = $this->request->param('class');
|
||||||
|
|
||||||
|
// Convert submitted class name to URI segment
|
||||||
|
if (isset($_POST['class']))
|
||||||
|
{
|
||||||
|
throw HTTP_Exception::factory(302)->location('codebench/'.trim($_POST['class']));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pass the class name on to the view
|
||||||
|
$this->template->class = (string) $class;
|
||||||
|
|
||||||
|
// Try to load the class, then run it
|
||||||
|
if (Kohana::auto_load($class) === TRUE)
|
||||||
|
{
|
||||||
|
$codebench = new $class;
|
||||||
|
$this->template->codebench = $codebench->run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
217
modules/codebench/classes/Kohana/Codebench.php
Normal file
217
modules/codebench/classes/Kohana/Codebench.php
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
<?php defined('SYSPATH') or die('No direct access allowed.');
|
||||||
|
/**
|
||||||
|
* Codebench — A benchmarking module.
|
||||||
|
*
|
||||||
|
* @package Kohana/Codebench
|
||||||
|
* @category Base
|
||||||
|
* @author Kohana Team
|
||||||
|
* @copyright (c) 2009 Kohana Team
|
||||||
|
* @license http://kohanaphp.com/license.html
|
||||||
|
*/
|
||||||
|
abstract class Kohana_Codebench {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string Some optional explanatory comments about the benchmark file.
|
||||||
|
* HTML allowed. URLs will be converted to links automatically.
|
||||||
|
*/
|
||||||
|
public $description = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var integer How many times to execute each method per subject.
|
||||||
|
*/
|
||||||
|
public $loops = 1000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array The subjects to supply iteratively to your benchmark methods.
|
||||||
|
*/
|
||||||
|
public $subjects = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array Grade letters with their maximum scores. Used to color the graphs.
|
||||||
|
*/
|
||||||
|
public $grades = array
|
||||||
|
(
|
||||||
|
125 => 'A',
|
||||||
|
150 => 'B',
|
||||||
|
200 => 'C',
|
||||||
|
300 => 'D',
|
||||||
|
500 => 'E',
|
||||||
|
'default' => 'F',
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
// Set the maximum execution time
|
||||||
|
set_time_limit(Kohana::$config->load('codebench')->max_execution_time);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs Codebench on the extending class.
|
||||||
|
*
|
||||||
|
* @return array benchmark output
|
||||||
|
*/
|
||||||
|
public function run()
|
||||||
|
{
|
||||||
|
// Array of all methods to loop over
|
||||||
|
$methods = array_filter(get_class_methods($this), array($this, '_method_filter'));
|
||||||
|
|
||||||
|
// Make sure the benchmark runs at least once,
|
||||||
|
// also if no subject data has been provided.
|
||||||
|
if (empty($this->subjects))
|
||||||
|
{
|
||||||
|
$this->subjects = array('NULL' => NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize benchmark output
|
||||||
|
$codebench = array
|
||||||
|
(
|
||||||
|
'class' => get_class($this),
|
||||||
|
'description' => $this->description,
|
||||||
|
'loops' => array
|
||||||
|
(
|
||||||
|
'base' => (int) $this->loops,
|
||||||
|
'total' => (int) $this->loops * count($this->subjects) * count($methods),
|
||||||
|
),
|
||||||
|
'subjects' => $this->subjects,
|
||||||
|
'benchmarks' => array(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Benchmark each method
|
||||||
|
foreach ($methods as $method)
|
||||||
|
{
|
||||||
|
// Initialize benchmark output for this method
|
||||||
|
$codebench['benchmarks'][$method] = array('time' => 0, 'memory' => 0);
|
||||||
|
|
||||||
|
// Using Reflection because simply calling $this->$method($subject) in the loop below
|
||||||
|
// results in buggy benchmark times correlating to the length of the method name.
|
||||||
|
$reflection = new ReflectionMethod(get_class($this), $method);
|
||||||
|
|
||||||
|
// Benchmark each subject on each method
|
||||||
|
foreach ($this->subjects as $subject_key => $subject)
|
||||||
|
{
|
||||||
|
// Prerun each method/subject combo before the actual benchmark loop.
|
||||||
|
// This way relatively expensive initial processes won't be benchmarked, e.g. autoloading.
|
||||||
|
// At the same time we capture the return here so we don't have to do that in the loop anymore.
|
||||||
|
$return = $reflection->invoke($this, $subject);
|
||||||
|
|
||||||
|
// Start the timer for one subject
|
||||||
|
$token = Profiler::start('codebench', $method.$subject_key);
|
||||||
|
|
||||||
|
// The heavy work
|
||||||
|
for ($i = 0; $i < $this->loops; ++$i)
|
||||||
|
{
|
||||||
|
$reflection->invoke($this, $subject);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop and read the timer
|
||||||
|
$benchmark = Profiler::total($token);
|
||||||
|
|
||||||
|
// Benchmark output specific to the current method and subject
|
||||||
|
$codebench['benchmarks'][$method]['subjects'][$subject_key] = array
|
||||||
|
(
|
||||||
|
'return' => $return,
|
||||||
|
'time' => $benchmark[0],
|
||||||
|
'memory' => $benchmark[1],
|
||||||
|
);
|
||||||
|
|
||||||
|
// Update method totals
|
||||||
|
$codebench['benchmarks'][$method]['time'] += $benchmark[0];
|
||||||
|
$codebench['benchmarks'][$method]['memory'] += $benchmark[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the fastest and slowest benchmarks for both methods and subjects, time and memory,
|
||||||
|
// these values will be overwritten using min() and max() later on.
|
||||||
|
// The 999999999 values look like a hack, I know, but they work,
|
||||||
|
// unless your method runs for more than 31 years or consumes over 1GB of memory.
|
||||||
|
$fastest_method = $fastest_subject = array('time' => 999999999, 'memory' => 999999999);
|
||||||
|
$slowest_method = $slowest_subject = array('time' => 0, 'memory' => 0);
|
||||||
|
|
||||||
|
// Find the fastest and slowest benchmarks, needed for the percentage calculations
|
||||||
|
foreach ($methods as $method)
|
||||||
|
{
|
||||||
|
// Update the fastest and slowest method benchmarks
|
||||||
|
$fastest_method['time'] = min($fastest_method['time'], $codebench['benchmarks'][$method]['time']);
|
||||||
|
$fastest_method['memory'] = min($fastest_method['memory'], $codebench['benchmarks'][$method]['memory']);
|
||||||
|
$slowest_method['time'] = max($slowest_method['time'], $codebench['benchmarks'][$method]['time']);
|
||||||
|
$slowest_method['memory'] = max($slowest_method['memory'], $codebench['benchmarks'][$method]['memory']);
|
||||||
|
|
||||||
|
foreach ($this->subjects as $subject_key => $subject)
|
||||||
|
{
|
||||||
|
// Update the fastest and slowest subject benchmarks
|
||||||
|
$fastest_subject['time'] = min($fastest_subject['time'], $codebench['benchmarks'][$method]['subjects'][$subject_key]['time']);
|
||||||
|
$fastest_subject['memory'] = min($fastest_subject['memory'], $codebench['benchmarks'][$method]['subjects'][$subject_key]['memory']);
|
||||||
|
$slowest_subject['time'] = max($slowest_subject['time'], $codebench['benchmarks'][$method]['subjects'][$subject_key]['time']);
|
||||||
|
$slowest_subject['memory'] = max($slowest_subject['memory'], $codebench['benchmarks'][$method]['subjects'][$subject_key]['memory']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Percentage calculations for methods
|
||||||
|
foreach ($codebench['benchmarks'] as & $method)
|
||||||
|
{
|
||||||
|
// Calculate percentage difference relative to fastest and slowest methods
|
||||||
|
$method['percent']['fastest']['time'] = (empty($fastest_method['time'])) ? 0 : ($method['time'] / $fastest_method['time'] * 100);
|
||||||
|
$method['percent']['fastest']['memory'] = (empty($fastest_method['memory'])) ? 0 : ($method['memory'] / $fastest_method['memory'] * 100);
|
||||||
|
$method['percent']['slowest']['time'] = (empty($slowest_method['time'])) ? 0 : ($method['time'] / $slowest_method['time'] * 100);
|
||||||
|
$method['percent']['slowest']['memory'] = (empty($slowest_method['memory'])) ? 0 : ($method['memory'] / $slowest_method['memory'] * 100);
|
||||||
|
|
||||||
|
// Assign a grade for time and memory to each method
|
||||||
|
$method['grade']['time'] = $this->_grade($method['percent']['fastest']['time']);
|
||||||
|
$method['grade']['memory'] = $this->_grade($method['percent']['fastest']['memory']);
|
||||||
|
|
||||||
|
// Percentage calculations for subjects
|
||||||
|
foreach ($method['subjects'] as & $subject)
|
||||||
|
{
|
||||||
|
// Calculate percentage difference relative to fastest and slowest subjects for this method
|
||||||
|
$subject['percent']['fastest']['time'] = (empty($fastest_subject['time'])) ? 0 : ($subject['time'] / $fastest_subject['time'] * 100);
|
||||||
|
$subject['percent']['fastest']['memory'] = (empty($fastest_subject['memory'])) ? 0 : ($subject['memory'] / $fastest_subject['memory'] * 100);
|
||||||
|
$subject['percent']['slowest']['time'] = (empty($slowest_subject['time'])) ? 0 : ($subject['time'] / $slowest_subject['time'] * 100);
|
||||||
|
$subject['percent']['slowest']['memory'] = (empty($slowest_subject['memory'])) ? 0 : ($subject['memory'] / $slowest_subject['memory'] * 100);
|
||||||
|
|
||||||
|
// Assign a grade letter for time and memory to each subject
|
||||||
|
$subject['grade']['time'] = $this->_grade($subject['percent']['fastest']['time']);
|
||||||
|
$subject['grade']['memory'] = $this->_grade($subject['percent']['fastest']['memory']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $codebench;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback for array_filter().
|
||||||
|
* Filters out all methods not to benchmark.
|
||||||
|
*
|
||||||
|
* @param string method name
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
protected function _method_filter($method)
|
||||||
|
{
|
||||||
|
// Only benchmark methods with the "bench" prefix
|
||||||
|
return (substr($method, 0, 5) === 'bench');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the applicable grade letter for a score.
|
||||||
|
*
|
||||||
|
* @param integer|double score
|
||||||
|
* @return string grade letter
|
||||||
|
*/
|
||||||
|
protected function _grade($score)
|
||||||
|
{
|
||||||
|
foreach ($this->grades as $max => $grade)
|
||||||
|
{
|
||||||
|
if ($max === 'default')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if ($score <= $max)
|
||||||
|
return $grade;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->grades['default'];
|
||||||
|
}
|
||||||
|
}
|
16
modules/codebench/config/codebench.php
Normal file
16
modules/codebench/config/codebench.php
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?php defined('SYSPATH') or die('No direct access allowed.');
|
||||||
|
|
||||||
|
return array(
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The maximum execution time, in seconds. If set to zero, no time limit is imposed.
|
||||||
|
* Note: http://php.net/manual/en/function.set-time-limit.php#84563
|
||||||
|
*/
|
||||||
|
'max_execution_time' => 0,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expand all benchmark details by default.
|
||||||
|
*/
|
||||||
|
'expand_all' => FALSE,
|
||||||
|
|
||||||
|
);
|
23
modules/codebench/config/userguide.php
Normal file
23
modules/codebench/config/userguide.php
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?php defined('SYSPATH') or die('No direct script access.');
|
||||||
|
|
||||||
|
return array(
|
||||||
|
// Leave this alone
|
||||||
|
'modules' => array(
|
||||||
|
|
||||||
|
// This should be the path to this modules userguide pages, without the 'guide/'. Ex: '/guide/modulename/' would be 'modulename'
|
||||||
|
'codebench' => array(
|
||||||
|
|
||||||
|
// Whether this modules userguide pages should be shown
|
||||||
|
'enabled' => TRUE,
|
||||||
|
|
||||||
|
// The name that should show up on the userguide index page
|
||||||
|
'name' => 'Codebench',
|
||||||
|
|
||||||
|
// A short description of this module, shown on the index page
|
||||||
|
'description' => 'Code benchmarking tool.',
|
||||||
|
|
||||||
|
// Copyright message, shown in the footer for this module
|
||||||
|
'copyright' => '© 2008–2012 Kohana Team',
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
76
modules/codebench/guide/codebench/index.md
Normal file
76
modules/codebench/guide/codebench/index.md
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
# Using Codebench
|
||||||
|
|
||||||
|
[!!] The contents of this page are taken (with some minor changes) from <http://www.geertdedeckere.be/article/introducing-codebench> and are copyright Geert De Deckere.
|
||||||
|
|
||||||
|
For a long time I have been using a quick-and-dirty `benchmark.php` file to optimize bits of PHP code, many times regex-related stuff. The file contained not much more than a [gettimeofday](http://php.net/gettimeofday) function wrapped around a `for` loop. It worked, albeit not very efficiently. Something more solid was needed. I set out to create a far more usable piece of software to aid in the everlasting quest to squeeze every millisecond out of those regular expressions.
|
||||||
|
|
||||||
|
## Codebench Goals
|
||||||
|
|
||||||
|
### Benchmark multiple regular expressions at once
|
||||||
|
|
||||||
|
Being able to compare the speed of an arbitrary amount of regular expressions would be tremendously useful. In case you are wondering—yes, I had been writing down benchmark times for each regex, uncommenting them one by one. You get the idea. Those days should be gone forever now.
|
||||||
|
|
||||||
|
### Benchmark multiple subjects at once
|
||||||
|
|
||||||
|
What gets overlooked too often when testing and optimizing regular expressions is the fact that speed can vastly differ depending on the subjects, also known as input or target strings. Just because your regular expression matches, say, a valid email address quickly, does not necessarily mean it will quickly realize when an invalid email is provided. I plan to write a follow-up article with hands-on regex examples to demonstrate this point. Anyway, Codebench allows you to create an array of subjects which will be passed to each benchmark.
|
||||||
|
|
||||||
|
### Make it flexible enough to work for all PCRE functions
|
||||||
|
|
||||||
|
Initially I named the module “Regexbench”. I quickly realized, though, it would be flexible enough to benchmark all kinds of PHP code, hence the change to “Codebench”. While tools specifically built to help profiling PCRE functions, like [preg_match](http://php.net/preg_match) or [preg_replace](http://php.net/preg_replace), definitely have their use, more flexibility was needed here. You should be able to compare all kinds of constructions like combinations of PCRE functions and native PHP string functions.
|
||||||
|
|
||||||
|
### Create clean and portable benchmark cases
|
||||||
|
|
||||||
|
Throwing valuable benchmark data away every time I needed to optimize another regular expression had to stop. A clean file containing the complete set of all regex variations to compare, together with the set of subjects to test them against, would be more than welcome. Moreover, it would be easy to exchange benchmark cases with others.
|
||||||
|
|
||||||
|
### Visualize the benchmarks
|
||||||
|
|
||||||
|
Obviously providing a visual representation of the benchmark results, via simple graphs, would make interpreting them easier. Having not to think about Internet Explorer for once, made writing CSS a whole lot more easy and fun. It resulted in some fine graphs which are fully resizable.
|
||||||
|
|
||||||
|
Below are two screenshots of Codebench in action. `Valid_Color` is a class made for benchmarking different ways to validate hexadecimal HTML color values, e.g. `#FFF`. If you are interested in the story behind the actual regular expressions, take a look at [this topic in the Kohana forums](http://forum.kohanaphp.com/comments.php?DiscussionID=2192).
|
||||||
|
|
||||||
|
![Benchmarking several ways to validate HTML color values](codebench_screenshot1.png)
|
||||||
|
**Benchmarking seven ways to validate HTML color values**
|
||||||
|
|
||||||
|
![Collapsable results per subject for each method](codebench_screenshot2.png)
|
||||||
|
**Collapsable results per subject for each method**
|
||||||
|
|
||||||
|
## Working with Codebench
|
||||||
|
|
||||||
|
Codebench is included in Kohana 3, but if you need you [can download it](http://github.com/kohana/codebench/) from GitHub. Be sure Codebench is activated in your `application/bootstrap.php`.
|
||||||
|
|
||||||
|
Creating your own benchmarks is just a matter of creating a class that extends the Codebench class. The class should go in `classes/bench` and the class name should have the `Bench_` prefix. Put the code parts you want to compare into separate methods. Be sure to prefix those methods with `bench_`, other methods will not be benchmarked. Glance at the files in `modules/codebench/classes/bench/` for more examples.
|
||||||
|
|
||||||
|
Here is another short example with some extra explanations.
|
||||||
|
|
||||||
|
// classes/bench/ltrimdigits.php
|
||||||
|
class Bench_LtrimDigits extends Codebench {
|
||||||
|
|
||||||
|
// Some optional explanatory comments about the benchmark file.
|
||||||
|
// HTML allowed. URLs will be converted to links automatically.
|
||||||
|
public $description = 'Chopping off leading digits: regex vs ltrim.';
|
||||||
|
|
||||||
|
// How many times to execute each method per subject.
|
||||||
|
// Total loops = loops * number of methods * number of subjects
|
||||||
|
public $loops = 100000;
|
||||||
|
|
||||||
|
// The subjects to supply iteratively to your benchmark methods.
|
||||||
|
public $subjects = array
|
||||||
|
(
|
||||||
|
'123digits',
|
||||||
|
'no-digits',
|
||||||
|
);
|
||||||
|
|
||||||
|
public function bench_regex($subject)
|
||||||
|
{
|
||||||
|
return preg_replace('/^\d+/', '', $subject);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function bench_ltrim($subject)
|
||||||
|
{
|
||||||
|
return ltrim($subject, '0..9');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
And the winner is… [ltrim](http://php.net/ltrim). Happy benchmarking!
|
1
modules/codebench/guide/codebench/menu.md
Normal file
1
modules/codebench/guide/codebench/menu.md
Normal file
@ -0,0 +1 @@
|
|||||||
|
## [Codebench]()
|
8
modules/codebench/init.php
Normal file
8
modules/codebench/init.php
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?php defined('SYSPATH') or die('No direct script access.');
|
||||||
|
|
||||||
|
// Catch-all route for Codebench classes to run
|
||||||
|
Route::set('codebench', 'codebench(/<class>)')
|
||||||
|
->defaults(array(
|
||||||
|
'controller' => 'Codebench',
|
||||||
|
'action' => 'index',
|
||||||
|
'class' => NULL));
|
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
260
modules/codebench/views/codebench.php
Normal file
260
modules/codebench/views/codebench.php
Normal file
@ -0,0 +1,260 @@
|
|||||||
|
<?php defined('SYSPATH') or die('No direct access allowed.');
|
||||||
|
/**
|
||||||
|
* Codebench — A benchmarking module.
|
||||||
|
*
|
||||||
|
* @package Kohana/Codebench
|
||||||
|
* @author Kohana Team
|
||||||
|
* @copyright (c) 2009 Kohana Team
|
||||||
|
* @license http://kohanaphp.com/license.html
|
||||||
|
*/
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title><?php if ($class !== ''): ?>
|
||||||
|
<?php echo $class, ' · ' ?>
|
||||||
|
<?php endif; ?>Codebench</title>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* General styles*/
|
||||||
|
body { position:relative; margin:1em 2em; font:12px monaco,monospace; }
|
||||||
|
h1 { font-size:24px; letter-spacing:-0.05em; }
|
||||||
|
h2 { font-size:18px; letter-spacing:-0.1em; }
|
||||||
|
input, code { font:inherit; }
|
||||||
|
code { background:#e5e5e5; }
|
||||||
|
caption { display:none; }
|
||||||
|
|
||||||
|
/* Form */
|
||||||
|
#runner { margin-bottom:2em; }
|
||||||
|
#runner input[type="text"] { letter-spacing:-0.05em; }
|
||||||
|
|
||||||
|
/* Expand/Collapse all */
|
||||||
|
#toggle_all { position:absolute; top:0; right:0; margin:0; padding:0 4px; background:#000; font-size:18px; color:#fff; cursor:pointer; -moz-border-radius:2px; -webkit-border-radius:2px; }
|
||||||
|
|
||||||
|
/* Benchmark main graphs */
|
||||||
|
#bench { margin:2em 0; padding:0; list-style:none; }
|
||||||
|
#bench > li { margin:6px 0; }
|
||||||
|
#bench h2 { position:relative; margin:0; padding:2px; background:#ccc; border:1px solid #999; cursor:pointer; -moz-border-radius:3px; -webkit-border-radius:3px; }
|
||||||
|
#bench h2 > span { display:block; min-width:1px; height:33px; background:#fff; -moz-border-radius:2px; -webkit-border-radius:2px; }
|
||||||
|
#bench h2 .method { position:absolute; top:6px; left:8px; text-shadow:0 -1px 0 rgba(255,255,255,0.6); }
|
||||||
|
#bench h2 .method:before { content:'▸ '; }
|
||||||
|
#bench h2 .percent { position:absolute; top:6px; right:6px; padding:0 4px; background:#000; color:#fff; font-weight:normal; letter-spacing:0; -moz-border-radius:2px; -webkit-border-radius:2px; }
|
||||||
|
#bench h2:hover .method { left:10px; }
|
||||||
|
#bench h2.expanded { margin:12px 0 0; -moz-border-radius-bottomleft:0; -moz-border-radius-bottomright:0; -webkit-border-bottom-left-radius:0; -webkit-border-bottom-right-radius:0; }
|
||||||
|
#bench h2.expanded .method:before { content:'▾ '; }
|
||||||
|
|
||||||
|
/* Colorization of the bars */
|
||||||
|
#bench .grade-A { background:#3f0; }
|
||||||
|
#bench .grade-B { background:#fc0; }
|
||||||
|
#bench .grade-C { background:#f90; }
|
||||||
|
#bench .grade-D { background:#f60; }
|
||||||
|
#bench .grade-E { background:#f30; }
|
||||||
|
#bench .grade-F { background:#f00; }
|
||||||
|
|
||||||
|
/* Benchmark details */
|
||||||
|
#bench > li > div { display:none; margin:0 0 12px; padding:0 0 2px; background:#eee; border:1px solid #999; border-top:0; -moz-border-radius-bottomleft:3px; -moz-border-radius-bottomright:3px; -webkit-border-bottom-left-radius:3px; -webkit-border-bottom-right-radius:3px; }
|
||||||
|
#bench > li > div table { width:100%; background:#eee; border-collapse:collapse; }
|
||||||
|
#bench > li > div th { padding:6px; background:#ddd url(data:image/gif;base64,R0lGODlhAQASALMAAMfHx8TExM7Oztvb29jY2NbW1tPT09DQ0MrKygAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAAAAAAALAAAAAABABIAAAQNMAQAEBLiHGNKIcQwRAA7) repeat-x 0 1px; text-align:left; }
|
||||||
|
#bench > li > div td { padding:6px; border-top:1px solid #ccc; vertical-align:top; }
|
||||||
|
#bench .numeric { padding-left:18px; text-align:right; }
|
||||||
|
#bench .numeric span { position:relative; display:block; height:16px; }
|
||||||
|
#bench .numeric span span { position:absolute; top:0; right:0; min-width:1px; background:#ccc; -moz-border-radius:2px; -webkit-border-radius:2px; }
|
||||||
|
#bench .numeric span span span { top:0; right:0; background:none; }
|
||||||
|
#bench tbody tr:hover { background:#fff; }
|
||||||
|
#bench tbody tr.highlight { background:#ffc; }
|
||||||
|
|
||||||
|
/* Footer */
|
||||||
|
#footer { margin-top:2em; padding-top:1em; border-top:1px solid #ccc; color:#999; }
|
||||||
|
#footer a { color:inherit; }
|
||||||
|
|
||||||
|
/* Misc text styles */
|
||||||
|
.alert { padding:0 0.5em; background:#900; font-weight:normal; color:#fff; -moz-border-radius:3px; -webkit-border-radius:3px; }
|
||||||
|
.quiet { color:#999; }
|
||||||
|
.help { cursor:help; }
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
|
||||||
|
<script>
|
||||||
|
$(document).ready(function() {
|
||||||
|
// Insert "Toggle All" button
|
||||||
|
var expand_all_text = '▸ Expand all';
|
||||||
|
var collapse_all_text = '▾ Collapse all';
|
||||||
|
$('#bench').before('<p id="toggle_all">'+expand_all_text+'</p>');
|
||||||
|
|
||||||
|
// Cache these selection operations
|
||||||
|
var $runner = $('#runner');
|
||||||
|
var $toggle_all = $('#toggle_all');
|
||||||
|
var $bench_titles = $('#bench > li > h2');
|
||||||
|
var $bench_rows = $('#bench > li > div > table > tbody > tr');
|
||||||
|
|
||||||
|
// Runner form
|
||||||
|
$(':input:first', $runner).focus();
|
||||||
|
$runner.submit(function() {
|
||||||
|
$(':submit', this).attr('value', 'Running…').attr('disabled', 'disabled');
|
||||||
|
$('.alert', this).remove();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Toggle details for all benchmarks
|
||||||
|
$('#toggle_all').click(function() {
|
||||||
|
if ($(this).data('expanded')) {
|
||||||
|
$(this).data('expanded', false);
|
||||||
|
$(this).text(expand_all_text);
|
||||||
|
$bench_titles.removeClass('expanded').siblings().hide();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$(this).data('expanded', true);
|
||||||
|
$(this).text(collapse_all_text);
|
||||||
|
$bench_titles.addClass('expanded').siblings().show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
<?php if (Kohana::$config->load('codebench')->expand_all) { ?>
|
||||||
|
// Expand all benchmark details by default
|
||||||
|
$toggle_all.click();
|
||||||
|
<?php } ?>
|
||||||
|
|
||||||
|
// Toggle details for a single benchmark
|
||||||
|
$bench_titles.click(function() {
|
||||||
|
$(this).toggleClass('expanded').siblings().toggle();
|
||||||
|
|
||||||
|
// Counts of bench titles
|
||||||
|
var total_bench_titles = $bench_titles.length;
|
||||||
|
var expanded_bench_titles = $bench_titles.filter('.expanded').length;
|
||||||
|
|
||||||
|
// If no benchmark details are expanded, change "Collapse all" to "Expand all"
|
||||||
|
if (expanded_bench_titles == 0 && $toggle_all.data('expanded')) {
|
||||||
|
$toggle_all.click();
|
||||||
|
}
|
||||||
|
// If all benchmark details are expanded, change "Expand all" to "Collapse all"
|
||||||
|
else if (expanded_bench_titles == total_bench_titles && ! $toggle_all.data('expanded')) {
|
||||||
|
$toggle_all.click();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Highlight clicked rows
|
||||||
|
$bench_rows.click(function() {
|
||||||
|
$(this).toggleClass('highlight');
|
||||||
|
// Highlight doubleclicked rows globally
|
||||||
|
}).dblclick(function() {
|
||||||
|
var nth_row = $(this).parent().children().index(this) + 1;
|
||||||
|
if ($(this).hasClass('highlight')) {
|
||||||
|
$bench_rows.filter(':nth-child('+nth_row+')').removeClass('highlight');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$bench_rows.filter(':nth-child('+nth_row+')').addClass('highlight');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<!--[if IE]><p class="alert">This page is not meant to be viewed in Internet Explorer. Get a better browser.</p><![endif]-->
|
||||||
|
|
||||||
|
<form id="runner" method="post" action="<?php echo URL::site('codebench') ?>">
|
||||||
|
<h1>
|
||||||
|
<input name="class" type="text" value="<?php echo ($class !== '') ? $class : 'Bench_' ?>" size="25" title="Name of the Codebench library to run" />
|
||||||
|
<input type="submit" value="Run" />
|
||||||
|
<?php if ( ! empty($class)) { ?>
|
||||||
|
<?php if (empty($codebench)) { ?>
|
||||||
|
<strong class="alert">Library not found</strong>
|
||||||
|
<?php } elseif (empty($codebench['benchmarks'])) { ?>
|
||||||
|
<strong class="alert">No methods found to benchmark</strong>
|
||||||
|
<?php } ?>
|
||||||
|
<?php } ?>
|
||||||
|
</h1>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<?php if ( ! empty($codebench)) { ?>
|
||||||
|
|
||||||
|
<?php if (empty($codebench['benchmarks'])) { ?>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<strong>
|
||||||
|
Remember to prefix the methods you want to benchmark with “bench”.<br />
|
||||||
|
You might also want to overwrite <code>Codebench->method_filter()</code>.
|
||||||
|
</strong>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<?php } else { ?>
|
||||||
|
|
||||||
|
<ul id="bench">
|
||||||
|
<?php foreach ($codebench['benchmarks'] as $method => $benchmark) { ?>
|
||||||
|
<li>
|
||||||
|
|
||||||
|
<h2 title="<?php printf('%01.6f', $benchmark['time']) ?>s">
|
||||||
|
<span class="grade-<?php echo $benchmark['grade']['time'] ?>" style="width:<?php echo $benchmark['percent']['slowest']['time'] ?>%">
|
||||||
|
<span class="method"><?php echo $method ?></span>
|
||||||
|
<span class="percent">+<?php echo (int) $benchmark['percent']['fastest']['time'] ?>%</span>
|
||||||
|
</span>
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<table>
|
||||||
|
<caption>Benchmarks per subject for <?php echo $method ?></caption>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th style="width:50%">subject → return</th>
|
||||||
|
<th class="numeric" style="width:25%" title="Total method memory"><?php echo Text::bytes($benchmark['memory'], 'MB', '%01.6f%s') ?></th>
|
||||||
|
<th class="numeric" style="width:25%" title="Total method time"><?php printf('%01.6f', $benchmark['time']) ?>s</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
|
||||||
|
<?php foreach ($benchmark['subjects'] as $subject_key => $subject) { ?>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<strong class="help" title="(<?php echo gettype($codebench['subjects'][$subject_key]) ?>) <?php echo HTML::chars(var_export($codebench['subjects'][$subject_key], TRUE)) ?>">
|
||||||
|
[<?php echo HTML::chars($subject_key) ?>] →
|
||||||
|
</strong>
|
||||||
|
<span class="quiet">(<?php echo gettype($subject['return']) ?>)</span>
|
||||||
|
<?php echo HTML::chars(var_export($subject['return'], TRUE)) ?>
|
||||||
|
</td>
|
||||||
|
<td class="numeric">
|
||||||
|
<span title="+<?php echo (int) $subject['percent']['fastest']['memory'] ?>% memory">
|
||||||
|
<span style="width:<?php echo $subject['percent']['slowest']['memory'] ?>%">
|
||||||
|
<span><?php echo Text::bytes($subject['memory'], 'MB', '%01.6f%s') ?></span>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td class="numeric">
|
||||||
|
<span title="+<?php echo (int) $subject['percent']['fastest']['time'] ?>% time">
|
||||||
|
<span style="width:<?php echo $subject['percent']['slowest']['time'] ?>%">
|
||||||
|
<span><?php printf('%01.6f', $subject['time']) ?>s</span>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php } ?>
|
||||||
|
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
<?php } ?>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<?php } ?>
|
||||||
|
|
||||||
|
<?php if ( ! empty($codebench['description'])) { ?>
|
||||||
|
<?php echo Text::auto_p(Text::auto_link($codebench['description']), FALSE) ?>
|
||||||
|
<?php } ?>
|
||||||
|
|
||||||
|
<?php // echo '<h2>Raw output:</h2>', Debug::vars($codebench) ?>
|
||||||
|
|
||||||
|
<?php } ?>
|
||||||
|
|
||||||
|
<p id="footer">
|
||||||
|
Page executed in <strong><?php echo round(microtime(TRUE) - KOHANA_START_TIME, 2) ?> s</strong>
|
||||||
|
using <strong><?php echo Text::widont(Text::bytes(memory_get_usage(), 'MB')) ?></strong> of memory.<br />
|
||||||
|
<a href="http://github.com/kohana/codebench">Codebench</a>, a <a href="http://kohanaframework.org/">Kohana</a> module
|
||||||
|
by <a href="http://www.geertdedeckere.be/article/introducing-codebench">Geert De Deckere</a>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
12
modules/database/classes/Config/Database.php
Normal file
12
modules/database/classes/Config/Database.php
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?php defined('SYSPATH') OR die('No direct script access.');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transparent extension for the Kohana_Config_Database class
|
||||||
|
*
|
||||||
|
* @package Kohana/Database
|
||||||
|
* @category Configuration
|
||||||
|
* @author Kohana Team
|
||||||
|
* @copyright (c) 2012 Kohana Team
|
||||||
|
* @license http://kohanaframework.org/license
|
||||||
|
*/
|
||||||
|
class Config_Database extends Kohana_Config_Database {}
|
15
modules/database/classes/Config/Database/Reader.php
Normal file
15
modules/database/classes/Config/Database/Reader.php
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?php defined('SYSPATH') OR die('No direct script access.');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transparent extension of the Kohana_Config_Database_Reader class
|
||||||
|
*
|
||||||
|
* @package Kohana/Database
|
||||||
|
* @category Configuration
|
||||||
|
* @author Kohana Team
|
||||||
|
* @copyright (c) 2012 Kohana Team
|
||||||
|
* @license http://kohanaframework.org/license
|
||||||
|
*/
|
||||||
|
class Config_Database_Reader extends Kohana_Config_Database_Reader
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
15
modules/database/classes/Config/Database/Writer.php
Normal file
15
modules/database/classes/Config/Database/Writer.php
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?php defined('SYSPATH') OR die('No direct script access.');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transparent extension for the Kohana_Config_Database_Writer class
|
||||||
|
*
|
||||||
|
* @package Kohana/Database
|
||||||
|
* @category Configuration
|
||||||
|
* @author Kohana Team
|
||||||
|
* @copyright (c) 2012 Kohana Team
|
||||||
|
* @license http://kohanaframework.org/license
|
||||||
|
*/
|
||||||
|
class Config_Database_Writer extends Kohana_Config_Database_Writer
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
3
modules/database/classes/DB.php
Normal file
3
modules/database/classes/DB.php
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<?php defined('SYSPATH') OR die('No direct script access.');
|
||||||
|
|
||||||
|
class DB extends Kohana_DB {}
|
3
modules/database/classes/Database.php
Normal file
3
modules/database/classes/Database.php
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<?php defined('SYSPATH') OR die('No direct script access.');
|
||||||
|
|
||||||
|
abstract class Database extends Kohana_Database {}
|
3
modules/database/classes/Database/Exception.php
Normal file
3
modules/database/classes/Database/Exception.php
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<?php defined('SYSPATH') OR die('No direct script access.');
|
||||||
|
|
||||||
|
class Database_Exception extends Kohana_Database_Exception {}
|
3
modules/database/classes/Database/Expression.php
Normal file
3
modules/database/classes/Database/Expression.php
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<?php defined('SYSPATH') OR die('No direct script access.');
|
||||||
|
|
||||||
|
class Database_Expression extends Kohana_Database_Expression {}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user