Compare commits

...
This repository has been archived on 2024-04-08. You can view files and clone it, but cannot push or open issues or pull requests.

1 Commits

Author SHA1 Message Date
Deon George
68c7f4f159 Kohana v3.3.5 2016-05-01 20:50:24 +10:00
170 changed files with 4565 additions and 1176 deletions

View File

@ -1,13 +1,13 @@
# 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/develop
- 3.3/master
- 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
@ -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/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.
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"
# ...
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
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
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.
@ -68,7 +68,7 @@ The naming convention for feature branches is:
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
`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.
Here's a quick example:
@ -81,7 +81,7 @@ Here's a quick example:
> 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
@ -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.
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.**
@ -101,11 +101,11 @@ branch is perfectly acceptable.
## 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`
# 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
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
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:
## 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"
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
@ -147,14 +147,14 @@ i.e.
> 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.
Git will remove the commit and you can then pull/merge the remote changes.
# 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
@ -164,4 +164,4 @@ Kohana currently uses phpunit for unit testing. This is installed with composer.
* Run `php composer.phar install` from the root of this repository
* Finally, run `phing test`
This will run the unit tests for core and all the modules and tell you if anything failed. If you haven't changed anything and you get failures, please create a new issue on [the tracker](http://dev.kohanaframework.org) and paste the output (including the error) in the issue.
This will run the unit tests for core and all the modules and tell you if anything failed. If you haven't changed anything and you get failures, please create a new issue on [the tracker](http://dev.kohanaframework.org) and paste the output (including the error) in the issue.

View File

@ -131,6 +131,15 @@ Kohana::modules(array(
// '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
* defaults for the URI.

View File

@ -1,6 +1,6 @@
{
"require": {
"phpunit/phpunit": "3.7.24",
"phpunit/phpunit": "3.7.24 - 4",
"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:

View File

@ -24,10 +24,18 @@
"kohana/core": ">=3.3",
"php": ">=5.3.3"
},
"require-dev": {
"kohana/core": "3.3.*@dev",
"kohana/unittest": "3.3.*@dev",
"kohana/koharness": "*@dev"
},
"extra": {
"branch-alias": {
"dev-3.3/develop": "3.3.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
====================
| 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.
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.
*
* return array(
* 'default' => array( // Default group
* 'memcache' => array( // Name of group
* 'driver' => 'memcache', // using Memcache driver
* 'servers' => array( // Available server definitions
* array(
@ -49,8 +49,12 @@
* ),
* )
*
* 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.
* In cases where only one cache group is required, set `Cache::$default` (in your bootstrap,
* 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
*

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
{
// Open the file and parse data
$created = $file->getMTime();
$data = $file->openFile();
$lifetime = $data->fgets();
// If we're at the EOF at this point, corrupted!
if ($data->eof())
// Test the expiry
if ($this->_is_expired($file))
{
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 = '';
while ($data->eof() === FALSE)
@ -158,17 +162,7 @@ class Kohana_Cache_File extends Cache implements Cache_GarbageCollect {
$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);
}
}
@ -219,14 +213,7 @@ class Kohana_Cache_File extends Cache implements Cache_GarbageCollect {
// If the directory path is not a directory
if ( ! $dir->isDir())
{
// Create the directory
if ( ! mkdir($directory, 0777, TRUE))
{
throw new Cache_Exception(__METHOD__.' unable to create directory : :directory', array(':directory' => $directory));
}
// chmod to solve potential umask issues
chmod($directory, 0777);
$this->_make_directory($directory, 0777, TRUE);
}
// 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));
$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
{
// Assess the file expiry to flag it for deletion
$json = $file->openFile('r')->current();
$data = json_decode($json);
$delete = $data->expiry < time();
$delete = $this->_is_expired($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
$fp = new SplFileInfo($files->getRealPath());
// Delete the file
$this->_delete_file($fp);
$this->_delete_file($fp, $retain_parent_directory, $ignore_errors, $only_expired);
}
// Move the file pointer on
@ -446,21 +431,55 @@ class Kohana_Cache_File extends Cache implements Cache_GarbageCollect {
* `mkdir` to ensure DRY principles
*
* @link http://php.net/manual/en/function.mkdir.php
* @param string $directory
* @param integer $mode
* @param boolean $recursive
* @param resource $context
* @param string $directory directory path
* @param integer $mode chmod mode
* @param boolean $recursive allows nested directories creation
* @param resource $context a stream context
* @return SplFileInfo
* @throws Cache_Exception
*/
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));
}
// chmod to solve potential umask issues
chmod($directory, $mode);
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.');
/**
* 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
* RFC 2616.
*
@ -36,7 +36,7 @@ class Kohana_HTTP_Cache {
* )
* );
*
* @uses [Cache]
* @uses Cache
* @param mixed $cache cache engine to use
* @param array $options options to set to this class
* @return HTTP_Cache
@ -298,7 +298,7 @@ class Kohana_HTTP_Cache {
* Controls whether the response can be cached. Uses HTTP
* protocol to determine whether the response can be cached.
*
* @link RFC 2616 http://www.w3.org/Protocols/rfc2616/
* @link http://www.w3.org/Protocols/rfc2616/rfc2616.html RFC 2616
* @param Response $response The Response
* @return boolean
*/

View File

@ -24,10 +24,18 @@
"kohana/core": ">=3.3",
"php": ">=5.3.3"
},
"require-dev": {
"kohana/core": "3.3.*@dev",
"kohana/unittest": "3.3.*@dev",
"kohana/koharness": "*@dev"
},
"extra": {
"branch-alias": {
"dev-3.3/develop": "3.3.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',
'default_expire' => 3600,
'compression' => FALSE, // Use Zlib compression
(can cause issues with integers)
// (can cause issues with integers)
'servers' => array
(
'local' => array
@ -71,7 +71,7 @@ failure_callback | __NO__ | (_[callback](http://www.php.net/manual/en/language
'driver' => 'memcachetag',
'default_expire' => 3600,
'compression' => FALSE, // Use Zlib compression
(can cause issues with integers)
// (can cause issues with integers)
'servers' => array
(
'local' => array
@ -90,7 +90,15 @@ failure_callback | __NO__ | (_[callback](http://www.php.net/manual/en/language
'driver' => 'apc',
'default_expire' => 3600,
),
## APCu settings
'apcu' => array
(
'driver' => 'apcu',
'default_expire' => 3600,
),
## SQLite settings
'sqlite' => array

View File

@ -6,7 +6,7 @@ instances of cache engines through a grouped singleton pattern.
## Supported cache engines
* APC ([Cache_Apc])
* APC/APCu ([Cache_Apc])
* File ([Cache_File])
* Memcached ([Cache_Memcache])
* Memcached-tags ([Cache_Memcachetag])
@ -16,13 +16,13 @@ instances of cache engines through a grouped singleton pattern.
## Introduction to caching
Caching should be implemented with consideration. Generally, caching the result of resources
is faster than reprocessing them. Choosing what, how and when to cache is vital. [PHP APC](http://php.net/manual/en/book.apc.php) is one of the fastest caching systems available, closely followed by [Memcached](http://memcached.org/). [SQLite](http://www.sqlite.org/) and File caching are two of the slowest cache methods, however usually faster than reprocessing
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.
Caching engines that use memory are considerably faster than file based alternatives. But
memory is limited whereas disk space is plentiful. If caching large datasets, such as large database result sets, it is best to use file caching.
[!!] Cache drivers require the relevant PHP extensions to be installed. APC, eAccelerator, Memecached and Xcache all require non-standard PHP extensions.
[!!] 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)
@ -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
---------------- | ------------ | --------- | -------- | ----------- | ---------------------------- | -----------------------
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
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
@ -54,4 +54,4 @@ It is possible to have hybrid cache solutions that use a combination of the engi
## Minimum requirements
* Kohana 3.0.4
* PHP 5.2.4 or greater
* PHP 5.2.4 or greater

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
$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.
@ -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.
[!!] 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
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.
[!!] 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 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.
@ -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.
[!!] 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
$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'
),
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(
'id' => 'bar',
@ -196,6 +207,28 @@ TESTTEXT;
'default' => NULL,
),
$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()
{
if ( ! Kohana::$config->load('cache.file'))
{
$this->markTestSkipped('Unable to load File configuration');
}
$cache = $this->getMockBuilder('Cache')
->disableOriginalConstructor()
->getMockForAbstractClass();
try
{
$cache_clone = clone(Cache::instance('file'));
clone($cache);
}
catch (Cache_Exception $e)
{

View File

@ -30,7 +30,21 @@ class Kohana_Cache_FileTest extends Kohana_CacheBasicMethodsTest {
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'));
@ -45,7 +59,7 @@ class Kohana_Cache_FileTest extends Kohana_CacheBasicMethodsTest {
{
$cache = $this->cache();
$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
file_put_contents($file, 'foobar');
@ -95,4 +109,55 @@ class Kohana_Cache_FileTest extends Kohana_CacheBasicMethodsTest {
$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

View File

@ -35,7 +35,16 @@ class Kohana_SqliteTest extends Kohana_CacheBasicMethodsTest {
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'));

View File

@ -1,39 +1,49 @@
<?php
include_once(Kohana::find_file('tests/cache', 'CacheBasicMethodsTest'));
/**
* @package Kohana/Cache
* @group kohana
* @group kohana.cache
* @category Test
* @author Kohana Team
* @copyright (c) 2009-2012 Kohana Team
* @license http://kohanaphp.com/license
*/
class Kohana_WincacheTest extends Kohana_CacheBasicMethodsTest {
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'));
/**
* 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
* @package Kohana/Cache
* @group kohana
* @group kohana.cache
* @category Test
* @author Kohana Team
* @copyright (c) 2009-2012 Kohana Team
* @license http://kohanaphp.com/license
*/
public function setUp()
{
parent::setUp();
class Kohana_WincacheTest extends Kohana_CacheBasicMethodsTest {
if ( ! extension_loaded('wincache'))
/**
* 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()
{
$this->markTestSkipped('Wincache PHP Extension is not available');
parent::setUp();
if ( ! extension_loaded('wincache'))
{
$this->markTestSkipped('Wincache PHP Extension is not available');
}
$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');
}
if ( ! Kohana::$config->load('cache.apc'))
{
Kohana::$config->load('cache')
->set(
'apc',
array(
'driver' => 'apc',
'default_expire' => 3600,
)
);
}
$this->cache(Cache::instance('apc'));
}
@ -57,7 +69,7 @@ class Kohana_ApcTest extends Kohana_CacheArithmeticMethodsTest {
*
* @dataProvider provider_set_get
*
* @param array data
* @param array data
* @param mixed expected
* @return void
*/

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'))
{
$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;

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",
"php": ">=5.3.3"
},
"require-dev": {
"kohana/core": "3.3.*@dev",
"kohana/unittest": "3.3.*@dev",
"kohana/koharness": "*@dev"
},
"extra": {
"branch-alias": {
"dev-3.3/develop": "3.3.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.
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 seven ways to validate HTML color values**
@ -73,4 +73,4 @@ Here is another short example with some extra explanations.
And the winner is… [ltrim](http://php.net/ltrim). Happy benchmarking!
And the winner is… [ltrim](http://php.net/ltrim). Happy benchmarking!

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
* @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
*
* 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
* @category Configuration
* @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
$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)
{
// Return an stdClass
@ -58,8 +61,32 @@ class Kohana_Database_MySQL_Result extends Database_Result {
}
elseif (is_string($this->_as_object))
{
// Return an object of given class name
return mysql_fetch_object($this->_result, $this->_as_object, $this->_object_params);
/* 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);
}
else
{
// Return an object of given class name without constructor params
return mysql_fetch_object($this->_result, $this->_as_object);
}
}
else
{

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()),
$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"
$op = 'IS';
}
elseif ($op === '!=')
elseif ($op === '!=' OR $op === '<>')
{
// Convert "val != NULL" to "valu IS NOT NULL"
$op = 'IS NOT';

View File

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

View File

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

View File

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

View File

@ -22,8 +22,13 @@
"require": {
"composer/installers": "~1.0",
"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": {
"ext-mysql": "*",
"ext-pdo": "*"
@ -32,6 +37,9 @@
"branch-alias": {
"dev-3.3/develop": "3.3.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',
'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

@ -10,6 +10,7 @@ The database configuration file contains an array of configuration groups. The s
'table_prefix' => string TABLE_PREFIX,
'charset' => string CHARACTER_SET,
),
Understanding each of these settings is important.
@ -17,7 +18,7 @@ INSTANCE_NAME
: Connections can be named anything you want, but you should always have at least one connection called "default".
DATABASE_TYPE
: One of the installed database drivers. Kohana comes with "mysql" and "pdo" drivers. Drivers must extend the Database class.
: 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
: 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
(
'type' => 'mysql',
'type' => 'MySQL',
'connection' => array(
'hostname' => 'localhost',
'username' => 'dbuser',
@ -61,7 +62,7 @@ The example file below shows 2 MySQL connections, one local and one remote.
'charset' => 'utf8',
),
'remote' => array(
'type' => 'mysql',
'type' => 'MySQL',
'connection' => array(
'hostname' => '55.55.55.55',
'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
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`
`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
A [PDO database](http://php.net/manual/en/book.pdo.php) can accept these options in the `connection` array:
@ -128,4 +149,4 @@ Type | Option | Description | Default value
The connection character set should be configured using the DSN string or `options` array.
[!!] If you are using PDO and are not sure what to use for the `dsn` option, review [PDO::__construct](http://php.net/pdo.construct).
[!!] If you are using PDO and are not sure what to use for the `dsn` option, review [PDO::__construct](http://php.net/pdo.construct).

View File

@ -25,7 +25,7 @@ In this example, we loop through an array of whitelisted input fields and for ea
//copy the query & execute it
$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
$config = Kohana::$config->load('pagination');
@ -49,4 +49,4 @@ In this example, we loop through an array of whitelisted input fields and for ea
TODO: example goes here
[!!] We could use more examples on this page.
[!!] We could use more examples on this page.

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.
$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:
@ -248,4 +248,4 @@ Once you are done building, you can execute the query using `execute()` and use
To use a different database [config group](config) pass either the name or the config object to `execute()`.
$result = $query->execute('config_name')
$result = $query->execute('config_name')

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

View File

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

View File

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

View File

@ -24,6 +24,11 @@
"kohana/core": ">=3.3",
"php": ">=5.3.3"
},
"require-dev": {
"kohana/core": "3.3.*@dev",
"kohana/unittest": "3.3.*@dev",
"kohana/koharness": "*@dev"
},
"suggest": {
"ext-gd": "*"
},
@ -31,6 +36,9 @@
"branch-alias": {
"dev-3.3/develop": "3.3.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
[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
@ -18,4 +35,4 @@ Kohana::modules(array(
));
~~~
Next: [Using the image module](using).
Next: [Using the image module](using).

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
| 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.
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
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.
unlink($vbscript);
@ -278,7 +278,7 @@ class Kohana_Minion_CLI {
* @copyright 2010 - 2011 Fuel Development Team
* @link http://fuelphp.com
* @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
* @return string the color coded string
*/

View File

@ -1,6 +1,6 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* Minipn exception
* Minion exception
*
* @package Kohana
* @category Minion
@ -15,8 +15,6 @@ class Kohana_Minion_Exception extends Kohana_Exception {
*
* 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
* @param Exception $e
* @return boolean
@ -25,6 +23,9 @@ class Kohana_Minion_Exception extends Kohana_Exception {
{
try
{
// Log the exception
Kohana_Exception::log($e);
if ($e instanceof Minion_Exception)
{
echo $e->format_for_cli();

View File

@ -24,10 +24,18 @@
"kohana/core": ">=3.3",
"php": ">=5.3.3"
},
"require-dev": {
"kohana/core": "3.3.*@dev",
"kohana/unittest": "3.3.*@dev",
"kohana/koharness": "*@dev"
},
"extra": {
"branch-alias": {
"dev-3.3/develop": "3.3.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')
{
// Ignore any selected columns for now
$selects[] = $method;
$selects[$key] = $method;
unset($this->_db_pending[$key]);
}
}

View File

@ -25,10 +25,19 @@
"kohana/database": ">=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": {
"branch-alias": {
"dev-3.3/develop": "3.3.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
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.
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

View File

@ -121,5 +121,10 @@ if (($ob_len = ob_get_length()) !== FALSE)
}
}
// Enable the unittest module
Kohana::modules(Kohana::modules() + array('unittest' => MODPATH.'unittest'));
// Enable the unittest module if it is not already loaded - use the absolute path
$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)
{
if ($module->isDir())
if ($module->isDir() AND ! $module->isDot())
{
$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
*
@ -10,11 +10,6 @@
*/
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
@ -49,16 +44,6 @@ abstract class Kohana_Unittest_Database_TestCase extends PHPUnit_Extensions_Data
*/
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->setEnvironment($this->environmentDefault);
@ -161,151 +146,4 @@ abstract class Kohana_Unittest_Database_TestCase extends PHPUnit_Extensions_Data
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

View File

@ -6,12 +6,6 @@
*/
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
* @var boolean
@ -39,16 +33,6 @@ abstract class Kohana_Unittest_TestCase extends PHPUnit_Framework_TestCase {
*/
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->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
* @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.
* NOTE:
* Overriding this method to remove the deprecation error
* when tested with PHPUnit 4.2.0+
*
* @param string $expected
* @param string $attributeName
* @param mixed $classOrObject
* 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
* @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 assertTag($matcher, $actual, $message = '', $isHtml = true)
{
if(self::$_assert_type_compatability)
{
return self::assertAttributeType($expected, $attributeName, $classOrObject, $message);
}
//trigger_error(__METHOD__ . ' is deprecated', E_USER_DEPRECATED);
return parent::assertAttributeInstanceOf($expected, $attributeName, $classOrObject, $message);
$matched = static::tag_match($matcher, $actual, $message, $isHtml);
static::assertTrue($matched, $message);
}
/**
* Asserts that a variable is not of a given type.
* This assertion is the exact opposite of assertTag
*
* @param string $expected
* @param mixed $actual
* Rather than asserting that $matcher results in a match, it asserts that
* $matcher does not match
*
* 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
* @since Method available since Release 3.5.0
* @param bool $isHtml
* @uses Unittest_TestCase::tag_match
*/
public static function assertNotInstanceOf($expected, $actual, $message = '')
public static function assertNotTag($matcher, $actual, $message = '', $isHtml = true)
{
if(self::$_assert_type_compatability)
{
return self::assertNotType($expected, $actual, $message);
}
//trigger_error(__METHOD__ . ' is deprecated', E_USER_DEPRECATED);
return parent::assertNotInstanceOf($expected, $actual, $message);
$matched = static::tag_match($matcher, $actual, $message, $isHtml);
static::assertFalse($matched, $message);
}
/**
* Asserts that an attribute is of a given type.
* Helper function to match HTML string tags against certain criteria
*
* @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 parent::assertAttributeNotInstanceOf($expected, $attributeName, $classOrObject, $message);
}
/**
* Asserts that a variable is of a given type.
* TODO:
* this should be removed when phpunit-dom-assertions gets released
* https://github.com/phpunit/phpunit-dom-assertions
*
* @param string $expected
* @param mixed $actual
* @param array $matcher
* @param string $actual
* @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 assertInternalType($expected, $actual, $message = '')
protected static function tag_match($matcher, $actual, $message = '', $isHtml = true)
{
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);
$dom = PHPUnit_Util_XML::load($actual, $isHtml);
$tags = PHPUnit_Util_XML::findNodes($dom, $matcher, $isHtml);
return count($tags) > 0 && $tags[0] instanceof DOMNode;
}
}

View File

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

View File

@ -23,12 +23,19 @@
"composer/installers": "~1.0",
"kohana/core": ">=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": {
"branch-alias": {
"dev-3.3/develop": "3.3.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?
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.
@ -66,4 +73,4 @@ If you want to have parameters, only put the brackets around the class and funct
You may include a view by putting the name of the view in double curly brackets. **If the view is not found, no exception or error will be shown!** The curly brackets and view will simply be shown an the page as is.
{{some/view}}
{{some/view}}

View File

@ -3,8 +3,10 @@
* Kohana user guide and api browser.
*
* @package Kohana/Userguide
* @category Controllers
* @category Controller
* @author Kohana Team
* @copyright (c) 2008-2013 Kohana Team
* @license http://kohanaframework.org/license
*/
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
else if (Route::name($this->request->route()) == 'docs/api')
elseif (Route::name($this->request->route()) == 'docs/api')
{
$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
if ( $page == 'index')
if ($page == 'index')
{
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 ( ! $page )
if ( ! $page)
{
$page = 'index';
}
@ -141,12 +143,14 @@ abstract class Kohana_Controller_Userguide extends Controller_Template {
Kodoc_Markdown::$image_url = URL::site($this->media->uri()).'/'.$module.'/';
// 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
Kodoc_Markdown::$show_toc = true;
Kodoc_Markdown::$show_toc = TRUE;
$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
$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))
{
// 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);
//echo Debug::vars($text);
$markdown .= $text;
}

View File

@ -5,8 +5,8 @@
* @package Kohana/Userguide
* @category Base
* @author Kohana Team
* @copyright (c) 2008-2012 Kohana Team
* @license http://kohanaphp.com/license
* @copyright (c) 2008-2013 Kohana Team
* @license http://kohanaframework.org/license
*/
class Kohana_Kodoc {
@ -34,6 +34,10 @@ class Kohana_Kodoc {
{
$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
{
$member = '#'.$matches[3];
@ -275,7 +279,7 @@ class Kohana_Kodoc {
* @param string $text Content of the tag
* @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
if ($tag !== 'access')
@ -290,7 +294,7 @@ class Kohana_Kodoc {
}
};
$comment = $tag = null;
$comment = $tag = NULL;
$end = count($lines[1]) - 1;
foreach ($lines[1] as $i => $line)
@ -415,12 +419,12 @@ class Kohana_Kodoc {
*
* Module developers can therefore add their own transparent extension
* namespaces and exclude them from the userguide.
*
* @param string $class The name of the class to check for transparency
* @param array $classes An optional list of all defined classes
* @return false If this is not a transparent extension class
* @return string The name of the class that extends this (in the case provided)
* @throws InvalidArgumentException If the $classes array is provided and the $class variable is not lowercase
*
* @param string $class The name of the class to check for transparency
* @param array $classes An optional list of all defined classes
* @return false If this is not a transparent extension class
* @return string The name of the class that extends this (in the case provided)
* @throws InvalidArgumentException If the $classes array is provided and the $class variable is not lowercase
*/
public static function is_transparent($class, $classes = NULL)
{
@ -447,11 +451,11 @@ class Kohana_Kodoc {
// Cater for Foo extends Module_Foo naming
$child_class = $segments[1];
}
// It is only a transparent class if the unprefixed class also exists
if ($classes AND ! isset($classes[$child_class]))
return FALSE;
// Return the name of the child class
return $child_class;
}
@ -462,5 +466,4 @@ class Kohana_Kodoc {
}
}
} // End Kodoc

View File

@ -5,8 +5,8 @@
* @package Kohana/Userguide
* @category Base
* @author Kohana Team
* @copyright (c) 2009-2012 Kohana Team
* @license http://kohanaphp.com/license
* @copyright (c) 2008-2013 Kohana Team
* @license http://kohanaframework.org/license
*/
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
* comment to find the description and tags.
*
* @param string class name
* @param string Class name
* @return void
*/
public function __construct($class)
@ -59,7 +59,7 @@ class Kohana_Kodoc_Class extends Kodoc {
$this->constants = $this->class->getConstants();
// If ReflectionClass::getParentClass() won't work if the class in
// If ReflectionClass::getParentClass() won't work if the class in
// question is an interface
if ($this->class->isInterface())
{
@ -154,7 +154,7 @@ class Kohana_Kodoc_Class extends Kodoc {
return $props;
}
protected function _prop_sort($a, $b)
{
// If one property is public, and the other is not, it goes on top
@ -162,13 +162,13 @@ class Kohana_Kodoc_Class extends Kodoc {
return -1;
if ($b->isPublic() AND ( ! $a->isPublic()))
return 1;
// If one property is protected and the other is private, it goes on top
if ($a->isProtected() AND $b->isPrivate())
return -1;
if ($b->isProtected() AND $a->isPrivate())
return 1;
// Otherwise just do alphabetical
return strcmp($a->name, $b->name);
}
@ -191,14 +191,15 @@ class Kohana_Kodoc_Class extends Kodoc {
return $methods;
}
/**
* 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
* - lastly, they will be sorted alphabetically
*
* * lastly, they will be sorted alphabetically
*
*/
protected function _method_sort($a, $b)
{
@ -207,16 +208,16 @@ class Kohana_Kodoc_Class extends Kodoc {
return -1;
if ($b->isPublic() AND ( ! $a->isPublic()))
return 1;
// If one method is protected and the other is private, it goes on top
if ($a->isProtected() AND $b->isPrivate())
return -1;
if ($b->isProtected() AND $a->isPrivate())
return 1;
// The methods have the same visibility, so check the declaring class depth:
/*
echo Debug::vars('a is '.$a->class.'::'.$a->name,'b is '.$b->class.'::'.$b->name,
'are the classes the same?', $a->class == $b->class,'if they are, the result is:',strcmp($a->name, $b->name),
@ -276,4 +277,5 @@ class Kohana_Kodoc_Class extends Kodoc {
return $result;
}
}
} // End Kodoc_Class

View File

@ -5,8 +5,8 @@
* @package Kohana/Userguide
* @category Base
* @author Kohana Team
* @copyright (c) 2009-2012 Kohana Team
* @license http://kohanaphp.com/license
* @copyright (c) 2008-2013 Kohana Team
* @license http://kohanaframework.org/license
*/
class Kohana_Kodoc_Markdown extends MarkdownExtra_Parser {
@ -21,9 +21,10 @@ class Kohana_Kodoc_Markdown extends MarkdownExtra_Parser {
public static $image_url = '';
/**
* Currently defined heading ids.
* Currently defined heading ids.
* Used to prevent creating multiple headings with same id.
* @var array
*
* @var 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
* 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]
@ -78,8 +79,8 @@ class Kohana_Kodoc_Markdown extends MarkdownExtra_Parser {
// Show table of contents for userguide pages
$this->document_gamut['doTOC'] = 100;
// PHP4 makes me sad.
parent::MarkdownExtra_Parser();
// Call parent constructor.
parent::__construct();
}
/**
@ -88,25 +89,27 @@ class Kohana_Kodoc_Markdown extends MarkdownExtra_Parser {
* Heading 1
* =========
*
* @param array Matches from regex call
* @return string Generated html
* @param array Matches from regex call
* @return string Generated html
*/
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];
$level = $matches[3]{0} == '=' ? 1 : 2;
$level = ($matches[3]{0} == '=') ? 1 : 2;
$attr = $this->_doHeaders_attr($id =& $matches[2]);
// Only auto-generate id if one doesn't exist
if(empty($attr))
if (empty($attr))
{
$attr = ' id="'.$this->make_heading_id($matches[1]).'"';
}
// Add this header to the page toc
$this->_add_to_toc($level,$matches[1],$this->make_heading_id($matches[1]));
$block = "<h$level$attr>".$this->runSpanGamut($matches[1])."</h$level>";
return "\n" . $this->hashBlock($block) . "\n\n";
return "\n".$this->hashBlock($block)."\n\n";
}
/**
@ -114,23 +117,25 @@ class Kohana_Kodoc_Markdown extends MarkdownExtra_Parser {
*
* # Heading 1
*
* @param array Matches from regex call
* @return string Generated html
* @param array Matches from regex call
* @return string Generated html
*/
function _doHeaders_callback_atx($matches)
function _doHeaders_callback_atx($matches)
{
$level = strlen($matches[1]);
$attr = $this->_doHeaders_attr($id =& $matches[3]);
// Only auto-generate id if one doesn't exist
if(empty($attr))
if (empty($attr))
{
$attr = ' id="'.$this->make_heading_id($matches[2]).'"';
}
// 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]));
$block = "<h$level$attr>".$this->runSpanGamut($matches[2])."</h$level>";
return "\n" . $this->hashBlock($block) . "\n\n";
return "\n".$this->hashBlock($block)."\n\n";
}
@ -138,14 +143,14 @@ class Kohana_Kodoc_Markdown extends MarkdownExtra_Parser {
* Makes a heading id from the heading text
* If any heading share the same name then subsequent headings will have an integer appended
*
* @param string The heading text
* @return string ID for the heading
* @param string The heading text
* @return string ID for the heading
*/
function make_heading_id($heading)
{
$id = url::title($heading, '-', TRUE);
if(isset($this->_heading_ids[$id]))
if (isset($this->_heading_ids[$id]))
{
$id .= '-';
@ -155,8 +160,8 @@ class Kohana_Kodoc_Markdown extends MarkdownExtra_Parser {
{
$id .= $count;
}
}
}
return $id;
}
@ -166,8 +171,6 @@ class Kohana_Kodoc_Markdown extends MarkdownExtra_Parser {
{
$replace = array();
$replace = array();
foreach ($matches as $set)
{
list($search, $view) = $set;
@ -203,7 +206,7 @@ class Kohana_Kodoc_Markdown extends MarkdownExtra_Parser {
*
* [filesystem](about.filesystem "Optional title")
*
* @param string span text
* @param string Span text
* @return string
*/
public function doBaseURL($text)
@ -217,7 +220,7 @@ class Kohana_Kodoc_Markdown extends MarkdownExtra_Parser {
*
* ![Install Page](img/install.png "Optional title")
*
* @param string span text
* @param string Span text
* @return string
*/
public function doImageURL($text)
@ -231,7 +234,7 @@ class Kohana_Kodoc_Markdown extends MarkdownExtra_Parser {
*
* [Class_Name], [Class::method] or [Class::$property]
*
* @param string span text
* @param string Span text
* @return string
*/
public function doAPI($text)
@ -244,7 +247,7 @@ class Kohana_Kodoc_Markdown extends MarkdownExtra_Parser {
*
* [!!] Remember the milk!
*
* @param string span text
* @param string Span text
* @return string
*/
public function doNotes($text)
@ -264,7 +267,7 @@ class Kohana_Kodoc_Markdown extends MarkdownExtra_Parser {
'name' => $name,
'id' => $id);
}
public function doTOC($text)
{
// Only add the toc do userguide pages, not api since they already have one

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -20,16 +20,16 @@ return array
// Whether this modules userguide pages should be shown
'enabled' => TRUE,
// The name that should show up on the userguide index page
'name' => 'Userguide',
// A short description of this module, shown on the index page
'description' => 'Documentation viewer and api generation.',
// Copyright message, shown in the footer for this module
'copyright' => '&copy; 20082012 Kohana Team',
)
'copyright' => '&copy; 20082014 Kohana Team',
)
),
// Set transparent class name segments

View File

@ -1,38 +1,42 @@
# 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
(
// Leave this alone
'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(
// Whether this modules userguide pages should be shown
// Whether this module's user guide pages should be shown
'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>',
// 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' => '&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
* excluded:
* excluded:
* - Modulename extends Modulename_Core
* - Foo extends Modulename_Foo
*
*
* For example, if you use Modulename_<class_name> for your base classes
* then you would define:
*/
@ -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]()
- [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
- [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]()
- [Using the Userguide](using)
- [How userguide works](works)
- [Contributing](contributing)
- [Markdown Syntax](markdown)
- [Configuration](config)
- [Adding your module](adding)
- Getting Started
- [Using the Userguide](using)
- [How the Userguide works](works)
- [Configuration](config)
- Learning More
- [Contributing](contributing)
- [Markdown Syntax](markdown)
- [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-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-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; }
@ -125,11 +125,23 @@ h6:hover a.permalink {
#kodoc-topics { }
#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 ol li { margin:0; padding: 0; margin-left: 1em; }
#kodoc-topics ul li a.current,
#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 a { display: block; padding: 0; margin: 0; }
#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
$('#kodoc-main .method-source').each(function()

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