Kohana v3.3.5

This commit is contained in:
Deon George 2016-05-01 20:50:24 +10:00
parent 8888719653
commit 68c7f4f159
170 changed files with 4565 additions and 1176 deletions

View File

@ -1,13 +1,13 @@
# Developing locally # Developing locally
Since Kohana maintains many concurrent versions at once, there is no single `master` branch. All versions have branches named with a prefix of it's version: Since Kohana maintains many concurrent versions at once, there is no single `master` branch. All versions have branches named with a prefix of its version:
- 3.2/master - 3.2/master
- 3.2/develop - 3.2/develop
- 3.3/master - 3.3/master
- 3.3/develop - 3.3/develop
and so on. All development of versions happens in the develop branch of that version. Before a release, new features are added here. After a major release is actually released, only bugfixes can happen here. New features and api changes must happen in the develop branch of the next version. and so on. All development of versions happens in the develop branch of that version. Before a release, new features are added here. After a major release is actually released, only bugfixes can happen here. New features and API changes must happen in the develop branch of the next version.
## Branch name meanings ## Branch name meanings
@ -17,7 +17,7 @@ and so on. All development of versions happens in the develop branch of that ver
- **3.3/release/*** - release branches are for maintenance work before a release. This branch should be branched from the develop branch only. Change the version number/code name here, and apply any other maintenance items needed before actually releasing. Merges from master should only come from this branch. It should be merged to develop when it's complete as well. This branch is deleted after it's done. - **3.3/release/*** - release branches are for maintenance work before a release. This branch should be branched from the develop branch only. Change the version number/code name here, and apply any other maintenance items needed before actually releasing. Merges from master should only come from this branch. It should be merged to develop when it's complete as well. This branch is deleted after it's done.
- **3.3/feature/*** - Details on these branches are outlined below. This branch is deleted after it's done. - **3.3/feature/*** - Details on these branches are outlined below. This branch is deleted after it's done.
If an bug/issue applies to multiple versions of kohana, it is first fixed in the lowest supported version it applies to, then merged to each higher branch it applies to. Each merge should only happen one version up. 3.1 should merge to 3.2, and 3.2 should merge to 3.3. 3.1 should not merge directly to 3.3. If an bug/issue applies to multiple versions of Kohana, it is first fixed in the lowest supported version it applies to, then merged to each higher branch it applies to. Each merge should only happen one version up. 3.1 should merge to 3.2, and 3.2 should merge to 3.3. 3.1 should not merge directly to 3.3.
To work on a specific release branch you need to check it out then check out the appropriate system branch. To work on a specific release branch you need to check it out then check out the appropriate system branch.
Release branch names follow the same convention in both kohana/kohana and kohana/core. Release branch names follow the same convention in both kohana/kohana and kohana/core.
@ -37,7 +37,7 @@ To work on 3.3.x you'd do the following:
> git submodule foreach "git fetch && git checkout 3.3/develop" > git submodule foreach "git fetch && git checkout 3.3/develop"
# ... # ...
It's important that you follow the last step, because unlike svn, git submodules point at a It's important that you follow the last step, because unlike SVN, Git submodules point at a
specific commit rather than the tip of a branch. If you cd into the system folder after specific commit rather than the tip of a branch. If you cd into the system folder after
a `git submodule update` and run `git status` you'll be told: a `git submodule update` and run `git status` you'll be told:
@ -48,7 +48,7 @@ a `git submodule update` and run `git status` you'll be told:
# Contributing to the project # Contributing to the project
All features and bugfixes must be fully tested and reference an issue in the [tracker](http://dev.kohanaframework.org/projects/kohana3), **there are absolutely no exceptions**. All features and bugfixes must be fully tested and reference an issue in [GitHub](https://github.com/kohana/kohana/issues), **there are absolutely no exceptions**.
It's highly recommended that you write/run unit tests during development as it can help you pick up on issues early on. See the Unit Testing section below. It's highly recommended that you write/run unit tests during development as it can help you pick up on issues early on. See the Unit Testing section below.
@ -68,7 +68,7 @@ The naming convention for feature branches is:
3.2/feature/4045-rewriting-config-system 3.2/feature/4045-rewriting-config-system
When a new feature is complete and fully tested it can be merged into its respective release branch using When a new feature is complete and fully tested it can be merged into its respective release branch using
`git pull --no-ff`. The `--no-ff` switch is important as it tells git to always create a commit `git pull --no-ff`. The `--no-ff` switch is important as it tells Git to always create a commit
detailing what branch you're merging from. This makes it a lot easier to analyse a feature's history. detailing what branch you're merging from. This makes it a lot easier to analyse a feature's history.
Here's a quick example: Here's a quick example:
@ -81,7 +81,7 @@ Here's a quick example:
> git merge --no-ff 3.2/feature/4045-rewriting-everything > git merge --no-ff 3.2/feature/4045-rewriting-everything
**If a change you make intentionally breaks the api then please correct the relevant tests before pushing!** **If a change you make intentionally breaks the API then please correct the relevant tests before pushing!**
## Bug fixing ## Bug fixing
@ -92,7 +92,7 @@ using the `@ticket` notation in the test to reference the bug's issue number
If you run the unit tests then the one you've just made should fail. If you run the unit tests then the one you've just made should fail.
Once you've written the bugfix, run the tests again before you commit to make sure that the Once you've written the bugfix, run the tests again before you commit to make sure that the
fix actually works,then commit both the fix and the test. fix actually works, then commit both the fix and the test.
**Bug fixes without tests written will be rejected! There are NO exceptions.** **Bug fixes without tests written will be rejected! There are NO exceptions.**
@ -101,11 +101,11 @@ branch is perfectly acceptable.
## Tagging releases ## Tagging releases
Tag names should be prefixed with a `v`, this helps to separate tag references from branch references in git. Tag names should be prefixed with a `v`, this helps to separate tag references from branch references in Git.
For example, if you were creating a tag for the `3.1.0` release the tag name would be `v3.1.0` For example, if you were creating a tag for the `3.1.0` release the tag name would be `v3.1.0`
# Merging Changes from Remote Repositories # Merging changes from remote repositories
Now that you have a remote repository, you can pull changes in the remote "kohana" repository Now that you have a remote repository, you can pull changes in the remote "kohana" repository
into your local repository: into your local repository:
@ -115,14 +115,14 @@ into your local repository:
**Note:** Before you pull changes you should make sure that any modifications you've made locally **Note:** Before you pull changes you should make sure that any modifications you've made locally
have been committed. have been committed.
Sometimes a commit you've made locally will conflict with one made in the "kohana" one. Sometimes a commit you've made locally will conflict with one made in the remote "kohana" repo.
There are a couple of scenarios where this might happen: There are a couple of scenarios where this might happen:
## The conflict is to do with a few unrelated commits and you want to keep changes made in both commits ## The conflict is due to a few unrelated commits and you want to keep changes made in both commits
You'll need to manually modify the files to resolve the conflict, see the "Resolving a merge" You'll need to manually modify the files to resolve the conflict, see the "Resolving a merge"
section [in the git-scm book](http://book.git-scm.com/3_basic_branching_and_merging.html) for more info section [in the Git SCM book](http://book.git-scm.com/3_basic_branching_and_merging.html) for more info
## You've fixed something locally which someone else has already done in the remote repo ## You've fixed something locally which someone else has already done in the remote repo
@ -147,14 +147,14 @@ i.e.
> git rebase -i 57d0b28 > git rebase -i 57d0b28
A text editor will open with a list of commits, delete the line containing the offending commit A text editor will open with a list of commits. Delete the line containing the offending commit
before saving the file & closing your editor. before saving the file & closing your editor.
Git will remove the commit and you can then pull/merge the remote changes. Git will remove the commit and you can then pull/merge the remote changes.
# Unit Testing # Unit Testing
Kohana currently uses phpunit for unit testing. This is installed with composer. Kohana currently uses PHPUnit for unit testing. This is installed with composer.
## How to run the tests ## How to run the tests

View File

@ -131,6 +131,15 @@ Kohana::modules(array(
// 'userguide' => MODPATH.'userguide', // User guide and API documentation // 'userguide' => MODPATH.'userguide', // User guide and API documentation
)); ));
/**
* Cookie Salt
* @see http://kohanaframework.org/3.3/guide/kohana/cookies
*
* If you have not defined a cookie salt in your Cookie class then
* uncomment the line below and define a preferrably long salt.
*/
// Cookie::$salt = NULL;
/** /**
* Set the routes. Each route must have a minimum of a name, a URI and a set of * Set the routes. Each route must have a minimum of a name, a URI and a set of
* defaults for the URI. * defaults for the URI.

View File

@ -1,6 +1,6 @@
{ {
"require": { "require": {
"phpunit/phpunit": "3.7.24", "phpunit/phpunit": "3.7.24 - 4",
"phing/phing": "dev-master" "phing/phing": "dev-master"
} }
} }

37
modules/auth/.travis.yml Normal file
View File

@ -0,0 +1,37 @@
sudo: false
language: php
# Only build the main develop/master branches - feature branches will be covered by PRs
branches:
only:
- /^[0-9\.]+\/(develop|master)$/
cache:
directories:
- vendor
- $HOME/.composer/cache
php:
- 5.3
- 5.4
- 5.5
- 5.6
- 7.0
- hhvm
matrix:
include:
- php: 5.3
env: 'COMPOSER_PHPUNIT="lowest"'
before_script:
- composer install --prefer-dist
- if [ "$COMPOSER_PHPUNIT" = "lowest" ]; then composer update --prefer-lowest --with-dependencies phpunit/phpunit; fi;
- vendor/bin/koharness
script:
- cd /tmp/koharness && ./vendor/bin/phpunit --bootstrap=modules/unittest/bootstrap.php modules/unittest/tests.php
notifications:
email: false

View File

@ -1,5 +1,9 @@
New Age Auth Kohana auth module
--- ---
| ver | Stable | Develop |
|-------|------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------|
| 3.3.x | [![Build Status - 3.3/master](https://travis-ci.org/kohana/auth.svg?branch=3.3%2Fmaster)](https://travis-ci.org/kohana/auth) | [![Build Status - 3.3/develop](https://travis-ci.org/kohana/auth.svg?branch=3.3%2Fdevelop)](https://travis-ci.org/kohana/auth) |
| 3.4.x | [![Build Status - 3.4/master](https://travis-ci.org/kohana/auth.svg?branch=3.4%2Fmaster)](https://travis-ci.org/kohana/auth) | [![Build Status - 3.4/develop](https://travis-ci.org/kohana/auth.svg?branch=3.4%2Fdevelop)](https://travis-ci.org/kohana/auth) |
I've forked the main Auth module because there were some fundamental flaws with it: I've forked the main Auth module because there were some fundamental flaws with it:

View File

@ -24,10 +24,18 @@
"kohana/core": ">=3.3", "kohana/core": ">=3.3",
"php": ">=5.3.3" "php": ">=5.3.3"
}, },
"require-dev": {
"kohana/core": "3.3.*@dev",
"kohana/unittest": "3.3.*@dev",
"kohana/koharness": "*@dev"
},
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-3.3/develop": "3.3.x-dev", "dev-3.3/develop": "3.3.x-dev",
"dev-3.4/develop": "3.4.x-dev" "dev-3.4/develop": "3.4.x-dev"
},
"installer-paths": {
"vendor/{$vendor}/{$name}": ["type:kohana-module"]
} }
} }
} }

View File

@ -0,0 +1,8 @@
<?php
// Configuration for koharness - builds a standalone skeleton Kohana app for running unit tests
return array(
'modules' => array(
'auth' => __DIR__,
'unittest' => __DIR__ . '/vendor/kohana/unittest'
),
);

45
modules/cache/.travis.yml vendored Normal file
View File

@ -0,0 +1,45 @@
sudo: false
language: php
# Only build the main develop/master branches - feature branches will be covered by PRs
branches:
only:
- /^[0-9\.]+\/(develop|master)$/
cache:
directories:
- vendor
- $HOME/.composer/cache
php:
- 5.3
- 5.4
- 5.5
- 5.6
- 7.0
- hhvm
matrix:
include:
- php: 5.3
env: 'COMPOSER_PHPUNIT="lowest"'
services:
- memcached
before_script:
- if [[ $TRAVIS_PHP_VERSION != "hhvm" ]]; then INI_FILE=~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini; else INI_FILE=/etc/hhvm/php.ini; fi;
- if [[ $TRAVIS_PHP_VERSION = 5.* ]]; then echo extension = memcache.so >> $INI_FILE; fi;
- if [[ $TRAVIS_PHP_VERSION = 5.* ]]; then (echo yes | pecl install -f apcu-4.0.10 && echo apc.enable_cli = 1 >> $INI_FILE); fi;
- if [[ $TRAVIS_PHP_VERSION = 7.* ]]; then (echo yes | pecl install -f apcu-5.1.2 && echo apc.enable_cli = 1 >> $INI_FILE); fi;
- if [[ $TRAVIS_PHP_VERSION != "hhvm" ]] ; then echo -e 'apc.max_file_size = 0\napc.cache_by_default = 0' >> $INI_FILE ; fi
- composer install --prefer-dist
- if [ "$COMPOSER_PHPUNIT" = "lowest" ]; then composer update --prefer-lowest --with-dependencies phpunit/phpunit; fi;
- vendor/bin/koharness
script:
- cd /tmp/koharness && ./vendor/bin/phpunit --bootstrap=modules/unittest/bootstrap.php modules/unittest/tests.php
notifications:
email: false

View File

@ -1,6 +1,12 @@
Kohana Cache library Kohana Cache library
==================== ====================
| ver | Stable | Develop |
|-------|--------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------|
| 3.3.x | [![Build Status - 3.3/master](https://travis-ci.org/kohana/cache.svg?branch=3.3%2Fmaster)](https://travis-ci.org/kohana/cache) | [![Build Status - 3.3/develop](https://travis-ci.org/kohana/cache.svg?branch=3.3%2Fdevelop)](https://travis-ci.org/kohana/cache) |
| 3.4.x | [![Build Status - 3.4/master](https://travis-ci.org/kohana/cache.svg?branch=3.4%2Fmaster)](https://travis-ci.org/kohana/cache) | [![Build Status - 3.4/develop](https://travis-ci.org/kohana/cache.svg?branch=3.4%2Fdevelop)](https://travis-ci.org/kohana/cache) |
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. 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 Supported cache solutions

3
modules/cache/classes/Cache/Apcu.php vendored Normal file
View File

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

View File

@ -36,7 +36,7 @@
* Below is an example of a _memcache_ server configuration. * Below is an example of a _memcache_ server configuration.
* *
* return array( * return array(
* 'default' => array( // Default group * 'memcache' => array( // Name of group
* 'driver' => 'memcache', // using Memcache driver * 'driver' => 'memcache', // using Memcache driver
* 'servers' => array( // Available server definitions * 'servers' => array( // Available server definitions
* array( * array(
@ -49,8 +49,12 @@
* ), * ),
* ) * )
* *
* In cases where only one cache group is required, if the group is named `default` there is * In cases where only one cache group is required, set `Cache::$default` (in your bootstrap,
* no need to pass the group name when instantiating a cache instance. * or by extending `Kohana_Cache` class) to the name of the group, and use:
*
* $cache = Cache::instance(); // instead of Cache::instance('memcache')
*
* It will return the cache instance of the group it has been set in `Cache::$default`.
* *
* #### General cache group configuration settings * #### General cache group configuration settings
* *

View File

@ -0,0 +1,174 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* [Kohana Cache](api/Kohana_Cache) APCu data store driver for Kohana Cache
* library.
*
* ### Configuration example
*
* Below is an example of an _apcu_ server configuration.
*
* return array(
* 'apcu' => array( // Driver group
* 'driver' => 'apcu', // using APCu 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
* * APCu PHP extension
*
* @package Kohana/Cache
* @category Base
* @author Kohana Team
* @copyright (c) 2009-2012 Kohana Team
* @license http://kohanaphp.com/license
*/
class Kohana_Cache_Apcu extends Cache implements Cache_Arithmetic {
/**
* Check for existence of the APCu 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('apcu'))
{
throw new Cache_Exception('PHP APCu extension is not available.');
}
parent::__construct($config);
}
/**
* Retrieve a cached value entry by id.
*
* // Retrieve cache entry from apcu group
* $data = Cache::instance('apcu')->get('foo');
*
* // Retrieve cache entry from apcu group and return 'bar' if miss
* $data = Cache::instance('apcu')->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 = apcu_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 apcu group, using default expiry
* Cache::instance('apcu')->set('foo', $data);
*
* // Set 'bar' to 'foo' in apcu group for 30 seconds
* Cache::instance('apcu')->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 apcu_store($this->_sanitize_id($id), $data, $lifetime);
}
/**
* Delete a cache entry based on id
*
* // Delete 'foo' entry from the apcu group
* Cache::instance('apcu')->delete('foo');
*
* @param string $id id to remove from cache
* @return boolean
*/
public function delete($id)
{
return apcu_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 apcu group
* Cache::instance('apcu')->delete_all();
*
* @return boolean
*/
public function delete_all()
{
return apcu_clear_cache();
}
/**
* 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)
{
if (apcu_exists($id)) {
return apcu_inc($id, $step);
} else {
return FALSE;
}
}
/**
* 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)
{
if (apcu_exists($id)) {
return apcu_dec($id, $step);
} else {
return FALSE;
}
}
} // End Kohana_Cache_Apcu

View File

@ -140,17 +140,21 @@ class Kohana_Cache_File extends Cache implements Cache_GarbageCollect {
} }
else else
{ {
// Open the file and parse data // Test the expiry
$created = $file->getMTime(); if ($this->_is_expired($file))
$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!'); // Delete the file
$this->_delete_file($file, FALSE, TRUE);
return $default;
} }
// open the file to read data
$data = $file->openFile();
// Run first fgets(). Cache data starts from the second line
// as the first contains the lifetime timestamp
$data->fgets();
$cache = ''; $cache = '';
while ($data->eof() === FALSE) while ($data->eof() === FALSE)
@ -158,18 +162,8 @@ class Kohana_Cache_File extends Cache implements Cache_GarbageCollect {
$cache .= $data->fgets(); $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); return unserialize($cache);
} }
}
} }
catch (ErrorException $e) catch (ErrorException $e)
@ -219,14 +213,7 @@ class Kohana_Cache_File extends Cache implements Cache_GarbageCollect {
// If the directory path is not a directory // If the directory path is not a directory
if ( ! $dir->isDir()) if ( ! $dir->isDir())
{ {
// Create the directory $this->_make_directory($directory, 0777, TRUE);
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 // Open file to inspect
@ -267,7 +254,7 @@ class Kohana_Cache_File extends Cache implements Cache_GarbageCollect {
$filename = Cache_File::filename($this->_sanitize_id($id)); $filename = Cache_File::filename($this->_sanitize_id($id));
$directory = $this->_resolve_directory($filename); $directory = $this->_resolve_directory($filename);
return $this->_delete_file(new SplFileInfo($directory.$filename), NULL, TRUE); return $this->_delete_file(new SplFileInfo($directory.$filename), FALSE, TRUE);
} }
/** /**
@ -337,9 +324,7 @@ class Kohana_Cache_File extends Cache implements Cache_GarbageCollect {
else else
{ {
// Assess the file expiry to flag it for deletion // Assess the file expiry to flag it for deletion
$json = $file->openFile('r')->current(); $delete = $this->_is_expired($file);
$data = json_decode($json);
$delete = $data->expiry < time();
} }
// If the delete flag is set delete file // If the delete flag is set delete file
@ -375,7 +360,7 @@ class Kohana_Cache_File extends Cache implements Cache_GarbageCollect {
// Create new file resource // Create new file resource
$fp = new SplFileInfo($files->getRealPath()); $fp = new SplFileInfo($files->getRealPath());
// Delete the file // Delete the file
$this->_delete_file($fp); $this->_delete_file($fp, $retain_parent_directory, $ignore_errors, $only_expired);
} }
// Move the file pointer on // Move the file pointer on
@ -446,21 +431,55 @@ class Kohana_Cache_File extends Cache implements Cache_GarbageCollect {
* `mkdir` to ensure DRY principles * `mkdir` to ensure DRY principles
* *
* @link http://php.net/manual/en/function.mkdir.php * @link http://php.net/manual/en/function.mkdir.php
* @param string $directory * @param string $directory directory path
* @param integer $mode * @param integer $mode chmod mode
* @param boolean $recursive * @param boolean $recursive allows nested directories creation
* @param resource $context * @param resource $context a stream context
* @return SplFileInfo * @return SplFileInfo
* @throws Cache_Exception * @throws Cache_Exception
*/ */
protected function _make_directory($directory, $mode = 0777, $recursive = FALSE, $context = NULL) protected function _make_directory($directory, $mode = 0777, $recursive = FALSE, $context = NULL)
{ {
if ( ! mkdir($directory, $mode, $recursive, $context)) // call mkdir according to the availability of a passed $context param
$mkdir_result = $context ?
mkdir($directory, $mode, $recursive, $context) :
mkdir($directory, $mode, $recursive);
// throw an exception if unsuccessful
if ( ! $mkdir_result)
{ {
throw new Cache_Exception('Failed to create the defined cache directory : :directory', array(':directory' => $directory)); throw new Cache_Exception('Failed to create the defined cache directory : :directory', array(':directory' => $directory));
} }
// chmod to solve potential umask issues
chmod($directory, $mode); chmod($directory, $mode);
return new SplFileInfo($directory); return new SplFileInfo($directory);
} }
/**
* Test if cache file is expired
*
* @param SplFileInfo $file the cache file
* @return boolean TRUE if expired false otherwise
*/
protected function _is_expired(SplFileInfo $file)
{
// Open the file and parse data
$created = $file->getMTime();
$data = $file->openFile("r");
$lifetime = (int) $data->fgets();
// If we're at the EOF at this point, corrupted!
if ($data->eof())
{
throw new Cache_Exception(__METHOD__ . ' corrupted cache file!');
}
//close file
$data = null;
// test for expiry and return
return (($lifetime !== 0) AND ( ($created + $lifetime) < time()));
}
} }

View File

@ -1,6 +1,6 @@
<?php defined('SYSPATH') or die('No direct script access.'); <?php defined('SYSPATH') or die('No direct script access.');
/** /**
* HTTT Caching adaptor class that provides caching services to the * HTTP Caching adaptor class that provides caching services to the
* [Request_Client] class, using HTTP cache control logic as defined in * [Request_Client] class, using HTTP cache control logic as defined in
* RFC 2616. * RFC 2616.
* *
@ -36,7 +36,7 @@ class Kohana_HTTP_Cache {
* ) * )
* ); * );
* *
* @uses [Cache] * @uses Cache
* @param mixed $cache cache engine to use * @param mixed $cache cache engine to use
* @param array $options options to set to this class * @param array $options options to set to this class
* @return HTTP_Cache * @return HTTP_Cache
@ -298,7 +298,7 @@ class Kohana_HTTP_Cache {
* Controls whether the response can be cached. Uses HTTP * Controls whether the response can be cached. Uses HTTP
* protocol to determine whether the response can be cached. * protocol to determine whether the response can be cached.
* *
* @link RFC 2616 http://www.w3.org/Protocols/rfc2616/ * @link http://www.w3.org/Protocols/rfc2616/rfc2616.html RFC 2616
* @param Response $response The Response * @param Response $response The Response
* @return boolean * @return boolean
*/ */

View File

@ -24,10 +24,18 @@
"kohana/core": ">=3.3", "kohana/core": ">=3.3",
"php": ">=5.3.3" "php": ">=5.3.3"
}, },
"require-dev": {
"kohana/core": "3.3.*@dev",
"kohana/unittest": "3.3.*@dev",
"kohana/koharness": "*@dev"
},
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-3.3/develop": "3.3.x-dev", "dev-3.3/develop": "3.3.x-dev",
"dev-3.4/develop": "3.4.x-dev" "dev-3.4/develop": "3.4.x-dev"
},
"installer-paths": {
"vendor/{$vendor}/{$name}": ["type:kohana-module"]
} }
} }
} }

View File

@ -55,7 +55,7 @@ failure_callback | __NO__ | (_[callback](http://www.php.net/manual/en/language
'driver' => 'memcache', 'driver' => 'memcache',
'default_expire' => 3600, 'default_expire' => 3600,
'compression' => FALSE, // Use Zlib compression 'compression' => FALSE, // Use Zlib compression
(can cause issues with integers) // (can cause issues with integers)
'servers' => array 'servers' => array
( (
'local' => array 'local' => array
@ -71,7 +71,7 @@ failure_callback | __NO__ | (_[callback](http://www.php.net/manual/en/language
'driver' => 'memcachetag', 'driver' => 'memcachetag',
'default_expire' => 3600, 'default_expire' => 3600,
'compression' => FALSE, // Use Zlib compression 'compression' => FALSE, // Use Zlib compression
(can cause issues with integers) // (can cause issues with integers)
'servers' => array 'servers' => array
( (
'local' => array 'local' => array
@ -91,6 +91,14 @@ failure_callback | __NO__ | (_[callback](http://www.php.net/manual/en/language
'default_expire' => 3600, 'default_expire' => 3600,
), ),
## APCu settings
'apcu' => array
(
'driver' => 'apcu',
'default_expire' => 3600,
),
## SQLite settings ## SQLite settings
'sqlite' => array 'sqlite' => array

View File

@ -6,7 +6,7 @@ instances of cache engines through a grouped singleton pattern.
## Supported cache engines ## Supported cache engines
* APC ([Cache_Apc]) * APC/APCu ([Cache_Apc])
* File ([Cache_File]) * File ([Cache_File])
* Memcached ([Cache_Memcache]) * Memcached ([Cache_Memcache])
* Memcached-tags ([Cache_Memcachetag]) * Memcached-tags ([Cache_Memcachetag])
@ -16,13 +16,13 @@ instances of cache engines through a grouped singleton pattern.
## Introduction to caching ## Introduction to caching
Caching should be implemented with consideration. Generally, caching the result of resources 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 is faster than reprocessing them. Choosing what, how and when to cache is vital. [PHP APCu](http://php.net/manual/en/book.apcu.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. a complex set of instructions.
Caching engines that use memory are considerably faster than file based alternatives. But 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. 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. [!!] 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) ## What the Kohana Cache module does (and does not do)
@ -43,7 +43,7 @@ Getting and setting values to cache is very simple when using the _Kohana Cache_
Driver | Storage | Speed | Tags | Distributed | Automatic Garbage Collection | Notes 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 APC/APCu | __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 Wincache | __Memory__ | Excellent | No | No | Yes | Windows variant of APC
File | __Disk__ | Poor | No | No | No | Marginally faster than execution 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 Memcache (tag) | __Memory__ | Good | No (yes) | Yes | Yes | Generally fast distributed solution, but has a speed hit due to variable network latency and serialization

View File

@ -9,7 +9,7 @@ Creating a new _Kohana Cache_ instance is simple, however it must be done using
// Create a new instance of cache using the default group // Create a new instance of cache using the default group
$cache = Cache::instance(); $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. The default group will use whatever is set to [Cache::$default] and must have a corresponding [configuration](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. To create a cache instance using a group other than the _default_, simply provide the group name as an argument.
@ -18,7 +18,7 @@ To create a cache instance using a group other than the _default_, simply provid
If there is a cache instance already instantiated then you can get it directly from the class member. 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. [!!] 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 // Check for the existance of the cache driver
if (isset(Cache::$instances['memcache'])) if (isset(Cache::$instances['memcache']))
@ -36,11 +36,11 @@ If there is a cache instance already instantiated then you can get it directly f
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. 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. [!!] Serialisation does not work with resource handles, such as filesystem, curl or socket resources.
### Setting a value to cache ### 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. 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. The first example demonstrates how to quickly load and set a value to the default cache instance.
@ -104,7 +104,7 @@ In cases where the requested key is not available or the entry has expired, a de
It is possible to retrieve values from cache grouped by tag, using the [Cache::find] method with drivers that support tagging. 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. [!!] The __Memcachetag__ driver does not support the `Cache::find($tag)` interface and will throw an exception.
// Get an instance of cache // Get an instance of cache
$cache = Cache::instance('memcachetag'); $cache = Cache::instance('memcachetag');

9
modules/cache/koharness.php vendored Normal file
View File

@ -0,0 +1,9 @@
<?php
// Configuration for koharness - builds a standalone skeleton Kohana app for running unit tests
return array(
'kohana_version' => '3.3',
'modules' => array(
'cache' => __DIR__,
'unittest' => __DIR__ . '/vendor/kohana/unittest'
),
);

View File

@ -164,6 +164,17 @@ TESTTEXT;
), ),
'foo bar snafu' 'foo bar snafu'
), ),
array(
array(
'id' => 'test ttl 0 means never expire',
'value' => 'cache value that should last',
'ttl' => 0,
'wait' => 1,
'type' => 'string',
'default' => NULL
),
'cache value that should last'
),
array( array(
array( array(
'id' => 'bar', 'id' => 'bar',
@ -196,6 +207,28 @@ TESTTEXT;
'default' => NULL, 'default' => NULL,
), ),
$html_text $html_text
),
array(
array(
'id' => 'test with 60*5',
'value' => 'blabla',
'ttl' => 60*5,
'wait' => FALSE,
'type' => 'string',
'default' => NULL,
),
'blabla'
),
array(
array(
'id' => 'test with 60*50',
'value' => 'bla bla',
'ttl' => 60*50,
'wait' => FALSE,
'type' => 'string',
'default' => NULL,
),
'bla bla'
) )
); );
} }

View File

@ -90,14 +90,13 @@ class Kohana_CacheTest extends PHPUnit_Framework_TestCase {
*/ */
public function test_cloning_fails() public function test_cloning_fails()
{ {
if ( ! Kohana::$config->load('cache.file')) $cache = $this->getMockBuilder('Cache')
{ ->disableOriginalConstructor()
$this->markTestSkipped('Unable to load File configuration'); ->getMockForAbstractClass();
}
try try
{ {
$cache_clone = clone(Cache::instance('file')); clone($cache);
} }
catch (Cache_Exception $e) catch (Cache_Exception $e)
{ {

View File

@ -30,7 +30,21 @@ class Kohana_Cache_FileTest extends Kohana_CacheBasicMethodsTest {
if ( ! Kohana::$config->load('cache.file')) if ( ! Kohana::$config->load('cache.file'))
{ {
$this->markTestSkipped('Unable to load File configuration'); Kohana::$config->load('cache')
->set(
'file',
array(
'driver' => 'file',
'cache_dir' => APPPATH.'cache',
'default_expire' => 3600,
'ignore_on_delete' => array(
'file_we_want_to_keep.cache',
'.gitignore',
'.git',
'.svn'
)
)
);
} }
$this->cache(Cache::instance('file')); $this->cache(Cache::instance('file'));
@ -45,7 +59,7 @@ class Kohana_Cache_FileTest extends Kohana_CacheBasicMethodsTest {
{ {
$cache = $this->cache(); $cache = $this->cache();
$config = Kohana::$config->load('cache')->file; $config = Kohana::$config->load('cache')->file;
$file = $config['cache_dir'].'/.gitignore'; $file = $config['cache_dir'].'/file_we_want_to_keep.cache';
// Lets pollute the cache folder // Lets pollute the cache folder
file_put_contents($file, 'foobar'); file_put_contents($file, 'foobar');
@ -95,4 +109,55 @@ class Kohana_Cache_FileTest extends Kohana_CacheBasicMethodsTest {
$this->assertSame($expected, $cache->get('utf8')); $this->assertSame($expected, $cache->get('utf8'));
} }
/**
* Tests garbage collection.
* Tests if non-expired cache files withstand garbage collection
*
* @test
*/
public function test_garbage_collection()
{
$cache = $this->cache();
$cache->set('persistent', 'dummy persistent data', 3);
$cache->set('volatile', 'dummy volatile data', 1);
$this->assertTrue($this->is_file('persistent'));
$this->assertTrue($this->is_file('volatile'));
// sleep for more than a second
sleep(2);
$cache->garbage_collect();
$this->assertTrue($this->is_file('persistent'));
$this->assertFalse($this->is_file('volatile'));
}
/**
* helper method for test_garbage_collection.
* Tests if cache file exists given cache id.
*
* @param string $id cache id
* @return boolean TRUE if file exists FALSE otherwise
*/
protected function is_file($id)
{
$cache = $this->cache();
$method_sanitize_id = new ReflectionMethod($cache, '_sanitize_id');
$method_sanitize_id->setAccessible(TRUE);
$method_filename = new ReflectionMethod($cache, 'filename');
$method_filename->setAccessible(TRUE);
$method_resolve_directory = new ReflectionMethod($cache, '_resolve_directory');
$method_resolve_directory->setAccessible(TRUE);
$sanitized_id = $method_sanitize_id->invoke($cache, $id);
$filename = $method_filename->invoke($cache, $sanitized_id);
$directory = $method_resolve_directory->invoke($cache, $filename);
$file = new SplFileInfo($directory.$filename);
//var_dump($cache->_is_expired($file));
return $file->isFile();
}
} // End Kohana_SqliteTest } // End Kohana_SqliteTest

View File

@ -35,7 +35,16 @@ class Kohana_SqliteTest extends Kohana_CacheBasicMethodsTest {
if ( ! Kohana::$config->load('cache.sqlite')) if ( ! Kohana::$config->load('cache.sqlite'))
{ {
$this->markTestIncomplete('Unable to load sqlite configuration'); Kohana::$config->load('cache')
->set(
'sqlite',
array(
'driver' => 'sqlite',
'default_expire' => 3600,
'database' => 'memory',
'schema' => 'CREATE TABLE caches(id VARCHAR(127) PRIMARY KEY, tags VARCHAR(255), expiration INTEGER, cache TEXT)',
)
);
} }
$this->cache(Cache::instance('sqlite')); $this->cache(Cache::instance('sqlite'));

View File

@ -1,7 +1,16 @@
<?php <?php
include_once(Kohana::find_file('tests/cache', 'CacheBasicMethodsTest')); if (isset($_ENV['TRAVIS']))
{
// This is really hacky, but without it the result is permanently full of noise that makes it impossible to see
// any unexpected skipped tests.
print "Skipping all Wincache driver tests as these will never run on Travis.".\PHP_EOL;
return;
}
else
{
include_once(Kohana::find_file('tests/cache', 'CacheBasicMethodsTest'));
/** /**
* @package Kohana/Cache * @package Kohana/Cache
* @group kohana * @group kohana
* @group kohana.cache * @group kohana.cache
@ -10,7 +19,7 @@ include_once(Kohana::find_file('tests/cache', 'CacheBasicMethodsTest'));
* @copyright (c) 2009-2012 Kohana Team * @copyright (c) 2009-2012 Kohana Team
* @license http://kohanaphp.com/license * @license http://kohanaphp.com/license
*/ */
class Kohana_WincacheTest extends Kohana_CacheBasicMethodsTest { class Kohana_WincacheTest extends Kohana_CacheBasicMethodsTest {
/** /**
* This method MUST be implemented by each driver to setup the `Cache` * This method MUST be implemented by each driver to setup the `Cache`
@ -36,4 +45,5 @@ class Kohana_WincacheTest extends Kohana_CacheBasicMethodsTest {
$this->cache(Cache::instance('wincache')); $this->cache(Cache::instance('wincache'));
} }
} // End Kohana_WincacheTest } // End Kohana_WincacheTest
}

View File

@ -39,6 +39,18 @@ class Kohana_ApcTest extends Kohana_CacheArithmeticMethodsTest {
'place "apc.enable_cli=1" in your php.ini file'); 'place "apc.enable_cli=1" in your php.ini file');
} }
if ( ! Kohana::$config->load('cache.apc'))
{
Kohana::$config->load('cache')
->set(
'apc',
array(
'driver' => 'apc',
'default_expire' => 3600,
)
);
}
$this->cache(Cache::instance('apc')); $this->cache(Cache::instance('apc'));
} }

View File

@ -0,0 +1,87 @@
<?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_ApcuTest 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('apcu'))
{
$this->markTestSkipped('APCu PHP Extension is not available');
}
if ( ! (ini_get('apc.enabled') AND ini_get('apc.enable_cli')))
{
$this->markTestSkipped('APCu is not enabled. To fix '.
'set "apc.enabled=1" and "apc.enable_cli=1" in your php.ini file');
}
if ( ! Kohana::$config->load('cache.apcu'))
{
Kohana::$config->load('cache')
->set(
'apcu',
array(
'driver' => 'apcu',
'default_expire' => 3600,
)
);
}
$this->cache(Cache::instance('apcu'));
}
/**
* 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 APCu 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_ApcuTest

View File

@ -35,7 +35,28 @@ class Kohana_CacheArithmeticMemcacheTest extends Kohana_CacheArithmeticMethodsTe
} }
if ( ! $config = Kohana::$config->load('cache.memcache')) if ( ! $config = Kohana::$config->load('cache.memcache'))
{ {
$this->markTestSkipped('Unable to load Memcache configuration'); Kohana::$config->load('cache')
->set(
'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,
)
);
$config = Kohana::$config->load('cache.memcache');
} }
$memcache = new Memcache; $memcache = new Memcache;

View File

@ -0,0 +1,37 @@
sudo: false
language: php
# Only build the main develop/master branches - feature branches will be covered by PRs
branches:
only:
- /^[0-9\.]+\/(develop|master)$/
cache:
directories:
- vendor
- $HOME/.composer/cache
php:
- 5.3
- 5.4
- 5.5
- 5.6
- 7.0
- hhvm
matrix:
include:
- php: 5.3
env: 'COMPOSER_PHPUNIT="lowest"'
before_script:
- composer install --prefer-dist
- if [ "$COMPOSER_PHPUNIT" = "lowest" ]; then composer update --prefer-lowest --with-dependencies phpunit/phpunit; fi;
- vendor/bin/koharness
script:
- cd /tmp/koharness && ./vendor/bin/phpunit --bootstrap=modules/unittest/bootstrap.php modules/unittest/tests.php
notifications:
email: false

View File

@ -24,10 +24,18 @@
"kohana/core": ">=3.3", "kohana/core": ">=3.3",
"php": ">=5.3.3" "php": ">=5.3.3"
}, },
"require-dev": {
"kohana/core": "3.3.*@dev",
"kohana/unittest": "3.3.*@dev",
"kohana/koharness": "*@dev"
},
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-3.3/develop": "3.3.x-dev", "dev-3.3/develop": "3.3.x-dev",
"dev-3.4/develop": "3.4.x-dev" "dev-3.4/develop": "3.4.x-dev"
},
"installer-paths": {
"vendor/{$vendor}/{$name}": ["type:kohana-module"]
} }
} }
} }

View File

@ -26,7 +26,7 @@ Throwing valuable benchmark data away every time I needed to optimize another re
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. 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). 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.kohanaframework.org/discussion/2192).
![Benchmarking several ways to validate HTML color values](codebench_screenshot1.png) ![Benchmarking several ways to validate HTML color values](codebench_screenshot1.png)
**Benchmarking seven ways to validate HTML color values** **Benchmarking seven ways to validate HTML color values**

View File

@ -0,0 +1,8 @@
<?php
// Configuration for koharness - builds a standalone skeleton Kohana app for running unit tests
return array(
'modules' => array(
'codebench' => __DIR__,
'unittest' => __DIR__ . '/vendor/kohana/unittest'
),
);

View File

@ -0,0 +1,37 @@
sudo: false
language: php
# Only build the main develop/master branches - feature branches will be covered by PRs
branches:
only:
- /^[0-9\.]+\/(develop|master)$/
cache:
directories:
- vendor
- $HOME/.composer/cache
php:
- 5.3
- 5.4
- 5.5
- 5.6
- 7.0
- hhvm
matrix:
include:
- php: 5.3
env: 'COMPOSER_PHPUNIT="lowest"'
before_script:
- composer install --prefer-dist
- if [ "$COMPOSER_PHPUNIT" = "lowest" ]; then composer update --prefer-lowest --with-dependencies phpunit/phpunit; fi;
- vendor/bin/koharness
script:
- cd /tmp/koharness && ./vendor/bin/phpunit --bootstrap=modules/unittest/bootstrap.php modules/unittest/tests.php
notifications:
email: false

View File

@ -0,0 +1,6 @@
# Kohana - database access module
| ver | Stable | Develop |
|-------|--------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------|
| 3.3.x | [![Build Status - 3.3/master](https://travis-ci.org/kohana/database.svg?branch=3.3%2Fmaster)](https://travis-ci.org/kohana/database) | [![Build Status - 3.3/develop](https://travis-ci.org/kohana/database.svg?branch=3.3%2Fdevelop)](https://travis-ci.org/kohana/database) |
| 3.4.x | [![Build Status - 3.4/master](https://travis-ci.org/kohana/database.svg?branch=3.4%2Fmaster)](https://travis-ci.org/kohana/database) | [![Build Status - 3.4/develop](https://travis-ci.org/kohana/database.svg?branch=3.4%2Fdevelop)](https://travis-ci.org/kohana/database) |

View File

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

View File

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

View File

@ -9,7 +9,7 @@
* @copyright (c) 2012 Kohana Team * @copyright (c) 2012 Kohana Team
* @license http://kohanaframework.org/license * @license http://kohanaframework.org/license
*/ */
class Kohana_Config_Database extends Kohana_Config_Database_Writer class Kohana_Config_Database extends Config_Database_Writer
{ {
} }

View File

@ -3,6 +3,15 @@
/** /**
* Database writer for the config system * Database writer for the config system
* *
* Schema for configuration table:
*
* CREATE TABLE IF NOT EXISTS `config` (
* `group_name` varchar(128) NOT NULL,
* `config_key` varchar(128) NOT NULL,
* `config_value` text,
* PRIMARY KEY (`group_name`,`config_key`)
* ) ENGINE=InnoDB;
*
* @package Kohana * @package Kohana
* @category Configuration * @category Configuration
* @author Kohana Team * @author Kohana Team

View File

@ -51,6 +51,9 @@ class Kohana_Database_MySQL_Result extends Database_Result {
// Increment internal row for optimization assuming rows are fetched in order // Increment internal row for optimization assuming rows are fetched in order
$this->_internal_row++; $this->_internal_row++;
// FIXME mysql_fetch_object has been deprecated as of php 5.5!
// Please use mysqli_fetch_object or PDOStatement::fetch(PDO::FETCH_OBJ) instead.
if ($this->_as_object === TRUE) if ($this->_as_object === TRUE)
{ {
// Return an stdClass // Return an stdClass
@ -58,10 +61,34 @@ class Kohana_Database_MySQL_Result extends Database_Result {
} }
elseif (is_string($this->_as_object)) elseif (is_string($this->_as_object))
{ {
// Return an object of given class name /* The second and third argument for mysql_fetch_object are optional, but do
* not have default values defined. Passing _object_params with a non-array value results
* in undefined behavior that varies by PHP version. For example, if NULL is supplied on
* PHP 5.3, the resulting behavior is identical to calling with array(), which results in the
* classes __construct function being called with no arguments. This is only an issue when
* the _as_object class does not have an explicit __construct method resulting in the
* cryptic error "Class %s does not have a constructor hence you cannot use ctor_params."
* In contrast, the same function call on PHP 5.5 will 'functionally' interpret
* _object_params == NULL as an omission of the third argument, resulting in the original
* intended functionally.
*
* Because the backing code for the mysql_fetch_object has not changed between 5.3 and 5.5,
* I suspect this discrepancy is due to the way the classes are instantiated on a boarder
* level. Additionally, mysql_fetch_object has been deprecated in 5.5 and should probably be
* replaced by mysqli_fetch_object or PDOStatement::fetch(PDO::FETCH_OBJ) in Kohana 3.4.
*/
if ($this->_object_params !== NULL)
{
// Return an object of given class name with constructor params
return mysql_fetch_object($this->_result, $this->_as_object, $this->_object_params); return mysql_fetch_object($this->_result, $this->_as_object, $this->_object_params);
} }
else else
{
// Return an object of given class name without constructor params
return mysql_fetch_object($this->_result, $this->_as_object);
}
}
else
{ {
// Return an array of the row // Return an array of the row
return mysql_fetch_assoc($this->_result); return mysql_fetch_assoc($this->_result);

View File

@ -0,0 +1,422 @@
<?php defined('SYSPATH') OR die('No direct script access.');
/**
* MySQLi database connection.
*
* @package Kohana/Database
* @category Drivers
* @author Kohana Team
* @copyright (c) 2008-2009 Kohana Team
* @license http://kohanaphp.com/license
*/
class Kohana_Database_MySQLi extends Database {
// Database in use by each connection
protected static $_current_databases = array();
// Use SET NAMES to set the character set
protected static $_set_names;
// Identifier for this connection within the PHP driver
protected $_connection_id;
// MySQL uses a backtick for identifiers
protected $_identifier = '`';
public function connect()
{
if ($this->_connection)
return;
if (Database_MySQLi::$_set_names === NULL)
{
// Determine if we can use mysqli_set_charset(), which is only
// available on PHP 5.2.3+ when compiled against MySQL 5.0+
Database_MySQLi::$_set_names = ! function_exists('mysqli_set_charset');
}
// Extract the connection parameters, adding required variabels
extract($this->_config['connection'] + array(
'database' => '',
'hostname' => '',
'username' => '',
'password' => '',
'socket' => '',
'port' => 3306,
'ssl' => NULL,
));
// Prevent this information from showing up in traces
unset($this->_config['connection']['username'], $this->_config['connection']['password']);
try
{
if(is_array($ssl))
{
$this->_connection = mysqli_init();
$this->_connection->ssl_set(
Arr::get($ssl, 'client_key_path'),
Arr::get($ssl, 'client_cert_path'),
Arr::get($ssl, 'ca_cert_path'),
Arr::get($ssl, 'ca_dir_path'),
Arr::get($ssl, 'cipher')
);
$this->_connection->real_connect($hostname, $username, $password, $database, $port, $socket, MYSQLI_CLIENT_SSL);
}
else
{
$this->_connection = new mysqli($hostname, $username, $password, $database, $port, $socket);
}
}
catch (Exception $e)
{
// No connection exists
$this->_connection = NULL;
throw new Database_Exception(':error', array(':error' => $e->getMessage()), $e->getCode());
}
// \xFF is a better delimiter, but the PHP driver uses underscore
$this->_connection_id = sha1($hostname.'_'.$username.'_'.$password);
if ( ! empty($this->_config['charset']))
{
// Set the character set
$this->set_charset($this->_config['charset']);
}
if ( ! empty($this->_config['connection']['variables']))
{
// Set session variables
$variables = array();
foreach ($this->_config['connection']['variables'] as $var => $val)
{
$variables[] = 'SESSION '.$var.' = '.$this->quote($val);
}
$this->_connection->query('SET '.implode(', ', $variables));
}
}
public function disconnect()
{
try
{
// Database is assumed disconnected
$status = TRUE;
if (is_resource($this->_connection))
{
if ($status = $this->_connection->close())
{
// Clear the connection
$this->_connection = NULL;
// Clear the instance
parent::disconnect();
}
}
}
catch (Exception $e)
{
// Database is probably not disconnected
$status = ! is_resource($this->_connection);
}
return $status;
}
public function set_charset($charset)
{
// Make sure the database is connected
$this->_connection or $this->connect();
if (Database_MySQLi::$_set_names === TRUE)
{
// PHP is compiled against MySQL 4.x
$status = (bool) $this->_connection->query('SET NAMES '.$this->quote($charset));
}
else
{
// PHP is compiled against MySQL 5.x
$status = $this->_connection->set_charset($charset);
}
if ($status === FALSE)
{
throw new Database_Exception(':error', array(':error' => $this->_connection->error), $this->_connection->errno);
}
}
public function query($type, $sql, $as_object = FALSE, array $params = NULL)
{
// Make sure the database is connected
$this->_connection or $this->connect();
if (Kohana::$profiling)
{
// Benchmark this query for the current instance
$benchmark = Profiler::start("Database ({$this->_instance})", $sql);
}
// Execute the query
if (($result = $this->_connection->query($sql)) === FALSE)
{
if (isset($benchmark))
{
// This benchmark is worthless
Profiler::delete($benchmark);
}
throw new Database_Exception(':error [ :query ]', array(
':error' => $this->_connection->error,
':query' => $sql
), $this->_connection->errno);
}
if (isset($benchmark))
{
Profiler::stop($benchmark);
}
// Set the last query
$this->last_query = $sql;
if ($type === Database::SELECT)
{
// Return an iterator of results
return new Database_MySQLi_Result($result, $sql, $as_object, $params);
}
elseif ($type === Database::INSERT)
{
// Return a list of insert id and rows created
return array(
$this->_connection->insert_id,
$this->_connection->affected_rows,
);
}
else
{
// Return the number of rows affected
return $this->_connection->affected_rows;
}
}
public function datatype($type)
{
static $types = array
(
'blob' => array('type' => 'string', 'binary' => TRUE, 'character_maximum_length' => '65535'),
'bool' => array('type' => 'bool'),
'bigint unsigned' => array('type' => 'int', 'min' => '0', 'max' => '18446744073709551615'),
'datetime' => array('type' => 'string'),
'decimal unsigned' => array('type' => 'float', 'exact' => TRUE, 'min' => '0'),
'double' => array('type' => 'float'),
'double precision unsigned' => array('type' => 'float', 'min' => '0'),
'double unsigned' => array('type' => 'float', 'min' => '0'),
'enum' => array('type' => 'string'),
'fixed' => array('type' => 'float', 'exact' => TRUE),
'fixed unsigned' => array('type' => 'float', 'exact' => TRUE, 'min' => '0'),
'float unsigned' => array('type' => 'float', 'min' => '0'),
'geometry' => array('type' => 'string', 'binary' => TRUE),
'int unsigned' => array('type' => 'int', 'min' => '0', 'max' => '4294967295'),
'integer unsigned' => array('type' => 'int', 'min' => '0', 'max' => '4294967295'),
'longblob' => array('type' => 'string', 'binary' => TRUE, 'character_maximum_length' => '4294967295'),
'longtext' => array('type' => 'string', 'character_maximum_length' => '4294967295'),
'mediumblob' => array('type' => 'string', 'binary' => TRUE, 'character_maximum_length' => '16777215'),
'mediumint' => array('type' => 'int', 'min' => '-8388608', 'max' => '8388607'),
'mediumint unsigned' => array('type' => 'int', 'min' => '0', 'max' => '16777215'),
'mediumtext' => array('type' => 'string', 'character_maximum_length' => '16777215'),
'national varchar' => array('type' => 'string'),
'numeric unsigned' => array('type' => 'float', 'exact' => TRUE, 'min' => '0'),
'nvarchar' => array('type' => 'string'),
'point' => array('type' => 'string', 'binary' => TRUE),
'real unsigned' => array('type' => 'float', 'min' => '0'),
'set' => array('type' => 'string'),
'smallint unsigned' => array('type' => 'int', 'min' => '0', 'max' => '65535'),
'text' => array('type' => 'string', 'character_maximum_length' => '65535'),
'tinyblob' => array('type' => 'string', 'binary' => TRUE, 'character_maximum_length' => '255'),
'tinyint' => array('type' => 'int', 'min' => '-128', 'max' => '127'),
'tinyint unsigned' => array('type' => 'int', 'min' => '0', 'max' => '255'),
'tinytext' => array('type' => 'string', 'character_maximum_length' => '255'),
'year' => array('type' => 'string'),
);
$type = str_replace(' zerofill', '', $type);
if (isset($types[$type]))
return $types[$type];
return parent::datatype($type);
}
/**
* Start a SQL transaction
*
* @link http://dev.mysql.com/doc/refman/5.0/en/set-transaction.html
*
* @param string $mode Isolation level
* @return boolean
*/
public function begin($mode = NULL)
{
// Make sure the database is connected
$this->_connection or $this->connect();
if ($mode AND ! $this->_connection->query("SET TRANSACTION ISOLATION LEVEL $mode"))
{
throw new Database_Exception(':error', array(
':error' => $this->_connection->error
), $this->_connection->errno);
}
return (bool) $this->_connection->query('START TRANSACTION');
}
/**
* Commit a SQL transaction
*
* @return boolean
*/
public function commit()
{
// Make sure the database is connected
$this->_connection or $this->connect();
return (bool) $this->_connection->query('COMMIT');
}
/**
* Rollback a SQL transaction
*
* @return boolean
*/
public function rollback()
{
// Make sure the database is connected
$this->_connection or $this->connect();
return (bool) $this->_connection->query('ROLLBACK');
}
public function list_tables($like = NULL)
{
if (is_string($like))
{
// Search for table names
$result = $this->query(Database::SELECT, 'SHOW TABLES LIKE '.$this->quote($like), FALSE);
}
else
{
// Find all table names
$result = $this->query(Database::SELECT, 'SHOW TABLES', FALSE);
}
$tables = array();
foreach ($result as $row)
{
$tables[] = reset($row);
}
return $tables;
}
public function list_columns($table, $like = NULL, $add_prefix = TRUE)
{
// Quote the table name
$table = ($add_prefix === TRUE) ? $this->quote_table($table) : $table;
if (is_string($like))
{
// Search for column names
$result = $this->query(Database::SELECT, 'SHOW FULL COLUMNS FROM '.$table.' LIKE '.$this->quote($like), FALSE);
}
else
{
// Find all column names
$result = $this->query(Database::SELECT, 'SHOW FULL COLUMNS FROM '.$table, FALSE);
}
$count = 0;
$columns = array();
foreach ($result as $row)
{
list($type, $length) = $this->_parse_type($row['Type']);
$column = $this->datatype($type);
$column['column_name'] = $row['Field'];
$column['column_default'] = $row['Default'];
$column['data_type'] = $type;
$column['is_nullable'] = ($row['Null'] == 'YES');
$column['ordinal_position'] = ++$count;
switch ($column['type'])
{
case 'float':
if (isset($length))
{
list($column['numeric_precision'], $column['numeric_scale']) = explode(',', $length);
}
break;
case 'int':
if (isset($length))
{
// MySQL attribute
$column['display'] = $length;
}
break;
case 'string':
switch ($column['data_type'])
{
case 'binary':
case 'varbinary':
$column['character_maximum_length'] = $length;
break;
case 'char':
case 'varchar':
$column['character_maximum_length'] = $length;
case 'text':
case 'tinytext':
case 'mediumtext':
case 'longtext':
$column['collation_name'] = $row['Collation'];
break;
case 'enum':
case 'set':
$column['collation_name'] = $row['Collation'];
$column['options'] = explode('\',\'', substr($length, 1, -1));
break;
}
break;
}
// MySQL attributes
$column['comment'] = $row['Comment'];
$column['extra'] = $row['Extra'];
$column['key'] = $row['Key'];
$column['privileges'] = $row['Privileges'];
$columns[$row['Field']] = $column;
}
return $columns;
}
public function escape($value)
{
// Make sure the database is connected
$this->_connection or $this->connect();
if (($value = $this->_connection->real_escape_string( (string) $value)) === FALSE)
{
throw new Database_Exception(':error', array(
':error' => $this->_connection->error,
), $this->_connection->errno);
}
// SQL standard is to use single-quotes for all values
return "'$value'";
}
} // End Database_MySQLi

View File

@ -0,0 +1,71 @@
<?php defined('SYSPATH') OR die('No direct script access.');
/**
* MySQLi database result. See [Results](/database/results) for usage and examples.
*
* @package Kohana/Database
* @category Query/Result
* @author Kohana Team
* @copyright (c) 2008-2009 Kohana Team
* @license http://kohanaphp.com/license
*/
class Kohana_Database_MySQLi_Result extends Database_Result {
protected $_internal_row = 0;
public function __construct($result, $sql, $as_object = FALSE, array $params = NULL)
{
parent::__construct($result, $sql, $as_object, $params);
// Find the number of rows in the result
$this->_total_rows = $result->num_rows;
}
public function __destruct()
{
if (is_resource($this->_result))
{
$this->_result->free();
}
}
public function seek($offset)
{
if ($this->offsetExists($offset) AND $this->_result->data_seek($offset))
{
// Set the current row to the offset
$this->_current_row = $this->_internal_row = $offset;
return TRUE;
}
else
{
return FALSE;
}
}
public function current()
{
if ($this->_current_row !== $this->_internal_row AND ! $this->seek($this->_current_row))
return NULL;
// Increment internal row for optimization assuming rows are fetched in order
$this->_internal_row++;
if ($this->_as_object === TRUE)
{
// Return an stdClass
return $this->_result->fetch_object();
}
elseif (is_string($this->_as_object))
{
// Return an object of given class name
return $this->_result->fetch_object($this->_as_object, (array) $this->_object_params);
}
else
{
// Return an array of the row
return $this->_result->fetch_assoc();
}
}
} // End Database_MySQLi_Result_Select

View File

@ -60,6 +60,12 @@ class Kohana_Database_PDO extends Database {
array(':error' => $e->getMessage()), array(':error' => $e->getMessage()),
$e->getCode()); $e->getCode());
} }
if ( ! empty($this->_config['charset']))
{
// Set the character set
$this->set_charset($this->_config['charset']);
}
} }
/** /**

View File

@ -80,7 +80,7 @@ abstract class Kohana_Database_Query_Builder extends Database_Query {
// Convert "val = NULL" to "val IS NULL" // Convert "val = NULL" to "val IS NULL"
$op = 'IS'; $op = 'IS';
} }
elseif ($op === '!=') elseif ($op === '!=' OR $op === '<>')
{ {
// Convert "val != NULL" to "valu IS NOT NULL" // Convert "val != NULL" to "valu IS NOT NULL"
$op = 'IS NOT'; $op = 'IS NOT';

View File

@ -31,7 +31,7 @@ class Kohana_Database_Query_Builder_Insert extends Database_Query_Builder {
if ($table) if ($table)
{ {
// Set the inital table name // Set the inital table name
$this->_table = $table; $this->table($table);
} }
if ($columns) if ($columns)
@ -47,11 +47,14 @@ class Kohana_Database_Query_Builder_Insert extends Database_Query_Builder {
/** /**
* Sets the table to insert into. * Sets the table to insert into.
* *
* @param mixed $table table name or array($table, $alias) or object * @param string $table table name
* @return $this * @return $this
*/ */
public function table($table) public function table($table)
{ {
if ( ! is_string($table))
throw new Kohana_Exception('INSERT INTO syntax does not allow table aliasing');
$this->_table = $table; $this->_table = $table;
return $this; return $this;
@ -87,7 +90,10 @@ class Kohana_Database_Query_Builder_Insert extends Database_Query_Builder {
// Get all of the passed values // Get all of the passed values
$values = func_get_args(); $values = func_get_args();
$this->_values = array_merge($this->_values, $values); foreach ($values as $value)
{
$this->_values[] = $value;
}
return $this; return $this;
} }

View File

@ -309,7 +309,7 @@ class Kohana_Database_Query_Builder_Select extends Database_Query_Builder_Where
*/ */
public function offset($number) public function offset($number)
{ {
$this->_offset = $number; $this->_offset = ($number === NULL) ? NULL : (int) $number;
return $this; return $this;
} }
@ -404,13 +404,14 @@ class Kohana_Database_Query_Builder_Select extends Database_Query_Builder_Where
if ( ! empty($this->_union)) if ( ! empty($this->_union))
{ {
$query = '('.$query.')';
foreach ($this->_union as $u) { foreach ($this->_union as $u) {
$query .= ' UNION '; $query .= ' UNION ';
if ($u['all'] === TRUE) if ($u['all'] === TRUE)
{ {
$query .= 'ALL '; $query .= 'ALL ';
} }
$query .= $u['select']->compile($db); $query .= '('.$u['select']->compile($db).')';
} }
} }

View File

@ -172,7 +172,7 @@ abstract class Kohana_Database_Query_Builder_Where extends Database_Query_Builde
*/ */
public function limit($number) public function limit($number)
{ {
$this->_limit = $number; $this->_limit = ($number === NULL) ? NULL : (int) $number;
return $this; return $this;
} }

View File

@ -204,6 +204,9 @@ class Kohana_Session_Database extends Session {
// Execute the query // Execute the query
$query->execute($this->_db); $query->execute($this->_db);
// Delete the old session id
$this->_update_id = NULL;
// Delete the cookie // Delete the cookie
Cookie::delete($this->_name); Cookie::delete($this->_name);
} }

View File

@ -22,7 +22,12 @@
"require": { "require": {
"composer/installers": "~1.0", "composer/installers": "~1.0",
"kohana/core": ">=3.3", "kohana/core": ">=3.3",
"php": ">=5.3.3" "php": ">=5.3.6"
},
"require-dev": {
"kohana/core": "3.3.*@dev",
"kohana/unittest": "3.3.*@dev",
"kohana/koharness": "*@dev"
}, },
"suggest": { "suggest": {
"ext-mysql": "*", "ext-mysql": "*",
@ -32,6 +37,9 @@
"branch-alias": { "branch-alias": {
"dev-3.3/develop": "3.3.x-dev", "dev-3.3/develop": "3.3.x-dev",
"dev-3.4/develop": "3.4.x-dev" "dev-3.4/develop": "3.4.x-dev"
},
"installer-paths": {
"vendor/{$vendor}/{$name}": ["type:kohana-module"]
} }
} }
} }

View File

@ -53,4 +53,38 @@ return array
'charset' => 'utf8', 'charset' => 'utf8',
'caching' => FALSE, 'caching' => FALSE,
), ),
/**
* MySQLi driver config information
*
* The following options are available for MySQLi:
*
* string hostname server hostname, or socket
* string database database name
* string username database username
* string password database password
* boolean persistent use persistent connections?
* array ssl ssl parameters as "key => value" pairs.
* Available keys: client_key_path, client_cert_path, ca_cert_path, ca_dir_path, cipher
* array variables system variables as "key => value" pairs
*
* Ports and sockets may be appended to the hostname.
*
* MySQLi driver config example:
*
*/
// 'alternate_mysqli' => array
// (
// 'type' => 'MySQLi',
// 'connection' => array(
// 'hostname' => 'localhost',
// 'database' => 'kohana',
// 'username' => FALSE,
// 'password' => FALSE,
// 'persistent' => FALSE,
// 'ssl' => NULL,
// ),
// 'table_prefix' => '',
// 'charset' => 'utf8',
// 'caching' => FALSE,
// ),
); );

View File

@ -11,13 +11,14 @@ The database configuration file contains an array of configuration groups. The s
'charset' => string CHARACTER_SET, 'charset' => string CHARACTER_SET,
), ),
Understanding each of these settings is important. Understanding each of these settings is important.
INSTANCE_NAME INSTANCE_NAME
: Connections can be named anything you want, but you should always have at least one connection called "default". : Connections can be named anything you want, but you should always have at least one connection called "default".
DATABASE_TYPE DATABASE_TYPE
: One of the installed database drivers. Kohana comes with "mysql" and "pdo" drivers. Drivers must extend the Database class. : One of the installed database drivers. Kohana comes with "MySQL", "MySQLi", and "PDO" drivers. Drivers must extend the Database class. This parameter is case sensitive. Note the mysql php extension used by the MySQL driver is deprecated as of PHP 5.5 and you should look to use an alternative driver.
CONNECTION_ARRAY CONNECTION_ARRAY
: Specific driver options for connecting to your database. (Driver options are explained [below](#connection-settings).) : Specific driver options for connecting to your database. (Driver options are explained [below](#connection-settings).)
@ -49,7 +50,7 @@ The example file below shows 2 MySQL connections, one local and one remote.
( (
'default' => array 'default' => array
( (
'type' => 'mysql', 'type' => 'MySQL',
'connection' => array( 'connection' => array(
'hostname' => 'localhost', 'hostname' => 'localhost',
'username' => 'dbuser', 'username' => 'dbuser',
@ -61,7 +62,7 @@ The example file below shows 2 MySQL connections, one local and one remote.
'charset' => 'utf8', 'charset' => 'utf8',
), ),
'remote' => array( 'remote' => array(
'type' => 'mysql', 'type' => 'MySQL',
'connection' => array( 'connection' => array(
'hostname' => '55.55.55.55', 'hostname' => '55.55.55.55',
'username' => 'remote_user', 'username' => 'remote_user',
@ -74,6 +75,8 @@ The example file below shows 2 MySQL connections, one local and one remote.
), ),
); );
[!!] Note that the 'type' parameter is case sensitive (eg 'MySQL', 'PDO').
## Connections and Instances ## Connections and Instances
Each configuration group is referred to as a database instance. Each instance can be accessed by calling [Database::instance]. If you don't provide a parameter, the default instance is used. Each configuration group is referred to as a database instance. Each instance can be accessed by calling [Database::instance]. If you don't provide a parameter, the default instance is used.
@ -114,6 +117,24 @@ Type | Option | Description | Default value
`boolean` | persistent | Persistent connections | `FALSE` `boolean` | persistent | Persistent connections | `FALSE`
`string` | database | Database name | `kohana` `string` | database | Database name | `kohana`
### MySQLi
A [MySQL database](http://php.net/manual/en/book.mysqli.php) can accept the following options in the `connection` array:
Type | Option | Description | Default value
----------|------------|----------------------------| -------------------------
`string` | hostname | Hostname of the database | `localhost`
`integer` | port | Port number | `NULL`
`string` | socket | UNIX socket | `NULL`
`string` | username | Database username | `NULL`
`string` | password | Database password | `NULL`
`boolean` | persistent | Persistent connections | `FALSE`
`string` | database | Database name | `kohana`
`array` | ssl | SSL parameters | `NULL`
SSL parameters should be specified as `key` => `value` pairs.
Available keys: client_key_path, client_cert_path, ca_cert_path, ca_dir_path, cipher
### PDO ### PDO
A [PDO database](http://php.net/manual/en/book.pdo.php) can accept these options in the `connection` array: A [PDO database](http://php.net/manual/en/book.pdo.php) can accept these options in the `connection` array:

View File

@ -25,7 +25,7 @@ In this example, we loop through an array of whitelisted input fields and for ea
//copy the query & execute it //copy the query & execute it
$pagination_query = clone $query; $pagination_query = clone $query;
$count = $pagination_query->select(DB::expr('COUNT(*)) AS mycount')->execute()->get('mycount'); $count = $pagination_query->select(DB::expr('COUNT(*) AS mycount'))->execute()->get('mycount');
//pass the total item count to Pagination //pass the total item count to Pagination
$config = Kohana::$config->load('pagination'); $config = Kohana::$config->load('pagination');

View File

@ -76,7 +76,7 @@ This query would generate the following SQL:
Often you will want the results in a particular order and rather than sorting the results, it's better to have the results returned to you in the correct order. You can do this by using the order_by() method. It takes the column name and an optional direction string as the parameters. Multiple `order_by()` methods can be used to add additional sorting capability. Often you will want the results in a particular order and rather than sorting the results, it's better to have the results returned to you in the correct order. You can do this by using the order_by() method. It takes the column name and an optional direction string as the parameters. Multiple `order_by()` methods can be used to add additional sorting capability.
$query = DB::select()->from(`posts`)->order_by(`published`, `DESC`); $query = DB::select()->from('posts')->order_by('published', 'DESC');
This query would generate the following SQL: This query would generate the following SQL:

View File

@ -0,0 +1,8 @@
<?php
// Configuration for koharness - builds a standalone skeleton Kohana app for running unit tests
return array(
'modules' => array(
'database' => __DIR__,
'unittest' => __DIR__ . '/vendor/kohana/unittest'
),
);

37
modules/image/.travis.yml Normal file
View File

@ -0,0 +1,37 @@
sudo: false
language: php
# Only build the main develop/master branches - feature branches will be covered by PRs
branches:
only:
- /^[0-9\.]+\/(develop|master)$/
cache:
directories:
- vendor
- $HOME/.composer/cache
php:
- 5.3
- 5.4
- 5.5
- 5.6
- 7.0
- hhvm
matrix:
include:
- php: 5.3
env: 'COMPOSER_PHPUNIT="lowest"'
before_script:
- composer install --prefer-dist
- if [ "$COMPOSER_PHPUNIT" = "lowest" ]; then composer update --prefer-lowest --with-dependencies phpunit/phpunit; fi;
- vendor/bin/koharness
script:
- cd /tmp/koharness && ./vendor/bin/phpunit --bootstrap=modules/unittest/bootstrap.php modules/unittest/tests.php
notifications:
email: false

View File

@ -0,0 +1,6 @@
# Kohana - image processing module
| ver | Stable | Develop |
|-------|--------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------|
| 3.3.x | [![Build Status - 3.3/master](https://travis-ci.org/kohana/image.svg?branch=3.3%2Fmaster)](https://travis-ci.org/kohana/image) | [![Build Status - 3.3/develop](https://travis-ci.org/kohana/image.svg?branch=3.3%2Fdevelop)](https://travis-ci.org/kohana/image) |
| 3.4.x | [![Build Status - 3.4/master](https://travis-ci.org/kohana/image.svg?branch=3.4%2Fmaster)](https://travis-ci.org/kohana/image) | [![Build Status - 3.4/develop](https://travis-ci.org/kohana/image.svg?branch=3.4%2Fdevelop)](https://travis-ci.org/kohana/image) |

View File

@ -23,6 +23,7 @@ abstract class Kohana_Image {
const VERTICAL = 0x12; const VERTICAL = 0x12;
/** /**
* @deprecated - provide an image.default_driver value in your configuration instead
* @var string default driver: GD, ImageMagick, etc * @var string default driver: GD, ImageMagick, etc
*/ */
public static $default_driver = 'GD'; public static $default_driver = 'GD';
@ -44,8 +45,9 @@ abstract class Kohana_Image {
{ {
if ($driver === NULL) if ($driver === NULL)
{ {
// Use the default driver // Use the driver from configuration file or default one
$driver = Image::$default_driver; $configured_driver = Kohana::$config->load('image.default_driver');
$driver = ($configured_driver) ? $configured_driver : Image::$default_driver;
} }
// Set the class name // Set the class name

View File

@ -611,6 +611,7 @@ class Kohana_Image_GD extends Image {
switch (strtolower($extension)) switch (strtolower($extension))
{ {
case 'jpg': case 'jpg':
case 'jpe':
case 'jpeg': case 'jpeg':
// Save a JPG file // Save a JPG file
$save = 'imagejpeg'; $save = 'imagejpeg';

View File

@ -314,6 +314,7 @@ class Kohana_Image_Imagick extends Image {
switch ($format) switch ($format)
{ {
case 'jpg': case 'jpg':
case 'jpe':
case 'jpeg': case 'jpeg':
$type = IMAGETYPE_JPEG; $type = IMAGETYPE_JPEG;
break; break;

View File

@ -24,6 +24,11 @@
"kohana/core": ">=3.3", "kohana/core": ">=3.3",
"php": ">=5.3.3" "php": ">=5.3.3"
}, },
"require-dev": {
"kohana/core": "3.3.*@dev",
"kohana/unittest": "3.3.*@dev",
"kohana/koharness": "*@dev"
},
"suggest": { "suggest": {
"ext-gd": "*" "ext-gd": "*"
}, },
@ -31,6 +36,9 @@
"branch-alias": { "branch-alias": {
"dev-3.3/develop": "3.3.x-dev", "dev-3.3/develop": "3.3.x-dev",
"dev-3.4/develop": "3.4.x-dev" "dev-3.4/develop": "3.4.x-dev"
},
"installer-paths": {
"vendor/{$vendor}/{$name}": ["type:kohana-module"]
} }
} }
} }

View File

@ -0,0 +1,8 @@
<?php defined('SYSPATH') OR die('No direct script access.');
return array(
// Provide the default driver to use - eg a value of Imagick will use Image_Imagick as the driver class
// If you set a value for this config key, then the Image::$default_driver static variable that was used
// to configure earlier versions will be ignored.
'default_driver' => NULL,
);

View File

@ -4,7 +4,24 @@ Kohana 3.x provides a simple yet powerful image manipulation module. The [Image]
## Drivers ## Drivers
[Image] module ships with [Image_GD] driver which requires `GD` extension enabled in your PHP installation. This is the default driver. Additional drivers can be created by extending the [Image] class. [Image] module ships with [Image_GD] driver which requires `GD` extension enabled in your PHP installation, and
[Image_Imagick] driver which requires the `imagick` PHP extension. Additional drivers can be created by extending
the [Image] class.
The [Image_GD] driver is the default. You can change this by providing an `image.default_driver` configuration option
- for example:
~~~
// application/config/image.php
<?php
return array(
'default_driver' => 'Imagick'
);
~~~
[!!] Older versions of Kohana allowed you to configure the driver with the `Image::$default_driver` static variable in
the bootstrap, an extension class, or elsewhere. That variable is now deprecated and will be ignored if you set a
config value.
## Getting Started ## Getting Started

View File

@ -0,0 +1,8 @@
<?php
// Configuration for koharness - builds a standalone skeleton Kohana app for running unit tests
return array(
'modules' => array(
'image' => __DIR__,
'unittest' => __DIR__ . '/vendor/kohana/unittest'
),
);

View File

@ -0,0 +1,37 @@
sudo: false
language: php
# Only build the main develop/master branches - feature branches will be covered by PRs
branches:
only:
- /^[0-9\.]+\/(develop|master)$/
cache:
directories:
- vendor
- $HOME/.composer/cache
php:
- 5.3
- 5.4
- 5.5
- 5.6
- 7.0
- hhvm
matrix:
include:
- php: 5.3
env: 'COMPOSER_PHPUNIT="lowest"'
before_script:
- composer install --prefer-dist
- if [ "$COMPOSER_PHPUNIT" = "lowest" ]; then composer update --prefer-lowest --with-dependencies phpunit/phpunit; fi;
- vendor/bin/koharness
script:
- cd /tmp/koharness && ./vendor/bin/phpunit --bootstrap=modules/unittest/bootstrap.php modules/unittest/tests.php
notifications:
email: false

View File

@ -1,5 +1,10 @@
# Minion # Minion
| ver | Stable | Develop |
|-------|----------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------|
| 3.3.x | [![Build Status - 3.3/master](https://travis-ci.org/kohana/minion.svg?branch=3.3%2Fmaster)](https://travis-ci.org/kohana/minion) | [![Build Status - 3.3/develop](https://travis-ci.org/kohana/minion.svg?branch=3.3%2Fdevelop)](https://travis-ci.org/kohana/minion) |
| 3.4.x | [![Build Status - 3.4/master](https://travis-ci.org/kohana/minion.svg?branch=3.4%2Fmaster)](https://travis-ci.org/kohana/minion) | [![Build Status - 3.4/develop](https://travis-ci.org/kohana/minion.svg?branch=3.4%2Fdevelop)](https://travis-ci.org/kohana/minion) |
Minion is a framework for running tasks via the CLI. Minion is a framework for running tasks via the CLI.
The system is inspired by ruckusing, which had a nice system for defining tasks but lacked the desired flexibility for kohana integration. The system is inspired by ruckusing, which had a nice system for defining tasks but lacked the desired flexibility for kohana integration.

View File

@ -170,7 +170,7 @@ class Kohana_Minion_CLI {
// Create temporary file // Create temporary file
file_put_contents($vbscript, 'wscript.echo(InputBox("'.addslashes($text).'"))'); file_put_contents($vbscript, 'wscript.echo(InputBox("'.addslashes($text).'"))');
$password = shell_exec('cscript //nologo '.escapeshellarg($text)); $password = shell_exec('cscript //nologo '.escapeshellarg($vbscript));
// Remove temporary file. // Remove temporary file.
unlink($vbscript); unlink($vbscript);
@ -278,7 +278,7 @@ class Kohana_Minion_CLI {
* @copyright 2010 - 2011 Fuel Development Team * @copyright 2010 - 2011 Fuel Development Team
* @link http://fuelphp.com * @link http://fuelphp.com
* @param string $text the text to color * @param string $text the text to color
* @param atring $foreground the foreground color * @param string $foreground the foreground color
* @param string $background the background color * @param string $background the background color
* @return string the color coded string * @return string the color coded string
*/ */

View File

@ -1,6 +1,6 @@
<?php defined('SYSPATH') or die('No direct script access.'); <?php defined('SYSPATH') or die('No direct script access.');
/** /**
* Minipn exception * Minion exception
* *
* @package Kohana * @package Kohana
* @category Minion * @category Minion
@ -15,8 +15,6 @@ class Kohana_Minion_Exception extends Kohana_Exception {
* *
* Should this display a stack trace? It's useful. * Should this display a stack trace? It's useful.
* *
* Should this still log? Maybe not as useful since we'll see the error on the screen.
*
* @uses Kohana_Exception::text * @uses Kohana_Exception::text
* @param Exception $e * @param Exception $e
* @return boolean * @return boolean
@ -25,6 +23,9 @@ class Kohana_Minion_Exception extends Kohana_Exception {
{ {
try try
{ {
// Log the exception
Kohana_Exception::log($e);
if ($e instanceof Minion_Exception) if ($e instanceof Minion_Exception)
{ {
echo $e->format_for_cli(); echo $e->format_for_cli();

View File

@ -24,10 +24,18 @@
"kohana/core": ">=3.3", "kohana/core": ">=3.3",
"php": ">=5.3.3" "php": ">=5.3.3"
}, },
"require-dev": {
"kohana/core": "3.3.*@dev",
"kohana/unittest": "3.3.*@dev",
"kohana/koharness": "*@dev"
},
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-3.3/develop": "3.3.x-dev", "dev-3.3/develop": "3.3.x-dev",
"dev-3.4/develop": "3.4.x-dev" "dev-3.4/develop": "3.4.x-dev"
},
"installer-paths": {
"vendor/{$vendor}/{$name}": ["type:kohana-module"]
} }
} }
} }

View File

@ -0,0 +1,8 @@
<?php
// Configuration for koharness - builds a standalone skeleton Kohana app for running unit tests
return array(
'modules' => array(
'minion' => __DIR__,
'unittest' => __DIR__ . '/vendor/kohana/unittest'
),
);

View File

@ -1,70 +0,0 @@
<?php
/**
* Test case for Minion_Util
*
* @package Kohana/Minion
* @group kohana
* @group kohana.minion
* @category Test
* @author Kohana Team
* @copyright (c) 2009-2012 Kohana Team
* @license http://kohanaframework.org/license
*/
class Minion_TaskTest extends Kohana_Unittest_TestCase
{
/**
* Provides test data for test_convert_task_to_class_name()
*
* @return array
*/
public function provider_convert_task_to_class_name()
{
return array(
array('Task_Db_Migrate', 'db:migrate'),
array('Task_Db_Status', 'db:status'),
array('', ''),
);
}
/**
* Tests that a task can be converted to a class name
*
* @test
* @covers Minion_Task::convert_task_to_class_name
* @dataProvider provider_convert_task_to_class_name
* @param string Expected class name
* @param string Input task name
*/
public function test_convert_task_to_class_name($expected, $task_name)
{
$this->assertSame($expected, Minion_Task::convert_task_to_class_name($task_name));
}
/**
* Provides test data for test_convert_class_to_task()
*
* @return array
*/
public function provider_convert_class_to_task()
{
return array(
array('db:migrate', 'Task_Db_Migrate'),
);
}
/**
* Tests that the task name can be found from a class name / object
*
* @test
* @covers Minion_Task::convert_class_to_task
* @dataProvider provider_convert_class_to_task
* @param string Expected task name
* @param mixed Input class
*/
public function test_convert_class_to_task($expected, $class)
{
$this->assertSame($expected, Minion_Task::convert_class_to_task($class));
}
}

37
modules/orm/.travis.yml Normal file
View File

@ -0,0 +1,37 @@
sudo: false
language: php
# Only build the main develop/master branches - feature branches will be covered by PRs
branches:
only:
- /^[0-9\.]+\/(develop|master)$/
cache:
directories:
- vendor
- $HOME/.composer/cache
php:
- 5.3
- 5.4
- 5.5
- 5.6
- 7.0
- hhvm
matrix:
include:
- php: 5.3
env: 'COMPOSER_PHPUNIT="lowest"'
before_script:
- composer install --prefer-dist
- if [ "$COMPOSER_PHPUNIT" = "lowest" ]; then composer update --prefer-lowest --with-dependencies phpunit/phpunit; fi;
- vendor/bin/koharness
script:
- cd /tmp/koharness && ./vendor/bin/phpunit --bootstrap=modules/unittest/bootstrap.php modules/unittest/tests.php
notifications:
email: false

6
modules/orm/README.md Normal file
View File

@ -0,0 +1,6 @@
# Kohana - ORM module
| ver | Stable | Develop |
|-------|----------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------|
| 3.3.x | [![Build Status - 3.3/master](https://travis-ci.org/kohana/orm.svg?branch=3.3%2Fmaster)](https://travis-ci.org/kohana/orm) | [![Build Status - 3.3/develop](https://travis-ci.org/kohana/orm.svg?branch=3.3%2Fdevelop)](https://travis-ci.org/kohana/orm) |
| 3.4.x | [![Build Status - 3.4/master](https://travis-ci.org/kohana/orm.svg?branch=3.4%2Fmaster)](https://travis-ci.org/kohana/orm) | [![Build Status - 3.4/develop](https://travis-ci.org/kohana/orm.svg?branch=3.4%2Fdevelop)](https://travis-ci.org/kohana/orm) |

View File

@ -1627,7 +1627,7 @@ class Kohana_ORM extends Model implements serializable {
if ($method['name'] == 'select') if ($method['name'] == 'select')
{ {
// Ignore any selected columns for now // Ignore any selected columns for now
$selects[] = $method; $selects[$key] = $method;
unset($this->_db_pending[$key]); unset($this->_db_pending[$key]);
} }
} }

View File

@ -25,10 +25,19 @@
"kohana/database": ">=3.3", "kohana/database": ">=3.3",
"php": ">=5.3.3" "php": ">=5.3.3"
}, },
"require-dev": {
"kohana/core": "3.3.*@dev",
"kohana/database": "3.3.*@dev",
"kohana/unittest": "3.3.*@dev",
"kohana/koharness": "*@dev"
},
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-3.3/develop": "3.3.x-dev", "dev-3.3/develop": "3.3.x-dev",
"dev-3.4/develop": "3.4.x-dev" "dev-3.4/develop": "3.4.x-dev"
},
"installer-paths": {
"vendor/{$vendor}/{$name}": ["type:kohana-module"]
} }
} }
} }

View File

@ -0,0 +1,8 @@
<?php
// Configuration for koharness - builds a standalone skeleton Kohana app for running unit tests
return array(
'modules' => array(
'orm' => __DIR__,
'unittest' => __DIR__ . '/vendor/kohana/unittest'
),
);

View File

@ -0,0 +1,37 @@
sudo: false
language: php
# Only build the main develop/master branches - feature branches will be covered by PRs
branches:
only:
- /^[0-9\.]+\/(develop|master)$/
cache:
directories:
- vendor
- $HOME/.composer/cache
php:
- 5.3
- 5.4
- 5.5
- 5.6
- 7.0
- hhvm
matrix:
include:
- php: 5.3
env: 'COMPOSER_PHPUNIT="lowest"'
before_script:
- composer install --prefer-dist
- if [ "$COMPOSER_PHPUNIT" = "lowest" ]; then composer update --prefer-lowest --with-dependencies phpunit/phpunit; fi;
- vendor/bin/koharness
script:
- cd /tmp/koharness && ./vendor/bin/phpunit --bootstrap=modules/unittest/bootstrap.php modules/unittest/tests.php
notifications:
email: false

View File

@ -1,13 +1,29 @@
# Kohana-PHPUnit integration # Kohana-PHPUnit integration
This module integrates PHPUnit with Kohana. | ver | Stable | Develop |
|-------|--------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------|
| 3.3.x | [![Build Status - 3.3/master](https://travis-ci.org/kohana/unittest.svg?branch=3.3%2Fmaster)](https://travis-ci.org/kohana/unittest) | [![Build Status - 3.3/develop](https://travis-ci.org/kohana/unittest.svg?branch=3.3%2Fdevelop)](https://travis-ci.org/kohana/unittest) |
| 3.4.x | [![Build Status - 3.4/master](https://travis-ci.org/kohana/unittest.svg?branch=3.4%2Fmaster)](https://travis-ci.org/kohana/unittest) | [![Build Status - 3.4/develop](https://travis-ci.org/kohana/unittest.svg?branch=3.4%2Fdevelop)](https://travis-ci.org/kohana/unittest) |
This module integrates PHPUnit with Kohana and is used to run all the core Kohana tests. In most cases you will not
need to use this module for testing your own projects. If there are particular helpers provided here that you rely on,
that may be a sign that your own code is too closely coupled to the behaviour of the Kohana core classes.
If you look through any of the tests provided in this module you'll probably notice all theHorribleCamelCase. If you look through any of the tests provided in this module you'll probably notice all theHorribleCamelCase.
I've chosen to do this because it's part of the PHPUnit coding conventions and is required for certain features such as auto documentation. I've chosen to do this because it's part of the PHPUnit coding conventions and is required for certain features such as auto documentation.
## Requirements ## Requirements and installation
* [PHPUnit](http://www.phpunit.de/) >= 3.4 Dependencies are listed in the composer.json - run `composer install` to install the module and all external requirements.
Note that more usually you will add this module to your own module's composer.json:
```json
{
"require-dev": {
"kohana/unittest": "3.3.*@dev"
}
}
```
## Usage ## Usage

View File

@ -121,5 +121,10 @@ if (($ob_len = ob_get_length()) !== FALSE)
} }
} }
// Enable the unittest module // Enable the unittest module if it is not already loaded - use the absolute path
Kohana::modules(Kohana::modules() + array('unittest' => MODPATH.'unittest')); $modules = Kohana::modules();
$unittest_path = realpath(__DIR__).DIRECTORY_SEPARATOR;
if ( ! in_array($unittest_path, $modules)) {
$modules['unittest'] = $unittest_path;
Kohana::modules($modules);
}

View File

@ -9,7 +9,7 @@ $modules = array();
foreach ($modules_iterator as $module) foreach ($modules_iterator as $module)
{ {
if ($module->isDir()) if ($module->isDir() AND ! $module->isDot())
{ {
$modules[$module->getFilename()] = MODPATH.$module->getFilename(); $modules[$module->getFilename()] = MODPATH.$module->getFilename();
} }

View File

@ -1,4 +1,4 @@
<?php <?php defined('SYSPATH') or die('No direct script access.');
/** /**
* TestCase for testing a database * TestCase for testing a database
* *
@ -10,11 +10,6 @@
*/ */
abstract class Kohana_Unittest_Database_TestCase extends PHPUnit_Extensions_Database_TestCase { abstract class Kohana_Unittest_Database_TestCase extends PHPUnit_Extensions_Database_TestCase {
/**
* Whether we should enable work arounds to make the tests compatible with phpunit 3.4
* @var boolean
*/
protected static $_assert_type_compatability = NULL;
/** /**
* Make sure PHPUnit backs up globals * Make sure PHPUnit backs up globals
@ -49,16 +44,6 @@ abstract class Kohana_Unittest_Database_TestCase extends PHPUnit_Extensions_Data
*/ */
public function setUp() public function setUp()
{ {
if(self::$_assert_type_compatability === NULL)
{
if( ! class_exists('PHPUnit_Runner_Version'))
{
require_once 'PHPUnit/Runner/Version.php';
}
self::$_assert_type_compatability = version_compare(PHPUnit_Runner_Version::id(), '3.5.0', '<=');
}
$this->_helpers = new Kohana_Unittest_Helpers; $this->_helpers = new Kohana_Unittest_Helpers;
$this->setEnvironment($this->environmentDefault); $this->setEnvironment($this->environmentDefault);
@ -161,151 +146,4 @@ abstract class Kohana_Unittest_Database_TestCase extends PHPUnit_Extensions_Data
return Kohana_Unittest_Helpers::has_internet(); return Kohana_Unittest_Helpers::has_internet();
} }
/**
* Asserts that a variable is of a given type.
*
* @param string $expected
* @param mixed $actual
* @param string $message
* @since Method available since Release 3.5.0
*/
public static function assertInstanceOf($expected, $actual, $message = '')
{
if(self::$_assert_type_compatability)
{
return self::assertType($expected, $actual, $message);
}
return parent::assertInstanceOf($expected, $actual, $message);
}
/**
* Asserts that an attribute is of a given type.
*
* @param string $expected
* @param string $attributeName
* @param mixed $classOrObject
* @param string $message
* @since Method available since Release 3.5.0
*/
public static function assertAttributeInstanceOf($expected, $attributeName, $classOrObject, $message = '')
{
if(self::$_assert_type_compatability)
{
return self::assertAttributeType($expected, $attributeName, $classOrObject, $message);
}
return parent::assertAttributeInstanceOf($expected, $attributeName, $classOrObject, $message);
}
/**
* Asserts that a variable is not of a given type.
*
* @param string $expected
* @param mixed $actual
* @param string $message
* @since Method available since Release 3.5.0
*/
public static function assertNotInstanceOf($expected, $actual, $message = '')
{
if(self::$_assert_type_compatability)
{
return self::assertNotType($expected, $actual, $message);
}
return self::assertNotInstanceOf($expected, $actual, $message);
}
/**
* Asserts that an attribute is of a given type.
*
* @param string $expected
* @param string $attributeName
* @param mixed $classOrObject
* @param string $message
* @since Method available since Release 3.5.0
*/
public static function assertAttributeNotInstanceOf($expected, $attributeName, $classOrObject, $message = '')
{
if(self::$_assert_type_compatability)
{
return self::assertAttributeNotType($expected, $attributeName, $classOrObject, $message);
}
return self::assertAttributeNotInstanceOf($expected, $attributeName, $classOrObject, $message);
}
/**
* Asserts that a variable is of a given type.
*
* @param string $expected
* @param mixed $actual
* @param string $message
* @since Method available since Release 3.5.0
*/
public static function assertInternalType($expected, $actual, $message = '')
{
if(self::$_assert_type_compatability)
{
return self::assertType($expected, $actual, $message);
}
return parent::assertInternalType($expected, $actual, $message);
}
/**
* Asserts that an attribute is of a given type.
*
* @param string $expected
* @param string $attributeName
* @param mixed $classOrObject
* @param string $message
* @since Method available since Release 3.5.0
*/
public static function assertAttributeInternalType($expected, $attributeName, $classOrObject, $message = '')
{
if(self::$_assert_type_compatability)
{
return self::assertAttributeType($expected, $attributeName, $classOrObject, $message);
}
return self::assertAttributeInternalType($expected, $attributeName, $classOrObject, $message);
}
/**
* Asserts that a variable is not of a given type.
*
* @param string $expected
* @param mixed $actual
* @param string $message
* @since Method available since Release 3.5.0
*/
public static function assertNotInternalType($expected, $actual, $message = '')
{
if(self::$_assert_type_compatability)
{
return self::assertNotType($expected, $actual, $message);
}
return self::assertNotInternalType($expected, $actual, $message);
}
/**
* Asserts that an attribute is of a given type.
*
* @param string $expected
* @param string $attributeName
* @param mixed $classOrObject
* @param string $message
* @since Method available since Release 3.5.0
*/
public static function assertAttributeNotInternalType($expected, $attributeName, $classOrObject, $message = '')
{
if(self::$_assert_type_compatability)
{
return self::assertAttributeNotType($expected, $attributeName, $classOrObject, $message);
}
return self::assertAttributeNotInternalType($expected, $attributeName, $classOrObject, $message);
}
} }

View File

@ -1,4 +1,4 @@
<?php <?php defined('SYSPATH') or die('No direct script access.');
/** /**
* Unit testing helpers * Unit testing helpers

View File

@ -6,12 +6,6 @@
*/ */
abstract class Kohana_Unittest_TestCase extends PHPUnit_Framework_TestCase { abstract class Kohana_Unittest_TestCase extends PHPUnit_Framework_TestCase {
/**
* Whether we should enable work arounds to make the tests compatible with phpunit 3.4
* @var boolean
*/
protected static $_assert_type_compatability = NULL;
/** /**
* Make sure PHPUnit backs up globals * Make sure PHPUnit backs up globals
* @var boolean * @var boolean
@ -39,16 +33,6 @@ abstract class Kohana_Unittest_TestCase extends PHPUnit_Framework_TestCase {
*/ */
public function setUp() public function setUp()
{ {
if(self::$_assert_type_compatability === NULL)
{
if( ! class_exists('PHPUnit_Runner_Version'))
{
require_once 'PHPUnit/Runner/Version.php';
}
self::$_assert_type_compatability = version_compare(PHPUnit_Runner_Version::id(), '3.5.0', '<=');
}
$this->_helpers = new Unittest_Helpers; $this->_helpers = new Unittest_Helpers;
$this->setEnvironment($this->environmentDefault); $this->setEnvironment($this->environmentDefault);
@ -112,150 +96,75 @@ abstract class Kohana_Unittest_TestCase extends PHPUnit_Framework_TestCase {
} }
/** /**
* Asserts that a variable is of a given type. * Evaluate an HTML or XML string and assert its structure and/or contents.
* *
* @param string $expected * NOTE:
* @param mixed $actual * Overriding this method to remove the deprecation error
* when tested with PHPUnit 4.2.0+
*
* TODO:
* this should be removed when phpunit-dom-assertions gets released
* https://github.com/phpunit/phpunit-dom-assertions
*
* @param array $matcher
* @param string $actual
* @param string $message * @param string $message
* @since Method available since Release 3.5.0 * @param bool $isHtml
* @uses Unittest_TestCase::tag_match
*/ */
public static function assertInstanceOf($expected, $actual, $message = '') public static function assertTag($matcher, $actual, $message = '', $isHtml = true)
{ {
if(self::$_assert_type_compatability) //trigger_error(__METHOD__ . ' is deprecated', E_USER_DEPRECATED);
{
return self::assertType($expected, $actual, $message);
}
return parent::assertInstanceOf($expected, $actual, $message); $matched = static::tag_match($matcher, $actual, $message, $isHtml);
static::assertTrue($matched, $message);
} }
/** /**
* Asserts that an attribute is of a given type. * This assertion is the exact opposite of assertTag
* *
* @param string $expected * Rather than asserting that $matcher results in a match, it asserts that
* @param string $attributeName * $matcher does not match
* @param mixed $classOrObject *
* NOTE:
* Overriding this method to remove the deprecation error
* when tested with PHPUnit 4.2.0+
*
* TODO:
* this should be removed when phpunit-dom-assertions gets released
* https://github.com/phpunit/phpunit-dom-assertions
*
* @param array $matcher
* @param string $actual
* @param string $message * @param string $message
* @since Method available since Release 3.5.0 * @param bool $isHtml
* @uses Unittest_TestCase::tag_match
*/ */
public static function assertAttributeInstanceOf($expected, $attributeName, $classOrObject, $message = '') public static function assertNotTag($matcher, $actual, $message = '', $isHtml = true)
{ {
if(self::$_assert_type_compatability) //trigger_error(__METHOD__ . ' is deprecated', E_USER_DEPRECATED);
{
return self::assertAttributeType($expected, $attributeName, $classOrObject, $message);
}
return parent::assertAttributeInstanceOf($expected, $attributeName, $classOrObject, $message); $matched = static::tag_match($matcher, $actual, $message, $isHtml);
static::assertFalse($matched, $message);
} }
/** /**
* Asserts that a variable is not of a given type. * Helper function to match HTML string tags against certain criteria
* *
* @param string $expected * TODO:
* @param mixed $actual * this should be removed when phpunit-dom-assertions gets released
* @param string $message * https://github.com/phpunit/phpunit-dom-assertions
* @since Method available since Release 3.5.0
*/
public static function assertNotInstanceOf($expected, $actual, $message = '')
{
if(self::$_assert_type_compatability)
{
return self::assertNotType($expected, $actual, $message);
}
return parent::assertNotInstanceOf($expected, $actual, $message);
}
/**
* Asserts that an attribute is of a given type.
* *
* @param string $expected * @param array $matcher
* @param string $attributeName * @param string $actual
* @param mixed $classOrObject
* @param string $message * @param string $message
* @since Method available since Release 3.5.0 * @param bool $isHtml
* @return bool TRUE if there is a match FALSE otherwise
*/ */
public static function assertAttributeNotInstanceOf($expected, $attributeName, $classOrObject, $message = '') protected static function tag_match($matcher, $actual, $message = '', $isHtml = true)
{ {
if(self::$_assert_type_compatability) $dom = PHPUnit_Util_XML::load($actual, $isHtml);
{ $tags = PHPUnit_Util_XML::findNodes($dom, $matcher, $isHtml);
return self::assertAttributeNotType($expected, $attributeName, $classOrObject, $message); return count($tags) > 0 && $tags[0] instanceof DOMNode;
}
return parent::assertAttributeNotInstanceOf($expected, $attributeName, $classOrObject, $message);
}
/**
* Asserts that a variable is of a given type.
*
* @param string $expected
* @param mixed $actual
* @param string $message
* @since Method available since Release 3.5.0
*/
public static function assertInternalType($expected, $actual, $message = '')
{
if(self::$_assert_type_compatability)
{
return self::assertType($expected, $actual, $message);
}
return parent::assertInternalType($expected, $actual, $message);
}
/**
* Asserts that an attribute is of a given type.
*
* @param string $expected
* @param string $attributeName
* @param mixed $classOrObject
* @param string $message
* @since Method available since Release 3.5.0
*/
public static function assertAttributeInternalType($expected, $attributeName, $classOrObject, $message = '')
{
if(self::$_assert_type_compatability)
{
return self::assertAttributeType($expected, $attributeName, $classOrObject, $message);
}
return parent::assertAttributeInternalType($expected, $attributeName, $classOrObject, $message);
}
/**
* Asserts that a variable is not of a given type.
*
* @param string $expected
* @param mixed $actual
* @param string $message
* @since Method available since Release 3.5.0
*/
public static function assertNotInternalType($expected, $actual, $message = '')
{
if(self::$_assert_type_compatability)
{
return self::assertNotType($expected, $actual, $message);
}
return parent::assertNotInternalType($expected, $actual, $message);
}
/**
* Asserts that an attribute is of a given type.
*
* @param string $expected
* @param string $attributeName
* @param mixed $classOrObject
* @param string $message
* @since Method available since Release 3.5.0
*/
public static function assertAttributeNotInternalType($expected, $attributeName, $classOrObject, $message = '')
{
if(self::$_assert_type_compatability)
{
return self::assertAttributeNotType($expected, $attributeName, $classOrObject, $message);
}
return parent::assertAttributeNotInternalType($expected, $attributeName, $classOrObject, $message);
} }
} }

View File

@ -1,4 +1,4 @@
<?php <?php defined('SYSPATH') or die('No direct script access.');
/** /**
* Transparent extension for Kohana_Unittest_Database_TestCase * Transparent extension for Kohana_Unittest_Database_TestCase

View File

@ -23,12 +23,19 @@
"composer/installers": "~1.0", "composer/installers": "~1.0",
"kohana/core": ">=3.3", "kohana/core": ">=3.3",
"php": ">=5.3.3", "php": ">=5.3.3",
"phpunit/phpunit": "3.7.*" "phpunit/phpunit": "3.7.24 - 4"
},
"require-dev": {
"kohana/core": "3.3.*@dev",
"kohana/koharness": "*@dev"
}, },
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-3.3/develop": "3.3.x-dev", "dev-3.3/develop": "3.3.x-dev",
"dev-3.4/develop": "3.4.x-dev" "dev-3.4/develop": "3.4.x-dev"
},
"installer-paths": {
"vendor/{$vendor}/{$name}": ["type:kohana-module"]
} }
} }
} }

View File

@ -0,0 +1,7 @@
<?php
// Configuration for koharness - builds a standalone skeleton Kohana app for running unit tests
return array(
'modules' => array(
'unittest' => __DIR__
),
);

View File

@ -0,0 +1,37 @@
sudo: false
language: php
# Only build the main develop/master branches - feature branches will be covered by PRs
branches:
only:
- /^[0-9\.]+\/(develop|master)$/
cache:
directories:
- vendor
- $HOME/.composer/cache
php:
- 5.3
- 5.4
- 5.5
- 5.6
- 7.0
- hhvm
matrix:
include:
- php: 5.3
env: 'COMPOSER_PHPUNIT="lowest"'
before_script:
- composer install --prefer-dist
- if [ "$COMPOSER_PHPUNIT" = "lowest" ]; then composer update --prefer-lowest --with-dependencies phpunit/phpunit; fi;
- vendor/bin/koharness
script:
- cd /tmp/koharness && ./vendor/bin/phpunit --bootstrap=modules/unittest/bootstrap.php modules/unittest/tests.php
notifications:
email: false

View File

@ -1,3 +1,10 @@
# Kohana - userguide module
| ver | Stable | Develop |
|-------|----------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------|
| 3.3.x | [![Build Status - 3.3/master](https://travis-ci.org/kohana/userguide.svg?branch=3.3%2Fmaster)](https://travis-ci.org/kohana/userguide) | [![Build Status - 3.3/develop](https://travis-ci.org/kohana/userguide.svg?branch=3.3%2Fdevelop)](https://travis-ci.org/kohana/userguide) |
| 3.4.x | [![Build Status - 3.4/master](https://travis-ci.org/kohana/userguide.svg?branch=3.4%2Fmaster)](https://travis-ci.org/kohana/userguide) | [![Build Status - 3.4/develop](https://travis-ci.org/kohana/userguide.svg?branch=3.4%2Fdevelop)](https://travis-ci.org/kohana/userguide) |
## What needs to be done? ## What needs to be done?
Most articles are stubs, with a couple links to pages to be used as a reference when writing the page. The idea is to use the information on those links to help write the new ones. Some of the old userguide pages can probably be mostly copied, with a few improvements, others will be better to be completely rewritten. If you ever have questions, please feel free to jump in the kohana irc channel. Most articles are stubs, with a couple links to pages to be used as a reference when writing the page. The idea is to use the information on those links to help write the new ones. Some of the old userguide pages can probably be mostly copied, with a few improvements, others will be better to be completely rewritten. If you ever have questions, please feel free to jump in the kohana irc channel.

View File

@ -3,8 +3,10 @@
* Kohana user guide and api browser. * Kohana user guide and api browser.
* *
* @package Kohana/Userguide * @package Kohana/Userguide
* @category Controllers * @category Controller
* @author Kohana Team * @author Kohana Team
* @copyright (c) 2008-2013 Kohana Team
* @license http://kohanaframework.org/license
*/ */
abstract class Kohana_Controller_Userguide extends Controller_Template { abstract class Kohana_Controller_Userguide extends Controller_Template {
@ -76,7 +78,7 @@ abstract class Kohana_Controller_Userguide extends Controller_Template {
); );
} }
// If we are in the api browser, show the menu and show the api browser in the breadcrumbs // If we are in the api browser, show the menu and show the api browser in the breadcrumbs
else if (Route::name($this->request->route()) == 'docs/api') elseif (Route::name($this->request->route()) == 'docs/api')
{ {
$this->template->menu = Kodoc::menu(); $this->template->menu = Kodoc::menu();
@ -116,13 +118,13 @@ abstract class Kohana_Controller_Userguide extends Controller_Template {
} }
// Prevent "guide/module" and "guide/module/index" from having duplicate content // Prevent "guide/module" and "guide/module/index" from having duplicate content
if ( $page == 'index') if ($page == 'index')
{ {
return $this->error('Userguide page not found'); return $this->error('Userguide page not found');
} }
// If a module is set, but no page was provided in the url, show the index page // If a module is set, but no page was provided in the url, show the index page
if ( ! $page ) if ( ! $page)
{ {
$page = 'index'; $page = 'index';
} }
@ -141,12 +143,14 @@ abstract class Kohana_Controller_Userguide extends Controller_Template {
Kodoc_Markdown::$image_url = URL::site($this->media->uri()).'/'.$module.'/'; Kodoc_Markdown::$image_url = URL::site($this->media->uri()).'/'.$module.'/';
// Set the page title // Set the page title
$this->template->title = $page == 'index' ? Kohana::$config->load('userguide.modules.'.$module.'.name') : $this->title($page); $this->template->title = ($page == 'index')
? Kohana::$config->load('userguide.modules.'.$module.'.name')
: $this->title($page);
// Parse the page contents into the template // Parse the page contents into the template
Kodoc_Markdown::$show_toc = true; Kodoc_Markdown::$show_toc = TRUE;
$this->template->content = Kodoc_Markdown::markdown(file_get_contents($file)); $this->template->content = Kodoc_Markdown::markdown(file_get_contents($file));
Kodoc_Markdown::$show_toc = false; Kodoc_Markdown::$show_toc = FALSE;
// Attach this module's menu to the template // Attach this module's menu to the template
$this->template->menu = Kodoc_Markdown::markdown($this->_get_all_menu_markdown()); $this->template->menu = Kodoc_Markdown::markdown($this->_get_all_menu_markdown());
@ -358,11 +362,7 @@ abstract class Kohana_Controller_Userguide extends Controller_Template {
if ($file AND $text = file_get_contents($file)) if ($file AND $text = file_get_contents($file))
{ {
// Add spans around non-link categories. This is a terrible hack. // Add spans around non-link categories. This is a terrible hack.
//echo Debug::vars($text);
//$text = preg_replace('/(\s*[\-\*\+]\s*)(.*)/','$1<span>$2</span>',$text);
$text = preg_replace('/^(\s*[\-\*\+]\s*)([^\[\]]+)$/m','$1<span>$2</span>',$text); $text = preg_replace('/^(\s*[\-\*\+]\s*)([^\[\]]+)$/m','$1<span>$2</span>',$text);
//echo Debug::vars($text);
$markdown .= $text; $markdown .= $text;
} }

View File

@ -5,8 +5,8 @@
* @package Kohana/Userguide * @package Kohana/Userguide
* @category Base * @category Base
* @author Kohana Team * @author Kohana Team
* @copyright (c) 2008-2012 Kohana Team * @copyright (c) 2008-2013 Kohana Team
* @license http://kohanaphp.com/license * @license http://kohanaframework.org/license
*/ */
class Kohana_Kodoc { class Kohana_Kodoc {
@ -34,6 +34,10 @@ class Kohana_Kodoc {
{ {
$member = '#property:'.substr($matches[3], 1); $member = '#property:'.substr($matches[3], 1);
} }
elseif (preg_match('/^[A-Z_\x7f-\xff][A-Z0-9_\x7f-\xff]*$/', $matches[3]))
{
$member = '#constant:'.substr($matches[3],0);
}
else else
{ {
$member = '#'.$matches[3]; $member = '#'.$matches[3];
@ -275,7 +279,7 @@ class Kohana_Kodoc {
* @param string $text Content of the tag * @param string $text Content of the tag
* @return void * @return void
*/ */
$add_tag = function($tag, $text) use ($html, &$tags) $add_tag = function ($tag, $text) use ($html, & $tags)
{ {
// Don't show @access lines, they are shown elsewhere // Don't show @access lines, they are shown elsewhere
if ($tag !== 'access') if ($tag !== 'access')
@ -290,7 +294,7 @@ class Kohana_Kodoc {
} }
}; };
$comment = $tag = null; $comment = $tag = NULL;
$end = count($lines[1]) - 1; $end = count($lines[1]) - 1;
foreach ($lines[1] as $i => $line) foreach ($lines[1] as $i => $line)
@ -462,5 +466,4 @@ class Kohana_Kodoc {
} }
} }
} // End Kodoc } // End Kodoc

View File

@ -5,8 +5,8 @@
* @package Kohana/Userguide * @package Kohana/Userguide
* @category Base * @category Base
* @author Kohana Team * @author Kohana Team
* @copyright (c) 2009-2012 Kohana Team * @copyright (c) 2008-2013 Kohana Team
* @license http://kohanaphp.com/license * @license http://kohanaframework.org/license
*/ */
class Kohana_Kodoc_Class extends Kodoc { class Kohana_Kodoc_Class extends Kodoc {
@ -45,7 +45,7 @@ class Kohana_Kodoc_Class extends Kodoc {
* the class. Reads the class modifiers, constants and comment. Parses the * the class. Reads the class modifiers, constants and comment. Parses the
* comment to find the description and tags. * comment to find the description and tags.
* *
* @param string class name * @param string Class name
* @return void * @return void
*/ */
public function __construct($class) public function __construct($class)
@ -194,10 +194,11 @@ class Kohana_Kodoc_Class extends Kodoc {
/** /**
* Sort methods based on their visibility and declaring class based on: * Sort methods based on their visibility and declaring class based on:
* - methods will be sorted public, protected, then private. *
* - methods that are declared by an ancestor will be after classes * * methods will be sorted public, protected, then private.
* * methods that are declared by an ancestor will be after classes
* declared by the current class * declared by the current class
* - lastly, they will be sorted alphabetically * * lastly, they will be sorted alphabetically
* *
*/ */
protected function _method_sort($a, $b) protected function _method_sort($a, $b)
@ -276,4 +277,5 @@ class Kohana_Kodoc_Class extends Kodoc {
return $result; return $result;
} }
}
} // End Kodoc_Class

View File

@ -5,8 +5,8 @@
* @package Kohana/Userguide * @package Kohana/Userguide
* @category Base * @category Base
* @author Kohana Team * @author Kohana Team
* @copyright (c) 2009-2012 Kohana Team * @copyright (c) 2008-2013 Kohana Team
* @license http://kohanaphp.com/license * @license http://kohanaframework.org/license
*/ */
class Kohana_Kodoc_Markdown extends MarkdownExtra_Parser { class Kohana_Kodoc_Markdown extends MarkdownExtra_Parser {
@ -23,6 +23,7 @@ class Kohana_Kodoc_Markdown extends MarkdownExtra_Parser {
/** /**
* Currently defined heading ids. * Currently defined heading ids.
* Used to prevent creating multiple headings with same id. * Used to prevent creating multiple headings with same id.
*
* @var array * @var array
*/ */
protected $_heading_ids = array(); protected $_heading_ids = array();
@ -36,7 +37,7 @@ class Kohana_Kodoc_Markdown extends MarkdownExtra_Parser {
* Slightly less terrible way to make it so the TOC only shows up when we * Slightly less terrible way to make it so the TOC only shows up when we
* want it to. set this to true to show the toc. * want it to. set this to true to show the toc.
*/ */
public static $show_toc = false; public static $show_toc = FALSE;
/** /**
* Transform some text using [Kodoc_Markdown] * Transform some text using [Kodoc_Markdown]
@ -78,8 +79,8 @@ class Kohana_Kodoc_Markdown extends MarkdownExtra_Parser {
// Show table of contents for userguide pages // Show table of contents for userguide pages
$this->document_gamut['doTOC'] = 100; $this->document_gamut['doTOC'] = 100;
// PHP4 makes me sad. // Call parent constructor.
parent::MarkdownExtra_Parser(); parent::__construct();
} }
/** /**
@ -93,20 +94,22 @@ class Kohana_Kodoc_Markdown extends MarkdownExtra_Parser {
*/ */
function _doHeaders_callback_setext($matches) function _doHeaders_callback_setext($matches)
{ {
if ($matches[3] == '-' && preg_match('{^- }', $matches[1])) if ($matches[3] == '-' AND preg_match('{^- }', $matches[1]))
return $matches[0]; return $matches[0];
$level = $matches[3]{0} == '=' ? 1 : 2; $level = ($matches[3]{0} == '=') ? 1 : 2;
$attr = $this->_doHeaders_attr($id =& $matches[2]); $attr = $this->_doHeaders_attr($id =& $matches[2]);
// Only auto-generate id if one doesn't exist // Only auto-generate id if one doesn't exist
if(empty($attr)) if (empty($attr))
{
$attr = ' id="'.$this->make_heading_id($matches[1]).'"'; $attr = ' id="'.$this->make_heading_id($matches[1]).'"';
}
// Add this header to the page toc // Add this header to the page toc
$this->_add_to_toc($level,$matches[1],$this->make_heading_id($matches[1])); $this->_add_to_toc($level,$matches[1],$this->make_heading_id($matches[1]));
$block = "<h$level$attr>".$this->runSpanGamut($matches[1])."</h$level>"; $block = "<h$level$attr>".$this->runSpanGamut($matches[1])."</h$level>";
return "\n" . $this->hashBlock($block) . "\n\n"; return "\n".$this->hashBlock($block)."\n\n";
} }
/** /**
@ -123,14 +126,16 @@ class Kohana_Kodoc_Markdown extends MarkdownExtra_Parser {
$attr = $this->_doHeaders_attr($id =& $matches[3]); $attr = $this->_doHeaders_attr($id =& $matches[3]);
// Only auto-generate id if one doesn't exist // Only auto-generate id if one doesn't exist
if(empty($attr)) if (empty($attr))
{
$attr = ' id="'.$this->make_heading_id($matches[2]).'"'; $attr = ' id="'.$this->make_heading_id($matches[2]).'"';
}
// Add this header to the page toc // Add this header to the page toc
$this->_add_to_toc($level, $matches[2], $this->make_heading_id(empty($matches[3]) ? $matches[2] : $matches[3])); $this->_add_to_toc($level, $matches[2], $this->make_heading_id(empty($matches[3]) ? $matches[2] : $matches[3]));
$block = "<h$level$attr>".$this->runSpanGamut($matches[2])."</h$level>"; $block = "<h$level$attr>".$this->runSpanGamut($matches[2])."</h$level>";
return "\n" . $this->hashBlock($block) . "\n\n"; return "\n".$this->hashBlock($block)."\n\n";
} }
@ -145,7 +150,7 @@ class Kohana_Kodoc_Markdown extends MarkdownExtra_Parser {
{ {
$id = url::title($heading, '-', TRUE); $id = url::title($heading, '-', TRUE);
if(isset($this->_heading_ids[$id])) if (isset($this->_heading_ids[$id]))
{ {
$id .= '-'; $id .= '-';
@ -166,8 +171,6 @@ class Kohana_Kodoc_Markdown extends MarkdownExtra_Parser {
{ {
$replace = array(); $replace = array();
$replace = array();
foreach ($matches as $set) foreach ($matches as $set)
{ {
list($search, $view) = $set; list($search, $view) = $set;
@ -203,7 +206,7 @@ class Kohana_Kodoc_Markdown extends MarkdownExtra_Parser {
* *
* [filesystem](about.filesystem "Optional title") * [filesystem](about.filesystem "Optional title")
* *
* @param string span text * @param string Span text
* @return string * @return string
*/ */
public function doBaseURL($text) public function doBaseURL($text)
@ -217,7 +220,7 @@ class Kohana_Kodoc_Markdown extends MarkdownExtra_Parser {
* *
* ![Install Page](img/install.png "Optional title") * ![Install Page](img/install.png "Optional title")
* *
* @param string span text * @param string Span text
* @return string * @return string
*/ */
public function doImageURL($text) public function doImageURL($text)
@ -231,7 +234,7 @@ class Kohana_Kodoc_Markdown extends MarkdownExtra_Parser {
* *
* [Class_Name], [Class::method] or [Class::$property] * [Class_Name], [Class::method] or [Class::$property]
* *
* @param string span text * @param string Span text
* @return string * @return string
*/ */
public function doAPI($text) public function doAPI($text)
@ -244,7 +247,7 @@ class Kohana_Kodoc_Markdown extends MarkdownExtra_Parser {
* *
* [!!] Remember the milk! * [!!] Remember the milk!
* *
* @param string span text * @param string Span text
* @return string * @return string
*/ */
public function doNotes($text) public function doNotes($text)

View File

@ -5,8 +5,8 @@
* @package Kohana/Userguide * @package Kohana/Userguide
* @category Base * @category Base
* @author Kohana Team * @author Kohana Team
* @copyright (c) 2009 Kohana Team * @copyright (c) 2008-2013 Kohana Team
* @license http://kohanaphp.com/license * @license http://kohanaframework.org/license
*/ */
class Kohana_Kodoc_Method extends Kodoc { class Kohana_Kodoc_Method extends Kodoc {
@ -16,17 +16,17 @@ class Kohana_Kodoc_Method extends Kodoc {
public $method; public $method;
/** /**
* @var array array of Kodoc_Method_Param * @var array Array of Kodoc_Method_Param
*/ */
public $params; public $params;
/** /**
* @var array the things this function can return * @var array The things this function can return
*/ */
public $return = array(); public $return = array();
/** /**
* @var string the source code for this function * @var string The source code for this function
*/ */
public $source; public $source;

View File

@ -5,8 +5,8 @@
* @package Kohana/Userguide * @package Kohana/Userguide
* @category Base * @category Base
* @author Kohana Team * @author Kohana Team
* @copyright (c) 2009 Kohana Team * @copyright (c) 2008-2013 Kohana Team
* @license http://kohanaphp.com/license * @license http://kohanaframework.org/license
*/ */
class Kohana_Kodoc_Method_Param extends Kodoc { class Kohana_Kodoc_Method_Param extends Kodoc {

View File

@ -7,6 +7,8 @@
* @package Kohana/Userguide * @package Kohana/Userguide
* @category Undocumented * @category Undocumented
* @author Kohana Team * @author Kohana Team
* @copyright (c) 2008-2013 Kohana Team
* @license http://kohanaframework.org/license
* @since 3.0.7 * @since 3.0.7
*/ */
abstract class Kohana_Kodoc_Missing { abstract class Kohana_Kodoc_Missing {

View File

@ -5,8 +5,8 @@
* @package Kohana/Userguide * @package Kohana/Userguide
* @category Base * @category Base
* @author Kohana Team * @author Kohana Team
* @copyright (c) 2009-2012 Kohana Team * @copyright (c) 2008-2013 Kohana Team
* @license http://kohanaphp.com/license * @license http://kohanaframework.org/license
*/ */
class Kohana_Kodoc_Property extends Kodoc { class Kohana_Kodoc_Property extends Kodoc {

View File

@ -24,10 +24,18 @@
"kohana/core": ">=3.3", "kohana/core": ">=3.3",
"php": ">=5.3.3" "php": ">=5.3.3"
}, },
"require-dev": {
"kohana/core": "3.3.*@dev",
"kohana/unittest": "3.3.*@dev",
"kohana/koharness": "*@dev"
},
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-3.3/develop": "3.3.x-dev", "dev-3.3/develop": "3.3.x-dev",
"dev-3.4/develop": "3.4.x-dev" "dev-3.4/develop": "3.4.x-dev"
},
"installer-paths": {
"vendor/{$vendor}/{$name}": ["type:kohana-module"]
} }
} }
} }

View File

@ -28,7 +28,7 @@ return array
'description' => 'Documentation viewer and api generation.', 'description' => 'Documentation viewer and api generation.',
// Copyright message, shown in the footer for this module // Copyright message, shown in the footer for this module
'copyright' => '&copy; 20082012 Kohana Team', 'copyright' => '&copy; 20082014 Kohana Team',
) )
), ),

View File

@ -1,33 +1,37 @@
# Adding your module to the userguide # Adding your module
Making your module work with the userguide is simple. Making your module work with the User Guide is simple.
First, copy this config and place in it `<module>/config/userguide.php`, replacing anything in `<>` with the appropriate things: First, copy this config and place in it `<module>/config/userguide.php`, replacing anything in `<>` with the appropriate values:
return array return array
( (
// Leave this alone // Leave this alone
'modules' => array( 'modules' => array(
// This should be the path to this modules userguide pages, without the 'guide/'. Ex: '/guide/modulename/' would be 'modulename' /*
* The path to this module's userguide pages, without the 'guide/'.
*
* For example, '/guide/modulename/' would be 'modulename'
*/
'<modulename>' => array( '<modulename>' => array(
// Whether this modules userguide pages should be shown // Whether this module's user guide pages should be shown
'enabled' => TRUE, 'enabled' => TRUE,
// The name that should show up on the userguide index page // The name that should show up on the user guide index page
'name' => '<Module Name>', 'name' => '<Module Name>',
// A short description of this module, shown on the index page // A short description of this module, shown on the index page
'description' => '<Description goes here.>', 'description' => '<Description goes here>',
// Copyright message, shown in the footer for this module // Copyright message, shown in the footer for this module
'copyright' => '&copy; 20102011 <Your Name>', 'copyright' => '&copy; 2012 <Your Name>',
) )
), ),
/* /*
* If you use transparent extension outside the Kohana_ namespace, * If you use transparent extensions outside the Kohana_ namespace,
* add your class prefix here. Both common Kohana naming conventions are * add your class prefix here. Both common Kohana naming conventions are
* excluded: * excluded:
* - Modulename extends Modulename_Core * - Modulename extends Modulename_Core
@ -41,7 +45,11 @@ First, copy this config and place in it `<module>/config/userguide.php`, replaci
) )
); );
Next, create a folder in your module directory called `guide/<modulename>` and create `index.md` and `menu.md`. All userguide pages use [Markdown](markdown). The index page is what is shown on the index of your module, the menu is what shows up in the side column. The menu should be formatted like this: Next, create a folder in your module directory called `guide/<modulename>` and create `index.md` and `menu.md`. The contents of `index.md` is what is shown on the index page of your module.
## Creating the side menu
The contents of the `menu.md` file is what shows up in the side column and should be formatted like this:
## [Module Name]() ## [Module Name]()
- [Page name](page-path) - [Page name](page-path)
@ -52,4 +60,16 @@ Next, create a folder in your module directory called `guide/<modulename>` and c
- Categories do not have to be a link to a page - Categories do not have to be a link to a page
- [Etcetera](etc) - [Etcetera](etc)
Page paths are relative to `guide/<modulename>`. So `[Page name](path-path)` would look for `guide/<modulename>/page-name.md` and `[Another](category/another)` would look for `guide/<modulename>/page-name.md`. The guide pages can be named or arranged any way you want within that folder (with the exception of `menu.md` and `index.md`). The breadcrumbs and page titles are pulled from the `menu.md file`, not the file names or paths. You can have items that are not pages (a category that doesn't have a corresponding page). To link to the `index.md` page, you should have an empty link, e.g. `[Module Name]()`. Do not include `.md` in your links. You can have items that are not linked pages (a category that doesn't have a corresponding page).
Guide pages can be named or arranged any way you want within that folder (with the exception of `index.md` and `menu.md` which must appear directly below the `guide/` folder).
## Formatting page titles and links
Page paths are relative to `guide/<modulename>`. So `[Page name](page-name)` would look for `guide/<modulename>/page-name.md` and `[Another](category/another)` would look for `guide/<modulename>/category/another.md`.
The breadcrumbs and page titles are pulled from the `menu.md` file, not the filenames or paths.
To link to the `index.md` page, you should have an empty link, e.g. `[Module Name]()`. Do not include `.md` in your links.
All user guide pages use [Markdown](markdown).

View File

@ -1,7 +1,10 @@
## [Userguide]() ## [Userguide]()
- Getting Started
- [Using the Userguide](using) - [Using the Userguide](using)
- [How userguide works](works) - [How the Userguide works](works)
- [Configuration](config)
- Learning More
- [Contributing](contributing) - [Contributing](contributing)
- [Markdown Syntax](markdown) - [Markdown Syntax](markdown)
- [Configuration](config)
- [Adding your module](adding) - [Adding your module](adding)

View File

@ -0,0 +1,8 @@
<?php
// Configuration for koharness - builds a standalone skeleton Kohana app for running unit tests
return array(
'modules' => array(
'userguide' => __DIR__,
'unittest' => __DIR__ . '/vendor/kohana/unittest'
),
);

View File

@ -99,7 +99,7 @@ h6:hover a.permalink {
#kodoc-content, #kodoc-content,
#kodoc-footer { float: left; clear: both; width: 100%; } #kodoc-footer { float: left; clear: both; width: 100%; }
#kodoc-header { padding: 58px 0 2em; background: #77c244 url(../img/header.png) center top repeat-x; } #kodoc-header { padding: 20px 0 2em; background: #77c244 url(../img/header.png) center top repeat-x; }
#kodoc-logo { display: block; float: left; } #kodoc-logo { display: block; float: left; }
#kodoc-menu { float: right; margin-top: 12px; background: #113c32; -moz-border-radius: 5px; -webkit-border-radius: 5px; } #kodoc-menu { float: right; margin-top: 12px; background: #113c32; -moz-border-radius: 5px; -webkit-border-radius: 5px; }
#kodoc-menu ul { float: left; margin: 0; padding: 0 0.5em 0 0; } #kodoc-menu ul { float: left; margin: 0; padding: 0 0.5em 0 0; }
@ -125,11 +125,23 @@ h6:hover a.permalink {
#kodoc-topics { } #kodoc-topics { }
#kodoc-topics ul, #kodoc-topics ul,
#kodoc-topics ol { list-style-type:none; margin: 0; padding: 0;} #kodoc-topics ol { list-style-type:none; margin: 0 0 0 0; padding: 0;}
#kodoc-topics ul li, #kodoc-topics ul li,
#kodoc-topics ol li { margin:0; padding: 0; margin-left: 1em; } #kodoc-topics ol li { margin:0; padding: 0; margin-left: 1em; }
#kodoc-topics ul li a.current, #kodoc-topics ul li a.current,
#kodoc-topics ol li a.current { font-weight: bold; } #kodoc-topics ol li a.current { font-weight: bold; }
#kodoc-topics > ul > li {
margin-bottom: 20px;
}
#kodoc-topics > ul > li > a,
#kodoc-topics > ul > li > span {
font-weight: bold;
color: #EE8C0E;
}
#kodoc-topics span, #kodoc-topics span,
#kodoc-topics a { display: block; padding: 0; margin: 0; } #kodoc-topics a { display: block; padding: 0; margin: 0; }
#kodoc-topics span { cursor: pointer; } #kodoc-topics span { cursor: pointer; }

View File

@ -32,41 +32,6 @@ $(document).ready(function()
}); });
}); });
// Collapsing menus
$('#kodoc-topics li:has(li)').each(function()
{
var $this = $(this);
var toggle = $('<span class="toggle"></span>');
var menu = $this.find('>ul,>ol');
toggle.click(function()
{
if (menu.is(':visible'))
{
menu.stop(true, true).slideUp('fast');
toggle.html('+');
}
else
{
menu.stop(true, true).slideDown('fast');
toggle.html('&ndash;');
}
});
$this.find('>span').click(function()
{
// Menu without a link
toggle.click();
});
if ( ! $this.is(':has(a.current)'))
{
menu.hide();
}
toggle.html(menu.is(':visible') ? '&ndash;' : '+').prependTo($this);
});
// Show source links // Show source links
$('#kodoc-main .method-source').each(function() $('#kodoc-main .method-source').each(function()

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