Added Kohana v3.0.9

This commit is contained in:
Deon George
2011-01-14 01:49:56 +11:00
parent fe11dd5f51
commit b6e9961846
520 changed files with 54728 additions and 0 deletions

View File

@@ -0,0 +1,50 @@
<?php defined('SYSPATH') or die('No direct script access.');
if ( ! defined('KOHANA_START_TIME'))
{
/**
* Define the start time of the application, used for profiling.
*/
define('KOHANA_START_TIME', microtime(TRUE));
}
if ( ! defined('KOHANA_START_MEMORY'))
{
/**
* Define the memory usage at the start of the application, used for profiling.
*/
define('KOHANA_START_MEMORY', memory_get_usage());
}
/**
* Kohana translation/internationalization function. The PHP function
* [strtr](http://php.net/strtr) is used for replacing parameters.
*
* __('Welcome back, :user', array(':user' => $username));
*
* [!!] The target language is defined by [I18n::$lang]. The default source
* language is defined by [I18n::$source].
*
* @uses I18n::get
* @param string text to translate
* @param array values to replace in the translated text
* @param string source language
* @return string
*/
function __($string, array $values = NULL, $source = NULL)
{
if ( ! $source)
{
// Use the default source language
$source = I18n::$source;
}
if ($source !== I18n::$lang)
{
// The message and target languages are different
// Get the translation for this message
$string = I18n::get($string);
}
return empty($values) ? $string : strtr($string, $values);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,547 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* Array helper.
*
* @package Kohana
* @category Helpers
* @author Kohana Team
* @copyright (c) 2007-2010 Kohana Team
* @license http://kohanaframework.org/license
*/
class Kohana_Arr {
/**
* @var string default delimiter for path()
*/
public static $delimiter = '.';
/**
* Tests if an array is associative or not.
*
* // Returns TRUE
* Arr::is_assoc(array('username' => 'john.doe'));
*
* // Returns FALSE
* Arr::is_assoc('foo', 'bar');
*
* @param array array to check
* @return boolean
*/
public static function is_assoc(array $array)
{
// Keys of the array
$keys = array_keys($array);
// If the array keys of the keys match the keys, then the array must
// not be associative (e.g. the keys array looked like {0:0, 1:1...}).
return array_keys($keys) !== $keys;
}
/**
* Test if a value is an array with an additional check for array-like objects.
*
* // Returns TRUE
* Arr::is_array(array());
* Arr::is_array(new ArrayObject);
*
* // Returns FALSE
* Arr::is_array(FALSE);
* Arr::is_array('not an array!');
* Arr::is_array(Database::instance());
*
* @param mixed value to check
* @return boolean
*/
public static function is_array($value)
{
if (is_array($value))
{
// Definitely an array
return TRUE;
}
else
{
// Possibly a Traversable object, functionally the same as an array
return (is_object($value) AND $value instanceof Traversable);
}
}
/**
* Gets a value from an array using a dot separated path.
*
* // Get the value of $array['foo']['bar']
* $value = Arr::path($array, 'foo.bar');
*
* Using a wildcard "*" will search intermediate arrays and return an array.
*
* // Get the values of "color" in theme
* $colors = Arr::path($array, 'theme.*.color');
*
* // Using an array of keys
* $colors = Arr::path($array, array('theme', '*', 'color'));
*
* @param array array to search
* @param mixed key path string (delimiter separated) or array of keys
* @param mixed default value if the path is not set
* @param string key path delimiter
* @return mixed
*/
public static function path($array, $path, $default = NULL, $delimiter = NULL)
{
if ( ! Arr::is_array($array))
{
// This is not an array!
return $default;
}
if (is_array($path))
{
// The path has already been separated into keys
$keys = $path;
}
else
{
if (array_key_exists($path, $array))
{
// No need to do extra processing
return $array[$path];
}
if ($delimiter === NULL)
{
// Use the default delimiter
$delimiter = Arr::$delimiter;
}
// Remove starting delimiters and spaces
$path = ltrim($path, "{$delimiter} ");
// Remove ending delimiters, spaces, and wildcards
$path = rtrim($path, "{$delimiter} *");
// Split the keys by delimiter
$keys = explode($delimiter, $path);
}
do
{
$key = array_shift($keys);
if (ctype_digit($key))
{
// Make the key an integer
$key = (int) $key;
}
if (isset($array[$key]))
{
if ($keys)
{
if (Arr::is_array($array[$key]))
{
// Dig down into the next part of the path
$array = $array[$key];
}
else
{
// Unable to dig deeper
break;
}
}
else
{
// Found the path requested
return $array[$key];
}
}
elseif ($key === '*')
{
// Handle wildcards
$values = array();
foreach ($array as $arr)
{
if ($value = Arr::path($arr, implode('.', $keys)))
{
$values[] = $value;
}
}
if ($values)
{
// Found the values requested
return $values;
}
else
{
// Unable to dig deeper
break;
}
}
else
{
// Unable to dig deeper
break;
}
}
while ($keys);
// Unable to find the value requested
return $default;
}
/**
* Fill an array with a range of numbers.
*
* // Fill an array with values 5, 10, 15, 20
* $values = Arr::range(5, 20);
*
* @param integer stepping
* @param integer ending number
* @return array
*/
public static function range($step = 10, $max = 100)
{
if ($step < 1)
return array();
$array = array();
for ($i = $step; $i <= $max; $i += $step)
{
$array[$i] = $i;
}
return $array;
}
/**
* Retrieve a single key from an array. If the key does not exist in the
* array, the default value will be returned instead.
*
* // Get the value "username" from $_POST, if it exists
* $username = Arr::get($_POST, 'username');
*
* // Get the value "sorting" from $_GET, if it exists
* $sorting = Arr::get($_GET, 'sorting');
*
* @param array array to extract from
* @param string key name
* @param mixed default value
* @return mixed
*/
public static function get($array, $key, $default = NULL)
{
return isset($array[$key]) ? $array[$key] : $default;
}
/**
* Retrieves multiple keys from an array. If the key does not exist in the
* array, the default value will be added instead.
*
* // Get the values "username", "password" from $_POST
* $auth = Arr::extract($_POST, array('username', 'password'));
*
* @param array array to extract keys from
* @param array list of key names
* @param mixed default value
* @return array
*/
public static function extract($array, array $keys, $default = NULL)
{
$found = array();
foreach ($keys as $key)
{
$found[$key] = isset($array[$key]) ? $array[$key] : $default;
}
return $found;
}
/**
* Retrieves muliple single-key values from a list of arrays.
*
* // Get all of the "id" values from a result
* $ids = Arr::pluck($result, 'id');
*
* [!!] A list of arrays is an array that contains arrays, eg: array(array $a, array $b, array $c, ...)
*
* @param array list of arrays to check
* @param string key to pluck
* @return array
*/
public static function pluck($array, $key)
{
$values = array();
foreach ($array as $row)
{
if (isset($row[$key]))
{
// Found a value in this row
$values[] = $row[$key];
}
}
return $values;
}
/**
* Binary search algorithm.
*
* @deprecated Use [array_search](http://php.net/array_search) instead
*
* @param mixed the value to search for
* @param array an array of values to search in
* @param boolean sort the array now
* @return integer the index of the match
* @return FALSE no matching index found
*/
public static function binary_search($needle, $haystack, $sort = FALSE)
{
return array_search($needle, $haystack);
}
/**
* Adds a value to the beginning of an associative array.
*
* // Add an empty value to the start of a select list
* Arr::unshift_assoc($array, 'none', 'Select a value');
*
* @param array array to modify
* @param string array key name
* @param mixed array value
* @return array
*/
public static function unshift( array & $array, $key, $val)
{
$array = array_reverse($array, TRUE);
$array[$key] = $val;
$array = array_reverse($array, TRUE);
return $array;
}
/**
* Recursive version of [array_map](http://php.net/array_map), applies the
* same callback to all elements in an array, including sub-arrays.
*
* // Apply "strip_tags" to every element in the array
* $array = Arr::map('strip_tags', $array);
*
* [!!] Unlike `array_map`, this method requires a callback and will only map
* a single array.
*
* @param mixed callback applied to every element in the array
* @param array array to map
* @return array
*/
public static function map($callback, $array)
{
foreach ($array as $key => $val)
{
if (is_array($val))
{
$array[$key] = Arr::map($callback, $val);
}
else
{
$array[$key] = call_user_func($callback, $val);
}
}
return $array;
}
/**
* Merges one or more arrays recursively and preserves all keys.
* Note that this does not work the same as [array_merge_recursive](http://php.net/array_merge_recursive)!
*
* $john = array('name' => 'john', 'children' => array('fred', 'paul', 'sally', 'jane'));
* $mary = array('name' => 'mary', 'children' => array('jane'));
*
* // John and Mary are married, merge them together
* $john = Arr::merge($john, $mary);
*
* // The output of $john will now be:
* array('name' => 'mary', 'children' => array('fred', 'paul', 'sally', 'jane'))
*
* @param array initial array
* @param array array to merge
* @param array ...
* @return array
*/
public static function merge(array $a1, array $a2)
{
$result = array();
for ($i = 0, $total = func_num_args(); $i < $total; $i++)
{
// Get the next array
$arr = func_get_arg($i);
// Is the array associative?
$assoc = Arr::is_assoc($arr);
foreach ($arr as $key => $val)
{
if (isset($result[$key]))
{
if (is_array($val) AND is_array($result[$key]))
{
if (Arr::is_assoc($val))
{
// Associative arrays are merged recursively
$result[$key] = Arr::merge($result[$key], $val);
}
else
{
// Find the values that are not already present
$diff = array_diff($val, $result[$key]);
// Indexed arrays are merged to prevent duplicates
$result[$key] = array_merge($result[$key], $diff);
}
}
else
{
if ($assoc)
{
// Associative values are replaced
$result[$key] = $val;
}
elseif ( ! in_array($val, $result, TRUE))
{
// Indexed values are added only if they do not yet exist
$result[] = $val;
}
}
}
else
{
// New values are added
$result[$key] = $val;
}
}
}
return $result;
}
/**
* Overwrites an array with values from input arrays.
* Keys that do not exist in the first array will not be added!
*
* $a1 = array('name' => 'john', 'mood' => 'happy', 'food' => 'bacon');
* $a2 = array('name' => 'jack', 'food' => 'tacos', 'drink' => 'beer');
*
* // Overwrite the values of $a1 with $a2
* $array = Arr::overwrite($a1, $a2);
*
* // The output of $array will now be:
* array('name' => 'jack', 'mood' => 'happy', 'food' => 'bacon')
*
* @param array master array
* @param array input arrays that will overwrite existing values
* @return array
*/
public static function overwrite($array1, $array2)
{
foreach (array_intersect_key($array2, $array1) as $key => $value)
{
$array1[$key] = $value;
}
if (func_num_args() > 2)
{
foreach (array_slice(func_get_args(), 2) as $array2)
{
foreach (array_intersect_key($array2, $array1) as $key => $value)
{
$array1[$key] = $value;
}
}
}
return $array1;
}
/**
* Creates a callable function and parameter list from a string representation.
* Note that this function does not validate the callback string.
*
* // Get the callback function and parameters
* list($func, $params) = Arr::callback('Foo::bar(apple,orange)');
*
* // Get the result of the callback
* $result = call_user_func_array($func, $params);
*
* @param string callback string
* @return array function, params
*/
public static function callback($str)
{
// Overloaded as parts are found
$command = $params = NULL;
// command[param,param]
if (preg_match('/^([^\(]*+)\((.*)\)$/', $str, $match))
{
// command
$command = $match[1];
if ($match[2] !== '')
{
// param,param
$params = preg_split('/(?<!\\\\),/', $match[2]);
$params = str_replace('\,', ',', $params);
}
}
else
{
// command
$command = $str;
}
if (strpos($command, '::') !== FALSE)
{
// Create a static method callable command
$command = explode('::', $command, 2);
}
return array($command, $params);
}
/**
* Convert a multi-dimensional array into a single-dimensional array.
*
* $array = array('set' => array('one' => 'something'), 'two' => 'other');
*
* // Flatten the array
* $array = Arr::flatten($array);
*
* // The array will now be
* array('one' => 'something', 'two' => 'other');
*
* [!!] The keys of array values will be discarded.
*
* @param array array to flatten
* @return array
* @since 3.0.6
*/
public static function flatten($array)
{
$flat = array();
foreach ($array as $key => $value)
{
if (is_array($value))
{
$flat += Arr::flatten($value);
}
else
{
$flat[$key] = $value;
}
}
return $flat;
}
} // End arr

View File

@@ -0,0 +1,75 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* Helper functions for working in a command-line environment.
*
* @package Kohana
* @category Helpers
* @author Kohana Team
* @copyright (c) 2009-2010 Kohana Team
* @license http://kohanaframework.org/license
*/
class Kohana_CLI {
/**
* Returns one or more command-line options. Options are specified using
* standard CLI syntax:
*
* php index.php --username=john.smith --password=secret --var="some value with spaces"
*
* // Get the values of "username" and "password"
* $auth = CLI::options('username', 'password');
*
* @param string option name
* @param ...
* @return array
*/
public static function options($options)
{
// Get all of the requested options
$options = func_get_args();
// Found option values
$values = array();
// Skip the first option, it is always the file executed
for ($i = 1; $i < $_SERVER['argc']; $i++)
{
if ( ! isset($_SERVER['argv'][$i]))
{
// No more args left
break;
}
// Get the option
$opt = $_SERVER['argv'][$i];
if (substr($opt, 0, 2) !== '--')
{
// This is not an option argument
continue;
}
// Remove the "--" prefix
$opt = substr($opt, 2);
if (strpos($opt, '='))
{
// Separate the name and value
list ($opt, $value) = explode('=', $opt, 2);
}
else
{
$value = NULL;
}
if (in_array($opt, $options))
{
// Set the given value
$values[$opt] = $value;
}
}
return $values;
}
} // End CLI

View File

@@ -0,0 +1,157 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* Wrapper for configuration arrays. Multiple configuration readers can be
* attached to allow loading configuration from files, database, etc.
*
* @package Kohana
* @category Configuration
* @author Kohana Team
* @copyright (c) 2009-2010 Kohana Team
* @license http://kohanaframework.org/license
*/
class Kohana_Config {
/**
* @var Kohana_Config Singleton static instance
*/
protected static $_instance;
/**
* Get the singleton instance of Kohana_Config.
*
* $config = Kohana_Config::instance();
*
* @return Kohana_Config
*/
public static function instance()
{
if (self::$_instance === NULL)
{
// Create a new instance
self::$_instance = new self;
}
return self::$_instance;
}
/**
* @var array Configuration readers
*/
protected $_readers = array();
/**
* Attach a configuration reader. By default, the reader will be added as
* the first used reader. However, if the reader should be used only when
* all other readers fail, use `FALSE` for the second parameter.
*
* $config->attach($reader); // Try first
* $config->attach($reader, FALSE); // Try last
*
* @param object Kohana_Config_Reader instance
* @param boolean add the reader as the first used object
* @return $this
*/
public function attach(Kohana_Config_Reader $reader, $first = TRUE)
{
if ($first === TRUE)
{
// Place the log reader at the top of the stack
array_unshift($this->_readers, $reader);
}
else
{
// Place the reader at the bottom of the stack
$this->_readers[] = $reader;
}
return $this;
}
/**
* Detach a configuration reader.
*
* $config->detach($reader);
*
* @param object Kohana_Config_Reader instance
* @return $this
*/
public function detach(Kohana_Config_Reader $reader)
{
if (($key = array_search($reader, $this->_readers)) !== FALSE)
{
// Remove the writer
unset($this->_readers[$key]);
}
return $this;
}
/**
* Load a configuration group. Searches the readers in order until the
* group is found. If the group does not exist, an empty configuration
* array will be loaded using the first reader.
*
* $array = $config->load($name);
*
* @param string configuration group name
* @return object Kohana_Config_Reader
* @throws Kohana_Exception
*/
public function load($group)
{
foreach ($this->_readers as $reader)
{
if ($config = $reader->load($group))
{
// Found a reader for this configuration group
return $config;
}
}
// Reset the iterator
reset($this->_readers);
if ( ! is_object($config = current($this->_readers)))
{
throw new Kohana_Exception('No configuration readers attached');
}
// Load the reader as an empty array
return $config->load($group, array());
}
/**
* Copy one configuration group to all of the other readers.
*
* $config->copy($name);
*
* @param string configuration group name
* @return $this
*/
public function copy($group)
{
// Load the configuration group
$config = $this->load($group);
foreach ($this->_readers as $reader)
{
if ($config instanceof $reader)
{
// Do not copy the config to the same group
continue;
}
// Load the configuration object
$object = $reader->load($group, array());
foreach ($config as $key => $value)
{
// Copy each value in the config
$object->offsetSet($key, $value);
}
}
return $this;
}
} // End Kohana_Config

View File

@@ -0,0 +1,60 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* File-based configuration reader. Multiple configuration directories can be
* used by attaching multiple instances of this class to [Kohana_Config].
*
* @package Kohana
* @category Configuration
* @author Kohana Team
* @copyright (c) 2009-2010 Kohana Team
* @license http://kohanaframework.org/license
*/
class Kohana_Config_File extends Kohana_Config_Reader {
/**
* @var string Configuration group name
*/
protected $_configuration_group;
/**
* @var bool Has the config group changed?
*/
protected $_configuration_modified = FALSE;
public function __construct($directory = 'config')
{
// Set the configuration directory name
$this->_directory = trim($directory, '/');
// Load the empty array
parent::__construct();
}
/**
* Load and merge all of the configuration files in this group.
*
* $config->load($name);
*
* @param string configuration group name
* @param array configuration array
* @return $this clone of the current object
* @uses Kohana::load
*/
public function load($group, array $config = NULL)
{
if ($files = Kohana::find_file($this->_directory, $group, NULL, TRUE))
{
// Initialize the config array
$config = array();
foreach ($files as $file)
{
// Merge each file to the configuration array
$config = Arr::merge($config, Kohana::load($file));
}
}
return parent::load($group, $config);
}
} // End Kohana_Config

View File

@@ -0,0 +1,115 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* Abstract configuration reader. All configuration readers must extend
* this class.
*
* @package Kohana
* @category Configuration
* @author Kohana Team
* @copyright (c) 2008-2010 Kohana Team
* @license http://kohanaframework.org/license
*/
abstract class Kohana_Config_Reader extends ArrayObject {
/**
* @var string Configuration group name
*/
protected $_configuration_group;
/**
* Loads an empty array as the initial configuration and enables array
* keys to be used as properties.
*
* @return void
*/
public function __construct()
{
parent::__construct(array(), ArrayObject::ARRAY_AS_PROPS);
}
/**
* Return the current group in serialized form.
*
* echo $config;
*
* @return string
*/
public function __toString()
{
return serialize($this->getArrayCopy());
}
/**
* Loads a configuration group.
*
* $config->load($name, $array);
*
* This method must be extended by all readers. After the group has been
* loaded, call `parent::load($group, $config)` for final preparation.
*
* @param string configuration group name
* @param array configuration array
* @return $this a clone of this object
*/
public function load($group, array $config = NULL)
{
if ($config === NULL)
{
return FALSE;
}
// Clone the current object
$object = clone $this;
// Set the group name
$object->_configuration_group = $group;
// Swap the array with the actual configuration
$object->exchangeArray($config);
return $object;
}
/**
* Return the raw array that is being used for this object.
*
* $array = $config->as_array();
*
* @return array
*/
public function as_array()
{
return $this->getArrayCopy();
}
/**
* Get a variable from the configuration or return the default value.
*
* $value = $config->get($key);
*
* @param string array key
* @param mixed default value
* @return mixed
*/
public function get($key, $default = NULL)
{
return $this->offsetExists($key) ? $this->offsetGet($key) : $default;
}
/**
* Sets a value in the configuration array.
*
* $config->set($key, $new_value);
*
* @param string array key
* @param mixed array value
* @return $this
*/
public function set($key, $value)
{
$this->offsetSet($key, $value);
return $this;
}
} // End Kohana_Config_Reader

View File

@@ -0,0 +1,66 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* Abstract controller class. Controllers should only be created using a [Request].
*
* Controllers methods will be automatically called in the following order by
* the request:
*
* $controller = new Controller_Foo($request);
* $controller->before();
* $controller->action_bar();
* $controller->after();
*
* The controller action should add the output it creates to
* `$this->request->response`, typically in the form of a [View], during the
* "action" part of execution.
*
* @package Kohana
* @category Controller
* @author Kohana Team
* @copyright (c) 2008-2010 Kohana Team
* @license http://kohanaframework.org/license
*/
abstract class Kohana_Controller {
/**
* @var Request Request that created the controller
*/
public $request;
/**
* Creates a new controller instance. Each controller must be constructed
* with the request object that created it.
*
* @param Request Request that created the controller
* @return void
*/
public function __construct(Request $request)
{
// Assign the request to the controller
$this->request = $request;
}
/**
* Automatically executed before the controller action. Can be used to set
* class properties, do authorization checks, and execute other custom code.
*
* @return void
*/
public function before()
{
// Nothing by default
}
/**
* Automatically executed after the controller action. Can be used to apply
* transformation to the request response, add extra output, and execute
* other custom code.
*
* @return void
*/
public function after()
{
// Nothing by default
}
} // End Controller

View File

@@ -0,0 +1,80 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* Abstract Controller class for RESTful controller mapping. Supports GET, PUT,
* POST, and DELETE. By default, these methods will be mapped to these actions:
*
* GET
* : Mapped to the "index" action, lists all objects
*
* POST
* : Mapped to the "create" action, creates a new object
*
* PUT
* : Mapped to the "update" action, update an existing object
*
* DELETE
* : Mapped to the "delete" action, delete an existing object
*
* Additional methods can be supported by adding the method and action to
* the `$_action_map` property.
*
* [!!] Using this class within a website will require heavy modification,
* due to most web browsers only supporting the GET and POST methods.
* Generally, this class should only be used for web services and APIs.
*
* @package Kohana
* @category Controller
* @author Kohana Team
* @copyright (c) 2009-2010 Kohana Team
* @license http://kohanaframework.org/license
*/
abstract class Kohana_Controller_REST extends Controller {
/**
* @var array REST types
*/
protected $_action_map = array
(
'GET' => 'index',
'PUT' => 'update',
'POST' => 'create',
'DELETE' => 'delete',
);
/**
* @var string requested action
*/
protected $_action_requested = '';
/**
* Checks the requested method against the available methods. If the method
* is supported, sets the request action from the map. If not supported,
* the "invalid" action will be called.
*/
public function before()
{
$this->_action_requested = $this->request->action;
if ( ! isset($this->_action_map[Request::$method]))
{
$this->request->action = 'invalid';
}
else
{
$this->request->action = $this->_action_map[Request::$method];
}
return parent::before();
}
/**
* Sends a 405 "Method Not Allowed" response and a list of allowed actions.
*/
public function action_invalid()
{
// Send the "Method Not Allowed" response
$this->request->status = 405;
$this->request->headers['Allow'] = implode(', ', array_keys($this->_action_map));
}
} // End REST

View File

@@ -0,0 +1,50 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* Abstract controller class for automatic templating.
*
* @package Kohana
* @category Controller
* @author Kohana Team
* @copyright (c) 2008-2010 Kohana Team
* @license http://kohanaframework.org/license
*/
abstract class Kohana_Controller_Template extends Controller {
/**
* @var View page template
*/
public $template = 'template';
/**
* @var boolean auto render template
**/
public $auto_render = TRUE;
/**
* Loads the template [View] object.
*/
public function before()
{
if ($this->auto_render === TRUE)
{
// Load the template
$this->template = View::factory($this->template);
}
return parent::before();
}
/**
* Assigns the template [View] as the request response.
*/
public function after()
{
if ($this->auto_render === TRUE)
{
$this->request->response = $this->template;
}
return parent::after();
}
} // End Controller_Template

View File

@@ -0,0 +1,155 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* Cookie helper.
*
* @package Kohana
* @category Helpers
* @author Kohana Team
* @copyright (c) 2008-2010 Kohana Team
* @license http://kohanaframework.org/license
*/
class Kohana_Cookie {
/**
* @var string Magic salt to add to the cookie
*/
public static $salt = 'kooky';
/**
* @var integer Number of seconds before the cookie expires
*/
public static $expiration = 0;
/**
* @var string Restrict the path that the cookie is available to
*/
public static $path = '/';
/**
* @var string Restrict the domain that the cookie is available to
*/
public static $domain = NULL;
/**
* @var boolean Only transmit cookies over secure connections
*/
public static $secure = FALSE;
/**
* @var boolean Only transmit cookies over HTTP, disabling Javascript access
*/
public static $httponly = FALSE;
/**
* Gets the value of a signed cookie. Cookies without signatures will not
* be returned. If the cookie signature is present, but invalid, the cookie
* will be deleted.
*
* // Get the "theme" cookie, or use "blue" if the cookie does not exist
* $theme = Cookie::get('theme', 'blue');
*
* @param string cookie name
* @param mixed default value to return
* @return string
*/
public static function get($key, $default = NULL)
{
if ( ! isset($_COOKIE[$key]))
{
// The cookie does not exist
return $default;
}
// Get the cookie value
$cookie = $_COOKIE[$key];
// Find the position of the split between salt and contents
$split = strlen(Cookie::salt($key, NULL));
if (isset($cookie[$split]) AND $cookie[$split] === '~')
{
// Separate the salt and the value
list ($hash, $value) = explode('~', $cookie, 2);
if (Cookie::salt($key, $value) === $hash)
{
// Cookie signature is valid
return $value;
}
// The cookie signature is invalid, delete it
Cookie::delete($key);
}
return $default;
}
/**
* Sets a signed cookie. Note that all cookie values must be strings and no
* automatic serialization will be performed!
*
* // Set the "theme" cookie
* Cookie::set('theme', 'red');
*
* @param string name of cookie
* @param string value of cookie
* @param integer lifetime in seconds
* @return boolean
* @uses Cookie::salt
*/
public static function set($name, $value, $expiration = NULL)
{
if ($expiration === NULL)
{
// Use the default expiration
$expiration = Cookie::$expiration;
}
if ($expiration !== 0)
{
// The expiration is expected to be a UNIX timestamp
$expiration += time();
}
// Add the salt to the cookie value
$value = Cookie::salt($name, $value).'~'.$value;
return setcookie($name, $value, $expiration, Cookie::$path, Cookie::$domain, Cookie::$secure, Cookie::$httponly);
}
/**
* Deletes a cookie by making the value NULL and expiring it.
*
* Cookie::delete('theme');
*
* @param string cookie name
* @return boolean
* @uses Cookie::set
*/
public static function delete($name)
{
// Remove the cookie
unset($_COOKIE[$name]);
// Nullify the cookie and make it expire
return Cookie::set($name, NULL, -86400);
}
/**
* Generates a salt string for a cookie based on the name and value.
*
* $salt = Cookie::salt('theme', 'red');
*
* @param string name of cookie
* @param string value of cookie
* @return string
*/
public static function salt($name, $value)
{
// Determine the user agent
$agent = isset($_SERVER['HTTP_USER_AGENT']) ? strtolower($_SERVER['HTTP_USER_AGENT']) : 'unknown';
return sha1($agent.$name.$value.Cookie::$salt);
}
} // End cookie

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,566 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* Date helper.
*
* @package Kohana
* @category Helpers
* @author Kohana Team
* @copyright (c) 2007-2010 Kohana Team
* @license http://kohanaframework.org/license
*/
class Kohana_Date {
// Second amounts for various time increments
const YEAR = 31556926;
const MONTH = 2629744;
const WEEK = 604800;
const DAY = 86400;
const HOUR = 3600;
const MINUTE = 60;
/**
* Default timestamp format for formatted_time
* @var string
*/
public static $timestamp_format = 'Y-m-d H:i:s';
/**
* Timezone for formatted_time
* @link http://uk2.php.net/manual/en/timezones.php
* @var string
*/
public static $timezone;
/**
* Returns the offset (in seconds) between two time zones. Use this to
* display dates to users in different time zones.
*
* $seconds = Date::offset('America/Chicago', 'GMT');
*
* [!!] A list of time zones that PHP supports can be found at
* <http://php.net/timezones>.
*
* @param string timezone that to find the offset of
* @param string timezone used as the baseline
* @param mixed UNIX timestamp or date string
* @return integer
*/
public static function offset($remote, $local = NULL, $now = NULL)
{
if ($local === NULL)
{
// Use the default timezone
$local = date_default_timezone_get();
}
if (is_int($now))
{
// Convert the timestamp into a string
$now = date(DateTime::RFC2822, $now);
}
// Create timezone objects
$zone_remote = new DateTimeZone($remote);
$zone_local = new DateTimeZone($local);
// Create date objects from timezones
$time_remote = new DateTime($now, $zone_remote);
$time_local = new DateTime($now, $zone_local);
// Find the offset
$offset = $zone_remote->getOffset($time_remote) - $zone_local->getOffset($time_local);
return $offset;
}
/**
* Number of seconds in a minute, incrementing by a step. Typically used as
* a shortcut for generating a list that can used in a form.
*
* $seconds = Date::seconds(); // 01, 02, 03, ..., 58, 59, 60
*
* @param integer amount to increment each step by, 1 to 30
* @param integer start value
* @param integer end value
* @return array A mirrored (foo => foo) array from 1-60.
*/
public static function seconds($step = 1, $start = 0, $end = 60)
{
// Always integer
$step = (int) $step;
$seconds = array();
for ($i = $start; $i < $end; $i += $step)
{
$seconds[$i] = sprintf('%02d', $i);
}
return $seconds;
}
/**
* Number of minutes in an hour, incrementing by a step. Typically used as
* a shortcut for generating a list that can be used in a form.
*
* $minutes = Date::minutes(); // 05, 10, 15, ..., 50, 55, 60
*
* @uses Date::seconds
* @param integer amount to increment each step by, 1 to 30
* @return array A mirrored (foo => foo) array from 1-60.
*/
public static function minutes($step = 5)
{
// Because there are the same number of minutes as seconds in this set,
// we choose to re-use seconds(), rather than creating an entirely new
// function. Shhhh, it's cheating! ;) There are several more of these
// in the following methods.
return Date::seconds($step);
}
/**
* Number of hours in a day. Typically used as a shortcut for generating a
* list that can be used in a form.
*
* $hours = Date::hours(); // 01, 02, 03, ..., 10, 11, 12
*
* @param integer amount to increment each step by
* @param boolean use 24-hour time
* @param integer the hour to start at
* @return array A mirrored (foo => foo) array from start-12 or start-23.
*/
public static function hours($step = 1, $long = FALSE, $start = NULL)
{
// Default values
$step = (int) $step;
$long = (bool) $long;
$hours = array();
// Set the default start if none was specified.
if ($start === NULL)
{
$start = ($long === FALSE) ? 1 : 0;
}
$hours = array();
// 24-hour time has 24 hours, instead of 12
$size = ($long === TRUE) ? 23 : 12;
for ($i = $start; $i <= $size; $i += $step)
{
$hours[$i] = (string) $i;
}
return $hours;
}
/**
* Returns AM or PM, based on a given hour (in 24 hour format).
*
* $type = Date::ampm(12); // PM
* $type = Date::ampm(1); // AM
*
* @param integer number of the hour
* @return string
*/
public static function ampm($hour)
{
// Always integer
$hour = (int) $hour;
return ($hour > 11) ? 'PM' : 'AM';
}
/**
* Adjusts a non-24-hour number into a 24-hour number.
*
* $hour = Date::adjust(3, 'pm'); // 15
*
* @param integer hour to adjust
* @param string AM or PM
* @return string
*/
public static function adjust($hour, $ampm)
{
$hour = (int) $hour;
$ampm = strtolower($ampm);
switch ($ampm)
{
case 'am':
if ($hour == 12)
{
$hour = 0;
}
break;
case 'pm':
if ($hour < 12)
{
$hour += 12;
}
break;
}
return sprintf('%02d', $hour);
}
/**
* Number of days in a given month and year. Typically used as a shortcut
* for generating a list that can be used in a form.
*
* Date::days(4, 2010); // 1, 2, 3, ..., 28, 29, 30
*
* @param integer number of month
* @param integer number of year to check month, defaults to the current year
* @return array A mirrored (foo => foo) array of the days.
*/
public static function days($month, $year = FALSE)
{
static $months;
if ($year === FALSE)
{
// Use the current year by default
$year = date('Y');
}
// Always integers
$month = (int) $month;
$year = (int) $year;
// We use caching for months, because time functions are used
if (empty($months[$year][$month]))
{
$months[$year][$month] = array();
// Use date to find the number of days in the given month
$total = date('t', mktime(1, 0, 0, $month, 1, $year)) + 1;
for ($i = 1; $i < $total; $i++)
{
$months[$year][$month][$i] = (string) $i;
}
}
return $months[$year][$month];
}
/**
* Number of months in a year. Typically used as a shortcut for generating
* a list that can be used in a form.
*
* Date::months(); // 01, 02, 03, ..., 10, 11, 12
*
* @uses Date::hours
* @return array A mirrored (foo => foo) array from 1-12.
*/
public static function months()
{
return Date::hours();
}
/**
* Returns an array of years between a starting and ending year. By default,
* the the current year - 5 and current year + 5 will be used. Typically used
* as a shortcut for generating a list that can be used in a form.
*
* $years = Date::years(2000, 2010); // 2000, 2001, ..., 2009, 2010
*
* @param integer starting year (default is current year - 5)
* @param integer ending year (default is current year + 5)
* @return array
*/
public static function years($start = FALSE, $end = FALSE)
{
// Default values
$start = ($start === FALSE) ? (date('Y') - 5) : (int) $start;
$end = ($end === FALSE) ? (date('Y') + 5) : (int) $end;
$years = array();
for ($i = $start; $i <= $end; $i++)
{
$years[$i] = (string) $i;
}
return $years;
}
/**
* Returns time difference between two timestamps, in human readable format.
* If the second timestamp is not given, the current time will be used.
* Also consider using [Date::fuzzy_span] when displaying a span.
*
* $span = Date::span(60, 182, 'minutes,seconds'); // array('minutes' => 2, 'seconds' => 2)
* $span = Date::span(60, 182, 'minutes'); // 2
*
* @param integer timestamp to find the span of
* @param integer timestamp to use as the baseline
* @param string formatting string
* @return string when only a single output is requested
* @return array associative list of all outputs requested
*/
public static function span($remote, $local = NULL, $output = 'years,months,weeks,days,hours,minutes,seconds')
{
// Normalize output
$output = trim(strtolower( (string) $output));
if ( ! $output)
{
// Invalid output
return FALSE;
}
// Array with the output formats
$output = preg_split('/[^a-z]+/', $output);
// Convert the list of outputs to an associative array
$output = array_combine($output, array_fill(0, count($output), 0));
// Make the output values into keys
extract(array_flip($output), EXTR_SKIP);
if ($local === NULL)
{
// Calculate the span from the current time
$local = time();
}
// Calculate timespan (seconds)
$timespan = abs($remote - $local);
if (isset($output['years']))
{
$timespan -= Date::YEAR * ($output['years'] = (int) floor($timespan / Date::YEAR));
}
if (isset($output['months']))
{
$timespan -= Date::MONTH * ($output['months'] = (int) floor($timespan / Date::MONTH));
}
if (isset($output['weeks']))
{
$timespan -= Date::WEEK * ($output['weeks'] = (int) floor($timespan / Date::WEEK));
}
if (isset($output['days']))
{
$timespan -= Date::DAY * ($output['days'] = (int) floor($timespan / Date::DAY));
}
if (isset($output['hours']))
{
$timespan -= Date::HOUR * ($output['hours'] = (int) floor($timespan / Date::HOUR));
}
if (isset($output['minutes']))
{
$timespan -= Date::MINUTE * ($output['minutes'] = (int) floor($timespan / Date::MINUTE));
}
// Seconds ago, 1
if (isset($output['seconds']))
{
$output['seconds'] = $timespan;
}
if (count($output) === 1)
{
// Only a single output was requested, return it
return array_pop($output);
}
// Return array
return $output;
}
/**
* Returns the difference between a time and now in a "fuzzy" way.
* Displaying a fuzzy time instead of a date is usually faster to read and understand.
*
* $span = Date::fuzzy_span(time() - 10); // "moments ago"
* $span = Date::fuzzy_span(time() + 20); // "in moments"
*
* A second parameter is available to manually set the "local" timestamp,
* however this parameter shouldn't be needed in normal usage and is only
* included for unit tests
*
* @param integer "remote" timestamp
* @param integer "local" timestamp, defaults to time()
* @return string
*/
public static function fuzzy_span($timestamp, $local_timestamp = NULL)
{
$local_timestamp = ($local_timestamp === NULL) ? time() : (int) $local_timestamp;
// Determine the difference in seconds
$offset = abs($local_timestamp - $timestamp);
if ($offset <= Date::MINUTE)
{
$span = 'moments';
}
elseif ($offset < (Date::MINUTE * 20))
{
$span = 'a few minutes';
}
elseif ($offset < Date::HOUR)
{
$span = 'less than an hour';
}
elseif ($offset < (Date::HOUR * 4))
{
$span = 'a couple of hours';
}
elseif ($offset < Date::DAY)
{
$span = 'less than a day';
}
elseif ($offset < (Date::DAY * 2))
{
$span = 'about a day';
}
elseif ($offset < (Date::DAY * 4))
{
$span = 'a couple of days';
}
elseif ($offset < Date::WEEK)
{
$span = 'less than a week';
}
elseif ($offset < (Date::WEEK * 2))
{
$span = 'about a week';
}
elseif ($offset < Date::MONTH)
{
$span = 'less than a month';
}
elseif ($offset < (Date::MONTH * 2))
{
$span = 'about a month';
}
elseif ($offset < (Date::MONTH * 4))
{
$span = 'a couple of months';
}
elseif ($offset < Date::YEAR)
{
$span = 'less than a year';
}
elseif ($offset < (Date::YEAR * 2))
{
$span = 'about a year';
}
elseif ($offset < (Date::YEAR * 4))
{
$span = 'a couple of years';
}
elseif ($offset < (Date::YEAR * 8))
{
$span = 'a few years';
}
elseif ($offset < (Date::YEAR * 12))
{
$span = 'about a decade';
}
elseif ($offset < (Date::YEAR * 24))
{
$span = 'a couple of decades';
}
elseif ($offset < (Date::YEAR * 64))
{
$span = 'several decades';
}
else
{
$span = 'a long time';
}
if ($timestamp <= $local_timestamp)
{
// This is in the past
return $span.' ago';
}
else
{
// This in the future
return 'in '.$span;
}
}
/**
* Converts a UNIX timestamp to DOS format. There are very few cases where
* this is needed, but some binary formats use it (eg: zip files.)
* Converting the other direction is done using {@link Date::dos2unix}.
*
* $dos = Date::unix2dos($unix);
*
* @param integer UNIX timestamp
* @return integer
*/
public static function unix2dos($timestamp = FALSE)
{
$timestamp = ($timestamp === FALSE) ? getdate() : getdate($timestamp);
if ($timestamp['year'] < 1980)
{
return (1 << 21 | 1 << 16);
}
$timestamp['year'] -= 1980;
// What voodoo is this? I have no idea... Geert can explain it though,
// and that's good enough for me.
return ($timestamp['year'] << 25 | $timestamp['mon'] << 21 |
$timestamp['mday'] << 16 | $timestamp['hours'] << 11 |
$timestamp['minutes'] << 5 | $timestamp['seconds'] >> 1);
}
/**
* Converts a DOS timestamp to UNIX format.There are very few cases where
* this is needed, but some binary formats use it (eg: zip files.)
* Converting the other direction is done using {@link Date::unix2dos}.
*
* $unix = Date::dos2unix($dos);
*
* @param integer DOS timestamp
* @return integer
*/
public static function dos2unix($timestamp = FALSE)
{
$sec = 2 * ($timestamp & 0x1f);
$min = ($timestamp >> 5) & 0x3f;
$hrs = ($timestamp >> 11) & 0x1f;
$day = ($timestamp >> 16) & 0x1f;
$mon = ($timestamp >> 21) & 0x0f;
$year = ($timestamp >> 25) & 0x7f;
return mktime($hrs, $min, $sec, $mon, $day, $year + 1980);
}
/**
* Returns a date/time string with the specified timestamp format
*
* $time = Date::formatted_time('5 minutes ago');
*
* @see http://php.net/manual/en/datetime.construct.php
* @param string datetime_str datetime string
* @param string timestamp_format timestamp format
* @return string
*/
public static function formatted_time($datetime_str = 'now', $timestamp_format = NULL, $timezone = NULL)
{
$timestamp_format = ($timestamp_format == NULL) ? Date::$timestamp_format : $timestamp_format;
$timezone = ($timezone === NULL) ? Date::$timezone : $timezone;
$time = new DateTime($datetime_str, new DateTimeZone(
$timezone ? $timezone : date_default_timezone_get()
));
return $time->format($timestamp_format);
}
} // End date

View File

@@ -0,0 +1,213 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* The Encrypt library provides two-way encryption of text and binary strings
* using the [Mcrypt](http://php.net/mcrypt) extension, which consists of three
* parts: the key, the cipher, and the mode.
*
* The Key
* : A secret passphrase that is used for encoding and decoding
*
* The Cipher
* : A [cipher](http://php.net/mcrypt.ciphers) determines how the encryption
* is mathematically calculated. By default, the "rijndael-128" cipher
* is used. This is commonly known as "AES-128" and is an industry standard.
*
* The Mode
* : The [mode](http://php.net/mcrypt.constants) determines how the encrypted
* data is written in binary form. By default, the "nofb" mode is used,
* which produces short output with high entropy.
*
* @package Kohana
* @category Security
* @author Kohana Team
* @copyright (c) 2007-2010 Kohana Team
* @license http://kohanaframework.org/license
*/
class Kohana_Encrypt {
/**
* @var string default instance name
*/
public static $default = 'default';
/**
* @var array Encrypt class instances
*/
public static $instances = array();
/**
* @var string OS-dependent RAND type to use
*/
protected static $_rand;
/**
* Returns a singleton instance of Encrypt. An encryption key must be
* provided in your "encrypt" configuration file.
*
* $encrypt = Encrypt::instance();
*
* @param string configuration group name
* @return Encrypt
*/
public static function instance($name = NULL)
{
if ($name === NULL)
{
// Use the default instance name
$name = Encrypt::$default;
}
if ( ! isset(Encrypt::$instances[$name]))
{
// Load the configuration data
$config = Kohana::config('encrypt')->$name;
if ( ! isset($config['key']))
{
// No default encryption key is provided!
throw new Kohana_Exception('No encryption key is defined in the encryption configuration group: :group',
array(':group' => $name));
}
if ( ! isset($config['mode']))
{
// Add the default mode
$config['mode'] = MCRYPT_MODE_NOFB;
}
if ( ! isset($config['cipher']))
{
// Add the default cipher
$config['cipher'] = MCRYPT_RIJNDAEL_128;
}
// Create a new instance
Encrypt::$instances[$name] = new Encrypt($config['key'], $config['mode'], $config['cipher']);
}
return Encrypt::$instances[$name];
}
/**
* Creates a new mcrypt wrapper.
*
* @param string encryption key
* @param string mcrypt mode
* @param string mcrypt cipher
*/
public function __construct($key, $mode, $cipher)
{
// Find the max length of the key, based on cipher and mode
$size = mcrypt_get_key_size($cipher, $mode);
if (isset($key[$size]))
{
// Shorten the key to the maximum size
$key = substr($key, 0, $size);
}
// Store the key, mode, and cipher
$this->_key = $key;
$this->_mode = $mode;
$this->_cipher = $cipher;
// Store the IV size
$this->_iv_size = mcrypt_get_iv_size($this->_cipher, $this->_mode);
}
/**
* Encrypts a string and returns an encrypted string that can be decoded.
*
* $data = $encrypt->encode($data);
*
* The encrypted binary data is encoded using [base64](http://php.net/base64_encode)
* to convert it to a string. This string can be stored in a database,
* displayed, and passed using most other means without corruption.
*
* @param string data to be encrypted
* @return string
*/
public function encode($data)
{
// Set the rand type if it has not already been set
if (Encrypt::$_rand === NULL)
{
if (Kohana::$is_windows)
{
// Windows only supports the system random number generator
Encrypt::$_rand = MCRYPT_RAND;
}
else
{
if (defined('MCRYPT_DEV_URANDOM'))
{
// Use /dev/urandom
Encrypt::$_rand = MCRYPT_DEV_URANDOM;
}
elseif (defined('MCRYPT_DEV_RANDOM'))
{
// Use /dev/random
Encrypt::$_rand = MCRYPT_DEV_RANDOM;
}
else
{
// Use the system random number generator
Encrypt::$_rand = MCRYPT_RAND;
}
}
}
if (Encrypt::$_rand === MCRYPT_RAND)
{
// The system random number generator must always be seeded each
// time it is used, or it will not produce true random results
mt_srand();
}
// Create a random initialization vector of the proper size for the current cipher
$iv = mcrypt_create_iv($this->_iv_size, Encrypt::$_rand);
// Encrypt the data using the configured options and generated iv
$data = mcrypt_encrypt($this->_cipher, $this->_key, $data, $this->_mode, $iv);
// Use base64 encoding to convert to a string
return base64_encode($iv.$data);
}
/**
* Decrypts an encoded string back to its original value.
*
* $data = $encrypt->decode($data);
*
* @param string encoded string to be decrypted
* @return FALSE if decryption fails
* @return string
*/
public function decode($data)
{
// Convert the data back to binary
$data = base64_decode($data, TRUE);
if ( ! $data)
{
// Invalid base64 data
return FALSE;
}
// Extract the initialization vector from the data
$iv = substr($data, 0, $this->_iv_size);
if ($this->_iv_size !== strlen($iv))
{
// The iv is not the expected size
return FALSE;
}
// Remove the iv from the data
$data = substr($data, $this->_iv_size);
// Return the decrypted data, trimming the \0 padding bytes from the end of the data
return rtrim(mcrypt_decrypt($this->_cipher, $this->_key, $data, $this->_mode, $iv), "\0");
}
} // End Encrypt

View File

@@ -0,0 +1,46 @@
<?php defined('SYSPATH') or die('No direct access');
/**
* Kohana exception class. Translates exceptions using the [I18n] class.
*
* @package Kohana
* @category Exceptions
* @author Kohana Team
* @copyright (c) 2008-2010 Kohana Team
* @license http://kohanaframework.org/license
*/
class Kohana_Exception extends Exception {
/**
* Creates a new translated exception.
*
* throw new Kohana_Exception('Something went terrible wrong, :user',
* array(':user' => $user));
*
* @param string error message
* @param array translation variables
* @param integer the exception code
* @return void
*/
public function __construct($message, array $variables = NULL, $code = 0)
{
// Set the message
$message = __($message, $variables);
// Pass the message to the parent
parent::__construct($message, $code);
}
/**
* Magic object-to-string method.
*
* echo $exception;
*
* @uses Kohana::exception_text
* @return string
*/
public function __toString()
{
return Kohana::exception_text($this);
}
} // End Kohana_Exception

View File

@@ -0,0 +1,176 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* RSS and Atom feed helper.
*
* @package Kohana
* @category Helpers
* @author Kohana Team
* @copyright (c) 2007-2010 Kohana Team
* @license http://kohanaframework.org/license
*/
class Kohana_Feed {
/**
* Parses a remote feed into an array.
*
* @param string remote feed URL
* @param integer item limit to fetch
* @return array
*/
public static function parse($feed, $limit = 0)
{
// Check if SimpleXML is installed
if ( ! function_exists('simplexml_load_file'))
throw new Kohana_Exception('SimpleXML must be installed!');
// Make limit an integer
$limit = (int) $limit;
// Disable error reporting while opening the feed
$error_level = error_reporting(0);
// Allow loading by filename or raw XML string
$load = (is_file($feed) OR validate::url($feed)) ? 'simplexml_load_file' : 'simplexml_load_string';
// Load the feed
$feed = $load($feed, 'SimpleXMLElement', LIBXML_NOCDATA);
// Restore error reporting
error_reporting($error_level);
// Feed could not be loaded
if ($feed === FALSE)
return array();
$namespaces = $feed->getNamespaces(true);
// Detect the feed type. RSS 1.0/2.0 and Atom 1.0 are supported.
$feed = isset($feed->channel) ? $feed->xpath('//item') : $feed->entry;
$i = 0;
$items = array();
foreach ($feed as $item)
{
if ($limit > 0 AND $i++ === $limit)
break;
$item_fields = (array) $item;
// get namespaced tags
foreach ($namespaces as $ns)
{
$item_fields += (array) $item->children($ns);
}
$items[] = $item_fields;
}
return $items;
}
/**
* Creates a feed from the given parameters.
*
* @param array feed information
* @param array items to add to the feed
* @param string define which format to use (only rss2 is supported)
* @param string define which encoding to use
* @return string
*/
public static function create($info, $items, $format = 'rss2', $encoding = 'UTF-8')
{
$info += array('title' => 'Generated Feed', 'link' => '', 'generator' => 'KohanaPHP');
$feed = '<?xml version="1.0" encoding="'.$encoding.'"?><rss version="2.0"><channel></channel></rss>';
$feed = simplexml_load_string($feed);
foreach ($info as $name => $value)
{
if ($name === 'image')
{
// Create an image element
$image = $feed->channel->addChild('image');
if ( ! isset($value['link'], $value['url'], $value['title']))
{
throw new Kohana_Exception('Feed images require a link, url, and title');
}
if (strpos($value['link'], '://') === FALSE)
{
// Convert URIs to URLs
$value['link'] = URL::site($value['link'], 'http');
}
if (strpos($value['url'], '://') === FALSE)
{
// Convert URIs to URLs
$value['url'] = URL::site($value['url'], 'http');
}
// Create the image elements
$image->addChild('link', $value['link']);
$image->addChild('url', $value['url']);
$image->addChild('title', $value['title']);
}
else
{
if (($name === 'pubDate' OR $name === 'lastBuildDate') AND (is_int($value) OR ctype_digit($value)))
{
// Convert timestamps to RFC 822 formatted dates
$value = date('r', $value);
}
elseif (($name === 'link' OR $name === 'docs') AND strpos($value, '://') === FALSE)
{
// Convert URIs to URLs
$value = URL::site($value, 'http');
}
// Add the info to the channel
$feed->channel->addChild($name, $value);
}
}
foreach ($items as $item)
{
// Add the item to the channel
$row = $feed->channel->addChild('item');
foreach ($item as $name => $value)
{
if ($name === 'pubDate' AND (is_int($value) OR ctype_digit($value)))
{
// Convert timestamps to RFC 822 formatted dates
$value = date('r', $value);
}
elseif (($name === 'link' OR $name === 'guid') AND strpos($value, '://') === FALSE)
{
// Convert URIs to URLs
$value = URL::site($value, 'http');
}
// Add the info to the row
$row->addChild($name, $value);
}
}
if (function_exists('dom_import_simplexml'))
{
// Convert the feed object to a DOM object
$feed = dom_import_simplexml($feed)->ownerDocument;
// DOM generates more readable XML
$feed->formatOutput = TRUE;
// Export the document as XML
$feed = $feed->saveXML();
}
else
{
// Export the document as XML
$feed = $feed->asXML();
}
return $feed;
}
} // End Feed

View File

@@ -0,0 +1,179 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* File helper class.
*
* @package Kohana
* @category Helpers
* @author Kohana Team
* @copyright (c) 2007-2010 Kohana Team
* @license http://kohanaframework.org/license
*/
class Kohana_File {
/**
* Attempt to get the mime type from a file. This method is horribly
* unreliable, due to PHP being horribly unreliable when it comes to
* determining the mime type of a file.
*
* $mime = File::mime($file);
*
* @param string file name or path
* @return string mime type on success
* @return FALSE on failure
*/
public static function mime($filename)
{
// Get the complete path to the file
$filename = realpath($filename);
// Get the extension from the filename
$extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
if (preg_match('/^(?:jpe?g|png|[gt]if|bmp|swf)$/', $extension))
{
// Use getimagesize() to find the mime type on images
$file = getimagesize($filename);
if (isset($file['mime']))
return $file['mime'];
}
if (class_exists('finfo', FALSE))
{
if ($info = new finfo(defined('FILEINFO_MIME_TYPE') ? FILEINFO_MIME_TYPE : FILEINFO_MIME))
{
return $info->file($filename);
}
}
if (ini_get('mime_magic.magicfile') AND function_exists('mime_content_type'))
{
// The mime_content_type function is only useful with a magic file
return mime_content_type($filename);
}
if ( ! empty($extension))
{
return File::mime_by_ext($extension);
}
// Unable to find the mime-type
return FALSE;
}
/**
* Return the mime type of an extension.
*
* $mime = File::mime_by_ext('png'); // "image/png"
*
* @param string extension: php, pdf, txt, etc
* @return string mime type on success
* @return FALSE on failure
*/
public static function mime_by_ext($extension)
{
// Load all of the mime types
$mimes = Kohana::config('mimes');
return isset($mimes[$extension]) ? $mimes[$extension][0] : FALSE;
}
/**
* Split a file into pieces matching a specific size. Used when you need to
* split large files into smaller pieces for easy transmission.
*
* $count = File::split($file);
*
* @param string file to be split
* @param string directory to output to, defaults to the same directory as the file
* @param integer size, in MB, for each piece to be
* @return integer The number of pieces that were created
*/
public static function split($filename, $piece_size = 10)
{
// Open the input file
$file = fopen($filename, 'rb');
// Change the piece size to bytes
$piece_size = floor($piece_size * 1024 * 1024);
// Write files in 8k blocks
$block_size = 1024 * 8;
// Total number of peices
$peices = 0;
while ( ! feof($file))
{
// Create another piece
$peices += 1;
// Create a new file piece
$piece = str_pad($peices, 3, '0', STR_PAD_LEFT);
$piece = fopen($filename.'.'.$piece, 'wb+');
// Number of bytes read
$read = 0;
do
{
// Transfer the data in blocks
fwrite($piece, fread($file, $block_size));
// Another block has been read
$read += $block_size;
}
while ($read < $piece_size);
// Close the piece
fclose($piece);
}
// Close the file
fclose($file);
return $peices;
}
/**
* Join a split file into a whole file. Does the reverse of [File::split].
*
* $count = File::join($file);
*
* @param string split filename, without .000 extension
* @param string output filename, if different then an the filename
* @return integer The number of pieces that were joined.
*/
public static function join($filename)
{
// Open the file
$file = fopen($filename, 'wb+');
// Read files in 8k blocks
$block_size = 1024 * 8;
// Total number of peices
$pieces = 0;
while (is_file($piece = $filename.'.'.str_pad($pieces + 1, 3, '0', STR_PAD_LEFT)))
{
// Read another piece
$pieces += 1;
// Open the piece for reading
$piece = fopen($piece, 'rb');
while ( ! feof($piece))
{
// Transfer the data in blocks
fwrite($file, fread($piece, $block_size));
}
// Close the peice
fclose($piece);
}
return $pieces;
}
} // End file

View File

@@ -0,0 +1,434 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* Form helper class. Unless otherwise noted, all generated HTML will be made
* safe using the [HTML::chars] method. This prevents against simple XSS
* attacks that could otherwise be trigged by inserting HTML characters into
* form fields.
*
* @package Kohana
* @category Helpers
* @author Kohana Team
* @copyright (c) 2007-2010 Kohana Team
* @license http://kohanaframework.org/license
*/
class Kohana_Form {
/**
* Generates an opening HTML form tag.
*
* // Form will submit back to the current page using POST
* echo Form::open();
*
* // Form will submit to 'search' using GET
* echo Form::open('search', array('method' => 'get'));
*
* // When "file" inputs are present, you must include the "enctype"
* echo Form::open(NULL, array('enctype' => 'multipart/form-data'));
*
* @param string form action, defaults to the current request URI
* @param array html attributes
* @return string
* @uses Request::instance
* @uses URL::site
* @uses HTML::attributes
*/
public static function open($action = NULL, array $attributes = NULL)
{
if ($action === NULL)
{
// Use the current URI
$action = Request::current()->uri;
}
if ($action === '')
{
// Use only the base URI
$action = Kohana::$base_url;
}
elseif (strpos($action, '://') === FALSE)
{
// Make the URI absolute
$action = URL::site($action);
}
// Add the form action to the attributes
$attributes['action'] = $action;
// Only accept the default character set
$attributes['accept-charset'] = Kohana::$charset;
if ( ! isset($attributes['method']))
{
// Use POST method
$attributes['method'] = 'post';
}
return '<form'.HTML::attributes($attributes).'>';
}
/**
* Creates the closing form tag.
*
* echo Form::close();
*
* @return string
*/
public static function close()
{
return '</form>';
}
/**
* Creates a form input. If no type is specified, a "text" type input will
* be returned.
*
* echo Form::input('username', $username);
*
* @param string input name
* @param string input value
* @param array html attributes
* @return string
* @uses HTML::attributes
*/
public static function input($name, $value = NULL, array $attributes = NULL)
{
// Set the input name
$attributes['name'] = $name;
// Set the input value
$attributes['value'] = $value;
if ( ! isset($attributes['type']))
{
// Default type is text
$attributes['type'] = 'text';
}
return '<input'.HTML::attributes($attributes).' />';
}
/**
* Creates a hidden form input.
*
* echo Form::hidden('csrf', $token);
*
* @param string input name
* @param string input value
* @param array html attributes
* @return string
* @uses Form::input
*/
public static function hidden($name, $value = NULL, array $attributes = NULL)
{
$attributes['type'] = 'hidden';
return Form::input($name, $value, $attributes);
}
/**
* Creates a password form input.
*
* echo Form::password('password');
*
* @param string input name
* @param string input value
* @param array html attributes
* @return string
* @uses Form::input
*/
public static function password($name, $value = NULL, array $attributes = NULL)
{
$attributes['type'] = 'password';
return Form::input($name, $value, $attributes);
}
/**
* Creates a file upload form input. No input value can be specified.
*
* echo Form::file('image');
*
* @param string input name
* @param array html attributes
* @return string
* @uses Form::input
*/
public static function file($name, array $attributes = NULL)
{
$attributes['type'] = 'file';
return Form::input($name, NULL, $attributes);
}
/**
* Creates a checkbox form input.
*
* echo Form::checkbox('remember_me', 1, (bool) $remember);
*
* @param string input name
* @param string input value
* @param boolean checked status
* @param array html attributes
* @return string
* @uses Form::input
*/
public static function checkbox($name, $value = NULL, $checked = FALSE, array $attributes = NULL)
{
$attributes['type'] = 'checkbox';
if ($checked === TRUE)
{
// Make the checkbox active
$attributes['checked'] = 'checked';
}
return Form::input($name, $value, $attributes);
}
/**
* Creates a radio form input.
*
* echo Form::radio('like_cats', 1, $cats);
* echo Form::radio('like_cats', 0, ! $cats);
*
* @param string input name
* @param string input value
* @param boolean checked status
* @param array html attributes
* @return string
* @uses Form::input
*/
public static function radio($name, $value = NULL, $checked = FALSE, array $attributes = NULL)
{
$attributes['type'] = 'radio';
if ($checked === TRUE)
{
// Make the radio active
$attributes['checked'] = 'checked';
}
return Form::input($name, $value, $attributes);
}
/**
* Creates a textarea form input.
*
* echo Form::textarea('about', $about);
*
* @param string textarea name
* @param string textarea body
* @param array html attributes
* @param boolean encode existing HTML characters
* @return string
* @uses HTML::attributes
* @uses HTML::chars
*/
public static function textarea($name, $body = '', array $attributes = NULL, $double_encode = TRUE)
{
// Set the input name
$attributes['name'] = $name;
// Add default rows and cols attributes (required)
$attributes += array('rows' => 10, 'cols' => 50);
return '<textarea'.HTML::attributes($attributes).'>'.HTML::chars($body, $double_encode).'</textarea>';
}
/**
* Creates a select form input.
*
* echo Form::select('country', $countries, $country);
*
* [!!] Support for multiple selected options was added in v3.0.7.
*
* @param string input name
* @param array available options
* @param mixed selected option string, or an array of selected options
* @param array html attributes
* @return string
* @uses HTML::attributes
*/
public static function select($name, array $options = NULL, $selected = NULL, array $attributes = NULL)
{
// Set the input name
$attributes['name'] = $name;
if (is_array($selected))
{
// This is a multi-select, god save us!
$attributes['multiple'] = 'multiple';
}
if ( ! is_array($selected))
{
if ($selected === NULL)
{
// Use an empty array
$selected = array();
}
else
{
// Convert the selected options to an array
$selected = array( (string) $selected);
}
}
if (empty($options))
{
// There are no options
$options = '';
}
else
{
foreach ($options as $value => $name)
{
if (is_array($name))
{
// Create a new optgroup
$group = array('label' => $value);
// Create a new list of options
$_options = array();
foreach ($name as $_value => $_name)
{
// Force value to be string
$_value = (string) $_value;
// Create a new attribute set for this option
$option = array('value' => $_value);
if (in_array($_value, $selected))
{
// This option is selected
$option['selected'] = 'selected';
}
// Change the option to the HTML string
$_options[] = '<option'.HTML::attributes($option).'>'.HTML::chars($_name, FALSE).'</option>';
}
// Compile the options into a string
$_options = "\n".implode("\n", $_options)."\n";
$options[$value] = '<optgroup'.HTML::attributes($group).'>'.$_options.'</optgroup>';
}
else
{
// Force value to be string
$value = (string) $value;
// Create a new attribute set for this option
$option = array('value' => $value);
if (in_array($value, $selected))
{
// This option is selected
$option['selected'] = 'selected';
}
// Change the option to the HTML string
$options[$value] = '<option'.HTML::attributes($option).'>'.HTML::chars($name, FALSE).'</option>';
}
}
// Compile the options into a single string
$options = "\n".implode("\n", $options)."\n";
}
return '<select'.HTML::attributes($attributes).'>'.$options.'</select>';
}
/**
* Creates a submit form input.
*
* echo Form::submit(NULL, 'Login');
*
* @param string input name
* @param string input value
* @param array html attributes
* @return string
* @uses Form::input
*/
public static function submit($name, $value, array $attributes = NULL)
{
$attributes['type'] = 'submit';
return Form::input($name, $value, $attributes);
}
/**
* Creates a image form input.
*
* echo Form::image(NULL, NULL, array('src' => 'media/img/login.png'));
*
* @param string input name
* @param string input value
* @param array html attributes
* @param boolean add index file to URL?
* @return string
* @uses Form::input
*/
public static function image($name, $value, array $attributes = NULL, $index = FALSE)
{
if ( ! empty($attributes['src']))
{
if (strpos($attributes['src'], '://') === FALSE)
{
// Add the base URL
$attributes['src'] = URL::base($index).$attributes['src'];
}
}
$attributes['type'] = 'image';
return Form::input($name, $value, $attributes);
}
/**
* Creates a button form input. Note that the body of a button is NOT escaped,
* to allow images and other HTML to be used.
*
* echo Form::button('save', 'Save Profile', array('type' => 'submit'));
*
* @param string input name
* @param string input value
* @param array html attributes
* @return string
* @uses HTML::attributes
*/
public static function button($name, $body, array $attributes = NULL)
{
// Set the input name
$attributes['name'] = $name;
return '<button'.HTML::attributes($attributes).'>'.$body.'</button>';
}
/**
* Creates a form label. Label text is not automatically translated.
*
* echo Form::label('username', 'Username');
*
* @param string target input
* @param string label text
* @param array html attributes
* @return string
* @uses HTML::attributes
*/
public static function label($input, $text = NULL, array $attributes = NULL)
{
if ($text === NULL)
{
// Use the input name as the text
$text = ucwords(preg_replace('/[\W_]+/', ' ', $input));
}
// Set the label target
$attributes['for'] = $input;
return '<label'.HTML::attributes($attributes).'>'.$text.'</label>';
}
} // End form

View File

@@ -0,0 +1,147 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* View fragment caching. This is primarily used to cache small parts of a view
* that rarely change. For instance, you may want to cache the footer of your
* template because it has very little dynamic content. Or you could cache a
* user profile page and delete the fragment when the user updates.
*
* For obvious reasons, fragment caching should not be applied to any
* content that contains forms.
*
* [!!] Multiple language (I18n) support was added in v3.0.4.
*
* @package Kohana
* @category Helpers
* @author Kohana Team
* @copyright (c) 2009-2010 Kohana Team
* @license http://kohanaframework.org/license
* @uses Kohana::cache
*/
class Kohana_Fragment {
/**
* @var integer default number of seconds to cache for
*/
public static $lifetime = 30;
/**
* @var boolean use multilingual fragment support?
*/
public static $i18n = FALSE;
/**
* @var array list of buffer => cache key
*/
protected static $_caches = array();
/**
* Generate the cache key name for a fragment.
*
* $key = Fragment::_cache_key('footer', TRUE);
*
* @param string fragment name
* @param boolean multilingual fragment support
* @return string
* @uses I18n::lang
* @since 3.0.4
*/
protected static function _cache_key($name, $i18n = NULL)
{
if ($i18n === NULL)
{
// Use the default setting
$i18n = Fragment::$i18n;
}
// Language prefix for cache key
$i18n = ($i18n === TRUE) ? I18n::lang() : '';
// Note: $i18n and $name need to be delimited to prevent naming collisions
return 'Fragment::cache('.$i18n.'+'.$name.')';
}
/**
* Load a fragment from cache and display it. Multiple fragments can
* be nested with different life times.
*
* if ( ! Fragment::load('footer')) {
* // Anything that is echo'ed here will be saved
* Fragment::save();
* }
*
* @param string fragment name
* @param integer fragment cache lifetime
* @param boolean multilingual fragment support
* @return boolean
*/
public static function load($name, $lifetime = NULL, $i18n = NULL)
{
// Set the cache lifetime
$lifetime = ($lifetime === NULL) ? Fragment::$lifetime : (int) $lifetime;
// Get the cache key name
$cache_key = Fragment::_cache_key($name, $i18n);
if ($fragment = Kohana::cache($cache_key, NULL, $lifetime))
{
// Display the cached fragment now
echo $fragment;
return TRUE;
}
else
{
// Start the output buffer
ob_start();
// Store the cache key by the buffer level
Fragment::$_caches[ob_get_level()] = $cache_key;
return FALSE;
}
}
/**
* Saves the currently open fragment in the cache.
*
* Fragment::save();
*
* @return void
*/
public static function save()
{
// Get the buffer level
$level = ob_get_level();
if (isset(Fragment::$_caches[$level]))
{
// Get the cache key based on the level
$cache_key = Fragment::$_caches[$level];
// Delete the cache key, we don't need it anymore
unset(Fragment::$_caches[$level]);
// Get the output buffer and display it at the same time
$fragment = ob_get_flush();
// Cache the fragment
Kohana::cache($cache_key, $fragment);
}
}
/**
* Delete a cached fragment.
*
* Fragment::delete($key);
*
* @param string fragment name
* @param boolean multilingual fragment support
* @return void
*/
public static function delete($name, $i18n = NULL)
{
// Invalid the cache
Kohana::cache(Fragment::_cache_key($name, $i18n), NULL, -3600);
}
} // End Fragment

View File

@@ -0,0 +1,380 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* HTML helper class. Provides generic methods for generating various HTML
* tags and making output HTML safe.
*
* @package Kohana
* @category Helpers
* @author Kohana Team
* @copyright (c) 2007-2010 Kohana Team
* @license http://kohanaframework.org/license
*/
class Kohana_HTML {
/**
* @var array preferred order of attributes
*/
public static $attribute_order = array
(
'action',
'method',
'type',
'id',
'name',
'value',
'href',
'src',
'width',
'height',
'cols',
'rows',
'size',
'maxlength',
'rel',
'media',
'accept-charset',
'accept',
'tabindex',
'accesskey',
'alt',
'title',
'class',
'style',
'selected',
'checked',
'readonly',
'disabled',
);
/**
* @var boolean automatically target external URLs to a new window?
*/
public static $windowed_urls = FALSE;
/**
* Convert special characters to HTML entities. All untrusted content
* should be passed through this method to prevent XSS injections.
*
* echo HTML::chars($username);
*
* @param string string to convert
* @param boolean encode existing entities
* @return string
*/
public static function chars($value, $double_encode = TRUE)
{
return htmlspecialchars( (string) $value, ENT_QUOTES, Kohana::$charset, $double_encode);
}
/**
* Convert all applicable characters to HTML entities. All characters
* that cannot be represented in HTML with the current character set
* will be converted to entities.
*
* echo HTML::entities($username);
*
* @param string string to convert
* @param boolean encode existing entities
* @return string
*/
public static function entities($value, $double_encode = TRUE)
{
return htmlentities( (string) $value, ENT_QUOTES, Kohana::$charset, $double_encode);
}
/**
* Create HTML link anchors. Note that the title is not escaped, to allow
* HTML elements within links (images, etc).
*
* echo HTML::anchor('/user/profile', 'My Profile');
*
* @param string URL or URI string
* @param string link text
* @param array HTML anchor attributes
* @param string use a specific protocol
* @return string
* @uses URL::base
* @uses URL::site
* @uses HTML::attributes
*/
public static function anchor($uri, $title = NULL, array $attributes = NULL, $protocol = NULL)
{
if ($title === NULL)
{
// Use the URI as the title
$title = $uri;
}
if ($uri === '')
{
// Only use the base URL
$uri = URL::base(FALSE, $protocol);
}
else
{
if (strpos($uri, '://') !== FALSE)
{
if (HTML::$windowed_urls === TRUE AND empty($attributes['target']))
{
// Make the link open in a new window
$attributes['target'] = '_blank';
}
}
elseif ($uri[0] !== '#')
{
// Make the URI absolute for non-id anchors
$uri = URL::site($uri, $protocol);
}
}
// Add the sanitized link to the attributes
$attributes['href'] = $uri;
return '<a'.HTML::attributes($attributes).'>'.$title.'</a>';
}
/**
* Creates an HTML anchor to a file. Note that the title is not escaped,
* to allow HTML elements within links (images, etc).
*
* echo HTML::file_anchor('media/doc/user_guide.pdf', 'User Guide');
*
* @param string name of file to link to
* @param string link text
* @param array HTML anchor attributes
* @param string non-default protocol, eg: ftp
* @return string
* @uses URL::base
* @uses HTML::attributes
*/
public static function file_anchor($file, $title = NULL, array $attributes = NULL, $protocol = NULL)
{
if ($title === NULL)
{
// Use the file name as the title
$title = basename($file);
}
// Add the file link to the attributes
$attributes['href'] = URL::base(FALSE, $protocol).$file;
return '<a'.HTML::attributes($attributes).'>'.$title.'</a>';
}
/**
* Generates an obfuscated version of a string. Text passed through this
* method is less likely to be read by web crawlers and robots, which can
* be helpful for spam prevention, but can prevent legitimate robots from
* reading your content.
*
* echo HTML::obfuscate($text);
*
* @param string string to obfuscate
* @return string
* @since 3.0.3
*/
public static function obfuscate($string)
{
$safe = '';
foreach (str_split($string) as $letter)
{
switch (rand(1, 3))
{
// HTML entity code
case 1:
$safe .= '&#'.ord($letter).';';
break;
// Hex character code
case 2:
$safe .= '&#x'.dechex(ord($letter)).';';
break;
// Raw (no) encoding
case 3:
$safe .= $letter;
}
}
return $safe;
}
/**
* Generates an obfuscated version of an email address. Helps prevent spam
* robots from finding email addresses.
*
* echo HTML::email($address);
*
* @param string email address
* @return string
* @uses HTML::obfuscate
*/
public static function email($email)
{
// Make sure the at sign is always obfuscated
return str_replace('@', '&#64;', HTML::obfuscate($email));
}
/**
* Creates an email (mailto:) anchor. Note that the title is not escaped,
* to allow HTML elements within links (images, etc).
*
* echo HTML::mailto($address);
*
* @param string email address to send to
* @param string link text
* @param array HTML anchor attributes
* @return string
* @uses HTML::email
* @uses HTML::attributes
*/
public static function mailto($email, $title = NULL, array $attributes = NULL)
{
// Obfuscate email address
$email = HTML::email($email);
if ($title === NULL)
{
// Use the email address as the title
$title = $email;
}
return '<a href="&#109;&#097;&#105;&#108;&#116;&#111;&#058;'.$email.'"'.HTML::attributes($attributes).'>'.$title.'</a>';
}
/**
* Creates a style sheet link element.
*
* echo HTML::style('media/css/screen.css');
*
* @param string file name
* @param array default attributes
* @param boolean include the index page
* @return string
* @uses URL::base
* @uses HTML::attributes
*/
public static function style($file, array $attributes = NULL, $index = FALSE)
{
if (strpos($file, '://') === FALSE)
{
// Add the base URL
$file = URL::base($index).$file;
}
// Set the stylesheet link
$attributes['href'] = $file;
// Set the stylesheet rel
$attributes['rel'] = 'stylesheet';
// Set the stylesheet type
$attributes['type'] = 'text/css';
return '<link'.HTML::attributes($attributes).' />';
}
/**
* Creates a script link.
*
* echo HTML::script('media/js/jquery.min.js');
*
* @param string file name
* @param array default attributes
* @param boolean include the index page
* @return string
* @uses URL::base
* @uses HTML::attributes
*/
public static function script($file, array $attributes = NULL, $index = FALSE)
{
if (strpos($file, '://') === FALSE)
{
// Add the base URL
$file = URL::base($index).$file;
}
// Set the script link
$attributes['src'] = $file;
// Set the script type
$attributes['type'] = 'text/javascript';
return '<script'.HTML::attributes($attributes).'></script>';
}
/**
* Creates a image link.
*
* echo HTML::image('media/img/logo.png', array('alt' => 'My Company'));
*
* @param string file name
* @param array default attributes
* @return string
* @uses URL::base
* @uses HTML::attributes
*/
public static function image($file, array $attributes = NULL, $index = FALSE)
{
if (strpos($file, '://') === FALSE)
{
// Add the base URL
$file = URL::base($index).$file;
}
// Add the image link
$attributes['src'] = $file;
return '<img'.HTML::attributes($attributes).' />';
}
/**
* Compiles an array of HTML attributes into an attribute string.
* Attributes will be sorted using HTML::$attribute_order for consistency.
*
* echo '<div'.HTML::attributes($attrs).'>'.$content.'</div>';
*
* @param array attribute list
* @return string
*/
public static function attributes(array $attributes = NULL)
{
if (empty($attributes))
return '';
$sorted = array();
foreach (HTML::$attribute_order as $key)
{
if (isset($attributes[$key]))
{
// Add the attribute to the sorted list
$sorted[$key] = $attributes[$key];
}
}
// Combine the sorted attributes
$attributes = $sorted + $attributes;
$compiled = '';
foreach ($attributes as $key => $value)
{
if ($value === NULL)
{
// Skip attributes that have NULL values
continue;
}
if (is_int($key))
{
// Assume non-associative keys are mirrored attributes
$key = $value;
}
// Add the attribute value
$compiled .= ' '.$key.'="'.HTML::chars($value).'"';
}
return $compiled;
}
} // End html

View File

@@ -0,0 +1,139 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* Internationalization (i18n) class. Provides language loading and translation
* methods without dependencies on [gettext](http://php.net/gettext).
*
* Typically this class would never be used directly, but used via the __()
* function, which loads the message and replaces parameters:
*
* // Display a translated message
* echo __('Hello, world');
*
* // With parameter replacement
* echo __('Hello, :user', array(':user' => $username));
*
* [!!] The __() function is declared in `SYSPATH/base.php`.
*
* @package Kohana
* @category Base
* @author Kohana Team
* @copyright (c) 2008-2010 Kohana Team
* @license http://kohanaframework.org/license
*/
class Kohana_I18n {
/**
* @var string target language: en-us, es-es, zh-cn, etc
*/
public static $lang = 'en-us';
/**
* @var string source language: en-us, es-es, zh-cn, etc
*/
public static $source = 'en-us';
/**
* @var array cache of loaded languages
*/
protected static $_cache = array();
/**
* Get and set the target language.
*
* // Get the current language
* $lang = I18n::lang();
*
* // Change the current language to Spanish
* I18n::lang('es-es');
*
* @param string new language setting
* @return string
* @since 3.0.2
*/
public static function lang($lang = NULL)
{
if ($lang)
{
// Normalize the language
I18n::$lang = strtolower(str_replace(array(' ', '_'), '-', $lang));
}
return I18n::$lang;
}
/**
* Returns translation of a string. If no translation exists, the original
* string will be returned. No parameters are replaced.
*
* $hello = I18n::get('Hello friends, my name is :name');
*
* @param string text to translate
* @param string target language
* @return string
*/
public static function get($string, $lang = NULL)
{
if ( ! $lang)
{
// Use the global target language
$lang = I18n::$lang;
}
// Load the translation table for this language
$table = I18n::load($lang);
// Return the translated string if it exists
return isset($table[$string]) ? $table[$string] : $string;
}
/**
* Returns the translation table for a given language.
*
* // Get all defined Spanish messages
* $messages = I18n::load('es-es');
*
* @param string language to load
* @return array
*/
public static function load($lang)
{
if (isset(I18n::$_cache[$lang]))
{
return I18n::$_cache[$lang];
}
// New translation table
$table = array();
// Split the language: language, region, locale, etc
$parts = explode('-', $lang);
do
{
// Create a path for this set of parts
$path = implode(DIRECTORY_SEPARATOR, $parts);
if ($files = Kohana::find_file('i18n', $path, NULL, TRUE))
{
$t = array();
foreach ($files as $file)
{
// Merge the language strings into the sub table
$t = array_merge($t, Kohana::load($file));
}
// Append the sub table, preventing less specific language
// files from overloading more specific files
$table += $t;
}
// Remove the last part
array_pop($parts);
}
while ($parts);
// Cache the translation table locally
return I18n::$_cache[$lang] = $table;
}
} // End I18n

View File

@@ -0,0 +1,269 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* Inflector helper class. Inflection is changing the form of a word based on
* the context it is used in. For example, changing a word into a plural form.
*
* [!!] Inflection is only tested with English, and is will not work with other languages.
*
* @package Kohana
* @category Helpers
* @author Kohana Team
* @copyright (c) 2007-2010 Kohana Team
* @license http://kohanaframework.org/license
*/
class Kohana_Inflector {
/**
* @var array cached inflections
*/
protected static $cache = array();
/**
* @var array uncountable words
*/
protected static $uncountable;
/**
* @var array irregular words
*/
protected static $irregular;
/**
* Checks if a word is defined as uncountable. An uncountable word has a
* single form. For instance, one "fish" and many "fish", not "fishes".
*
* Inflector::uncountable('fish'); // TRUE
* Inflector::uncountable('cat'); // FALSE
*
* If you find a word is being pluralized improperly, it has probably not
* been defined as uncountable in `config/inflector.php`. If this is the
* case, please report [an issue](http://dev.kohanaphp.com/projects/kohana3/issues).
*
* @param string word to check
* @return boolean
*/
public static function uncountable($str)
{
if (Inflector::$uncountable === NULL)
{
// Cache uncountables
Inflector::$uncountable = Kohana::config('inflector')->uncountable;
// Make uncountables mirrored
Inflector::$uncountable = array_combine(Inflector::$uncountable, Inflector::$uncountable);
}
return isset(Inflector::$uncountable[strtolower($str)]);
}
/**
* Makes a plural word singular.
*
* echo Inflector::singular('cats'); // "cat"
* echo Inflector::singular('fish'); // "fish", uncountable
*
* You can also provide the count to make inflection more intelligent.
* In this case, it will only return the singular value if the count is
* greater than one and not zero.
*
* echo Inflector::singular('cats', 2); // "cats"
*
* [!!] Special inflections are defined in `config/inflector.php`.
*
* @param string word to singularize
* @param integer count of thing
* @return string
* @uses Inflector::uncountable
*/
public static function singular($str, $count = NULL)
{
// $count should always be a float
$count = ($count === NULL) ? 1.0 : (float) $count;
// Do nothing when $count is not 1
if ($count != 1)
return $str;
// Remove garbage
$str = strtolower(trim($str));
// Cache key name
$key = 'singular_'.$str.$count;
if (isset(Inflector::$cache[$key]))
return Inflector::$cache[$key];
if (Inflector::uncountable($str))
return Inflector::$cache[$key] = $str;
if (empty(Inflector::$irregular))
{
// Cache irregular words
Inflector::$irregular = Kohana::config('inflector')->irregular;
}
if ($irregular = array_search($str, Inflector::$irregular))
{
$str = $irregular;
}
elseif (preg_match('/us$/', $str))
{
// http://en.wikipedia.org/wiki/Plural_form_of_words_ending_in_-us
// Already singular, do nothing
}
elseif (preg_match('/[sxz]es$/', $str) OR preg_match('/[^aeioudgkprt]hes$/', $str))
{
// Remove "es"
$str = substr($str, 0, -2);
}
elseif (preg_match('/[^aeiou]ies$/', $str))
{
// Replace "ies" with "y"
$str = substr($str, 0, -3).'y';
}
elseif (substr($str, -1) === 's' AND substr($str, -2) !== 'ss')
{
// Remove singular "s"
$str = substr($str, 0, -1);
}
return Inflector::$cache[$key] = $str;
}
/**
* Makes a singular word plural.
*
* echo Inflector::plural('fish'); // "fish", uncountable
* echo Inflector::plural('cat'); // "cats"
*
* You can also provide the count to make inflection more intelligent.
* In this case, it will only return the plural value if the count is
* not one.
*
* echo Inflector::singular('cats', 3); // "cats"
*
* [!!] Special inflections are defined in `config/inflector.php`.
*
* @param string word to pluralize
* @param integer count of thing
* @return string
* @uses Inflector::uncountable
*/
public static function plural($str, $count = NULL)
{
// $count should always be a float
$count = ($count === NULL) ? 0.0 : (float) $count;
// Do nothing with singular
if ($count == 1)
return $str;
// Remove garbage
$str = trim($str);
// Cache key name
$key = 'plural_'.$str.$count;
// Check uppercase
$is_uppercase = ctype_upper($str);
if (isset(Inflector::$cache[$key]))
return Inflector::$cache[$key];
if (Inflector::uncountable($str))
return Inflector::$cache[$key] = $str;
if (empty(Inflector::$irregular))
{
// Cache irregular words
Inflector::$irregular = Kohana::config('inflector')->irregular;
}
if (isset(Inflector::$irregular[$str]))
{
$str = Inflector::$irregular[$str];
}
elseif (preg_match('/[sxz]$/', $str) OR preg_match('/[^aeioudgkprt]h$/', $str))
{
$str .= 'es';
}
elseif (preg_match('/[^aeiou]y$/', $str))
{
// Change "y" to "ies"
$str = substr_replace($str, 'ies', -1);
}
else
{
$str .= 's';
}
// Convert to uppsecase if nessasary
if ($is_uppercase)
{
$str = strtoupper($str);
}
// Set the cache and return
return Inflector::$cache[$key] = $str;
}
/**
* Makes a phrase camel case. Spaces and underscores will be removed.
*
* $str = Inflector::camelize('mother cat'); // "motherCat"
* $str = Inflector::camelize('kittens in bed'); // "kittensInBed"
*
* @param string phrase to camelize
* @return string
*/
public static function camelize($str)
{
$str = 'x'.strtolower(trim($str));
$str = ucwords(preg_replace('/[\s_]+/', ' ', $str));
return substr(str_replace(' ', '', $str), 1);
}
/**
* Converts a camel case phrase into a spaced phrase.
*
* $str = Inflector::decamelize('houseCat'); // "house cat"
* $str = Inflector::decamelize('kingAllyCat'); // "king ally cat"
*
* @param string phrase to camelize
* @param string word separator
* @return string
*/
public static function decamelize($str, $sep = ' ')
{
return strtolower(preg_replace('/([a-z])([A-Z])/', '$1'.$sep.'$2', trim($str)));
}
/**
* Makes a phrase underscored instead of spaced.
*
* $str = Inflector::underscore('five cats'); // "five_cats";
*
* @param string phrase to underscore
* @return string
*/
public static function underscore($str)
{
return preg_replace('/\s+/', '_', trim($str));
}
/**
* Makes an underscored or dashed phrase human-readable.
*
* $str = Inflector::humanize('kittens-are-cats'); // "kittens are cats"
* $str = Inflector::humanize('dogs_as_well'); // "dogs as well"
*
* @param string phrase to make human-readable
* @return string
*/
public static function humanize($str)
{
return preg_replace('/[_-]+/', ' ', trim($str));
}
} // End Inflector

View File

@@ -0,0 +1,189 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* Message logging with observer-based log writing.
*
* [!!] This class does not support extensions, only additional writers.
*
* @package Kohana
* @category Logging
* @author Kohana Team
* @copyright (c) 2008-2010 Kohana Team
* @license http://kohanaframework.org/license
*/
class Kohana_Log {
/**
* @var string timestamp format for log entries
*/
public static $timestamp = 'Y-m-d H:i:s';
/**
* @var string timezone for log entries
*/
public static $timezone;
/**
* @var boolean immediately write when logs are added
*/
public static $write_on_add = FALSE;
/**
* @var Kohana_Log Singleton instance container
*/
private static $_instance;
/**
* Get the singleton instance of this class and enable writing at shutdown.
*
* $log = Kohana_Log::instance();
*
* @return Kohana_Log
*/
public static function instance()
{
if (self::$_instance === NULL)
{
// Create a new instance
self::$_instance = new self;
// Write the logs at shutdown
register_shutdown_function(array(self::$_instance, 'write'));
}
return self::$_instance;
}
/**
* @var array list of added messages
*/
private $_messages = array();
/**
* @var array list of log writers
*/
private $_writers = array();
/**
* Attaches a log writer, and optionally limits the types of messages that
* will be written by the writer.
*
* $log->attach($writer);
*
* @param object Kohana_Log_Writer instance
* @param array messages types to write
* @return Kohana_Log
*/
public function attach(Kohana_Log_Writer $writer, array $types = NULL)
{
$this->_writers["{$writer}"] = array
(
'object' => $writer,
'types' => $types
);
return $this;
}
/**
* Detaches a log writer. The same writer object must be used.
*
* $log->detach($writer);
*
* @param object Kohana_Log_Writer instance
* @return Kohana_Log
*/
public function detach(Kohana_Log_Writer $writer)
{
// Remove the writer
unset($this->_writers["{$writer}"]);
return $this;
}
/**
* Adds a message to the log. Replacement values must be passed in to be
* replaced using [strtr](http://php.net/strtr).
*
* $log->add('error', 'Could not locate user: :user', array(
* ':user' => $username,
* ));
*
* @param string type of message
* @param string message body
* @param array values to replace in the message
* @return Kohana_Log
*/
public function add($type, $message, array $values = NULL)
{
if ($values)
{
// Insert the values into the message
$message = strtr($message, $values);
}
// Create a new message and timestamp it
$this->_messages[] = array
(
'time' => Date::formatted_time('now', self::$timestamp, self::$timezone),
'type' => $type,
'body' => $message,
);
if (self::$write_on_add)
{
// Write logs as they are added
$this->write();
}
return $this;
}
/**
* Write and clear all of the messages.
*
* $log->write();
*
* @return void
*/
public function write()
{
if (empty($this->_messages))
{
// There is nothing to write, move along
return;
}
// Import all messages locally
$messages = $this->_messages;
// Reset the messages array
$this->_messages = array();
foreach ($this->_writers as $writer)
{
if (empty($writer['types']))
{
// Write all of the messages
$writer['object']->write($messages);
}
else
{
// Filtered messages
$filtered = array();
foreach ($messages as $message)
{
if (in_array($message['type'], $writer['types']))
{
// Writer accepts this kind of message
$filtered[] = $message;
}
}
// Write the filtered messages
$writer['object']->write($filtered);
}
}
}
} // End Kohana_Log

View File

@@ -0,0 +1,97 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* File log writer. Writes out messages and stores them in a YYYY/MM directory.
*
* @package Kohana
* @category Logging
* @author Kohana Team
* @copyright (c) 2008-2010 Kohana Team
* @license http://kohanaframework.org/license
*/
class Kohana_Log_File extends Kohana_Log_Writer {
/**
* @var string Directory to place log files in
*/
protected $_directory;
/**
* Creates a new file logger. Checks that the directory exists and
* is writable.
*
* $writer = new Kohana_Log_File($directory);
*
* @param string log directory
* @return void
*/
public function __construct($directory)
{
if ( ! is_dir($directory) OR ! is_writable($directory))
{
throw new Kohana_Exception('Directory :dir must be writable',
array(':dir' => Kohana::debug_path($directory)));
}
// Determine the directory path
$this->_directory = realpath($directory).DIRECTORY_SEPARATOR;
}
/**
* Writes each of the messages into the log file. The log file will be
* appended to the `YYYY/MM/DD.log.php` file, where YYYY is the current
* year, MM is the current month, and DD is the current day.
*
* $writer->write($messages);
*
* @param array messages
* @return void
*/
public function write(array $messages)
{
// Set the yearly directory name
$directory = $this->_directory.date('Y');
if ( ! is_dir($directory))
{
// Create the yearly directory
mkdir($directory, 02777);
// Set permissions (must be manually set to fix umask issues)
chmod($directory, 02777);
}
// Add the month to the directory
$directory .= DIRECTORY_SEPARATOR.date('m');
if ( ! is_dir($directory))
{
// Create the yearly directory
mkdir($directory, 02777);
// Set permissions (must be manually set to fix umask issues)
chmod($directory, 02777);
}
// Set the name of the log file
$filename = $directory.DIRECTORY_SEPARATOR.date('d').EXT;
if ( ! file_exists($filename))
{
// Create the log file
file_put_contents($filename, Kohana::FILE_SECURITY.' ?>'.PHP_EOL);
// Allow anyone to write to log files
chmod($filename, 0666);
}
// Set the log line format
$format = 'time --- type: body';
foreach ($messages as $message)
{
// Write each message into the log file
file_put_contents($filename, PHP_EOL.strtr($format, $message), FILE_APPEND);
}
}
} // End Kohana_Log_File

View File

@@ -0,0 +1,31 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* STDERR log writer. Writes out messages to STDERR.
*
* @package Kohana
* @category Logging
* @author Kohana Team
* @copyright (c) 2008-2010 Kohana Team
* @license http://kohanaphp.com/license
*/
class Kohana_Log_StdErr extends Kohana_Log_Writer {
/**
* Writes each of the messages to STDERR.
*
* $writer->write($messages);
*
* @param array messages
* @return void
*/
public function write(array $messages)
{
// Set the log line format
$format = 'time --- type: body';
foreach ($messages as $message)
{
// Writes out each message
fwrite(STDERR, PHP_EOL.strtr($format, $message));
}
}
} // End Kohana_Log_StdErr

View File

@@ -0,0 +1,31 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* STDOUT log writer. Writes out messages to STDOUT.
*
* @package Kohana
* @category Logging
* @author Kohana Team
* @copyright (c) 2008-2010 Kohana Team
* @license http://kohanaphp.com/license
*/
class Kohana_Log_StdOut extends Kohana_Log_Writer {
/**
* Writes each of the messages to STDOUT.
*
* $writer->write($messages);
*
* @param array messages
* @return void
*/
public function write(array $messages)
{
// Set the log line format
$format = 'time --- type: body';
foreach ($messages as $message)
{
// Writes out each message
fwrite(STDOUT, PHP_EOL.strtr($format, $message));
}
}
} // End Kohana_Log_StdOut

View File

@@ -0,0 +1,70 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* Syslog log writer.
*
* @package Kohana
* @category Logging
* @author Jeremy Bush
* @copyright (c) 2010 Kohana Team
* @license http://kohanaframework.org/license
*/
class Kohana_Log_Syslog extends Kohana_Log_Writer {
/**
* @var string The syslog identifier
*/
protected $_ident;
/**
* @var array log levels
*/
protected $_syslog_levels = array('ERROR' => LOG_ERR,
'CRITICAL' => LOG_CRIT,
'STRACE' => LOG_ALERT,
'ALERT' => LOG_WARNING,
'INFO' => LOG_INFO,
'DEBUG' => LOG_DEBUG);
/**
* Creates a new syslog logger.
*
* @see http://us2.php.net/openlog
*
* @param string syslog identifier
* @param int facility to log to
* @return void
*/
public function __construct($ident = 'KohanaPHP', $facility = LOG_USER)
{
$this->_ident = $ident;
// Open the connection to syslog
openlog($this->_ident, LOG_CONS, $facility);
}
/**
* Writes each of the messages into the syslog.
*
* @param array messages
* @return void
*/
public function write(array $messages)
{
foreach ($messages as $message)
{
syslog($this->_syslog_levels[$message['type']], $message['body']);
}
}
/**
* Closes the syslog connection
*
* @return void
*/
public function __destruct()
{
// Close connection to syslog
closelog();
}
} // End Kohana_Log_Syslog

View File

@@ -0,0 +1,35 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* Log writer abstract class. All [Kohana_Log] writers must extend this class.
*
* @package Kohana
* @category Logging
* @author Kohana Team
* @copyright (c) 2008-2010 Kohana Team
* @license http://kohanaframework.org/license
*/
abstract class Kohana_Log_Writer {
/**
* Write an array of messages.
*
* $writer->write($messages);
*
* @param array messages
* @return void
*/
abstract public function write(array $messages);
/**
* Allows the writer to have a unique key when stored.
*
* echo $writer;
*
* @return string
*/
final public function __toString()
{
return spl_object_hash($this);
}
} // End Kohana_Log_Writer

View File

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

View File

@@ -0,0 +1,81 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* Number helper class. Provides additional formatting methods that for working
* with numbers.
*
* @package Kohana
* @category Helpers
* @author Kohana Team
* @copyright (c) 2009-2010 Kohana Team
* @license http://kohanaframework.org/license
*/
class Kohana_Num {
/**
* Returns the English ordinal suffix (th, st, nd, etc) of a number.
*
* echo 2, Num::ordinal(2); // "2nd"
* echo 10, Num::ordinal(10); // "10th"
* echo 33, Num::ordinal(33); // "33rd"
*
* @param integer number
* @return string
*/
public static function ordinal($number)
{
if ($number % 100 > 10 AND $number % 100 < 14)
{
return 'th';
}
switch ($number % 10)
{
case 1:
return 'st';
case 2:
return 'nd';
case 3:
return 'rd';
default:
return 'th';
}
}
/**
* Locale-aware number and monetary formatting.
*
* // In English, "1,200.05"
* // In Spanish, "1200,05"
* // In Portuguese, "1 200,05"
* echo Num::format(1200.05, 2);
*
* // In English, "1,200.05"
* // In Spanish, "1.200,05"
* // In Portuguese, "1.200.05"
* echo Num::format(1200.05, 2, TRUE);
*
* @param float number to format
* @param integer decimal places
* @param boolean monetary formatting?
* @return string
* @since 3.0.2
*/
public static function format($number, $places, $monetary = FALSE)
{
$info = localeconv();
if ($monetary)
{
$decimal = $info['mon_decimal_point'];
$thousands = $info['mon_thousands_sep'];
}
else
{
$decimal = $info['decimal_point'];
$thousands = $info['thousands_sep'];
}
return number_format($number, $places, $decimal, $thousands);
}
} // End num

View File

@@ -0,0 +1,385 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* Provides simple benchmarking and profiling. To display the statistics that
* have been collected, load the `profiler/stats` [View]:
*
* echo View::factory('profiler/stats');
*
* @package Kohana
* @category Helpers
* @author Kohana Team
* @copyright (c) 2009-2010 Kohana Team
* @license http://kohanaframework.org/license
*/
class Kohana_Profiler {
/**
* @var integer maximium number of application stats to keep
*/
public static $rollover = 1000;
/**
* @var array collected benchmarks
*/
protected static $_marks = array();
/**
* Starts a new benchmark and returns a unique token. The returned token
* _must_ be used when stopping the benchmark.
*
* $token = Profiler::start('test', 'profiler');
*
* @param string group name
* @param string benchmark name
* @return string
*/
public static function start($group, $name)
{
static $counter = 0;
// Create a unique token based on the counter
$token = 'kp/'.base_convert($counter++, 10, 32);
Profiler::$_marks[$token] = array
(
'group' => strtolower($group),
'name' => (string) $name,
// Start the benchmark
'start_time' => microtime(TRUE),
'start_memory' => memory_get_usage(),
// Set the stop keys without values
'stop_time' => FALSE,
'stop_memory' => FALSE,
);
return $token;
}
/**
* Stops a benchmark.
*
* Profiler::stop($token);
*
* @param string token
* @return void
*/
public static function stop($token)
{
// Stop the benchmark
Profiler::$_marks[$token]['stop_time'] = microtime(TRUE);
Profiler::$_marks[$token]['stop_memory'] = memory_get_usage();
}
/**
* Deletes a benchmark. If an error occurs during the benchmark, it is
* recommended to delete the benchmark to prevent statistics from being
* adversely affected.
*
* Profiler::delete($token);
*
* @param string token
* @return void
*/
public static function delete($token)
{
// Remove the benchmark
unset(Profiler::$_marks[$token]);
}
/**
* Returns all the benchmark tokens by group and name as an array.
*
* $groups = Profiler::groups();
*
* @return array
*/
public static function groups()
{
$groups = array();
foreach (Profiler::$_marks as $token => $mark)
{
// Sort the tokens by the group and name
$groups[$mark['group']][$mark['name']][] = $token;
}
return $groups;
}
/**
* Gets the min, max, average and total of a set of tokens as an array.
*
* $stats = Profiler::stats($tokens);
*
* @param array profiler tokens
* @return array min, max, average, total
* @uses Profiler::total
*/
public static function stats(array $tokens)
{
// Min and max are unknown by default
$min = $max = array(
'time' => NULL,
'memory' => NULL);
// Total values are always integers
$total = array(
'time' => 0,
'memory' => 0);
foreach ($tokens as $token)
{
// Get the total time and memory for this benchmark
list($time, $memory) = Profiler::total($token);
if ($max['time'] === NULL OR $time > $max['time'])
{
// Set the maximum time
$max['time'] = $time;
}
if ($min['time'] === NULL OR $time < $min['time'])
{
// Set the minimum time
$min['time'] = $time;
}
// Increase the total time
$total['time'] += $time;
if ($max['memory'] === NULL OR $memory > $max['memory'])
{
// Set the maximum memory
$max['memory'] = $memory;
}
if ($min['memory'] === NULL OR $memory < $min['memory'])
{
// Set the minimum memory
$min['memory'] = $memory;
}
// Increase the total memory
$total['memory'] += $memory;
}
// Determine the number of tokens
$count = count($tokens);
// Determine the averages
$average = array(
'time' => $total['time'] / $count,
'memory' => $total['memory'] / $count);
return array(
'min' => $min,
'max' => $max,
'total' => $total,
'average' => $average);
}
/**
* Gets the min, max, average and total of profiler groups as an array.
*
* $stats = Profiler::group_stats('test');
*
* @param mixed single group name string, or array with group names; all groups by default
* @return array min, max, average, total
* @uses Profiler::groups
* @uses Profiler::stats
*/
public static function group_stats($groups = NULL)
{
// Which groups do we need to calculate stats for?
$groups = ($groups === NULL)
? Profiler::groups()
: array_intersect_key(Profiler::groups(), array_flip( (array) $groups));
// All statistics
$stats = array();
foreach ($groups as $group => $names)
{
foreach ($names as $name => $tokens)
{
// Store the stats for each subgroup.
// We only need the values for "total".
$_stats = Profiler::stats($tokens);
$stats[$group][$name] = $_stats['total'];
}
}
// Group stats
$groups = array();
foreach ($stats as $group => $names)
{
// Min and max are unknown by default
$groups[$group]['min'] = $groups[$group]['max'] = array(
'time' => NULL,
'memory' => NULL);
// Total values are always integers
$groups[$group]['total'] = array(
'time' => 0,
'memory' => 0);
foreach ($names as $total)
{
if ( ! isset($groups[$group]['min']['time']) OR $groups[$group]['min']['time'] > $total['time'])
{
// Set the minimum time
$groups[$group]['min']['time'] = $total['time'];
}
if ( ! isset($groups[$group]['min']['memory']) OR $groups[$group]['min']['memory'] > $total['memory'])
{
// Set the minimum memory
$groups[$group]['min']['memory'] = $total['memory'];
}
if ( ! isset($groups[$group]['max']['time']) OR $groups[$group]['max']['time'] < $total['time'])
{
// Set the maximum time
$groups[$group]['max']['time'] = $total['time'];
}
if ( ! isset($groups[$group]['max']['memory']) OR $groups[$group]['max']['memory'] < $total['memory'])
{
// Set the maximum memory
$groups[$group]['max']['memory'] = $total['memory'];
}
// Increase the total time and memory
$groups[$group]['total']['time'] += $total['time'];
$groups[$group]['total']['memory'] += $total['memory'];
}
// Determine the number of names (subgroups)
$count = count($names);
// Determine the averages
$groups[$group]['average']['time'] = $groups[$group]['total']['time'] / $count;
$groups[$group]['average']['memory'] = $groups[$group]['total']['memory'] / $count;
}
return $groups;
}
/**
* Gets the total execution time and memory usage of a benchmark as a list.
*
* list($time, $memory) = Profiler::total($token);
*
* @param string token
* @return array execution time, memory
*/
public static function total($token)
{
// Import the benchmark data
$mark = Profiler::$_marks[$token];
if ($mark['stop_time'] === FALSE)
{
// The benchmark has not been stopped yet
$mark['stop_time'] = microtime(TRUE);
$mark['stop_memory'] = memory_get_usage();
}
return array
(
// Total time in seconds
$mark['stop_time'] - $mark['start_time'],
// Amount of memory in bytes
$mark['stop_memory'] - $mark['start_memory'],
);
}
/**
* Gets the total application run time and memory usage. Caches the result
* so that it can be compared between requests.
*
* list($time, $memory) = Profiler::application();
*
* @return array execution time, memory
* @uses Kohana::cache
*/
public static function application()
{
// Load the stats from cache, which is valid for 1 day
$stats = Kohana::cache('profiler_application_stats', NULL, 3600 * 24);
if ( ! is_array($stats) OR $stats['count'] > Profiler::$rollover)
{
// Initialize the stats array
$stats = array(
'min' => array(
'time' => NULL,
'memory' => NULL),
'max' => array(
'time' => NULL,
'memory' => NULL),
'total' => array(
'time' => NULL,
'memory' => NULL),
'count' => 0);
}
// Get the application run time
$time = microtime(TRUE) - KOHANA_START_TIME;
// Get the total memory usage
$memory = memory_get_usage() - KOHANA_START_MEMORY;
// Calculate max time
if ($stats['max']['time'] === NULL OR $time > $stats['max']['time'])
{
$stats['max']['time'] = $time;
}
// Calculate min time
if ($stats['min']['time'] === NULL OR $time < $stats['min']['time'])
{
$stats['min']['time'] = $time;
}
// Add to total time
$stats['total']['time'] += $time;
// Calculate max memory
if ($stats['max']['memory'] === NULL OR $memory > $stats['max']['memory'])
{
$stats['max']['memory'] = $memory;
}
// Calculate min memory
if ($stats['min']['memory'] === NULL OR $memory < $stats['min']['memory'])
{
$stats['min']['memory'] = $memory;
}
// Add to total memory
$stats['total']['memory'] += $memory;
// Another mark has been added to the stats
$stats['count']++;
// Determine the averages
$stats['average'] = array(
'time' => $stats['total']['time'] / $stats['count'],
'memory' => $stats['total']['memory'] / $stats['count']);
// Cache the new stats
Kohana::cache('profiler_application_stats', $stats);
// Set the current application execution time and memory
// Do NOT cache these, they are specific to the current request only
$stats['current']['time'] = $time;
$stats['current']['memory'] = $memory;
// Return the total application run time and memory usage
return $stats;
}
} // End Profiler

View File

@@ -0,0 +1,154 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* Provides remote server communications options using [curl](http://php.net/curl).
*
* @package Kohana
* @category Helpers
* @author Kohana Team
* @copyright (c) 2008-2010 Kohana Team
* @license http://kohanaframework.org/license
*/
class Kohana_Remote {
/**
* @var array default cURL options
*/
public static $default_options = array
(
CURLOPT_USERAGENT => 'Mozilla/5.0 (compatible; Kohana v3.0 +http://kohanaframework.org/)',
CURLOPT_CONNECTTIMEOUT => 5,
CURLOPT_TIMEOUT => 5,
);
/**
* Returns the output of a remote URL. Any [curl option](http://php.net/curl_setopt)
* may be used.
*
* // Do a simple GET request
* $data = Remote::get($url);
*
* // Do a POST request
* $data = Remote::get($url, array(
* CURLOPT_POST => TRUE,
* CURLOPT_POSTFIELDS => http_build_query($array),
* ));
*
* @param string remote URL
* @param array curl options
* @return string
* @throws Kohana_Exception
*/
public static function get($url, array $options = NULL)
{
if ($options === NULL)
{
// Use default options
$options = Remote::$default_options;
}
else
{
// Add default options
$options = $options + Remote::$default_options;
}
// The transfer must always be returned
$options[CURLOPT_RETURNTRANSFER] = TRUE;
// Open a new remote connection
$remote = curl_init($url);
// Set connection options
if ( ! curl_setopt_array($remote, $options))
{
throw new Kohana_Exception('Failed to set CURL options, check CURL documentation: :url',
array(':url' => 'http://php.net/curl_setopt_array'));
}
// Get the response
$response = curl_exec($remote);
// Get the response information
$code = curl_getinfo($remote, CURLINFO_HTTP_CODE);
if ($code AND $code < 200 OR $code > 299)
{
$error = $response;
}
elseif ($response === FALSE)
{
$error = curl_error($remote);
}
// Close the connection
curl_close($remote);
if (isset($error))
{
throw new Kohana_Exception('Error fetching remote :url [ status :code ] :error',
array(':url' => $url, ':code' => $code, ':error' => $error));
}
return $response;
}
/**
* Returns the status code (200, 500, etc) for a URL.
*
* $status = Remote::status($url);
*
* @param string URL to check
* @return integer
*/
public static function status($url)
{
// Get the hostname and path
$url = parse_url($url);
if (empty($url['path']))
{
// Request the root document
$url['path'] = '/';
}
// Open a remote connection
$port = isset($url['port']) ? $url['port'] : 80;
$remote = fsockopen($url['host'], $port, $errno, $errstr, 5);
if ( ! is_resource($remote))
return FALSE;
// Set CRLF
$line_feed = "\r\n";
// Send request
fwrite($remote, 'HEAD '.$url['path'].' HTTP/1.0'.$line_feed);
fwrite($remote, 'Host: '.$url['host'].$line_feed);
fwrite($remote, 'Connection: close'.$line_feed);
fwrite($remote, 'User-Agent: Kohana Framework (+http://kohanaframework.org/)'.$line_feed);
// Send one more CRLF to terminate the headers
fwrite($remote, $line_feed);
// Remote is offline
$response = FALSE;
while ( ! feof($remote))
{
// Get the line
$line = trim(fgets($remote, 512));
if ($line !== '' AND preg_match('#^HTTP/1\.[01] (\d{3})#', $line, $matches))
{
// Response code found
$response = (int) $matches[1];
break;
}
}
// Close the connection
fclose($remote);
return $response;
}
} // End remote

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,9 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* @package Kohana
* @category Exceptions
* @author Kohana Team
* @copyright (c) 2009-2010 Kohana Team
* @license http://kohanaframework.org/license
*/
class Kohana_Request_Exception extends Kohana_Exception { }

View File

@@ -0,0 +1,418 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* Routes are used to determine the controller and action for a requested URI.
* Every route generates a regular expression which is used to match a URI
* and a route. Routes may also contain keys which can be used to set the
* controller, action, and parameters.
*
* Each <key> will be translated to a regular expression using a default
* regular expression pattern. You can override the default pattern by providing
* a pattern for the key:
*
* // This route will only match when <id> is a digit
* Route::set('user', 'user/<action>/<id>', array('id' => '\d+'));
*
* // This route will match when <path> is anything
* Route::set('file', '<path>', array('path' => '.*'));
*
* It is also possible to create optional segments by using parentheses in
* the URI definition:
*
* // This is the standard default route, and no keys are required
* Route::set('default', '(<controller>(/<action>(/<id>)))');
*
* // This route only requires the <file> key
* Route::set('file', '(<path>/)<file>(.<format>)', array('path' => '.*', 'format' => '\w+'));
*
* Routes also provide a way to generate URIs (called "reverse routing"), which
* makes them an extremely powerful and flexible way to generate internal links.
*
* @package Kohana
* @category Base
* @author Kohana Team
* @copyright (c) 2008-2010 Kohana Team
* @license http://kohanaframework.org/license
*/
class Kohana_Route {
// Defines the pattern of a <segment>
const REGEX_KEY = '<([a-zA-Z0-9_]++)>';
// What can be part of a <segment> value
const REGEX_SEGMENT = '[^/.,;?\n]++';
// What must be escaped in the route regex
const REGEX_ESCAPE = '[.\\+*?[^\\]${}=!|]';
/**
* @var string default action for all routes
*/
public static $default_action = 'index';
/**
* @var array list of route objects
*/
protected static $_routes = array();
/**
* Stores a named route and returns it. The "action" will always be set to
* "index" if it is not defined.
*
* Route::set('default', '(<controller>(/<action>(/<id>)))')
* ->defaults(array(
* 'controller' => 'welcome',
* ));
*
* @param string route name
* @param string URI pattern
* @param array regex patterns for route keys
* @return Route
*/
public static function set($name, $uri, array $regex = NULL)
{
return Route::$_routes[$name] = new Route($uri, $regex);
}
/**
* Retrieves a named route.
*
* $route = Route::get('default');
*
* @param string route name
* @return Route
* @throws Kohana_Exception
*/
public static function get($name)
{
if ( ! isset(Route::$_routes[$name]))
{
throw new Kohana_Exception('The requested route does not exist: :route',
array(':route' => $name));
}
return Route::$_routes[$name];
}
/**
* Retrieves all named routes.
*
* $routes = Route::all();
*
* @return array routes by name
*/
public static function all()
{
return Route::$_routes;
}
/**
* Get the name of a route.
*
* $name = Route::name($route)
*
* @param object Route instance
* @return string
*/
public static function name(Route $route)
{
return array_search($route, Route::$_routes);
}
/**
* Saves or loads the route cache. If your routes will remain the same for
* a long period of time, use this to reload the routes from the cache
* rather than redefining them on every page load.
*
* if ( ! Route::cache())
* {
* // Set routes here
* Route::cache(TRUE);
* }
*
* @param boolean cache the current routes
* @return void when saving routes
* @return boolean when loading routes
* @uses Kohana::cache
*/
public static function cache($save = FALSE)
{
if ($save === TRUE)
{
// Cache all defined routes
Kohana::cache('Route::cache()', Route::$_routes);
}
else
{
if ($routes = Kohana::cache('Route::cache()'))
{
Route::$_routes = $routes;
// Routes were cached
return TRUE;
}
else
{
// Routes were not cached
return FALSE;
}
}
}
/**
* Create a URL from a route name. This is a shortcut for:
*
* echo URL::site(Route::get($name)->uri($params), $protocol);
*
* @param string route name
* @param array URI parameters
* @param mixed protocol string or boolean, adds protocol and domain
* @return string
* @since 3.0.7
* @uses URL::site
*/
public static function url($name, array $params = NULL, $protocol = NULL)
{
// Create a URI with the route and convert it to a URL
return URL::site(Route::get($name)->uri($params), $protocol);
}
// Route URI string
protected $_uri = '';
// Regular expressions for route keys
protected $_regex = array();
// Default values for route keys
protected $_defaults = array('action' => 'index');
// Compiled regex cache
protected $_route_regex;
/**
* Creates a new route. Sets the URI and regular expressions for keys.
* Routes should always be created with [Route::set] or they will not
* be properly stored.
*
* $route = new Route($uri, $regex);
*
* @param string route URI pattern
* @param array key patterns
* @return void
* @uses Route::_compile
*/
public function __construct($uri = NULL, array $regex = NULL)
{
if ($uri === NULL)
{
// Assume the route is from cache
return;
}
if ( ! empty($regex))
{
$this->_regex = $regex;
}
// Store the URI that this route will match
$this->_uri = $uri;
// Store the compiled regex locally
$this->_route_regex = $this->_compile();
}
/**
* Provides default values for keys when they are not present. The default
* action will always be "index" unless it is overloaded here.
*
* $route->defaults(array(
* 'controller' => 'welcome',
* 'action' => 'index'
* ));
*
* @param array key values
* @return $this
*/
public function defaults(array $defaults = NULL)
{
$this->_defaults = $defaults;
return $this;
}
/**
* Tests if the route matches a given URI. A successful match will return
* all of the routed parameters as an array. A failed match will return
* boolean FALSE.
*
* // Params: controller = users, action = edit, id = 10
* $params = $route->matches('users/edit/10');
*
* This method should almost always be used within an if/else block:
*
* if ($params = $route->matches($uri))
* {
* // Parse the parameters
* }
*
* @param string URI to match
* @return array on success
* @return FALSE on failure
*/
public function matches($uri)
{
if ( ! preg_match($this->_route_regex, $uri, $matches))
return FALSE;
$params = array();
foreach ($matches as $key => $value)
{
if (is_int($key))
{
// Skip all unnamed keys
continue;
}
// Set the value for all matched keys
$params[$key] = $value;
}
foreach ($this->_defaults as $key => $value)
{
if ( ! isset($params[$key]) OR $params[$key] === '')
{
// Set default values for any key that was not matched
$params[$key] = $value;
}
}
return $params;
}
/**
* Generates a URI for the current route based on the parameters given.
*
* // Using the "default" route: "users/profile/10"
* $route->uri(array(
* 'controller' => 'users',
* 'action' => 'profile',
* 'id' => '10'
* ));
*
* @param array URI parameters
* @return string
* @throws Kohana_Exception
* @uses Route::REGEX_Key
*/
public function uri(array $params = NULL)
{
if ($params === NULL)
{
// Use the default parameters
$params = $this->_defaults;
}
else
{
// Add the default parameters
$params += $this->_defaults;
}
// Start with the routed URI
$uri = $this->_uri;
if (strpos($uri, '<') === FALSE AND strpos($uri, '(') === FALSE)
{
// This is a static route, no need to replace anything
return $uri;
}
while (preg_match('#\([^()]++\)#', $uri, $match))
{
// Search for the matched value
$search = $match[0];
// Remove the parenthesis from the match as the replace
$replace = substr($match[0], 1, -1);
while (preg_match('#'.Route::REGEX_KEY.'#', $replace, $match))
{
list($key, $param) = $match;
if (isset($params[$param]))
{
// Replace the key with the parameter value
$replace = str_replace($key, $params[$param], $replace);
}
else
{
// This group has missing parameters
$replace = '';
break;
}
}
// Replace the group in the URI
$uri = str_replace($search, $replace, $uri);
}
while (preg_match('#'.Route::REGEX_KEY.'#', $uri, $match))
{
list($key, $param) = $match;
if ( ! isset($params[$param]))
{
// Ungrouped parameters are required
throw new Kohana_Exception('Required route parameter not passed: :param',
array(':param' => $param));
}
$uri = str_replace($key, $params[$param], $uri);
}
// Trim all extra slashes from the URI
$uri = preg_replace('#//+#', '/', rtrim($uri, '/'));
return $uri;
}
/**
* Returns the compiled regular expression for the route. This translates
* keys and optional groups to a proper PCRE regular expression.
*
* $regex = $route->_compile();
*
* @return string
* @uses Route::REGEX_ESCAPE
* @uses Route::REGEX_SEGMENT
*/
protected function _compile()
{
// The URI should be considered literal except for keys and optional parts
// Escape everything preg_quote would escape except for : ( ) < >
$regex = preg_replace('#'.Route::REGEX_ESCAPE.'#', '\\\\$0', $this->_uri);
if (strpos($regex, '(') !== FALSE)
{
// Make optional parts of the URI non-capturing and optional
$regex = str_replace(array('(', ')'), array('(?:', ')?'), $regex);
}
// Insert default regex for keys
$regex = str_replace(array('<', '>'), array('(?P<', '>'.Route::REGEX_SEGMENT.')'), $regex);
if ( ! empty($this->_regex))
{
$search = $replace = array();
foreach ($this->_regex as $key => $value)
{
$search[] = "<$key>".Route::REGEX_SEGMENT;
$replace[] = "<$key>$value";
}
// Replace the default regex with the user-specified regex
$regex = str_replace($search, $replace, $regex);
}
return '#^'.$regex.'$#uD';
}
} // End Route

View File

@@ -0,0 +1,193 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* Security helper class.
*
* @package Kohana
* @category Security
* @author Kohana Team
* @copyright (c) 2007-2010 Kohana Team
* @license http://kohanaframework.org/license
*/
class Kohana_Security {
/**
* @var string key name used for token storage
*/
public static $token_name = 'security_token';
/**
* Remove XSS from user input.
*
* $str = Security::xss_clean($str);
*
* @author Christian Stocker <chregu@bitflux.ch>
* @copyright (c) 2001-2006 Bitflux GmbH
* @param mixed string or array to sanitize
* @return string
* @deprecated since v3.0.5
*/
public static function xss_clean($str)
{
// http://svn.bitflux.ch/repos/public/popoon/trunk/classes/externalinput.php
// +----------------------------------------------------------------------+
// | Copyright (c) 2001-2006 Bitflux GmbH |
// +----------------------------------------------------------------------+
// | Licensed under the Apache License, Version 2.0 (the "License"); |
// | you may not use this file except in compliance with the License. |
// | You may obtain a copy of the License at |
// | http://www.apache.org/licenses/LICENSE-2.0 |
// | Unless required by applicable law or agreed to in writing, software |
// | distributed under the License is distributed on an "AS IS" BASIS, |
// | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
// | implied. See the License for the specific language governing |
// | permissions and limitations under the License. |
// +----------------------------------------------------------------------+
// | Author: Christian Stocker <chregu@bitflux.ch> |
// +----------------------------------------------------------------------+
//
// Kohana Modifications:
// * Changed double quotes to single quotes, changed indenting and spacing
// * Removed magic_quotes stuff
// * Increased regex readability:
// * Used delimeters that aren't found in the pattern
// * Removed all unneeded escapes
// * Deleted U modifiers and swapped greediness where needed
// * Increased regex speed:
// * Made capturing parentheses non-capturing where possible
// * Removed parentheses where possible
// * Split up alternation alternatives
// * Made some quantifiers possessive
// * Handle arrays recursively
if (is_array($str) OR is_object($str))
{
foreach ($str as $k => $s)
{
$str[$k] = Security::xss_clean($s);
}
return $str;
}
// Remove all NULL bytes
$str = str_replace("\0", '', $str);
// Fix &entity\n;
$str = str_replace(array('&amp;','&lt;','&gt;'), array('&amp;amp;','&amp;lt;','&amp;gt;'), $str);
$str = preg_replace('/(&#*\w+)[\x00-\x20]+;/u', '$1;', $str);
$str = preg_replace('/(&#x*[0-9A-F]+);*/iu', '$1;', $str);
$str = html_entity_decode($str, ENT_COMPAT, Kohana::$charset);
// Remove any attribute starting with "on" or xmlns
$str = preg_replace('#(?:on[a-z]+|xmlns)\s*=\s*[\'"\x00-\x20]?[^\'>"]*[\'"\x00-\x20]?\s?#iu', '', $str);
// Remove javascript: and vbscript: protocols
$str = preg_replace('#([a-z]*)[\x00-\x20]*=[\x00-\x20]*([`\'"]*)[\x00-\x20]*j[\x00-\x20]*a[\x00-\x20]*v[\x00-\x20]*a[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iu', '$1=$2nojavascript...', $str);
$str = preg_replace('#([a-z]*)[\x00-\x20]*=([\'"]*)[\x00-\x20]*v[\x00-\x20]*b[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iu', '$1=$2novbscript...', $str);
$str = preg_replace('#([a-z]*)[\x00-\x20]*=([\'"]*)[\x00-\x20]*-moz-binding[\x00-\x20]*:#u', '$1=$2nomozbinding...', $str);
// Only works in IE: <span style="width: expression(alert('Ping!'));"></span>
$str = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?expression[\x00-\x20]*\([^>]*+>#is', '$1>', $str);
$str = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?behaviour[\x00-\x20]*\([^>]*+>#is', '$1>', $str);
$str = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:*[^>]*+>#ius', '$1>', $str);
// Remove namespaced elements (we do not need them)
$str = preg_replace('#</*\w+:\w[^>]*+>#i', '', $str);
do
{
// Remove really unwanted tags
$old = $str;
$str = preg_replace('#</*(?:applet|b(?:ase|gsound|link)|embed|frame(?:set)?|i(?:frame|layer)|l(?:ayer|ink)|meta|object|s(?:cript|tyle)|title|xml)[^>]*+>#i', '', $str);
}
while ($old !== $str);
return $str;
}
/**
* Generate and store a unique token which can be used to help prevent
* [CSRF](http://wikipedia.org/wiki/Cross_Site_Request_Forgery) attacks.
*
* $token = Security::token();
*
* You can insert this token into your forms as a hidden field:
*
* echo Form::hidden('csrf', Security::token());
*
* And then check it when using [Validate]:
*
* $array->rules('csrf', array(
* 'not_empty' => NULL,
* 'Security::check' => NULL,
* ));
*
* This provides a basic, but effective, method of preventing CSRF attacks.
*
* @param boolean force a new token to be generated?
* @return string
* @uses Session::instance
*/
public static function token($new = FALSE)
{
$session = Session::instance();
// Get the current token
$token = $session->get(Security::$token_name);
if ($new === TRUE OR ! $token)
{
// Generate a new unique token
$token = uniqid('security');
// Store the new token
$session->set(Security::$token_name, $token);
}
return $token;
}
/**
* Check that the given token matches the currently stored security token.
*
* if (Security::check($token))
* {
* // Pass
* }
*
* @param string token to check
* @return boolean
* @uses Security::token
*/
public static function check($token)
{
return Security::token() === $token;
}
/**
* Remove image tags from a string.
*
* $str = Security::strip_image_tags($str);
*
* @param string string to sanitize
* @return string
*/
public static function strip_image_tags($str)
{
return preg_replace('#<img\s.*?(?:src\s*=\s*["\']?([^"\'<>\s]*)["\']?[^>]*)?>#is', '$1', $str);
}
/**
* Encodes PHP tags in a string.
*
* $str = Security::encode_php_tags($str);
*
* @param string string to sanitize
* @return string
*/
public static function encode_php_tags($str)
{
return str_replace(array('<?', '?>'), array('&lt;?', '?&gt;'), $str);
}
} // End security

View File

@@ -0,0 +1,427 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* Base session class.
*
* @package Kohana
* @category Session
* @author Kohana Team
* @copyright (c) 2008-2010 Kohana Team
* @license http://kohanaframework.org/license
*/
abstract class Kohana_Session {
/**
* @var string default session adapter
*/
public static $default = 'native';
/**
* @var array session instances
*/
protected static $instances = array();
/**
* Creates a singleton session of the given type. Some session types
* (native, database) also support restarting a session by passing a
* session id as the second parameter.
*
* $session = Session::instance();
*
* [!!] [Session::write] will automatically be called when the request ends.
*
* @param string type of session (native, cookie, etc)
* @param string session identifier
* @return Session
* @uses Kohana::config
*/
public static function instance($type = NULL, $id = NULL)
{
if ($type === NULL)
{
// Use the default type
$type = Session::$default;
}
if ( ! isset(Session::$instances[$type]))
{
// Load the configuration for this type
$config = Kohana::config('session')->get($type);
// Set the session class name
$class = 'Session_'.ucfirst($type);
// Create a new session instance
Session::$instances[$type] = $session = new $class($config, $id);
// Write the session at shutdown
register_shutdown_function(array($session, 'write'));
}
return Session::$instances[$type];
}
/**
* @var string cookie name
*/
protected $_name = 'session';
/**
* @var int cookie lifetime
*/
protected $_lifetime = 0;
/**
* @var bool encrypt session data?
*/
protected $_encrypted = FALSE;
/**
* @var array session data
*/
protected $_data = array();
/**
* @var bool session destroyed?
*/
protected $_destroyed = FALSE;
/**
* Overloads the name, lifetime, and encrypted session settings.
*
* [!!] Sessions can only be created using the [Session::instance] method.
*
* @param array configuration
* @param string session id
* @return void
* @uses Session::read
*/
public function __construct(array $config = NULL, $id = NULL)
{
if (isset($config['name']))
{
// Cookie name to store the session id in
$this->_name = (string) $config['name'];
}
if (isset($config['lifetime']))
{
// Cookie lifetime
$this->_lifetime = (int) $config['lifetime'];
}
if (isset($config['encrypted']))
{
if ($config['encrypted'] === TRUE)
{
// Use the default Encrypt instance
$config['encrypted'] = 'default';
}
// Enable or disable encryption of data
$this->_encrypted = $config['encrypted'];
}
// Load the session
$this->read($id);
}
/**
* Session object is rendered to a serialized string. If encryption is
* enabled, the session will be encrypted. If not, the output string will
* be encoded using [base64_encode].
*
* echo $session;
*
* @return string
* @uses Encrypt::encode
*/
public function __toString()
{
// Serialize the data array
$data = serialize($this->_data);
if ($this->_encrypted)
{
// Encrypt the data using the default key
$data = Encrypt::instance($this->_encrypted)->encode($data);
}
else
{
// Obfuscate the data with base64 encoding
$data = base64_encode($data);
}
return $data;
}
/**
* Returns the current session array. The returned array can also be
* assigned by reference.
*
* // Get a copy of the current session data
* $data = $session->as_array();
*
* // Assign by reference for modification
* $data =& $session->as_array();
*
* @return array
*/
public function & as_array()
{
return $this->_data;
}
/**
* Get the current session id, if the session supports it.
*
* $id = $session->id();
*
* [!!] Not all session types have ids.
*
* @return string
* @since 3.0.8
*/
public function id()
{
return NULL;
}
/**
* Get the current session cookie name.
*
* $name = $session->name();
*
* @return string
* @since 3.0.8
*/
public function name()
{
return $this->_name;
}
/**
* Get a variable from the session array.
*
* $foo = $session->get('foo');
*
* @param string variable name
* @param mixed default value to return
* @return mixed
*/
public function get($key, $default = NULL)
{
return array_key_exists($key, $this->_data) ? $this->_data[$key] : $default;
}
/**
* Get and delete a variable from the session array.
*
* $bar = $session->get_once('bar');
*
* @param string variable name
* @param mixed default value to return
* @return mixed
*/
public function get_once($key, $default = NULL)
{
$value = $this->get($key, $default);
unset($this->_data[$key]);
return $value;
}
/**
* Set a variable in the session array.
*
* $session->set('foo', 'bar');
*
* @param string variable name
* @param mixed value
* @return $this
*/
public function set($key, $value)
{
$this->_data[$key] = $value;
return $this;
}
/**
* Set a variable by reference.
*
* $session->bind('foo', $foo);
*
* @param string variable name
* @param mixed referenced value
* @return $this
*/
public function bind($key, & $value)
{
$this->_data[$key] =& $value;
return $this;
}
/**
* Removes a variable in the session array.
*
* $session->delete('foo');
*
* @param string variable name
* @param ...
* @return $this
*/
public function delete($key)
{
$args = func_get_args();
foreach ($args as $key)
{
unset($this->_data[$key]);
}
return $this;
}
/**
* Loads existing session data.
*
* $session->read();
*
* @param string session id
* @return void
*/
public function read($id = NULL)
{
if (is_string($data = $this->_read($id)))
{
try
{
if ($this->_encrypted)
{
// Decrypt the data using the default key
$data = Encrypt::instance($this->_encrypted)->decode($data);
}
else
{
// Decode the base64 encoded data
$data = base64_decode($data);
}
// Unserialize the data
$data = unserialize($data);
}
catch (Exception $e)
{
// Ignore all reading errors
}
}
if (is_array($data))
{
// Load the data locally
$this->_data = $data;
}
}
/**
* Generates a new session id and returns it.
*
* $id = $session->regenerate();
*
* @return string
*/
public function regenerate()
{
return $this->_regenerate();
}
/**
* Sets the last_active timestamp and saves the session.
*
* $session->write();
*
* [!!] Any errors that occur during session writing will be logged,
* but not displayed, because sessions are written after output has
* been sent.
*
* @return boolean
* @uses Kohana::$log
*/
public function write()
{
if (headers_sent() OR $this->_destroyed)
{
// Session cannot be written when the headers are sent or when
// the session has been destroyed
return FALSE;
}
// Set the last active timestamp
$this->_data['last_active'] = time();
try
{
return $this->_write();
}
catch (Exception $e)
{
// Log & ignore all errors when a write fails
Kohana::$log->add(Kohana::ERROR, Kohana::exception_text($e))->write();
return FALSE;
}
}
/**
* Completely destroy the current session.
*
* $success = $session->destroy();
*
* @return boolean
*/
public function destroy()
{
if ($this->_destroyed === FALSE)
{
if ($this->_destroyed = $this->_destroy())
{
// The session has been destroyed, clear all data
$this->_data = array();
}
}
return $this->_destroyed;
}
/**
* Loads the raw session data string and returns it.
*
* @param string session id
* @return string
*/
abstract protected function _read($id = NULL);
/**
* Generate a new session id and return it.
*
* @return string
*/
abstract protected function _regenerate();
/**
* Writes the current session.
*
* @return boolean
*/
abstract protected function _write();
/**
* Destroys the current session.
*
* @return boolean
*/
abstract protected function _destroy();
} // End Session

View File

@@ -0,0 +1,47 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* Cookie-based session class.
*
* @package Kohana
* @category Session
* @author Kohana Team
* @copyright (c) 2008-2010 Kohana Team
* @license http://kohanaframework.org/license
*/
class Kohana_Session_Cookie extends Session {
/**
* @param string $id session id
* @return string
*/
protected function _read($id = NULL)
{
return Cookie::get($this->_name, NULL);
}
/**
* @return null
*/
protected function _regenerate()
{
// Cookie sessions have no id
return NULL;
}
/**
* @return bool
*/
protected function _write()
{
return Cookie::set($this->_name, $this->__toString(), $this->_lifetime);
}
/**
* @return bool
*/
protected function _destroy()
{
return Cookie::delete($this->_name);
}
} // End Session_Cookie

View File

@@ -0,0 +1,93 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* Native PHP session class.
*
* @package Kohana
* @category Session
* @author Kohana Team
* @copyright (c) 2008-2010 Kohana Team
* @license http://kohanaframework.org/license
*/
class Kohana_Session_Native extends Session {
/**
* @return string
*/
public function id()
{
return session_id();
}
/**
* @param string $id session id
* @return null
*/
protected function _read($id = NULL)
{
// Sync up the session cookie with Cookie parameters
session_set_cookie_params($this->_lifetime, Cookie::$path, Cookie::$domain, Cookie::$secure, Cookie::$httponly);
// Do not allow PHP to send Cache-Control headers
session_cache_limiter(FALSE);
// Set the session cookie name
session_name($this->_name);
if ($id)
{
// Set the session id
session_id($id);
}
// Start the session
session_start();
// Use the $_SESSION global for storing data
$this->_data =& $_SESSION;
return NULL;
}
/**
* @return string
*/
protected function _regenerate()
{
// Regenerate the session id
session_regenerate_id();
return session_id();
}
/**
* @return bool
*/
protected function _write()
{
// Write and close the session
session_write_close();
return TRUE;
}
/**
* @return bool
*/
protected function _destroy()
{
// Destroy the current session
session_destroy();
// Did destruction work?
$status = ! session_id();
if ($status)
{
// Make sure the session cannot be restarted
Cookie::delete($this->_name);
}
return $status;
}
} // End Session_Native

View File

@@ -0,0 +1,590 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* Text helper class. Provides simple methods for working with text.
*
* @package Kohana
* @category Helpers
* @author Kohana Team
* @copyright (c) 2007-2010 Kohana Team
* @license http://kohanaframework.org/license
*/
class Kohana_Text {
/**
* @var array number units and text equivalents
*/
public static $units = array(
1000000000 => 'billion',
1000000 => 'million',
1000 => 'thousand',
100 => 'hundred',
90 => 'ninety',
80 => 'eighty',
70 => 'seventy',
60 => 'sixty',
50 => 'fifty',
40 => 'fourty',
30 => 'thirty',
20 => 'twenty',
19 => 'nineteen',
18 => 'eighteen',
17 => 'seventeen',
16 => 'sixteen',
15 => 'fifteen',
14 => 'fourteen',
13 => 'thirteen',
12 => 'twelve',
11 => 'eleven',
10 => 'ten',
9 => 'nine',
8 => 'eight',
7 => 'seven',
6 => 'six',
5 => 'five',
4 => 'four',
3 => 'three',
2 => 'two',
1 => 'one',
);
/**
* Limits a phrase to a given number of words.
*
* $text = Text::limit_words($text);
*
* @param string phrase to limit words of
* @param integer number of words to limit to
* @param string end character or entity
* @return string
*/
public static function limit_words($str, $limit = 100, $end_char = NULL)
{
$limit = (int) $limit;
$end_char = ($end_char === NULL) ? '…' : $end_char;
if (trim($str) === '')
return $str;
if ($limit <= 0)
return $end_char;
preg_match('/^\s*+(?:\S++\s*+){1,'.$limit.'}/u', $str, $matches);
// Only attach the end character if the matched string is shorter
// than the starting string.
return rtrim($matches[0]).((strlen($matches[0]) === strlen($str)) ? '' : $end_char);
}
/**
* Limits a phrase to a given number of characters.
*
* $text = Text::limit_chars($text);
*
* @param string phrase to limit characters of
* @param integer number of characters to limit to
* @param string end character or entity
* @param boolean enable or disable the preservation of words while limiting
* @return string
* @uses UTF8::strlen
*/
public static function limit_chars($str, $limit = 100, $end_char = NULL, $preserve_words = FALSE)
{
$end_char = ($end_char === NULL) ? '…' : $end_char;
$limit = (int) $limit;
if (trim($str) === '' OR UTF8::strlen($str) <= $limit)
return $str;
if ($limit <= 0)
return $end_char;
if ($preserve_words === FALSE)
return rtrim(UTF8::substr($str, 0, $limit)).$end_char;
// Don't preserve words. The limit is considered the top limit.
// No strings with a length longer than $limit should be returned.
if ( ! preg_match('/^.{0,'.$limit.'}\s/us', $str, $matches))
return $end_char;
return rtrim($matches[0]).((strlen($matches[0]) === strlen($str)) ? '' : $end_char);
}
/**
* Alternates between two or more strings.
*
* echo Text::alternate('one', 'two'); // "one"
* echo Text::alternate('one', 'two'); // "two"
* echo Text::alternate('one', 'two'); // "one"
*
* Note that using multiple iterations of different strings may produce
* unexpected results.
*
* @param string strings to alternate between
* @return string
*/
public static function alternate()
{
static $i;
if (func_num_args() === 0)
{
$i = 0;
return '';
}
$args = func_get_args();
return $args[($i++ % count($args))];
}
/**
* Generates a random string of a given type and length.
*
*
* $str = Text::random(); // 8 character random string
*
* The following types are supported:
*
* alnum
* : Upper and lower case a-z, 0-9 (default)
*
* alpha
* : Upper and lower case a-z
*
* hexdec
* : Hexadecimal characters a-f, 0-9
*
* distinct
* : Uppercase characters and numbers that cannot be confused
*
* You can also create a custom type by providing the "pool" of characters
* as the type.
*
* @param string a type of pool, or a string of characters to use as the pool
* @param integer length of string to return
* @return string
* @uses UTF8::split
*/
public static function random($type = NULL, $length = 8)
{
if ($type === NULL)
{
// Default is to generate an alphanumeric string
$type = 'alnum';
}
$utf8 = FALSE;
switch ($type)
{
case 'alnum':
$pool = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
break;
case 'alpha':
$pool = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
break;
case 'hexdec':
$pool = '0123456789abcdef';
break;
case 'numeric':
$pool = '0123456789';
break;
case 'nozero':
$pool = '123456789';
break;
case 'distinct':
$pool = '2345679ACDEFHJKLMNPRSTUVWXYZ';
break;
default:
$pool = (string) $type;
$utf8 = ! UTF8::is_ascii($pool);
break;
}
// Split the pool into an array of characters
$pool = ($utf8 === TRUE) ? UTF8::str_split($pool, 1) : str_split($pool, 1);
// Largest pool key
$max = count($pool) - 1;
$str = '';
for ($i = 0; $i < $length; $i++)
{
// Select a random character from the pool and add it to the string
$str .= $pool[mt_rand(0, $max)];
}
// Make sure alnum strings contain at least one letter and one digit
if ($type === 'alnum' AND $length > 1)
{
if (ctype_alpha($str))
{
// Add a random digit
$str[mt_rand(0, $length - 1)] = chr(mt_rand(48, 57));
}
elseif (ctype_digit($str))
{
// Add a random letter
$str[mt_rand(0, $length - 1)] = chr(mt_rand(65, 90));
}
}
return $str;
}
/**
* Reduces multiple slashes in a string to single slashes.
*
* $str = Text::reduce_slashes('foo//bar/baz'); // "foo/bar/baz"
*
* @param string string to reduce slashes of
* @return string
*/
public static function reduce_slashes($str)
{
return preg_replace('#(?<!:)//+#', '/', $str);
}
/**
* Replaces the given words with a string.
*
* // Displays "What the #####, man!"
* echo Text::censor('What the frick, man!', array(
* 'frick' => '#####',
* ));
*
* @param string phrase to replace words in
* @param array words to replace
* @param string replacement string
* @param boolean replace words across word boundries (space, period, etc)
* @return string
* @uses UTF8::strlen
*/
public static function censor($str, $badwords, $replacement = '#', $replace_partial_words = TRUE)
{
foreach ( (array) $badwords as $key => $badword)
{
$badwords[$key] = str_replace('\*', '\S*?', preg_quote( (string) $badword));
}
$regex = '('.implode('|', $badwords).')';
if ($replace_partial_words === FALSE)
{
// Just using \b isn't sufficient when we need to replace a badword that already contains word boundaries itself
$regex = '(?<=\b|\s|^)'.$regex.'(?=\b|\s|$)';
}
$regex = '!'.$regex.'!ui';
if (UTF8::strlen($replacement) == 1)
{
$regex .= 'e';
return preg_replace($regex, 'str_repeat($replacement, UTF8::strlen(\'$1\'))', $str);
}
return preg_replace($regex, $replacement, $str);
}
/**
* Finds the text that is similar between a set of words.
*
* $match = Text::similar(array('fred', 'fran', 'free'); // "fr"
*
* @param array words to find similar text of
* @return string
*/
public static function similar(array $words)
{
// First word is the word to match against
$word = current($words);
for ($i = 0, $max = strlen($word); $i < $max; ++$i)
{
foreach ($words as $w)
{
// Once a difference is found, break out of the loops
if ( ! isset($w[$i]) OR $w[$i] !== $word[$i])
break 2;
}
}
// Return the similar text
return substr($word, 0, $i);
}
/**
* Converts text email addresses and anchors into links. Existing links
* will not be altered.
*
* echo Text::auto_link($text);
*
* [!!] This method is not foolproof since it uses regex to parse HTML.
*
* @param string text to auto link
* @return string
* @uses Text::auto_link_urls
* @uses Text::auto_link_emails
*/
public static function auto_link($text)
{
// Auto link emails first to prevent problems with "www.domain.com@example.com"
return Text::auto_link_urls(Text::auto_link_emails($text));
}
/**
* Converts text anchors into links. Existing links will not be altered.
*
* echo Text::auto_link_urls($text);
*
* [!!] This method is not foolproof since it uses regex to parse HTML.
*
* @param string text to auto link
* @return string
* @uses HTML::anchor
*/
public static function auto_link_urls($text)
{
// Find and replace all http/https/ftp/ftps links that are not part of an existing html anchor
$text = preg_replace_callback('~\b(?<!href="|">)(?:ht|f)tps?://\S+(?:/|\b)~i', 'Text::_auto_link_urls_callback1', $text);
// Find and replace all naked www.links.com (without http://)
return preg_replace_callback('~\b(?<!://|">)www(?:\.[a-z0-9][-a-z0-9]*+)+\.[a-z]{2,6}\b~i', 'Text::_auto_link_urls_callback2', $text);
}
protected static function _auto_link_urls_callback1($matches)
{
return HTML::anchor($matches[0]);
}
protected static function _auto_link_urls_callback2($matches)
{
return HTML::anchor('http://'.$matches[0], $matches[0]);
}
/**
* Converts text email addresses into links. Existing links will not
* be altered.
*
* echo Text::auto_link_emails($text);
*
* [!!] This method is not foolproof since it uses regex to parse HTML.
*
* @param string text to auto link
* @return string
* @uses HTML::mailto
*/
public static function auto_link_emails($text)
{
// Find and replace all email addresses that are not part of an existing html mailto anchor
// Note: The "58;" negative lookbehind prevents matching of existing encoded html mailto anchors
// The html entity for a colon (:) is &#58; or &#058; or &#0058; etc.
return preg_replace_callback('~\b(?<!href="mailto:|58;)(?!\.)[-+_a-z0-9.]++(?<!\.)@(?![-.])[-a-z0-9.]+(?<!\.)\.[a-z]{2,6}\b(?!</a>)~i', 'Text::_auto_link_emails_callback', $text);
}
protected static function _auto_link_emails_callback($matches)
{
return HTML::mailto($matches[0]);
}
/**
* Automatically applies "p" and "br" markup to text.
* Basically [nl2br](http://php.net/nl2br) on steroids.
*
* echo Text::auto_p($text);
*
* [!!] This method is not foolproof since it uses regex to parse HTML.
*
* @param string subject
* @param boolean convert single linebreaks to <br />
* @return string
*/
public static function auto_p($str, $br = TRUE)
{
// Trim whitespace
if (($str = trim($str)) === '')
return '';
// Standardize newlines
$str = str_replace(array("\r\n", "\r"), "\n", $str);
// Trim whitespace on each line
$str = preg_replace('~^[ \t]+~m', '', $str);
$str = preg_replace('~[ \t]+$~m', '', $str);
// The following regexes only need to be executed if the string contains html
if ($html_found = (strpos($str, '<') !== FALSE))
{
// Elements that should not be surrounded by p tags
$no_p = '(?:p|div|h[1-6r]|ul|ol|li|blockquote|d[dlt]|pre|t[dhr]|t(?:able|body|foot|head)|c(?:aption|olgroup)|form|s(?:elect|tyle)|a(?:ddress|rea)|ma(?:p|th))';
// Put at least two linebreaks before and after $no_p elements
$str = preg_replace('~^<'.$no_p.'[^>]*+>~im', "\n$0", $str);
$str = preg_replace('~</'.$no_p.'\s*+>$~im', "$0\n", $str);
}
// Do the <p> magic!
$str = '<p>'.trim($str).'</p>';
$str = preg_replace('~\n{2,}~', "</p>\n\n<p>", $str);
// The following regexes only need to be executed if the string contains html
if ($html_found !== FALSE)
{
// Remove p tags around $no_p elements
$str = preg_replace('~<p>(?=</?'.$no_p.'[^>]*+>)~i', '', $str);
$str = preg_replace('~(</?'.$no_p.'[^>]*+>)</p>~i', '$1', $str);
}
// Convert single linebreaks to <br />
if ($br === TRUE)
{
$str = preg_replace('~(?<!\n)\n(?!\n)~', "<br />\n", $str);
}
return $str;
}
/**
* Returns human readable sizes. Based on original functions written by
* [Aidan Lister](http://aidanlister.com/repos/v/function.size_readable.php)
* and [Quentin Zervaas](http://www.phpriot.com/d/code/strings/filesize-format/).
*
* echo Text::bytes(filesize($file));
*
* @param integer size in bytes
* @param string a definitive unit
* @param string the return string format
* @param boolean whether to use SI prefixes or IEC
* @return string
*/
public static function bytes($bytes, $force_unit = NULL, $format = NULL, $si = TRUE)
{
// Format string
$format = ($format === NULL) ? '%01.2f %s' : (string) $format;
// IEC prefixes (binary)
if ($si == FALSE OR strpos($force_unit, 'i') !== FALSE)
{
$units = array('B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB');
$mod = 1024;
}
// SI prefixes (decimal)
else
{
$units = array('B', 'kB', 'MB', 'GB', 'TB', 'PB');
$mod = 1000;
}
// Determine unit to use
if (($power = array_search( (string) $force_unit, $units)) === FALSE)
{
$power = ($bytes > 0) ? floor(log($bytes, $mod)) : 0;
}
return sprintf($format, $bytes / pow($mod, $power), $units[$power]);
}
/**
* Format a number to human-readable text.
*
* // Display: one thousand and twenty-four
* echo Text::number(1024);
*
* // Display: five million, six hundred and thirty-two
* echo Text::number(5000632);
*
* @param integer number to format
* @return string
* @since 3.0.8
*/
public static function number($number)
{
// The number must always be an integer
$number = (int) $number;
// Uncompiled text version
$text = array();
// Last matched unit within the loop
$last_unit = NULL;
// The last matched item within the loop
$last_item = '';
foreach (Text::$units as $unit => $name)
{
if ($number / $unit >= 1)
{
// $value = the number of times the number is divisble by unit
$number -= $unit * ($value = (int) floor($number / $unit));
// Temporary var for textifying the current unit
$item = '';
if ($unit < 100)
{
if ($last_unit < 100 AND $last_unit >= 20)
{
$last_item .= '-'.$name;
}
else
{
$item = $name;
}
}
else
{
$item = Text::number($value).' '.$name;
}
// In the situation that we need to make a composite number (i.e. twenty-three)
// then we need to modify the previous entry
if (empty($item))
{
array_pop($text);
$item = $last_item;
}
$last_item = $text[] = $item;
$last_unit = $unit;
}
}
if (count($text) > 1)
{
$and = array_pop($text);
}
$text = implode(', ', $text);
if (isset($and))
{
$text .= ' and '.$and;
}
return $text;
}
/**
* Prevents [widow words](http://www.shauninman.com/archive/2006/08/22/widont_wordpress_plugin)
* by inserting a non-breaking space between the last two words.
*
* echo Text::widont($text);
*
* @param string text to remove widows from
* @return string
*/
public static function widont($str)
{
$str = rtrim($str);
$space = strrpos($str, ' ');
if ($space !== FALSE)
{
$str = substr($str, 0, $space).'&nbsp;'.substr($str, $space + 1);
}
return $str;
}
} // End text

View File

@@ -0,0 +1,213 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* Upload helper class for working with uploaded files and [Validate].
*
* $array = Validate::factory($_FILES);
*
* [!!] Remember to define your form with "enctype=multipart/form-data" or file
* uploading will not work!
*
* The following configuration properties can be set:
*
* - [Upload::$remove_spaces]
* - [Upload::$default_directory]
*
* @package Kohana
* @category Helpers
* @author Kohana Team
* @copyright (c) 2007-2010 Kohana Team
* @license http://kohanaframework.org/license
*/
class Kohana_Upload {
/**
* @var boolean remove spaces in uploaded files
*/
public static $remove_spaces = TRUE;
/**
* @var string default upload directory
*/
public static $default_directory = 'upload';
/**
* Save an uploaded file to a new location. If no filename is provided,
* the original filename will be used, with a unique prefix added.
*
* This method should be used after validating the $_FILES array:
*
* if ($array->check())
* {
* // Upload is valid, save it
* Upload::save($_FILES['file']);
* }
*
* @param array uploaded file data
* @param string new filename
* @param string new directory
* @param integer chmod mask
* @return string on success, full path to new file
* @return FALSE on failure
*/
public static function save(array $file, $filename = NULL, $directory = NULL, $chmod = 0644)
{
if ( ! isset($file['tmp_name']) OR ! is_uploaded_file($file['tmp_name']))
{
// Ignore corrupted uploads
return FALSE;
}
if ($filename === NULL)
{
// Use the default filename, with a timestamp pre-pended
$filename = uniqid().$file['name'];
}
if (Upload::$remove_spaces === TRUE)
{
// Remove spaces from the filename
$filename = preg_replace('/\s+/', '_', $filename);
}
if ($directory === NULL)
{
// Use the pre-configured upload directory
$directory = Upload::$default_directory;
}
if ( ! is_dir($directory) OR ! is_writable(realpath($directory)))
{
throw new Kohana_Exception('Directory :dir must be writable',
array(':dir' => Kohana::debug_path($directory)));
}
// Make the filename into a complete path
$filename = realpath($directory).DIRECTORY_SEPARATOR.$filename;
if (move_uploaded_file($file['tmp_name'], $filename))
{
if ($chmod !== FALSE)
{
// Set permissions on filename
chmod($filename, $chmod);
}
// Return new file path
return $filename;
}
return FALSE;
}
/**
* Tests if upload data is valid, even if no file was uploaded. If you
* _do_ require a file to be uploaded, add the [Upload::not_empty] rule
* before this rule.
*
* $array->rule('file', 'Upload::valid')
*
* @param array $_FILES item
* @return bool
*/
public static function valid($file)
{
return (isset($file['error'])
AND isset($file['name'])
AND isset($file['type'])
AND isset($file['tmp_name'])
AND isset($file['size']));
}
/**
* Tests if a successful upload has been made.
*
* $array->rule('file', 'Upload::not_empty');
*
* @param array $_FILES item
* @return bool
*/
public static function not_empty(array $file)
{
return (isset($file['error'])
AND isset($file['tmp_name'])
AND $file['error'] === UPLOAD_ERR_OK
AND is_uploaded_file($file['tmp_name'])
);
}
/**
* Test if an uploaded file is an allowed file type, by extension.
*
* $array->rule('file', 'Upload::type', array(array('jpg', 'png', 'gif')));
*
* @param array $_FILES item
* @param array allowed file extensions
* @return bool
*/
public static function type(array $file, array $allowed)
{
if ($file['error'] !== UPLOAD_ERR_OK)
return TRUE;
$ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
return in_array($ext, $allowed);
}
/**
* Validation rule to test if an uploaded file is allowed by file size.
* File sizes are defined as: SB, where S is the size (1, 15, 300, etc) and
* B is the byte modifier: (B)ytes, (K)ilobytes, (M)egabytes, (G)igabytes.
*
* $array->rule('file', 'Upload::size', array('1M'))
*
* @param array $_FILES item
* @param string maximum file size
* @return bool
*/
public static function size(array $file, $size)
{
if ($file['error'] === UPLOAD_ERR_INI_SIZE)
{
// Upload is larger than PHP allowed size
return FALSE;
}
if ($file['error'] !== UPLOAD_ERR_OK)
{
// The upload failed, no size to check
return TRUE;
}
// Only one size is allowed
$size = strtoupper(trim($size));
if ( ! preg_match('/^[0-9]++[BKMG]$/', $size))
{
throw new Kohana_Exception('Size does not contain a digit and a byte value: :size', array(
':size' => $size,
));
}
// Make the size into a power of 1024
switch (substr($size, -1))
{
case 'G':
$size = intval($size) * pow(1024, 3);
break;
case 'M':
$size = intval($size) * pow(1024, 2);
break;
case 'K':
$size = intval($size) * pow(1024, 1);
break;
default:
$size = intval($size);
break;
}
// Test that the file is under or equal to the max size
return ($file['size'] <= $size);
}
} // End upload

View File

@@ -0,0 +1,186 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* URL helper class.
*
* @package Kohana
* @category Helpers
* @author Kohana Team
* @copyright (c) 2007-2010 Kohana Team
* @license http://kohanaframework.org/license
*/
class Kohana_URL {
/**
* Gets the base URL to the application. To include the current protocol,
* use TRUE. To specify a protocol, provide the protocol as a string.
* If a protocol is used, a complete URL will be generated using the
* `$_SERVER['HTTP_HOST']` variable.
*
* // Absolute relative, no host or protocol
* echo URL::base();
*
* // Complete relative, with host and protocol
* echo URL::base(TRUE, TRUE);
*
* // Complete relative, with host and "https" protocol
* echo URL::base(TRUE, 'https');
*
* @param boolean add index file to URL?
* @param mixed protocol string or boolean, add protocol and domain?
* @return string
* @uses Kohana::$index_file
* @uses Request::$protocol
*/
public static function base($index = FALSE, $protocol = FALSE)
{
// Start with the configured base URL
$base_url = Kohana::$base_url;
if ($protocol === TRUE)
{
// Use the current protocol
$protocol = Request::$protocol;
}
elseif ($protocol === FALSE AND $scheme = parse_url($base_url, PHP_URL_SCHEME))
{
// Use the configured default protocol
$protocol = $scheme;
}
if ($index === TRUE AND ! empty(Kohana::$index_file))
{
// Add the index file to the URL
$base_url .= Kohana::$index_file.'/';
}
if (is_string($protocol))
{
if ($port = parse_url($base_url, PHP_URL_PORT))
{
// Found a port, make it usable for the URL
$port = ':'.$port;
}
if ($domain = parse_url($base_url, PHP_URL_HOST))
{
// Remove everything but the path from the URL
$base_url = parse_url($base_url, PHP_URL_PATH);
}
else
{
// Attempt to use HTTP_HOST and fallback to SERVER_NAME
$domain = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : $_SERVER['SERVER_NAME'];
}
// Add the protocol and domain to the base URL
$base_url = $protocol.'://'.$domain.$port.$base_url;
}
return $base_url;
}
/**
* Fetches an absolute site URL based on a URI segment.
*
* echo URL::site('foo/bar');
*
* @param string site URI to convert
* @param mixed protocol string or boolean, add protocol and domain?
* @return string
* @uses URL::base
*/
public static function site($uri = '', $protocol = FALSE)
{
// Chop off possible scheme, host, port, user and pass parts
$path = preg_replace('~^[-a-z0-9+.]++://[^/]++/?~', '', trim($uri, '/'));
if ( ! UTF8::is_ascii($path))
{
// Encode all non-ASCII characters, as per RFC 1738
$path = preg_replace('~([^/]+)~e', 'rawurlencode("$1")', $path);
}
// Concat the URL
return URL::base(TRUE, $protocol).$path;
}
/**
* Merges the current GET parameters with an array of new or overloaded
* parameters and returns the resulting query string.
*
* // Returns "?sort=title&limit=10" combined with any existing GET values
* $query = URL::query(array('sort' => 'title', 'limit' => 10));
*
* Typically you would use this when you are sorting query results,
* or something similar.
*
* [!!] Parameters with a NULL value are left out.
*
* @param array array of GET parameters
* @param boolean include current request GET parameters
* @return string
*/
public static function query(array $params = NULL, $use_get = TRUE)
{
if ($use_get)
{
if ($params === NULL)
{
// Use only the current parameters
$params = $_GET;
}
else
{
// Merge the current and new parameters
$params = array_merge($_GET, $params);
}
}
if (empty($params))
{
// No query parameters
return '';
}
// Note: http_build_query returns an empty string for a params array with only NULL values
$query = http_build_query($params, '', '&');
// Don't prepend '?' to an empty string
return ($query === '') ? '' : ('?'.$query);
}
/**
* Convert a phrase to a URL-safe title.
*
* echo URL::title('My Blog Post'); // "my-blog-post"
*
* @param string phrase to convert
* @param string word separator (any single character)
* @param boolean transliterate to ASCII?
* @return string
* @uses UTF8::transliterate_to_ascii
*/
public static function title($title, $separator = '-', $ascii_only = FALSE)
{
if ($ascii_only === TRUE)
{
// Transliterate non-ASCII characters
$title = UTF8::transliterate_to_ascii($title);
// Remove all characters that are not the separator, a-z, 0-9, or whitespace
$title = preg_replace('![^'.preg_quote($separator).'a-z0-9\s]+!', '', strtolower($title));
}
else
{
// Remove all characters that are not the separator, letters, numbers, or whitespace
$title = preg_replace('![^'.preg_quote($separator).'\pL\pN\s]+!u', '', UTF8::strtolower($title));
}
// Replace all separator characters and whitespace by a single separator
$title = preg_replace('!['.preg_quote($separator).'\s]+!u', $separator, $title);
// Trim separators from the beginning and end
return trim($title, $separator);
}
} // End url

View File

@@ -0,0 +1,767 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* A port of [phputf8](http://phputf8.sourceforge.net/) to a unified set
* of files. Provides multi-byte aware replacement string functions.
*
* For UTF-8 support to work correctly, the following requirements must be met:
*
* - PCRE needs to be compiled with UTF-8 support (--enable-utf8)
* - Support for [Unicode properties](http://php.net/manual/reference.pcre.pattern.modifiers.php)
* is highly recommended (--enable-unicode-properties)
* - UTF-8 conversion will be much more reliable if the
* [iconv extension](http://php.net/iconv) is loaded
* - The [mbstring extension](http://php.net/mbstring) is highly recommended,
* but must not be overloading string functions
*
* [!!] This file is licensed differently from the rest of Kohana. As a port of
* [phputf8](http://phputf8.sourceforge.net/), this file is released under the LGPL.
*
* @package Kohana
* @category Base
* @author Kohana Team
* @copyright (c) 2007-2010 Kohana Team
* @copyright (c) 2005 Harry Fuecks
* @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt
*/
class Kohana_UTF8 {
/**
* @var boolean Does the server support UTF-8 natively?
*/
public static $server_utf8 = NULL;
/**
* @var array List of called methods that have had their required file included.
*/
public static $called = array();
/**
* Recursively cleans arrays, objects, and strings. Removes ASCII control
* codes and converts to the requested charset while silently discarding
* incompatible characters.
*
* UTF8::clean($_GET); // Clean GET data
*
* [!!] This method requires [Iconv](http://php.net/iconv)
*
* @param mixed variable to clean
* @param string character set, defaults to Kohana::$charset
* @return mixed
* @uses UTF8::strip_ascii_ctrl
* @uses UTF8::is_ascii
*/
public static function clean($var, $charset = NULL)
{
if ( ! $charset)
{
// Use the application character set
$charset = Kohana::$charset;
}
if (is_array($var) OR is_object($var))
{
foreach ($var as $key => $val)
{
// Recursion!
$var[self::clean($key)] = self::clean($val);
}
}
elseif (is_string($var) AND $var !== '')
{
// Remove control characters
$var = self::strip_ascii_ctrl($var);
if ( ! self::is_ascii($var))
{
// Disable notices
$error_reporting = error_reporting(~E_NOTICE);
// iconv is expensive, so it is only used when needed
$var = iconv($charset, $charset.'//IGNORE', $var);
// Turn notices back on
error_reporting($error_reporting);
}
}
return $var;
}
/**
* Tests whether a string contains only 7-bit ASCII bytes. This is used to
* determine when to use native functions or UTF-8 functions.
*
* $ascii = UTF8::is_ascii($str);
*
* @param mixed string or array of strings to check
* @return boolean
*/
public static function is_ascii($str)
{
if (is_array($str))
{
$str = implode($str);
}
return ! preg_match('/[^\x00-\x7F]/S', $str);
}
/**
* Strips out device control codes in the ASCII range.
*
* $str = UTF8::strip_ascii_ctrl($str);
*
* @param string string to clean
* @return string
*/
public static function strip_ascii_ctrl($str)
{
return preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]+/S', '', $str);
}
/**
* Strips out all non-7bit ASCII bytes.
*
* $str = UTF8::strip_non_ascii($str);
*
* @param string string to clean
* @return string
*/
public static function strip_non_ascii($str)
{
return preg_replace('/[^\x00-\x7F]+/S', '', $str);
}
/**
* Replaces special/accented UTF-8 characters by ASCII-7 "equivalents".
*
* $ascii = UTF8::transliterate_to_ascii($utf8);
*
* @author Andreas Gohr <andi@splitbrain.org>
* @param string string to transliterate
* @param integer -1 lowercase only, +1 uppercase only, 0 both cases
* @return string
*/
public static function transliterate_to_ascii($str, $case = 0)
{
if ( ! isset(self::$called[__FUNCTION__]))
{
require SYSPATH.'utf8'.DIRECTORY_SEPARATOR.__FUNCTION__.EXT;
// Function has been called
self::$called[__FUNCTION__] = TRUE;
}
return _transliterate_to_ascii($str, $case);
}
/**
* Returns the length of the given string. This is a UTF8-aware version
* of [strlen](http://php.net/strlen).
*
* $length = UTF8::strlen($str);
*
* @param string string being measured for length
* @return integer
* @uses UTF8::$server_utf8
*/
public static function strlen($str)
{
if (UTF8::$server_utf8)
return mb_strlen($str, Kohana::$charset);
if ( ! isset(self::$called[__FUNCTION__]))
{
require SYSPATH.'utf8'.DIRECTORY_SEPARATOR.__FUNCTION__.EXT;
// Function has been called
self::$called[__FUNCTION__] = TRUE;
}
return _strlen($str);
}
/**
* Finds position of first occurrence of a UTF-8 string. This is a
* UTF8-aware version of [strpos](http://php.net/strpos).
*
* $position = UTF8::strpos($str, $search);
*
* @author Harry Fuecks <hfuecks@gmail.com>
* @param string haystack
* @param string needle
* @param integer offset from which character in haystack to start searching
* @return integer position of needle
* @return boolean FALSE if the needle is not found
* @uses UTF8::$server_utf8
*/
public static function strpos($str, $search, $offset = 0)
{
if (UTF8::$server_utf8)
return mb_strpos($str, $search, $offset, Kohana::$charset);
if ( ! isset(self::$called[__FUNCTION__]))
{
require SYSPATH.'utf8'.DIRECTORY_SEPARATOR.__FUNCTION__.EXT;
// Function has been called
self::$called[__FUNCTION__] = TRUE;
}
return _strpos($str, $search, $offset);
}
/**
* Finds position of last occurrence of a char in a UTF-8 string. This is
* a UTF8-aware version of [strrpos](http://php.net/strrpos).
*
* $position = UTF8::strrpos($str, $search);
*
* @author Harry Fuecks <hfuecks@gmail.com>
* @param string haystack
* @param string needle
* @param integer offset from which character in haystack to start searching
* @return integer position of needle
* @return boolean FALSE if the needle is not found
* @uses UTF8::$server_utf8
*/
public static function strrpos($str, $search, $offset = 0)
{
if (UTF8::$server_utf8)
return mb_strrpos($str, $search, $offset, Kohana::$charset);
if ( ! isset(self::$called[__FUNCTION__]))
{
require SYSPATH.'utf8'.DIRECTORY_SEPARATOR.__FUNCTION__.EXT;
// Function has been called
self::$called[__FUNCTION__] = TRUE;
}
return _strrpos($str, $search, $offset);
}
/**
* Returns part of a UTF-8 string. This is a UTF8-aware version
* of [substr](http://php.net/substr).
*
* $sub = UTF8::substr($str, $offset);
*
* @author Chris Smith <chris@jalakai.co.uk>
* @param string input string
* @param integer offset
* @param integer length limit
* @return string
* @uses UTF8::$server_utf8
* @uses Kohana::$charset
*/
public static function substr($str, $offset, $length = NULL)
{
if (UTF8::$server_utf8)
return ($length === NULL)
? mb_substr($str, $offset, mb_strlen($str), Kohana::$charset)
: mb_substr($str, $offset, $length, Kohana::$charset);
if ( ! isset(self::$called[__FUNCTION__]))
{
require SYSPATH.'utf8'.DIRECTORY_SEPARATOR.__FUNCTION__.EXT;
// Function has been called
self::$called[__FUNCTION__] = TRUE;
}
return _substr($str, $offset, $length);
}
/**
* Replaces text within a portion of a UTF-8 string. This is a UTF8-aware
* version of [substr_replace](http://php.net/substr_replace).
*
* $str = UTF8::substr_replace($str, $replacement, $offset);
*
* @author Harry Fuecks <hfuecks@gmail.com>
* @param string input string
* @param string replacement string
* @param integer offset
* @return string
*/
public static function substr_replace($str, $replacement, $offset, $length = NULL)
{
if ( ! isset(self::$called[__FUNCTION__]))
{
require SYSPATH.'utf8'.DIRECTORY_SEPARATOR.__FUNCTION__.EXT;
// Function has been called
self::$called[__FUNCTION__] = TRUE;
}
return _substr_replace($str, $replacement, $offset, $length);
}
/**
* Makes a UTF-8 string lowercase. This is a UTF8-aware version
* of [strtolower](http://php.net/strtolower).
*
* $str = UTF8::strtolower($str);
*
* @author Andreas Gohr <andi@splitbrain.org>
* @param string mixed case string
* @return string
* @uses UTF8::$server_utf8
*/
public static function strtolower($str)
{
if (UTF8::$server_utf8)
return mb_strtolower($str, Kohana::$charset);
if ( ! isset(self::$called[__FUNCTION__]))
{
require SYSPATH.'utf8'.DIRECTORY_SEPARATOR.__FUNCTION__.EXT;
// Function has been called
self::$called[__FUNCTION__] = TRUE;
}
return _strtolower($str);
}
/**
* Makes a UTF-8 string uppercase. This is a UTF8-aware version
* of [strtoupper](http://php.net/strtoupper).
*
* @author Andreas Gohr <andi@splitbrain.org>
* @param string mixed case string
* @return string
* @uses UTF8::$server_utf8
* @uses Kohana::$charset
*/
public static function strtoupper($str)
{
if (UTF8::$server_utf8)
return mb_strtoupper($str, Kohana::$charset);
if ( ! isset(self::$called[__FUNCTION__]))
{
require SYSPATH.'utf8'.DIRECTORY_SEPARATOR.__FUNCTION__.EXT;
// Function has been called
self::$called[__FUNCTION__] = TRUE;
}
return _strtoupper($str);
}
/**
* Makes a UTF-8 string's first character uppercase. This is a UTF8-aware
* version of [ucfirst](http://php.net/ucfirst).
*
* $str = UTF8::ucfirst($str);
*
* @author Harry Fuecks <hfuecks@gmail.com>
* @param string mixed case string
* @return string
*/
public static function ucfirst($str)
{
if ( ! isset(self::$called[__FUNCTION__]))
{
require SYSPATH.'utf8'.DIRECTORY_SEPARATOR.__FUNCTION__.EXT;
// Function has been called
self::$called[__FUNCTION__] = TRUE;
}
return _ucfirst($str);
}
/**
* Makes the first character of every word in a UTF-8 string uppercase.
* This is a UTF8-aware version of [ucwords](http://php.net/ucwords).
*
* $str = UTF8::ucwords($str);
*
* @author Harry Fuecks <hfuecks@gmail.com>
* @param string mixed case string
* @return string
* @uses UTF8::$server_utf8
*/
public static function ucwords($str)
{
if ( ! isset(self::$called[__FUNCTION__]))
{
require SYSPATH.'utf8'.DIRECTORY_SEPARATOR.__FUNCTION__.EXT;
// Function has been called
self::$called[__FUNCTION__] = TRUE;
}
return _ucwords($str);
}
/**
* Case-insensitive UTF-8 string comparison. This is a UTF8-aware version
* of [strcasecmp](http://php.net/strcasecmp).
*
* $compare = UTF8::strcasecmp($str1, $str2);
*
* @author Harry Fuecks <hfuecks@gmail.com>
* @param string string to compare
* @param string string to compare
* @return integer less than 0 if str1 is less than str2
* @return integer greater than 0 if str1 is greater than str2
* @return integer 0 if they are equal
*/
public static function strcasecmp($str1, $str2)
{
if ( ! isset(self::$called[__FUNCTION__]))
{
require SYSPATH.'utf8'.DIRECTORY_SEPARATOR.__FUNCTION__.EXT;
// Function has been called
self::$called[__FUNCTION__] = TRUE;
}
return _strcasecmp($str1, $str2);
}
/**
* Returns a string or an array with all occurrences of search in subject
* (ignoring case) and replaced with the given replace value. This is a
* UTF8-aware version of [str_ireplace](http://php.net/str_ireplace).
*
* [!!] This function is very slow compared to the native version. Avoid
* using it when possible.
*
* @author Harry Fuecks <hfuecks@gmail.com
* @param string|array text to replace
* @param string|array replacement text
* @param string|array subject text
* @param integer number of matched and replaced needles will be returned via this parameter which is passed by reference
* @return string if the input was a string
* @return array if the input was an array
*/
public static function str_ireplace($search, $replace, $str, & $count = NULL)
{
if ( ! isset(self::$called[__FUNCTION__]))
{
require SYSPATH.'utf8'.DIRECTORY_SEPARATOR.__FUNCTION__.EXT;
// Function has been called
self::$called[__FUNCTION__] = TRUE;
}
return _str_ireplace($search, $replace, $str, $count);
}
/**
* Case-insenstive UTF-8 version of strstr. Returns all of input string
* from the first occurrence of needle to the end. This is a UTF8-aware
* version of [stristr](http://php.net/stristr).
*
* $found = UTF8::stristr($str, $search);
*
* @author Harry Fuecks <hfuecks@gmail.com>
* @param string input string
* @param string needle
* @return string matched substring if found
* @return FALSE if the substring was not found
*/
public static function stristr($str, $search)
{
if ( ! isset(self::$called[__FUNCTION__]))
{
require SYSPATH.'utf8'.DIRECTORY_SEPARATOR.__FUNCTION__.EXT;
// Function has been called
self::$called[__FUNCTION__] = TRUE;
}
return _stristr($str, $search);
}
/**
* Finds the length of the initial segment matching mask. This is a
* UTF8-aware version of [strspn](http://php.net/strspn).
*
* $found = UTF8::strspn($str, $mask);
*
* @author Harry Fuecks <hfuecks@gmail.com>
* @param string input string
* @param string mask for search
* @param integer start position of the string to examine
* @param integer length of the string to examine
* @return integer length of the initial segment that contains characters in the mask
*/
public static function strspn($str, $mask, $offset = NULL, $length = NULL)
{
if ( ! isset(self::$called[__FUNCTION__]))
{
require SYSPATH.'utf8'.DIRECTORY_SEPARATOR.__FUNCTION__.EXT;
// Function has been called
self::$called[__FUNCTION__] = TRUE;
}
return _strspn($str, $mask, $offset, $length);
}
/**
* Finds the length of the initial segment not matching mask. This is a
* UTF8-aware version of [strcspn](http://php.net/strcspn).
*
* $found = UTF8::strcspn($str, $mask);
*
* @author Harry Fuecks <hfuecks@gmail.com>
* @param string input string
* @param string mask for search
* @param integer start position of the string to examine
* @param integer length of the string to examine
* @return integer length of the initial segment that contains characters not in the mask
*/
public static function strcspn($str, $mask, $offset = NULL, $length = NULL)
{
if ( ! isset(self::$called[__FUNCTION__]))
{
require SYSPATH.'utf8'.DIRECTORY_SEPARATOR.__FUNCTION__.EXT;
// Function has been called
self::$called[__FUNCTION__] = TRUE;
}
return _strcspn($str, $mask, $offset, $length);
}
/**
* Pads a UTF-8 string to a certain length with another string. This is a
* UTF8-aware version of [str_pad](http://php.net/str_pad).
*
* $str = UTF8::str_pad($str, $length);
*
* @author Harry Fuecks <hfuecks@gmail.com>
* @param string input string
* @param integer desired string length after padding
* @param string string to use as padding
* @param string padding type: STR_PAD_RIGHT, STR_PAD_LEFT, or STR_PAD_BOTH
* @return string
*/
public static function str_pad($str, $final_str_length, $pad_str = ' ', $pad_type = STR_PAD_RIGHT)
{
if ( ! isset(self::$called[__FUNCTION__]))
{
require SYSPATH.'utf8'.DIRECTORY_SEPARATOR.__FUNCTION__.EXT;
// Function has been called
self::$called[__FUNCTION__] = TRUE;
}
return _str_pad($str, $final_str_length, $pad_str, $pad_type);
}
/**
* Converts a UTF-8 string to an array. This is a UTF8-aware version of
* [str_split](http://php.net/str_split).
*
* $array = UTF8::str_split($str);
*
* @author Harry Fuecks <hfuecks@gmail.com>
* @param string input string
* @param integer maximum length of each chunk
* @return array
*/
public static function str_split($str, $split_length = 1)
{
if ( ! isset(self::$called[__FUNCTION__]))
{
require SYSPATH.'utf8'.DIRECTORY_SEPARATOR.__FUNCTION__.EXT;
// Function has been called
self::$called[__FUNCTION__] = TRUE;
}
return _str_split($str, $split_length);
}
/**
* Reverses a UTF-8 string. This is a UTF8-aware version of [strrev](http://php.net/strrev).
*
* $str = UTF8::strrev($str);
*
* @author Harry Fuecks <hfuecks@gmail.com>
* @param string string to be reversed
* @return string
*/
public static function strrev($str)
{
if ( ! isset(self::$called[__FUNCTION__]))
{
require SYSPATH.'utf8'.DIRECTORY_SEPARATOR.__FUNCTION__.EXT;
// Function has been called
self::$called[__FUNCTION__] = TRUE;
}
return _strrev($str);
}
/**
* Strips whitespace (or other UTF-8 characters) from the beginning and
* end of a string. This is a UTF8-aware version of [trim](http://php.net/trim).
*
* $str = UTF8::trim($str);
*
* @author Andreas Gohr <andi@splitbrain.org>
* @param string input string
* @param string string of characters to remove
* @return string
*/
public static function trim($str, $charlist = NULL)
{
if ( ! isset(self::$called[__FUNCTION__]))
{
require SYSPATH.'utf8'.DIRECTORY_SEPARATOR.__FUNCTION__.EXT;
// Function has been called
self::$called[__FUNCTION__] = TRUE;
}
return _trim($str, $charlist);
}
/**
* Strips whitespace (or other UTF-8 characters) from the beginning of
* a string. This is a UTF8-aware version of [ltrim](http://php.net/ltrim).
*
* $str = UTF8::ltrim($str);
*
* @author Andreas Gohr <andi@splitbrain.org>
* @param string input string
* @param string string of characters to remove
* @return string
*/
public static function ltrim($str, $charlist = NULL)
{
if ( ! isset(self::$called[__FUNCTION__]))
{
require SYSPATH.'utf8'.DIRECTORY_SEPARATOR.__FUNCTION__.EXT;
// Function has been called
self::$called[__FUNCTION__] = TRUE;
}
return _ltrim($str, $charlist);
}
/**
* Strips whitespace (or other UTF-8 characters) from the end of a string.
* This is a UTF8-aware version of [rtrim](http://php.net/rtrim).
*
* $str = UTF8::rtrim($str);
*
* @author Andreas Gohr <andi@splitbrain.org>
* @param string input string
* @param string string of characters to remove
* @return string
*/
public static function rtrim($str, $charlist = NULL)
{
if ( ! isset(self::$called[__FUNCTION__]))
{
require SYSPATH.'utf8'.DIRECTORY_SEPARATOR.__FUNCTION__.EXT;
// Function has been called
self::$called[__FUNCTION__] = TRUE;
}
return _rtrim($str, $charlist);
}
/**
* Returns the unicode ordinal for a character. This is a UTF8-aware
* version of [ord](http://php.net/ord).
*
* $digit = UTF8::ord($character);
*
* @author Harry Fuecks <hfuecks@gmail.com>
* @param string UTF-8 encoded character
* @return integer
*/
public static function ord($chr)
{
if ( ! isset(self::$called[__FUNCTION__]))
{
require SYSPATH.'utf8'.DIRECTORY_SEPARATOR.__FUNCTION__.EXT;
// Function has been called
self::$called[__FUNCTION__] = TRUE;
}
return _ord($chr);
}
/**
* Takes an UTF-8 string and returns an array of ints representing the Unicode characters.
* Astral planes are supported i.e. the ints in the output can be > 0xFFFF.
* Occurrences of the BOM are ignored. Surrogates are not allowed.
*
* $array = UTF8::to_unicode($str);
*
* The Original Code is Mozilla Communicator client code.
* The Initial Developer of the Original Code is Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1998 the Initial Developer.
* Ported to PHP by Henri Sivonen <hsivonen@iki.fi>, see <http://hsivonen.iki.fi/php-utf8/>
* Slight modifications to fit with phputf8 library by Harry Fuecks <hfuecks@gmail.com>
*
* @param string UTF-8 encoded string
* @return array unicode code points
* @return FALSE if the string is invalid
*/
public static function to_unicode($str)
{
if ( ! isset(self::$called[__FUNCTION__]))
{
require SYSPATH.'utf8'.DIRECTORY_SEPARATOR.__FUNCTION__.EXT;
// Function has been called
self::$called[__FUNCTION__] = TRUE;
}
return _to_unicode($str);
}
/**
* Takes an array of ints representing the Unicode characters and returns a UTF-8 string.
* Astral planes are supported i.e. the ints in the input can be > 0xFFFF.
* Occurrances of the BOM are ignored. Surrogates are not allowed.
*
* $str = UTF8::to_unicode($array);
*
* The Original Code is Mozilla Communicator client code.
* The Initial Developer of the Original Code is Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1998 the Initial Developer.
* Ported to PHP by Henri Sivonen <hsivonen@iki.fi>, see http://hsivonen.iki.fi/php-utf8/
* Slight modifications to fit with phputf8 library by Harry Fuecks <hfuecks@gmail.com>.
*
* @param array unicode code points representing a string
* @return string utf8 string of characters
* @return boolean FALSE if a code point cannot be found
*/
public static function from_unicode($arr)
{
if ( ! isset(self::$called[__FUNCTION__]))
{
require SYSPATH.'utf8'.DIRECTORY_SEPARATOR.__FUNCTION__.EXT;
// Function has been called
self::$called[__FUNCTION__] = TRUE;
}
return _from_unicode($arr);
}
} // End UTF8
if (Kohana_UTF8::$server_utf8 === NULL)
{
// Determine if this server supports UTF-8 natively
Kohana_UTF8::$server_utf8 = extension_loaded('mbstring');
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,29 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* @package Kohana
* @category Exceptions
* @author Kohana Team
* @copyright (c) 2009-2010 Kohana Team
* @license http://kohanaframework.org/license
*/
class Kohana_Validate_Exception extends Kohana_Exception {
/**
* @var object Validate instance
*/
public $array;
/**
* @param Validate Validate object
* @param string error message
* @param array translation variables
* @param int the exception code
*/
public function __construct(Validate $array, $message = 'Failed to validate array', array $values = NULL, $code = 0)
{
$this->array = $array;
parent::__construct($message, $values, $code);
}
} // End Kohana_Validate_Exception

View File

@@ -0,0 +1,346 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* Acts as an object wrapper for HTML pages with embedded PHP, called "views".
* Variables can be assigned with the view object and referenced locally within
* the view.
*
* @package Kohana
* @category Base
* @author Kohana Team
* @copyright (c) 2008-2010 Kohana Team
* @license http://kohanaframework.org/license
*/
class Kohana_View {
// Array of global variables
protected static $_global_data = array();
/**
* Returns a new View object. If you do not define the "file" parameter,
* you must call [View::set_filename].
*
* $view = View::factory($file);
*
* @param string view filename
* @param array array of values
* @return View
*/
public static function factory($file = NULL, array $data = NULL)
{
return new View($file, $data);
}
/**
* Captures the output that is generated when a view is included.
* The view data will be extracted to make local variables. This method
* is static to prevent object scope resolution.
*
* $output = View::capture($file, $data);
*
* @param string filename
* @param array variables
* @return string
*/
protected static function capture($kohana_view_filename, array $kohana_view_data)
{
// Import the view variables to local namespace
extract($kohana_view_data, EXTR_SKIP);
if (View::$_global_data)
{
// Import the global view variables to local namespace and maintain references
extract(View::$_global_data, EXTR_REFS);
}
// Capture the view output
ob_start();
try
{
// Load the view within the current scope
include $kohana_view_filename;
}
catch (Exception $e)
{
// Delete the output buffer
ob_end_clean();
// Re-throw the exception
throw $e;
}
// Get the captured output and close the buffer
return ob_get_clean();
}
/**
* Sets a global variable, similar to [View::set], except that the
* variable will be accessible to all views.
*
* View::set_global($name, $value);
*
* @param string variable name or an array of variables
* @param mixed value
* @return void
*/
public static function set_global($key, $value = NULL)
{
if (is_array($key))
{
foreach ($key as $key2 => $value)
{
View::$_global_data[$key2] = $value;
}
}
else
{
View::$_global_data[$key] = $value;
}
}
/**
* Assigns a global variable by reference, similar to [View::bind], except
* that the variable will be accessible to all views.
*
* View::bind_global($key, $value);
*
* @param string variable name
* @param mixed referenced variable
* @return void
*/
public static function bind_global($key, & $value)
{
View::$_global_data[$key] =& $value;
}
// View filename
protected $_file;
// Array of local variables
protected $_data = array();
/**
* Sets the initial view filename and local data. Views should almost
* always only be created using [View::factory].
*
* $view = new View($file);
*
* @param string view filename
* @param array array of values
* @return void
* @uses View::set_filename
*/
public function __construct($file = NULL, array $data = NULL)
{
if ($file !== NULL)
{
$this->set_filename($file);
}
if ($data !== NULL)
{
// Add the values to the current data
$this->_data = $data + $this->_data;
}
}
/**
* Magic method, searches for the given variable and returns its value.
* Local variables will be returned before global variables.
*
* $value = $view->foo;
*
* [!!] If the variable has not yet been set, an exception will be thrown.
*
* @param string variable name
* @return mixed
* @throws Kohana_Exception
*/
public function & __get($key)
{
if (array_key_exists($key, $this->_data))
{
return $this->_data[$key];
}
elseif (array_key_exists($key, View::$_global_data))
{
return View::$_global_data[$key];
}
else
{
throw new Kohana_Exception('View variable is not set: :var',
array(':var' => $key));
}
}
/**
* Magic method, calls [View::set] with the same parameters.
*
* $view->foo = 'something';
*
* @param string variable name
* @param mixed value
* @return void
*/
public function __set($key, $value)
{
$this->set($key, $value);
}
/**
* Magic method, determines if a variable is set.
*
* isset($view->foo);
*
* [!!] `NULL` variables are not considered to be set by [isset](http://php.net/isset).
*
* @param string variable name
* @return boolean
*/
public function __isset($key)
{
return (isset($this->_data[$key]) OR isset(View::$_global_data[$key]));
}
/**
* Magic method, unsets a given variable.
*
* unset($view->foo);
*
* @param string variable name
* @return void
*/
public function __unset($key)
{
unset($this->_data[$key], View::$_global_data[$key]);
}
/**
* Magic method, returns the output of [View::render].
*
* @return string
* @uses View::render
*/
public function __toString()
{
try
{
return $this->render();
}
catch (Exception $e)
{
// Display the exception message
Kohana::exception_handler($e);
return '';
}
}
/**
* Sets the view filename.
*
* $view->set_filename($file);
*
* @param string view filename
* @return View
* @throws Kohana_View_Exception
*/
public function set_filename($file)
{
if (($path = Kohana::find_file('views', $file)) === FALSE)
{
throw new Kohana_View_Exception('The requested view :file could not be found', array(
':file' => $file,
));
}
// Store the file path locally
$this->_file = $path;
return $this;
}
/**
* Assigns a variable by name. Assigned values will be available as a
* variable within the view file:
*
* // This value can be accessed as $foo within the view
* $view->set('foo', 'my value');
*
* You can also use an array to set several values at once:
*
* // Create the values $food and $beverage in the view
* $view->set(array('food' => 'bread', 'beverage' => 'water'));
*
* @param string variable name or an array of variables
* @param mixed value
* @return $this
*/
public function set($key, $value = NULL)
{
if (is_array($key))
{
foreach ($key as $name => $value)
{
$this->_data[$name] = $value;
}
}
else
{
$this->_data[$key] = $value;
}
return $this;
}
/**
* Assigns a value by reference. The benefit of binding is that values can
* be altered without re-setting them. It is also possible to bind variables
* before they have values. Assigned values will be available as a
* variable within the view file:
*
* // This reference can be accessed as $ref within the view
* $view->bind('ref', $bar);
*
* @param string variable name
* @param mixed referenced variable
* @return $this
*/
public function bind($key, & $value)
{
$this->_data[$key] =& $value;
return $this;
}
/**
* Renders the view object to a string. Global and local data are merged
* and extracted to create local variables within the view file.
*
* $output = $view->render();
*
* [!!] Global variables with the same key name as local variables will be
* overwritten by the local variable.
*
* @param string view filename
* @return string
* @throws Kohana_View_Exception
* @uses View::capture
*/
public function render($file = NULL)
{
if ($file !== NULL)
{
$this->set_filename($file);
}
if (empty($this->_file))
{
throw new Kohana_View_Exception('You must set the file to use within your view before rendering');
}
// Combine local and global data and capture the output
return View::capture($this->_file, $this->_data);
}
} // End View

View File

@@ -0,0 +1,9 @@
<?php defined('SYSPATH') or die('No direct script access.');
/**
* @package Kohana
* @category Exceptions
* @author Kohana Team
* @copyright (c) 2009-2010 Kohana Team
* @license http://kohanaframework.org/license
*/
class Kohana_View_Exception extends Kohana_Exception { }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,60 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* Credit card validation configuration.
*
* Options for each credit card:
* length - All the allowed card number lengths, in a comma separated string
* prefix - The digits the card needs to start with, in regex format
* luhn - Enable or disable card number validation by the Luhn algorithm
*/
return array(
'default' => array(
'length' => '13,14,15,16,17,18,19',
'prefix' => '',
'luhn' => TRUE,
),
'american express' => array(
'length' => '15',
'prefix' => '3[47]',
'luhn' => TRUE,
),
'diners club' => array(
'length' => '14,16',
'prefix' => '36|55|30[0-5]',
'luhn' => TRUE,
),
'discover' => array(
'length' => '16',
'prefix' => '6(?:5|011)',
'luhn' => TRUE,
),
'jcb' => array(
'length' => '15,16',
'prefix' => '3|1800|2131',
'luhn' => TRUE,
),
'maestro' => array(
'length' => '16,18',
'prefix' => '50(?:20|38)|6(?:304|759)',
'luhn' => TRUE,
),
'mastercard' => array(
'length' => '16',
'prefix' => '5[1-5]',
'luhn' => TRUE,
),
'visa' => array(
'length' => '13,16',
'prefix' => '4',
'luhn' => TRUE,
),
);

View File

@@ -0,0 +1,17 @@
<?php defined('SYSPATH') or die('No direct script access.');
return array(
'default' => array(
/**
* The following options must be set:
*
* string key secret passphrase
* integer mode encryption mode, one of MCRYPT_MODE_*
* integer cipher encryption cipher, one of the Mcrpyt cipher constants
*/
'cipher' => MCRYPT_RIJNDAEL_128,
'mode' => MCRYPT_MODE_NOFB,
),
);

View File

@@ -0,0 +1,65 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
return array(
'uncountable' => array(
'access',
'advice',
'art',
'baggage',
'dances',
'equipment',
'fish',
'fuel',
'furniture',
'heat',
'honey',
'homework',
'impatience',
'information',
'knowledge',
'luggage',
'media',
'money',
'music',
'news',
'patience',
'progress',
'pollution',
'research',
'rice',
'sand',
'series',
'sheep',
'sms',
'spam',
'species',
'staff',
'toothpaste',
'traffic',
'understanding',
'water',
'weather',
'work',
),
'irregular' => array(
'child' => 'children',
'clothes' => 'clothing',
'man' => 'men',
'movie' => 'movies',
'person' => 'people',
'woman' => 'women',
'mouse' => 'mice',
'goose' => 'geese',
'ox' => 'oxen',
'leaf' => 'leaves',
'course' => 'courses',
'size' => 'sizes',
'was' => 'were',
'is' => 'are',
'verse' => 'verses',
'hero' => 'heroes',
'purchase' => 'purchases',
),
);

View File

@@ -0,0 +1,225 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
/**
* A list of mime types. Our list is generally more complete and accurate than
* the operating system MIME list.
*
* If there are any missing options, please create a ticket on our issue tracker,
* http://kohanaphp.com/trac/newticket. Be sure to give the filename and
* expected MIME type, as well as any additional information you can provide.
*/
return array
(
'323' => array('text/h323'),
'7z' => array('application/x-7z-compressed'),
'abw' => array('application/x-abiword'),
'acx' => array('application/internet-property-stream'),
'ai' => array('application/postscript'),
'aif' => array('audio/x-aiff'),
'aifc' => array('audio/x-aiff'),
'aiff' => array('audio/x-aiff'),
'asf' => array('video/x-ms-asf'),
'asr' => array('video/x-ms-asf'),
'asx' => array('video/x-ms-asf'),
'atom' => array('application/atom+xml'),
'avi' => array('video/avi', 'video/msvideo', 'video/x-msvideo'),
'bin' => array('application/octet-stream','application/macbinary'),
'bmp' => array('image/bmp'),
'c' => array('text/x-csrc'),
'c++' => array('text/x-c++src'),
'cab' => array('application/x-cab'),
'cc' => array('text/x-c++src'),
'cda' => array('application/x-cdf'),
'class' => array('application/octet-stream'),
'cpp' => array('text/x-c++src'),
'cpt' => array('application/mac-compactpro'),
'csh' => array('text/x-csh'),
'css' => array('text/css'),
'csv' => array('text/x-comma-separated-values', 'application/vnd.ms-excel', 'text/comma-separated-values', 'text/csv'),
'dbk' => array('application/docbook+xml'),
'dcr' => array('application/x-director'),
'deb' => array('application/x-debian-package'),
'diff' => array('text/x-diff'),
'dir' => array('application/x-director'),
'divx' => array('video/divx'),
'dll' => array('application/octet-stream', 'application/x-msdos-program'),
'dmg' => array('application/x-apple-diskimage'),
'dms' => array('application/octet-stream'),
'doc' => array('application/msword'),
'docx' => array('application/vnd.openxmlformats-officedocument.wordprocessingml.document'),
'dvi' => array('application/x-dvi'),
'dxr' => array('application/x-director'),
'eml' => array('message/rfc822'),
'eps' => array('application/postscript'),
'evy' => array('application/envoy'),
'exe' => array('application/x-msdos-program', 'application/octet-stream'),
'fla' => array('application/octet-stream'),
'flac' => array('application/x-flac'),
'flc' => array('video/flc'),
'fli' => array('video/fli'),
'flv' => array('video/x-flv'),
'gif' => array('image/gif'),
'gtar' => array('application/x-gtar'),
'gz' => array('application/x-gzip'),
'h' => array('text/x-chdr'),
'h++' => array('text/x-c++hdr'),
'hh' => array('text/x-c++hdr'),
'hpp' => array('text/x-c++hdr'),
'hqx' => array('application/mac-binhex40'),
'hs' => array('text/x-haskell'),
'htm' => array('text/html'),
'html' => array('text/html'),
'ico' => array('image/x-icon'),
'ics' => array('text/calendar'),
'iii' => array('application/x-iphone'),
'ins' => array('application/x-internet-signup'),
'iso' => array('application/x-iso9660-image'),
'isp' => array('application/x-internet-signup'),
'jar' => array('application/java-archive'),
'java' => array('application/x-java-applet'),
'jpe' => array('image/jpeg', 'image/pjpeg'),
'jpeg' => array('image/jpeg', 'image/pjpeg'),
'jpg' => array('image/jpeg', 'image/pjpeg'),
'js' => array('application/x-javascript'),
'json' => array('application/json'),
'latex' => array('application/x-latex'),
'lha' => array('application/octet-stream'),
'log' => array('text/plain', 'text/x-log'),
'lzh' => array('application/octet-stream'),
'm4a' => array('audio/mpeg'),
'm4p' => array('video/mp4v-es'),
'm4v' => array('video/mp4'),
'man' => array('application/x-troff-man'),
'mdb' => array('application/x-msaccess'),
'midi' => array('audio/midi'),
'mid' => array('audio/midi'),
'mif' => array('application/vnd.mif'),
'mka' => array('audio/x-matroska'),
'mkv' => array('video/x-matroska'),
'mov' => array('video/quicktime'),
'movie' => array('video/x-sgi-movie'),
'mp2' => array('audio/mpeg'),
'mp3' => array('audio/mpeg'),
'mp4' => array('application/mp4','audio/mp4','video/mp4'),
'mpa' => array('video/mpeg'),
'mpe' => array('video/mpeg'),
'mpeg' => array('video/mpeg'),
'mpg' => array('video/mpeg'),
'mpg4' => array('video/mp4'),
'mpga' => array('audio/mpeg'),
'mpp' => array('application/vnd.ms-project'),
'mpv' => array('video/x-matroska'),
'mpv2' => array('video/mpeg'),
'ms' => array('application/x-troff-ms'),
'msg' => array('application/msoutlook','application/x-msg'),
'msi' => array('application/x-msi'),
'nws' => array('message/rfc822'),
'oda' => array('application/oda'),
'odb' => array('application/vnd.oasis.opendocument.database'),
'odc' => array('application/vnd.oasis.opendocument.chart'),
'odf' => array('application/vnd.oasis.opendocument.forumla'),
'odg' => array('application/vnd.oasis.opendocument.graphics'),
'odi' => array('application/vnd.oasis.opendocument.image'),
'odm' => array('application/vnd.oasis.opendocument.text-master'),
'odp' => array('application/vnd.oasis.opendocument.presentation'),
'ods' => array('application/vnd.oasis.opendocument.spreadsheet'),
'odt' => array('application/vnd.oasis.opendocument.text'),
'oga' => array('audio/ogg'),
'ogg' => array('application/ogg'),
'ogv' => array('video/ogg'),
'otg' => array('application/vnd.oasis.opendocument.graphics-template'),
'oth' => array('application/vnd.oasis.opendocument.web'),
'otp' => array('application/vnd.oasis.opendocument.presentation-template'),
'ots' => array('application/vnd.oasis.opendocument.spreadsheet-template'),
'ott' => array('application/vnd.oasis.opendocument.template'),
'p' => array('text/x-pascal'),
'pas' => array('text/x-pascal'),
'patch' => array('text/x-diff'),
'pbm' => array('image/x-portable-bitmap'),
'pdf' => array('application/pdf', 'application/x-download'),
'php' => array('application/x-httpd-php'),
'php3' => array('application/x-httpd-php'),
'php4' => array('application/x-httpd-php'),
'php5' => array('application/x-httpd-php'),
'phps' => array('application/x-httpd-php-source'),
'phtml' => array('application/x-httpd-php'),
'pl' => array('text/x-perl'),
'pm' => array('text/x-perl'),
'png' => array('image/png', 'image/x-png'),
'po' => array('text/x-gettext-translation'),
'pot' => array('application/vnd.ms-powerpoint'),
'pps' => array('application/vnd.ms-powerpoint'),
'ppt' => array('application/powerpoint'),
'pptx' => array('application/vnd.openxmlformats-officedocument.presentationml.presentation'),
'ps' => array('application/postscript'),
'psd' => array('application/x-photoshop', 'image/x-photoshop'),
'pub' => array('application/x-mspublisher'),
'py' => array('text/x-python'),
'qt' => array('video/quicktime'),
'ra' => array('audio/x-realaudio'),
'ram' => array('audio/x-realaudio', 'audio/x-pn-realaudio'),
'rar' => array('application/rar'),
'rgb' => array('image/x-rgb'),
'rm' => array('audio/x-pn-realaudio'),
'rpm' => array('audio/x-pn-realaudio-plugin', 'application/x-redhat-package-manager'),
'rss' => array('application/rss+xml'),
'rtf' => array('text/rtf'),
'rtx' => array('text/richtext'),
'rv' => array('video/vnd.rn-realvideo'),
'sea' => array('application/octet-stream'),
'sh' => array('text/x-sh'),
'shtml' => array('text/html'),
'sit' => array('application/x-stuffit'),
'smi' => array('application/smil'),
'smil' => array('application/smil'),
'so' => array('application/octet-stream'),
'src' => array('application/x-wais-source'),
'svg' => array('image/svg+xml'),
'swf' => array('application/x-shockwave-flash'),
't' => array('application/x-troff'),
'tar' => array('application/x-tar'),
'tcl' => array('text/x-tcl'),
'tex' => array('application/x-tex'),
'text' => array('text/plain'),
'texti' => array('application/x-texinfo'),
'textinfo' => array('application/x-texinfo'),
'tgz' => array('application/x-tar'),
'tif' => array('image/tiff'),
'tiff' => array('image/tiff'),
'torrent' => array('application/x-bittorrent'),
'tr' => array('application/x-troff'),
'tsv' => array('text/tab-separated-values'),
'txt' => array('text/plain'),
'wav' => array('audio/x-wav'),
'wax' => array('audio/x-ms-wax'),
'wbxml' => array('application/wbxml'),
'wm' => array('video/x-ms-wm'),
'wma' => array('audio/x-ms-wma'),
'wmd' => array('application/x-ms-wmd'),
'wmlc' => array('application/wmlc'),
'wmv' => array('video/x-ms-wmv', 'application/octet-stream'),
'wmx' => array('video/x-ms-wmx'),
'wmz' => array('application/x-ms-wmz'),
'word' => array('application/msword', 'application/octet-stream'),
'wp5' => array('application/wordperfect5.1'),
'wpd' => array('application/vnd.wordperfect'),
'wvx' => array('video/x-ms-wvx'),
'xbm' => array('image/x-xbitmap'),
'xcf' => array('image/xcf'),
'xhtml' => array('application/xhtml+xml'),
'xht' => array('application/xhtml+xml'),
'xl' => array('application/excel', 'application/vnd.ms-excel'),
'xla' => array('application/excel', 'application/vnd.ms-excel'),
'xlc' => array('application/excel', 'application/vnd.ms-excel'),
'xlm' => array('application/excel', 'application/vnd.ms-excel'),
'xls' => array('application/excel', 'application/vnd.ms-excel'),
'xlsx' => array('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'),
'xlt' => array('application/excel', 'application/vnd.ms-excel'),
'xml' => array('text/xml', 'application/xml'),
'xof' => array('x-world/x-vrml'),
'xpm' => array('image/x-xpixmap'),
'xsl' => array('text/xml'),
'xvid' => array('video/x-xvid'),
'xwd' => array('image/x-xwindowdump'),
'z' => array('application/x-compress'),
'zip' => array('application/x-zip', 'application/zip', 'application/x-zip-compressed')
);

View File

@@ -0,0 +1,7 @@
<?php defined('SYSPATH') or die('No direct script access.');
return array(
'cookie' => array(
'encrypted' => FALSE,
),
);

View File

@@ -0,0 +1,104 @@
<?php defined('SYSPATH') or die('No direct access allowed.');
return array(
'platform' => array(
'windows nt 6.1' => 'Windows 7',
'windows nt 6.0' => 'Windows Vista',
'windows nt 5.2' => 'Windows 2003',
'windows nt 5.1' => 'Windows XP',
'windows nt 5.0' => 'Windows 2000',
'windows nt 4.0' => 'Windows NT',
'winnt4.0' => 'Windows NT',
'winnt 4.0' => 'Windows NT',
'winnt' => 'Windows NT',
'windows 98' => 'Windows 98',
'win98' => 'Windows 98',
'windows 95' => 'Windows 95',
'win95' => 'Windows 95',
'windows' => 'Unknown Windows OS',
'os x' => 'Mac OS X',
'intel mac' => 'Intel Mac',
'ppc mac' => 'PowerPC Mac',
'powerpc' => 'PowerPC',
'ppc' => 'PowerPC',
'cygwin' => 'Cygwin',
'linux' => 'Linux',
'debian' => 'Debian',
'openvms' => 'OpenVMS',
'sunos' => 'Sun Solaris',
'amiga' => 'Amiga',
'beos' => 'BeOS',
'apachebench' => 'ApacheBench',
'freebsd' => 'FreeBSD',
'netbsd' => 'NetBSD',
'bsdi' => 'BSDi',
'openbsd' => 'OpenBSD',
'os/2' => 'OS/2',
'warp' => 'OS/2',
'aix' => 'AIX',
'irix' => 'Irix',
'osf' => 'DEC OSF',
'hp-ux' => 'HP-UX',
'hurd' => 'GNU/Hurd',
'unix' => 'Unknown Unix OS',
),
'browser' => array(
'Opera' => 'Opera',
'MSIE' => 'Internet Explorer',
'Internet Explorer' => 'Internet Explorer',
'Shiira' => 'Shiira',
'Firefox' => 'Firefox',
'Chimera' => 'Chimera',
'Phoenix' => 'Phoenix',
'Firebird' => 'Firebird',
'Camino' => 'Camino',
'Navigator' => 'Netscape',
'Netscape' => 'Netscape',
'OmniWeb' => 'OmniWeb',
'Chrome' => 'Chrome',
'Safari' => 'Safari',
'CFNetwork' => 'Safari', // Core Foundation for OSX, WebKit/Safari
'Konqueror' => 'Konqueror',
'Epiphany' => 'Epiphany',
'Galeon' => 'Galeon',
'Mozilla' => 'Mozilla',
'icab' => 'iCab',
'lynx' => 'Lynx',
'links' => 'Links',
'hotjava' => 'HotJava',
'amaya' => 'Amaya',
'IBrowse' => 'IBrowse',
),
'mobile' => array(
'mobileexplorer' => 'Mobile Explorer',
'openwave' => 'Open Wave',
'opera mini' => 'Opera Mini',
'operamini' => 'Opera Mini',
'elaine' => 'Palm',
'palmsource' => 'Palm',
'digital paths' => 'Palm',
'avantgo' => 'Avantgo',
'xiino' => 'Xiino',
'palmscape' => 'Palmscape',
'nokia' => 'Nokia',
'ericsson' => 'Ericsson',
'blackBerry' => 'BlackBerry',
'motorola' => 'Motorola',
'iphone' => 'iPhone',
'android' => 'Android',
),
'robot' => array(
'googlebot' => 'Googlebot',
'msnbot' => 'MSNBot',
'slurp' => 'Inktomi Slurp',
'yahoo' => 'Yahoo',
'askjeeves' => 'AskJeeves',
'fastcrawler' => 'FastCrawler',
'infoseek' => 'InfoSeek Robot 1.0',
'lycos' => 'Lycos',
),
);

View File

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

View File

@@ -0,0 +1,69 @@
# Loading Classes
Kohana takes advantage of PHP [autoloading](http://php.net/manual/language.oop5.autoload.php). This removes the need to call [include](http://php.net/include) or [require](http://php.net/require) before using a class. When you use a class Kohana will find and include the class file for you. For instance, when you want to use the [Cookie::set] method, you simply call:
Cookie::set('mycookie', 'any string value');
Or to load an [Encrypt] instance, just call [Encrypt::instance]:
$encrypt = Encrypt::instance();
Classes are loaded via the [Kohana::auto_load] method, which makes a simple conversion from class name to file name:
1. Classes are placed in the `classes/` directory of the [filesystem](files)
2. Any underscore characters in the class name are converted to slashes
2. The filename is lowercase
When calling a class that has not been loaded (eg: `Session_Cookie`), Kohana will search the filesystem using [Kohana::find_file] for a file named `classes/session/cookie.php`.
If your classes do not follow this convention, they cannot be autoloaded by Kohana. You will have to manually included your files, or add your own [autoload function.](http://us3.php.net/manual/en/function.spl-autoload-register.php)
## Custom Autoloaders
Kohana's default autoloader is enabled in `application/bootstrap.php` using [spl_autoload_register](http://php.net/spl_autoload_register):
spl_autoload_register(array('Kohana', 'auto_load'));
This allows [Kohana::auto_load] to attempt to find and include any class that does not yet exist when the class is first used.
### Example: Zend
You can easily gain access to other libraries if they include an autoloader. For example, here is how to enable Zend's autoloader so you can use Zend libraries in your Kohana application.
#### Download and install the Zend Framework files
- [Download the latest Zend Framework files](http://framework.zend.com/download/latest).
- Create a `vendor` directory at `application/vendor`. This keeps third party software separate from your application classes.
- Move the decompressed Zend folder containing Zend Framework to `application/vendor/Zend`.
#### Include Zend's Autoloader in your bootstrap
Somewhere in `application/bootstrap.php`, copy the following code:
/**
* Enable Zend Framework autoloading
*/
if ($path = Kohana::find_file('vendor', 'Zend/Loader'))
{
ini_set('include_path',
ini_get('include_path').PATH_SEPARATOR.dirname(dirname($path)));
require_once 'Zend/Loader/Autoloader.php';
Zend_Loader_Autoloader::getInstance();
}
#### Usage example
You can now autoload any Zend Framework classes from inside your Kohana application.
if ($validate($_POST))
{
$mailer = new Zend_Mail;
$mailer->setBodyHtml($view)
->setFrom(Kohana::config('site')->email_from)
->addTo($email)
->setSubject($message)
->send();
}

View File

@@ -0,0 +1,164 @@
# Bootstrap
The bootstrap is located at `application/bootstrap.php`. It is responsible for setting up the Kohana environment and executing the main response. It is included by `index.php` (see [Request flow](flow))
[!!] The bootstrap is responsible for the flow of your application. In previous versions of Kohana the bootstrap was in `system` and was somewhat of an unseen, uneditible force. In Kohana 3 the bootstrap takes on a much more integral and versatile role. Do not be afraid to edit and change your bootstrap however you see fit.
## Environment setup
First the bootstrap sets the timezone and the locale, and adds Kohana's autoloader so the [cascading filesystem](files) works. You could add any other settings that all your application needed here.
~~~
// Sample excerpt from bootstrap.php with comments trimmed down
// Set the default time zone.
date_default_timezone_set('America/Chicago');
// Set the default locale.
setlocale(LC_ALL, 'en_US.utf-8');
// Enable the Kohana auto-loader.
spl_autoload_register(array('Kohana', 'auto_load'));
// Enable the Kohana auto-loader for unserialization.
ini_set('unserialize_callback_func', 'spl_autoload_call');
~~~
## Initilization and Configuration
Kohana is then initialized by calling [Kohana::init], and the log and [config](files/config) reader/writers are enabled.
~~~
// Sample excerpt from bootstrap.php with comments trimmed down
Kohana::init(array('
base_url' => '/kohana/',
index_file => false,
));
// Attach the file writer to logging. Multiple writers are supported.
Kohana::$log->attach(new Kohana_Log_File(APPPATH.'logs'));
// Attach a file reader to config. Multiple readers are supported.
Kohana::$config->attach(new Kohana_Config_File);
~~~
You can add conditional statements to make the bootstrap have different values based on certain settings. For example, detect whether we are live by checking `$_SERVER['HTTP_HOST']` and set caching, profiling, etc. accordingly. This is just an example, there are many different ways to accomplish the same thing.
~~~
// Excerpt from http://github.com/isaiahdw/kohanaphp.com/blob/f2afe8e28b/application/bootstrap.php
... [trimmed]
/**
* Set the environment status by the domain.
*/
if (strpos($_SERVER['HTTP_HOST'], 'kohanaphp.com') !== FALSE)
{
// We are live!
Kohana::$environment = Kohana::PRODUCTION;
// Turn off notices and strict errors
error_reporting(E_ALL ^ E_NOTICE ^ E_STRICT);
}
/**
* Initialize Kohana, setting the default options.
... [trimmed]
*/
Kohana::init(array(
'base_url' => Kohana::$environment === Kohana::PRODUCTION ? '/' : '/kohanaphp.com/',
'caching' => Kohana::$environment === Kohana::PRODUCTION,
'profile' => Kohana::$environment !== Kohana::PRODUCTION,
'index_file' => FALSE,
));
... [trimmed]
try
{
$request = Request::instance()->execute();
}
catch (Exception $e)
{
// If we are in development and the error wasn't a 404, show the stack trace.
if ( Kohana::$environment == "development" AND $e->getCode() != 404 )
{
throw $e;
}
...[trimmed]
~~~
[!!] Note: The default bootstrap will set `Kohana::$environment = $_ENV['KOHANA_ENV']` if set. Docs on how to supply this variable are available in your web server's documentation (e.g. [Apache](http://httpd.apache.org/docs/1.3/mod/mod_env.html#setenv), [Lighttpd](http://redmine.lighttpd.net/wiki/1/Docs:ModSetEnv#Options)). This is considered better practice than many alternative methods to set `Kohana::$enviroment`, as you can change the setting per server, without having to rely on config options or hostnames.
## Modules
**Read the [Modules](modules) page for a more detailed description.**
[Modules](modules) are then loaded using [Kohana::modules()]. Including modules is optional.
Each key in the array should be the name of the module, and the value is the path to the module, either relative or absolute.
~~~
// Example excerpt from bootstrap.php
Kohana::modules(array(
'database' => MODPATH.'database',
'orm' => MODPATH.'orm',
'userguide' => MODPATH.'userguide',
));
~~~
## Routes
**Read the [Routing](routing) page for a more detailed description and more examples.**
[Routes](routing) are then defined via [Route::set()].
~~~
// The default route that comes with Kohana 3
Route::set('default', '(<controller>(/<action>(/<id>)))')
->defaults(array(
'controller' => 'welcome',
'action' => 'index',
));
~~~
## Execution
Once our environment is initialized and routes defined, it's time to execute our application. This area of the bootstrap is very flexible. Do not be afraid to change this around to whatever suits your needs.
### Basic Example
The most simple way to do this, and what comes default with Kohana 3 is simply:
~~~
// Execute the main request
echo Request::instance()
->execute()
->send_headers()
->response;
~~~
### Catching Exceptions
**See [Error Handling](errors) for a more detailed description and more examples.**
The previous example provides no error catching, which means if an error occurs a stack trace would be shown which could show sensitive info, as well as be unfriendly for the user. One way to solve this is to add a `try catch` block. If we get an exception, we will show the view located at `views/errors/404.php`. **Note: Because we catch the exception, Kohana will not log the error! It is your responsibility to log the error.**
~~~
try
{
// Execute the main request
$request = Request::instance()->execute();
}
catch (Exception $e)
{
// Be sure to log the error
Kohana::$log->add(Kohana::ERROR, Kohana::exception_text($e));
// If there was an error, send a 404 response and display an error
$request->status = 404;
$request->response = View::factory('errors/404');
}
// Send the headers and echo the response
$request->send_headers();
echo $request->response;
~~~

View File

@@ -0,0 +1 @@
This will discuss controller basics, like before() and after(), private function, and about extending controllers like the Controller_Template, or using a parent::before() for authentication.

View File

@@ -0,0 +1,306 @@
# Conventions and Coding Style
It is encouraged that you follow Kohana's [coding style](http://dev.kohanaframework.org/wiki/kohana2/CodingStyle). This makes code more readable and allows for easier code sharing and contributing.
## Class Names and File Location
Class names in Kohana follow a strict convention to facilitate [autoloading](autoloading). Class names should have uppercase first letters with underscores to separate words. Underscores are significant as they directly reflect the file location in the filesystem.
The following conventions apply:
1. CamelCased class names should not be used, except when it is undesirable to create a new directory level.
2. All class file names and directory names are lowercase.
3. All classes should be in the `classes` directory. This may be at any level in the [cascading filesystem](files).
[!!] Unlike Kohana v2.x, there is no separation between "controllers", "models", "libraries" and "helpers". All classes are placed in the "classes/" directory, regardless if they are static "helpers" or object "libraries". You can use whatever kind of class design you want: static, singleton, adapter, etc.
### Examples {#class-name-examples}
Remember that in a class, an underscore means a new directory. Consider the following examples:
Class Name | File Path
----------------------|-------------------------------
Controller_Template | classes/controller/template.php
Model_User | classes/model/user.php
Database | classes/database.php
Database_Query | classes/database/query.php
Form | classes/form.php
## Coding Standards
In order to produce highly consistent source code, we ask that everyone follow the coding standards as closely as possible.
### Brackets
Please use [BSD/Allman Style](http://en.wikipedia.org/wiki/Indent_style#BSD.2FAllman_style) bracketing. Brackets are always on their own line. The exception to this rule is the opening bracket for a class, which can be on the same line.
if ($foo == 'bar')
{
$baz->bar();
}
else
{
$baz->default();
}
// The opening bracket for a class can be on the same line
Class Foobar {
### Naming Conventions
Kohana uses under_score naming, not camelCase naming.
#### Classes
// Controller class, uses Controller_ prefix
class Controller_Apple extends Controller {
// Model class, uses Model_ prefix
class Model_Cheese extends Model {
// Regular class
class Peanut {
When creating an instance of a class, don't use parentheses if you're not passing something on to the constructor:
// Correct:
$db = new Database;
// Incorrect:
$db = new Database();
#### Functions and Methods
Functions should be all lowercase, and use under_scores to separate words:
function drink_beverage($beverage)
{
#### Variables
All variables should be lowercase and use under_score, not camelCase:
// Correct:
$foo = 'bar';
$long_example = 'uses underscores';
// Incorrect:
$weDontWantThis = 'understood?';
### Indentation
You must use tabs to indent your code. Using spaces for tabbing is strictly forbidden.
Vertical spacing (for multi-line) is done with spaces. Tabs are not good for vertical alignment because different people have different tab widths.
$text = 'this is a long text block that is wrapped. Normally, we aim for '
.'wrapping at 80 chars. Vertical alignment is very important for '
.'code readability. Remember that all indentation is done with tabs,'
.'but vertical alignment should be completed with spaces, after '
.'indenting with tabs.';
### String concatenation
Do not put spaces around the concatenation operator:
// Correct:
$str = 'one'.$var.'two';
// Incorrect:
$str = 'one'. $var .'two';
$str = 'one' . $var . 'two';
### Single Line Statements
Single-line IF statements should only be used when breaking normal execution (e.g. return or continue):
// Acceptable:
if ($foo == $bar)
return $foo;
if ($foo == $bar)
continue;
if ($foo == $bar)
break;
if ($foo == $bar)
throw new Exception('You screwed up!');
// Not acceptable:
if ($baz == $bun)
$baz = $bar + 2;
### Comparison Operations
Please use OR and AND for comparison:
// Correct:
if (($foo AND $bar) OR ($b AND $c))
// Incorrect:
if (($foo && $bar) || ($b && $c))
Please use elseif, not else if:
// Correct:
elseif ($bar)
// Incorrect:
else if($bar)
### Switch structures
Each case, break and default should be on a separate line. The block inside a case or default must be indented by 1 tab.
switch ($var)
{
case 'bar':
case 'foo':
echo 'hello';
break;
case 1:
echo 'one';
break;
default:
echo 'bye';
break;
}
### Parentheses
There should be one space after statement name, followed by a parenthesis. The ! (bang) character must have a space on either side to ensure maximum readability. Except in the case of a bang or type casting, there should be no whitespace after an opening parenthesis or before a closing parenthesis.
// Correct:
if ($foo == $bar)
if ( ! $foo)
// Incorrect:
if($foo == $bar)
if(!$foo)
if ((int) $foo)
if ( $foo == $bar )
if (! $foo)
### Ternaries
All ternary operations should follow a standard format. Use parentheses around expressions only, not around just variables.
$foo = ($bar == $foo) ? $foo : $bar;
$foo = $bar ? $foo : $bar;
All comparisons and operations must be done inside of a parentheses group:
$foo = ($bar > 5) ? ($bar + $foo) : strlen($bar);
When separating complex ternaries (ternaries where the first part goes beyond ~80 chars) into multiple lines, spaces should be used to line up operators, which should be at the front of the successive lines:
$foo = ($bar == $foo)
? $foo
: $bar;
### Type Casting
Type casting should be done with spaces on each side of the cast:
// Correct:
$foo = (string) $bar;
if ( (string) $bar)
// Incorrect:
$foo = (string)$bar;
When possible, please use type casting instead of ternary operations:
// Correct:
$foo = (bool) $bar;
// Incorrect:
$foo = ($bar == TRUE) ? TRUE : FALSE;
When casting type to integer or boolean, use the short format:
// Correct:
$foo = (int) $bar;
$foo = (bool) $bar;
// Incorrect:
$foo = (integer) $bar;
$foo = (boolean) $bar;
### Constants
Always use uppercase for constants:
// Correct:
define('MY_CONSTANT', 'my_value');
$a = TRUE;
$b = NULL;
// Incorrect:
define('MyConstant', 'my_value');
$a = True;
$b = null;
Place constant comparisons at the end of tests:
// Correct:
if ($foo !== FALSE)
// Incorrect:
if (FALSE !== $foo)
This is a slightly controversial choice, so I will explain the reasoning. If we were to write the previous example in plain English, the correct example would read:
if variable $foo is not exactly FALSE
And the incorrect example would read:
if FALSE is not exactly variable $foo
Since we are reading left to right, it simply doesn't make sense to put the constant first.
### Comments
#### One-line comments
Use //, preferably above the line of code you're commenting on. Leave a space after it and start with a capital. Never use #.
// Correct
//Incorrect
// incorrect
# Incorrect
### Regular expressions
When coding regular expressions please use PCRE rather than the POSIX flavor. PCRE is considered more powerful and faster.
// Correct:
if (preg_match('/abc/i'), $str)
// Incorrect:
if (eregi('abc', $str))
Use single quotes around your regular expressions rather than double quotes. Single-quoted strings are more convenient because of their simplicity. Unlike double-quoted strings they don't support variable interpolation nor integrated backslash sequences like \n or \t, etc.
// Correct:
preg_match('/abc/', $str);
// Incorrect:
preg_match("/abc/", $str);
When performing a regular expression search and replace, please use the $n notation for backreferences. This is preferred over \\n.
// Correct:
preg_replace('/(\d+) dollar/', '$1 euro', $str);
// Incorrect:
preg_replace('/(\d+) dollar/', '\\1 euro', $str);
Finally, please note that the $ character for matching the position at the end of the line allows for a following newline character. Use the D modifier to fix this if needed. [More info](http://blog.php-security.org/archives/76-Holes-in-most-preg_match-filters.html).
$str = "email@example.com\n";
preg_match('/^.+@.+$/', $str); // TRUE
preg_match('/^.+@.+$/D', $str); // FALSE

View File

@@ -0,0 +1,89 @@
# Cookies
Kohana provides classes that make it easy to work with both cookies and sessions. At a high level both sessions and cookies provide the same functionality. They allow the developer to store temporary or persistent information about a specific client for later retrieval, usually to make something persistent between requests.
[Cookies](http://en.wikipedia.org/wiki/HTTP_cookie) should be used for storing non-private data that is persistent for a long period of time. For example storing a user preference or a language setting. Use the [Cookie] class for getting and setting cookies.
[!!] Kohana uses "signed" cookies. Every cookie that is stored is combined with a secure hash to prevent modification of the cookie. If a cookie is modified outside of Kohana the hash will be incorrect and the cookie will be deleted. This hash is generated using [Cookie::salt()], which uses the [Cookie::$salt] property. You should change this setting when your application is live.
Nothing stops you from using `$_COOKIE` like normal, but you can not mix using the Cookie class and the regular `$_COOKIE` global, because the hash that Kohana uses to sign cookies will not be present, and Kohana will delete the cookie.
## Storing, Retrieving, and Deleting Data
[Cookie] and [Session] provide a very similar API for storing data. The main difference between them is that sessions are accessed using an object, and cookies are accessed using a static class.
### Storing Data
Storing session or cookie data is done using the [Cookie::set] method:
// Set cookie data
Cookie::set($key, $value);
// Store a user id
Cookie::set('user_id', 10);
### Retrieving Data
Getting session or cookie data is done using the [Cookie::get] method:
// Get cookie data
$data = Cookie::get($key, $default_value);
// Get the user id
$user = Cookie::get('user_id');
### Deleting Data
Deleting session or cookie data is done using the [Cookie::delete] method:
// Delete cookie data
Cookie::delete($key);
// Delete the user id
Cookie::delete('user_id');
## Cookie Settings
All of the cookie settings are changed using static properties. You can either change these settings in `bootstrap.php` or by using [transparent extension](extension). Always check these settings before making your application live, as many of them will have a direct affect on the security of your application.
The most important setting is [Cookie::$salt], which is used for secure signing. This value should be changed and kept secret:
Cookie::$salt = 'your secret is safe with me';
[!!] Changing this value will render all cookies that have been set before invalid.
By default, cookies are stored until the browser is closed. To use a specific lifetime, change the [Cookie::$expiration] setting:
// Set cookies to expire after 1 week
Cookie::$expiration = 604800;
// Alternative to using raw integers, for better clarity
Cookie::$expiration = Date::WEEK;
The path that the cookie can be accessed from can be restricted using the [Cookie::$path] setting.
// Allow cookies only when going to /public/*
Cookie::$path = '/public/';
The domain that the cookie can be accessed from can also be restricted, using the [Cookie::$domain] setting.
// Allow cookies only on the domain www.example.com
Cookie::$domain = 'www.example.com';
If you want to make the cookie accessible on all subdomains, use a dot at the beginning of the domain.
// Allow cookies to be accessed on example.com and *.example.com
Cookie::$domain = '.example.com';
To only allow the cookie to be accessed over a secure (HTTPS) connection, use the [Cookie::$secure] setting.
// Allow cookies to be accessed only on a secure connection
Cookie::$secure = TRUE;
// Allow cookies to be accessed on any connection
Cookie::$secure = FALSE;
To prevent cookies from being accessed using Javascript, you can change the [Cookie::$httponly] setting.
// Make cookies inaccessible to Javascript
Cookie::$httponly = TRUE;

View File

@@ -0,0 +1,20 @@
# Debugging
Kohana includes several tools to help you debug your application.
The most basic of these is [Kohana::debug]. This simple method will display any number of variables, similar to [var_export](http://php.net/var_export) or [print_r](http://php.net/print_r), but using HTML for extra formatting.
// Display a dump of the $foo and $bar variables
echo Kohana::debug($foo, $bar);
Kohana also provides a method to show the source code of a particular file using [Kohana::debug_source].
// Display this line of source code
echo Kohana::debug_source(__FILE__, __LINE__);
If you want to display information about your application files without exposing the installation directory, you can use [Kohana::debug_path]:
// Displays "APPPATH/cache" rather than the real path
echo Kohana::debug_path(APPPATH.'cache');
If you are having trouble getting something to work correctly, you could check your Kohana logs and your webserver logs, as well as using a debugging tool like [Xdebug](http://www.xdebug.org/).

View File

@@ -0,0 +1,85 @@
# Error/Exception Handling
Kohana provides both an exception handler and an error handler that transforms errors into exceptions using PHP's [ErrorException](http://php.net/errorexception) class. Many details of the error and the internal state of the application is displayed by the handler:
1. Exception class
2. Error level
3. Error message
4. Source of the error, with the error line highlighted
5. A [debug backtrace](http://php.net/debug_backtrace) of the execution flow
6. Included files, loaded extensions, and global variables
## Example
Click any of the links to toggle the display of additional information:
<div>{{userguide/examples/error}}</div>
## Disabling Error/Exception Handling
If you do not want to use the internal error handling, you can disable it when calling [Kohana::init]:
Kohana::init(array('errors' => FALSE));
## Error Reporting
By default, Kohana displays all errors, including strict mode warnings. This is set using [error_reporting](http://php.net/error_reporting):
error_reporting(E_ALL | E_STRICT);
When you application is live and in production, a more conservative setting is recommended, such as ignoring notices:
error_reporting(E_ALL & ~E_NOTICE);
If you get a white screen when an error is triggered, your host probably has disabled displaying errors. You can turn it on again by adding this line just after your `error_reporting` call:
ini_set('display_errors', TRUE);
Errors should **always** be displayed, even in production, because it allows you to use [exception and error handling](debugging.errors) to serve a nice error page rather than a blank white screen when an error happens.
## Last thoughts
In production, **your application should never have any uncaught exceptions**, as this can expose sensitive information (via the stack trace). In the previous example we make the assumption that there is actually a view called 'views/errors/404', which is fairly safe to assume. One solution is to turn 'errors' off in Kohana::init for your production machine, so it displays the normal php errors rather than a stack trace.
~~~
// snippet from bootstrap.php
Kohana::init(array('
...
'errors' => false,
));
~~~
So rather than displaying the Kohana error page with the stack trace, it will display the default php error. Something like:
**Fatal error: Uncaught Kohana_View_Exception [ 0 ]: The requested view errors/404 could not be found ~ SYSPATH/classes/kohana/view.php [ 215 ] thrown in /var/www/kohanut/docs.kohanaphp.com/3.0/system/classes/kohana/view.php on line 215**
Keep in mind what I said earlier though: **your application should never have any uncaught exceptions**, so this should not be necesarry, though it is a good idea, simply because stack traces on a production environment are a *very* bad idea.
Another solution is to always have a `catch` statement that can't fail, something like an `echo` and an `exit` or a `die()`. This should almost never be necesarry, but it makes some people feel better at night. You can either wrap your entire bootstrap in a try catch, or simply wrap the contents of the catch in another try catch. For example:
~~~
try
{
// Execute the main request
$request->execute();
}
catch (Exception $e)
{
try
{
// Be sure to log the error
Kohana::$log->add(Kohana::ERROR, Kohana::exception_text($e));
// If there was an error, send a 404 response and display an error
$request->status = 404;
$request->response = View::factory('errors/404');
}
catch
{
// This is completely overkill, but helps some people sleep at night
echo "Something went terribly wrong. Try again in a few minutes.";
exit;
}
}
~~~

View File

@@ -0,0 +1,101 @@
# Transparent Class Extension
The [cascading filesystem](files) allows transparent class extension. For instance, the class [Cookie] is defined in `SYSPATH/classes/cookie.php` as:
class Cookie extends Kohana_Cookie {}
The default Kohana classes, and many extensions, use this definition so that almost all classes can be extended. You extend any class transparently, by defining your own class in `APPPATH/classes/cookie.php` to add your own methods.
[!!] You should **never** modify any of the files that are distributed with Kohana. Always make modifications to classes using transparent extension to prevent upgrade issues.
For instance, if you wanted to create method that sets encrypted cookies using the [Encrypt] class, you would creat a file at `application/classes/cookie.php` that extends Kohana_Cookie, and adds your functions:
<?php defined('SYSPATH') or die('No direct script access.');
class Cookie extends Kohana_Cookie {
/**
* @var mixed default encryption instance
*/
public static $encryption = 'default';
/**
* Sets an encrypted cookie.
*
* @uses Cookie::set
* @uses Encrypt::encode
*/
public static function encrypt($name, $value, $expiration = NULL)
{
$value = Encrypt::instance(Cookie::$encrpytion)->encode((string) $value);
parent::set($name, $value, $expiration);
}
/**
* Gets an encrypted cookie.
*
* @uses Cookie::get
* @uses Encrypt::decode
*/
public static function decrypt($name, $default = NULL)
{
if ($value = parent::get($name, NULL))
{
$value = Encrypt::instance(Cookie::$encryption)->decode($value);
}
return isset($value) ? $value : $default;
}
} // End Cookie
Now calling `Cookie::encrypt('secret', $data)` will create an encrypted cookie which we can decrypt with `$data = Cookie::decrypt('secret')`.
## How it works
To understand how this works, let's look at what happens normally. When you use the Cookie class, [Kohana::autoload] looks for `classes/cookie.php` in the [cascading filesystem](files). It looks in `application`, then each module, then `system`. The file is found in `system` and is included. Of coures, `system/classes/cookie.php` is just an empty class which extends `Kohana_Cookie`. Again, [Kohana::autoload] is called this time looking for `classes/kohana/cookie.php` which it finds in `system`.
When you add your transparently extended cookie class at `application/classes/cookie.php` this file essentially "replaces" the file at `system/classes/cookie.php` without actually touching it. This happens because this time when we use the Cookie class [Kohana::autoload] looks for `classes/cookie.php` and finds the file in `application` and includes that one, instead of the one in system.
## Example: changing [Cookie] settings
If you are using the [Cookie](cookies) class, and want to change a setting, you should do so using transparent extension, rather than editing the file in the system folder. If you edit it directly, and in the future you upgrade your Kohana version by replacing the system folder, your changes will be reverted and your cookies will probably be invalid. Instead, create a cookie.php file either in `application/classes/cookie.php` or a module (`MODPATH/<modulename>/classes/cookie.php`).
class Cookie extends Kohana_Cookie {
// Set a new salt
public $salt = "some new better random salt phrase";
// Don't allow javascript access to cookies
public $httponly = TRUE;
}
## Example: TODO: an example
Just post the code and breif descript of what function it adds, you don't have to do the "How it works" like above.
## Example: TODO: something else
Just post the code and breif descript of what function it adds, you don't have to do the "How it works" like above.
## More examples
TODO: Provide some links to modules on github, etc that have examples of transparent extension in use.
## Multiple Levels of Extension
If you are extending a Kohana class in a module, you should maintain transparent extensions. In other words, do not include any variables or function in the "base" class (eg. Cookie). Instead make your own namespaced class, and have the "base" class extend that one. With our Encrypted cookie example we can create `MODPATH/mymod/encrypted/cookie.php`:
class Encrypted_Cookie extends Kohana_Cookie {
// Use the same encrypt() and decrypt() methods as above
}
And create `MODPATH/mymod/cookie.php`:
class Cookie extends Encrypted_Cookie {}
This will still allow users to add their own extension to [Cookie] while leaving your extensions intact. To do that they would make a cookie class that extends `Encrypted_Cookie` (rather than `Kohana_Cookie`) in their application folder.

View File

@@ -0,0 +1,83 @@
# Cascading Filesystem
The Kohana filesystem is a heirarchy of similar directory structures that cascade. The heirarchy in Kohana (used when a file is loaded by [Kohana::find_file]) is in the following order:
1. **Application Path**
Defined as `APPPATH` in `index.php`. The default value is `application`.
2. **Module Paths**
This is set as an associative array using [Kohana::modules] in `APPPATH/bootstrap.php`. Each of the values of the array will be searched **in the order that the modules are added**.
3. **System Path**
Defined as `SYSPATH` in `index.php`. The default value is `system`. All of the main or "core" files and classes are defined here.
Files that are in directories higher up the include path order take precedence over files of the same name lower down the order, which makes it is possible to overload any file by placing a file with the same name in a "higher" directory:
![Cascading Filesystem Infographic](cascading_filesystem.png)
This image is only shows certain files, but we can use it to illustrate some examples of the cascading filesystem:
* If Kohana catches an error, it would display the `kohana/error.php` view, So it would call `Kohana::find_file('views', 'kohana/error')`. This would return `application/views/kohana/error.php` because it takes precidence over `system/views/kohana/error.php`. By doing this we can change the error view without editing the system folder.
* If we used `View::factory('welcome')` it would call `Kohana::find_file('views','welcome')` which would return `application/views/welcome.php` because it takes precidence over `modules/common/views/welcome.php`. By doing this, you can overwrite things in a module without editing the modules files.
* If use the Cookie class, [Kohana::auto_load] will call `Kohana::find_file('classes', 'cookie')` which will return `application/classes/cookie.php`. Assuming Cookie extends Kohana_Cookie, the autoloader would then call `Kohana::find_file('classes','kohana/cookie')` which will return `system/classes/kohana/cookie.php` because that file does not exist anywhere higher in the cascade. This is an example of [transparent extension](extension).
* If you used `View::factory('user')` it would call `Kohana::find_file('views','user')` which would return `modules/common/views/user.php`.
* If we wanted to change something in `config/database.php` we could copy the file to `application/config/database.php` and make the changes there. Keep in mind that [config files are merged](files/config#merge) rather than overwritten by the cascade.
## Types of Files
The top level directories of the application, module, and system paths have the following default directories:
classes/
: All classes that you want to [autoload](autoloading) should be stored here. This includes [controllers](mvc/controllers), [models](mvc/models), and all other classes. All classes must follow the [class naming conventions](conventions#class-names-and-file-location).
config/
: Configuration files return an associative array of options that can be loaded using [Kohana::config]. Config files are merged rather than overwritten by the cascade. See [config files](files/config) for more information.
i18n/
: Translation files return an associative array of strings. Translation is done using the `__()` method. To translate "Hello, world!" into Spanish, you would call `__('Hello, world!')` with [I18n::$lang] set to "es-es". I18n files are merged rather than overwritten by the cascade. See [I18n files](files/i18n) for more information.
messages/
: Message files return an associative array of strings that can be loaded using [Kohana::message]. Messages and i18n files differ in that messages are not translated, but always written in the default language and referred to by a single key. Message files are merged rather than overwritten by the cascade. See [message files](files/messages) for more information.
views/
: Views are plain PHP files which are used to generate HTML or other output. The view file is loaded into a [View] object and assigned variables, which it then converts into an HTML fragment. Multiple views can be used within each other. See [views](mvc/views) for more information.
*other*
: You can include any other folders in your cascading filesystem. Examples include, but are not limited to, `guide`, `vendor`, `media`, whatever you want. For example, to find `media/logo.png` in the cascading filesystem you would call `Kohana::find_file('media','logo','png')`.
## Finding Files
The path to any file within the filesystem can be found by calling [Kohana::find_file]:
// Find the full path to "classes/cookie.php"
$path = Kohana::find_file('classes', 'cookie');
// Find the full path to "views/user/login.php"
$path = Kohana::find_file('views', 'user/login');
If the file doesn't have a `.php` extension, pass the extension as the third param.
// Find the full path to "guide/menu.md"
$path = Kohana::find_file('guide', 'menu', 'md');
// If $name is "2000-01-01-first-post" this would look for "posts/2000-01-01-first-post.textile"
$path = Kohana::find_file('posts', $name, '.textile');
## Vendor Extensions
We call extensions or external libraries that are not specific to Kohana "vendor" extensions, and they go in the vendor folder, either in application or in a module. Because these libraries do not follow Kohana's file naming conventions, they cannot be autoloaded by Kohana, so you will have to manually included them. Some examples of vendor libraries are [Markdown](http://daringfireball.net/projects/markdown/), [DOMPDF](http://code.google.com/p/dompdf), [Mustache](http://github.com/bobthecow/mustache.php) and [Swiftmailer](http://swiftmailer.org/).
For example, if you wanted to use [DOMPDF](http://code.google.com/p/dompdf), you would copy it to `application/vendor/dompdf` and include the DOMPDF autoloading class. It can be useful to do this in a controller's before method, as part of a module's init.php, or the contstructor of a singleton class.
require Kohana::find_file('vendor', 'dompdf/dompdf/dompdf_config','inc');
Now you can use DOMPDF without loading any more files:
$pdf = new DOMPDF;
[!!] If you want to convert views into PDFs using DOMPDF, try the [PDFView](http://github.com/shadowhand/pdfview) module.

View File

@@ -0,0 +1,41 @@
# Classes
TODO: Brief intro to classes.
[Models](mvc/models) and [Controllers](mvc/controllers) are classes as well, but are treated slightly differently by Kohana. Read their respective pages to learn more.
## Helper or Library?
Kohana 3 does not differentiate between "helper" classes and "library" classes like in previous versions. They are all placed in the `classes/` folder and follow the same conventions. The distinction is that in general, a "helper" class is used statically, (for examples see the [helpers included in Kohana](helpers)), and library classes are typically instanciated and used as objects (like the [Database query builders](../database/query/builder)). The distinction is not black and white, and is irrelevant anyways, since they are treated the same by Kohana.
## Creating a class
To create a new class, simply place a file in the `classes/` directory at any point in the [Cascading Filesystem](files), that follows the [Class naming conventions](conventions#class-names-and-file-location). For example, lets create a `Foobar` class.
// classes/foobar.php
class Foobar {
static function magic() {
// Does something
}
}
We can now call `Foobar::magic()` any where and Kohana will [autoload](autoloading) the file for us.
We can also put classes in subdirectories.
// classes/professor/baxter.php
class Professor_Baxter {
static function teach() {
// Does something
}
}
We could now call `Professor_Baxter::teach()` any where we want.
For examples of how to create and use classes, simply look at the 'classes' folder in `system` or any module.
## Namespacing your classes
TODO: Discuss namespacing to provide transparent extension functionality in your own classes/modules.

View File

@@ -0,0 +1,95 @@
# Config Files
Configuration files are used to store any kind of configuration needed for a module, class, or anything else you want. They are plain PHP files, stored in the `config/` directory, which return an associative array:
<?php defined('SYSPATH') or die('No direct script access.');
return array(
'setting' => 'value',
'options' => array(
'foo' => 'bar',
),
);
If the above configuration file was called `myconf.php`, you could access it using:
$config = Kohana::config('myconf');
$options = $config['options'];
[Kohana::config] also provides a shortcut for accessing individual keys from configuration arrays using "dot paths" similar to [Arr::path].
Get the "options" array:
$options = Kohana::config('myconf.options');
Get the "foo" key from the "options" array:
$foo = Kohana::config('myconf.options.foo');
Configuration arrays can also be accessed as objects, if you prefer that method:
$options = Kohana::config('myconf')->options;
Please note that you can only access the top level of keys as object properties, all child keys must be accessed using standard array syntax:
$foo = Kohana::config('myconf')->options['foo'];
## Merge
Configuration files are slightly different from most other files within the [cascading filesystem](files) in that they are **merged** rather than overloaded. This means that all configuration files with the same file path are combined to produce the final configuration. The end result is that you can overload *individual* settings rather than duplicating an entire file.
For example, if we wanted to change something in some file
[TODO]
TODO exmaple of adding something to inflector
## Creating your own config files
Let's say we want a config file to store and easily change things like the title of a website, or the google analytics code. We would create a config file, let's call it `site.php`:
// config/site.php
<?php defined('SYSPATH') or die('No direct script access.');
return array(
'title' => 'Our Shiny Website',
'analytics' => FALSE, // analytics code goes here, set to FALSE to disable
);
We could now call `Kohana::config('site.title')` to get the site name, and `Kohana::config('site.analytics')` to get the analytics code.
Let's say we want an archive of versions of some software. We could use config files to store each version, and include links to download, documentation, and issue tracking.
// config/versions.php
<?php defined('SYSPATH') or die('No direct script access.');
return array(
'1.0.0' => array(
'codename' => 'Frog',
'download' => 'files/ourapp-1.0.0.tar.gz',
'documentation' => 'docs/1.0.0',
'released' => '06/05/2009',
'issues' => 'link/to/bug/tracker',
),
'1.1.0' => array(
'codename' => 'Lizard',
'download' => 'files/ourapp-1.1.0.tar.gz',
'documentation' => 'docs/1.1.0',
'released' => '10/15/2009',
'issues' => 'link/to/bug/tracker',
),
/// ... etc ...
);
You could then do the following:
// In your controller
$view->versions = Kohana::config('versions');
// In your view:
foreach ($versions as $version)
{
// echo some html to display each version
}

View File

@@ -0,0 +1 @@
Discuss the format of i18n files, and how to use them.

View File

@@ -0,0 +1,5 @@
<http://kohanaframework.org/guide/using.messages>
Add that message files can be in subfolders, and you can use dot notation to retreive an array path: `Kohana::message('folder/subfolder/file','array.subarray.key')`
Also reinforce that messages are merged by the cascade, not overwritten.

View File

@@ -0,0 +1,26 @@
# Request Flow
Every application follows the same flow:
1. Application starts from `index.php`.
1. The application, module, and system paths are set. (`APPPATH`, `MODPATH`, and `SYSPATH`)
2. Error reporting levels are set.
3. Install file is loaded, if it exists.
4. The [Kohana] class is loaded.
5. The bootstrap file, `APPPATH/bootstrap.php`, is included.
2. Once we are in `bootstrap.php`:
7. [Kohana::init] is called, which sets up error handling, caching, and logging.
8. [Kohana_Config] readers and [Kohana_Log] writers are attached.
9. [Kohana::modules] is called to enable additional modules.
* Module paths are added to the [cascading filesystem](files).
* Includes each module's `init.php` file, if it exists.
* The `init.php` file can perform additional environment setup, including adding routes.
10. [Route::set] is called multiple times to define the [application routes](routing).
11. [Request::instance] is called to start processing the request.
1. Checks each route that has been set until a match is found.
2. Creates the controller instance and passes the request to it.
3. Calls the [Controller::before] method.
4. Calls the controller action, which generates the request response.
5. Calls the [Controller::after] method.
* The above 5 steps can be repeated multiple times when using [HMVC sub-requests](requests).
12. The main [Request] response is displayed

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