Imported Upstream version 0.6.24+dfsg1
This commit is contained in:
208
lib/kohana/system/libraries/Cache.php
Normal file
208
lib/kohana/system/libraries/Cache.php
Normal 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
|
||||
362
lib/kohana/system/libraries/Calendar.php
Normal file
362
lib/kohana/system/libraries/Calendar.php
Normal 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
|
||||
307
lib/kohana/system/libraries/Calendar_Event.php
Normal file
307
lib/kohana/system/libraries/Calendar_Event.php
Normal 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
|
||||
279
lib/kohana/system/libraries/Captcha.php
Normal file
279
lib/kohana/system/libraries/Captcha.php
Normal 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
|
||||
86
lib/kohana/system/libraries/Controller.php
Normal file
86
lib/kohana/system/libraries/Controller.php
Normal 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
|
||||
1444
lib/kohana/system/libraries/Database.php
Normal file
1444
lib/kohana/system/libraries/Database.php
Normal file
File diff suppressed because it is too large
Load Diff
26
lib/kohana/system/libraries/Database_Expression.php
Normal file
26
lib/kohana/system/libraries/Database_Expression.php
Normal 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
|
||||
164
lib/kohana/system/libraries/Encrypt.php
Normal file
164
lib/kohana/system/libraries/Encrypt.php
Normal 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
|
||||
70
lib/kohana/system/libraries/Event_Observer.php
Normal file
70
lib/kohana/system/libraries/Event_Observer.php
Normal 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
|
||||
67
lib/kohana/system/libraries/Event_Subject.php
Normal file
67
lib/kohana/system/libraries/Event_Subject.php
Normal 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
|
||||
431
lib/kohana/system/libraries/Image.php
Normal file
431
lib/kohana/system/libraries/Image.php
Normal 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
|
||||
452
lib/kohana/system/libraries/Input.php
Normal file
452
lib/kohana/system/libraries/Input.php
Normal 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('&','<','>'), array('&amp;','&lt;','&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
|
||||
31
lib/kohana/system/libraries/Model.php
Normal file
31
lib/kohana/system/libraries/Model.php
Normal 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
|
||||
1431
lib/kohana/system/libraries/ORM.php
Normal file
1431
lib/kohana/system/libraries/ORM.php
Normal file
File diff suppressed because it is too large
Load Diff
228
lib/kohana/system/libraries/ORM_Iterator.php
Normal file
228
lib/kohana/system/libraries/ORM_Iterator.php
Normal 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
|
||||
76
lib/kohana/system/libraries/ORM_Tree.php
Normal file
76
lib/kohana/system/libraries/ORM_Tree.php
Normal 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
|
||||
143
lib/kohana/system/libraries/ORM_Versioned.php
Normal file
143
lib/kohana/system/libraries/ORM_Versioned.php
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
236
lib/kohana/system/libraries/Pagination.php
Normal file
236
lib/kohana/system/libraries/Pagination.php
Normal 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
|
||||
271
lib/kohana/system/libraries/Profiler.php
Normal file
271
lib/kohana/system/libraries/Profiler.php
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
69
lib/kohana/system/libraries/Profiler_Table.php
Normal file
69
lib/kohana/system/libraries/Profiler_Table.php
Normal 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();
|
||||
}
|
||||
}
|
||||
304
lib/kohana/system/libraries/Router.php
Normal file
304
lib/kohana/system/libraries/Router.php
Normal 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
|
||||
457
lib/kohana/system/libraries/Session.php
Normal file
457
lib/kohana/system/libraries/Session.php
Normal 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
|
||||
130
lib/kohana/system/libraries/Tagcloud.php
Normal file
130
lib/kohana/system/libraries/Tagcloud.php
Normal 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(' ', ' ', $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
|
||||
279
lib/kohana/system/libraries/URI.php
Normal file
279
lib/kohana/system/libraries/URI.php
Normal 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
|
||||
826
lib/kohana/system/libraries/Validation.php
Normal file
826
lib/kohana/system/libraries/Validation.php
Normal 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
|
||||
309
lib/kohana/system/libraries/View.php
Normal file
309
lib/kohana/system/libraries/View.php
Normal 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
|
||||
40
lib/kohana/system/libraries/drivers/Cache.php
Normal file
40
lib/kohana/system/libraries/drivers/Cache.php
Normal 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
|
||||
64
lib/kohana/system/libraries/drivers/Cache/Apc.php
Normal file
64
lib/kohana/system/libraries/drivers/Cache/Apc.php
Normal 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
|
||||
66
lib/kohana/system/libraries/drivers/Cache/Eaccelerator.php
Normal file
66
lib/kohana/system/libraries/drivers/Cache/Eaccelerator.php
Normal 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
|
||||
261
lib/kohana/system/libraries/drivers/Cache/File.php
Normal file
261
lib/kohana/system/libraries/drivers/Cache/File.php
Normal 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
|
||||
191
lib/kohana/system/libraries/drivers/Cache/Memcache.php
Normal file
191
lib/kohana/system/libraries/drivers/Cache/Memcache.php
Normal 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
|
||||
257
lib/kohana/system/libraries/drivers/Cache/Sqlite.php
Normal file
257
lib/kohana/system/libraries/drivers/Cache/Sqlite.php
Normal 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
|
||||
119
lib/kohana/system/libraries/drivers/Cache/Xcache.php
Normal file
119
lib/kohana/system/libraries/drivers/Cache/Xcache.php
Normal 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
|
||||
227
lib/kohana/system/libraries/drivers/Captcha.php
Normal file
227
lib/kohana/system/libraries/drivers/Captcha.php
Normal 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
|
||||
92
lib/kohana/system/libraries/drivers/Captcha/Alpha.php
Normal file
92
lib/kohana/system/libraries/drivers/Captcha/Alpha.php
Normal 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
|
||||
81
lib/kohana/system/libraries/drivers/Captcha/Basic.php
Normal file
81
lib/kohana/system/libraries/drivers/Captcha/Basic.php
Normal 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
|
||||
72
lib/kohana/system/libraries/drivers/Captcha/Black.php
Normal file
72
lib/kohana/system/libraries/drivers/Captcha/Black.php
Normal 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
|
||||
61
lib/kohana/system/libraries/drivers/Captcha/Math.php
Normal file
61
lib/kohana/system/libraries/drivers/Captcha/Math.php
Normal 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
|
||||
47
lib/kohana/system/libraries/drivers/Captcha/Riddle.php
Normal file
47
lib/kohana/system/libraries/drivers/Captcha/Riddle.php
Normal 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
|
||||
37
lib/kohana/system/libraries/drivers/Captcha/Word.php
Normal file
37
lib/kohana/system/libraries/drivers/Captcha/Word.php
Normal 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
|
||||
636
lib/kohana/system/libraries/drivers/Database.php
Normal file
636
lib/kohana/system/libraries/drivers/Database.php
Normal 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
|
||||
462
lib/kohana/system/libraries/drivers/Database/Mssql.php
Normal file
462
lib/kohana/system/libraries/drivers/Database/Mssql.php
Normal 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
|
||||
496
lib/kohana/system/libraries/drivers/Database/Mysql.php
Normal file
496
lib/kohana/system/libraries/drivers/Database/Mysql.php
Normal 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
|
||||
358
lib/kohana/system/libraries/drivers/Database/Mysqli.php
Normal file
358
lib/kohana/system/libraries/drivers/Database/Mysqli.php
Normal 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;
|
||||
}
|
||||
}
|
||||
486
lib/kohana/system/libraries/drivers/Database/Pdosqlite.php
Normal file
486
lib/kohana/system/libraries/drivers/Database/Pdosqlite.php
Normal 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
|
||||
538
lib/kohana/system/libraries/drivers/Database/Pgsql.php
Normal file
538
lib/kohana/system/libraries/drivers/Database/Pgsql.php
Normal 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;
|
||||
}
|
||||
}
|
||||
149
lib/kohana/system/libraries/drivers/Image.php
Normal file
149
lib/kohana/system/libraries/drivers/Image.php
Normal 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
|
||||
379
lib/kohana/system/libraries/drivers/Image/GD.php
Normal file
379
lib/kohana/system/libraries/drivers/Image/GD.php
Normal 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
|
||||
211
lib/kohana/system/libraries/drivers/Image/GraphicsMagick.php
Normal file
211
lib/kohana/system/libraries/drivers/Image/GraphicsMagick.php
Normal 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
|
||||
212
lib/kohana/system/libraries/drivers/Image/ImageMagick.php
Normal file
212
lib/kohana/system/libraries/drivers/Image/ImageMagick.php
Normal 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
|
||||
70
lib/kohana/system/libraries/drivers/Session.php
Normal file
70
lib/kohana/system/libraries/drivers/Session.php
Normal 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
|
||||
105
lib/kohana/system/libraries/drivers/Session/Cache.php
Normal file
105
lib/kohana/system/libraries/drivers/Session/Cache.php
Normal 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
|
||||
80
lib/kohana/system/libraries/drivers/Session/Cookie.php
Normal file
80
lib/kohana/system/libraries/drivers/Session/Cookie.php
Normal 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
|
||||
163
lib/kohana/system/libraries/drivers/Session/Database.php
Normal file
163
lib/kohana/system/libraries/drivers/Session/Database.php
Normal 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
|
||||
Reference in New Issue
Block a user