Imported Upstream version 0.6.24+dfsg1

This commit is contained in:
Mario Fetka
2017-05-20 15:26:21 +02:00
commit 32a360eca6
705 changed files with 87250 additions and 0 deletions

View File

@@ -0,0 +1,208 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* Provides a driver-based interface for finding, creating, and deleting cached
* resources. Caches are identified by a unique string. Tagging of caches is
* also supported, and caches can be found and deleted by id or tag.
*
* $Id: Cache.php 4321 2009-05-04 01:39:44Z kiall $
*
* @package Cache
* @author Kohana Team
* @copyright (c) 2007-2008 Kohana Team
* @license http://kohanaphp.com/license.html
*/
class Cache_Core {
protected static $instances = array();
// For garbage collection
protected static $loaded;
// Configuration
protected $config;
// Driver object
protected $driver;
/**
* Returns a singleton instance of Cache.
*
* @param string configuration
* @return Cache_Core
*/
public static function & instance($config = FALSE)
{
if ( ! isset(Cache::$instances[$config]))
{
// Create a new instance
Cache::$instances[$config] = new Cache($config);
}
return Cache::$instances[$config];
}
/**
* Loads the configured driver and validates it.
*
* @param array|string custom configuration or config group name
* @return void
*/
public function __construct($config = FALSE)
{
if (is_string($config))
{
$name = $config;
// Test the config group name
if (($config = Kohana::config('cache.'.$config)) === NULL)
throw new Kohana_Exception('cache.undefined_group', $name);
}
if (is_array($config))
{
// Append the default configuration options
$config += Kohana::config('cache.default');
}
else
{
// Load the default group
$config = Kohana::config('cache.default');
}
// Cache the config in the object
$this->config = $config;
// Set driver name
$driver = 'Cache_'.ucfirst($this->config['driver']).'_Driver';
// Load the driver
if ( ! Kohana::auto_load($driver))
throw new Kohana_Exception('core.driver_not_found', $this->config['driver'], get_class($this));
// Initialize the driver
$this->driver = new $driver($this->config['params']);
// Validate the driver
if ( ! ($this->driver instanceof Cache_Driver))
throw new Kohana_Exception('core.driver_implements', $this->config['driver'], get_class($this), 'Cache_Driver');
Kohana::log('debug', 'Cache Library initialized');
if (Cache::$loaded !== TRUE)
{
$this->config['requests'] = (int) $this->config['requests'];
if ($this->config['requests'] > 0 AND mt_rand(1, $this->config['requests']) === 1)
{
// Do garbage collection
$this->driver->delete_expired();
Kohana::log('debug', 'Cache: Expired caches deleted.');
}
// Cache has been loaded once
Cache::$loaded = TRUE;
}
}
/**
* Fetches a cache by id. NULL is returned when a cache item is not found.
*
* @param string cache id
* @return mixed cached data or NULL
*/
public function get($id)
{
// Sanitize the ID
$id = $this->sanitize_id($id);
return $this->driver->get($id);
}
/**
* Fetches all of the caches for a given tag. An empty array will be
* returned when no matching caches are found.
*
* @param string cache tag
* @return array all cache items matching the tag
*/
public function find($tag)
{
return $this->driver->find($tag);
}
/**
* Set a cache item by id. Tags may also be added and a custom lifetime
* can be set. Non-string data is automatically serialized.
*
* @param string unique cache id
* @param mixed data to cache
* @param array|string tags for this item
* @param integer number of seconds until the cache expires
* @return boolean
*/
function set($id, $data, $tags = NULL, $lifetime = NULL)
{
if (is_resource($data))
throw new Kohana_Exception('cache.resources');
// Sanitize the ID
$id = $this->sanitize_id($id);
if ($lifetime === NULL)
{
// Get the default lifetime
$lifetime = $this->config['lifetime'];
}
return $this->driver->set($id, $data, (array) $tags, $lifetime);
}
/**
* Delete a cache item by id.
*
* @param string cache id
* @return boolean
*/
public function delete($id)
{
// Sanitize the ID
$id = $this->sanitize_id($id);
return $this->driver->delete($id);
}
/**
* Delete all cache items with a given tag.
*
* @param string cache tag name
* @return boolean
*/
public function delete_tag($tag)
{
return $this->driver->delete($tag, TRUE);
}
/**
* Delete ALL cache items items.
*
* @return boolean
*/
public function delete_all()
{
return $this->driver->delete(TRUE);
}
/**
* Replaces troublesome characters with underscores.
*
* @param string cache id
* @return string
*/
protected function sanitize_id($id)
{
// Change slashes and spaces to underscores
return str_replace(array('/', '\\', ' '), '_', $id);
}
} // End Cache

View File

@@ -0,0 +1,362 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* Calendar creation library.
*
* $Id: Calendar.php 3769 2008-12-15 00:48:56Z zombor $
*
* @package Calendar
* @author Kohana Team
* @copyright (c) 2007-2008 Kohana Team
* @license http://kohanaphp.com/license.html
*/
class Calendar_Core extends Event_Subject {
// Start the calendar on Sunday by default
public static $start_monday = FALSE;
// Month and year to use for calendaring
protected $month;
protected $year;
// Week starts on Sunday
protected $week_start = 0;
// Observed data
protected $observed_data;
/**
* Returns an array of the names of the days, using the current locale.
*
* @param integer left of day names
* @return array
*/
public static function days($length = TRUE)
{
// strftime day format
$format = ($length > 3) ? '%A' : '%a';
// Days of the week
$days = array('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday');
if (Calendar::$start_monday === TRUE)
{
// Push Sunday to the end of the days
array_push($days, array_shift($days));
}
if (strpos(Kohana::config('locale.language.0'), 'en') !== 0)
{
// This is a bit awkward, but it works properly and is reliable
foreach ($days as $i => $day)
{
// Convert the English names to i18n names
$days[$i] = strftime($format, strtotime($day));
}
}
if (is_int($length) OR ctype_digit($length))
{
foreach ($days as $i => $day)
{
// Shorten the days to the expected length
$days[$i] = utf8::substr($day, 0, $length);
}
}
return $days;
}
/**
* Create a new Calendar instance. A month and year can be specified.
* By default, the current month and year are used.
*
* @param integer month number
* @param integer year number
* @return object
*/
public static function factory($month = NULL, $year = NULL)
{
return new Calendar($month, $year);
}
/**
* Create a new Calendar instance. A month and year can be specified.
* By default, the current month and year are used.
*
* @param integer month number
* @param integer year number
* @return void
*/
public function __construct($month = NULL, $year = NULL)
{
empty($month) and $month = date('n'); // Current month
empty($year) and $year = date('Y'); // Current year
// Set the month and year
$this->month = (int) $month;
$this->year = (int) $year;
if (Calendar::$start_monday === TRUE)
{
// Week starts on Monday
$this->week_start = 1;
}
}
/**
* Allows fetching the current month and year.
*
* @param string key to get
* @return mixed
*/
public function __get($key)
{
if ($key === 'month' OR $key === 'year')
{
return $this->$key;
}
}
/**
* Calendar_Event factory method.
*
* @param string unique name for the event
* @return object Calendar_Event
*/
public function event($name = NULL)
{
return new Calendar_Event($this);
}
/**
* Calendar_Event factory method.
*
* @chainable
* @param string standard event type
* @return object
*/
public function standard($name)
{
switch ($name)
{
case 'today':
// Add an event for the current day
$this->attach($this->event()->condition('timestamp', strtotime('today'))->add_class('today'));
break;
case 'prev-next':
// Add an event for padding days
$this->attach($this->event()->condition('current', FALSE)->add_class('prev-next'));
break;
case 'holidays':
// Base event
$event = $this->event()->condition('current', TRUE)->add_class('holiday');
// Attach New Years
$holiday = clone $event;
$this->attach($holiday->condition('month', 1)->condition('day', 1));
// Attach Valentine's Day
$holiday = clone $event;
$this->attach($holiday->condition('month', 2)->condition('day', 14));
// Attach St. Patrick's Day
$holiday = clone $event;
$this->attach($holiday->condition('month', 3)->condition('day', 17));
// Attach Easter
$holiday = clone $event;
$this->attach($holiday->condition('easter', TRUE));
// Attach Memorial Day
$holiday = clone $event;
$this->attach($holiday->condition('month', 5)->condition('day_of_week', 1)->condition('last_occurrence', TRUE));
// Attach Independance Day
$holiday = clone $event;
$this->attach($holiday->condition('month', 7)->condition('day', 4));
// Attach Labor Day
$holiday = clone $event;
$this->attach($holiday->condition('month', 9)->condition('day_of_week', 1)->condition('occurrence', 1));
// Attach Halloween
$holiday = clone $event;
$this->attach($holiday->condition('month', 10)->condition('day', 31));
// Attach Thanksgiving
$holiday = clone $event;
$this->attach($holiday->condition('month', 11)->condition('day_of_week', 4)->condition('occurrence', 4));
// Attach Christmas
$holiday = clone $event;
$this->attach($holiday->condition('month', 12)->condition('day', 25));
break;
case 'weekends':
// Weekend events
$this->attach($this->event()->condition('weekend', TRUE)->add_class('weekend'));
break;
}
return $this;
}
/**
* Returns an array for use with a view. The array contains an array for
* each week. Each week contains 7 arrays, with a day number and status:
* TRUE if the day is in the month, FALSE if it is padding.
*
* @return array
*/
public function weeks()
{
// First day of the month as a timestamp
$first = mktime(1, 0, 0, $this->month, 1, $this->year);
// Total number of days in this month
$total = (int) date('t', $first);
// Last day of the month as a timestamp
$last = mktime(1, 0, 0, $this->month, $total, $this->year);
// Make the month and week empty arrays
$month = $week = array();
// Number of days added. When this reaches 7, start a new week
$days = 0;
$week_number = 1;
if (($w = (int) date('w', $first) - $this->week_start) < 0)
{
$w = 6;
}
if ($w > 0)
{
// Number of days in the previous month
$n = (int) date('t', mktime(1, 0, 0, $this->month - 1, 1, $this->year));
// i = number of day, t = number of days to pad
for ($i = $n - $w + 1, $t = $w; $t > 0; $t--, $i++)
{
// Notify the listeners
$this->notify(array($this->month - 1, $i, $this->year, $week_number, FALSE));
// Add previous month padding days
$week[] = array($i, FALSE, $this->observed_data);
$days++;
}
}
// i = number of day
for ($i = 1; $i <= $total; $i++)
{
if ($days % 7 === 0)
{
// Start a new week
$month[] = $week;
$week = array();
$week_number++;
}
// Notify the listeners
$this->notify(array($this->month, $i, $this->year, $week_number, TRUE));
// Add days to this month
$week[] = array($i, TRUE, $this->observed_data);
$days++;
}
if (($w = (int) date('w', $last) - $this->week_start) < 0)
{
$w = 6;
}
if ($w >= 0)
{
// i = number of day, t = number of days to pad
for ($i = 1, $t = 6 - $w; $t > 0; $t--, $i++)
{
// Notify the listeners
$this->notify(array($this->month + 1, $i, $this->year, $week_number, FALSE));
// Add next month padding days
$week[] = array($i, FALSE, $this->observed_data);
}
}
if ( ! empty($week))
{
// Append the remaining days
$month[] = $week;
}
return $month;
}
/**
* Adds new data from an observer. All event data contains and array of CSS
* classes and an array of output messages.
*
* @param array observer data.
* @return void
*/
public function add_data(array $data)
{
// Add new classes
$this->observed_data['classes'] += $data['classes'];
if ( ! empty($data['output']))
{
// Only add output if it's not empty
$this->observed_data['output'][] = $data['output'];
}
}
/**
* Resets the observed data and sends a notify to all attached events.
*
* @param array UNIX timestamp
* @return void
*/
public function notify($data)
{
// Reset observed data
$this->observed_data = array
(
'classes' => array(),
'output' => array(),
);
// Send a notify
parent::notify($data);
}
/**
* Convert the calendar to HTML using the kohana_calendar view.
*
* @return string
*/
public function render()
{
$view = new View('kohana_calendar', array
(
'month' => $this->month,
'year' => $this->year,
'weeks' => $this->weeks(),
));
return $view->render();
}
/**
* Magically convert this object to a string, the rendered calendar.
*
* @return string
*/
public function __toString()
{
return $this->render();
}
} // End Calendar

View File

@@ -0,0 +1,307 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* Calendar event observer class.
*
* $Id: Calendar_Event.php 4129 2009-03-27 17:47:03Z zombor $
*
* @package Calendar
* @author Kohana Team
* @copyright (c) 2007-2008 Kohana Team
* @license http://kohanaphp.com/license.html
*/
class Calendar_Event_Core extends Event_Observer {
// Boolean conditions
protected $booleans = array
(
'current',
'weekend',
'first_day',
'last_day',
'last_occurrence',
'easter',
);
// Rendering conditions
protected $conditions = array();
// Cell classes
protected $classes = array();
// Cell output
protected $output = '';
/**
* Adds a condition to the event. The condition can be one of the following:
*
* timestamp - UNIX timestamp
* day - day number (1-31)
* week - week number (1-5)
* month - month number (1-12)
* year - year number (4 digits)
* day_of_week - day of week (1-7)
* current - active month (boolean) (only show data for the month being rendered)
* weekend - weekend day (boolean)
* first_day - first day of month (boolean)
* last_day - last day of month (boolean)
* occurrence - occurrence of the week day (1-5) (use with "day_of_week")
* last_occurrence - last occurrence of week day (boolean) (use with "day_of_week")
* easter - Easter day (boolean)
* callback - callback test (boolean)
*
* To unset a condition, call condition with a value of NULL.
*
* @chainable
* @param string condition key
* @param mixed condition value
* @return object
*/
public function condition($key, $value)
{
if ($value === NULL)
{
unset($this->conditions[$key]);
}
else
{
if ($key === 'callback')
{
// Do nothing
}
elseif (in_array($key, $this->booleans))
{
// Make the value boolean
$value = (bool) $value;
}
else
{
// Make the value an int
$value = (int) $value;
}
$this->conditions[$key] = $value;
}
return $this;
}
/**
* Add a CSS class for this event. This can be called multiple times.
*
* @chainable
* @param string CSS class name
* @return object
*/
public function add_class($class)
{
$this->classes[$class] = $class;
return $this;
}
/**
* Remove a CSS class for this event. This can be called multiple times.
*
* @chainable
* @param string CSS class name
* @return object
*/
public function remove_class($class)
{
unset($this->classes[$class]);
return $this;
}
/**
* Set HTML output for this event.
*
* @chainable
* @param string HTML output
* @return object
*/
public function output($str)
{
$this->output = $str;
return $this;
}
/**
* Add a CSS class for this event. This can be called multiple times.
*
* @chainable
* @param string CSS class name
* @return object
*/
public function notify($data)
{
// Split the date and current status
list ($month, $day, $year, $week, $current) = $data;
// Get a timestamp for the day
$timestamp = mktime(0, 0, 0, $month, $day, $year);
// Date conditionals
$condition = array
(
'timestamp' => (int) $timestamp,
'day' => (int) date('j', $timestamp),
'week' => (int) $week,
'month' => (int) date('n', $timestamp),
'year' => (int) date('Y', $timestamp),
'day_of_week' => (int) date('w', $timestamp),
'current' => (bool) $current,
);
// Tested conditions
$tested = array();
foreach ($condition as $key => $value)
{
// Timestamps need to be handled carefully
if($key === 'timestamp' AND isset($this->conditions['timestamp']))
{
// This adds 23 hours, 59 minutes and 59 seconds to today's timestamp, as 24 hours
// is classed as a new day
$next_day = $timestamp + 86399;
if($this->conditions['timestamp'] < $timestamp OR $this->conditions['timestamp'] > $next_day)
return FALSE;
}
// Test basic conditions first
elseif (isset($this->conditions[$key]) AND $this->conditions[$key] !== $value)
return FALSE;
// Condition has been tested
$tested[$key] = TRUE;
}
if (isset($this->conditions['weekend']))
{
// Weekday vs Weekend
$condition['weekend'] = ($condition['day_of_week'] === 0 OR $condition['day_of_week'] === 6);
}
if (isset($this->conditions['first_day']))
{
// First day of month
$condition['first_day'] = ($condition['day'] === 1);
}
if (isset($this->conditions['last_day']))
{
// Last day of month
$condition['last_day'] = ($condition['day'] === (int) date('t', $timestamp));
}
if (isset($this->conditions['occurrence']))
{
// Get the occurance of the current day
$condition['occurrence'] = $this->day_occurrence($timestamp);
}
if (isset($this->conditions['last_occurrence']))
{
// Test if the next occurance of this date is next month
$condition['last_occurrence'] = ((int) date('n', $timestamp + 604800) !== $condition['month']);
}
if (isset($this->conditions['easter']))
{
if ($condition['month'] === 3 OR $condition['month'] === 4)
{
// This algorithm is from Practical Astronomy With Your Calculator, 2nd Edition by Peter
// Duffett-Smith. It was originally from Butcher's Ecclesiastical Calendar, published in
// 1876. This algorithm has also been published in the 1922 book General Astronomy by
// Spencer Jones; in The Journal of the British Astronomical Association (Vol.88, page
// 91, December 1977); and in Astronomical Algorithms (1991) by Jean Meeus.
$a = $condition['year'] % 19;
$b = (int) ($condition['year'] / 100);
$c = $condition['year'] % 100;
$d = (int) ($b / 4);
$e = $b % 4;
$f = (int) (($b + 8) / 25);
$g = (int) (($b - $f + 1) / 3);
$h = (19 * $a + $b - $d - $g + 15) % 30;
$i = (int) ($c / 4);
$k = $c % 4;
$l = (32 + 2 * $e + 2 * $i - $h - $k) % 7;
$m = (int) (($a + 11 * $h + 22 * $l) / 451);
$p = ($h + $l - 7 * $m + 114) % 31;
$month = (int) (($h + $l - 7 * $m + 114) / 31);
$day = $p + 1;
$condition['easter'] = ($condition['month'] === $month AND $condition['day'] === $day);
}
else
{
// Easter can only happen in March or April
$condition['easter'] = FALSE;
}
}
if (isset($this->conditions['callback']))
{
// Use a callback to determine validity
$condition['callback'] = call_user_func($this->conditions['callback'], $condition, $this);
}
$conditions = array_diff_key($this->conditions, $tested);
foreach ($conditions as $key => $value)
{
if ($key === 'callback')
{
// Callbacks are tested on a TRUE/FALSE basis
$value = TRUE;
}
// Test advanced conditions
if ($condition[$key] !== $value)
return FALSE;
}
$this->caller->add_data(array
(
'classes' => $this->classes,
'output' => $this->output,
));
}
/**
* Find the week day occurrence for a specific timestamp. The occurrence is
* relative to the current month. For example, the second Saturday of any
* given month will return "2" as the occurrence. This is used in combination
* with the "occurrence" condition.
*
* @param integer UNIX timestamp
* @return integer
*/
protected function day_occurrence($timestamp)
{
// Get the current month for the timestamp
$month = date('m', $timestamp);
// Default occurrence is one
$occurrence = 1;
// Reduce the timestamp by one week for each loop. This has the added
// benefit of preventing an infinite loop.
while ($timestamp -= 604800)
{
if (date('m', $timestamp) !== $month)
{
// Once the timestamp has gone into the previous month, the
// proper occurrence has been found.
return $occurrence;
}
// Increment the occurrence
$occurrence++;
}
}
} // End Calendar Event

View File

@@ -0,0 +1,279 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* Captcha library.
*
* $Id: Captcha.php 4072 2009-03-13 17:20:38Z jheathco $
*
* @package Captcha
* @author Kohana Team
* @copyright (c) 2007-2008 Kohana Team
* @license http://kohanaphp.com/license.html
*/
class Captcha_Core {
// Captcha singleton
protected static $instance;
// Style-dependent Captcha driver
protected $driver;
// Config values
public static $config = array
(
'style' => 'basic',
'width' => 150,
'height' => 50,
'complexity' => 4,
'background' => '',
'fontpath' => '',
'fonts' => array(),
'promote' => FALSE,
);
/**
* Singleton instance of Captcha.
*
* @return object
*/
public static function instance()
{
// Create the instance if it does not exist
empty(Captcha::$instance) and new Captcha;
return Captcha::$instance;
}
/**
* Constructs and returns a new Captcha object.
*
* @param string config group name
* @return object
*/
public static function factory($group = NULL)
{
return new Captcha($group);
}
/**
* Constructs a new Captcha object.
*
* @throws Kohana_Exception
* @param string config group name
* @return void
*/
public function __construct($group = NULL)
{
// Create a singleton instance once
empty(Captcha::$instance) and Captcha::$instance = $this;
// No config group name given
if ( ! is_string($group))
{
$group = 'default';
}
// Load and validate config group
if ( ! is_array($config = Kohana::config('captcha.'.$group)))
throw new Kohana_Exception('captcha.undefined_group', $group);
// All captcha config groups inherit default config group
if ($group !== 'default')
{
// Load and validate default config group
if ( ! is_array($default = Kohana::config('captcha.default')))
throw new Kohana_Exception('captcha.undefined_group', 'default');
// Merge config group with default config group
$config += $default;
}
// Assign config values to the object
foreach ($config as $key => $value)
{
if (array_key_exists($key, Captcha::$config))
{
Captcha::$config[$key] = $value;
}
}
// Store the config group name as well, so the drivers can access it
Captcha::$config['group'] = $group;
// If using a background image, check if it exists
if ( ! empty($config['background']))
{
Captcha::$config['background'] = str_replace('\\', '/', realpath($config['background']));
if ( ! is_file(Captcha::$config['background']))
throw new Kohana_Exception('captcha.file_not_found', Captcha::$config['background']);
}
// If using any fonts, check if they exist
if ( ! empty($config['fonts']))
{
Captcha::$config['fontpath'] = str_replace('\\', '/', realpath($config['fontpath'])).'/';
foreach ($config['fonts'] as $font)
{
if ( ! is_file(Captcha::$config['fontpath'].$font))
throw new Kohana_Exception('captcha.file_not_found', Captcha::$config['fontpath'].$font);
}
}
// Set driver name
$driver = 'Captcha_'.ucfirst($config['style']).'_Driver';
// Load the driver
if ( ! Kohana::auto_load($driver))
throw new Kohana_Exception('core.driver_not_found', $config['style'], get_class($this));
// Initialize the driver
$this->driver = new $driver;
// Validate the driver
if ( ! ($this->driver instanceof Captcha_Driver))
throw new Kohana_Exception('core.driver_implements', $config['style'], get_class($this), 'Captcha_Driver');
Kohana::log('debug', 'Captcha Library initialized');
}
/**
* Validates a Captcha response and updates response counter.
*
* @param string captcha response
* @return boolean
*/
public static function valid($response)
{
// Maximum one count per page load
static $counted;
// User has been promoted, always TRUE and don't count anymore
if (Captcha::instance()->promoted())
return TRUE;
// Challenge result
$result = (bool) Captcha::instance()->driver->valid($response);
// Increment response counter
if ($counted !== TRUE)
{
$counted = TRUE;
// Valid response
if ($result === TRUE)
{
Captcha::instance()->valid_count(Session::instance()->get('captcha_valid_count') + 1);
}
// Invalid response
else
{
Captcha::instance()->invalid_count(Session::instance()->get('captcha_invalid_count') + 1);
}
}
return $result;
}
/**
* Gets or sets the number of valid Captcha responses for this session.
*
* @param integer new counter value
* @param boolean trigger invalid counter (for internal use only)
* @return integer counter value
*/
public function valid_count($new_count = NULL, $invalid = FALSE)
{
// Pick the right session to use
$session = ($invalid === TRUE) ? 'captcha_invalid_count' : 'captcha_valid_count';
// Update counter
if ($new_count !== NULL)
{
$new_count = (int) $new_count;
// Reset counter = delete session
if ($new_count < 1)
{
Session::instance()->delete($session);
}
// Set counter to new value
else
{
Session::instance()->set($session, (int) $new_count);
}
// Return new count
return (int) $new_count;
}
// Return current count
return (int) Session::instance()->get($session);
}
/**
* Gets or sets the number of invalid Captcha responses for this session.
*
* @param integer new counter value
* @return integer counter value
*/
public function invalid_count($new_count = NULL)
{
return $this->valid_count($new_count, TRUE);
}
/**
* Resets the Captcha response counters and removes the count sessions.
*
* @return void
*/
public function reset_count()
{
$this->valid_count(0);
$this->valid_count(0, TRUE);
}
/**
* Checks whether user has been promoted after having given enough valid responses.
*
* @param integer valid response count threshold
* @return boolean
*/
public function promoted($threshold = NULL)
{
// Promotion has been disabled
if (Captcha::$config['promote'] === FALSE)
return FALSE;
// Use the config threshold
if ($threshold === NULL)
{
$threshold = Captcha::$config['promote'];
}
// Compare the valid response count to the threshold
return ($this->valid_count() >= $threshold);
}
/**
* Returns or outputs the Captcha challenge.
*
* @param boolean TRUE to output html, e.g. <img src="#" />
* @return mixed html string or void
*/
public function render($html = TRUE)
{
return $this->driver->render($html);
}
/**
* Magically outputs the Captcha challenge.
*
* @return mixed
*/
public function __toString()
{
return $this->render();
}
} // End Captcha Class

View File

@@ -0,0 +1,86 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* Kohana Controller class. The controller class must be extended to work
* properly, so this class is defined as abstract.
*
* $Id: Controller.php 4365 2009-05-27 21:09:27Z samsoir $
*
* @package Core
* @author Kohana Team
* @copyright (c) 2007-2008 Kohana Team
* @license http://kohanaphp.com/license.html
*/
abstract class Controller_Core {
// Allow all controllers to run in production by default
const ALLOW_PRODUCTION = TRUE;
/**
* Loads URI, and Input into this controller.
*
* @return void
*/
public function __construct()
{
if (Kohana::$instance == NULL)
{
// Set the instance to the first controller loaded
Kohana::$instance = $this;
}
// URI should always be available
$this->uri = URI::instance();
// Input should always be available
$this->input = Input::instance();
}
/**
* Handles methods that do not exist.
*
* @param string method name
* @param array arguments
* @return void
*/
public function __call($method, $args)
{
// Default to showing a 404 page
Event::run('system.404');
}
/**
* Includes a View within the controller scope.
*
* @param string view filename
* @param array array of view variables
* @return string
*/
public function _kohana_load_view($kohana_view_filename, $kohana_input_data)
{
if ($kohana_view_filename == '')
return;
// Buffering on
ob_start();
// Import the view variables to local namespace
extract($kohana_input_data, EXTR_SKIP);
// Views are straight HTML pages with embedded PHP, so importing them
// this way insures that $this can be accessed as if the user was in
// the controller, which gives the easiest access to libraries in views
try
{
include $kohana_view_filename;
}
catch (Exception $e)
{
ob_end_clean();
throw $e;
}
// Fetch the output and close the buffer
return ob_get_clean();
}
} // End Controller Class

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,26 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* Database expression class to allow for explicit joins and where expressions.
*
* $Id: Database_Expression.php 4037 2009-03-04 23:35:53Z jheathco $
*
* @package Core
* @author Kohana Team
* @copyright (c) 2007-2009 Kohana Team
* @license http://kohanaphp.com/license.html
*/
class Database_Expression_Core {
protected $expression;
public function __construct($expression)
{
$this->expression = $expression;
}
public function __toString()
{
return (string) $this->expression;
}
} // End Database Expr Class

View File

@@ -0,0 +1,164 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* The Encrypt library provides two-way encryption of text and binary strings
* using the MCrypt extension.
* @see http://php.net/mcrypt
*
* $Id: Encrypt.php 4072 2009-03-13 17:20:38Z jheathco $
*
* @package Core
* @author Kohana Team
* @copyright (c) 2007-2008 Kohana Team
* @license http://kohanaphp.com/license.html
*/
class Encrypt_Core {
// OS-dependant RAND type to use
protected static $rand;
// Configuration
protected $config;
/**
* Returns a singleton instance of Encrypt.
*
* @param array configuration options
* @return Encrypt_Core
*/
public static function instance($config = NULL)
{
static $instance;
// Create the singleton
empty($instance) and $instance = new Encrypt((array) $config);
return $instance;
}
/**
* Loads encryption configuration and validates the data.
*
* @param array|string custom configuration or config group name
* @throws Kohana_Exception
*/
public function __construct($config = FALSE)
{
if ( ! defined('MCRYPT_ENCRYPT'))
throw new Kohana_Exception('encrypt.requires_mcrypt');
if (is_string($config))
{
$name = $config;
// Test the config group name
if (($config = Kohana::config('encryption.'.$config)) === NULL)
throw new Kohana_Exception('encrypt.undefined_group', $name);
}
if (is_array($config))
{
// Append the default configuration options
$config += Kohana::config('encryption.default');
}
else
{
// Load the default group
$config = Kohana::config('encryption.default');
}
if (empty($config['key']))
throw new Kohana_Exception('encrypt.no_encryption_key');
// Find the max length of the key, based on cipher and mode
$size = mcrypt_get_key_size($config['cipher'], $config['mode']);
if (strlen($config['key']) > $size)
{
// Shorten the key to the maximum size
$config['key'] = substr($config['key'], 0, $size);
}
// Find the initialization vector size
$config['iv_size'] = mcrypt_get_iv_size($config['cipher'], $config['mode']);
// Cache the config in the object
$this->config = $config;
Kohana::log('debug', 'Encrypt Library initialized');
}
/**
* Encrypts a string and returns an encrypted string that can be decoded.
*
* @param string data to be encrypted
* @return string encrypted data
*/
public function encode($data)
{
// Set the rand type if it has not already been set
if (Encrypt::$rand === NULL)
{
if (KOHANA_IS_WIN)
{
// 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->config['iv_size'], Encrypt::$rand);
// Encrypt the data using the configured options and generated iv
$data = mcrypt_encrypt($this->config['cipher'], $this->config['key'], $data, $this->config['mode'], $iv);
// Use base64 encoding to convert to a string
return base64_encode($iv.$data);
}
/**
* Decrypts an encoded string back to its original value.
*
* @param string encoded string to be decrypted
* @return string decrypted data
*/
public function decode($data)
{
// Convert the data back to binary
$data = base64_decode($data);
// Extract the initialization vector from the data
$iv = substr($data, 0, $this->config['iv_size']);
// Remove the iv from the data
$data = substr($data, $this->config['iv_size']);
// Return the decrypted data, trimming the \0 padding bytes from the end of the data
return rtrim(mcrypt_decrypt($this->config['cipher'], $this->config['key'], $data, $this->config['mode'], $iv), "\0");
}
} // End Encrypt

View File

@@ -0,0 +1,70 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* Kohana event observer. Uses the SPL observer pattern.
*
* $Id: Event_Observer.php 3769 2008-12-15 00:48:56Z zombor $
*
* @package Core
* @author Kohana Team
* @copyright (c) 2007-2008 Kohana Team
* @license http://kohanaphp.com/license.html
*/
abstract class Event_Observer implements SplObserver {
// Calling object
protected $caller;
/**
* Initializes a new observer and attaches the subject as the caller.
*
* @param object Event_Subject
* @return void
*/
public function __construct(SplSubject $caller)
{
// Update the caller
$this->update($caller);
}
/**
* Updates the observer subject with a new caller.
*
* @chainable
* @param object Event_Subject
* @return object
*/
public function update(SplSubject $caller)
{
if ( ! ($caller instanceof Event_Subject))
throw new Kohana_Exception('event.invalid_subject', get_class($caller), get_class($this));
// Update the caller
$this->caller = $caller;
return $this;
}
/**
* Detaches this observer from the subject.
*
* @chainable
* @return object
*/
public function remove()
{
// Detach this observer from the caller
$this->caller->detach($this);
return $this;
}
/**
* Notify the observer of a new message. This function must be defined in
* all observers and must take exactly one parameter of any type.
*
* @param mixed message string, object, or array
* @return void
*/
abstract public function notify($message);
} // End Event Observer

View File

@@ -0,0 +1,67 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* Kohana event subject. Uses the SPL observer pattern.
*
* $Id: Event_Subject.php 3769 2008-12-15 00:48:56Z zombor $
*
* @package Core
* @author Kohana Team
* @copyright (c) 2007-2008 Kohana Team
* @license http://kohanaphp.com/license.html
*/
abstract class Event_Subject implements SplSubject {
// Attached subject listeners
protected $listeners = array();
/**
* Attach an observer to the object.
*
* @chainable
* @param object Event_Observer
* @return object
*/
public function attach(SplObserver $obj)
{
if ( ! ($obj instanceof Event_Observer))
throw new Kohana_Exception('eventable.invalid_observer', get_class($obj), get_class($this));
// Add a new listener
$this->listeners[spl_object_hash($obj)] = $obj;
return $this;
}
/**
* Detach an observer from the object.
*
* @chainable
* @param object Event_Observer
* @return object
*/
public function detach(SplObserver $obj)
{
// Remove the listener
unset($this->listeners[spl_object_hash($obj)]);
return $this;
}
/**
* Notify all attached observers of a new message.
*
* @chainable
* @param mixed message string, object, or array
* @return object
*/
public function notify($message)
{
foreach ($this->listeners as $obj)
{
$obj->notify($message);
}
return $this;
}
} // End Event Subject

View File

@@ -0,0 +1,431 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* Manipulate images using standard methods such as resize, crop, rotate, etc.
* This class must be re-initialized for every image you wish to manipulate.
*
* $Id: Image.php 4072 2009-03-13 17:20:38Z jheathco $
*
* @package Image
* @author Kohana Team
* @copyright (c) 2007-2008 Kohana Team
* @license http://kohanaphp.com/license.html
*/
class Image_Core {
// Master Dimension
const NONE = 1;
const AUTO = 2;
const HEIGHT = 3;
const WIDTH = 4;
// Flip Directions
const HORIZONTAL = 5;
const VERTICAL = 6;
// Allowed image types
public static $allowed_types = array
(
IMAGETYPE_GIF => 'gif',
IMAGETYPE_JPEG => 'jpg',
IMAGETYPE_PNG => 'png',
IMAGETYPE_TIFF_II => 'tiff',
IMAGETYPE_TIFF_MM => 'tiff',
);
// Driver instance
protected $driver;
// Driver actions
protected $actions = array();
// Reference to the current image filename
protected $image = '';
/**
* Creates a new Image instance and returns it.
*
* @param string filename of image
* @param array non-default configurations
* @return object
*/
public static function factory($image, $config = NULL)
{
return new Image($image, $config);
}
/**
* Creates a new image editor instance.
*
* @throws Kohana_Exception
* @param string filename of image
* @param array non-default configurations
* @return void
*/
public function __construct($image, $config = NULL)
{
static $check;
// Make the check exactly once
($check === NULL) and $check = function_exists('getimagesize');
if ($check === FALSE)
throw new Kohana_Exception('image.getimagesize_missing');
// Check to make sure the image exists
if ( ! is_file($image))
throw new Kohana_Exception('image.file_not_found', $image);
// Disable error reporting, to prevent PHP warnings
$ER = error_reporting(0);
// Fetch the image size and mime type
$image_info = getimagesize($image);
// Turn on error reporting again
error_reporting($ER);
// Make sure that the image is readable and valid
if ( ! is_array($image_info) OR count($image_info) < 3)
throw new Kohana_Exception('image.file_unreadable', $image);
// Check to make sure the image type is allowed
if ( ! isset(Image::$allowed_types[$image_info[2]]))
throw new Kohana_Exception('image.type_not_allowed', $image);
// Image has been validated, load it
$this->image = array
(
'file' => str_replace('\\', '/', realpath($image)),
'width' => $image_info[0],
'height' => $image_info[1],
'type' => $image_info[2],
'ext' => Image::$allowed_types[$image_info[2]],
'mime' => $image_info['mime']
);
// Load configuration
$this->config = (array) $config + Kohana::config('image');
// Set driver class name
$driver = 'Image_'.ucfirst($this->config['driver']).'_Driver';
// Load the driver
if ( ! Kohana::auto_load($driver))
throw new Kohana_Exception('core.driver_not_found', $this->config['driver'], get_class($this));
// Initialize the driver
$this->driver = new $driver($this->config['params']);
// Validate the driver
if ( ! ($this->driver instanceof Image_Driver))
throw new Kohana_Exception('core.driver_implements', $this->config['driver'], get_class($this), 'Image_Driver');
}
/**
* Handles retrieval of pre-save image properties
*
* @param string property name
* @return mixed
*/
public function __get($property)
{
if (isset($this->image[$property]))
{
return $this->image[$property];
}
else
{
throw new Kohana_Exception('core.invalid_property', $property, get_class($this));
}
}
/**
* Resize an image to a specific width and height. By default, Kohana will
* maintain the aspect ratio using the width as the master dimension. If you
* wish to use height as master dim, set $image->master_dim = Image::HEIGHT
* This method is chainable.
*
* @throws Kohana_Exception
* @param integer width
* @param integer height
* @param integer one of: Image::NONE, Image::AUTO, Image::WIDTH, Image::HEIGHT
* @return object
*/
public function resize($width, $height, $master = NULL)
{
if ( ! $this->valid_size('width', $width))
throw new Kohana_Exception('image.invalid_width', $width);
if ( ! $this->valid_size('height', $height))
throw new Kohana_Exception('image.invalid_height', $height);
if (empty($width) AND empty($height))
throw new Kohana_Exception('image.invalid_dimensions', __FUNCTION__);
if ($master === NULL)
{
// Maintain the aspect ratio by default
$master = Image::AUTO;
}
elseif ( ! $this->valid_size('master', $master))
throw new Kohana_Exception('image.invalid_master');
$this->actions['resize'] = array
(
'width' => $width,
'height' => $height,
'master' => $master,
);
return $this;
}
/**
* Crop an image to a specific width and height. You may also set the top
* and left offset.
* This method is chainable.
*
* @throws Kohana_Exception
* @param integer width
* @param integer height
* @param integer top offset, pixel value or one of: top, center, bottom
* @param integer left offset, pixel value or one of: left, center, right
* @return object
*/
public function crop($width, $height, $top = 'center', $left = 'center')
{
if ( ! $this->valid_size('width', $width))
throw new Kohana_Exception('image.invalid_width', $width);
if ( ! $this->valid_size('height', $height))
throw new Kohana_Exception('image.invalid_height', $height);
if ( ! $this->valid_size('top', $top))
throw new Kohana_Exception('image.invalid_top', $top);
if ( ! $this->valid_size('left', $left))
throw new Kohana_Exception('image.invalid_left', $left);
if (empty($width) AND empty($height))
throw new Kohana_Exception('image.invalid_dimensions', __FUNCTION__);
$this->actions['crop'] = array
(
'width' => $width,
'height' => $height,
'top' => $top,
'left' => $left,
);
return $this;
}
/**
* Allows rotation of an image by 180 degrees clockwise or counter clockwise.
*
* @param integer degrees
* @return object
*/
public function rotate($degrees)
{
$degrees = (int) $degrees;
if ($degrees > 180)
{
do
{
// Keep subtracting full circles until the degrees have normalized
$degrees -= 360;
}
while($degrees > 180);
}
if ($degrees < -180)
{
do
{
// Keep adding full circles until the degrees have normalized
$degrees += 360;
}
while($degrees < -180);
}
$this->actions['rotate'] = $degrees;
return $this;
}
/**
* Flip an image horizontally or vertically.
*
* @throws Kohana_Exception
* @param integer direction
* @return object
*/
public function flip($direction)
{
if ($direction !== Image::HORIZONTAL AND $direction !== Image::VERTICAL)
throw new Kohana_Exception('image.invalid_flip');
$this->actions['flip'] = $direction;
return $this;
}
/**
* Change the quality of an image.
*
* @param integer quality as a percentage
* @return object
*/
public function quality($amount)
{
$this->actions['quality'] = max(1, min($amount, 100));
return $this;
}
/**
* Sharpen an image.
*
* @param integer amount to sharpen, usually ~20 is ideal
* @return object
*/
public function sharpen($amount)
{
$this->actions['sharpen'] = max(1, min($amount, 100));
return $this;
}
/**
* Save the image to a new image or overwrite this image.
*
* @throws Kohana_Exception
* @param string new image filename
* @param integer permissions for new image
* @param boolean keep or discard image process actions
* @return object
*/
public function save($new_image = FALSE, $chmod = 0644, $keep_actions = FALSE)
{
// If no new image is defined, use the current image
empty($new_image) and $new_image = $this->image['file'];
// Separate the directory and filename
$dir = pathinfo($new_image, PATHINFO_DIRNAME);
$file = pathinfo($new_image, PATHINFO_BASENAME);
// Normalize the path
$dir = str_replace('\\', '/', realpath($dir)).'/';
if ( ! is_writable($dir))
throw new Kohana_Exception('image.directory_unwritable', $dir);
if ($status = $this->driver->process($this->image, $this->actions, $dir, $file))
{
if ($chmod !== FALSE)
{
// Set permissions
chmod($new_image, $chmod);
}
}
// Reset actions. Subsequent save() or render() will not apply previous actions.
if ($keep_actions === FALSE)
$this->actions = array();
return $status;
}
/**
* Output the image to the browser.
*
* @param boolean keep or discard image process actions
* @return object
*/
public function render($keep_actions = FALSE)
{
$new_image = $this->image['file'];
// Separate the directory and filename
$dir = pathinfo($new_image, PATHINFO_DIRNAME);
$file = pathinfo($new_image, PATHINFO_BASENAME);
// Normalize the path
$dir = str_replace('\\', '/', realpath($dir)).'/';
// Process the image with the driver
$status = $this->driver->process($this->image, $this->actions, $dir, $file, $render = TRUE);
// Reset actions. Subsequent save() or render() will not apply previous actions.
if ($keep_actions === FALSE)
$this->actions = array();
return $status;
}
/**
* Sanitize a given value type.
*
* @param string type of property
* @param mixed property value
* @return boolean
*/
protected function valid_size($type, & $value)
{
if (is_null($value))
return TRUE;
if ( ! is_scalar($value))
return FALSE;
switch ($type)
{
case 'width':
case 'height':
if (is_string($value) AND ! ctype_digit($value))
{
// Only numbers and percent signs
if ( ! preg_match('/^[0-9]++%$/D', $value))
return FALSE;
}
else
{
$value = (int) $value;
}
break;
case 'top':
if (is_string($value) AND ! ctype_digit($value))
{
if ( ! in_array($value, array('top', 'bottom', 'center')))
return FALSE;
}
else
{
$value = (int) $value;
}
break;
case 'left':
if (is_string($value) AND ! ctype_digit($value))
{
if ( ! in_array($value, array('left', 'right', 'center')))
return FALSE;
}
else
{
$value = (int) $value;
}
break;
case 'master':
if ($value !== Image::NONE AND
$value !== Image::AUTO AND
$value !== Image::WIDTH AND
$value !== Image::HEIGHT)
return FALSE;
break;
}
return TRUE;
}
} // End Image

View File

@@ -0,0 +1,452 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* Input library.
*
* $Id: Input.php 4346 2009-05-11 17:08:15Z zombor $
*
* @package Core
* @author Kohana Team
* @copyright (c) 2007-2008 Kohana Team
* @license http://kohanaphp.com/license.html
*/
class Input_Core {
// Enable or disable automatic XSS cleaning
protected $use_xss_clean = FALSE;
// Are magic quotes enabled?
protected $magic_quotes_gpc = FALSE;
// IP address of current user
public $ip_address;
// Input singleton
protected static $instance;
/**
* Retrieve a singleton instance of Input. This will always be the first
* created instance of this class.
*
* @return object
*/
public static function instance()
{
if (Input::$instance === NULL)
{
// Create a new instance
return new Input;
}
return Input::$instance;
}
/**
* Sanitizes global GET, POST and COOKIE data. Also takes care of
* magic_quotes and register_globals, if they have been enabled.
*
* @return void
*/
public function __construct()
{
// Use XSS clean?
$this->use_xss_clean = (bool) Kohana::config('core.global_xss_filtering');
if (Input::$instance === NULL)
{
// magic_quotes_runtime is enabled
if (get_magic_quotes_runtime())
{
set_magic_quotes_runtime(0);
Kohana::log('debug', 'Disable magic_quotes_runtime! It is evil and deprecated: http://php.net/magic_quotes');
}
// magic_quotes_gpc is enabled
if (get_magic_quotes_gpc())
{
$this->magic_quotes_gpc = TRUE;
Kohana::log('debug', 'Disable magic_quotes_gpc! It is evil and deprecated: http://php.net/magic_quotes');
}
// register_globals is enabled
if (ini_get('register_globals'))
{
if (isset($_REQUEST['GLOBALS']))
{
// Prevent GLOBALS override attacks
exit('Global variable overload attack.');
}
// Destroy the REQUEST global
$_REQUEST = array();
// These globals are standard and should not be removed
$preserve = array('GLOBALS', '_REQUEST', '_GET', '_POST', '_FILES', '_COOKIE', '_SERVER', '_ENV', '_SESSION');
// This loop has the same effect as disabling register_globals
foreach (array_diff(array_keys($GLOBALS), $preserve) as $key)
{
global $$key;
$$key = NULL;
// Unset the global variable
unset($GLOBALS[$key], $$key);
}
// Warn the developer about register globals
Kohana::log('debug', 'Disable register_globals! It is evil and deprecated: http://php.net/register_globals');
}
if (is_array($_GET))
{
foreach ($_GET as $key => $val)
{
// Sanitize $_GET
$_GET[$this->clean_input_keys($key)] = $this->clean_input_data($val);
}
}
else
{
$_GET = array();
}
if (is_array($_POST))
{
foreach ($_POST as $key => $val)
{
// Sanitize $_POST
$_POST[$this->clean_input_keys($key)] = $this->clean_input_data($val);
}
}
else
{
$_POST = array();
}
if (is_array($_COOKIE))
{
foreach ($_COOKIE as $key => $val)
{
// Ignore special attributes in RFC2109 compliant cookies
if ($key == '$Version' OR $key == '$Path' OR $key == '$Domain')
continue;
// Sanitize $_COOKIE
$_COOKIE[$this->clean_input_keys($key)] = $this->clean_input_data($val);
}
}
else
{
$_COOKIE = array();
}
// Create a singleton
Input::$instance = $this;
Kohana::log('debug', 'Global GET, POST and COOKIE data sanitized');
}
}
/**
* Fetch an item from the $_GET array.
*
* @param string key to find
* @param mixed default value
* @param boolean XSS clean the value
* @return mixed
*/
public function get($key = array(), $default = NULL, $xss_clean = FALSE)
{
return $this->search_array($_GET, $key, $default, $xss_clean);
}
/**
* Fetch an item from the $_POST array.
*
* @param string key to find
* @param mixed default value
* @param boolean XSS clean the value
* @return mixed
*/
public function post($key = array(), $default = NULL, $xss_clean = FALSE)
{
return $this->search_array($_POST, $key, $default, $xss_clean);
}
/**
* Fetch an item from the $_COOKIE array.
*
* @param string key to find
* @param mixed default value
* @param boolean XSS clean the value
* @return mixed
*/
public function cookie($key = array(), $default = NULL, $xss_clean = FALSE)
{
return $this->search_array($_COOKIE, $key, $default, $xss_clean);
}
/**
* Fetch an item from the $_SERVER array.
*
* @param string key to find
* @param mixed default value
* @param boolean XSS clean the value
* @return mixed
*/
public function server($key = array(), $default = NULL, $xss_clean = FALSE)
{
return $this->search_array($_SERVER, $key, $default, $xss_clean);
}
/**
* Fetch an item from a global array.
*
* @param array array to search
* @param string key to find
* @param mixed default value
* @param boolean XSS clean the value
* @return mixed
*/
protected function search_array($array, $key, $default = NULL, $xss_clean = FALSE)
{
if ($key === array())
return $array;
if ( ! isset($array[$key]))
return $default;
// Get the value
$value = $array[$key];
if ($this->use_xss_clean === FALSE AND $xss_clean === TRUE)
{
// XSS clean the value
$value = $this->xss_clean($value);
}
return $value;
}
/**
* Fetch the IP Address.
*
* @return string
*/
public function ip_address()
{
if ($this->ip_address !== NULL)
return $this->ip_address;
// Server keys that could contain the client IP address
$keys = array('HTTP_X_FORWARDED_FOR', 'HTTP_CLIENT_IP', 'REMOTE_ADDR');
foreach ($keys as $key)
{
if ($ip = $this->server($key))
{
$this->ip_address = $ip;
// An IP address has been found
break;
}
}
if ($comma = strrpos($this->ip_address, ',') !== FALSE)
{
$this->ip_address = substr($this->ip_address, $comma + 1);
}
if ( ! valid::ip($this->ip_address))
{
// Use an empty IP
$this->ip_address = '0.0.0.0';
}
return $this->ip_address;
}
/**
* Clean cross site scripting exploits from string.
* HTMLPurifier may be used if installed, otherwise defaults to built in method.
* Note - This function should only be used to deal with data upon submission.
* It's not something that should be used for general runtime processing
* since it requires a fair amount of processing overhead.
*
* @param string data to clean
* @param string xss_clean method to use ('htmlpurifier' or defaults to built-in method)
* @return string
*/
public function xss_clean($data, $tool = NULL)
{
if ($tool === NULL)
{
// Use the default tool
$tool = Kohana::config('core.global_xss_filtering');
}
if (is_array($data))
{
foreach ($data as $key => $val)
{
$data[$key] = $this->xss_clean($val, $tool);
}
return $data;
}
// Do not clean empty strings
if (trim($data) === '')
return $data;
if ($tool === TRUE)
{
// NOTE: This is necessary because switch is NOT type-sensative!
$tool = 'default';
}
switch ($tool)
{
case 'htmlpurifier':
/**
* @todo License should go here, http://htmlpurifier.org/
*/
if ( ! class_exists('HTMLPurifier_Config', FALSE))
{
// Load HTMLPurifier
require Kohana::find_file('vendor', 'htmlpurifier/HTMLPurifier.auto', TRUE);
require 'HTMLPurifier.func.php';
}
// Set configuration
$config = HTMLPurifier_Config::createDefault();
$config->set('HTML', 'TidyLevel', 'none'); // Only XSS cleaning now
// Run HTMLPurifier
$data = HTMLPurifier($data, $config);
break;
default:
// 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
// Fix &entity\n;
$data = str_replace(array('&amp;','&lt;','&gt;'), array('&amp;amp;','&amp;lt;','&amp;gt;'), $data);
$data = preg_replace('/(&#*\w+)[\x00-\x20]+;/u', '$1;', $data);
$data = preg_replace('/(&#x*[0-9A-F]+);*/iu', '$1;', $data);
$data = html_entity_decode($data, ENT_COMPAT, 'UTF-8');
// Remove any attribute starting with "on" or xmlns
$data = preg_replace('#(<[^>]+?[\x00-\x20"\'])(?:on|xmlns)[^>]*+>#iu', '$1>', $data);
// Remove javascript: and vbscript: protocols
$data = 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...', $data);
$data = 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...', $data);
$data = preg_replace('#([a-z]*)[\x00-\x20]*=([\'"]*)[\x00-\x20]*-moz-binding[\x00-\x20]*:#u', '$1=$2nomozbinding...', $data);
// Only works in IE: <span style="width: expression(alert('Ping!'));"></span>
$data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?expression[\x00-\x20]*\([^>]*+>#i', '$1>', $data);
$data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?behaviour[\x00-\x20]*\([^>]*+>#i', '$1>', $data);
$data = 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]*:*[^>]*+>#iu', '$1>', $data);
// Remove namespaced elements (we do not need them)
$data = preg_replace('#</*\w+:\w[^>]*+>#i', '', $data);
do
{
// Remove really unwanted tags
$old_data = $data;
$data = preg_replace('#</*(?:applet|b(?:ase|gsound|link)|embed|frame(?:set)?|i(?:frame|layer)|l(?:ayer|ink)|meta|object|s(?:cript|tyle)|title|xml)[^>]*+>#i', '', $data);
}
while ($old_data !== $data);
break;
}
return $data;
}
/**
* This is a helper method. It enforces W3C specifications for allowed
* key name strings, to prevent malicious exploitation.
*
* @param string string to clean
* @return string
*/
public function clean_input_keys($str)
{
$chars = PCRE_UNICODE_PROPERTIES ? '\pL' : 'a-zA-Z';
if ( ! preg_match('#^['.$chars.'0-9:_.-]++$#uD', $str))
{
exit('Disallowed key characters in global data.');
}
return $str;
}
/**
* This is a helper method. It escapes data and forces all newline
* characters to "\n".
*
* @param unknown_type string to clean
* @return string
*/
public function clean_input_data($str)
{
if (is_array($str))
{
$new_array = array();
foreach ($str as $key => $val)
{
// Recursion!
$new_array[$this->clean_input_keys($key)] = $this->clean_input_data($val);
}
return $new_array;
}
if ($this->magic_quotes_gpc === TRUE)
{
// Remove annoying magic quotes
$str = stripslashes($str);
}
if ($this->use_xss_clean === TRUE)
{
$str = $this->xss_clean($str);
}
if (strpos($str, "\r") !== FALSE)
{
// Standardize newlines
$str = str_replace(array("\r\n", "\r"), "\n", $str);
}
return $str;
}
} // End Input Class

View File

@@ -0,0 +1,31 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* Model base class.
*
* $Id: Model.php 4007 2009-02-20 01:54:00Z jheathco $
*
* @package Core
* @author Kohana Team
* @copyright (c) 2007-2009 Kohana Team
* @license http://kohanaphp.com/license.html
*/
class Model_Core {
// Database object
protected $db = 'default';
/**
* Loads the database instance, if the database is not already loaded.
*
* @return void
*/
public function __construct()
{
if ( ! is_object($this->db))
{
// Load the default database
$this->db = Database::instance($this->db);
}
}
} // End Model

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,228 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* Object Relational Mapping (ORM) result iterator.
*
* $Id: ORM_Iterator.php 3769 2008-12-15 00:48:56Z zombor $
*
* @package ORM
* @author Kohana Team
* @copyright (c) 2007-2008 Kohana Team
* @license http://kohanaphp.com/license.html
*/
class ORM_Iterator_Core implements Iterator, ArrayAccess, Countable {
// Class attributes
protected $class_name;
protected $primary_key;
protected $primary_val;
// Database result object
protected $result;
public function __construct(ORM $model, Database_Result $result)
{
// Class attributes
$this->class_name = get_class($model);
$this->primary_key = $model->primary_key;
$this->primary_val = $model->primary_val;
// Database result
$this->result = $result->result(TRUE);
}
/**
* Returns an array of the results as ORM objects.
*
* @return array
*/
public function as_array()
{
$array = array();
if ($results = $this->result->result_array())
{
// Import class name
$class = $this->class_name;
foreach ($results as $obj)
{
$array[] = new $class($obj);
}
}
return $array;
}
/**
* Return an array of all of the primary keys for this object.
*
* @return array
*/
public function primary_key_array()
{
$ids = array();
foreach ($this->result as $row)
{
$ids[] = $row->{$this->primary_key};
}
return $ids;
}
/**
* Create a key/value array from the results.
*
* @param string key column
* @param string value column
* @return array
*/
public function select_list($key = NULL, $val = NULL)
{
if ($key === NULL)
{
// Use the default key
$key = $this->primary_key;
}
if ($val === NULL)
{
// Use the default value
$val = $this->primary_val;
}
$array = array();
foreach ($this->result->result_array() as $row)
{
$array[$row->$key] = $row->$val;
}
return $array;
}
/**
* Return a range of offsets.
*
* @param integer start
* @param integer end
* @return array
*/
public function range($start, $end)
{
// Array of objects
$array = array();
if ($this->result->offsetExists($start))
{
// Import the class name
$class = $this->class_name;
// Set the end offset
$end = $this->result->offsetExists($end) ? $end : $this->count();
for ($i = $start; $i < $end; $i++)
{
// Insert each object in the range
$array[] = new $class($this->result->offsetGet($i));
}
}
return $array;
}
/**
* Countable: count
*/
public function count()
{
return $this->result->count();
}
/**
* Iterator: current
*/
public function current()
{
if ($row = $this->result->current())
{
// Import class name
$class = $this->class_name;
$row = new $class($row);
}
return $row;
}
/**
* Iterator: key
*/
public function key()
{
return $this->result->key();
}
/**
* Iterator: next
*/
public function next()
{
return $this->result->next();
}
/**
* Iterator: rewind
*/
public function rewind()
{
$this->result->rewind();
}
/**
* Iterator: valid
*/
public function valid()
{
return $this->result->valid();
}
/**
* ArrayAccess: offsetExists
*/
public function offsetExists($offset)
{
return $this->result->offsetExists($offset);
}
/**
* ArrayAccess: offsetGet
*/
public function offsetGet($offset)
{
if ($this->result->offsetExists($offset))
{
// Import class name
$class = $this->class_name;
return new $class($this->result->offsetGet($offset));
}
}
/**
* ArrayAccess: offsetSet
*
* @throws Kohana_Database_Exception
*/
public function offsetSet($offset, $value)
{
throw new Kohana_Database_Exception('database.result_read_only');
}
/**
* ArrayAccess: offsetUnset
*
* @throws Kohana_Database_Exception
*/
public function offsetUnset($offset)
{
throw new Kohana_Database_Exception('database.result_read_only');
}
} // End ORM Iterator

View File

@@ -0,0 +1,76 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* Object Relational Mapping (ORM) "tree" extension. Allows ORM objects to act
* as trees, with parents and children.
*
* $Id: ORM_Tree.php 3923 2009-01-22 15:37:04Z samsoir $
*
* @package ORM
* @author Kohana Team
* @copyright (c) 2007-2008 Kohana Team
* @license http://kohanaphp.com/license.html
*/
class ORM_Tree_Core extends ORM {
// Name of the child
protected $ORM_Tree_children;
// Parent keyword name
protected $ORM_Tree_parent_key = 'parent_id';
/**
* Overload ORM::__get to support "parent" and "children" properties.
*
* @param string column name
* @return mixed
*/
public function __get($column)
{
if ($column === 'parent')
{
if (empty($this->related[$column]))
{
// Load child model
$model = ORM::factory(inflector::singular($this->ORM_Tree_children));
if (array_key_exists($this->ORM_Tree_parent_key, $this->object))
{
// Find children of this parent
$model->where($model->primary_key, $this->object[$this->ORM_Tree_parent_key])->find();
}
$this->related[$column] = $model;
}
return $this->related[$column];
}
elseif ($column === 'children')
{
if (empty($this->related[$column]))
{
$model = ORM::factory(inflector::singular($this->ORM_Tree_children));
if ($this->ORM_Tree_children === $this->table_name)
{
// Load children within this table
$this->related[$column] = $model
->where($this->ORM_Tree_parent_key, $this->object[$this->primary_key])
->find_all();
}
else
{
// Find first selection of children
$this->related[$column] = $model
->where($this->foreign_key(), $this->object[$this->primary_key])
->where($this->ORM_Tree_parent_key, NULL)
->find_all();
}
}
return $this->related[$column];
}
return parent::__get($column);
}
} // End ORM Tree

View File

@@ -0,0 +1,143 @@
<?php
/**
* Object Relational Mapping (ORM) "versioned" extension. Allows ORM objects to
* be revisioned instead of updated.
*
* $Id$
*
* @package ORM
* @author Kohana Team
* @copyright (c) 2007-2008 Kohana Team
* @license http://kohanaphp.com/license.html
*/
class ORM_Versioned_Core extends ORM {
protected $last_version = NULL;
/**
* Overload ORM::save() to support versioned data
*
* @chainable
* @return ORM
*/
public function save()
{
$this->last_version = 1 + ($this->last_version === NULL ? $this->object['version'] : $this->last_version);
$this->__set('version', $this->last_version);
parent::save();
if ($this->saved)
{
$data = array();
foreach ($this->object as $key => $value)
{
if ($key === 'id')
continue;
$data[$key] = $value;
}
$data[$this->foreign_key()] = $this->id;
$this->db->insert($this->table_name.'_versions', $data);
}
return $this;
}
/**
* Loads previous version from current object
*
* @chainable
* @return ORM
*/
public function previous()
{
if ( ! $this->loaded)
return $this;
$this->last_version = ($this->last_version === NULL) ? $this->object['version'] : $this->last_version;
$version = $this->last_version - 1;
$query = $this->db
->where($this->foreign_key(), $this->object[$this->primary_key])
->where('version', $version)
->limit(1)
->get($this->table_name.'_versions');
if ($query->count())
{
$this->load_values($query->result(FALSE)->current());
}
return $this;
}
/**
* Restores the object with data from stored version
*
* @param integer version number you want to restore
* @return ORM
*/
public function restore($version)
{
if ( ! $this->loaded)
return $this;
$query = $this->db
->where($this->foreign_key(), $this->object[$this->primary_key])
->where('version', $version)
->limit(1)
->get($this->table_name.'_versions');
if ($query->count())
{
$row = $query->result(FALSE)->current();
foreach ($row as $key => $value)
{
if ($key === $this->primary_key OR $key === $this->foreign_key())
{
// Do not overwrite the primary key
continue;
}
if ($key === 'version')
{
// Always use the current version
$value = $this->version;
}
$this->__set($key, $value);
}
$this->save();
}
return $this;
}
/**
* Overloads ORM::delete() to delete all versioned entries of current object
* and the object itself
*
* @param integer id of the object you want to delete
* @return ORM
*/
public function delete($id = NULL)
{
if ($id === NULL)
{
// Use the current object id
$id = $this->object[$this->primary_key];
}
if ($status = parent::delete($id))
{
$this->db->where($this->foreign_key(), $id)->delete($this->table_name.'_versions');
}
return $status;
}
}

View File

@@ -0,0 +1,236 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* Pagination library.
*
* $Id: Pagination.php 3769 2008-12-15 00:48:56Z zombor $
*
* @package Core
* @author Kohana Team
* @copyright (c) 2007-2008 Kohana Team
* @license http://kohanaphp.com/license.html
*/
class Pagination_Core {
// Config values
protected $base_url = '';
protected $directory = 'pagination';
protected $style = 'classic';
protected $uri_segment = 3;
protected $query_string = '';
protected $items_per_page = 20;
protected $total_items = 0;
protected $auto_hide = FALSE;
// Autogenerated values
protected $url;
protected $current_page;
protected $total_pages;
protected $current_first_item;
protected $current_last_item;
protected $first_page;
protected $last_page;
protected $previous_page;
protected $next_page;
protected $sql_offset;
protected $sql_limit;
/**
* Constructs and returns a new Pagination object.
*
* @param array configuration settings
* @return object
*/
public function factory($config = array())
{
return new Pagination($config);
}
/**
* Constructs a new Pagination object.
*
* @param array configuration settings
* @return void
*/
public function __construct($config = array())
{
// No custom group name given
if ( ! isset($config['group']))
{
$config['group'] = 'default';
}
// Pagination setup
$this->initialize($config);
Kohana::log('debug', 'Pagination Library initialized');
}
/**
* Sets config values.
*
* @throws Kohana_Exception
* @param array configuration settings
* @return void
*/
public function initialize($config = array())
{
// Load config group
if (isset($config['group']))
{
// Load and validate config group
if ( ! is_array($group_config = Kohana::config('pagination.'.$config['group'])))
throw new Kohana_Exception('pagination.undefined_group', $config['group']);
// All pagination config groups inherit default config group
if ($config['group'] !== 'default')
{
// Load and validate default config group
if ( ! is_array($default_config = Kohana::config('pagination.default')))
throw new Kohana_Exception('pagination.undefined_group', 'default');
// Merge config group with default config group
$group_config += $default_config;
}
// Merge custom config items with config group
$config += $group_config;
}
// Assign config values to the object
foreach ($config as $key => $value)
{
if (property_exists($this, $key))
{
$this->$key = $value;
}
}
// Clean view directory
$this->directory = trim($this->directory, '/').'/';
// Build generic URL with page in query string
if ($this->query_string !== '')
{
// Extract current page
$this->current_page = isset($_GET[$this->query_string]) ? (int) $_GET[$this->query_string] : 1;
// Insert {page} placeholder
$_GET[$this->query_string] = '{page}';
// Create full URL
$base_url = ($this->base_url === '') ? Router::$current_uri : $this->base_url;
$this->url = url::site($base_url).'?'.str_replace('%7Bpage%7D', '{page}', http_build_query($_GET));
// Reset page number
$_GET[$this->query_string] = $this->current_page;
}
// Build generic URL with page as URI segment
else
{
// Use current URI if no base_url set
$this->url = ($this->base_url === '') ? Router::$segments : explode('/', trim($this->base_url, '/'));
// Convert uri 'label' to corresponding integer if needed
if (is_string($this->uri_segment))
{
if (($key = array_search($this->uri_segment, $this->url)) === FALSE)
{
// If uri 'label' is not found, auto add it to base_url
$this->url[] = $this->uri_segment;
$this->uri_segment = count($this->url) + 1;
}
else
{
$this->uri_segment = $key + 2;
}
}
// Insert {page} placeholder
$this->url[$this->uri_segment - 1] = '{page}';
// Create full URL
$this->url = url::site(implode('/', $this->url)).Router::$query_string;
// Extract current page
$this->current_page = URI::instance()->segment($this->uri_segment);
}
// Core pagination values
$this->total_items = (int) max(0, $this->total_items);
$this->items_per_page = (int) max(1, $this->items_per_page);
$this->total_pages = (int) ceil($this->total_items / $this->items_per_page);
$this->current_page = (int) min(max(1, $this->current_page), max(1, $this->total_pages));
$this->current_first_item = (int) min((($this->current_page - 1) * $this->items_per_page) + 1, $this->total_items);
$this->current_last_item = (int) min($this->current_first_item + $this->items_per_page - 1, $this->total_items);
// If there is no first/last/previous/next page, relative to the
// current page, value is set to FALSE. Valid page number otherwise.
$this->first_page = ($this->current_page === 1) ? FALSE : 1;
$this->last_page = ($this->current_page >= $this->total_pages) ? FALSE : $this->total_pages;
$this->previous_page = ($this->current_page > 1) ? $this->current_page - 1 : FALSE;
$this->next_page = ($this->current_page < $this->total_pages) ? $this->current_page + 1 : FALSE;
// SQL values
$this->sql_offset = (int) ($this->current_page - 1) * $this->items_per_page;
$this->sql_limit = sprintf(' LIMIT %d OFFSET %d ', $this->items_per_page, $this->sql_offset);
}
/**
* Generates the HTML for the chosen pagination style.
*
* @param string pagination style
* @return string pagination html
*/
public function render($style = NULL)
{
// Hide single page pagination
if ($this->auto_hide === TRUE AND $this->total_pages <= 1)
return '';
if ($style === NULL)
{
// Use default style
$style = $this->style;
}
// Return rendered pagination view
return View::factory($this->directory.$style, get_object_vars($this))->render();
}
/**
* Magically converts Pagination object to string.
*
* @return string pagination html
*/
public function __toString()
{
return $this->render();
}
/**
* Magically gets a pagination variable.
*
* @param string variable key
* @return mixed variable value if the key is found
* @return void if the key is not found
*/
public function __get($key)
{
if (isset($this->$key))
return $this->$key;
}
/**
* Adds a secondary interface for accessing properties, e.g. $pagination->total_pages().
* Note that $pagination->total_pages is the recommended way to access properties.
*
* @param string function name
* @return string
*/
public function __call($func, $args = NULL)
{
return $this->__get($func);
}
} // End Pagination Class

View File

@@ -0,0 +1,271 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* Adds useful information to the bottom of the current page for debugging and optimization purposes.
*
* Benchmarks - The times and memory usage of benchmarks run by the Benchmark library.
* Database - The raw SQL and number of affected rows of Database queries.
* Session Data - Data stored in the current session if using the Session library.
* POST Data - The name and values of any POST data submitted to the current page.
* Cookie Data - All cookies sent for the current request.
*
* $Id: Profiler.php 4383 2009-06-03 00:17:24Z ixmatus $
*
* @package Profiler
* @author Kohana Team
* @copyright (c) 2007-2008 Kohana Team
* @license http://kohanaphp.com/license.html
*/
class Profiler_Core {
protected $profiles = array();
protected $show;
public function __construct()
{
// Add all built in profiles to event
Event::add('profiler.run', array($this, 'benchmarks'));
Event::add('profiler.run', array($this, 'database'));
Event::add('profiler.run', array($this, 'session'));
Event::add('profiler.run', array($this, 'post'));
Event::add('profiler.run', array($this, 'cookies'));
// Add profiler to page output automatically
Event::add('system.display', array($this, 'render'));
Kohana::log('debug', 'Profiler Library initialized');
}
/**
* Magic __call method. Creates a new profiler section object.
*
* @param string input type
* @param string input name
* @return object
*/
public function __call($method, $args)
{
if ( ! $this->show OR (is_array($this->show) AND ! in_array($args[0], $this->show)))
return FALSE;
// Class name
$class = 'Profiler_'.ucfirst($method);
$class = new $class();
$this->profiles[$args[0]] = $class;
return $class;
}
/**
* Disables the profiler for this page only.
* Best used when profiler is autoloaded.
*
* @return void
*/
public function disable()
{
// Removes itself from the event queue
Event::clear('system.display', array($this, 'render'));
}
/**
* Render the profiler. Output is added to the bottom of the page by default.
*
* @param boolean return the output if TRUE
* @return void|string
*/
public function render($return = FALSE)
{
$start = microtime(TRUE);
$get = isset($_GET['profiler']) ? explode(',', $_GET['profiler']) : array();
$this->show = empty($get) ? Kohana::config('profiler.show') : $get;
Event::run('profiler.run', $this);
$styles = '';
foreach ($this->profiles as $profile)
{
$styles .= $profile->styles();
}
// Don't display if there's no profiles
if (empty($this->profiles))
return;
// Load the profiler view
$data = array
(
'profiles' => $this->profiles,
'styles' => $styles,
'execution_time' => microtime(TRUE) - $start
);
$view = new View('kohana_profiler', $data);
// Return rendered view if $return is TRUE
if ($return === TRUE)
return $view->render();
// Add profiler data to the output
if (stripos(Kohana::$output, '</body>') !== FALSE)
{
// Closing body tag was found, insert the profiler data before it
Kohana::$output = str_ireplace('</body>', $view->render().'</body>', Kohana::$output);
}
else
{
// Append the profiler data to the output
Kohana::$output .= $view->render();
}
}
/**
* Benchmark times and memory usage from the Benchmark library.
*
* @return void
*/
public function benchmarks()
{
if ( ! $table = $this->table('benchmarks'))
return;
$table->add_column();
$table->add_column('kp-column kp-data');
$table->add_column('kp-column kp-data');
$table->add_column('kp-column kp-data');
$table->add_row(array('Benchmarks', 'Time', 'Count', 'Memory'), 'kp-title', 'background-color: #FFE0E0');
$benchmarks = Benchmark::get(TRUE);
// Moves the first benchmark (total execution time) to the end of the array
$benchmarks = array_slice($benchmarks, 1) + array_slice($benchmarks, 0, 1);
text::alternate();
foreach ($benchmarks as $name => $benchmark)
{
// Clean unique id from system benchmark names
$name = ucwords(str_replace(array('_', '-'), ' ', str_replace(SYSTEM_BENCHMARK.'_', '', $name)));
$data = array($name, number_format($benchmark['time'], 3), $benchmark['count'], number_format($benchmark['memory'] / 1024 / 1024, 2).'MB');
$class = text::alternate('', 'kp-altrow');
if ($name == 'Total Execution')
$class = 'kp-totalrow';
$table->add_row($data, $class);
}
}
/**
* Database query benchmarks.
*
* @return void
*/
public function database()
{
if ( ! $table = $this->table('database'))
return;
$table->add_column();
$table->add_column('kp-column kp-data');
$table->add_column('kp-column kp-data');
$table->add_row(array('Queries', 'Time', 'Rows'), 'kp-title', 'background-color: #E0FFE0');
$queries = Database::$benchmarks;
text::alternate();
$total_time = $total_rows = 0;
foreach ($queries as $query)
{
$data = array($query['query'], number_format($query['time'], 3), $query['rows']);
$class = text::alternate('', 'kp-altrow');
$table->add_row($data, $class);
$total_time += $query['time'];
$total_rows += $query['rows'];
}
$data = array('Total: ' . count($queries), number_format($total_time, 3), $total_rows);
$table->add_row($data, 'kp-totalrow');
}
/**
* Session data.
*
* @return void
*/
public function session()
{
if (empty($_SESSION)) return;
if ( ! $table = $this->table('session'))
return;
$table->add_column('kp-name');
$table->add_column();
$table->add_row(array('Session', 'Value'), 'kp-title', 'background-color: #CCE8FB');
text::alternate();
foreach($_SESSION as $name => $value)
{
if (is_object($value))
{
$value = get_class($value).' [object]';
}
$data = array($name, $value);
$class = text::alternate('', 'kp-altrow');
$table->add_row($data, $class);
}
}
/**
* POST data.
*
* @return void
*/
public function post()
{
if (empty($_POST)) return;
if ( ! $table = $this->table('post'))
return;
$table->add_column('kp-name');
$table->add_column();
$table->add_row(array('POST', 'Value'), 'kp-title', 'background-color: #E0E0FF');
text::alternate();
foreach($_POST as $name => $value)
{
$data = array($name, $value);
$class = text::alternate('', 'kp-altrow');
$table->add_row($data, $class);
}
}
/**
* Cookie data.
*
* @return void
*/
public function cookies()
{
if (empty($_COOKIE)) return;
if ( ! $table = $this->table('cookies'))
return;
$table->add_column('kp-name');
$table->add_column();
$table->add_row(array('Cookies', 'Value'), 'kp-title', 'background-color: #FFF4D7');
text::alternate();
foreach($_COOKIE as $name => $value)
{
$data = array($name, $value);
$class = text::alternate('', 'kp-altrow');
$table->add_row($data, $class);
}
}
}

View File

@@ -0,0 +1,69 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* Provides a table layout for sections in the Profiler library.
*
* $Id$
*
* @package Profiler
* @author Kohana Team
* @copyright (c) 2007-2008 Kohana Team
* @license http://kohanaphp.com/license.html
*/
class Profiler_Table_Core {
protected $columns = array();
protected $rows = array();
/**
* Get styles for table.
*
* @return string
*/
public function styles()
{
static $styles_output;
if ( ! $styles_output)
{
$styles_output = TRUE;
return file_get_contents(Kohana::find_file('views', 'kohana_profiler_table', FALSE, 'css'));
}
return '';
}
/**
* Add column to table.
*
* @param string CSS class
* @param string CSS style
*/
public function add_column($class = '', $style = '')
{
$this->columns[] = array('class' => $class, 'style' => $style);
}
/**
* Add row to table.
*
* @param array data to go in table cells
* @param string CSS class
* @param string CSS style
*/
public function add_row($data, $class = '', $style = '')
{
$this->rows[] = array('data' => $data, 'class' => $class, 'style' => $style);
}
/**
* Render table.
*
* @return string
*/
public function render()
{
$data['rows'] = $this->rows;
$data['columns'] = $this->columns;
return View::factory('kohana_profiler_table', $data)->render();
}
}

View File

@@ -0,0 +1,304 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* Router
*
* $Id: Router.php 4391 2009-06-04 03:10:12Z zombor $
*
* @package Core
* @author Kohana Team
* @copyright (c) 2007-2008 Kohana Team
* @license http://kohanaphp.com/license.html
*/
class Router_Core {
protected static $routes;
public static $current_uri = '';
public static $query_string = '';
public static $complete_uri = '';
public static $routed_uri = '';
public static $url_suffix = '';
public static $segments;
public static $rsegments;
public static $controller;
public static $controller_path;
public static $method = 'index';
public static $arguments = array();
/**
* Router setup routine. Automatically called during Kohana setup process.
*
* @return void
*/
public static function setup()
{
if ( ! empty($_SERVER['QUERY_STRING']))
{
// Set the query string to the current query string
Router::$query_string = '?'.trim($_SERVER['QUERY_STRING'], '&/');
}
if (Router::$routes === NULL)
{
// Load routes
Router::$routes = Kohana::config('routes');
}
// Default route status
$default_route = FALSE;
if (Router::$current_uri === '')
{
// Make sure the default route is set
if ( ! isset(Router::$routes['_default']))
throw new Kohana_Exception('core.no_default_route');
// Use the default route when no segments exist
Router::$current_uri = Router::$routes['_default'];
// Default route is in use
$default_route = TRUE;
}
// Make sure the URL is not tainted with HTML characters
Router::$current_uri = html::specialchars(Router::$current_uri, FALSE);
// Remove all dot-paths from the URI, they are not valid
Router::$current_uri = preg_replace('#\.[\s./]*/#', '', Router::$current_uri);
// At this point segments, rsegments, and current URI are all the same
Router::$segments = Router::$rsegments = Router::$current_uri = trim(Router::$current_uri, '/');
// Set the complete URI
Router::$complete_uri = Router::$current_uri.Router::$query_string;
// Explode the segments by slashes
Router::$segments = ($default_route === TRUE OR Router::$segments === '') ? array() : explode('/', Router::$segments);
if ($default_route === FALSE AND count(Router::$routes) > 1)
{
// Custom routing
Router::$rsegments = Router::routed_uri(Router::$current_uri);
}
// The routed URI is now complete
Router::$routed_uri = Router::$rsegments;
// Routed segments will never be empty
Router::$rsegments = explode('/', Router::$rsegments);
// Prepare to find the controller
$controller_path = '';
$method_segment = NULL;
// Paths to search
$paths = Kohana::include_paths();
foreach (Router::$rsegments as $key => $segment)
{
// Add the segment to the search path
$controller_path .= $segment;
$found = FALSE;
foreach ($paths as $dir)
{
// Search within controllers only
$dir .= 'controllers/';
if (is_dir($dir.$controller_path) OR is_file($dir.$controller_path.EXT))
{
// Valid path
$found = TRUE;
// The controller must be a file that exists with the search path
if ($c = str_replace('\\', '/', realpath($dir.$controller_path.EXT))
AND is_file($c) AND strpos($c, $dir) === 0)
{
// Set controller name
Router::$controller = $segment;
// Change controller path
Router::$controller_path = $c;
// Set the method segment
$method_segment = $key + 1;
// Stop searching
break;
}
}
}
if ($found === FALSE)
{
// Maximum depth has been reached, stop searching
break;
}
// Add another slash
$controller_path .= '/';
}
if ($method_segment !== NULL AND isset(Router::$rsegments[$method_segment]))
{
// Set method
Router::$method = Router::$rsegments[$method_segment];
if (isset(Router::$rsegments[$method_segment + 1]))
{
// Set arguments
Router::$arguments = array_slice(Router::$rsegments, $method_segment + 1);
}
}
// Last chance to set routing before a 404 is triggered
Event::run('system.post_routing');
if (Router::$controller === NULL)
{
// No controller was found, so no page can be rendered
Event::run('system.404');
}
}
/**
* Attempts to determine the current URI using CLI, GET, PATH_INFO, ORIG_PATH_INFO, or PHP_SELF.
*
* @return void
*/
public static function find_uri()
{
if (PHP_SAPI === 'cli')
{
// Command line requires a bit of hacking
if (isset($_SERVER['argv'][1]))
{
Router::$current_uri = $_SERVER['argv'][1];
// Remove GET string from segments
if (($query = strpos(Router::$current_uri, '?')) !== FALSE)
{
list (Router::$current_uri, $query) = explode('?', Router::$current_uri, 2);
// Parse the query string into $_GET
parse_str($query, $_GET);
// Convert $_GET to UTF-8
$_GET = utf8::clean($_GET);
}
}
}
elseif (isset($_GET['kohana_uri']))
{
// Use the URI defined in the query string
Router::$current_uri = $_GET['kohana_uri'];
// Remove the URI from $_GET
unset($_GET['kohana_uri']);
// Remove the URI from $_SERVER['QUERY_STRING']
$_SERVER['QUERY_STRING'] = preg_replace('~\bkohana_uri\b[^&]*+&?~', '', $_SERVER['QUERY_STRING']);
}
elseif (isset($_SERVER['PATH_INFO']) AND $_SERVER['PATH_INFO'])
{
Router::$current_uri = $_SERVER['PATH_INFO'];
}
elseif (isset($_SERVER['ORIG_PATH_INFO']) AND $_SERVER['ORIG_PATH_INFO'])
{
Router::$current_uri = $_SERVER['ORIG_PATH_INFO'];
}
elseif (isset($_SERVER['PHP_SELF']) AND $_SERVER['PHP_SELF'])
{
Router::$current_uri = $_SERVER['PHP_SELF'];
}
if (($strpos_fc = strpos(Router::$current_uri, KOHANA)) !== FALSE)
{
// Remove the front controller from the current uri
Router::$current_uri = (string) substr(Router::$current_uri, $strpos_fc + strlen(KOHANA));
}
// Remove slashes from the start and end of the URI
Router::$current_uri = trim(Router::$current_uri, '/');
if (Router::$current_uri !== '')
{
if ($suffix = Kohana::config('core.url_suffix') AND strpos(Router::$current_uri, $suffix) !== FALSE)
{
// Remove the URL suffix
Router::$current_uri = preg_replace('#'.preg_quote($suffix).'$#u', '', Router::$current_uri);
// Set the URL suffix
Router::$url_suffix = $suffix;
}
// Reduce multiple slashes into single slashes
Router::$current_uri = preg_replace('#//+#', '/', Router::$current_uri);
}
}
/**
* Generates routed URI from given URI.
*
* @param string URI to convert
* @return string Routed uri
*/
public static function routed_uri($uri)
{
if (Router::$routes === NULL)
{
// Load routes
Router::$routes = Kohana::config('routes');
}
// Prepare variables
$routed_uri = $uri = trim($uri, '/');
if (isset(Router::$routes[$uri]))
{
// Literal match, no need for regex
$routed_uri = Router::$routes[$uri];
}
else
{
// Loop through the routes and see if anything matches
foreach (Router::$routes as $key => $val)
{
if ($key === '_default') continue;
// Trim slashes
$key = trim($key, '/');
$val = trim($val, '/');
if (preg_match('#^'.$key.'$#u', $uri))
{
if (strpos($val, '$') !== FALSE)
{
// Use regex routing
$routed_uri = preg_replace('#^'.$key.'$#u', $val, $uri);
}
else
{
// Standard routing
$routed_uri = $val;
}
// A valid route has been found
break;
}
}
}
if (isset(Router::$routes[$routed_uri]))
{
// Check for double routing (without regex)
$routed_uri = Router::$routes[$routed_uri];
}
return trim($routed_uri, '/');
}
} // End Router

View File

@@ -0,0 +1,457 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* Session library.
*
* $Id: Session.php 4073 2009-03-13 17:53:58Z Shadowhand $
*
* @package Core
* @author Kohana Team
* @copyright (c) 2007-2008 Kohana Team
* @license http://kohanaphp.com/license.html
*/
class Session_Core {
// Session singleton
protected static $instance;
// Protected key names (cannot be set by the user)
protected static $protect = array('session_id', 'user_agent', 'last_activity', 'ip_address', 'total_hits', '_kf_flash_');
// Configuration and driver
protected static $config;
protected static $driver;
// Flash variables
protected static $flash;
// Input library
protected $input;
/**
* Singleton instance of Session.
*/
public static function instance()
{
if (Session::$instance == NULL)
{
// Create a new instance
new Session;
}
return Session::$instance;
}
/**
* On first session instance creation, sets up the driver and creates session.
*/
public function __construct()
{
$this->input = Input::instance();
// This part only needs to be run once
if (Session::$instance === NULL)
{
// Load config
Session::$config = Kohana::config('session');
// Makes a mirrored array, eg: foo=foo
Session::$protect = array_combine(Session::$protect, Session::$protect);
// Configure garbage collection
ini_set('session.gc_probability', (int) Session::$config['gc_probability']);
ini_set('session.gc_divisor', 100);
ini_set('session.gc_maxlifetime', (Session::$config['expiration'] == 0) ? 86400 : Session::$config['expiration']);
// Create a new session
$this->create();
if (Session::$config['regenerate'] > 0 AND ($_SESSION['total_hits'] % Session::$config['regenerate']) === 0)
{
// Regenerate session id and update session cookie
$this->regenerate();
}
else
{
// Always update session cookie to keep the session alive
cookie::set(Session::$config['name'], $_SESSION['session_id'], Session::$config['expiration']);
}
// Close the session just before sending the headers, so that
// the session cookie(s) can be written.
Event::add('system.send_headers', array($this, 'write_close'));
// Make sure that sessions are closed before exiting
register_shutdown_function(array($this, 'write_close'));
// Singleton instance
Session::$instance = $this;
}
Kohana::log('debug', 'Session Library initialized');
}
/**
* Get the session id.
*
* @return string
*/
public function id()
{
return $_SESSION['session_id'];
}
/**
* Create a new session.
*
* @param array variables to set after creation
* @return void
*/
public function create($vars = NULL)
{
// Destroy any current sessions
$this->destroy();
if (Session::$config['driver'] !== 'native')
{
// Set driver name
$driver = 'Session_'.ucfirst(Session::$config['driver']).'_Driver';
// Load the driver
if ( ! Kohana::auto_load($driver))
throw new Kohana_Exception('core.driver_not_found', Session::$config['driver'], get_class($this));
// Initialize the driver
Session::$driver = new $driver();
// Validate the driver
if ( ! (Session::$driver instanceof Session_Driver))
throw new Kohana_Exception('core.driver_implements', Session::$config['driver'], get_class($this), 'Session_Driver');
// Register non-native driver as the session handler
session_set_save_handler
(
array(Session::$driver, 'open'),
array(Session::$driver, 'close'),
array(Session::$driver, 'read'),
array(Session::$driver, 'write'),
array(Session::$driver, 'destroy'),
array(Session::$driver, 'gc')
);
}
// Validate the session name
if ( ! preg_match('~^(?=.*[a-z])[a-z0-9_]++$~iD', Session::$config['name']))
throw new Kohana_Exception('session.invalid_session_name', Session::$config['name']);
// Name the session, this will also be the name of the cookie
session_name(Session::$config['name']);
// Set the session cookie parameters
session_set_cookie_params
(
Session::$config['expiration'],
Kohana::config('cookie.path'),
Kohana::config('cookie.domain'),
Kohana::config('cookie.secure')
);
// Start the session!
session_start();
// Put session_id in the session variable
$_SESSION['session_id'] = session_id();
// Set defaults
if ( ! isset($_SESSION['_kf_flash_']))
{
$_SESSION['total_hits'] = 0;
$_SESSION['_kf_flash_'] = array();
$_SESSION['user_agent'] = Kohana::$user_agent;
$_SESSION['ip_address'] = $this->input->ip_address();
}
// Set up flash variables
Session::$flash =& $_SESSION['_kf_flash_'];
// Increase total hits
$_SESSION['total_hits'] += 1;
// Validate data only on hits after one
if ($_SESSION['total_hits'] > 1)
{
// Validate the session
foreach (Session::$config['validate'] as $valid)
{
switch ($valid)
{
// Check user agent for consistency
case 'user_agent':
if ($_SESSION[$valid] !== Kohana::$user_agent)
return $this->create();
break;
// Check ip address for consistency
case 'ip_address':
if ($_SESSION[$valid] !== $this->input->$valid())
return $this->create();
break;
// Check expiration time to prevent users from manually modifying it
case 'expiration':
if (time() - $_SESSION['last_activity'] > ini_get('session.gc_maxlifetime'))
return $this->create();
break;
}
}
}
// Expire flash keys
$this->expire_flash();
// Update last activity
$_SESSION['last_activity'] = time();
// Set the new data
Session::set($vars);
}
/**
* Regenerates the global session id.
*
* @return void
*/
public function regenerate()
{
if (Session::$config['driver'] === 'native')
{
// Generate a new session id
// Note: also sets a new session cookie with the updated id
session_regenerate_id(TRUE);
// Update session with new id
$_SESSION['session_id'] = session_id();
}
else
{
// Pass the regenerating off to the driver in case it wants to do anything special
$_SESSION['session_id'] = Session::$driver->regenerate();
}
// Get the session name
$name = session_name();
if (isset($_COOKIE[$name]))
{
// Change the cookie value to match the new session id to prevent "lag"
$_COOKIE[$name] = $_SESSION['session_id'];
}
}
/**
* Destroys the current session.
*
* @return void
*/
public function destroy()
{
if (session_id() !== '')
{
// Get the session name
$name = session_name();
// Destroy the session
session_destroy();
// Re-initialize the array
$_SESSION = array();
// Delete the session cookie
cookie::delete($name);
}
}
/**
* Runs the system.session_write event, then calls session_write_close.
*
* @return void
*/
public function write_close()
{
static $run;
if ($run === NULL)
{
$run = TRUE;
// Run the events that depend on the session being open
Event::run('system.session_write');
// Expire flash keys
$this->expire_flash();
// Close the session
session_write_close();
}
}
/**
* Set a session variable.
*
* @param string|array key, or array of values
* @param mixed value (if keys is not an array)
* @return void
*/
public function set($keys, $val = FALSE)
{
if (empty($keys))
return FALSE;
if ( ! is_array($keys))
{
$keys = array($keys => $val);
}
foreach ($keys as $key => $val)
{
if (isset(Session::$protect[$key]))
continue;
// Set the key
$_SESSION[$key] = $val;
}
}
/**
* Set a flash variable.
*
* @param string|array key, or array of values
* @param mixed value (if keys is not an array)
* @return void
*/
public function set_flash($keys, $val = FALSE)
{
if (empty($keys))
return FALSE;
if ( ! is_array($keys))
{
$keys = array($keys => $val);
}
foreach ($keys as $key => $val)
{
if ($key == FALSE)
continue;
Session::$flash[$key] = 'new';
Session::set($key, $val);
}
}
/**
* Freshen one, multiple or all flash variables.
*
* @param string variable key(s)
* @return void
*/
public function keep_flash($keys = NULL)
{
$keys = ($keys === NULL) ? array_keys(Session::$flash) : func_get_args();
foreach ($keys as $key)
{
if (isset(Session::$flash[$key]))
{
Session::$flash[$key] = 'new';
}
}
}
/**
* Expires old flash data and removes it from the session.
*
* @return void
*/
public function expire_flash()
{
static $run;
// Method can only be run once
if ($run === TRUE)
return;
if ( ! empty(Session::$flash))
{
foreach (Session::$flash as $key => $state)
{
if ($state === 'old')
{
// Flash has expired
unset(Session::$flash[$key], $_SESSION[$key]);
}
else
{
// Flash will expire
Session::$flash[$key] = 'old';
}
}
}
// Method has been run
$run = TRUE;
}
/**
* Get a variable. Access to sub-arrays is supported with key.subkey.
*
* @param string variable key
* @param mixed default value returned if variable does not exist
* @return mixed Variable data if key specified, otherwise array containing all session data.
*/
public function get($key = FALSE, $default = FALSE)
{
if (empty($key))
return $_SESSION;
$result = isset($_SESSION[$key]) ? $_SESSION[$key] : Kohana::key_string($_SESSION, $key);
return ($result === NULL) ? $default : $result;
}
/**
* Get a variable, and delete it.
*
* @param string variable key
* @param mixed default value returned if variable does not exist
* @return mixed
*/
public function get_once($key, $default = FALSE)
{
$return = Session::get($key, $default);
Session::delete($key);
return $return;
}
/**
* Delete one or more variables.
*
* @param string variable key(s)
* @return void
*/
public function delete($keys)
{
$args = func_get_args();
foreach ($args as $key)
{
if (isset(Session::$protect[$key]))
continue;
// Unset the key
unset($_SESSION[$key]);
}
}
} // End Session Class

View File

@@ -0,0 +1,130 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* [Tag cloud][ref-tcl] creation library.
*
* [ref-tcl]: http://en.wikipedia.org/wiki/Tag_cloud
*
* $Id: Tagcloud.php 3824 2008-12-20 17:13:06Z samsoir $
*
* @package Tagcloud
* @author Kohana Team
* @copyright (c) 2008 Kohana Team
* @license http://kohanaphp.com/license.html
*/
class Tagcloud_Core {
/**
* Creates a new Tagcloud instance and returns it.
*
* @chainable
* @param array elements of the tagcloud
* @param integer minimum font size
* @param integer maximum font size
* @return Tagcloud
*/
public static function factory(array $elements, $min_size = NULL, $max_size = NULL, $shuffle = FALSE)
{
return new Tagcloud($elements, $min_size, $max_size, $shuffle);
}
public $min_size = 80;
public $max_size = 140;
public $attributes = array('class' => 'tag');
public $shuffle = FALSE;
// Tag elements, biggest and smallest values
protected $elements;
protected $biggest;
protected $smallest;
/**
* Construct a new tagcloud. The elements must be passed in as an array,
* with each entry in the array having a "title" ,"link", and "count" key.
* Font sizes will be applied via the "style" attribute as a percentage.
*
* @param array elements of the tagcloud
* @param integer minimum font size
* @param integer maximum font size
* @return void
*/
public function __construct(array $elements, $min_size = NULL, $max_size = NULL, $shuffle = FALSE)
{
$this->elements = $elements;
if($shuffle !== FALSE)
{
$this->shuffle = TRUE;
}
$counts = array();
foreach ($elements as $data)
{
$counts[] = $data['count'];
}
// Find the biggest and smallest values of the elements
$this->biggest = max($counts);
$this->smallest = min($counts);
if ($min_size !== NULL)
{
$this->min_size = $min_size;
}
if ($max_size !== NULL)
{
$this->max_size = $max_size;
}
}
/**
* Magic __toString method. Returns all of the links as a single string.
*
* @return string
*/
public function __toString()
{
return implode("\n", $this->render());
}
/**
* Renders the elements of the tagcloud into an array of links.
*
* @return array
*/
public function render()
{
if ($this->shuffle === TRUE)
{
shuffle($this->elements);
}
// Minimum values must be 1 to prevent divide by zero errors
$range = max($this->biggest - $this->smallest, 1);
$scale = max($this->max_size - $this->min_size, 1);
// Import the attributes locally to prevent overwrites
$attr = $this->attributes;
$output = array();
foreach ($this->elements as $data)
{
if (strpos($data['title'], ' ') !== FALSE)
{
// Replace spaces with non-breaking spaces to prevent line wrapping
// in the middle of a link
$data['title'] = str_replace(' ', '&nbsp;', $data['title']);
}
// Determine the size based on the min/max scale and the smallest/biggest range
$size = ((($data['count'] - $this->smallest) * $scale) / $range) + $this->min_size;
$attr['style'] = 'font-size: '.round($size, 0).'%';
$output[] = html::anchor($data['link'], $data['title'], $attr)."\n";
}
return $output;
}
} // End Tagcloud

View File

@@ -0,0 +1,279 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* URI library.
*
* $Id: URI.php 4072 2009-03-13 17:20:38Z jheathco $
*
* @package Core
* @author Kohana Team
* @copyright (c) 2007-2008 Kohana Team
* @license http://kohanaphp.com/license.html
*/
class URI_Core extends Router {
/**
* Returns a singleton instance of URI.
*
* @return object
*/
public static function instance()
{
static $instance;
if ($instance == NULL)
{
// Initialize the URI instance
$instance = new URI;
}
return $instance;
}
/**
* Retrieve a specific URI segment.
*
* @param integer|string segment number or label
* @param mixed default value returned if segment does not exist
* @return string
*/
public function segment($index = 1, $default = FALSE)
{
if (is_string($index))
{
if (($key = array_search($index, URI::$segments)) === FALSE)
return $default;
$index = $key + 2;
}
$index = (int) $index - 1;
return isset(URI::$segments[$index]) ? URI::$segments[$index] : $default;
}
/**
* Retrieve a specific routed URI segment.
*
* @param integer|string rsegment number or label
* @param mixed default value returned if segment does not exist
* @return string
*/
public function rsegment($index = 1, $default = FALSE)
{
if (is_string($index))
{
if (($key = array_search($index, URI::$rsegments)) === FALSE)
return $default;
$index = $key + 2;
}
$index = (int) $index - 1;
return isset(URI::$rsegments[$index]) ? URI::$rsegments[$index] : $default;
}
/**
* Retrieve a specific URI argument.
* This is the part of the segments that does not indicate controller or method
*
* @param integer|string argument number or label
* @param mixed default value returned if segment does not exist
* @return string
*/
public function argument($index = 1, $default = FALSE)
{
if (is_string($index))
{
if (($key = array_search($index, URI::$arguments)) === FALSE)
return $default;
$index = $key + 2;
}
$index = (int) $index - 1;
return isset(URI::$arguments[$index]) ? URI::$arguments[$index] : $default;
}
/**
* Returns an array containing all the URI segments.
*
* @param integer segment offset
* @param boolean return an associative array
* @return array
*/
public function segment_array($offset = 0, $associative = FALSE)
{
return $this->build_array(URI::$segments, $offset, $associative);
}
/**
* Returns an array containing all the re-routed URI segments.
*
* @param integer rsegment offset
* @param boolean return an associative array
* @return array
*/
public function rsegment_array($offset = 0, $associative = FALSE)
{
return $this->build_array(URI::$rsegments, $offset, $associative);
}
/**
* Returns an array containing all the URI arguments.
*
* @param integer segment offset
* @param boolean return an associative array
* @return array
*/
public function argument_array($offset = 0, $associative = FALSE)
{
return $this->build_array(URI::$arguments, $offset, $associative);
}
/**
* Creates a simple or associative array from an array and an offset.
* Used as a helper for (r)segment_array and argument_array.
*
* @param array array to rebuild
* @param integer offset to start from
* @param boolean create an associative array
* @return array
*/
public function build_array($array, $offset = 0, $associative = FALSE)
{
// Prevent the keys from being improperly indexed
array_unshift($array, 0);
// Slice the array, preserving the keys
$array = array_slice($array, $offset + 1, count($array) - 1, TRUE);
if ($associative === FALSE)
return $array;
$associative = array();
$pairs = array_chunk($array, 2);
foreach ($pairs as $pair)
{
// Add the key/value pair to the associative array
$associative[$pair[0]] = isset($pair[1]) ? $pair[1] : '';
}
return $associative;
}
/**
* Returns the complete URI as a string.
*
* @return string
*/
public function string()
{
return URI::$current_uri;
}
/**
* Magic method for converting an object to a string.
*
* @return string
*/
public function __toString()
{
return URI::$current_uri;
}
/**
* Returns the total number of URI segments.
*
* @return integer
*/
public function total_segments()
{
return count(URI::$segments);
}
/**
* Returns the total number of re-routed URI segments.
*
* @return integer
*/
public function total_rsegments()
{
return count(URI::$rsegments);
}
/**
* Returns the total number of URI arguments.
*
* @return integer
*/
public function total_arguments()
{
return count(URI::$arguments);
}
/**
* Returns the last URI segment.
*
* @param mixed default value returned if segment does not exist
* @return string
*/
public function last_segment($default = FALSE)
{
if (($end = $this->total_segments()) < 1)
return $default;
return URI::$segments[$end - 1];
}
/**
* Returns the last re-routed URI segment.
*
* @param mixed default value returned if segment does not exist
* @return string
*/
public function last_rsegment($default = FALSE)
{
if (($end = $this->total_segments()) < 1)
return $default;
return URI::$rsegments[$end - 1];
}
/**
* Returns the path to the current controller (not including the actual
* controller), as a web path.
*
* @param boolean return a full url, or only the path specifically
* @return string
*/
public function controller_path($full = TRUE)
{
return ($full) ? url::site(URI::$controller_path) : URI::$controller_path;
}
/**
* Returns the current controller, as a web path.
*
* @param boolean return a full url, or only the controller specifically
* @return string
*/
public function controller($full = TRUE)
{
return ($full) ? url::site(URI::$controller_path.URI::$controller) : URI::$controller;
}
/**
* Returns the current method, as a web path.
*
* @param boolean return a full url, or only the method specifically
* @return string
*/
public function method($full = TRUE)
{
return ($full) ? url::site(URI::$controller_path.URI::$controller.'/'.URI::$method) : URI::$method;
}
} // End URI Class

View File

@@ -0,0 +1,826 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* Validation library.
*
* $Id: Validation.php 4120 2009-03-25 19:22:31Z jheathco $
*
* @package Validation
* @author Kohana Team
* @copyright (c) 2007-2008 Kohana Team
* @license http://kohanaphp.com/license.html
*/
class Validation_Core extends ArrayObject {
// Filters
protected $pre_filters = array();
protected $post_filters = array();
// Rules and callbacks
protected $rules = array();
protected $callbacks = array();
// Rules that are allowed to run on empty fields
protected $empty_rules = array('required', 'matches');
// Errors
protected $errors = array();
protected $messages = array();
// Fields that are expected to be arrays
protected $array_fields = array();
// Checks if there is data to validate.
protected $submitted;
/**
* Creates a new Validation instance.
*
* @param array array to use for validation
* @return object
*/
public static function factory(array $array)
{
return new Validation($array);
}
/**
* Sets the unique "any field" key and creates an ArrayObject from the
* passed array.
*
* @param array array to validate
* @return void
*/
public function __construct(array $array)
{
// The array is submitted if the array is not empty
$this->submitted = ! empty($array);
parent::__construct($array, ArrayObject::ARRAY_AS_PROPS | ArrayObject::STD_PROP_LIST);
}
/**
* Magic clone method, clears errors and messages.
*
* @return void
*/
public function __clone()
{
$this->errors = array();
$this->messages = array();
}
/**
* Create a copy of the current validation rules and change the array.
*
* @chainable
* @param array new array to validate
* @return Validation
*/
public function copy(array $array)
{
$copy = clone $this;
$copy->exchangeArray($array);
return $copy;
}
/**
* Test if the data has been submitted.
*
* @return boolean
*/
public function submitted($value = NULL)
{
if (is_bool($value))
{
$this->submitted = $value;
}
return $this->submitted;
}
/**
* Returns an array of all the field names that have filters, rules, or callbacks.
*
* @return array
*/
public function field_names()
{
// All the fields that are being validated
$fields = array_keys(array_merge
(
$this->pre_filters,
$this->rules,
$this->callbacks,
$this->post_filters
));
// Remove wildcard fields
$fields = array_diff($fields, array('*'));
return $fields;
}
/**
* Returns the array values of the current object.
*
* @return array
*/
public function as_array()
{
return $this->getArrayCopy();
}
/**
* Returns the ArrayObject values, removing all inputs without rules.
* To choose specific inputs, list the field name as arguments.
*
* @param boolean return only fields with filters, rules, and callbacks
* @return array
*/
public function safe_array()
{
// Load choices
$choices = func_get_args();
$choices = empty($choices) ? NULL : array_combine($choices, $choices);
// Get field names
$fields = $this->field_names();
$safe = array();
foreach ($fields as $field)
{
if ($choices === NULL OR isset($choices[$field]))
{
if (isset($this[$field]))
{
$value = $this[$field];
if (is_object($value))
{
// Convert the value back into an array
$value = $value->getArrayCopy();
}
}
else
{
// Even if the field is not in this array, it must be set
$value = NULL;
}
// Add the field to the array
$safe[$field] = $value;
}
}
return $safe;
}
/**
* Add additional rules that will forced, even for empty fields. All arguments
* passed will be appended to the list.
*
* @chainable
* @param string rule name
* @return object
*/
public function allow_empty_rules($rules)
{
// Any number of args are supported
$rules = func_get_args();
// Merge the allowed rules
$this->empty_rules = array_merge($this->empty_rules, $rules);
return $this;
}
/**
* Converts a filter, rule, or callback into a fully-qualified callback array.
*
* @return mixed
*/
protected function callback($callback)
{
if (is_string($callback))
{
if (strpos($callback, '::') !== FALSE)
{
$callback = explode('::', $callback);
}
elseif (function_exists($callback))
{
// No need to check if the callback is a method
$callback = $callback;
}
elseif (method_exists($this, $callback))
{
// The callback exists in Validation
$callback = array($this, $callback);
}
elseif (method_exists('valid', $callback))
{
// The callback exists in valid::
$callback = array('valid', $callback);
}
}
if ( ! is_callable($callback, FALSE))
{
if (is_array($callback))
{
if (is_object($callback[0]))
{
// Object instance syntax
$name = get_class($callback[0]).'->'.$callback[1];
}
else
{
// Static class syntax
$name = $callback[0].'::'.$callback[1];
}
}
else
{
// Function syntax
$name = $callback;
}
throw new Kohana_Exception('validation.not_callable', $name);
}
return $callback;
}
/**
* Add a pre-filter to one or more inputs. Pre-filters are applied before
* rules or callbacks are executed.
*
* @chainable
* @param callback filter
* @param string fields to apply filter to, use TRUE for all fields
* @return object
*/
public function pre_filter($filter, $field = TRUE)
{
if ($field === TRUE OR $field === '*')
{
// Use wildcard
$fields = array('*');
}
else
{
// Add the filter to specific inputs
$fields = func_get_args();
$fields = array_slice($fields, 1);
}
// Convert to a proper callback
$filter = $this->callback($filter);
foreach ($fields as $field)
{
// Add the filter to specified field
$this->pre_filters[$field][] = $filter;
}
return $this;
}
/**
* Add a post-filter to one or more inputs. Post-filters are applied after
* rules and callbacks have been executed.
*
* @chainable
* @param callback filter
* @param string fields to apply filter to, use TRUE for all fields
* @return object
*/
public function post_filter($filter, $field = TRUE)
{
if ($field === TRUE)
{
// Use wildcard
$fields = array('*');
}
else
{
// Add the filter to specific inputs
$fields = func_get_args();
$fields = array_slice($fields, 1);
}
// Convert to a proper callback
$filter = $this->callback($filter);
foreach ($fields as $field)
{
// Add the filter to specified field
$this->post_filters[$field][] = $filter;
}
return $this;
}
/**
* Add rules to a field. Validation rules may only return TRUE or FALSE and
* can not manipulate the value of a field.
*
* @chainable
* @param string field name
* @param callback rules (one or more arguments)
* @return object
*/
public function add_rules($field, $rules)
{
// Get the rules
$rules = func_get_args();
$rules = array_slice($rules, 1);
if ($field === TRUE)
{
// Use wildcard
$field = '*';
}
foreach ($rules as $rule)
{
// Arguments for rule
$args = NULL;
if (is_string($rule))
{
if (preg_match('/^([^\[]++)\[(.+)\]$/', $rule, $matches))
{
// Split the rule into the function and args
$rule = $matches[1];
$args = preg_split('/(?<!\\\\),\s*/', $matches[2]);
// Replace escaped comma with comma
$args = str_replace('\,', ',', $args);
}
}
if ($rule === 'is_array')
{
// This field is expected to be an array
$this->array_fields[$field] = $field;
}
// Convert to a proper callback
$rule = $this->callback($rule);
// Add the rule, with args, to the field
$this->rules[$field][] = array($rule, $args);
}
return $this;
}
/**
* Add callbacks to a field. Callbacks must accept the Validation object
* and the input name. Callback returns are not processed.
*
* @chainable
* @param string field name
* @param callbacks callbacks (unlimited number)
* @return object
*/
public function add_callbacks($field, $callbacks)
{
// Get all callbacks as an array
$callbacks = func_get_args();
$callbacks = array_slice($callbacks, 1);
if ($field === TRUE)
{
// Use wildcard
$field = '*';
}
foreach ($callbacks as $callback)
{
// Convert to a proper callback
$callback = $this->callback($callback);
// Add the callback to specified field
$this->callbacks[$field][] = $callback;
}
return $this;
}
/**
* Validate by processing pre-filters, rules, callbacks, and post-filters.
* All fields that have filters, rules, or callbacks will be initialized if
* they are undefined. Validation will only be run if there is data already
* in the array.
*
* @param object Validation object, used only for recursion
* @param object name of field for errors
* @return bool
*/
public function validate($object = NULL, $field_name = NULL)
{
if ($object === NULL)
{
// Use the current object
$object = $this;
}
// Get all field names
$fields = $this->field_names();
// Copy the array from the object, to optimize multiple sets
$array = $this->getArrayCopy();
foreach ($fields as $field)
{
if ($field === '*')
{
// Ignore wildcard
continue;
}
if ( ! isset($array[$field]))
{
if (isset($this->array_fields[$field]))
{
// This field must be an array
$array[$field] = array();
}
else
{
$array[$field] = NULL;
}
}
}
// Swap the array back into the object
$this->exchangeArray($array);
// Get all defined field names
$fields = array_keys($array);
foreach ($this->pre_filters as $field => $callbacks)
{
foreach ($callbacks as $callback)
{
if ($field === '*')
{
foreach ($fields as $f)
{
$this[$f] = is_array($this[$f]) ? array_map($callback, $this[$f]) : call_user_func($callback, $this[$f]);
}
}
else
{
$this[$field] = is_array($this[$field]) ? array_map($callback, $this[$field]) : call_user_func($callback, $this[$field]);
}
}
}
if ($this->submitted === FALSE)
return FALSE;
foreach ($this->rules as $field => $callbacks)
{
foreach ($callbacks as $callback)
{
// Separate the callback and arguments
list ($callback, $args) = $callback;
// Function or method name of the rule
$rule = is_array($callback) ? $callback[1] : $callback;
if ($field === '*')
{
foreach ($fields as $f)
{
// Note that continue, instead of break, is used when
// applying rules using a wildcard, so that all fields
// will be validated.
if (isset($this->errors[$f]))
{
// Prevent other rules from being evaluated if an error has occurred
continue;
}
if (empty($this[$f]) AND ! in_array($rule, $this->empty_rules))
{
// This rule does not need to be processed on empty fields
continue;
}
if ($args === NULL)
{
if ( ! call_user_func($callback, $this[$f]))
{
$this->errors[$f] = $rule;
// Stop validating this field when an error is found
continue;
}
}
else
{
if ( ! call_user_func($callback, $this[$f], $args))
{
$this->errors[$f] = $rule;
// Stop validating this field when an error is found
continue;
}
}
}
}
else
{
if (isset($this->errors[$field]))
{
// Prevent other rules from being evaluated if an error has occurred
break;
}
if ( ! in_array($rule, $this->empty_rules) AND ! $this->required($this[$field]))
{
// This rule does not need to be processed on empty fields
continue;
}
if ($args === NULL)
{
if ( ! call_user_func($callback, $this[$field]))
{
$this->errors[$field] = $rule;
// Stop validating this field when an error is found
break;
}
}
else
{
if ( ! call_user_func($callback, $this[$field], $args))
{
$this->errors[$field] = $rule;
// Stop validating this field when an error is found
break;
}
}
}
}
}
foreach ($this->callbacks as $field => $callbacks)
{
foreach ($callbacks as $callback)
{
if ($field === '*')
{
foreach ($fields as $f)
{
// Note that continue, instead of break, is used when
// applying rules using a wildcard, so that all fields
// will be validated.
if (isset($this->errors[$f]))
{
// Stop validating this field when an error is found
continue;
}
call_user_func($callback, $this, $f);
}
}
else
{
if (isset($this->errors[$field]))
{
// Stop validating this field when an error is found
break;
}
call_user_func($callback, $this, $field);
}
}
}
foreach ($this->post_filters as $field => $callbacks)
{
foreach ($callbacks as $callback)
{
if ($field === '*')
{
foreach ($fields as $f)
{
$this[$f] = is_array($this[$f]) ? array_map($callback, $this[$f]) : call_user_func($callback, $this[$f]);
}
}
else
{
$this[$field] = is_array($this[$field]) ? array_map($callback, $this[$field]) : call_user_func($callback, $this[$field]);
}
}
}
// Return TRUE if there are no errors
return $this->errors === array();
}
/**
* Add an error to an input.
*
* @chainable
* @param string input name
* @param string unique error name
* @return object
*/
public function add_error($field, $name)
{
$this->errors[$field] = $name;
return $this;
}
/**
* Sets or returns the message for an input.
*
* @chainable
* @param string input key
* @param string message to set
* @return string|object
*/
public function message($input = NULL, $message = NULL)
{
if ($message === NULL)
{
if ($input === NULL)
{
$messages = array();
$keys = array_keys($this->messages);
foreach ($keys as $input)
{
$messages[] = $this->message($input);
}
return implode("\n", $messages);
}
// Return nothing if no message exists
if (empty($this->messages[$input]))
return '';
// Return the HTML message string
return $this->messages[$input];
}
else
{
$this->messages[$input] = $message;
}
return $this;
}
/**
* Return the errors array.
*
* @param boolean load errors from a lang file
* @return array
*/
public function errors($file = NULL)
{
if ($file === NULL)
{
return $this->errors;
}
else
{
$errors = array();
foreach ($this->errors as $input => $error)
{
// Key for this input error
$key = "$file.$input.$error";
if (($errors[$input] = Kohana::lang($key)) === $key)
{
// Get the default error message
$errors[$input] = Kohana::lang("$file.$input.default");
}
}
return $errors;
}
}
/**
* Rule: required. Generates an error if the field has an empty value.
*
* @param mixed input value
* @return bool
*/
public function required($str)
{
if (is_object($str) AND $str instanceof ArrayObject)
{
// Get the array from the ArrayObject
$str = $str->getArrayCopy();
}
if (is_array($str))
{
return ! empty($str);
}
else
{
return ! ($str === '' OR $str === NULL OR $str === FALSE);
}
}
/**
* Rule: matches. Generates an error if the field does not match one or more
* other fields.
*
* @param mixed input value
* @param array input names to match against
* @return bool
*/
public function matches($str, array $inputs)
{
foreach ($inputs as $key)
{
if ($str !== (isset($this[$key]) ? $this[$key] : NULL))
return FALSE;
}
return TRUE;
}
/**
* Rule: length. Generates an error if the field is too long or too short.
*
* @param mixed input value
* @param array minimum, maximum, or exact length to match
* @return bool
*/
public function length($str, array $length)
{
if ( ! is_string($str))
return FALSE;
$size = utf8::strlen($str);
$status = FALSE;
if (count($length) > 1)
{
list ($min, $max) = $length;
if ($size >= $min AND $size <= $max)
{
$status = TRUE;
}
}
else
{
$status = ($size === (int) $length[0]);
}
return $status;
}
/**
* Rule: depends_on. Generates an error if the field does not depend on one
* or more other fields.
*
* @param mixed field name
* @param array field names to check dependency
* @return bool
*/
public function depends_on($field, array $fields)
{
foreach ($fields as $depends_on)
{
if ( ! isset($this[$depends_on]) OR $this[$depends_on] == NULL)
return FALSE;
}
return TRUE;
}
/**
* Rule: chars. Generates an error if the field contains characters outside of the list.
*
* @param string field value
* @param array allowed characters
* @return bool
*/
public function chars($value, array $chars)
{
return ! preg_match('![^'.implode('', $chars).']!u', $value);
}
} // End Validation

View File

@@ -0,0 +1,309 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* Loads and displays Kohana view files. Can also handle output of some binary
* files, such as image, Javascript, and CSS files.
*
* $Id: View.php 4072 2009-03-13 17:20:38Z jheathco $
*
* @package Core
* @author Kohana Team
* @copyright (c) 2007-2008 Kohana Team
* @license http://kohanaphp.com/license.html
*/
class View_Core {
// The view file name and type
protected $kohana_filename = FALSE;
protected $kohana_filetype = FALSE;
// View variable storage
protected $kohana_local_data = array();
protected static $kohana_global_data = array();
/**
* Creates a new View using the given parameters.
*
* @param string view name
* @param array pre-load data
* @param string type of file: html, css, js, etc.
* @return object
*/
public static function factory($name = NULL, $data = NULL, $type = NULL)
{
return new View($name, $data, $type);
}
/**
* Attempts to load a view and pre-load view data.
*
* @throws Kohana_Exception if the requested view cannot be found
* @param string view name
* @param array pre-load data
* @param string type of file: html, css, js, etc.
* @return void
*/
public function __construct($name = NULL, $data = NULL, $type = NULL)
{
if (is_string($name) AND $name !== '')
{
// Set the filename
$this->set_filename($name, $type);
}
if (is_array($data) AND ! empty($data))
{
// Preload data using array_merge, to allow user extensions
$this->kohana_local_data = array_merge($this->kohana_local_data, $data);
}
}
/**
* Magic method access to test for view property
*
* @param string View property to test for
* @return boolean
*/
public function __isset($key = NULL)
{
return $this->is_set($key);
}
/**
* Sets the view filename.
*
* @chainable
* @param string view filename
* @param string view file type
* @return object
*/
public function set_filename($name, $type = NULL)
{
if ($type == NULL)
{
// Load the filename and set the content type
$this->kohana_filename = Kohana::find_file('views', $name, TRUE);
$this->kohana_filetype = EXT;
}
else
{
// Check if the filetype is allowed by the configuration
if ( ! in_array($type, Kohana::config('view.allowed_filetypes')))
throw new Kohana_Exception('core.invalid_filetype', $type);
// Load the filename and set the content type
$this->kohana_filename = Kohana::find_file('views', $name, TRUE, $type);
$this->kohana_filetype = Kohana::config('mimes.'.$type);
if ($this->kohana_filetype == NULL)
{
// Use the specified type
$this->kohana_filetype = $type;
}
}
return $this;
}
/**
* Sets a view variable.
*
* @param string|array name of variable or an array of variables
* @param mixed value when using a named variable
* @return object
*/
public function set($name, $value = NULL)
{
if (is_array($name))
{
foreach ($name as $key => $value)
{
$this->__set($key, $value);
}
}
else
{
$this->__set($name, $value);
}
return $this;
}
/**
* Checks for a property existence in the view locally or globally. Unlike the built in __isset(),
* this method can take an array of properties to test simultaneously.
*
* @param string $key property name to test for
* @param array $key array of property names to test for
* @return boolean property test result
* @return array associative array of keys and boolean test result
*/
public function is_set( $key = FALSE )
{
// Setup result;
$result = FALSE;
// If key is an array
if (is_array($key))
{
// Set the result to an array
$result = array();
// Foreach key
foreach ($key as $property)
{
// Set the result to an associative array
$result[$property] = (array_key_exists($property, $this->kohana_local_data) OR array_key_exists($property, View::$kohana_global_data)) ? TRUE : FALSE;
}
}
else
{
// Otherwise just check one property
$result = (array_key_exists($key, $this->kohana_local_data) OR array_key_exists($key, View::$kohana_global_data)) ? TRUE : FALSE;
}
// Return the result
return $result;
}
/**
* Sets a bound variable by reference.
*
* @param string name of variable
* @param mixed variable to assign by reference
* @return object
*/
public function bind($name, & $var)
{
$this->kohana_local_data[$name] =& $var;
return $this;
}
/**
* Sets a view global variable.
*
* @param string|array name of variable or an array of variables
* @param mixed value when using a named variable
* @return void
*/
public static function set_global($name, $value = NULL)
{
if (is_array($name))
{
foreach ($name as $key => $value)
{
View::$kohana_global_data[$key] = $value;
}
}
else
{
View::$kohana_global_data[$name] = $value;
}
}
/**
* Magically sets a view variable.
*
* @param string variable key
* @param string variable value
* @return void
*/
public function __set($key, $value)
{
$this->kohana_local_data[$key] = $value;
}
/**
* Magically gets a view variable.
*
* @param string variable key
* @return mixed variable value if the key is found
* @return void if the key is not found
*/
public function &__get($key)
{
if (isset($this->kohana_local_data[$key]))
return $this->kohana_local_data[$key];
if (isset(View::$kohana_global_data[$key]))
return View::$kohana_global_data[$key];
if (isset($this->$key))
return $this->$key;
}
/**
* Magically converts view object to string.
*
* @return string
*/
public function __toString()
{
try
{
return $this->render();
}
catch (Exception $e)
{
// Display the exception using its internal __toString method
return (string) $e;
}
}
/**
* Renders a view.
*
* @param boolean set to TRUE to echo the output instead of returning it
* @param callback special renderer to pass the output through
* @return string if print is FALSE
* @return void if print is TRUE
*/
public function render($print = FALSE, $renderer = FALSE)
{
if (empty($this->kohana_filename))
throw new Kohana_Exception('core.view_set_filename');
if (is_string($this->kohana_filetype))
{
// Merge global and local data, local overrides global with the same name
$data = array_merge(View::$kohana_global_data, $this->kohana_local_data);
// Load the view in the controller for access to $this
$output = Kohana::$instance->_kohana_load_view($this->kohana_filename, $data);
if ($renderer !== FALSE AND is_callable($renderer, TRUE))
{
// Pass the output through the user defined renderer
$output = call_user_func($renderer, $output);
}
if ($print === TRUE)
{
// Display the output
echo $output;
return;
}
}
else
{
// Set the content type and size
header('Content-Type: '.$this->kohana_filetype[0]);
if ($print === TRUE)
{
if ($file = fopen($this->kohana_filename, 'rb'))
{
// Display the output
fpassthru($file);
fclose($file);
}
return;
}
// Fetch the file contents
$output = file_get_contents($this->kohana_filename);
}
return $output;
}
} // End View

View File

@@ -0,0 +1,40 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* Cache driver interface.
*
* $Id: Cache.php 4046 2009-03-05 19:23:29Z Shadowhand $
*
* @package Cache
* @author Kohana Team
* @copyright (c) 2007-2008 Kohana Team
* @license http://kohanaphp.com/license.html
*/
interface Cache_Driver {
/**
* Set a cache item.
*/
public function set($id, $data, array $tags = NULL, $lifetime);
/**
* Find all of the cache ids for a given tag.
*/
public function find($tag);
/**
* Get a cache item.
* Return NULL if the cache item is not found.
*/
public function get($id);
/**
* Delete cache items by id or tag.
*/
public function delete($id, $tag = FALSE);
/**
* Deletes all expired cache items.
*/
public function delete_expired();
} // End Cache Driver

View File

@@ -0,0 +1,64 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* APC-based Cache driver.
*
* $Id: Apc.php 4046 2009-03-05 19:23:29Z Shadowhand $
*
* @package Cache
* @author Kohana Team
* @copyright (c) 2007-2008 Kohana Team
* @license http://kohanaphp.com/license.html
*/
class Cache_Apc_Driver implements Cache_Driver {
public function __construct()
{
if ( ! extension_loaded('apc'))
throw new Kohana_Exception('cache.extension_not_loaded', 'apc');
}
public function get($id)
{
return (($return = apc_fetch($id)) === FALSE) ? NULL : $return;
}
public function set($id, $data, array $tags = NULL, $lifetime)
{
if ( ! empty($tags))
{
Kohana::log('error', 'Cache: tags are unsupported by the APC driver');
}
return apc_store($id, $data, $lifetime);
}
public function find($tag)
{
Kohana::log('error', 'Cache: tags are unsupported by the APC driver');
return array();
}
public function delete($id, $tag = FALSE)
{
if ($tag === TRUE)
{
Kohana::log('error', 'Cache: tags are unsupported by the APC driver');
return FALSE;
}
elseif ($id === TRUE)
{
return apc_clear_cache('user');
}
else
{
return apc_delete($id);
}
}
public function delete_expired()
{
return TRUE;
}
} // End Cache APC Driver

View File

@@ -0,0 +1,66 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* Eaccelerator-based Cache driver.
*
* $Id: Eaccelerator.php 4046 2009-03-05 19:23:29Z Shadowhand $
*
* @package Cache
* @author Kohana Team
* @copyright (c) 2007-2008 Kohana Team
* @license http://kohanaphp.com/license.html
*/
class Cache_Eaccelerator_Driver implements Cache_Driver {
public function __construct()
{
if ( ! extension_loaded('eaccelerator'))
throw new Kohana_Exception('cache.extension_not_loaded', 'eaccelerator');
}
public function get($id)
{
return eaccelerator_get($id);
}
public function find($tag)
{
Kohana::log('error', 'tags are unsupported by the eAccelerator driver');
return array();
}
public function set($id, $data, array $tags = NULL, $lifetime)
{
if ( ! empty($tags))
{
Kohana::log('error', 'tags are unsupported by the eAccelerator driver');
}
return eaccelerator_put($id, $data, $lifetime);
}
public function delete($id, $tag = FALSE)
{
if ($tag === TRUE)
{
Kohana::log('error', 'tags are unsupported by the eAccelerator driver');
return FALSE;
}
elseif ($id === TRUE)
{
return eaccelerator_clean();
}
else
{
return eaccelerator_rm($id);
}
}
public function delete_expired()
{
eaccelerator_gc();
return TRUE;
}
} // End Cache eAccelerator Driver

View File

@@ -0,0 +1,261 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* File-based Cache driver.
*
* $Id: File.php 4046 2009-03-05 19:23:29Z Shadowhand $
*
* @package Cache
* @author Kohana Team
* @copyright (c) 2007-2008 Kohana Team
* @license http://kohanaphp.com/license.html
*/
class Cache_File_Driver implements Cache_Driver {
protected $directory = '';
/**
* Tests that the storage location is a directory and is writable.
*/
public function __construct($directory)
{
// Find the real path to the directory
$directory = str_replace('\\', '/', realpath($directory)).'/';
// Make sure the cache directory is writable
if ( ! is_dir($directory) OR ! is_writable($directory))
throw new Kohana_Exception('cache.unwritable', $directory);
// Directory is valid
$this->directory = $directory;
}
/**
* Finds an array of files matching the given id or tag.
*
* @param string cache id or tag
* @param bool search for tags
* @return array of filenames matching the id or tag
*/
public function exists($id, $tag = FALSE)
{
if ($id === TRUE)
{
// Find all the files
return glob($this->directory.'*~*~*');
}
elseif ($tag === TRUE)
{
// Find all the files that have the tag name
$paths = glob($this->directory.'*~*'.$id.'*~*');
// Find all tags matching the given tag
$files = array();
foreach ($paths as $path)
{
// Split the files
$tags = explode('~', basename($path));
// Find valid tags
if (count($tags) !== 3 OR empty($tags[1]))
continue;
// Split the tags by plus signs, used to separate tags
$tags = explode('+', $tags[1]);
if (in_array($tag, $tags))
{
// Add the file to the array, it has the requested tag
$files[] = $path;
}
}
return $files;
}
else
{
// Find the file matching the given id
return glob($this->directory.$id.'~*');
}
}
/**
* Sets a cache item to the given data, tags, and lifetime.
*
* @param string cache id to set
* @param string data in the cache
* @param array cache tags
* @param integer lifetime
* @return bool
*/
public function set($id, $data, array $tags = NULL, $lifetime)
{
// Remove old cache files
$this->delete($id);
// Cache File driver expects unix timestamp
if ($lifetime !== 0)
{
$lifetime += time();
}
if ( ! empty($tags))
{
// Convert the tags into a string list
$tags = implode('+', $tags);
}
// Write out a serialized cache
return (bool) file_put_contents($this->directory.$id.'~'.$tags.'~'.$lifetime, serialize($data));
}
/**
* Finds an array of ids for a given tag.
*
* @param string tag name
* @return array of ids that match the tag
*/
public function find($tag)
{
// An array will always be returned
$result = array();
if ($paths = $this->exists($tag, TRUE))
{
// Length of directory name
$offset = strlen($this->directory);
// Find all the files with the given tag
foreach ($paths as $path)
{
// Get the id from the filename
list($id, $junk) = explode('~', basename($path), 2);
if (($data = $this->get($id)) !== FALSE)
{
// Add the result to the array
$result[$id] = $data;
}
}
}
return $result;
}
/**
* Fetches a cache item. This will delete the item if it is expired or if
* the hash does not match the stored hash.
*
* @param string cache id
* @return mixed|NULL
*/
public function get($id)
{
if ($file = $this->exists($id))
{
// Use the first file
$file = current($file);
// Validate that the cache has not expired
if ($this->expired($file))
{
// Remove this cache, it has expired
$this->delete($id);
}
else
{
// Turn off errors while reading the file
$ER = error_reporting(0);
if (($data = file_get_contents($file)) !== FALSE)
{
// Unserialize the data
$data = unserialize($data);
}
else
{
// Delete the data
unset($data);
}
// Turn errors back on
error_reporting($ER);
}
}
// Return NULL if there is no data
return isset($data) ? $data : NULL;
}
/**
* Deletes a cache item by id or tag
*
* @param string cache id or tag, or TRUE for "all items"
* @param boolean use tags
* @return boolean
*/
public function delete($id, $tag = FALSE)
{
$files = $this->exists($id, $tag);
if (empty($files))
return FALSE;
// Disable all error reporting while deleting
$ER = error_reporting(0);
foreach ($files as $file)
{
// Remove the cache file
if ( ! unlink($file))
Kohana::log('error', 'Cache: Unable to delete cache file: '.$file);
}
// Turn on error reporting again
error_reporting($ER);
return TRUE;
}
/**
* Deletes all cache files that are older than the current time.
*
* @return void
*/
public function delete_expired()
{
if ($files = $this->exists(TRUE))
{
// Disable all error reporting while deleting
$ER = error_reporting(0);
foreach ($files as $file)
{
if ($this->expired($file))
{
// The cache file has already expired, delete it
if ( ! unlink($file))
Kohana::log('error', 'Cache: Unable to delete cache file: '.$file);
}
}
// Turn on error reporting again
error_reporting($ER);
}
}
/**
* Check if a cache file has expired by filename.
*
* @param string filename
* @return bool
*/
protected function expired($file)
{
// Get the expiration time
$expires = (int) substr($file, strrpos($file, '~') + 1);
// Expirations of 0 are "never expire"
return ($expires !== 0 AND $expires <= time());
}
} // End Cache File Driver

View File

@@ -0,0 +1,191 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* Memcache-based Cache driver.
*
* $Id: Memcache.php 4102 2009-03-19 12:55:54Z Shadowhand $
*
* @package Cache
* @author Kohana Team
* @copyright (c) 2007-2008 Kohana Team
* @license http://kohanaphp.com/license.html
*/
class Cache_Memcache_Driver implements Cache_Driver {
const TAGS_KEY = 'memcache_tags_array';
// Cache backend object and flags
protected $backend;
protected $flags;
// Tags array
protected static $tags;
// Have the tags been changed?
protected static $tags_changed = FALSE;
public function __construct()
{
if ( ! extension_loaded('memcache'))
throw new Kohana_Exception('cache.extension_not_loaded', 'memcache');
$this->backend = new Memcache;
$this->flags = Kohana::config('cache_memcache.compression') ? MEMCACHE_COMPRESSED : FALSE;
$servers = Kohana::config('cache_memcache.servers');
foreach ($servers as $server)
{
// Make sure all required keys are set
$server += array('host' => '127.0.0.1', 'port' => 11211, 'persistent' => FALSE);
// Add the server to the pool
$this->backend->addServer($server['host'], $server['port'], (bool) $server['persistent'])
or Kohana::log('error', 'Cache: Connection failed: '.$server['host']);
}
// Load tags
self::$tags = $this->backend->get(self::TAGS_KEY);
if ( ! is_array(self::$tags))
{
// Create a new tags array
self::$tags = array();
// Tags have been created
self::$tags_changed = TRUE;
}
}
public function __destruct()
{
if (self::$tags_changed === TRUE)
{
// Save the tags
$this->backend->set(self::TAGS_KEY, self::$tags, $this->flags, 0);
// Tags are now unchanged
self::$tags_changed = FALSE;
}
}
public function find($tag)
{
if (isset(self::$tags[$tag]) AND $results = $this->backend->get(self::$tags[$tag]))
{
// Return all the found caches
return $results;
}
else
{
// No matching tags
return array();
}
}
public function get($id)
{
return (($return = $this->backend->get($id)) === FALSE) ? NULL : $return;
}
public function set($id, $data, array $tags = NULL, $lifetime)
{
if ( ! empty($tags))
{
// Tags will be changed
self::$tags_changed = TRUE;
foreach ($tags as $tag)
{
// Add the id to each tag
self::$tags[$tag][$id] = $id;
}
}
if ($lifetime !== 0)
{
// Memcache driver expects unix timestamp
$lifetime += time();
}
// Set a new value
return $this->backend->set($id, $data, $this->flags, $lifetime);
}
public function delete($id, $tag = FALSE)
{
// Tags will be changed
self::$tags_changed = TRUE;
if ($id === TRUE)
{
if ($status = $this->backend->flush())
{
// Remove all tags, all items have been deleted
self::$tags = array();
// We must sleep after flushing, or overwriting will not work!
// @see http://php.net/manual/en/function.memcache-flush.php#81420
sleep(1);
}
return $status;
}
elseif ($tag === TRUE)
{
if (isset(self::$tags[$id]))
{
foreach (self::$tags[$id] as $_id)
{
// Delete each id in the tag
$this->backend->delete($_id);
}
// Delete the tag
unset(self::$tags[$id]);
}
return TRUE;
}
else
{
foreach (self::$tags as $tag => $_ids)
{
if (isset(self::$tags[$tag][$id]))
{
// Remove the id from the tags
unset(self::$tags[$tag][$id]);
}
}
return $this->backend->delete($id);
}
}
public function delete_expired()
{
// Tags will be changed
self::$tags_changed = TRUE;
foreach (self::$tags as $tag => $_ids)
{
foreach ($_ids as $id)
{
if ( ! $this->backend->get($id))
{
// This id has disappeared, delete it from the tags
unset(self::$tags[$tag][$id]);
}
}
if (empty(self::$tags[$tag]))
{
// The tag no longer has any valid ids
unset(self::$tags[$tag]);
}
}
// Memcache handles garbage collection internally
return TRUE;
}
} // End Cache Memcache Driver

View File

@@ -0,0 +1,257 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* SQLite-based Cache driver.
*
* $Id: Sqlite.php 4046 2009-03-05 19:23:29Z Shadowhand $
*
* @package Cache
* @author Kohana Team
* @copyright (c) 2007-2008 Kohana Team
* @license http://kohanaphp.com/license.html
*/
class Cache_Sqlite_Driver implements Cache_Driver {
// SQLite database instance
protected $db;
// Database error messages
protected $error;
/**
* Logs an SQLite error.
*/
protected static function log_error($code)
{
// Log an error
Kohana::log('error', 'Cache: SQLite error: '.sqlite_error_string($error));
}
/**
* Tests that the storage location is a directory and is writable.
*/
public function __construct($filename)
{
// Get the directory name
$directory = str_replace('\\', '/', realpath(pathinfo($filename, PATHINFO_DIRNAME))).'/';
// Set the filename from the real directory path
$filename = $directory.basename($filename);
// Make sure the cache directory is writable
if ( ! is_dir($directory) OR ! is_writable($directory))
throw new Kohana_Exception('cache.unwritable', $directory);
// Make sure the cache database is writable
if (is_file($filename) AND ! is_writable($filename))
throw new Kohana_Exception('cache.unwritable', $filename);
// Open up an instance of the database
$this->db = new SQLiteDatabase($filename, '0666', $error);
// Throw an exception if there's an error
if ( ! empty($error))
throw new Kohana_Exception('cache.driver_error', sqlite_error_string($error));
$query = "SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'caches'";
$tables = $this->db->query($query, SQLITE_BOTH, $error);
// Throw an exception if there's an error
if ( ! empty($error))
throw new Kohana_Exception('cache.driver_error', sqlite_error_string($error));
if ($tables->numRows() == 0)
{
Kohana::log('error', 'Cache: Initializing new SQLite cache database');
// Issue a CREATE TABLE command
$this->db->unbufferedQuery(Kohana::config('cache_sqlite.schema'));
}
}
/**
* Checks if a cache id is already set.
*
* @param string cache id
* @return boolean
*/
public function exists($id)
{
// Find the id that matches
$query = "SELECT id FROM caches WHERE id = '$id'";
return ($this->db->query($query)->numRows() > 0);
}
/**
* Sets a cache item to the given data, tags, and lifetime.
*
* @param string cache id to set
* @param string data in the cache
* @param array cache tags
* @param integer lifetime
* @return bool
*/
public function set($id, $data, array $tags = NULL, $lifetime)
{
// Serialize and escape the data
$data = sqlite_escape_string(serialize($data));
if ( ! empty($tags))
{
// Escape the tags, adding brackets so the tag can be explicitly matched
$tags = sqlite_escape_string('<'.implode('>,<', $tags).'>');
}
// Cache Sqlite driver expects unix timestamp
if ($lifetime !== 0)
{
$lifetime += time();
}
$query = $this->exists($id)
? "UPDATE caches SET tags = '$tags', expiration = '$lifetime', cache = '$data' WHERE id = '$id'"
: "INSERT INTO caches VALUES('$id', '$tags', '$lifetime', '$data')";
// Run the query
$this->db->unbufferedQuery($query, SQLITE_BOTH, $error);
if ( ! empty($error))
{
self::log_error($error);
return FALSE;
}
else
{
return TRUE;
}
}
/**
* Finds an array of ids for a given tag.
*
* @param string tag name
* @return array of ids that match the tag
*/
public function find($tag)
{
$query = "SELECT id,cache FROM caches WHERE tags LIKE '%<{$tag}>%'";
$query = $this->db->query($query, SQLITE_BOTH, $error);
// An array will always be returned
$result = array();
if ( ! empty($error))
{
self::log_error($error);
}
elseif ($query->numRows() > 0)
{
// Disable notices for unserializing
$ER = error_reporting(~E_NOTICE);
while ($row = $query->fetchObject())
{
// Add each cache to the array
$result[$row->id] = unserialize($row->cache);
}
// Turn notices back on
error_reporting($ER);
}
return $result;
}
/**
* Fetches a cache item. This will delete the item if it is expired or if
* the hash does not match the stored hash.
*
* @param string cache id
* @return mixed|NULL
*/
public function get($id)
{
$query = "SELECT id, expiration, cache FROM caches WHERE id = '$id' LIMIT 0, 1";
$query = $this->db->query($query, SQLITE_BOTH, $error);
if ( ! empty($error))
{
self::log_error($error);
}
elseif ($cache = $query->fetchObject())
{
// Make sure the expiration is valid and that the hash matches
if ($cache->expiration != 0 AND $cache->expiration <= time())
{
// Cache is not valid, delete it now
$this->delete($cache->id);
}
else
{
// Disable notices for unserializing
$ER = error_reporting(~E_NOTICE);
// Return the valid cache data
$data = $cache->cache;
// Turn notices back on
error_reporting($ER);
}
}
// No valid cache found
return NULL;
}
/**
* Deletes a cache item by id or tag
*
* @param string cache id or tag, or TRUE for "all items"
* @param bool delete a tag
* @return bool
*/
public function delete($id, $tag = FALSE)
{
if ($id === TRUE)
{
// Delete all caches
$where = '1';
}
elseif ($tag === TRUE)
{
// Delete by tag
$where = "tags LIKE '%<{$id}>%'";
}
else
{
// Delete by id
$where = "id = '$id'";
}
$this->db->unbufferedQuery('DELETE FROM caches WHERE '.$where, SQLITE_BOTH, $error);
if ( ! empty($error))
{
self::log_error($error);
return FALSE;
}
else
{
return TRUE;
}
}
/**
* Deletes all cache files that are older than the current time.
*/
public function delete_expired()
{
// Delete all expired caches
$query = 'DELETE FROM caches WHERE expiration != 0 AND expiration <= '.time();
$this->db->unbufferedQuery($query);
return TRUE;
}
} // End Cache SQLite Driver

View File

@@ -0,0 +1,119 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* Xcache Cache driver.
*
* $Id: Xcache.php 4046 2009-03-05 19:23:29Z Shadowhand $
*
* @package Cache
* @author Kohana Team
* @copyright (c) 2007-2008 Kohana Team
* @license http://kohanaphp.com/license.html
*/
class Cache_Xcache_Driver implements Cache_Driver {
public function __construct()
{
if ( ! extension_loaded('xcache'))
throw new Kohana_Exception('cache.extension_not_loaded', 'xcache');
}
public function get($id)
{
if (xcache_isset($id))
return xcache_get($id);
return NULL;
}
public function set($id, $data, array $tags = NULL, $lifetime)
{
if ( ! empty($tags))
{
Kohana::log('error', 'Cache: tags are unsupported by the Xcache driver');
}
return xcache_set($id, $data, $lifetime);
}
public function find($tag)
{
Kohana::log('error', 'Cache: tags are unsupported by the Xcache driver');
return FALSE;
}
public function delete($id, $tag = FALSE)
{
if ($tag !== FALSE)
{
Kohana::log('error', 'Cache: tags are unsupported by the Xcache driver');
return TRUE;
}
elseif ($id !== TRUE)
{
if (xcache_isset($id))
return xcache_unset($id);
return FALSE;
}
else
{
// Do the login
$this->auth();
$result = TRUE;
for ($i = 0, $max = xcache_count(XC_TYPE_VAR); $i < $max; $i++)
{
if (xcache_clear_cache(XC_TYPE_VAR, $i) !== NULL)
{
$result = FALSE;
break;
}
}
// Undo the login
$this->auth(TRUE);
return $result;
}
return TRUE;
}
public function delete_expired()
{
return TRUE;
}
private function auth($reverse = FALSE)
{
static $backup = array();
$keys = array('PHP_AUTH_USER', 'PHP_AUTH_PW');
foreach ($keys as $key)
{
if ($reverse)
{
if (isset($backup[$key]))
{
$_SERVER[$key] = $backup[$key];
unset($backup[$key]);
}
else
{
unset($_SERVER[$key]);
}
}
else
{
$value = getenv($key);
if ( ! empty($value))
{
$backup[$key] = $value;
}
$_SERVER[$key] = Kohana::config('cache_xcache.'.$key);
}
}
}
} // End Cache Xcache Driver

View File

@@ -0,0 +1,227 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* Captcha driver class.
*
* $Id: Captcha.php 3769 2008-12-15 00:48:56Z zombor $
*
* @package Captcha
* @author Kohana Team
* @copyright (c) 2007-2008 Kohana Team
* @license http://kohanaphp.com/license.html
*/
abstract class Captcha_Driver {
// The correct Captcha challenge answer
protected $response;
// Image resource identifier and type ("png", "gif" or "jpeg")
protected $image;
protected $image_type = 'png';
/**
* Constructs a new challenge.
*
* @return void
*/
public function __construct()
{
// Generate a new challenge
$this->response = $this->generate_challenge();
// Store the correct Captcha response in a session
Event::add('system.post_controller', array($this, 'update_response_session'));
}
/**
* Generate a new Captcha challenge.
*
* @return string the challenge answer
*/
abstract public function generate_challenge();
/**
* Output the Captcha challenge.
*
* @param boolean html output
* @return mixed the rendered Captcha (e.g. an image, riddle, etc.)
*/
abstract public function render($html);
/**
* Stores the response for the current Captcha challenge in a session so it is available
* on the next page load for Captcha::valid(). This method is called after controller
* execution (in the system.post_controller event) in order not to overwrite itself too soon.
*
* @return void
*/
public function update_response_session()
{
Session::instance()->set('captcha_response', sha1(strtoupper($this->response)));
}
/**
* Validates a Captcha response from a user.
*
* @param string captcha response
* @return boolean
*/
public function valid($response)
{
return (sha1(strtoupper($response)) === Session::instance()->get('captcha_response'));
}
/**
* Returns the image type.
*
* @param string filename
* @return string|FALSE image type ("png", "gif" or "jpeg")
*/
public function image_type($filename)
{
switch (strtolower(substr(strrchr($filename, '.'), 1)))
{
case 'png':
return 'png';
case 'gif':
return 'gif';
case 'jpg':
case 'jpeg':
// Return "jpeg" and not "jpg" because of the GD2 function names
return 'jpeg';
default:
return FALSE;
}
}
/**
* Creates an image resource with the dimensions specified in config.
* If a background image is supplied, the image dimensions are used.
*
* @throws Kohana_Exception if no GD2 support
* @param string path to the background image file
* @return void
*/
public function image_create($background = NULL)
{
// Check for GD2 support
if ( ! function_exists('imagegd2'))
throw new Kohana_Exception('captcha.requires_GD2');
// Create a new image (black)
$this->image = imagecreatetruecolor(Captcha::$config['width'], Captcha::$config['height']);
// Use a background image
if ( ! empty($background))
{
// Create the image using the right function for the filetype
$function = 'imagecreatefrom'.$this->image_type($background);
$this->background_image = $function($background);
// Resize the image if needed
if (imagesx($this->background_image) !== Captcha::$config['width']
OR imagesy($this->background_image) !== Captcha::$config['height'])
{
imagecopyresampled
(
$this->image, $this->background_image, 0, 0, 0, 0,
Captcha::$config['width'], Captcha::$config['height'],
imagesx($this->background_image), imagesy($this->background_image)
);
}
// Free up resources
imagedestroy($this->background_image);
}
}
/**
* Fills the background with a gradient.
*
* @param resource gd image color identifier for start color
* @param resource gd image color identifier for end color
* @param string direction: 'horizontal' or 'vertical', 'random' by default
* @return void
*/
public function image_gradient($color1, $color2, $direction = NULL)
{
$directions = array('horizontal', 'vertical');
// Pick a random direction if needed
if ( ! in_array($direction, $directions))
{
$direction = $directions[array_rand($directions)];
// Switch colors
if (mt_rand(0, 1) === 1)
{
$temp = $color1;
$color1 = $color2;
$color2 = $temp;
}
}
// Extract RGB values
$color1 = imagecolorsforindex($this->image, $color1);
$color2 = imagecolorsforindex($this->image, $color2);
// Preparations for the gradient loop
$steps = ($direction === 'horizontal') ? Captcha::$config['width'] : Captcha::$config['height'];
$r1 = ($color1['red'] - $color2['red']) / $steps;
$g1 = ($color1['green'] - $color2['green']) / $steps;
$b1 = ($color1['blue'] - $color2['blue']) / $steps;
if ($direction === 'horizontal')
{
$x1 =& $i;
$y1 = 0;
$x2 =& $i;
$y2 = Captcha::$config['height'];
}
else
{
$x1 = 0;
$y1 =& $i;
$x2 = Captcha::$config['width'];
$y2 =& $i;
}
// Execute the gradient loop
for ($i = 0; $i <= $steps; $i++)
{
$r2 = $color1['red'] - floor($i * $r1);
$g2 = $color1['green'] - floor($i * $g1);
$b2 = $color1['blue'] - floor($i * $b1);
$color = imagecolorallocate($this->image, $r2, $g2, $b2);
imageline($this->image, $x1, $y1, $x2, $y2, $color);
}
}
/**
* Returns the img html element or outputs the image to the browser.
*
* @param boolean html output
* @return mixed html string or void
*/
public function image_render($html)
{
// Output html element
if ($html)
return '<img alt="Captcha" src="'.url::site('captcha/'.Captcha::$config['group']).'" width="'.Captcha::$config['width'].'" height="'.Captcha::$config['height'].'" />';
// Send the correct HTTP header
header('Content-Type: image/'.$this->image_type);
// Pick the correct output function
$function = 'image'.$this->image_type;
$function($this->image);
// Free up resources
imagedestroy($this->image);
}
} // End Captcha Driver

View File

@@ -0,0 +1,92 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* Captcha driver for "alpha" style.
*
* $Id: Alpha.php 4367 2009-05-27 21:23:57Z samsoir $
*
* @package Captcha
* @author Kohana Team
* @copyright (c) 2007-2008 Kohana Team
* @license http://kohanaphp.com/license.html
*/
class Captcha_Alpha_Driver extends Captcha_Driver {
/**
* Generates a new Captcha challenge.
*
* @return string the challenge answer
*/
public function generate_challenge()
{
// Complexity setting is used as character count
return text::random('distinct', max(1, Captcha::$config['complexity']));
}
/**
* Outputs the Captcha image.
*
* @param boolean html output
* @return mixed
*/
public function render($html)
{
// Creates $this->image
$this->image_create(Captcha::$config['background']);
// Add a random gradient
if (empty(Captcha::$config['background']))
{
$color1 = imagecolorallocate($this->image, mt_rand(0, 100), mt_rand(0, 100), mt_rand(0, 100));
$color2 = imagecolorallocate($this->image, mt_rand(0, 100), mt_rand(0, 100), mt_rand(0, 100));
$this->image_gradient($color1, $color2);
}
// Add a few random circles
for ($i = 0, $count = mt_rand(10, Captcha::$config['complexity'] * 3); $i < $count; $i++)
{
$color = imagecolorallocatealpha($this->image, mt_rand(0, 255), mt_rand(0, 255), mt_rand(0, 255), mt_rand(80, 120));
$size = mt_rand(5, Captcha::$config['height'] / 3);
imagefilledellipse($this->image, mt_rand(0, Captcha::$config['width']), mt_rand(0, Captcha::$config['height']), $size, $size, $color);
}
// Calculate character font-size and spacing
$default_size = min(Captcha::$config['width'], Captcha::$config['height'] * 2) / strlen($this->response);
$spacing = (int) (Captcha::$config['width'] * 0.9 / strlen($this->response));
// Background alphabetic character attributes
$color_limit = mt_rand(96, 160);
$chars = 'ABEFGJKLPQRTVY';
// Draw each Captcha character with varying attributes
for ($i = 0, $strlen = strlen($this->response); $i < $strlen; $i++)
{
// Use different fonts if available
$font = Captcha::$config['fontpath'].Captcha::$config['fonts'][array_rand(Captcha::$config['fonts'])];
$angle = mt_rand(-40, 20);
// Scale the character size on image height
$size = $default_size / 10 * mt_rand(8, 12);
$box = imageftbbox($size, $angle, $font, $this->response[$i]);
// Calculate character starting coordinates
$x = $spacing / 4 + $i * $spacing;
$y = Captcha::$config['height'] / 2 + ($box[2] - $box[5]) / 4;
// Draw captcha text character
// Allocate random color, size and rotation attributes to text
$color = imagecolorallocate($this->image, mt_rand(150, 255), mt_rand(200, 255), mt_rand(0, 255));
// Write text character to image
imagefttext($this->image, $size, $angle, $x, $y, $color, $font, $this->response[$i]);
// Draw "ghost" alphabetic character
$text_color = imagecolorallocatealpha($this->image, mt_rand($color_limit + 8, 255), mt_rand($color_limit + 8, 255), mt_rand($color_limit + 8, 255), mt_rand(70, 120));
$char = $chars[mt_rand(0, 14)];
imagettftext($this->image, $size * 2, mt_rand(-45, 45), ($x - (mt_rand(5, 10))), ($y + (mt_rand(5, 10))), $text_color, $font, $char);
}
// Output
return $this->image_render($html);
}
} // End Captcha Alpha Driver Class

View File

@@ -0,0 +1,81 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* Captcha driver for "basic" style.
*
* $Id: Basic.php 3769 2008-12-15 00:48:56Z zombor $
*
* @package Captcha
* @author Kohana Team
* @copyright (c) 2007-2008 Kohana Team
* @license http://kohanaphp.com/license.html
*/
class Captcha_Basic_Driver extends Captcha_Driver {
/**
* Generates a new Captcha challenge.
*
* @return string the challenge answer
*/
public function generate_challenge()
{
// Complexity setting is used as character count
return text::random('distinct', max(1, Captcha::$config['complexity']));
}
/**
* Outputs the Captcha image.
*
* @param boolean html output
* @return mixed
*/
public function render($html)
{
// Creates $this->image
$this->image_create(Captcha::$config['background']);
// Add a random gradient
if (empty(Captcha::$config['background']))
{
$color1 = imagecolorallocate($this->image, mt_rand(200, 255), mt_rand(200, 255), mt_rand(150, 255));
$color2 = imagecolorallocate($this->image, mt_rand(200, 255), mt_rand(200, 255), mt_rand(150, 255));
$this->image_gradient($color1, $color2);
}
// Add a few random lines
for ($i = 0, $count = mt_rand(5, Captcha::$config['complexity'] * 4); $i < $count; $i++)
{
$color = imagecolorallocatealpha($this->image, mt_rand(0, 255), mt_rand(0, 255), mt_rand(100, 255), mt_rand(50, 120));
imageline($this->image, mt_rand(0, Captcha::$config['width']), 0, mt_rand(0, Captcha::$config['width']), Captcha::$config['height'], $color);
}
// Calculate character font-size and spacing
$default_size = min(Captcha::$config['width'], Captcha::$config['height'] * 2) / (strlen($this->response) + 1);
$spacing = (int) (Captcha::$config['width'] * 0.9 / strlen($this->response));
// Draw each Captcha character with varying attributes
for ($i = 0, $strlen = strlen($this->response); $i < $strlen; $i++)
{
// Use different fonts if available
$font = Captcha::$config['fontpath'].Captcha::$config['fonts'][array_rand(Captcha::$config['fonts'])];
// Allocate random color, size and rotation attributes to text
$color = imagecolorallocate($this->image, mt_rand(0, 150), mt_rand(0, 150), mt_rand(0, 150));
$angle = mt_rand(-40, 20);
// Scale the character size on image height
$size = $default_size / 10 * mt_rand(8, 12);
$box = imageftbbox($size, $angle, $font, $this->response[$i]);
// Calculate character starting coordinates
$x = $spacing / 4 + $i * $spacing;
$y = Captcha::$config['height'] / 2 + ($box[2] - $box[5]) / 4;
// Write text character to image
imagefttext($this->image, $size, $angle, $x, $y, $color, $font, $this->response[$i]);
}
// Output
return $this->image_render($html);
}
} // End Captcha Basic Driver Class

View File

@@ -0,0 +1,72 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* Captcha driver for "black" style.
*
* $Id: Black.php 3769 2008-12-15 00:48:56Z zombor $
*
* @package Captcha
* @author Kohana Team
* @copyright (c) 2007-2008 Kohana Team
* @license http://kohanaphp.com/license.html
*/
class Captcha_Black_Driver extends Captcha_Driver {
/**
* Generates a new Captcha challenge.
*
* @return string the challenge answer
*/
public function generate_challenge()
{
// Complexity setting is used as character count
return text::random('distinct', max(1, ceil(Captcha::$config['complexity'] / 1.5)));
}
/**
* Outputs the Captcha image.
*
* @param boolean html output
* @return mixed
*/
public function render($html)
{
// Creates a black image to start from
$this->image_create(Captcha::$config['background']);
// Add random white/gray arcs, amount depends on complexity setting
$count = (Captcha::$config['width'] + Captcha::$config['height']) / 2;
$count = $count / 5 * min(10, Captcha::$config['complexity']);
for ($i = 0; $i < $count; $i++)
{
imagesetthickness($this->image, mt_rand(1, 2));
$color = imagecolorallocatealpha($this->image, 255, 255, 255, mt_rand(0, 120));
imagearc($this->image, mt_rand(-Captcha::$config['width'], Captcha::$config['width']), mt_rand(-Captcha::$config['height'], Captcha::$config['height']), mt_rand(-Captcha::$config['width'], Captcha::$config['width']), mt_rand(-Captcha::$config['height'], Captcha::$config['height']), mt_rand(0, 360), mt_rand(0, 360), $color);
}
// Use different fonts if available
$font = Captcha::$config['fontpath'].Captcha::$config['fonts'][array_rand(Captcha::$config['fonts'])];
// Draw the character's white shadows
$size = (int) min(Captcha::$config['height'] / 2, Captcha::$config['width'] * 0.8 / strlen($this->response));
$angle = mt_rand(-15 + strlen($this->response), 15 - strlen($this->response));
$x = mt_rand(1, Captcha::$config['width'] * 0.9 - $size * strlen($this->response));
$y = ((Captcha::$config['height'] - $size) / 2) + $size;
$color = imagecolorallocate($this->image, 255, 255, 255);
imagefttext($this->image, $size, $angle, $x + 1, $y + 1, $color, $font, $this->response);
// Add more shadows for lower complexities
(Captcha::$config['complexity'] < 10) and imagefttext($this->image, $size, $angle, $x - 1, $y - 1, $color, $font , $this->response);
(Captcha::$config['complexity'] < 8) and imagefttext($this->image, $size, $angle, $x - 2, $y + 2, $color, $font , $this->response);
(Captcha::$config['complexity'] < 6) and imagefttext($this->image, $size, $angle, $x + 2, $y - 2, $color, $font , $this->response);
(Captcha::$config['complexity'] < 4) and imagefttext($this->image, $size, $angle, $x + 3, $y + 3, $color, $font , $this->response);
(Captcha::$config['complexity'] < 2) and imagefttext($this->image, $size, $angle, $x - 3, $y - 3, $color, $font , $this->response);
// Finally draw the foreground characters
$color = imagecolorallocate($this->image, 0, 0, 0);
imagefttext($this->image, $size, $angle, $x, $y, $color, $font, $this->response);
// Output
return $this->image_render($html);
}
} // End Captcha Black Driver Class

View File

@@ -0,0 +1,61 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* Captcha driver for "math" style.
*
* $Id: Math.php 3769 2008-12-15 00:48:56Z zombor $
*
* @package Captcha
* @author Kohana Team
* @copyright (c) 2007-2008 Kohana Team
* @license http://kohanaphp.com/license.html
*/
class Captcha_Math_Driver extends Captcha_Driver {
private $math_exercice;
/**
* Generates a new Captcha challenge.
*
* @return string the challenge answer
*/
public function generate_challenge()
{
// Easy
if (Captcha::$config['complexity'] < 4)
{
$numbers[] = mt_rand(1, 5);
$numbers[] = mt_rand(1, 4);
}
// Normal
elseif (Captcha::$config['complexity'] < 7)
{
$numbers[] = mt_rand(10, 20);
$numbers[] = mt_rand(1, 10);
}
// Difficult, well, not really ;)
else
{
$numbers[] = mt_rand(100, 200);
$numbers[] = mt_rand(10, 20);
$numbers[] = mt_rand(1, 10);
}
// Store the question for output
$this->math_exercice = implode(' + ', $numbers).' = ';
// Return the answer
return array_sum($numbers);
}
/**
* Outputs the Captcha riddle.
*
* @param boolean html output
* @return mixed
*/
public function render($html)
{
return $this->math_exercice;
}
} // End Captcha Math Driver Class

View File

@@ -0,0 +1,47 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* Captcha driver for "riddle" style.
*
* $Id: Riddle.php 3769 2008-12-15 00:48:56Z zombor $
*
* @package Captcha
* @author Kohana Team
* @copyright (c) 2007-2008 Kohana Team
* @license http://kohanaphp.com/license.html
*/
class Captcha_Riddle_Driver extends Captcha_Driver {
private $riddle;
/**
* Generates a new Captcha challenge.
*
* @return string the challenge answer
*/
public function generate_challenge()
{
// Load riddles from the current language
$riddles = Kohana::lang('captcha.riddles');
// Pick a random riddle
$riddle = $riddles[array_rand($riddles)];
// Store the question for output
$this->riddle = $riddle[0];
// Return the answer
return $riddle[1];
}
/**
* Outputs the Captcha riddle.
*
* @param boolean html output
* @return mixed
*/
public function render($html)
{
return $this->riddle;
}
} // End Captcha Riddle Driver Class

View File

@@ -0,0 +1,37 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* Captcha driver for "word" style.
*
* $Id: Word.php 3769 2008-12-15 00:48:56Z zombor $
*
* @package Captcha
* @author Kohana Team
* @copyright (c) 2007-2008 Kohana Team
* @license http://kohanaphp.com/license.html
*/
class Captcha_Word_Driver extends Captcha_Basic_Driver {
/**
* Generates a new Captcha challenge.
*
* @return string the challenge answer
*/
public function generate_challenge()
{
// Load words from the current language and randomize them
$words = Kohana::lang('captcha.words');
shuffle($words);
// Loop over each word...
foreach ($words as $word)
{
// ...until we find one of the desired length
if (abs(Captcha::$config['complexity'] - strlen($word)) < 2)
return strtoupper($word);
}
// Return any random word as final fallback
return strtoupper($words[array_rand($words)]);
}
} // End Captcha Word Driver Class

View File

@@ -0,0 +1,636 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* Database API driver
*
* $Id: Database.php 4343 2009-05-08 17:04:48Z jheathco $
*
* @package Core
* @author Kohana Team
* @copyright (c) 2007-2008 Kohana Team
* @license http://kohanaphp.com/license.html
*/
abstract class Database_Driver {
protected $query_cache;
/**
* Connect to our database.
* Returns FALSE on failure or a MySQL resource.
*
* @return mixed
*/
abstract public function connect();
/**
* Perform a query based on a manually written query.
*
* @param string SQL query to execute
* @return Database_Result
*/
abstract public function query($sql);
/**
* Builds a DELETE query.
*
* @param string table name
* @param array where clause
* @return string
*/
public function delete($table, $where)
{
return 'DELETE FROM '.$this->escape_table($table).' WHERE '.implode(' ', $where);
}
/**
* Builds an UPDATE query.
*
* @param string table name
* @param array key => value pairs
* @param array where clause
* @return string
*/
public function update($table, $values, $where)
{
foreach ($values as $key => $val)
{
$valstr[] = $this->escape_column($key).' = '.$val;
}
return 'UPDATE '.$this->escape_table($table).' SET '.implode(', ', $valstr).' WHERE '.implode(' ',$where);
}
/**
* Set the charset using 'SET NAMES <charset>'.
*
* @param string character set to use
*/
public function set_charset($charset)
{
throw new Kohana_Database_Exception('database.not_implemented', __FUNCTION__);
}
/**
* Wrap the tablename in backticks, has support for: table.field syntax.
*
* @param string table name
* @return string
*/
abstract public function escape_table($table);
/**
* Escape a column/field name, has support for special commands.
*
* @param string column name
* @return string
*/
abstract public function escape_column($column);
/**
* Builds a WHERE portion of a query.
*
* @param mixed key
* @param string value
* @param string type
* @param int number of where clauses
* @param boolean escape the value
* @return string
*/
public function where($key, $value, $type, $num_wheres, $quote)
{
$prefix = ($num_wheres == 0) ? '' : $type;
if ($quote === -1)
{
$value = '';
}
else
{
if ($value === NULL)
{
if ( ! $this->has_operator($key))
{
$key .= ' IS';
}
$value = ' NULL';
}
elseif (is_bool($value))
{
if ( ! $this->has_operator($key))
{
$key .= ' =';
}
$value = ($value == TRUE) ? ' 1' : ' 0';
}
else
{
if ( ! $this->has_operator($key) AND ! empty($key))
{
$key = $this->escape_column($key).' =';
}
else
{
preg_match('/^(.+?)([<>!=]+|\bIS(?:\s+NULL))\s*$/i', $key, $matches);
if (isset($matches[1]) AND isset($matches[2]))
{
$key = $this->escape_column(trim($matches[1])).' '.trim($matches[2]);
}
}
$value = ' '.(($quote == TRUE) ? $this->escape($value) : $value);
}
}
return $prefix.$key.$value;
}
/**
* Builds a LIKE portion of a query.
*
* @param mixed field name
* @param string value to match with field
* @param boolean add wildcards before and after the match
* @param string clause type (AND or OR)
* @param int number of likes
* @return string
*/
public function like($field, $match, $auto, $type, $num_likes)
{
$prefix = ($num_likes == 0) ? '' : $type;
$match = $this->escape_str($match);
if ($auto === TRUE)
{
// Add the start and end quotes
$match = '%'.str_replace('%', '\\%', $match).'%';
}
return $prefix.' '.$this->escape_column($field).' LIKE \''.$match . '\'';
}
/**
* Builds a NOT LIKE portion of a query.
*
* @param mixed field name
* @param string value to match with field
* @param string clause type (AND or OR)
* @param int number of likes
* @return string
*/
public function notlike($field, $match, $auto, $type, $num_likes)
{
$prefix = ($num_likes == 0) ? '' : $type;
$match = $this->escape_str($match);
if ($auto === TRUE)
{
// Add the start and end quotes
$match = '%'.$match.'%';
}
return $prefix.' '.$this->escape_column($field).' NOT LIKE \''.$match.'\'';
}
/**
* Builds a REGEX portion of a query.
*
* @param string field name
* @param string value to match with field
* @param string clause type (AND or OR)
* @param integer number of regexes
* @return string
*/
public function regex($field, $match, $type, $num_regexs)
{
throw new Kohana_Database_Exception('database.not_implemented', __FUNCTION__);
}
/**
* Builds a NOT REGEX portion of a query.
*
* @param string field name
* @param string value to match with field
* @param string clause type (AND or OR)
* @param integer number of regexes
* @return string
*/
public function notregex($field, $match, $type, $num_regexs)
{
throw new Kohana_Database_Exception('database.not_implemented', __FUNCTION__);
}
/**
* Builds an INSERT query.
*
* @param string table name
* @param array keys
* @param array values
* @return string
*/
public function insert($table, $keys, $values)
{
// Escape the column names
foreach ($keys as $key => $value)
{
$keys[$key] = $this->escape_column($value);
}
return 'INSERT INTO '.$this->escape_table($table).' ('.implode(', ', $keys).') VALUES ('.implode(', ', $values).')';
}
/**
* Builds a MERGE portion of a query.
*
* @param string table name
* @param array keys
* @param array values
* @return string
*/
public function merge($table, $keys, $values)
{
throw new Kohana_Database_Exception('database.not_implemented', __FUNCTION__);
}
/**
* Builds a LIMIT portion of a query.
*
* @param integer limit
* @param integer offset
* @return string
*/
abstract public function limit($limit, $offset = 0);
/**
* Creates a prepared statement.
*
* @param string SQL query
* @return Database_Stmt
*/
public function stmt_prepare($sql = '')
{
throw new Kohana_Database_Exception('database.not_implemented', __FUNCTION__);
}
/**
* Compiles the SELECT statement.
* Generates a query string based on which functions were used.
* Should not be called directly, the get() function calls it.
*
* @param array select query values
* @return string
*/
abstract public function compile_select($database);
/**
* Determines if the string has an arithmetic operator in it.
*
* @param string string to check
* @return boolean
*/
public function has_operator($str)
{
return (bool) preg_match('/[<>!=]|\sIS(?:\s+NOT\s+)?\b|BETWEEN/i', trim($str));
}
/**
* Escapes any input value.
*
* @param mixed value to escape
* @return string
*/
public function escape($value)
{
if ( ! $this->db_config['escape'])
return $value;
switch (gettype($value))
{
case 'string':
$value = '\''.$this->escape_str($value).'\'';
break;
case 'boolean':
$value = (int) $value;
break;
case 'double':
// Convert to non-locale aware float to prevent possible commas
$value = sprintf('%F', $value);
break;
default:
$value = ($value === NULL) ? 'NULL' : $value;
break;
}
return (string) $value;
}
/**
* Escapes a string for a query.
*
* @param mixed value to escape
* @return string
*/
abstract public function escape_str($str);
/**
* Lists all tables in the database.
*
* @return array
*/
abstract public function list_tables();
/**
* Lists all fields in a table.
*
* @param string table name
* @return array
*/
abstract function list_fields($table);
/**
* Returns the last database error.
*
* @return string
*/
abstract public function show_error();
/**
* Returns field data about a table.
*
* @param string table name
* @return array
*/
abstract public function field_data($table);
/**
* Fetches SQL type information about a field, in a generic format.
*
* @param string field datatype
* @return array
*/
protected function sql_type($str)
{
static $sql_types;
if ($sql_types === NULL)
{
// Load SQL data types
$sql_types = Kohana::config('sql_types');
}
$str = strtolower(trim($str));
if (($open = strpos($str, '(')) !== FALSE)
{
// Find closing bracket
$close = strpos($str, ')', $open) - 1;
// Find the type without the size
$type = substr($str, 0, $open);
}
else
{
// No length
$type = $str;
}
empty($sql_types[$type]) and exit
(
'Unknown field type: '.$type.'. '.
'Please report this: http://trac.kohanaphp.com/newticket'
);
// Fetch the field definition
$field = $sql_types[$type];
switch ($field['type'])
{
case 'string':
case 'float':
if (isset($close))
{
// Add the length to the field info
$field['length'] = substr($str, $open + 1, $close - $open);
}
break;
case 'int':
// Add unsigned value
$field['unsigned'] = (strpos($str, 'unsigned') !== FALSE);
break;
}
return $field;
}
/**
* Clears the internal query cache.
*
* @param string SQL query
*/
public function clear_cache($sql = NULL)
{
if (empty($sql))
{
$this->query_cache = array();
}
else
{
unset($this->query_cache[$this->query_hash($sql)]);
}
Kohana::log('debug', 'Database cache cleared: '.get_class($this));
}
/**
* Creates a hash for an SQL query string. Replaces newlines with spaces,
* trims, and hashes.
*
* @param string SQL query
* @return string
*/
protected function query_hash($sql)
{
return sha1(str_replace("\n", ' ', trim($sql)));
}
} // End Database Driver Interface
/**
* Database_Result
*
*/
abstract class Database_Result implements ArrayAccess, Iterator, Countable {
// Result resource, insert id, and SQL
protected $result;
protected $insert_id;
protected $sql;
// Current and total rows
protected $current_row = 0;
protected $total_rows = 0;
// Fetch function and return type
protected $fetch_type;
protected $return_type;
/**
* Returns the SQL used to fetch the result.
*
* @return string
*/
public function sql()
{
return $this->sql;
}
/**
* Returns the insert id from the result.
*
* @return mixed
*/
public function insert_id()
{
return $this->insert_id;
}
/**
* Prepares the query result.
*
* @param boolean return rows as objects
* @param mixed type
* @return Database_Result
*/
abstract function result($object = TRUE, $type = FALSE);
/**
* Builds an array of query results.
*
* @param boolean return rows as objects
* @param mixed type
* @return array
*/
abstract function result_array($object = NULL, $type = FALSE);
/**
* Gets the fields of an already run query.
*
* @return array
*/
abstract public function list_fields();
/**
* Seek to an offset in the results.
*
* @return boolean
*/
abstract public function seek($offset);
/**
* Countable: count
*/
public function count()
{
return $this->total_rows;
}
/**
* ArrayAccess: offsetExists
*/
public function offsetExists($offset)
{
if ($this->total_rows > 0)
{
$min = 0;
$max = $this->total_rows - 1;
return ! ($offset < $min OR $offset > $max);
}
return FALSE;
}
/**
* ArrayAccess: offsetGet
*/
public function offsetGet($offset)
{
if ( ! $this->seek($offset))
return FALSE;
// Return the row by calling the defined fetching callback
return call_user_func($this->fetch_type, $this->result, $this->return_type);
}
/**
* ArrayAccess: offsetSet
*
* @throws Kohana_Database_Exception
*/
final public function offsetSet($offset, $value)
{
throw new Kohana_Database_Exception('database.result_read_only');
}
/**
* ArrayAccess: offsetUnset
*
* @throws Kohana_Database_Exception
*/
final public function offsetUnset($offset)
{
throw new Kohana_Database_Exception('database.result_read_only');
}
/**
* Iterator: current
*/
public function current()
{
return $this->offsetGet($this->current_row);
}
/**
* Iterator: key
*/
public function key()
{
return $this->current_row;
}
/**
* Iterator: next
*/
public function next()
{
++$this->current_row;
return $this;
}
/**
* Iterator: prev
*/
public function prev()
{
--$this->current_row;
return $this;
}
/**
* Iterator: rewind
*/
public function rewind()
{
$this->current_row = 0;
return $this;
}
/**
* Iterator: valid
*/
public function valid()
{
return $this->offsetExists($this->current_row);
}
} // End Database Result Interface

View File

@@ -0,0 +1,462 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* MSSQL Database Driver
*
* @package Core
* @author Kohana Team
* @copyright (c) 2007-2008 Kohana Team
* @license http://kohanaphp.com/license.html
*/
class Database_Mssql_Driver extends Database_Driver
{
/**
* Database connection link
*/
protected $link;
/**
* Database configuration
*/
protected $db_config;
/**
* Sets the config for the class.
*
* @param array database configuration
*/
public function __construct($config)
{
$this->db_config = $config;
Kohana::log('debug', 'MSSQL Database Driver Initialized');
}
/**
* Closes the database connection.
*/
public function __destruct()
{
is_resource($this->link) and mssql_close($this->link);
}
/**
* Make the connection
*
* @return return connection
*/
public function connect()
{
// Check if link already exists
if (is_resource($this->link))
return $this->link;
// Import the connect variables
extract($this->db_config['connection']);
// Persistent connections enabled?
$connect = ($this->db_config['persistent'] == TRUE) ? 'mssql_pconnect' : 'mssql_connect';
// Build the connection info
$host = isset($host) ? $host : $socket;
// Windows uses a comma instead of a colon
$port = (isset($port) AND is_string($port)) ? (KOHANA_IS_WIN ? ',' : ':').$port : '';
// Make the connection and select the database
if (($this->link = $connect($host.$port, $user, $pass, TRUE)) AND mssql_select_db($database, $this->link))
{
/* This is being removed so I can use it, will need to come up with a more elegant workaround in the future...
*
if ($charset = $this->db_config['character_set'])
{
$this->set_charset($charset);
}
*/
// Clear password after successful connect
$this->db_config['connection']['pass'] = NULL;
return $this->link;
}
return FALSE;
}
public function query($sql)
{
// Only cache if it's turned on, and only cache if it's not a write statement
if ($this->db_config['cache'] AND ! preg_match('#\b(?:INSERT|UPDATE|REPLACE|SET)\b#i', $sql))
{
$hash = $this->query_hash($sql);
if ( ! isset($this->query_cache[$hash]))
{
// Set the cached object
$this->query_cache[$hash] = new Mssql_Result(mssql_query($sql, $this->link), $this->link, $this->db_config['object'], $sql);
}
else
{
// Rewind cached result
$this->query_cache[$hash]->rewind();
}
// Return the cached query
return $this->query_cache[$hash];
}
return new Mssql_Result(mssql_query($sql, $this->link), $this->link, $this->db_config['object'], $sql);
}
public function escape_table($table)
{
if (stripos($table, ' AS ') !== FALSE)
{
// Force 'AS' to uppercase
$table = str_ireplace(' AS ', ' AS ', $table);
// Runs escape_table on both sides of an AS statement
$table = array_map(array($this, __FUNCTION__), explode(' AS ', $table));
// Re-create the AS statement
return implode(' AS ', $table);
}
return '['.str_replace('.', '[.]', $table).']';
}
public function escape_column($column)
{
if (!$this->db_config['escape'])
return $column;
if ($column == '*')
return $column;
// This matches any functions we support to SELECT.
if ( preg_match('/(avg|count|sum|max|min)\(\s*(.*)\s*\)(\s*as\s*(.+)?)?/i', $column, $matches))
{
if ( count($matches) == 3)
{
return $matches[1].'('.$this->escape_column($matches[2]).')';
}
else if ( count($matches) == 5)
{
return $matches[1].'('.$this->escape_column($matches[2]).') AS '.$this->escape_column($matches[2]);
}
}
// This matches any modifiers we support to SELECT.
if ( ! preg_match('/\b(?:rand|all|distinct(?:row)?|high_priority|sql_(?:small_result|b(?:ig_result|uffer_result)|no_cache|ca(?:che|lc_found_rows)))\s/i', $column))
{
if (stripos($column, ' AS ') !== FALSE)
{
// Force 'AS' to uppercase
$column = str_ireplace(' AS ', ' AS ', $column);
// Runs escape_column on both sides of an AS statement
$column = array_map(array($this, __FUNCTION__), explode(' AS ', $column));
// Re-create the AS statement
return implode(' AS ', $column);
}
return preg_replace('/[^.*]+/', '[$0]', $column);
}
$parts = explode(' ', $column);
$column = '';
for ($i = 0, $c = count($parts); $i < $c; $i++)
{
// The column is always last
if ($i == ($c - 1))
{
$column .= preg_replace('/[^.*]+/', '[$0]', $parts[$i]);
}
else // otherwise, it's a modifier
{
$column .= $parts[$i].' ';
}
}
return $column;
}
/**
* Limit in SQL Server 2000 only uses the keyword
* 'TOP'; 2007 may have an offset keyword, but
* I am unsure - for pagination style limit,offset
* functionality, a fancy query needs to be built.
*
* @param unknown_type $limit
* @return unknown
*/
public function limit($limit, $offset=null)
{
return 'TOP '.$limit;
}
public function compile_select($database)
{
$sql = ($database['distinct'] == TRUE) ? 'SELECT DISTINCT ' : 'SELECT ';
$sql .= (count($database['select']) > 0) ? implode(', ', $database['select']) : '*';
if (count($database['from']) > 0)
{
// Escape the tables
$froms = array();
foreach ($database['from'] as $from)
$froms[] = $this->escape_column($from);
$sql .= "\nFROM ";
$sql .= implode(', ', $froms);
}
if (count($database['join']) > 0)
{
foreach($database['join'] AS $join)
{
$sql .= "\n".$join['type'].'JOIN '.implode(', ', $join['tables']).' ON '.$join['conditions'];
}
}
if (count($database['where']) > 0)
{
$sql .= "\nWHERE ";
}
$sql .= implode("\n", $database['where']);
if (count($database['groupby']) > 0)
{
$sql .= "\nGROUP BY ";
$sql .= implode(', ', $database['groupby']);
}
if (count($database['having']) > 0)
{
$sql .= "\nHAVING ";
$sql .= implode("\n", $database['having']);
}
if (count($database['orderby']) > 0)
{
$sql .= "\nORDER BY ";
$sql .= implode(', ', $database['orderby']);
}
if (is_numeric($database['limit']))
{
$sql .= "\n";
$sql .= $this->limit($database['limit']);
}
return $sql;
}
public function escape_str($str)
{
if (!$this->db_config['escape'])
return $str;
is_resource($this->link) or $this->connect();
//mssql_real_escape_string($str, $this->link); <-- this function doesn't exist
$characters = array('/\x00/', '/\x1a/', '/\n/', '/\r/', '/\\\/', '/\'/');
$replace = array('\\\x00', '\\x1a', '\\n', '\\r', '\\\\', "''");
return preg_replace($characters, $replace, $str);
}
public function list_tables()
{
$sql = 'SHOW TABLES FROM ['.$this->db_config['connection']['database'].']';
$result = $this->query($sql)->result(FALSE, MSSQL_ASSOC);
$retval = array();
foreach ($result as $row)
{
$retval[] = current($row);
}
return $retval;
}
public function show_error()
{
return mssql_get_last_message($this->link);
}
public function list_fields($table)
{
$result = array();
foreach ($this->field_data($table) as $row)
{
// Make an associative array
$result[$row->Field] = $this->sql_type($row->Type);
}
return $result;
}
public function field_data($table)
{
$query = $this->query("SELECT COLUMN_NAME AS Field, DATA_TYPE as Type FROM INFORMATION_SCHEMA.Columns WHERE TABLE_NAME = '".$this->escape_table($table)."'", $this->link);
return $query->result_array(TRUE);
}
}
/**
* MSSQL Result
*/
class Mssql_Result extends Database_Result {
// Fetch function and return type
protected $fetch_type = 'mssql_fetch_object';
protected $return_type = MSSQL_ASSOC;
/**
* Sets up the result variables.
*
* @param resource query result
* @param resource database link
* @param boolean return objects or arrays
* @param string SQL query that was run
*/
public function __construct($result, $link, $object = TRUE, $sql)
{
$this->result = $result;
// If the query is a resource, it was a SELECT, SHOW, DESCRIBE, EXPLAIN query
if (is_resource($result))
{
$this->current_row = 0;
$this->total_rows = mssql_num_rows($this->result);
$this->fetch_type = ($object === TRUE) ? 'mssql_fetch_object' : 'mssql_fetch_array';
}
elseif (is_bool($result))
{
if ($result == FALSE)
{
// SQL error
throw new Kohana_Database_Exception('database.error', mssql_get_last_message($link).' - '.$sql);
}
else
{
// Its an DELETE, INSERT, REPLACE, or UPDATE querys
$last_id = mssql_query('SELECT @@IDENTITY AS last_id', $link);
$result = mssql_fetch_assoc($last_id);
$this->insert_id = $result['last_id'];
$this->total_rows = mssql_rows_affected($link);
}
}
// Set result type
$this->result($object);
// Store the SQL
$this->sql = $sql;
}
/**
* Destruct, the cleanup crew!
*/
public function __destruct()
{
if (is_resource($this->result))
{
mssql_free_result($this->result);
}
}
public function result($object = TRUE, $type = MSSQL_ASSOC)
{
$this->fetch_type = ((bool) $object) ? 'mssql_fetch_object' : 'mssql_fetch_array';
// This check has to be outside the previous statement, because we do not
// know the state of fetch_type when $object = NULL
// NOTE - The class set by $type must be defined before fetching the result,
// autoloading is disabled to save a lot of stupid overhead.
if ($this->fetch_type == 'mssql_fetch_object')
{
$this->return_type = (is_string($type) AND Kohana::auto_load($type)) ? $type : 'stdClass';
}
else
{
$this->return_type = $type;
}
return $this;
}
public function as_array($object = NULL, $type = MSSQL_ASSOC)
{
return $this->result_array($object, $type);
}
public function result_array($object = NULL, $type = MSSQL_ASSOC)
{
$rows = array();
if (is_string($object))
{
$fetch = $object;
}
elseif (is_bool($object))
{
if ($object === TRUE)
{
$fetch = 'mssql_fetch_object';
// NOTE - The class set by $type must be defined before fetching the result,
// autoloading is disabled to save a lot of stupid overhead.
$type = (is_string($type) AND Kohana::auto_load($type)) ? $type : 'stdClass';
}
else
{
$fetch = 'mssql_fetch_array';
}
}
else
{
// Use the default config values
$fetch = $this->fetch_type;
if ($fetch == 'mssql_fetch_object')
{
$type = (is_string($type) AND Kohana::auto_load($type)) ? $type : 'stdClass';
}
}
if (mssql_num_rows($this->result))
{
// Reset the pointer location to make sure things work properly
mssql_data_seek($this->result, 0);
while ($row = $fetch($this->result, $type))
{
$rows[] = $row;
}
}
return isset($rows) ? $rows : array();
}
public function list_fields()
{
$field_names = array();
while ($field = mssql_fetch_field($this->result))
{
$field_names[] = $field->name;
}
return $field_names;
}
public function seek($offset)
{
if ( ! $this->offsetExists($offset))
return FALSE;
return mssql_data_seek($this->result, $offset);
}
} // End mssql_Result Class

View File

@@ -0,0 +1,496 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* MySQL Database Driver
*
* $Id: Mysql.php 4344 2009-05-11 16:41:39Z zombor $
*
* @package Core
* @author Kohana Team
* @copyright (c) 2007-2008 Kohana Team
* @license http://kohanaphp.com/license.html
*/
class Database_Mysql_Driver extends Database_Driver {
/**
* Database connection link
*/
protected $link;
/**
* Database configuration
*/
protected $db_config;
/**
* Sets the config for the class.
*
* @param array database configuration
*/
public function __construct($config)
{
$this->db_config = $config;
Kohana::log('debug', 'MySQL Database Driver Initialized');
}
/**
* Closes the database connection.
*/
public function __destruct()
{
is_resource($this->link) and mysql_close($this->link);
}
public function connect()
{
// Check if link already exists
if (is_resource($this->link))
return $this->link;
// Import the connect variables
extract($this->db_config['connection']);
// Persistent connections enabled?
$connect = ($this->db_config['persistent'] == TRUE) ? 'mysql_pconnect' : 'mysql_connect';
// Build the connection info
$host = isset($host) ? $host : $socket;
$port = isset($port) ? ':'.$port : '';
// Make the connection and select the database
if (($this->link = $connect($host.$port, $user, $pass, TRUE)) AND mysql_select_db($database, $this->link))
{
if ($charset = $this->db_config['character_set'])
{
$this->set_charset($charset);
}
// Clear password after successful connect
$this->db_config['connection']['pass'] = NULL;
return $this->link;
}
return FALSE;
}
public function query($sql)
{
// Only cache if it's turned on, and only cache if it's not a write statement
if ($this->db_config['cache'] AND ! preg_match('#\b(?:INSERT|UPDATE|REPLACE|SET|DELETE|TRUNCATE)\b#i', $sql))
{
$hash = $this->query_hash($sql);
if ( ! isset($this->query_cache[$hash]))
{
// Set the cached object
$this->query_cache[$hash] = new Mysql_Result(mysql_query($sql, $this->link), $this->link, $this->db_config['object'], $sql);
}
else
{
// Rewind cached result
$this->query_cache[$hash]->rewind();
}
// Return the cached query
return $this->query_cache[$hash];
}
return new Mysql_Result(mysql_query($sql, $this->link), $this->link, $this->db_config['object'], $sql);
}
public function set_charset($charset)
{
$this->query('SET NAMES '.$this->escape_str($charset));
}
public function escape_table($table)
{
if (!$this->db_config['escape'])
return $table;
if (stripos($table, ' AS ') !== FALSE)
{
// Force 'AS' to uppercase
$table = str_ireplace(' AS ', ' AS ', $table);
// Runs escape_table on both sides of an AS statement
$table = array_map(array($this, __FUNCTION__), explode(' AS ', $table));
// Re-create the AS statement
return implode(' AS ', $table);
}
return '`'.str_replace('.', '`.`', $table).'`';
}
public function escape_column($column)
{
if (!$this->db_config['escape'])
return $column;
if ($column == '*')
return $column;
// This matches any functions we support to SELECT.
if ( preg_match('/(avg|count|sum|max|min)\(\s*(.*)\s*\)(\s*as\s*(.+)?)?/i', $column, $matches))
{
if ( count($matches) == 3)
{
return $matches[1].'('.$this->escape_column($matches[2]).')';
}
else if ( count($matches) == 5)
{
return $matches[1].'('.$this->escape_column($matches[2]).') AS '.$this->escape_column($matches[2]);
}
}
// This matches any modifiers we support to SELECT.
if ( ! preg_match('/\b(?:rand|all|distinct(?:row)?|high_priority|sql_(?:small_result|b(?:ig_result|uffer_result)|no_cache|ca(?:che|lc_found_rows)))\s/i', $column))
{
if (stripos($column, ' AS ') !== FALSE)
{
// Force 'AS' to uppercase
$column = str_ireplace(' AS ', ' AS ', $column);
// Runs escape_column on both sides of an AS statement
$column = array_map(array($this, __FUNCTION__), explode(' AS ', $column));
// Re-create the AS statement
return implode(' AS ', $column);
}
return preg_replace('/[^.*]+/', '`$0`', $column);
}
$parts = explode(' ', $column);
$column = '';
for ($i = 0, $c = count($parts); $i < $c; $i++)
{
// The column is always last
if ($i == ($c - 1))
{
$column .= preg_replace('/[^.*]+/', '`$0`', $parts[$i]);
}
else // otherwise, it's a modifier
{
$column .= $parts[$i].' ';
}
}
return $column;
}
public function regex($field, $match, $type, $num_regexs)
{
$prefix = ($num_regexs == 0) ? '' : $type;
return $prefix.' '.$this->escape_column($field).' REGEXP \''.$this->escape_str($match).'\'';
}
public function notregex($field, $match, $type, $num_regexs)
{
$prefix = $num_regexs == 0 ? '' : $type;
return $prefix.' '.$this->escape_column($field).' NOT REGEXP \''.$this->escape_str($match) . '\'';
}
public function merge($table, $keys, $values)
{
// Escape the column names
foreach ($keys as $key => $value)
{
$keys[$key] = $this->escape_column($value);
}
return 'REPLACE INTO '.$this->escape_table($table).' ('.implode(', ', $keys).') VALUES ('.implode(', ', $values).')';
}
public function limit($limit, $offset = 0)
{
return 'LIMIT '.$offset.', '.$limit;
}
public function compile_select($database)
{
$sql = ($database['distinct'] == TRUE) ? 'SELECT DISTINCT ' : 'SELECT ';
$sql .= (count($database['select']) > 0) ? implode(', ', $database['select']) : '*';
if (count($database['from']) > 0)
{
// Escape the tables
$froms = array();
foreach ($database['from'] as $from)
{
$froms[] = $this->escape_column($from);
}
$sql .= "\nFROM (";
$sql .= implode(', ', $froms).")";
}
if (count($database['join']) > 0)
{
foreach($database['join'] AS $join)
{
$sql .= "\n".$join['type'].'JOIN '.implode(', ', $join['tables']).' ON '.$join['conditions'];
}
}
if (count($database['where']) > 0)
{
$sql .= "\nWHERE ";
}
$sql .= implode("\n", $database['where']);
if (count($database['groupby']) > 0)
{
$sql .= "\nGROUP BY ";
$sql .= implode(', ', $database['groupby']);
}
if (count($database['having']) > 0)
{
$sql .= "\nHAVING ";
$sql .= implode("\n", $database['having']);
}
if (count($database['orderby']) > 0)
{
$sql .= "\nORDER BY ";
$sql .= implode(', ', $database['orderby']);
}
if (is_numeric($database['limit']))
{
$sql .= "\n";
$sql .= $this->limit($database['limit'], $database['offset']);
}
return $sql;
}
public function escape_str($str)
{
if (!$this->db_config['escape'])
return $str;
is_resource($this->link) or $this->connect();
return mysql_real_escape_string($str, $this->link);
}
public function list_tables()
{
$tables = array();
if ($query = $this->query('SHOW TABLES FROM '.$this->escape_table($this->db_config['connection']['database'])))
{
foreach ($query->result(FALSE) as $row)
{
$tables[] = current($row);
}
}
return $tables;
}
public function show_error()
{
return mysql_error($this->link);
}
public function list_fields($table)
{
$result = NULL;
foreach ($this->field_data($table) as $row)
{
// Make an associative array
$result[$row->Field] = $this->sql_type($row->Type);
if ($row->Key === 'PRI' AND $row->Extra === 'auto_increment')
{
// For sequenced (AUTO_INCREMENT) tables
$result[$row->Field]['sequenced'] = TRUE;
}
if ($row->Null === 'YES')
{
// Set NULL status
$result[$row->Field]['null'] = TRUE;
}
}
if (!isset($result))
throw new Kohana_Database_Exception('database.table_not_found', $table);
return $result;
}
public function field_data($table)
{
$result = $this->query('SHOW COLUMNS FROM '.$this->escape_table($table));
return $result->result_array(TRUE);
}
} // End Database_Mysql_Driver Class
/**
* MySQL Result
*/
class Mysql_Result extends Database_Result {
// Fetch function and return type
protected $fetch_type = 'mysql_fetch_object';
protected $return_type = MYSQL_ASSOC;
/**
* Sets up the result variables.
*
* @param resource query result
* @param resource database link
* @param boolean return objects or arrays
* @param string SQL query that was run
*/
public function __construct($result, $link, $object = TRUE, $sql)
{
$this->result = $result;
// If the query is a resource, it was a SELECT, SHOW, DESCRIBE, EXPLAIN query
if (is_resource($result))
{
$this->current_row = 0;
$this->total_rows = mysql_num_rows($this->result);
$this->fetch_type = ($object === TRUE) ? 'mysql_fetch_object' : 'mysql_fetch_array';
}
elseif (is_bool($result))
{
if ($result == FALSE)
{
// SQL error
throw new Kohana_Database_Exception('database.error', mysql_error($link).' - '.$sql);
}
else
{
// Its an DELETE, INSERT, REPLACE, or UPDATE query
$this->insert_id = mysql_insert_id($link);
$this->total_rows = mysql_affected_rows($link);
}
}
// Set result type
$this->result($object);
// Store the SQL
$this->sql = $sql;
}
/**
* Destruct, the cleanup crew!
*/
public function __destruct()
{
if (is_resource($this->result))
{
mysql_free_result($this->result);
}
}
public function result($object = TRUE, $type = MYSQL_ASSOC)
{
$this->fetch_type = ((bool) $object) ? 'mysql_fetch_object' : 'mysql_fetch_array';
// This check has to be outside the previous statement, because we do not
// know the state of fetch_type when $object = NULL
// NOTE - The class set by $type must be defined before fetching the result,
// autoloading is disabled to save a lot of stupid overhead.
if ($this->fetch_type == 'mysql_fetch_object' AND $object === TRUE)
{
$this->return_type = (is_string($type) AND Kohana::auto_load($type)) ? $type : 'stdClass';
}
else
{
$this->return_type = $type;
}
return $this;
}
public function as_array($object = NULL, $type = MYSQL_ASSOC)
{
return $this->result_array($object, $type);
}
public function result_array($object = NULL, $type = MYSQL_ASSOC)
{
$rows = array();
if (is_string($object))
{
$fetch = $object;
}
elseif (is_bool($object))
{
if ($object === TRUE)
{
$fetch = 'mysql_fetch_object';
$type = (is_string($type) AND Kohana::auto_load($type)) ? $type : 'stdClass';
}
else
{
$fetch = 'mysql_fetch_array';
}
}
else
{
// Use the default config values
$fetch = $this->fetch_type;
if ($fetch == 'mysql_fetch_object')
{
$type = (is_string($this->return_type) AND Kohana::auto_load($this->return_type)) ? $this->return_type : 'stdClass';
}
}
if (mysql_num_rows($this->result))
{
// Reset the pointer location to make sure things work properly
mysql_data_seek($this->result, 0);
while ($row = $fetch($this->result, $type))
{
$rows[] = $row;
}
}
return isset($rows) ? $rows : array();
}
public function list_fields()
{
$field_names = array();
while ($field = mysql_fetch_field($this->result))
{
$field_names[] = $field->name;
}
return $field_names;
}
public function seek($offset)
{
if ($this->offsetExists($offset) AND mysql_data_seek($this->result, $offset))
{
// Set the current row to the offset
$this->current_row = $offset;
return TRUE;
}
else
{
return FALSE;
}
}
} // End Mysql_Result Class

View File

@@ -0,0 +1,358 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* MySQLi Database Driver
*
* $Id: Mysqli.php 4344 2009-05-11 16:41:39Z zombor $
*
* @package Core
* @author Kohana Team
* @copyright (c) 2007-2008 Kohana Team
* @license http://kohanaphp.com/license.html
*/
class Database_Mysqli_Driver extends Database_Mysql_Driver {
// Database connection link
protected $link;
protected $db_config;
protected $statements = array();
/**
* Sets the config for the class.
*
* @param array database configuration
*/
public function __construct($config)
{
$this->db_config = $config;
Kohana::log('debug', 'MySQLi Database Driver Initialized');
}
/**
* Closes the database connection.
*/
public function __destruct()
{
is_object($this->link) and $this->link->close();
}
public function connect()
{
// Check if link already exists
if (is_object($this->link))
return $this->link;
// Import the connect variables
extract($this->db_config['connection']);
// Build the connection info
$host = isset($host) ? $host : $socket;
// Make the connection and select the database
if ($this->link = new mysqli($host, $user, $pass, $database, $port))
{
if ($charset = $this->db_config['character_set'])
{
$this->set_charset($charset);
}
// Clear password after successful connect
$this->db_config['connection']['pass'] = NULL;
return $this->link;
}
return FALSE;
}
public function query($sql)
{
// Only cache if it's turned on, and only cache if it's not a write statement
if ($this->db_config['cache'] AND ! preg_match('#\b(?:INSERT|UPDATE|REPLACE|SET|DELETE|TRUNCATE)\b#i', $sql))
{
$hash = $this->query_hash($sql);
if ( ! isset($this->query_cache[$hash]))
{
// Set the cached object
$this->query_cache[$hash] = new Kohana_Mysqli_Result($this->link, $this->db_config['object'], $sql);
}
else
{
// Rewind cached result
$this->query_cache[$hash]->rewind();
}
// Return the cached query
return $this->query_cache[$hash];
}
return new Kohana_Mysqli_Result($this->link, $this->db_config['object'], $sql);
}
public function set_charset($charset)
{
if ($this->link->set_charset($charset) === FALSE)
throw new Kohana_Database_Exception('database.error', $this->show_error());
}
public function escape_str($str)
{
if (!$this->db_config['escape'])
return $str;
is_object($this->link) or $this->connect();
return $this->link->real_escape_string($str);
}
public function show_error()
{
return $this->link->error;
}
} // End Database_Mysqli_Driver Class
/**
* MySQLi Result
*/
class Kohana_Mysqli_Result extends Database_Result {
// Database connection
protected $link;
// Data fetching types
protected $fetch_type = 'mysqli_fetch_object';
protected $return_type = MYSQLI_ASSOC;
/**
* Sets up the result variables.
*
* @param object database link
* @param boolean return objects or arrays
* @param string SQL query that was run
*/
public function __construct($link, $object = TRUE, $sql)
{
$this->link = $link;
if ( ! $this->link->multi_query($sql))
{
// SQL error
throw new Kohana_Database_Exception('database.error', $this->link->error.' - '.$sql);
}
else
{
$this->result = $this->link->store_result();
// If the query is an object, it was a SELECT, SHOW, DESCRIBE, EXPLAIN query
if (is_object($this->result))
{
$this->current_row = 0;
$this->total_rows = $this->result->num_rows;
$this->fetch_type = ($object === TRUE) ? 'fetch_object' : 'fetch_array';
}
elseif ($this->link->error)
{
// SQL error
throw new Kohana_Database_Exception('database.error', $this->link->error.' - '.$sql);
}
else
{
// Its an DELETE, INSERT, REPLACE, or UPDATE query
$this->insert_id = $this->link->insert_id;
$this->total_rows = $this->link->affected_rows;
}
}
// Set result type
$this->result($object);
// Store the SQL
$this->sql = $sql;
}
/**
* Magic __destruct function, frees the result.
*/
public function __destruct()
{
if (is_object($this->result))
{
$this->result->free_result();
// this is kinda useless, but needs to be done to avoid the "Commands out of sync; you
// can't run this command now" error. Basically, we get all results after the first one
// (the one we actually need) and free them.
if (is_resource($this->link) AND $this->link->more_results())
{
do
{
if ($result = $this->link->store_result())
{
$result->free_result();
}
} while ($this->link->next_result());
}
}
}
public function result($object = TRUE, $type = MYSQLI_ASSOC)
{
$this->fetch_type = ((bool) $object) ? 'fetch_object' : 'fetch_array';
// This check has to be outside the previous statement, because we do not
// know the state of fetch_type when $object = NULL
// NOTE - The class set by $type must be defined before fetching the result,
// autoloading is disabled to save a lot of stupid overhead.
if ($this->fetch_type == 'fetch_object')
{
$this->return_type = (is_string($type) AND Kohana::auto_load($type)) ? $type : 'stdClass';
}
else
{
$this->return_type = $type;
}
return $this;
}
public function as_array($object = NULL, $type = MYSQLI_ASSOC)
{
return $this->result_array($object, $type);
}
public function result_array($object = NULL, $type = MYSQLI_ASSOC)
{
$rows = array();
if (is_string($object))
{
$fetch = $object;
}
elseif (is_bool($object))
{
if ($object === TRUE)
{
$fetch = 'fetch_object';
// NOTE - The class set by $type must be defined before fetching the result,
// autoloading is disabled to save a lot of stupid overhead.
$type = (is_string($type) AND Kohana::auto_load($type)) ? $type : 'stdClass';
}
else
{
$fetch = 'fetch_array';
}
}
else
{
// Use the default config values
$fetch = $this->fetch_type;
if ($fetch == 'fetch_object')
{
$type = (is_string($type) AND Kohana::auto_load($type)) ? $type : 'stdClass';
}
}
if ($this->result->num_rows)
{
// Reset the pointer location to make sure things work properly
$this->result->data_seek(0);
while ($row = $this->result->$fetch($type))
{
$rows[] = $row;
}
}
return isset($rows) ? $rows : array();
}
public function list_fields()
{
$field_names = array();
while ($field = $this->result->fetch_field())
{
$field_names[] = $field->name;
}
return $field_names;
}
public function seek($offset)
{
if ($this->offsetExists($offset) AND $this->result->data_seek($offset))
{
// Set the current row to the offset
$this->current_row = $offset;
return TRUE;
}
return FALSE;
}
public function offsetGet($offset)
{
if ( ! $this->seek($offset))
return FALSE;
// Return the row
$fetch = $this->fetch_type;
return $this->result->$fetch($this->return_type);
}
} // End Mysqli_Result Class
/**
* MySQLi Prepared Statement (experimental)
*/
class Kohana_Mysqli_Statement {
protected $link = NULL;
protected $stmt;
protected $var_names = array();
protected $var_values = array();
public function __construct($sql, $link)
{
$this->link = $link;
$this->stmt = $this->link->prepare($sql);
return $this;
}
public function __destruct()
{
$this->stmt->close();
}
// Sets the bind parameters
public function bind_params($param_types, $params)
{
$this->var_names = array_keys($params);
$this->var_values = array_values($params);
call_user_func_array(array($this->stmt, 'bind_param'), array_merge($param_types, $var_names));
return $this;
}
public function bind_result($params)
{
call_user_func_array(array($this->stmt, 'bind_result'), $params);
}
// Runs the statement
public function execute()
{
foreach ($this->var_names as $key => $name)
{
$$name = $this->var_values[$key];
}
$this->stmt->execute();
return $this->stmt;
}
}

View File

@@ -0,0 +1,486 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/*
* Class: Database_PdoSqlite_Driver
* Provides specific database items for Sqlite.
*
* Connection string should be, eg: "pdosqlite://path/to/database.db"
*
* Version 1.0 alpha
* author - Doutu, updated by gregmac
* copyright - (c) BSD
* license - <no>
*/
class Database_Pdosqlite_Driver extends Database_Driver {
// Database connection link
protected $link;
protected $db_config;
/*
* Constructor: __construct
* Sets up the config for the class.
*
* Parameters:
* config - database configuration
*
*/
public function __construct($config)
{
$this->db_config = $config;
Kohana::log('debug', 'PDO:Sqlite Database Driver Initialized');
}
public function connect()
{
// Import the connect variables
extract($this->db_config['connection']);
try
{
$this->link = new PDO('sqlite:'.$socket.$database, $user, $pass,
array(PDO::ATTR_PERSISTENT => $this->db_config['persistent']));
$this->link->setAttribute(PDO::ATTR_CASE, PDO::CASE_NATURAL);
//$this->link->query('PRAGMA count_changes=1;');
if ($charset = $this->db_config['character_set'])
{
$this->set_charset($charset);
}
}
catch (PDOException $e)
{
throw new Kohana_Database_Exception('database.error', $e->getMessage());
}
// Clear password after successful connect
$this->db_config['connection']['pass'] = NULL;
return $this->link;
}
public function query($sql)
{
try
{
$sth = $this->link->prepare($sql);
}
catch (PDOException $e)
{
throw new Kohana_Database_Exception('database.error', $e->getMessage());
}
return new Pdosqlite_Result($sth, $this->link, $this->db_config['object'], $sql);
}
public function set_charset($charset)
{
$this->link->query('PRAGMA encoding = '.$this->escape_str($charset));
}
public function escape_table($table)
{
if ( ! $this->db_config['escape'])
return $table;
return '`'.str_replace('.', '`.`', $table).'`';
}
public function escape_column($column)
{
if ( ! $this->db_config['escape'])
return $column;
if ($column == '*')
return $column;
// This matches any functions we support to SELECT.
if ( preg_match('/(avg|count|sum|max|min)\(\s*(.*)\s*\)(\s*as\s*(.+)?)?/i', $column, $matches))
{
if ( count($matches) == 3)
{
return $matches[1].'('.$this->escape_column($matches[2]).')';
}
else if ( count($matches) == 5)
{
return $matches[1].'('.$this->escape_column($matches[2]).') AS '.$this->escape_column($matches[2]);
}
}
// This matches any modifiers we support to SELECT.
if ( ! preg_match('/\b(?:rand|all|distinct(?:row)?|high_priority|sql_(?:small_result|b(?:ig_result|uffer_result)|no_cache|ca(?:che|lc_found_rows)))\s/i', $column))
{
if (stripos($column, ' AS ') !== FALSE)
{
// Force 'AS' to uppercase
$column = str_ireplace(' AS ', ' AS ', $column);
// Runs escape_column on both sides of an AS statement
$column = array_map(array($this, __FUNCTION__), explode(' AS ', $column));
// Re-create the AS statement
return implode(' AS ', $column);
}
return preg_replace('/[^.*]+/', '`$0`', $column);
}
$parts = explode(' ', $column);
$column = '';
for ($i = 0, $c = count($parts); $i < $c; $i++)
{
// The column is always last
if ($i == ($c - 1))
{
$column .= preg_replace('/[^.*]+/', '`$0`', $parts[$i]);
}
else // otherwise, it's a modifier
{
$column .= $parts[$i].' ';
}
}
return $column;
}
public function limit($limit, $offset = 0)
{
return 'LIMIT '.$offset.', '.$limit;
}
public function compile_select($database)
{
$sql = ($database['distinct'] == TRUE) ? 'SELECT DISTINCT ' : 'SELECT ';
$sql .= (count($database['select']) > 0) ? implode(', ', $database['select']) : '*';
if (count($database['from']) > 0)
{
$sql .= "\nFROM ";
$sql .= implode(', ', $database['from']);
}
if (count($database['join']) > 0)
{
foreach($database['join'] AS $join)
{
$sql .= "\n".$join['type'].'JOIN '.implode(', ', $join['tables']).' ON '.$join['conditions'];
}
}
if (count($database['where']) > 0)
{
$sql .= "\nWHERE ";
}
$sql .= implode("\n", $database['where']);
if (count($database['groupby']) > 0)
{
$sql .= "\nGROUP BY ";
$sql .= implode(', ', $database['groupby']);
}
if (count($database['having']) > 0)
{
$sql .= "\nHAVING ";
$sql .= implode("\n", $database['having']);
}
if (count($database['orderby']) > 0)
{
$sql .= "\nORDER BY ";
$sql .= implode(', ', $database['orderby']);
}
if (is_numeric($database['limit']))
{
$sql .= "\n";
$sql .= $this->limit($database['limit'], $database['offset']);
}
return $sql;
}
public function escape_str($str)
{
if ( ! $this->db_config['escape'])
return $str;
if (function_exists('sqlite_escape_string'))
{
$res = sqlite_escape_string($str);
}
else
{
$res = str_replace("'", "''", $str);
}
return $res;
}
public function list_tables()
{
$sql = "SELECT `name` FROM `sqlite_master` WHERE `type`='table' ORDER BY `name`;";
try
{
$result = $this->query($sql)->result(FALSE, PDO::FETCH_ASSOC);
$tables = array();
foreach ($result as $row)
{
$tables[] = current($row);
}
}
catch (PDOException $e)
{
throw new Kohana_Database_Exception('database.error', $e->getMessage());
}
return $tables;
}
public function show_error()
{
$err = $this->link->errorInfo();
return isset($err[2]) ? $err[2] : 'Unknown error!';
}
public function list_fields($table, $query = FALSE)
{
static $tables;
if (is_object($query))
{
if (empty($tables[$table]))
{
$tables[$table] = array();
foreach ($query->result() as $row)
{
$tables[$table][] = $row->name;
}
}
return $tables[$table];
}
else
{
$result = $this->link->query( 'PRAGMA table_info('.$this->escape_table($table).')' );
foreach ($result as $row)
{
$tables[$table][$row['name']] = $this->sql_type($row['type']);
}
return $tables[$table];
}
}
public function field_data($table)
{
Kohana::log('error', 'This method is under developing');
}
/**
* Version number query string
*
* @access public
* @return string
*/
function version()
{
return $this->link->getAttribute(constant("PDO::ATTR_SERVER_VERSION"));
}
} // End Database_PdoSqlite_Driver Class
/*
* PDO-sqlite Result
*/
class Pdosqlite_Result extends Database_Result {
// Data fetching types
protected $fetch_type = PDO::FETCH_OBJ;
protected $return_type = PDO::FETCH_ASSOC;
/**
* Sets up the result variables.
*
* @param resource query result
* @param resource database link
* @param boolean return objects or arrays
* @param string SQL query that was run
*/
public function __construct($result, $link, $object = TRUE, $sql)
{
if (is_object($result) OR $result = $link->prepare($sql))
{
// run the query. Return true if success, false otherwise
if( ! $result->execute())
{
// Throw Kohana Exception with error message. See PDOStatement errorInfo() method
$arr_infos = $result->errorInfo();
throw new Kohana_Database_Exception('database.error', $arr_infos[2]);
}
if (preg_match('/^SELECT|PRAGMA|EXPLAIN/i', $sql))
{
$this->result = $result;
$this->current_row = 0;
$this->total_rows = $this->sqlite_row_count();
$this->fetch_type = ($object === TRUE) ? PDO::FETCH_OBJ : PDO::FETCH_ASSOC;
}
elseif (preg_match('/^DELETE|INSERT|UPDATE/i', $sql))
{
$this->insert_id = $link->lastInsertId();
$this->total_rows = $result->rowCount();
}
}
else
{
// SQL error
throw new Kohana_Database_Exception('database.error', $link->errorInfo().' - '.$sql);
}
// Set result type
$this->result($object);
// Store the SQL
$this->sql = $sql;
}
private function sqlite_row_count()
{
$count = 0;
while ($this->result->fetch())
{
$count++;
}
// The query must be re-fetched now.
$this->result->execute();
return $count;
}
/*
* Destructor: __destruct
* Magic __destruct function, frees the result.
*/
public function __destruct()
{
if (is_object($this->result))
{
$this->result->closeCursor();
$this->result = NULL;
}
}
public function result($object = TRUE, $type = PDO::FETCH_BOTH)
{
$this->fetch_type = (bool) $object ? PDO::FETCH_OBJ : PDO::FETCH_BOTH;
if ($this->fetch_type == PDO::FETCH_OBJ)
{
$this->return_type = (is_string($type) AND Kohana::auto_load($type)) ? $type : 'stdClass';
}
else
{
$this->return_type = $type;
}
return $this;
}
public function as_array($object = NULL, $type = PDO::FETCH_ASSOC)
{
return $this->result_array($object, $type);
}
public function result_array($object = NULL, $type = PDO::FETCH_ASSOC)
{
$rows = array();
if (is_string($object))
{
$fetch = $object;
}
elseif (is_bool($object))
{
if ($object === TRUE)
{
$fetch = PDO::FETCH_OBJ;
// NOTE - The class set by $type must be defined before fetching the result,
// autoloading is disabled to save a lot of stupid overhead.
$type = (is_string($type) AND Kohana::auto_load($type)) ? $type : 'stdClass';
}
else
{
$fetch = PDO::FETCH_OBJ;
}
}
else
{
// Use the default config values
$fetch = $this->fetch_type;
if ($fetch == PDO::FETCH_OBJ)
{
$type = (is_string($type) AND Kohana::auto_load($type)) ? $type : 'stdClass';
}
}
try
{
while ($row = $this->result->fetch($fetch))
{
$rows[] = $row;
}
}
catch(PDOException $e)
{
throw new Kohana_Database_Exception('database.error', $e->getMessage());
return FALSE;
}
return $rows;
}
public function list_fields()
{
$field_names = array();
for ($i = 0, $max = $this->result->columnCount(); $i < $max; $i++)
{
$info = $this->result->getColumnMeta($i);
$field_names[] = $info['name'];
}
return $field_names;
}
public function seek($offset)
{
// To request a scrollable cursor for your PDOStatement object, you must
// set the PDO::ATTR_CURSOR attribute to PDO::CURSOR_SCROLL when you
// prepare the statement.
Kohana::log('error', get_class($this).' does not support scrollable cursors, '.__FUNCTION__.' call ignored');
return FALSE;
}
public function offsetGet($offset)
{
try
{
return $this->result->fetch($this->fetch_type, PDO::FETCH_ORI_ABS, $offset);
}
catch(PDOException $e)
{
throw new Kohana_Database_Exception('database.error', $e->getMessage());
}
}
public function rewind()
{
// Same problem that seek() has, see above.
return $this->seek(0);
}
} // End PdoSqlite_Result Class

View File

@@ -0,0 +1,538 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* PostgreSQL 8.1+ Database Driver
*
* $Id: Pgsql.php 4344 2009-05-11 16:41:39Z zombor $
*
* @package Core
* @author Kohana Team
* @copyright (c) 2007-2008 Kohana Team
* @license http://kohanaphp.com/license.html
*/
class Database_Pgsql_Driver extends Database_Driver {
// Database connection link
protected $link;
protected $db_config;
/**
* Sets the config for the class.
*
* @param array database configuration
*/
public function __construct($config)
{
$this->db_config = $config;
Kohana::log('debug', 'PgSQL Database Driver Initialized');
}
public function connect()
{
// Check if link already exists
if (is_resource($this->link))
return $this->link;
// Import the connect variables
extract($this->db_config['connection']);
// Persistent connections enabled?
$connect = ($this->db_config['persistent'] == TRUE) ? 'pg_pconnect' : 'pg_connect';
// Build the connection info
$port = isset($port) ? 'port=\''.$port.'\'' : '';
$host = isset($host) ? 'host=\''.$host.'\' '.$port : ''; // if no host, connect with the socket
$connection_string = $host.' dbname=\''.$database.'\' user=\''.$user.'\' password=\''.$pass.'\'';
// Make the connection and select the database
if ($this->link = $connect($connection_string))
{
if ($charset = $this->db_config['character_set'])
{
echo $this->set_charset($charset);
}
// Clear password after successful connect
$this->db_config['connection']['pass'] = NULL;
return $this->link;
}
return FALSE;
}
public function query($sql)
{
// Only cache if it's turned on, and only cache if it's not a write statement
if ($this->db_config['cache'] AND ! preg_match('#\b(?:INSERT|UPDATE|SET)\b#i', $sql))
{
$hash = $this->query_hash($sql);
if ( ! isset($this->query_cache[$hash]))
{
// Set the cached object
$this->query_cache[$hash] = new Pgsql_Result(pg_query($this->link, $sql), $this->link, $this->db_config['object'], $sql);
}
else
{
// Rewind cached result
$this->query_cache[$hash]->rewind();
}
return $this->query_cache[$hash];
}
// Suppress warning triggered when a database error occurs (e.g., a constraint violation)
return new Pgsql_Result(@pg_query($this->link, $sql), $this->link, $this->db_config['object'], $sql);
}
public function set_charset($charset)
{
$this->query('SET client_encoding TO '.pg_escape_string($this->link, $charset));
}
public function escape_table($table)
{
if (!$this->db_config['escape'])
return $table;
return '"'.str_replace('.', '"."', $table).'"';
}
public function escape_column($column)
{
if (!$this->db_config['escape'])
return $column;
if ($column == '*')
return $column;
// This matches any functions we support to SELECT.
if ( preg_match('/(avg|count|sum|max|min)\(\s*(.*)\s*\)(\s*as\s*(.+)?)?/i', $column, $matches))
{
if ( count($matches) == 3)
{
return $matches[1].'('.$this->escape_column($matches[2]).')';
}
else if ( count($matches) == 5)
{
return $matches[1].'('.$this->escape_column($matches[2]).') AS '.$this->escape_column($matches[2]);
}
}
// This matches any modifiers we support to SELECT.
if ( ! preg_match('/\b(?:all|distinct)\s/i', $column))
{
if (stripos($column, ' AS ') !== FALSE)
{
// Force 'AS' to uppercase
$column = str_ireplace(' AS ', ' AS ', $column);
// Runs escape_column on both sides of an AS statement
$column = array_map(array($this, __FUNCTION__), explode(' AS ', $column));
// Re-create the AS statement
return implode(' AS ', $column);
}
return preg_replace('/[^.*]+/', '"$0"', $column);
}
$parts = explode(' ', $column);
$column = '';
for ($i = 0, $c = count($parts); $i < $c; $i++)
{
// The column is always last
if ($i == ($c - 1))
{
$column .= preg_replace('/[^.*]+/', '"$0"', $parts[$i]);
}
else // otherwise, it's a modifier
{
$column .= $parts[$i].' ';
}
}
return $column;
}
public function regex($field, $match, $type, $num_regexs)
{
$prefix = ($num_regexs == 0) ? '' : $type;
return $prefix.' '.$this->escape_column($field).' ~* \''.$this->escape_str($match).'\'';
}
public function notregex($field, $match, $type, $num_regexs)
{
$prefix = $num_regexs == 0 ? '' : $type;
return $prefix.' '.$this->escape_column($field).' !~* \''.$this->escape_str($match) . '\'';
}
public function limit($limit, $offset = 0)
{
return 'LIMIT '.$limit.' OFFSET '.$offset;
}
public function compile_select($database)
{
$sql = ($database['distinct'] == TRUE) ? 'SELECT DISTINCT ' : 'SELECT ';
$sql .= (count($database['select']) > 0) ? implode(', ', $database['select']) : '*';
if (count($database['from']) > 0)
{
$sql .= "\nFROM ";
$sql .= implode(', ', $database['from']);
}
if (count($database['join']) > 0)
{
foreach($database['join'] AS $join)
{
$sql .= "\n".$join['type'].'JOIN '.implode(', ', $join['tables']).' ON '.$join['conditions'];
}
}
if (count($database['where']) > 0)
{
$sql .= "\nWHERE ";
}
$sql .= implode("\n", $database['where']);
if (count($database['groupby']) > 0)
{
$sql .= "\nGROUP BY ";
$sql .= implode(', ', $database['groupby']);
}
if (count($database['having']) > 0)
{
$sql .= "\nHAVING ";
$sql .= implode("\n", $database['having']);
}
if (count($database['orderby']) > 0)
{
$sql .= "\nORDER BY ";
$sql .= implode(', ', $database['orderby']);
}
if (is_numeric($database['limit']))
{
$sql .= "\n";
$sql .= $this->limit($database['limit'], $database['offset']);
}
return $sql;
}
public function escape_str($str)
{
if (!$this->db_config['escape'])
return $str;
is_resource($this->link) or $this->connect();
return pg_escape_string($this->link, $str);
}
public function list_tables()
{
$sql = 'SELECT table_schema || \'.\' || table_name FROM information_schema.tables WHERE table_schema NOT IN (\'pg_catalog\', \'information_schema\')';
$result = $this->query($sql)->result(FALSE, PGSQL_ASSOC);
$retval = array();
foreach ($result as $row)
{
$retval[] = current($row);
}
return $retval;
}
public function show_error()
{
return pg_last_error($this->link);
}
public function list_fields($table)
{
$result = NULL;
foreach ($this->field_data($table) as $row)
{
// Make an associative array
$result[$row->column_name] = $this->sql_type($row->data_type);
if (!strncmp($row->column_default, 'nextval(', 8))
{
$result[$row->column_name]['sequenced'] = TRUE;
}
if ($row->is_nullable === 'YES')
{
$result[$row->column_name]['null'] = TRUE;
}
}
if (!isset($result))
throw new Kohana_Database_Exception('database.table_not_found', $table);
return $result;
}
public function field_data($table)
{
// http://www.postgresql.org/docs/8.3/static/infoschema-columns.html
$result = $this->query('
SELECT column_name, column_default, is_nullable, data_type, udt_name,
character_maximum_length, numeric_precision, numeric_precision_radix, numeric_scale
FROM information_schema.columns
WHERE table_name = \''. $this->escape_str($table) .'\'
ORDER BY ordinal_position
');
return $result->result_array(TRUE);
}
} // End Database_Pgsql_Driver Class
/**
* PostgreSQL Result
*/
class Pgsql_Result extends Database_Result {
// Data fetching types
protected $fetch_type = 'pgsql_fetch_object';
protected $return_type = PGSQL_ASSOC;
/**
* Sets up the result variables.
*
* @param resource query result
* @param resource database link
* @param boolean return objects or arrays
* @param string SQL query that was run
*/
public function __construct($result, $link, $object = TRUE, $sql)
{
$this->link = $link;
$this->result = $result;
// If the query is a resource, it was a SELECT, SHOW, DESCRIBE, EXPLAIN query
if (is_resource($result))
{
// Its an DELETE, INSERT, REPLACE, or UPDATE query
if (preg_match('/^(?:delete|insert|replace|update)\b/iD', trim($sql), $matches))
{
$this->insert_id = (strtolower($matches[0]) == 'insert') ? $this->insert_id() : FALSE;
$this->total_rows = pg_affected_rows($this->result);
}
else
{
$this->current_row = 0;
$this->total_rows = pg_num_rows($this->result);
$this->fetch_type = ($object === TRUE) ? 'pg_fetch_object' : 'pg_fetch_array';
}
}
else
{
throw new Kohana_Database_Exception('database.error', pg_last_error().' - '.$sql);
}
// Set result type
$this->result($object);
// Store the SQL
$this->sql = $sql;
}
/**
* Magic __destruct function, frees the result.
*/
public function __destruct()
{
if (is_resource($this->result))
{
pg_free_result($this->result);
}
}
public function result($object = TRUE, $type = PGSQL_ASSOC)
{
$this->fetch_type = ((bool) $object) ? 'pg_fetch_object' : 'pg_fetch_array';
// This check has to be outside the previous statement, because we do not
// know the state of fetch_type when $object = NULL
// NOTE - The class set by $type must be defined before fetching the result,
// autoloading is disabled to save a lot of stupid overhead.
if ($this->fetch_type == 'pg_fetch_object')
{
$this->return_type = (is_string($type) AND Kohana::auto_load($type)) ? $type : 'stdClass';
}
else
{
$this->return_type = $type;
}
return $this;
}
public function as_array($object = NULL, $type = PGSQL_ASSOC)
{
return $this->result_array($object, $type);
}
public function result_array($object = NULL, $type = PGSQL_ASSOC)
{
$rows = array();
if (is_string($object))
{
$fetch = $object;
}
elseif (is_bool($object))
{
if ($object === TRUE)
{
$fetch = 'pg_fetch_object';
// NOTE - The class set by $type must be defined before fetching the result,
// autoloading is disabled to save a lot of stupid overhead.
$type = (is_string($type) AND Kohana::auto_load($type)) ? $type : 'stdClass';
}
else
{
$fetch = 'pg_fetch_array';
}
}
else
{
// Use the default config values
$fetch = $this->fetch_type;
if ($fetch == 'pg_fetch_object')
{
$type = (is_string($type) AND Kohana::auto_load($type)) ? $type : 'stdClass';
}
}
if ($this->total_rows)
{
pg_result_seek($this->result, 0);
while ($row = $fetch($this->result, NULL, $type))
{
$rows[] = $row;
}
}
return $rows;
}
public function insert_id()
{
if ($this->insert_id === NULL)
{
$query = 'SELECT LASTVAL() AS insert_id';
// Disable error reporting for this, just to silence errors on
// tables that have no serial column.
$ER = error_reporting(0);
$result = pg_query($this->link, $query);
$insert_id = pg_fetch_array($result, NULL, PGSQL_ASSOC);
$this->insert_id = $insert_id['insert_id'];
// Reset error reporting
error_reporting($ER);
}
return $this->insert_id;
}
public function seek($offset)
{
if ($this->offsetExists($offset) AND pg_result_seek($this->result, $offset))
{
// Set the current row to the offset
$this->current_row = $offset;
return TRUE;
}
return FALSE;
}
public function list_fields()
{
$field_names = array();
$fields = pg_num_fields($this->result);
for ($i = 0; $i < $fields; ++$i)
{
$field_names[] = pg_field_name($this->result, $i);
}
return $field_names;
}
/**
* ArrayAccess: offsetGet
*/
public function offsetGet($offset)
{
if ( ! $this->seek($offset))
return FALSE;
// Return the row by calling the defined fetching callback
$fetch = $this->fetch_type;
return $fetch($this->result, NULL, $this->return_type);
}
} // End Pgsql_Result Class
/**
* PostgreSQL Prepared Statement (experimental)
*/
class Kohana_Pgsql_Statement {
protected $link = NULL;
protected $stmt;
public function __construct($sql, $link)
{
$this->link = $link;
$this->stmt = $this->link->prepare($sql);
return $this;
}
public function __destruct()
{
$this->stmt->close();
}
// Sets the bind parameters
public function bind_params()
{
$argv = func_get_args();
return $this;
}
// sets the statement values to the bound parameters
public function set_vals()
{
return $this;
}
// Runs the statement
public function execute()
{
return $this;
}
}

View File

@@ -0,0 +1,149 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* Image API driver.
*
* $Id: Image.php 3769 2008-12-15 00:48:56Z zombor $
*
* @package Image
* @author Kohana Team
* @copyright (c) 2007-2008 Kohana Team
* @license http://kohanaphp.com/license.html
*/
abstract class Image_Driver {
// Reference to the current image
protected $image;
// Reference to the temporary processing image
protected $tmp_image;
// Processing errors
protected $errors = array();
/**
* Executes a set of actions, defined in pairs.
*
* @param array actions
* @return boolean
*/
public function execute($actions)
{
foreach ($actions as $func => $args)
{
if ( ! $this->$func($args))
return FALSE;
}
return TRUE;
}
/**
* Sanitize and normalize a geometry array based on the temporary image
* width and height. Valid properties are: width, height, top, left.
*
* @param array geometry properties
* @return void
*/
protected function sanitize_geometry( & $geometry)
{
list($width, $height) = $this->properties();
// Turn off error reporting
$reporting = error_reporting(0);
// Width and height cannot exceed current image size
$geometry['width'] = min($geometry['width'], $width);
$geometry['height'] = min($geometry['height'], $height);
// Set standard coordinates if given, otherwise use pixel values
if ($geometry['top'] === 'center')
{
$geometry['top'] = floor(($height / 2) - ($geometry['height'] / 2));
}
elseif ($geometry['top'] === 'top')
{
$geometry['top'] = 0;
}
elseif ($geometry['top'] === 'bottom')
{
$geometry['top'] = $height - $geometry['height'];
}
// Set standard coordinates if given, otherwise use pixel values
if ($geometry['left'] === 'center')
{
$geometry['left'] = floor(($width / 2) - ($geometry['width'] / 2));
}
elseif ($geometry['left'] === 'left')
{
$geometry['left'] = 0;
}
elseif ($geometry['left'] === 'right')
{
$geometry['left'] = $width - $geometry['height'];
}
// Restore error reporting
error_reporting($reporting);
}
/**
* Return the current width and height of the temporary image. This is mainly
* needed for sanitizing the geometry.
*
* @return array width, height
*/
abstract protected function properties();
/**
* Process an image with a set of actions.
*
* @param string image filename
* @param array actions to execute
* @param string destination directory path
* @param string destination filename
* @return boolean
*/
abstract public function process($image, $actions, $dir, $file);
/**
* Flip an image. Valid directions are horizontal and vertical.
*
* @param integer direction to flip
* @return boolean
*/
abstract function flip($direction);
/**
* Crop an image. Valid properties are: width, height, top, left.
*
* @param array new properties
* @return boolean
*/
abstract function crop($properties);
/**
* Resize an image. Valid properties are: width, height, and master.
*
* @param array new properties
* @return boolean
*/
abstract public function resize($properties);
/**
* Rotate an image. Valid amounts are -180 to 180.
*
* @param integer amount to rotate
* @return boolean
*/
abstract public function rotate($amount);
/**
* Sharpen and image. Valid amounts are 1 to 100.
*
* @param integer amount to sharpen
* @return boolean
*/
abstract public function sharpen($amount);
} // End Image Driver

View File

@@ -0,0 +1,379 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* GD Image Driver.
*
* $Id: GD.php 3769 2008-12-15 00:48:56Z zombor $
*
* @package Image
* @author Kohana Team
* @copyright (c) 2007-2008 Kohana Team
* @license http://kohanaphp.com/license.html
*/
class Image_GD_Driver extends Image_Driver {
// A transparent PNG as a string
protected static $blank_png;
protected static $blank_png_width;
protected static $blank_png_height;
public function __construct()
{
// Make sure that GD2 is available
if ( ! function_exists('gd_info'))
throw new Kohana_Exception('image.gd.requires_v2');
// Get the GD information
$info = gd_info();
// Make sure that the GD2 is installed
if (strpos($info['GD Version'], '2.') === FALSE)
throw new Kohana_Exception('image.gd.requires_v2');
}
public function process($image, $actions, $dir, $file, $render = FALSE)
{
// Set the "create" function
switch ($image['type'])
{
case IMAGETYPE_JPEG:
$create = 'imagecreatefromjpeg';
break;
case IMAGETYPE_GIF:
$create = 'imagecreatefromgif';
break;
case IMAGETYPE_PNG:
$create = 'imagecreatefrompng';
break;
}
// Set the "save" function
switch (strtolower(substr(strrchr($file, '.'), 1)))
{
case 'jpg':
case 'jpeg':
$save = 'imagejpeg';
break;
case 'gif':
$save = 'imagegif';
break;
case 'png':
$save = 'imagepng';
break;
}
// Make sure the image type is supported for import
if (empty($create) OR ! function_exists($create))
throw new Kohana_Exception('image.type_not_allowed', $image['file']);
// Make sure the image type is supported for saving
if (empty($save) OR ! function_exists($save))
throw new Kohana_Exception('image.type_not_allowed', $dir.$file);
// Load the image
$this->image = $image;
// Create the GD image resource
$this->tmp_image = $create($image['file']);
// Get the quality setting from the actions
$quality = arr::remove('quality', $actions);
if ($status = $this->execute($actions))
{
// Prevent the alpha from being lost
imagealphablending($this->tmp_image, TRUE);
imagesavealpha($this->tmp_image, TRUE);
switch ($save)
{
case 'imagejpeg':
// Default the quality to 95
($quality === NULL) and $quality = 95;
break;
case 'imagegif':
// Remove the quality setting, GIF doesn't use it
unset($quality);
break;
case 'imagepng':
// Always use a compression level of 9 for PNGs. This does not
// affect quality, it only increases the level of compression!
$quality = 9;
break;
}
if ($render === FALSE)
{
// Set the status to the save return value, saving with the quality requested
$status = isset($quality) ? $save($this->tmp_image, $dir.$file, $quality) : $save($this->tmp_image, $dir.$file);
}
else
{
// Output the image directly to the browser
switch ($save)
{
case 'imagejpeg':
header('Content-Type: image/jpeg');
break;
case 'imagegif':
header('Content-Type: image/gif');
break;
case 'imagepng':
header('Content-Type: image/png');
break;
}
$status = isset($quality) ? $save($this->tmp_image, NULL, $quality) : $save($this->tmp_image);
}
// Destroy the temporary image
imagedestroy($this->tmp_image);
}
return $status;
}
public function flip($direction)
{
// Get the current width and height
$width = imagesx($this->tmp_image);
$height = imagesy($this->tmp_image);
// Create the flipped image
$flipped = $this->imagecreatetransparent($width, $height);
if ($direction === Image::HORIZONTAL)
{
for ($x = 0; $x < $width; $x++)
{
$status = imagecopy($flipped, $this->tmp_image, $x, 0, $width - $x - 1, 0, 1, $height);
}
}
elseif ($direction === Image::VERTICAL)
{
for ($y = 0; $y < $height; $y++)
{
$status = imagecopy($flipped, $this->tmp_image, 0, $y, 0, $height - $y - 1, $width, 1);
}
}
else
{
// Do nothing
return TRUE;
}
if ($status === TRUE)
{
// Swap the new image for the old one
imagedestroy($this->tmp_image);
$this->tmp_image = $flipped;
}
return $status;
}
public function crop($properties)
{
// Sanitize the cropping settings
$this->sanitize_geometry($properties);
// Get the current width and height
$width = imagesx($this->tmp_image);
$height = imagesy($this->tmp_image);
// Create the temporary image to copy to
$img = $this->imagecreatetransparent($properties['width'], $properties['height']);
// Execute the crop
if ($status = imagecopyresampled($img, $this->tmp_image, 0, 0, $properties['left'], $properties['top'], $width, $height, $width, $height))
{
// Swap the new image for the old one
imagedestroy($this->tmp_image);
$this->tmp_image = $img;
}
return $status;
}
public function resize($properties)
{
// Get the current width and height
$width = imagesx($this->tmp_image);
$height = imagesy($this->tmp_image);
if (substr($properties['width'], -1) === '%')
{
// Recalculate the percentage to a pixel size
$properties['width'] = round($width * (substr($properties['width'], 0, -1) / 100));
}
if (substr($properties['height'], -1) === '%')
{
// Recalculate the percentage to a pixel size
$properties['height'] = round($height * (substr($properties['height'], 0, -1) / 100));
}
// Recalculate the width and height, if they are missing
empty($properties['width']) and $properties['width'] = round($width * $properties['height'] / $height);
empty($properties['height']) and $properties['height'] = round($height * $properties['width'] / $width);
if ($properties['master'] === Image::AUTO)
{
// Change an automatic master dim to the correct type
$properties['master'] = (($width / $properties['width']) > ($height / $properties['height'])) ? Image::WIDTH : Image::HEIGHT;
}
if (empty($properties['height']) OR $properties['master'] === Image::WIDTH)
{
// Recalculate the height based on the width
$properties['height'] = round($height * $properties['width'] / $width);
}
if (empty($properties['width']) OR $properties['master'] === Image::HEIGHT)
{
// Recalculate the width based on the height
$properties['width'] = round($width * $properties['height'] / $height);
}
// Test if we can do a resize without resampling to speed up the final resize
if ($properties['width'] > $width / 2 AND $properties['height'] > $height / 2)
{
// Presize width and height
$pre_width = $width;
$pre_height = $height;
// The maximum reduction is 10% greater than the final size
$max_reduction_width = round($properties['width'] * 1.1);
$max_reduction_height = round($properties['height'] * 1.1);
// Reduce the size using an O(2n) algorithm, until it reaches the maximum reduction
while ($pre_width / 2 > $max_reduction_width AND $pre_height / 2 > $max_reduction_height)
{
$pre_width /= 2;
$pre_height /= 2;
}
// Create the temporary image to copy to
$img = $this->imagecreatetransparent($pre_width, $pre_height);
if ($status = imagecopyresized($img, $this->tmp_image, 0, 0, 0, 0, $pre_width, $pre_height, $width, $height))
{
// Swap the new image for the old one
imagedestroy($this->tmp_image);
$this->tmp_image = $img;
}
// Set the width and height to the presize
$width = $pre_width;
$height = $pre_height;
}
// Create the temporary image to copy to
$img = $this->imagecreatetransparent($properties['width'], $properties['height']);
// Execute the resize
if ($status = imagecopyresampled($img, $this->tmp_image, 0, 0, 0, 0, $properties['width'], $properties['height'], $width, $height))
{
// Swap the new image for the old one
imagedestroy($this->tmp_image);
$this->tmp_image = $img;
}
return $status;
}
public function rotate($amount)
{
// Use current image to rotate
$img = $this->tmp_image;
// White, with an alpha of 0
$transparent = imagecolorallocatealpha($img, 255, 255, 255, 127);
// Rotate, setting the transparent color
$img = imagerotate($img, 360 - $amount, $transparent, -1);
// Fill the background with the transparent "color"
imagecolortransparent($img, $transparent);
// Merge the images
if ($status = imagecopymerge($this->tmp_image, $img, 0, 0, 0, 0, imagesx($this->tmp_image), imagesy($this->tmp_image), 100))
{
// Prevent the alpha from being lost
imagealphablending($img, TRUE);
imagesavealpha($img, TRUE);
// Swap the new image for the old one
imagedestroy($this->tmp_image);
$this->tmp_image = $img;
}
return $status;
}
public function sharpen($amount)
{
// Make sure that the sharpening function is available
if ( ! function_exists('imageconvolution'))
throw new Kohana_Exception('image.unsupported_method', __FUNCTION__);
// Amount should be in the range of 18-10
$amount = round(abs(-18 + ($amount * 0.08)), 2);
// Gaussian blur matrix
$matrix = array
(
array(-1, -1, -1),
array(-1, $amount, -1),
array(-1, -1, -1),
);
// Perform the sharpen
return imageconvolution($this->tmp_image, $matrix, $amount - 8, 0);
}
protected function properties()
{
return array(imagesx($this->tmp_image), imagesy($this->tmp_image));
}
/**
* Returns an image with a transparent background. Used for rotating to
* prevent unfilled backgrounds.
*
* @param integer image width
* @param integer image height
* @return resource
*/
protected function imagecreatetransparent($width, $height)
{
if (self::$blank_png === NULL)
{
// Decode the blank PNG if it has not been done already
self::$blank_png = imagecreatefromstring(base64_decode
(
'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29'.
'mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAADqSURBVHjaYvz//z/DYAYAAcTEMMgBQAANegcCBN'.
'CgdyBAAA16BwIE0KB3IEAADXoHAgTQoHcgQAANegcCBNCgdyBAAA16BwIE0KB3IEAADXoHAgTQoHcgQ'.
'AANegcCBNCgdyBAAA16BwIE0KB3IEAADXoHAgTQoHcgQAANegcCBNCgdyBAAA16BwIE0KB3IEAADXoH'.
'AgTQoHcgQAANegcCBNCgdyBAAA16BwIE0KB3IEAADXoHAgTQoHcgQAANegcCBNCgdyBAAA16BwIE0KB'.
'3IEAADXoHAgTQoHcgQAANegcCBNCgdyBAgAEAMpcDTTQWJVEAAAAASUVORK5CYII='
));
// Set the blank PNG width and height
self::$blank_png_width = imagesx(self::$blank_png);
self::$blank_png_height = imagesy(self::$blank_png);
}
$img = imagecreatetruecolor($width, $height);
// Resize the blank image
imagecopyresized($img, self::$blank_png, 0, 0, 0, 0, $width, $height, self::$blank_png_width, self::$blank_png_height);
// Prevent the alpha from being lost
imagealphablending($img, FALSE);
imagesavealpha($img, TRUE);
return $img;
}
} // End Image GD Driver

View File

@@ -0,0 +1,211 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* GraphicsMagick Image Driver.
*
* @package Image
* @author Kohana Team
* @copyright (c) 2007-2008 Kohana Team
* @license http://kohanaphp.com/license.html
*/
class Image_GraphicsMagick_Driver extends Image_Driver {
// Directory that GM is installed in
protected $dir = '';
// Command extension (exe for windows)
protected $ext = '';
// Temporary image filename
protected $tmp_image;
/**
* Attempts to detect the GraphicsMagick installation directory.
*
* @throws Kohana_Exception
* @param array configuration
* @return void
*/
public function __construct($config)
{
if (empty($config['directory']))
{
// Attempt to locate GM by using "which" (only works for *nix!)
if ( ! is_file($path = exec('which gm')))
throw new Kohana_Exception('image.graphicsmagick.not_found');
$config['directory'] = dirname($path);
}
// Set the command extension
$this->ext = (PHP_SHLIB_SUFFIX === 'dll') ? '.exe' : '';
// Check to make sure the provided path is correct
if ( ! is_file(realpath($config['directory']).'/gm'.$this->ext))
throw new Kohana_Exception('image.graphicsmagick.not_found', 'gm'.$this->ext);
// Set the installation directory
$this->dir = str_replace('\\', '/', realpath($config['directory'])).'/';
}
/**
* Creates a temporary image and executes the given actions. By creating a
* temporary copy of the image before manipulating it, this process is atomic.
*/
public function process($image, $actions, $dir, $file, $render = FALSE)
{
// We only need the filename
$image = $image['file'];
// Unique temporary filename
$this->tmp_image = $dir.'k2img--'.sha1(time().$dir.$file).substr($file, strrpos($file, '.'));
// Copy the image to the temporary file
copy($image, $this->tmp_image);
// Quality change is done last
$quality = (int) arr::remove('quality', $actions);
// Use 95 for the default quality
empty($quality) and $quality = 95;
// All calls to these will need to be escaped, so do it now
$this->cmd_image = escapeshellarg($this->tmp_image);
$this->new_image = ($render)? $this->cmd_image : escapeshellarg($dir.$file);
if ($status = $this->execute($actions))
{
// Use convert to change the image into its final version. This is
// done to allow the file type to change correctly, and to handle
// the quality conversion in the most effective way possible.
if ($error = exec(escapeshellcmd($this->dir.'gm'.$this->ext.' convert').' -quality '.$quality.'% '.$this->cmd_image.' '.$this->new_image))
{
$this->errors[] = $error;
}
else
{
// Output the image directly to the browser
if ($render !== FALSE)
{
$contents = file_get_contents($this->tmp_image);
switch (substr($file, strrpos($file, '.') + 1))
{
case 'jpg':
case 'jpeg':
header('Content-Type: image/jpeg');
break;
case 'gif':
header('Content-Type: image/gif');
break;
case 'png':
header('Content-Type: image/png');
break;
}
echo $contents;
}
}
}
// Remove the temporary image
unlink($this->tmp_image);
$this->tmp_image = '';
return $status;
}
public function crop($prop)
{
// Sanitize and normalize the properties into geometry
$this->sanitize_geometry($prop);
// Set the IM geometry based on the properties
$geometry = escapeshellarg($prop['width'].'x'.$prop['height'].'+'.$prop['left'].'+'.$prop['top']);
if ($error = exec(escapeshellcmd($this->dir.'gm'.$this->ext.' convert').' -crop '.$geometry.' '.$this->cmd_image.' '.$this->cmd_image))
{
$this->errors[] = $error;
return FALSE;
}
return TRUE;
}
public function flip($dir)
{
// Convert the direction into a GM command
$dir = ($dir === Image::HORIZONTAL) ? '-flop' : '-flip';
if ($error = exec(escapeshellcmd($this->dir.'gm'.$this->ext.' convert').' '.$dir.' '.$this->cmd_image.' '.$this->cmd_image))
{
$this->errors[] = $error;
return FALSE;
}
return TRUE;
}
public function resize($prop)
{
switch ($prop['master'])
{
case Image::WIDTH: // Wx
$dim = escapeshellarg($prop['width'].'x');
break;
case Image::HEIGHT: // xH
$dim = escapeshellarg('x'.$prop['height']);
break;
case Image::AUTO: // WxH
$dim = escapeshellarg($prop['width'].'x'.$prop['height']);
break;
case Image::NONE: // WxH!
$dim = escapeshellarg($prop['width'].'x'.$prop['height'].'!');
break;
}
// Use "convert" to change the width and height
if ($error = exec(escapeshellcmd($this->dir.'gm'.$this->ext.' convert').' -resize '.$dim.' '.$this->cmd_image.' '.$this->cmd_image))
{
$this->errors[] = $error;
return FALSE;
}
return TRUE;
}
public function rotate($amt)
{
if ($error = exec(escapeshellcmd($this->dir.'gm'.$this->ext.' convert').' -rotate '.escapeshellarg($amt).' -background transparent '.$this->cmd_image.' '.$this->cmd_image))
{
$this->errors[] = $error;
return FALSE;
}
return TRUE;
}
public function sharpen($amount)
{
// Set the sigma, radius, and amount. The amount formula allows a nice
// spread between 1 and 100 without pixelizing the image badly.
$sigma = 0.5;
$radius = $sigma * 2;
$amount = round(($amount / 80) * 3.14, 2);
// Convert the amount to an GM command
$sharpen = escapeshellarg($radius.'x'.$sigma.'+'.$amount.'+0');
if ($error = exec(escapeshellcmd($this->dir.'gm'.$this->ext.' convert').' -unsharp '.$sharpen.' '.$this->cmd_image.' '.$this->cmd_image))
{
$this->errors[] = $error;
return FALSE;
}
return TRUE;
}
protected function properties()
{
return array_slice(getimagesize($this->tmp_image), 0, 2, FALSE);
}
} // End Image GraphicsMagick Driver

View File

@@ -0,0 +1,212 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* ImageMagick Image Driver.
*
* $Id: ImageMagick.php 3769 2008-12-15 00:48:56Z zombor $
*
* @package Image
* @author Kohana Team
* @copyright (c) 2007-2008 Kohana Team
* @license http://kohanaphp.com/license.html
*/
class Image_ImageMagick_Driver extends Image_Driver {
// Directory that IM is installed in
protected $dir = '';
// Command extension (exe for windows)
protected $ext = '';
// Temporary image filename
protected $tmp_image;
/**
* Attempts to detect the ImageMagick installation directory.
*
* @throws Kohana_Exception
* @param array configuration
* @return void
*/
public function __construct($config)
{
if (empty($config['directory']))
{
// Attempt to locate IM by using "which" (only works for *nix!)
if ( ! is_file($path = exec('which convert')))
throw new Kohana_Exception('image.imagemagick.not_found');
$config['directory'] = dirname($path);
}
// Set the command extension
$this->ext = (PHP_SHLIB_SUFFIX === 'dll') ? '.exe' : '';
// Check to make sure the provided path is correct
if ( ! is_file(realpath($config['directory']).'/convert'.$this->ext))
throw new Kohana_Exception('image.imagemagick.not_found', 'convert'.$this->ext);
// Set the installation directory
$this->dir = str_replace('\\', '/', realpath($config['directory'])).'/';
}
/**
* Creates a temporary image and executes the given actions. By creating a
* temporary copy of the image before manipulating it, this process is atomic.
*/
public function process($image, $actions, $dir, $file, $render = FALSE)
{
// We only need the filename
$image = $image['file'];
// Unique temporary filename
$this->tmp_image = $dir.'k2img--'.sha1(time().$dir.$file).substr($file, strrpos($file, '.'));
// Copy the image to the temporary file
copy($image, $this->tmp_image);
// Quality change is done last
$quality = (int) arr::remove('quality', $actions);
// Use 95 for the default quality
empty($quality) and $quality = 95;
// All calls to these will need to be escaped, so do it now
$this->cmd_image = escapeshellarg($this->tmp_image);
$this->new_image = ($render)? $this->cmd_image : escapeshellarg($dir.$file);
if ($status = $this->execute($actions))
{
// Use convert to change the image into its final version. This is
// done to allow the file type to change correctly, and to handle
// the quality conversion in the most effective way possible.
if ($error = exec(escapeshellcmd($this->dir.'convert'.$this->ext).' -quality '.$quality.'% '.$this->cmd_image.' '.$this->new_image))
{
$this->errors[] = $error;
}
else
{
// Output the image directly to the browser
if ($render !== FALSE)
{
$contents = file_get_contents($this->tmp_image);
switch (substr($file, strrpos($file, '.') + 1))
{
case 'jpg':
case 'jpeg':
header('Content-Type: image/jpeg');
break;
case 'gif':
header('Content-Type: image/gif');
break;
case 'png':
header('Content-Type: image/png');
break;
}
echo $contents;
}
}
}
// Remove the temporary image
unlink($this->tmp_image);
$this->tmp_image = '';
return $status;
}
public function crop($prop)
{
// Sanitize and normalize the properties into geometry
$this->sanitize_geometry($prop);
// Set the IM geometry based on the properties
$geometry = escapeshellarg($prop['width'].'x'.$prop['height'].'+'.$prop['left'].'+'.$prop['top']);
if ($error = exec(escapeshellcmd($this->dir.'convert'.$this->ext).' -crop '.$geometry.' '.$this->cmd_image.' '.$this->cmd_image))
{
$this->errors[] = $error;
return FALSE;
}
return TRUE;
}
public function flip($dir)
{
// Convert the direction into a IM command
$dir = ($dir === Image::HORIZONTAL) ? '-flop' : '-flip';
if ($error = exec(escapeshellcmd($this->dir.'convert'.$this->ext).' '.$dir.' '.$this->cmd_image.' '.$this->cmd_image))
{
$this->errors[] = $error;
return FALSE;
}
return TRUE;
}
public function resize($prop)
{
switch ($prop['master'])
{
case Image::WIDTH: // Wx
$dim = escapeshellarg($prop['width'].'x');
break;
case Image::HEIGHT: // xH
$dim = escapeshellarg('x'.$prop['height']);
break;
case Image::AUTO: // WxH
$dim = escapeshellarg($prop['width'].'x'.$prop['height']);
break;
case Image::NONE: // WxH!
$dim = escapeshellarg($prop['width'].'x'.$prop['height'].'!');
break;
}
// Use "convert" to change the width and height
if ($error = exec(escapeshellcmd($this->dir.'convert'.$this->ext).' -resize '.$dim.' '.$this->cmd_image.' '.$this->cmd_image))
{
$this->errors[] = $error;
return FALSE;
}
return TRUE;
}
public function rotate($amt)
{
if ($error = exec(escapeshellcmd($this->dir.'convert'.$this->ext).' -rotate '.escapeshellarg($amt).' -background transparent '.$this->cmd_image.' '.$this->cmd_image))
{
$this->errors[] = $error;
return FALSE;
}
return TRUE;
}
public function sharpen($amount)
{
// Set the sigma, radius, and amount. The amount formula allows a nice
// spread between 1 and 100 without pixelizing the image badly.
$sigma = 0.5;
$radius = $sigma * 2;
$amount = round(($amount / 80) * 3.14, 2);
// Convert the amount to an IM command
$sharpen = escapeshellarg($radius.'x'.$sigma.'+'.$amount.'+0');
if ($error = exec(escapeshellcmd($this->dir.'convert'.$this->ext).' -unsharp '.$sharpen.' '.$this->cmd_image.' '.$this->cmd_image))
{
$this->errors[] = $error;
return FALSE;
}
return TRUE;
}
protected function properties()
{
return array_slice(getimagesize($this->tmp_image), 0, 2, FALSE);
}
} // End Image ImageMagick Driver

View File

@@ -0,0 +1,70 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* Session driver interface
*
* $Id: Session.php 3769 2008-12-15 00:48:56Z zombor $
*
* @package Core
* @author Kohana Team
* @copyright (c) 2007-2008 Kohana Team
* @license http://kohanaphp.com/license.html
*/
interface Session_Driver {
/**
* Opens a session.
*
* @param string save path
* @param string session name
* @return boolean
*/
public function open($path, $name);
/**
* Closes a session.
*
* @return boolean
*/
public function close();
/**
* Reads a session.
*
* @param string session id
* @return string
*/
public function read($id);
/**
* Writes a session.
*
* @param string session id
* @param string session data
* @return boolean
*/
public function write($id, $data);
/**
* Destroys a session.
*
* @param string session id
* @return boolean
*/
public function destroy($id);
/**
* Regenerates the session id.
*
* @return string
*/
public function regenerate();
/**
* Garbage collection.
*
* @param integer session expiration period
* @return boolean
*/
public function gc($maxlifetime);
} // End Session Driver Interface

View File

@@ -0,0 +1,105 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* Session cache driver.
*
* Cache library config goes in the session.storage config entry:
* $config['storage'] = array(
* 'driver' => 'apc',
* 'requests' => 10000
* );
* Lifetime does not need to be set as it is
* overridden by the session expiration setting.
*
* $Id: Cache.php 3769 2008-12-15 00:48:56Z zombor $
*
* @package Core
* @author Kohana Team
* @copyright (c) 2007-2008 Kohana Team
* @license http://kohanaphp.com/license.html
*/
class Session_Cache_Driver implements Session_Driver {
protected $cache;
protected $encrypt;
public function __construct()
{
// Load Encrypt library
if (Kohana::config('session.encryption'))
{
$this->encrypt = new Encrypt;
}
Kohana::log('debug', 'Session Cache Driver Initialized');
}
public function open($path, $name)
{
$config = Kohana::config('session.storage');
if (empty($config))
{
// Load the default group
$config = Kohana::config('cache.default');
}
elseif (is_string($config))
{
$name = $config;
// Test the config group name
if (($config = Kohana::config('cache.'.$config)) === NULL)
throw new Kohana_Exception('cache.undefined_group', $name);
}
$config['lifetime'] = (Kohana::config('session.expiration') == 0) ? 86400 : Kohana::config('session.expiration');
$this->cache = new Cache($config);
return is_object($this->cache);
}
public function close()
{
return TRUE;
}
public function read($id)
{
$id = 'session_'.$id;
if ($data = $this->cache->get($id))
{
return Kohana::config('session.encryption') ? $this->encrypt->decode($data) : $data;
}
// Return value must be string, NOT a boolean
return '';
}
public function write($id, $data)
{
$id = 'session_'.$id;
$data = Kohana::config('session.encryption') ? $this->encrypt->encode($data) : $data;
return $this->cache->set($id, $data);
}
public function destroy($id)
{
$id = 'session_'.$id;
return $this->cache->delete($id);
}
public function regenerate()
{
session_regenerate_id(TRUE);
// Return new session id
return session_id();
}
public function gc($maxlifetime)
{
// Just return, caches are automatically cleaned up
return TRUE;
}
} // End Session Cache Driver

View File

@@ -0,0 +1,80 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* Session cookie driver.
*
* $Id: Cookie.php 3769 2008-12-15 00:48:56Z zombor $
*
* @package Core
* @author Kohana Team
* @copyright (c) 2007-2008 Kohana Team
* @license http://kohanaphp.com/license.html
*/
class Session_Cookie_Driver implements Session_Driver {
protected $cookie_name;
protected $encrypt; // Library
public function __construct()
{
$this->cookie_name = Kohana::config('session.name').'_data';
if (Kohana::config('session.encryption'))
{
$this->encrypt = Encrypt::instance();
}
Kohana::log('debug', 'Session Cookie Driver Initialized');
}
public function open($path, $name)
{
return TRUE;
}
public function close()
{
return TRUE;
}
public function read($id)
{
$data = (string) cookie::get($this->cookie_name);
if ($data == '')
return $data;
return empty($this->encrypt) ? base64_decode($data) : $this->encrypt->decode($data);
}
public function write($id, $data)
{
$data = empty($this->encrypt) ? base64_encode($data) : $this->encrypt->encode($data);
if (strlen($data) > 4048)
{
Kohana::log('error', 'Session ('.$id.') data exceeds the 4KB limit, ignoring write.');
return FALSE;
}
return cookie::set($this->cookie_name, $data, Kohana::config('session.expiration'));
}
public function destroy($id)
{
return cookie::delete($this->cookie_name);
}
public function regenerate()
{
session_regenerate_id(TRUE);
// Return new id
return session_id();
}
public function gc($maxlifetime)
{
return TRUE;
}
} // End Session Cookie Driver Class

View File

@@ -0,0 +1,163 @@
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* Session database driver.
*
* $Id: Database.php 3769 2008-12-15 00:48:56Z zombor $
*
* @package Core
* @author Kohana Team
* @copyright (c) 2007-2008 Kohana Team
* @license http://kohanaphp.com/license.html
*/
class Session_Database_Driver implements Session_Driver {
/*
CREATE TABLE sessions
(
session_id VARCHAR(127) NOT NULL,
last_activity INT(10) UNSIGNED NOT NULL,
data TEXT NOT NULL,
PRIMARY KEY (session_id)
);
*/
// Database settings
protected $db = 'default';
protected $table = 'sessions';
// Encryption
protected $encrypt;
// Session settings
protected $session_id;
protected $written = FALSE;
public function __construct()
{
// Load configuration
$config = Kohana::config('session');
if ( ! empty($config['encryption']))
{
// Load encryption
$this->encrypt = Encrypt::instance();
}
if (is_array($config['storage']))
{
if ( ! empty($config['storage']['group']))
{
// Set the group name
$this->db = $config['storage']['group'];
}
if ( ! empty($config['storage']['table']))
{
// Set the table name
$this->table = $config['storage']['table'];
}
}
// Load database
$this->db = Database::instance($this->db);
Kohana::log('debug', 'Session Database Driver Initialized');
}
public function open($path, $name)
{
return TRUE;
}
public function close()
{
return TRUE;
}
public function read($id)
{
// Load the session
$query = $this->db->from($this->table)->where('session_id', $id)->limit(1)->get()->result(TRUE);
if ($query->count() === 0)
{
// No current session
$this->session_id = NULL;
return '';
}
// Set the current session id
$this->session_id = $id;
// Load the data
$data = $query->current()->data;
return ($this->encrypt === NULL) ? base64_decode($data) : $this->encrypt->decode($data);
}
public function write($id, $data)
{
$data = array
(
'session_id' => $id,
'last_activity' => time(),
'data' => ($this->encrypt === NULL) ? base64_encode($data) : $this->encrypt->encode($data)
);
if ($this->session_id === NULL)
{
// Insert a new session
$query = $this->db->insert($this->table, $data);
}
elseif ($id === $this->session_id)
{
// Do not update the session_id
unset($data['session_id']);
// Update the existing session
$query = $this->db->update($this->table, $data, array('session_id' => $id));
}
else
{
// Update the session and id
$query = $this->db->update($this->table, $data, array('session_id' => $this->session_id));
// Set the new session id
$this->session_id = $id;
}
return (bool) $query->count();
}
public function destroy($id)
{
// Delete the requested session
$this->db->delete($this->table, array('session_id' => $id));
// Session id is no longer valid
$this->session_id = NULL;
return TRUE;
}
public function regenerate()
{
// Generate a new session id
session_regenerate_id();
// Return new session id
return session_id();
}
public function gc($maxlifetime)
{
// Delete all expired sessions
$query = $this->db->delete($this->table, array('last_activity <' => time() - $maxlifetime));
Kohana::log('debug', 'Session garbage collected: '.$query->count().' row(s) deleted.');
return TRUE;
}
} // End Session Database Driver