first commit

This commit is contained in:
alazhar
2020-01-02 22:20:31 +07:00
commit 10eb3340ad
5753 changed files with 631345 additions and 0 deletions

View File

@ -0,0 +1,570 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Access
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
jimport('joomla.utilities.arrayhelper');
/**
* Class that handles all access authorisation routines.
*
* @package Joomla.Platform
* @subpackage Access
* @since 11.1
*/
class JAccess
{
/**
* Array of view levels
*
* @var array
* @since 11.1
*/
protected static $viewLevels = array();
/**
* Array of rules for the asset
*
* @var array
* @since 11.1
*/
protected static $assetRules = array();
/**
* Array of user groups.
*
* @var array
* @since 11.1
*/
protected static $userGroups = array();
/**
* Array of user group paths.
*
* @var array
* @since 11.1
*/
protected static $userGroupPaths = array();
/**
* Array of cached groups by user.
*
* @var array
* @since 11.1
*/
protected static $groupsByUser = array();
/**
* Method for clearing static caches.
*
* @return void
*
* @since 11.3
*/
public static function clearStatics()
{
self::$viewLevels = array();
self::$assetRules = array();
self::$userGroups = array();
self::$userGroupPaths = array();
self::$groupsByUser = array();
}
/**
* Method to check if a user is authorised to perform an action, optionally on an asset.
*
* @param integer $userId Id of the user for which to check authorisation.
* @param string $action The name of the action to authorise.
* @param mixed $asset Integer asset id or the name of the asset as a string. Defaults to the global asset node.
*
* @return boolean True if authorised.
*
* @since 11.1
*/
public static function check($userId, $action, $asset = null)
{
// Sanitise inputs.
$userId = (int) $userId;
$action = strtolower(preg_replace('#[\s\-]+#', '.', trim($action)));
$asset = strtolower(preg_replace('#[\s\-]+#', '.', trim($asset)));
// Default to the root asset node.
if (empty($asset))
{
$db = JFactory::getDbo();
$assets = JTable::getInstance('Asset', 'JTable', array('dbo' => $db));
$rootId = $assets->getRootId();
$asset = $rootId;
}
// Get the rules for the asset recursively to root if not already retrieved.
if (empty(self::$assetRules[$asset]))
{
self::$assetRules[$asset] = self::getAssetRules($asset, true);
}
// Get all groups against which the user is mapped.
$identities = self::getGroupsByUser($userId);
array_unshift($identities, $userId * -1);
return self::$assetRules[$asset]->allow($action, $identities);
}
/**
* Method to check if a group is authorised to perform an action, optionally on an asset.
*
* @param integer $groupId The path to the group for which to check authorisation.
* @param string $action The name of the action to authorise.
* @param mixed $asset Integer asset id or the name of the asset as a string. Defaults to the global asset node.
*
* @return boolean True if authorised.
*
* @since 11.1
*/
public static function checkGroup($groupId, $action, $asset = null)
{
// Sanitize inputs.
$groupId = (int) $groupId;
$action = strtolower(preg_replace('#[\s\-]+#', '.', trim($action)));
$asset = strtolower(preg_replace('#[\s\-]+#', '.', trim($asset)));
// Get group path for group
$groupPath = self::getGroupPath($groupId);
// Default to the root asset node.
if (empty($asset))
{
// TODO: $rootId doesn't seem to be used!
$db = JFactory::getDbo();
$assets = JTable::getInstance('Asset', 'JTable', array('dbo' => $db));
$rootId = $assets->getRootId();
}
// Get the rules for the asset recursively to root if not already retrieved.
if (empty(self::$assetRules[$asset]))
{
self::$assetRules[$asset] = self::getAssetRules($asset, true);
}
return self::$assetRules[$asset]->allow($action, $groupPath);
}
/**
* Gets the parent groups that a leaf group belongs to in its branch back to the root of the tree
* (including the leaf group id).
*
* @param mixed $groupId An integer or array of integers representing the identities to check.
*
* @return mixed True if allowed, false for an explicit deny, null for an implicit deny.
*
* @since 11.1
*/
protected static function getGroupPath($groupId)
{
// Preload all groups
if (empty(self::$userGroups))
{
$db = JFactory::getDbo();
$query = $db->getQuery(true)
->select('parent.id, parent.lft, parent.rgt')
->from('#__usergroups AS parent')
->order('parent.lft');
$db->setQuery($query);
self::$userGroups = $db->loadObjectList('id');
}
// Make sure groupId is valid
if (!array_key_exists($groupId, self::$userGroups))
{
return array();
}
// Get parent groups and leaf group
if (!isset(self::$userGroupPaths[$groupId]))
{
self::$userGroupPaths[$groupId] = array();
foreach (self::$userGroups as $group)
{
if ($group->lft <= self::$userGroups[$groupId]->lft && $group->rgt >= self::$userGroups[$groupId]->rgt)
{
self::$userGroupPaths[$groupId][] = $group->id;
}
}
}
return self::$userGroupPaths[$groupId];
}
/**
* Method to return the JAccessRules object for an asset. The returned object can optionally hold
* only the rules explicitly set for the asset or the summation of all inherited rules from
* parent assets and explicit rules.
*
* @param mixed $asset Integer asset id or the name of the asset as a string.
* @param boolean $recursive True to return the rules object with inherited rules.
*
* @return JAccessRules JAccessRules object for the asset.
*
* @since 11.1
*/
public static function getAssetRules($asset, $recursive = false)
{
// Get the database connection object.
$db = JFactory::getDbo();
// Build the database query to get the rules for the asset.
$query = $db->getQuery(true)
->select($recursive ? 'b.rules' : 'a.rules')
->from('#__assets AS a');
// SQLsrv change
$query->group($recursive ? 'b.id, b.rules, b.lft' : 'a.id, a.rules, a.lft');
// If the asset identifier is numeric assume it is a primary key, else lookup by name.
if (is_numeric($asset))
{
$query->where('(a.id = ' . (int) $asset . ')');
}
else
{
$query->where('(a.name = ' . $db->quote($asset) . ')');
}
// If we want the rules cascading up to the global asset node we need a self-join.
if ($recursive)
{
$query->join('LEFT', '#__assets AS b ON b.lft <= a.lft AND b.rgt >= a.rgt')
->order('b.lft');
}
// Execute the query and load the rules from the result.
$db->setQuery($query);
$result = $db->loadColumn();
// Get the root even if the asset is not found and in recursive mode
if (empty($result))
{
$db = JFactory::getDbo();
$assets = JTable::getInstance('Asset', 'JTable', array('dbo' => $db));
$rootId = $assets->getRootId();
$query->clear()
->select('rules')
->from('#__assets')
->where('id = ' . $db->quote($rootId));
$db->setQuery($query);
$result = $db->loadResult();
$result = array($result);
}
// Instantiate and return the JAccessRules object for the asset rules.
$rules = new JAccessRules;
$rules->mergeCollection($result);
return $rules;
}
/**
* Method to return a list of user groups mapped to a user. The returned list can optionally hold
* only the groups explicitly mapped to the user or all groups both explicitly mapped and inherited
* by the user.
*
* @param integer $userId Id of the user for which to get the list of groups.
* @param boolean $recursive True to include inherited user groups.
*
* @return array List of user group ids to which the user is mapped.
*
* @since 11.1
*/
public static function getGroupsByUser($userId, $recursive = true)
{
// Creates a simple unique string for each parameter combination:
$storeId = $userId . ':' . (int) $recursive;
if (!isset(self::$groupsByUser[$storeId]))
{
// TODO: Uncouple this from JComponentHelper and allow for a configuration setting or value injection.
if (class_exists('JComponentHelper'))
{
$guestUsergroup = JComponentHelper::getParams('com_users')->get('guest_usergroup', 1);
}
else
{
$guestUsergroup = 1;
}
// Guest user (if only the actually assigned group is requested)
if (empty($userId) && !$recursive)
{
$result = array($guestUsergroup);
}
// Registered user and guest if all groups are requested
else
{
$db = JFactory::getDbo();
// Build the database query to get the rules for the asset.
$query = $db->getQuery(true)
->select($recursive ? 'b.id' : 'a.id');
if (empty($userId))
{
$query->from('#__usergroups AS a')
->where('a.id = ' . (int) $guestUsergroup);
}
else
{
$query->from('#__user_usergroup_map AS map')
->where('map.user_id = ' . (int) $userId)
->join('LEFT', '#__usergroups AS a ON a.id = map.group_id');
}
// If we want the rules cascading up to the global asset node we need a self-join.
if ($recursive)
{
$query->join('LEFT', '#__usergroups AS b ON b.lft <= a.lft AND b.rgt >= a.rgt');
}
// Execute the query and load the rules from the result.
$db->setQuery($query);
$result = $db->loadColumn();
// Clean up any NULL or duplicate values, just in case
JArrayHelper::toInteger($result);
if (empty($result))
{
$result = array('1');
}
else
{
$result = array_unique($result);
}
}
self::$groupsByUser[$storeId] = $result;
}
return self::$groupsByUser[$storeId];
}
/**
* Method to return a list of user Ids contained in a Group
*
* @param integer $groupId The group Id
* @param boolean $recursive Recursively include all child groups (optional)
*
* @return array
*
* @since 11.1
* @todo This method should move somewhere else
*/
public static function getUsersByGroup($groupId, $recursive = false)
{
// Get a database object.
$db = JFactory::getDbo();
$test = $recursive ? '>=' : '=';
// First find the users contained in the group
$query = $db->getQuery(true)
->select('DISTINCT(user_id)')
->from('#__usergroups as ug1')
->join('INNER', '#__usergroups AS ug2 ON ug2.lft' . $test . 'ug1.lft AND ug1.rgt' . $test . 'ug2.rgt')
->join('INNER', '#__user_usergroup_map AS m ON ug2.id=m.group_id')
->where('ug1.id=' . $db->quote($groupId));
$db->setQuery($query);
$result = $db->loadColumn();
// Clean up any NULL values, just in case
JArrayHelper::toInteger($result);
return $result;
}
/**
* Method to return a list of view levels for which the user is authorised.
*
* @param integer $userId Id of the user for which to get the list of authorised view levels.
*
* @return array List of view levels for which the user is authorised.
*
* @since 11.1
*/
public static function getAuthorisedViewLevels($userId)
{
// Get all groups that the user is mapped to recursively.
$groups = self::getGroupsByUser($userId);
// Only load the view levels once.
if (empty(self::$viewLevels))
{
// Get a database object.
$db = JFactory::getDbo();
// Build the base query.
$query = $db->getQuery(true)
->select('id, rules')
->from($db->quoteName('#__viewlevels'));
// Set the query for execution.
$db->setQuery($query);
// Build the view levels array.
foreach ($db->loadAssocList() as $level)
{
self::$viewLevels[$level['id']] = (array) json_decode($level['rules']);
}
}
// Initialise the authorised array.
$authorised = array(1);
// Find the authorised levels.
foreach (self::$viewLevels as $level => $rule)
{
foreach ($rule as $id)
{
if (($id < 0) && (($id * -1) == $userId))
{
$authorised[] = $level;
break;
}
// Check to see if the group is mapped to the level.
elseif (($id >= 0) && in_array($id, $groups))
{
$authorised[] = $level;
break;
}
}
}
return $authorised;
}
/**
* Method to return a list of actions for which permissions can be set given a component and section.
*
* @param string $component The component from which to retrieve the actions.
* @param string $section The name of the section within the component from which to retrieve the actions.
*
* @return array List of actions available for the given component and section.
*
* @since 11.1
* @deprecated 12.3 (Platform) & 4.0 (CMS) Use JAccess::getActionsFromFile or JAccess::getActionsFromData instead.
* @codeCoverageIgnore
*/
public static function getActions($component, $section = 'component')
{
JLog::add(__METHOD__ . ' is deprecated. Use JAccess::getActionsFromFile or JAccess::getActionsFromData instead.', JLog::WARNING, 'deprecated');
$actions = self::getActionsFromFile(
JPATH_ADMINISTRATOR . '/components/' . $component . '/access.xml',
"/access/section[@name='" . $section . "']/"
);
if (empty($actions))
{
return array();
}
else
{
return $actions;
}
}
/**
* Method to return a list of actions from a file for which permissions can be set.
*
* @param string $file The path to the XML file.
* @param string $xpath An optional xpath to search for the fields.
*
* @return boolean|array False if case of error or the list of actions available.
*
* @since 12.1
*/
public static function getActionsFromFile($file, $xpath = "/access/section[@name='component']/")
{
if (!is_file($file) || !is_readable($file))
{
// If unable to find the file return false.
return false;
}
else
{
// Else return the actions from the xml.
$xml = simplexml_load_file($file);
return self::getActionsFromData($xml, $xpath);
}
}
/**
* Method to return a list of actions from a string or from an xml for which permissions can be set.
*
* @param string|SimpleXMLElement $data The XML string or an XML element.
* @param string $xpath An optional xpath to search for the fields.
*
* @return boolean|array False if case of error or the list of actions available.
*
* @since 12.1
*/
public static function getActionsFromData($data, $xpath = "/access/section[@name='component']/")
{
// If the data to load isn't already an XML element or string return false.
if ((!($data instanceof SimpleXMLElement)) && (!is_string($data)))
{
return false;
}
// Attempt to load the XML if a string.
if (is_string($data))
{
try
{
$data = new SimpleXMLElement($data);
}
catch (Exception $e)
{
return false;
}
// Make sure the XML loaded correctly.
if (!$data)
{
return false;
}
}
// Initialise the actions array
$actions = array();
// Get the elements from the xpath
$elements = $data->xpath($xpath . 'action[@name][@title][@description]');
// If there some elements, analyse them
if (!empty($elements))
{
foreach ($elements as $action)
{
// Add the action to the actions array
$actions[] = (object) array(
'name' => (string) $action['name'],
'title' => (string) $action['title'],
'description' => (string) $action['description']
);
}
}
// Finally return the actions array
return $actions;
}
}

View File

@ -0,0 +1 @@
<!DOCTYPE html><title></title>

View File

@ -0,0 +1,176 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Access
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* JAccessRule class.
*
* @package Joomla.Platform
* @subpackage Access
* @since 11.4
*/
class JAccessRule
{
/**
* A named array
*
* @var array
* @since 11.1
*/
protected $data = array();
/**
* Constructor.
*
* The input array must be in the form: array(-42 => true, 3 => true, 4 => false)
* or an equivalent JSON encoded string.
*
* @param mixed $identities A JSON format string (probably from the database) or a named array.
*
* @since 11.1
*/
public function __construct($identities)
{
// Convert string input to an array.
if (is_string($identities))
{
$identities = json_decode($identities, true);
}
$this->mergeIdentities($identities);
}
/**
* Get the data for the action.
*
* @return array A named array
*
* @since 11.1
*/
public function getData()
{
return $this->data;
}
/**
* Merges the identities
*
* @param mixed $identities An integer or array of integers representing the identities to check.
*
* @return void
*
* @since 11.1
*/
public function mergeIdentities($identities)
{
if ($identities instanceof JAccessRule)
{
$identities = $identities->getData();
}
if (is_array($identities))
{
foreach ($identities as $identity => $allow)
{
$this->mergeIdentity($identity, $allow);
}
}
}
/**
* Merges the values for an identity.
*
* @param integer $identity The identity.
* @param boolean $allow The value for the identity (true == allow, false == deny).
*
* @return void
*
* @since 11.1
*/
public function mergeIdentity($identity, $allow)
{
$identity = (int) $identity;
$allow = (int) ((boolean) $allow);
// Check that the identity exists.
if (isset($this->data[$identity]))
{
// Explicit deny always wins a merge.
if ($this->data[$identity] !== 0)
{
$this->data[$identity] = $allow;
}
}
else
{
$this->data[$identity] = $allow;
}
}
/**
* Checks that this action can be performed by an identity.
*
* The identity is an integer where +ve represents a user group,
* and -ve represents a user.
*
* @param mixed $identities An integer or array of integers representing the identities to check.
*
* @return mixed True if allowed, false for an explicit deny, null for an implicit deny.
*
* @since 11.1
*/
public function allow($identities)
{
// Implicit deny by default.
$result = null;
// Check that the inputs are valid.
if (!empty($identities))
{
if (!is_array($identities))
{
$identities = array($identities);
}
foreach ($identities as $identity)
{
// Technically the identity just needs to be unique.
$identity = (int) $identity;
// Check if the identity is known.
if (isset($this->data[$identity]))
{
$result = (boolean) $this->data[$identity];
// An explicit deny wins.
if ($result === false)
{
break;
}
}
}
}
return $result;
}
/**
* Convert this object into a JSON encoded string.
*
* @return string JSON encoded string
*
* @since 11.1
*/
public function __toString()
{
return json_encode($this->data);
}
}

View File

@ -0,0 +1,220 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Access
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* JAccessRules class.
*
* @package Joomla.Platform
* @subpackage Access
* @since 11.4
*/
class JAccessRules
{
/**
* A named array.
*
* @var array
* @since 11.1
*/
protected $data = array();
/**
* Constructor.
*
* The input array must be in the form: array('action' => array(-42 => true, 3 => true, 4 => false))
* or an equivalent JSON encoded string, or an object where properties are arrays.
*
* @param mixed $input A JSON format string (probably from the database) or a nested array.
*
* @since 11.1
*/
public function __construct($input = '')
{
// Convert in input to an array.
if (is_string($input))
{
$input = json_decode($input, true);
}
elseif (is_object($input))
{
$input = (array) $input;
}
if (is_array($input))
{
// Top level keys represent the actions.
foreach ($input as $action => $identities)
{
$this->mergeAction($action, $identities);
}
}
}
/**
* Get the data for the action.
*
* @return array A named array of JAccessRule objects.
*
* @since 11.1
*/
public function getData()
{
return $this->data;
}
/**
* Method to merge a collection of JAccessRules.
*
* @param mixed $input JAccessRule or array of JAccessRules
*
* @return void
*
* @since 11.1
*/
public function mergeCollection($input)
{
// Check if the input is an array.
if (is_array($input))
{
foreach ($input as $actions)
{
$this->merge($actions);
}
}
}
/**
* Method to merge actions with this object.
*
* @param mixed $actions JAccessRule object, an array of actions or a JSON string array of actions.
*
* @return void
*
* @since 11.1
*/
public function merge($actions)
{
if (is_string($actions))
{
$actions = json_decode($actions, true);
}
if (is_array($actions))
{
foreach ($actions as $action => $identities)
{
$this->mergeAction($action, $identities);
}
}
elseif ($actions instanceof JAccessRules)
{
$data = $actions->getData();
foreach ($data as $name => $identities)
{
$this->mergeAction($name, $identities);
}
}
}
/**
* Merges an array of identities for an action.
*
* @param string $action The name of the action.
* @param array $identities An array of identities
*
* @return void
*
* @since 11.1
*/
public function mergeAction($action, $identities)
{
if (isset($this->data[$action]))
{
// If exists, merge the action.
$this->data[$action]->mergeIdentities($identities);
}
else
{
// If new, add the action.
$this->data[$action] = new JAccessRule($identities);
}
}
/**
* Checks that an action can be performed by an identity.
*
* The identity is an integer where +ve represents a user group,
* and -ve represents a user.
*
* @param string $action The name of the action.
* @param mixed $identity An integer representing the identity, or an array of identities
*
* @return mixed Object or null if there is no information about the action.
*
* @since 11.1
*/
public function allow($action, $identity)
{
// Check we have information about this action.
if (isset($this->data[$action]))
{
return $this->data[$action]->allow($identity);
}
return null;
}
/**
* Get the allowed actions for an identity.
*
* @param mixed $identity An integer representing the identity or an array of identities
*
* @return JObject Allowed actions for the identity or identities
*
* @since 11.1
*/
public function getAllowed($identity)
{
// Sweep for the allowed actions.
$allowed = new JObject;
foreach ($this->data as $name => &$action)
{
if ($action->allow($identity))
{
$allowed->set($name, true);
}
}
return $allowed;
}
/**
* Magic method to convert the object to JSON string representation.
*
* @return string JSON representation of the actions array
*
* @since 11.1
*/
public function __toString()
{
$temp = array();
foreach ($this->data as $name => $rule)
{
// Convert the action to JSON, then back into an array otherwise
// re-encoding will quote the JSON for the identities in the action.
$temp[$name] = json_decode((string) $rule);
}
return json_encode($temp);
}
}

View File

@ -0,0 +1,151 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Application
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* Joomla Platform Base Application Class
*
* @package Joomla.Platform
* @subpackage Application
* @since 12.1
*/
abstract class JApplicationBase
{
/**
* The application dispatcher object.
*
* @var JEventDispatcher
* @since 12.1
*/
protected $dispatcher;
/**
* The application identity object.
*
* @var JUser
* @since 12.1
*/
protected $identity;
/**
* The application input object.
*
* @var JInput
* @since 12.1
*/
public $input = null;
/**
* Method to close the application.
*
* @param integer $code The exit code (optional; default is 0).
*
* @return void
*
* @codeCoverageIgnore
* @since 12.1
*/
public function close($code = 0)
{
exit($code);
}
/**
* Get the application identity.
*
* @return mixed A JUser object or null.
*
* @since 12.1
*/
public function getIdentity()
{
return $this->identity;
}
/**
* Registers a handler to a particular event group.
*
* @param string $event The event name.
* @param callable $handler The handler, a function or an instance of a event object.
*
* @return JApplicationBase The application to allow chaining.
*
* @since 12.1
*/
public function registerEvent($event, $handler)
{
if ($this->dispatcher instanceof JEventDispatcher)
{
$this->dispatcher->register($event, $handler);
}
return $this;
}
/**
* Calls all handlers associated with an event group.
*
* @param string $event The event name.
* @param array $args An array of arguments (optional).
*
* @return array An array of results from each function call, or null if no dispatcher is defined.
*
* @since 12.1
*/
public function triggerEvent($event, array $args = null)
{
if ($this->dispatcher instanceof JEventDispatcher)
{
return $this->dispatcher->trigger($event, $args);
}
return null;
}
/**
* Allows the application to load a custom or default dispatcher.
*
* The logic and options for creating this object are adequately generic for default cases
* but for many applications it will make sense to override this method and create event
* dispatchers, if required, based on more specific needs.
*
* @param JEventDispatcher $dispatcher An optional dispatcher object. If omitted, the factory dispatcher is created.
*
* @return JApplicationBase This method is chainable.
*
* @since 12.1
*/
public function loadDispatcher(JEventDispatcher $dispatcher = null)
{
$this->dispatcher = ($dispatcher === null) ? JEventDispatcher::getInstance() : $dispatcher;
return $this;
}
/**
* Allows the application to load a custom or default identity.
*
* The logic and options for creating this object are adequately generic for default cases
* but for many applications it will make sense to override this method and create an identity,
* if required, based on more specific needs.
*
* @param JUser $identity An optional identity object. If omitted, the factory user is created.
*
* @return JApplicationBase This method is chainable.
*
* @since 12.1
*/
public function loadIdentity(JUser $identity = null)
{
$this->identity = ($identity === null) ? JFactory::getUser() : $identity;
return $this;
}
}

View File

@ -0,0 +1,294 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Application
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* Base class for a Joomla! command line application.
*
* @package Joomla.Platform
* @subpackage Application
* @since 11.4
*/
class JApplicationCli extends JApplicationBase
{
/**
* @var JRegistry The application configuration object.
* @since 11.1
*/
protected $config;
/**
* @var JApplicationCli The application instance.
* @since 11.1
*/
protected static $instance;
/**
* Class constructor.
*
* @param mixed $input An optional argument to provide dependency injection for the application's
* input object. If the argument is a JInputCli object that object will become
* the application's input object, otherwise a default input object is created.
* @param mixed $config An optional argument to provide dependency injection for the application's
* config object. If the argument is a JRegistry object that object will become
* the application's config object, otherwise a default config object is created.
* @param mixed $dispatcher An optional argument to provide dependency injection for the application's
* event dispatcher. If the argument is a JEventDispatcher object that object will become
* the application's event dispatcher, if it is null then the default event dispatcher
* will be created based on the application's loadDispatcher() method.
*
* @see loadDispatcher()
* @since 11.1
*/
public function __construct(JInputCli $input = null, JRegistry $config = null, JEventDispatcher $dispatcher = null)
{
// Close the application if we are not executed from the command line.
// @codeCoverageIgnoreStart
if (!defined('STDOUT') || !defined('STDIN') || !isset($_SERVER['argv']))
{
$this->close();
}
// @codeCoverageIgnoreEnd
// If a input object is given use it.
if ($input instanceof JInput)
{
$this->input = $input;
}
// Create the input based on the application logic.
else
{
if (class_exists('JInput'))
{
$this->input = new JInputCLI;
}
}
// If a config object is given use it.
if ($config instanceof JRegistry)
{
$this->config = $config;
}
// Instantiate a new configuration object.
else
{
$this->config = new JRegistry;
}
$this->loadDispatcher($dispatcher);
// Load the configuration object.
$this->loadConfiguration($this->fetchConfigurationData());
// Set the execution datetime and timestamp;
$this->set('execution.datetime', gmdate('Y-m-d H:i:s'));
$this->set('execution.timestamp', time());
// Set the current directory.
$this->set('cwd', getcwd());
}
/**
* Returns a property of the object or the default value if the property is not set.
*
* @param string $key The name of the property.
* @param mixed $default The default value (optional) if none is set.
*
* @return mixed The value of the configuration.
*
* @since 11.3
*/
public function get($key, $default = null)
{
return $this->config->get($key, $default);
}
/**
* Returns a reference to the global JApplicationCli object, only creating it if it doesn't already exist.
*
* This method must be invoked as: $cli = JApplicationCli::getInstance();
*
* @param string $name The name (optional) of the JApplicationCli class to instantiate.
*
* @return JApplicationCli
*
* @since 11.1
*/
public static function getInstance($name = null)
{
// Only create the object if it doesn't exist.
if (empty(self::$instance))
{
if (class_exists($name) && (is_subclass_of($name, 'JApplicationCli')))
{
self::$instance = new $name;
}
else
{
self::$instance = new JApplicationCli;
}
}
return self::$instance;
}
/**
* Execute the application.
*
* @return void
*
* @since 11.1
*/
public function execute()
{
// Trigger the onBeforeExecute event.
$this->triggerEvent('onBeforeExecute');
// Perform application routines.
$this->doExecute();
// Trigger the onAfterExecute event.
$this->triggerEvent('onAfterExecute');
}
/**
* Load an object or array into the application configuration object.
*
* @param mixed $data Either an array or object to be loaded into the configuration object.
*
* @return JApplicationCli Instance of $this to allow chaining.
*
* @since 11.1
*/
public function loadConfiguration($data)
{
// Load the data into the configuration object.
if (is_array($data))
{
$this->config->loadArray($data);
}
elseif (is_object($data))
{
$this->config->loadObject($data);
}
return $this;
}
/**
* Write a string to standard output.
*
* @param string $text The text to display.
* @param boolean $nl True (default) to append a new line at the end of the output string.
*
* @return JApplicationCli Instance of $this to allow chaining.
*
* @codeCoverageIgnore
* @since 11.1
*/
public function out($text = '', $nl = true)
{
fwrite(STDOUT, $text . ($nl ? "\n" : null));
return $this;
}
/**
* Get a value from standard input.
*
* @return string The input string from standard input.
*
* @codeCoverageIgnore
* @since 11.1
*/
public function in()
{
return rtrim(fread(STDIN, 8192), "\n");
}
/**
* Modifies a property of the object, creating it if it does not already exist.
*
* @param string $key The name of the property.
* @param mixed $value The value of the property to set (optional).
*
* @return mixed Previous value of the property
*
* @since 11.3
*/
public function set($key, $value = null)
{
$previous = $this->config->get($key);
$this->config->set($key, $value);
return $previous;
}
/**
* Method to load a PHP configuration class file based on convention and return the instantiated data object. You
* will extend this method in child classes to provide configuration data from whatever data source is relevant
* for your specific application.
*
* @param string $file The path and filename of the configuration file. If not provided, configuration.php
* in JPATH_BASE will be used.
* @param string $class The class name to instantiate.
*
* @return mixed Either an array or object to be loaded into the configuration object.
*
* @since 11.1
*/
protected function fetchConfigurationData($file = '', $class = 'JConfig')
{
// Instantiate variables.
$config = array();
if (empty($file) && defined('JPATH_BASE'))
{
$file = JPATH_BASE . '/configuration.php';
// Applications can choose not to have any configuration data
// by not implementing this method and not having a config file.
if (!file_exists($file))
{
$file = '';
}
}
if (!empty($file))
{
JLoader::register($class, $file);
if (class_exists($class))
{
$config = new $class;
}
else
{
throw new RuntimeException('Configuration class does not exist.');
}
}
return $config;
}
/**
* Method to run the application routines. Most likely you will want to instantiate a controller
* and execute it, or perform some sort of task directly.
*
* @return void
*
* @codeCoverageIgnore
* @since 11.3
*/
protected function doExecute()
{
// Your application routines go here.
}
}

View File

@ -0,0 +1,915 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Application
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
jimport('joomla.filesystem.folder');
/**
* Class to turn JCli applications into daemons. It requires CLI and PCNTL support built into PHP.
*
* @package Joomla.Platform
* @subpackage Application
* @see http://www.php.net/manual/en/book.pcntl.php
* @see http://php.net/manual/en/features.commandline.php
* @since 11.1
*/
class JApplicationDaemon extends JApplicationCli
{
/**
* @var array The available POSIX signals to be caught by default.
* @see http://php.net/manual/pcntl.constants.php
* @since 11.1
*/
protected static $signals = array(
'SIGHUP',
'SIGINT',
'SIGQUIT',
'SIGILL',
'SIGTRAP',
'SIGABRT',
'SIGIOT',
'SIGBUS',
'SIGFPE',
'SIGUSR1',
'SIGSEGV',
'SIGUSR2',
'SIGPIPE',
'SIGALRM',
'SIGTERM',
'SIGSTKFLT',
'SIGCLD',
'SIGCHLD',
'SIGCONT',
'SIGTSTP',
'SIGTTIN',
'SIGTTOU',
'SIGURG',
'SIGXCPU',
'SIGXFSZ',
'SIGVTALRM',
'SIGPROF',
'SIGWINCH',
'SIGPOLL',
'SIGIO',
'SIGPWR',
'SIGSYS',
'SIGBABY',
'SIG_BLOCK',
'SIG_UNBLOCK',
'SIG_SETMASK'
);
/**
* @var boolean True if the daemon is in the process of exiting.
* @since 11.1
*/
protected $exiting = false;
/**
* @var integer The parent process id.
* @since 12.1
*/
protected $parentId = 0;
/**
* @var integer The process id of the daemon.
* @since 11.1
*/
protected $processId = 0;
/**
* @var boolean True if the daemon is currently running.
* @since 11.1
*/
protected $running = false;
/**
* Class constructor.
*
* @param mixed $input An optional argument to provide dependency injection for the application's
* input object. If the argument is a JInputCli object that object will become
* the application's input object, otherwise a default input object is created.
* @param mixed $config An optional argument to provide dependency injection for the application's
* config object. If the argument is a JRegistry object that object will become
* the application's config object, otherwise a default config object is created.
* @param mixed $dispatcher An optional argument to provide dependency injection for the application's
* event dispatcher. If the argument is a JEventDispatcher object that object will become
* the application's event dispatcher, if it is null then the default event dispatcher
* will be created based on the application's loadDispatcher() method.
*
* @since 11.1
* @throws RuntimeException
*/
public function __construct(JInputCli $input = null, JRegistry $config = null, JEventDispatcher $dispatcher = null)
{
// Verify that the process control extension for PHP is available.
// @codeCoverageIgnoreStart
if (!defined('SIGHUP'))
{
JLog::add('The PCNTL extension for PHP is not available.', JLog::ERROR);
throw new RuntimeException('The PCNTL extension for PHP is not available.');
}
// Verify that POSIX support for PHP is available.
if (!function_exists('posix_getpid'))
{
JLog::add('The POSIX extension for PHP is not available.', JLog::ERROR);
throw new RuntimeException('The POSIX extension for PHP is not available.');
}
// @codeCoverageIgnoreEnd
// Call the parent constructor.
parent::__construct($input, $config, $dispatcher);
// Set some system limits.
@set_time_limit($this->config->get('max_execution_time', 0));
if ($this->config->get('max_memory_limit') !== null)
{
ini_set('memory_limit', $this->config->get('max_memory_limit', '256M'));
}
// Flush content immediately.
ob_implicit_flush();
}
/**
* Method to handle POSIX signals.
*
* @param integer $signal The received POSIX signal.
*
* @return void
*
* @since 11.1
* @see pcntl_signal()
* @throws RuntimeException
*/
public static function signal($signal)
{
// Log all signals sent to the daemon.
JLog::add('Received signal: ' . $signal, JLog::DEBUG);
// Let's make sure we have an application instance.
if (!is_subclass_of(static::$instance, 'JApplicationDaemon'))
{
JLog::add('Cannot find the application instance.', JLog::EMERGENCY);
throw new RuntimeException('Cannot find the application instance.');
}
// Fire the onReceiveSignal event.
static::$instance->triggerEvent('onReceiveSignal', array($signal));
switch ($signal)
{
case SIGINT:
case SIGTERM:
// Handle shutdown tasks
if (static::$instance->running && static::$instance->isActive())
{
static::$instance->shutdown();
}
else
{
static::$instance->close();
}
break;
case SIGHUP:
// Handle restart tasks
if (static::$instance->running && static::$instance->isActive())
{
static::$instance->shutdown(true);
}
else
{
static::$instance->close();
}
break;
case SIGCHLD:
// A child process has died
while (static::$instance->pcntlWait($signal, WNOHANG || WUNTRACED) > 0)
{
usleep(1000);
}
break;
case SIGCLD:
while (static::$instance->pcntlWait($signal, WNOHANG) > 0)
{
$signal = static::$instance->pcntlChildExitStatus($signal);
}
break;
default:
break;
}
}
/**
* Check to see if the daemon is active. This does not assume that $this daemon is active, but
* only if an instance of the application is active as a daemon.
*
* @return boolean True if daemon is active.
*
* @since 11.1
*/
public function isActive()
{
// Get the process id file location for the application.
$pidFile = $this->config->get('application_pid_file');
// If the process id file doesn't exist then the daemon is obviously not running.
if (!is_file($pidFile))
{
return false;
}
// Read the contents of the process id file as an integer.
$fp = fopen($pidFile, 'r');
$pid = fread($fp, filesize($pidFile));
$pid = (int) $pid;
fclose($fp);
// Check to make sure that the process id exists as a positive integer.
if (!$pid)
{
return false;
}
// Check to make sure the process is active by pinging it and ensure it responds.
if (!posix_kill($pid, 0))
{
// No response so remove the process id file and log the situation.
@ unlink($pidFile);
JLog::add('The process found based on PID file was unresponsive.', JLog::WARNING);
return false;
}
return true;
}
/**
* Load an object or array into the application configuration object.
*
* @param mixed $data Either an array or object to be loaded into the configuration object.
*
* @return JCli Instance of $this to allow chaining.
*
* @since 11.1
*/
public function loadConfiguration($data)
{
// Execute the parent load method.
parent::loadConfiguration($data);
/*
* Setup some application metadata options. This is useful if we ever want to write out startup scripts
* or just have some sort of information available to share about things.
*/
// The application author name. This string is used in generating startup scripts and has
// a maximum of 50 characters.
$tmp = (string) $this->config->get('author_name', 'Joomla Platform');
$this->config->set('author_name', (strlen($tmp) > 50) ? substr($tmp, 0, 50) : $tmp);
// The application author email. This string is used in generating startup scripts.
$tmp = (string) $this->config->get('author_email', 'admin@joomla.org');
$this->config->set('author_email', filter_var($tmp, FILTER_VALIDATE_EMAIL));
// The application name. This string is used in generating startup scripts.
$tmp = (string) $this->config->get('application_name', 'JApplicationDaemon');
$this->config->set('application_name', (string) preg_replace('/[^A-Z0-9_-]/i', '', $tmp));
// The application description. This string is used in generating startup scripts.
$tmp = (string) $this->config->get('application_description', 'A generic Joomla Platform application.');
$this->config->set('application_description', filter_var($tmp, FILTER_SANITIZE_STRING));
/*
* Setup the application path options. This defines the default executable name, executable directory,
* and also the path to the daemon process id file.
*/
// The application executable daemon. This string is used in generating startup scripts.
$tmp = (string) $this->config->get('application_executable', basename($this->input->executable));
$this->config->set('application_executable', $tmp);
// The home directory of the daemon.
$tmp = (string) $this->config->get('application_directory', dirname($this->input->executable));
$this->config->set('application_directory', $tmp);
// The pid file location. This defaults to a path inside the /tmp directory.
$name = $this->config->get('application_name');
$tmp = (string) $this->config->get('application_pid_file', strtolower('/tmp/' . $name . '/' . $name . '.pid'));
$this->config->set('application_pid_file', $tmp);
/*
* Setup the application identity options. It is important to remember if the default of 0 is set for
* either UID or GID then changing that setting will not be attempted as there is no real way to "change"
* the identity of a process from some user to root.
*/
// The user id under which to run the daemon.
$tmp = (int) $this->config->get('application_uid', 0);
$options = array('options' => array('min_range' => 0, 'max_range' => 65000));
$this->config->set('application_uid', filter_var($tmp, FILTER_VALIDATE_INT, $options));
// The group id under which to run the daemon.
$tmp = (int) $this->config->get('application_gid', 0);
$options = array('options' => array('min_range' => 0, 'max_range' => 65000));
$this->config->set('application_gid', filter_var($tmp, FILTER_VALIDATE_INT, $options));
// Option to kill the daemon if it cannot switch to the chosen identity.
$tmp = (bool) $this->config->get('application_require_identity', 1);
$this->config->set('application_require_identity', $tmp);
/*
* Setup the application runtime options. By default our execution time limit is infinite obviously
* because a daemon should be constantly running unless told otherwise. The default limit for memory
* usage is 128M, which admittedly is a little high, but remember it is a "limit" and PHP's memory
* management leaves a bit to be desired :-)
*/
// The maximum execution time of the application in seconds. Zero is infinite.
$tmp = $this->config->get('max_execution_time');
if ($tmp !== null)
{
$this->config->set('max_execution_time', (int) $tmp);
}
// The maximum amount of memory the application can use.
$tmp = $this->config->get('max_memory_limit', '256M');
if ($tmp !== null)
{
$this->config->set('max_memory_limit', (string) $tmp);
}
return $this;
}
/**
* Execute the daemon.
*
* @return void
*
* @since 11.1
*/
public function execute()
{
// Trigger the onBeforeExecute event.
$this->triggerEvent('onBeforeExecute');
// Enable basic garbage collection.
gc_enable();
JLog::add('Starting ' . $this->name, JLog::INFO);
// Set off the process for becoming a daemon.
if ($this->daemonize())
{
// Declare ticks to start signal monitoring. When you declare ticks, PCNTL will monitor
// incoming signals after each tick and call the relevant signal handler automatically.
declare (ticks = 1);
// Start the main execution loop.
while (true)
{
// Perform basic garbage collection.
$this->gc();
// Don't completely overload the CPU.
usleep(1000);
// Execute the main application logic.
$this->doExecute();
}
}
// We were not able to daemonize the application so log the failure and die gracefully.
else
{
JLog::add('Starting ' . $this->name . ' failed', JLog::INFO);
}
// Trigger the onAfterExecute event.
$this->triggerEvent('onAfterExecute');
}
/**
* Restart daemon process.
*
* @return void
*
* @codeCoverageIgnore
* @since 11.1
*/
public function restart()
{
JLog::add('Stopping ' . $this->name, JLog::INFO);
$this->shutdown(true);
}
/**
* Stop daemon process.
*
* @return void
*
* @codeCoverageIgnore
* @since 11.1
*/
public function stop()
{
JLog::add('Stopping ' . $this->name, JLog::INFO);
$this->shutdown();
}
/**
* Method to change the identity of the daemon process and resources.
*
* @return boolean True if identity successfully changed
*
* @since 11.1
* @see posix_setuid()
*/
protected function changeIdentity()
{
// Get the group and user ids to set for the daemon.
$uid = (int) $this->config->get('application_uid', 0);
$gid = (int) $this->config->get('application_gid', 0);
// Get the application process id file path.
$file = $this->config->get('application_pid_file');
// Change the user id for the process id file if necessary.
if ($uid && (fileowner($file) != $uid) && (!@ chown($file, $uid)))
{
JLog::add('Unable to change user ownership of the process id file.', JLog::ERROR);
return false;
}
// Change the group id for the process id file if necessary.
if ($gid && (filegroup($file) != $gid) && (!@ chgrp($file, $gid)))
{
JLog::add('Unable to change group ownership of the process id file.', JLog::ERROR);
return false;
}
// Set the correct home directory for the process.
if ($uid && ($info = posix_getpwuid($uid)) && is_dir($info['dir']))
{
system('export HOME="' . $info['dir'] . '"');
}
// Change the user id for the process necessary.
if ($uid && (posix_getuid($file) != $uid) && (!@ posix_setuid($uid)))
{
JLog::add('Unable to change user ownership of the proccess.', JLog::ERROR);
return false;
}
// Change the group id for the process necessary.
if ($gid && (posix_getgid($file) != $gid) && (!@ posix_setgid($gid)))
{
JLog::add('Unable to change group ownership of the proccess.', JLog::ERROR);
return false;
}
// Get the user and group information based on uid and gid.
$user = posix_getpwuid($uid);
$group = posix_getgrgid($gid);
JLog::add('Changed daemon identity to ' . $user['name'] . ':' . $group['name'], JLog::INFO);
return true;
}
/**
* Method to put the application into the background.
*
* @return boolean
*
* @since 11.1
* @throws RuntimeException
*/
protected function daemonize()
{
// Is there already an active daemon running?
if ($this->isActive())
{
JLog::add($this->name . ' daemon is still running. Exiting the application.', JLog::EMERGENCY);
return false;
}
// Reset Process Information
$this->safeMode = !!@ ini_get('safe_mode');
$this->processId = 0;
$this->running = false;
// Detach process!
try
{
// Check if we should run in the foreground.
if (!$this->input->get('f'))
{
// Detach from the terminal.
$this->detach();
}
else
{
// Setup running values.
$this->exiting = false;
$this->running = true;
// Set the process id.
$this->processId = (int) posix_getpid();
$this->parentId = $this->processId;
}
}
catch (RuntimeException $e)
{
JLog::add('Unable to fork.', JLog::EMERGENCY);
return false;
}
// Verify the process id is valid.
if ($this->processId < 1)
{
JLog::add('The process id is invalid; the fork failed.', JLog::EMERGENCY);
return false;
}
// Clear the umask.
@ umask(0);
// Write out the process id file for concurrency management.
if (!$this->writeProcessIdFile())
{
JLog::add('Unable to write the pid file at: ' . $this->config->get('application_pid_file'), JLog::EMERGENCY);
return false;
}
// Attempt to change the identity of user running the process.
if (!$this->changeIdentity())
{
// If the identity change was required then we need to return false.
if ($this->config->get('application_require_identity'))
{
JLog::add('Unable to change process owner.', JLog::CRITICAL);
return false;
}
else
{
JLog::add('Unable to change process owner.', JLog::WARNING);
}
}
// Setup the signal handlers for the daemon.
if (!$this->setupSignalHandlers())
{
return false;
}
// Change the current working directory to the application working directory.
@ chdir($this->config->get('application_directory'));
return true;
}
/**
* This is truly where the magic happens. This is where we fork the process and kill the parent
* process, which is essentially what turns the application into a daemon.
*
* @return void
*
* @since 12.1
* @throws RuntimeException
*/
protected function detach()
{
JLog::add('Detaching the ' . $this->name . ' daemon.', JLog::DEBUG);
// Attempt to fork the process.
$pid = $this->fork();
// If the pid is positive then we successfully forked, and can close this application.
if ($pid)
{
// Add the log entry for debugging purposes and exit gracefully.
JLog::add('Ending ' . $this->name . ' parent process', JLog::DEBUG);
$this->close();
}
// We are in the forked child process.
else
{
// Setup some protected values.
$this->exiting = false;
$this->running = true;
// Set the parent to self.
$this->parentId = $this->processId;
}
}
/**
* Method to fork the process.
*
* @return integer The child process id to the parent process, zero to the child process.
*
* @since 11.1
* @throws RuntimeException
*/
protected function fork()
{
// Attempt to fork the process.
$pid = $this->pcntlFork();
// If the fork failed, throw an exception.
if ($pid === -1)
{
throw new RuntimeException('The process could not be forked.');
}
// Update the process id for the child.
elseif ($pid === 0)
{
$this->processId = (int) posix_getpid();
}
// Log the fork in the parent.
else
{
// Log the fork.
JLog::add('Process forked ' . $pid, JLog::DEBUG);
}
// Trigger the onFork event.
$this->postFork();
return $pid;
}
/**
* Method to perform basic garbage collection and memory management in the sense of clearing the
* stat cache. We will probably call this method pretty regularly in our main loop.
*
* @return void
*
* @codeCoverageIgnore
* @since 11.1
*/
protected function gc()
{
// Perform generic garbage collection.
gc_collect_cycles();
// Clear the stat cache so it doesn't blow up memory.
clearstatcache();
}
/**
* Method to attach the JApplicationDaemon signal handler to the known signals. Applications
* can override these handlers by using the pcntl_signal() function and attaching a different
* callback method.
*
* @return boolean
*
* @since 11.1
* @see pcntl_signal()
*/
protected function setupSignalHandlers()
{
// We add the error suppression for the loop because on some platforms some constants are not defined.
foreach (self::$signals as $signal)
{
// Ignore signals that are not defined.
if (!defined($signal) || !is_int(constant($signal)) || (constant($signal) === 0))
{
// Define the signal to avoid notices.
JLog::add('Signal "' . $signal . '" not defined. Defining it as null.', JLog::DEBUG);
define($signal, null);
// Don't listen for signal.
continue;
}
// Attach the signal handler for the signal.
if (!$this->pcntlSignal(constant($signal), array('JApplicationDaemon', 'signal')))
{
JLog::add(sprintf('Unable to reroute signal handler: %s', $signal), JLog::EMERGENCY);
return false;
}
}
return true;
}
/**
* Method to shut down the daemon and optionally restart it.
*
* @param boolean $restart True to restart the daemon on exit.
*
* @return void
*
* @since 11.1
*/
protected function shutdown($restart = false)
{
// If we are already exiting, chill.
if ($this->exiting)
{
return;
}
// If not, now we are.
else
{
$this->exiting = true;
}
// If we aren't already daemonized then just kill the application.
if (!$this->running && !$this->isActive())
{
JLog::add('Process was not daemonized yet, just halting current process', JLog::INFO);
$this->close();
}
// Only read the pid for the parent file.
if ($this->parentId == $this->processId)
{
// Read the contents of the process id file as an integer.
$fp = fopen($this->config->get('application_pid_file'), 'r');
$pid = fread($fp, filesize($this->config->get('application_pid_file')));
$pid = (int) $pid;
fclose($fp);
// Remove the process id file.
@ unlink($this->config->get('application_pid_file'));
// If we are supposed to restart the daemon we need to execute the same command.
if ($restart)
{
$this->close(exec(implode(' ', $GLOBALS['argv']) . ' > /dev/null &'));
}
// If we are not supposed to restart the daemon let's just kill -9.
else
{
passthru('kill -9 ' . $pid);
$this->close();
}
}
}
/**
* Method to write the process id file out to disk.
*
* @return boolean
*
* @since 11.1
*/
protected function writeProcessIdFile()
{
// Verify the process id is valid.
if ($this->processId < 1)
{
JLog::add('The process id is invalid.', JLog::EMERGENCY);
return false;
}
// Get the application process id file path.
$file = $this->config->get('application_pid_file');
if (empty($file))
{
JLog::add('The process id file path is empty.', JLog::ERROR);
return false;
}
// Make sure that the folder where we are writing the process id file exists.
$folder = dirname($file);
if (!is_dir($folder) && !JFolder::create($folder))
{
JLog::add('Unable to create directory: ' . $folder, JLog::ERROR);
return false;
}
// Write the process id file out to disk.
if (!file_put_contents($file, $this->processId))
{
JLog::add('Unable to write proccess id file: ' . $file, JLog::ERROR);
return false;
}
// Make sure the permissions for the proccess id file are accurate.
if (!chmod($file, 0644))
{
JLog::add('Unable to adjust permissions for the proccess id file: ' . $file, JLog::ERROR);
return false;
}
return true;
}
/**
* Method to handle post-fork triggering of the onFork event.
*
* @return void
*
* @since 12.1
*/
protected function postFork()
{
// Trigger the onFork event.
$this->triggerEvent('onFork');
}
/**
* Method to return the exit code of a terminated child process.
*
* @param integer $status The status parameter is the status parameter supplied to a successful call to pcntl_waitpid().
*
* @return integer The child process exit code.
*
* @codeCoverageIgnore
* @see pcntl_wexitstatus()
* @since 11.3
*/
protected function pcntlChildExitStatus($status)
{
return pcntl_wexitstatus($status);
}
/**
* Method to return the exit code of a terminated child process.
*
* @return integer On success, the PID of the child process is returned in the parent's thread
* of execution, and a 0 is returned in the child's thread of execution. On
* failure, a -1 will be returned in the parent's context, no child process
* will be created, and a PHP error is raised.
*
* @codeCoverageIgnore
* @see pcntl_fork()
* @since 11.3
*/
protected function pcntlFork()
{
return pcntl_fork();
}
/**
* Method to install a signal handler.
*
* @param integer $signal The signal number.
* @param callable $handler The signal handler which may be the name of a user created function,
* or method, or either of the two global constants SIG_IGN or SIG_DFL.
* @param boolean $restart Specifies whether system call restarting should be used when this
* signal arrives.
*
* @return boolean True on success.
*
* @codeCoverageIgnore
* @see pcntl_signal()
* @since 11.3
*/
protected function pcntlSignal($signal , $handler, $restart = true)
{
return pcntl_signal($signal, $handler, $restart);
}
/**
* Method to wait on or return the status of a forked child.
*
* @param integer &$status Status information.
* @param integer $options If wait3 is available on your system (mostly BSD-style systems),
* you can provide the optional options parameter.
*
* @return integer The process ID of the child which exited, -1 on error or zero if WNOHANG
* was provided as an option (on wait3-available systems) and no child was available.
*
* @codeCoverageIgnore
* @see pcntl_wait()
* @since 11.3
*/
protected function pcntlWait(&$status, $options = 0)
{
return pcntl_wait($status, $options);
}
}

View File

@ -0,0 +1 @@
<!DOCTYPE html><title></title>

View File

@ -0,0 +1,107 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Application
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* Route handling class
*
* @package Joomla.Platform
* @subpackage Application
* @since 11.1
*/
class JRoute
{
/**
* The route object so we don't have to keep fetching it.
*
* @var JRouter
* @since 12.2
*/
private static $_router = null;
/**
* Translates an internal Joomla URL to a humanly readible URL.
*
* @param string $url Absolute or Relative URI to Joomla resource.
* @param boolean $xhtml Replace & by &amp; for XML compilance.
* @param integer $ssl Secure state for the resolved URI.
* 1: Make URI secure using global secure site URI.
* 2: Make URI unsecure using the global unsecure site URI.
*
* @return The translated humanly readible URL.
*
* @since 11.1
*/
public static function _($url, $xhtml = true, $ssl = null)
{
if (!self::$_router)
{
// Get the router.
self::$_router = JFactory::getApplication()->getRouter();
// Make sure that we have our router
if (!self::$_router)
{
return null;
}
}
if ((strpos($url, '&') !== 0) && (strpos($url, 'index.php') !== 0))
{
return $url;
}
// Build route.
$uri = self::$_router->build($url);
$url = $uri->toString(array('path', 'query', 'fragment'));
// Replace spaces.
$url = preg_replace('/\s/u', '%20', $url);
/*
* Get the secure/unsecure URLs.
*
* If the first 5 characters of the BASE are 'https', then we are on an ssl connection over
* https and need to set our secure URL to the current request URL, if not, and the scheme is
* 'http', then we need to do a quick string manipulation to switch schemes.
*/
if ((int) $ssl)
{
$uri = JUri::getInstance();
// Get additional parts.
static $prefix;
if (!$prefix)
{
$prefix = $uri->toString(array('host', 'port'));
}
// Determine which scheme we want.
$scheme = ((int) $ssl === 1) ? 'https' : 'http';
// Make sure our URL path begins with a slash.
if (!preg_match('#^/#', $url))
{
$url = '/' . $url;
}
// Build the URL.
$url = $scheme . '://' . $prefix . $url;
}
if ($xhtml)
{
$url = htmlspecialchars($url);
}
return $url;
}
}

View File

@ -0,0 +1,521 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Application
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* Set the available masks for the routing mode
*/
const JROUTER_MODE_RAW = 0;
const JROUTER_MODE_SEF = 1;
/**
* Class to create and parse routes
*
* @package Joomla.Platform
* @subpackage Application
* @since 11.1
*/
class JRouter
{
/**
* The rewrite mode
*
* @var integer
* @since 11.1
*/
protected $mode = null;
/**
* The rewrite mode
*
* @var integer
* @since 11.1
* @deprecated use $mode declare as private
*/
protected $_mode = null;
/**
* An array of variables
*
* @var array
* @since 11.1
*/
protected $vars = array();
/**
* An array of variables
*
* @var array
* @since 11.1
* @deprecated use $vars declare as private
*/
protected $_vars = array();
/**
* An array of rules
*
* @var array
* @since 11.1
*/
protected $rules = array(
'build' => array(),
'parse' => array()
);
/**
* An array of rules
*
* @var array
* @since 11.1
* @deprecated use $rules declare as private
*/
protected $_rules = array(
'build' => array(),
'parse' => array()
);
/**
* @var array JRouter instances container.
* @since 11.3
*/
protected static $instances = array();
/**
* Class constructor
*
* @param array $options Array of options
*
* @since 11.1
*/
public function __construct($options = array())
{
if (array_key_exists('mode', $options))
{
$this->_mode = $options['mode'];
}
else
{
$this->_mode = JROUTER_MODE_RAW;
}
}
/**
* Returns the global JRouter object, only creating it if it
* doesn't already exist.
*
* @param string $client The name of the client
* @param array $options An associative array of options
*
* @return JRouter A JRouter object.
*
* @since 11.1
* @throws RuntimeException
*/
public static function getInstance($client, $options = array())
{
if (empty(self::$instances[$client]))
{
// Create a JRouter object
$classname = 'JRouter' . ucfirst($client);
if (!class_exists($classname))
{
JLog::add('Non-autoloadable JRouter subclasses are deprecated.', JLog::WARNING, 'deprecated');
// Load the router object
$info = JApplicationHelper::getClientInfo($client, true);
if (is_object($info))
{
$path = $info->path . '/includes/router.php';
if (file_exists($path))
{
include_once $path;
}
}
}
if (class_exists($classname))
{
self::$instances[$client] = new $classname($options);
}
else
{
throw new RuntimeException(JText::sprintf('JLIB_APPLICATION_ERROR_ROUTER_LOAD', $client), 500);
}
}
return self::$instances[$client];
}
/**
* Function to convert a route to an internal URI
*
* @param JUri $uri The uri.
*
* @return array
*
* @since 11.1
*/
public function parse($uri)
{
// Process the parsed variables based on custom defined rules
$vars = $this->_processParseRules($uri);
// Parse RAW URL
if ($this->_mode == JROUTER_MODE_RAW)
{
$vars += $this->_parseRawRoute($uri);
}
// Parse SEF URL
if ($this->_mode == JROUTER_MODE_SEF)
{
$vars += $this->_parseSefRoute($uri);
}
return array_merge($this->getVars(), $vars);
}
/**
* Function to convert an internal URI to a route
*
* @param string $url The internal URL
*
* @return string The absolute search engine friendly URL
*
* @since 11.1
*/
public function build($url)
{
// Create the URI object
$uri = $this->_createURI($url);
// Process the uri information based on custom defined rules
$this->_processBuildRules($uri);
// Build RAW URL
if ($this->_mode == JROUTER_MODE_RAW)
{
$this->_buildRawRoute($uri);
}
// Build SEF URL : mysite/route/index.php?var=x
if ($this->_mode == JROUTER_MODE_SEF)
{
$this->_buildSefRoute($uri);
}
return $uri;
}
/**
* Get the router mode
*
* @return integer
*
* @since 11.1
*/
public function getMode()
{
return $this->_mode;
}
/**
* Set the router mode
*
* @param integer $mode The routing mode.
*
* @return void
*
* @since 11.1
*/
public function setMode($mode)
{
$this->_mode = $mode;
}
/**
* Set a router variable, creating it if it doesn't exist
*
* @param string $key The name of the variable
* @param mixed $value The value of the variable
* @param boolean $create If True, the variable will be created if it doesn't exist yet
*
* @return void
*
* @since 11.1
*/
public function setVar($key, $value, $create = true)
{
if ($create || array_key_exists($key, $this->_vars))
{
$this->_vars[$key] = $value;
}
}
/**
* Set the router variable array
*
* @param array $vars An associative array with variables
* @param boolean $merge If True, the array will be merged instead of overwritten
*
* @return void
*
* @since 11.1
*/
public function setVars($vars = array(), $merge = true)
{
if ($merge)
{
$this->_vars = array_merge($this->_vars, $vars);
}
else
{
$this->_vars = $vars;
}
}
/**
* Get a router variable
*
* @param string $key The name of the variable
*
* @return mixed Value of the variable
*
* @since 11.1
*/
public function getVar($key)
{
$result = null;
if (isset($this->_vars[$key]))
{
$result = $this->_vars[$key];
}
return $result;
}
/**
* Get the router variable array
*
* @return array An associative array of router variables
*
* @since 11.1
*/
public function getVars()
{
return $this->_vars;
}
/**
* Attach a build rule
*
* @param callback $callback The function to be called
*
* @return void
*
* @since 11.1.
*/
public function attachBuildRule($callback)
{
$this->_rules['build'][] = $callback;
}
/**
* Attach a parse rule
*
* @param callback $callback The function to be called.
*
* @return void
*
* @since 11.1
*/
public function attachParseRule($callback)
{
$this->_rules['parse'][] = $callback;
}
/**
* Function to convert a raw route to an internal URI
*
* @param JUri $uri The raw route
*
* @return boolean
*
* @since 11.1
*/
protected function _parseRawRoute($uri)
{
return false;
}
/**
* Function to convert a sef route to an internal URI
*
* @param JUri $uri The sef URI
*
* @return string Internal URI
*
* @since 11.1
*/
protected function _parseSefRoute($uri)
{
return false;
}
/**
* Function to build a raw route
*
* @param JUri $uri The internal URL
*
* @return string Raw Route
*
* @since 11.1
*/
protected function _buildRawRoute($uri)
{
}
/**
* Function to build a sef route
*
* @param JUri $uri The uri
*
* @return string The SEF route
*
* @since 11.1
*/
protected function _buildSefRoute($uri)
{
}
/**
* Process the parsed router variables based on custom defined rules
*
* @param JUri $uri The URI to parse
*
* @return array The array of processed URI variables
*
* @since 11.1
*/
protected function _processParseRules($uri)
{
$vars = array();
foreach ($this->_rules['parse'] as $rule)
{
$vars += call_user_func_array($rule, array(&$this, &$uri));
}
return $vars;
}
/**
* Process the build uri query data based on custom defined rules
*
* @param JUri $uri The URI
*
* @return void
*
* @since 11.1
*/
protected function _processBuildRules($uri)
{
foreach ($this->_rules['build'] as $rule)
{
call_user_func_array($rule, array(&$this, &$uri));
}
}
/**
* Create a uri based on a full or partial url string
*
* @param string $url The URI
*
* @return JUri
*
* @since 11.1
*/
protected function _createURI($url)
{
// Create full URL if we are only appending variables to it
if (substr($url, 0, 1) == '&')
{
$vars = array();
if (strpos($url, '&amp;') !== false)
{
$url = str_replace('&amp;', '&', $url);
}
parse_str($url, $vars);
$vars = array_merge($this->getVars(), $vars);
foreach ($vars as $key => $var)
{
if ($var == "")
{
unset($vars[$key]);
}
}
$url = 'index.php?' . JUri::buildQuery($vars);
}
// Decompose link into url component parts
return new JUri($url);
}
/**
* Encode route segments
*
* @param array $segments An array of route segments
*
* @return array Array of encoded route segments
*
* @since 11.1
*/
protected function _encodeSegments($segments)
{
$total = count($segments);
for ($i = 0; $i < $total; $i++)
{
$segments[$i] = str_replace(':', '-', $segments[$i]);
}
return $segments;
}
/**
* Decode route segments
*
* @param array $segments An array of route segments
*
* @return array Array of decoded route segments
*
* @since 11.1
*/
protected function _decodeSegments($segments)
{
$total = count($segments);
for ($i = 0; $i < $total; $i++)
{
$segments[$i] = preg_replace('/-/', ':', $segments[$i], 1);
}
return $segments;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,516 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Application
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* Class to model a Web Client.
*
* @property-read integer $platform The detected platform on which the web client runs.
* @property-read boolean $mobile True if the web client is a mobile device.
* @property-read integer $engine The detected rendering engine used by the web client.
* @property-read integer $browser The detected browser used by the web client.
* @property-read string $browserVersion The detected browser version used by the web client.
* @property-read array $languages The priority order detected accepted languages for the client.
* @property-read array $encodings The priority order detected accepted encodings for the client.
* @property-read string $userAgent The web client's user agent string.
* @property-read string $acceptEncoding The web client's accepted encoding string.
* @property-read string $acceptLanguage The web client's accepted languages string.
* @property-read array $detection An array of flags determining whether or not a detection routine has been run.
* @property-read boolean $robot True if the web client is a robot
*
* @package Joomla.Platform
* @subpackage Application
* @since 12.1
*/
class JApplicationWebClient
{
const WINDOWS = 1;
const WINDOWS_PHONE = 2;
const WINDOWS_CE = 3;
const IPHONE = 4;
const IPAD = 5;
const IPOD = 6;
const MAC = 7;
const BLACKBERRY = 8;
const ANDROID = 9;
const LINUX = 10;
const TRIDENT = 11;
const WEBKIT = 12;
const GECKO = 13;
const PRESTO = 14;
const KHTML = 15;
const AMAYA = 16;
const IE = 17;
const FIREFOX = 18;
const CHROME = 19;
const SAFARI = 20;
const OPERA = 21;
const ANDROIDTABLET = 22;
/**
* @var integer The detected platform on which the web client runs.
* @since 12.1
*/
protected $platform;
/**
* @var boolean True if the web client is a mobile device.
* @since 12.1
*/
protected $mobile = false;
/**
* @var integer The detected rendering engine used by the web client.
* @since 12.1
*/
protected $engine;
/**
* @var integer The detected browser used by the web client.
* @since 12.1
*/
protected $browser;
/**
* @var string The detected browser version used by the web client.
* @since 12.1
*/
protected $browserVersion;
/**
* @var array The priority order detected accepted languages for the client.
* @since 12.1
*/
protected $languages = array();
/**
* @var array The priority order detected accepted encodings for the client.
* @since 12.1
*/
protected $encodings = array();
/**
* @var string The web client's user agent string.
* @since 12.1
*/
protected $userAgent;
/**
* @var string The web client's accepted encoding string.
* @since 12.1
*/
protected $acceptEncoding;
/**
* @var string The web client's accepted languages string.
* @since 12.1
*/
protected $acceptLanguage;
/**
* @var boolean True if the web client is a robot.
* @since 12.3
*/
protected $robot = false;
/**
* @var array An array of flags determining whether or not a detection routine has been run.
* @since 12.1
*/
protected $detection = array();
/**
* Class constructor.
*
* @param string $userAgent The optional user-agent string to parse.
* @param string $acceptEncoding The optional client accept encoding string to parse.
* @param string $acceptLanguage The optional client accept language string to parse.
*
* @since 12.1
*/
public function __construct($userAgent = null, $acceptEncoding = null, $acceptLanguage = null)
{
// If no explicit user agent string was given attempt to use the implicit one from server environment.
if (empty($userAgent) && isset($_SERVER['HTTP_USER_AGENT']))
{
$this->userAgent = $_SERVER['HTTP_USER_AGENT'];
}
else
{
$this->userAgent = $userAgent;
}
// If no explicit acceptable encoding string was given attempt to use the implicit one from server environment.
if (empty($acceptEncoding) && isset($_SERVER['HTTP_ACCEPT_ENCODING']))
{
$this->acceptEncoding = $_SERVER['HTTP_ACCEPT_ENCODING'];
}
else
{
$this->acceptEncoding = $acceptEncoding;
}
// If no explicit acceptable languages string was given attempt to use the implicit one from server environment.
if (empty($acceptLanguage) && isset($_SERVER['HTTP_ACCEPT_LANGUAGE']))
{
$this->acceptLanguage = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
}
else
{
$this->acceptLanguage = $acceptLanguage;
}
}
/**
* Magic method to get an object property's value by name.
*
* @param string $name Name of the property for which to return a value.
*
* @return mixed The requested value if it exists.
*
* @since 12.1
*/
public function __get($name)
{
switch ($name)
{
case 'mobile':
case 'platform':
if (empty($this->detection['platform']))
{
$this->detectPlatform($this->userAgent);
}
break;
case 'engine':
if (empty($this->detection['engine']))
{
$this->detectEngine($this->userAgent);
}
break;
case 'browser':
case 'browserVersion':
if (empty($this->detection['browser']))
{
$this->detectBrowser($this->userAgent);
}
break;
case 'languages':
if (empty($this->detection['acceptLanguage']))
{
$this->detectLanguage($this->acceptLanguage);
}
break;
case 'encodings':
if (empty($this->detection['acceptEncoding']))
{
$this->detectEncoding($this->acceptEncoding);
}
break;
case 'robot':
if (empty($this->detection['robot']))
{
$this->detectRobot($this->userAgent);
}
break;
}
// Return the property if it exists.
if (isset($this->$name))
{
return $this->$name;
}
}
/**
* Detects the client browser and version in a user agent string.
*
* @param string $userAgent The user-agent string to parse.
*
* @return void
*
* @since 12.1
*/
protected function detectBrowser($userAgent)
{
// Attempt to detect the browser type. Obviously we are only worried about major browsers.
if ((stripos($userAgent, 'MSIE') !== false) && (stripos($userAgent, 'Opera') === false))
{
$this->browser = self::IE;
$patternBrowser = 'MSIE';
}
elseif ((stripos($userAgent, 'Firefox') !== false) && (stripos($userAgent, 'like Firefox') === false))
{
$this->browser = self::FIREFOX;
$patternBrowser = 'Firefox';
}
elseif (stripos($userAgent, 'Chrome') !== false)
{
$this->browser = self::CHROME;
$patternBrowser = 'Chrome';
}
elseif (stripos($userAgent, 'Safari') !== false)
{
$this->browser = self::SAFARI;
$patternBrowser = 'Safari';
}
elseif (stripos($userAgent, 'Opera') !== false)
{
$this->browser = self::OPERA;
$patternBrowser = 'Opera';
}
// If we detected a known browser let's attempt to determine the version.
if ($this->browser)
{
// Build the REGEX pattern to match the browser version string within the user agent string.
$pattern = '#(?<browser>Version|' . $patternBrowser . ')[/ ]+(?<version>[0-9.|a-zA-Z.]*)#';
// Attempt to find version strings in the user agent string.
$matches = array();
if (preg_match_all($pattern, $userAgent, $matches))
{
// Do we have both a Version and browser match?
if (count($matches['browser']) == 2)
{
// See whether Version or browser came first, and use the number accordingly.
if (strripos($userAgent, 'Version') < strripos($userAgent, $patternBrowser))
{
$this->browserVersion = $matches['version'][0];
}
else
{
$this->browserVersion = $matches['version'][1];
}
}
elseif (count($matches['browser']) > 2)
{
$key = array_search('Version', $matches['browser']);
if ($key)
{
$this->browserVersion = $matches['version'][$key];
}
}
// We only have a Version or a browser so use what we have.
else
{
$this->browserVersion = $matches['version'][0];
}
}
}
// Mark this detection routine as run.
$this->detection['browser'] = true;
}
/**
* Method to detect the accepted response encoding by the client.
*
* @param string $acceptEncoding The client accept encoding string to parse.
*
* @return void
*
* @since 12.1
*/
protected function detectEncoding($acceptEncoding)
{
// Parse the accepted encodings.
$this->encodings = array_map('trim', (array) explode(',', $acceptEncoding));
// Mark this detection routine as run.
$this->detection['acceptEncoding'] = true;
}
/**
* Detects the client rendering engine in a user agent string.
*
* @param string $userAgent The user-agent string to parse.
*
* @return void
*
* @since 12.1
*/
protected function detectEngine($userAgent)
{
// Attempt to detect the client engine -- starting with the most popular ... for now.
if (stripos($userAgent, 'MSIE') !== false || stripos($userAgent, 'Trident') !== false)
{
$this->engine = self::TRIDENT;
}
// Evidently blackberry uses WebKit and doesn't necessarily report it. Bad RIM.
elseif (stripos($userAgent, 'AppleWebKit') !== false || stripos($userAgent, 'blackberry') !== false)
{
$this->engine = self::WEBKIT;
}
// We have to check for like Gecko because some other browsers spoof Gecko.
elseif (stripos($userAgent, 'Gecko') !== false && stripos($userAgent, 'like Gecko') === false)
{
$this->engine = self::GECKO;
}
// Sometimes Opera browsers don't say Presto.
elseif (stripos($userAgent, 'Opera') !== false || stripos($userAgent, 'Presto') !== false)
{
$this->engine = self::PRESTO;
}
// *sigh*
elseif (stripos($userAgent, 'KHTML') !== false)
{
$this->engine = self::KHTML;
}
// Lesser known engine but it finishes off the major list from Wikipedia :-)
elseif (stripos($userAgent, 'Amaya') !== false)
{
$this->engine = self::AMAYA;
}
// Mark this detection routine as run.
$this->detection['engine'] = true;
}
/**
* Method to detect the accepted languages by the client.
*
* @param mixed $acceptLanguage The client accept language string to parse.
*
* @return void
*
* @since 12.1
*/
protected function detectLanguage($acceptLanguage)
{
// Parse the accepted encodings.
$this->languages = array_map('trim', (array) explode(',', $acceptLanguage));
// Mark this detection routine as run.
$this->detection['acceptLanguage'] = true;
}
/**
* Detects the client platform in a user agent string.
*
* @param string $userAgent The user-agent string to parse.
*
* @return void
*
* @since 12.1
*/
protected function detectPlatform($userAgent)
{
// Attempt to detect the client platform.
if (stripos($userAgent, 'Windows') !== false)
{
$this->platform = self::WINDOWS;
// Let's look at the specific mobile options in the Windows space.
if (stripos($userAgent, 'Windows Phone') !== false)
{
$this->mobile = true;
$this->platform = self::WINDOWS_PHONE;
}
elseif (stripos($userAgent, 'Windows CE') !== false)
{
$this->mobile = true;
$this->platform = self::WINDOWS_CE;
}
}
// Interestingly 'iPhone' is present in all iOS devices so far including iPad and iPods.
elseif (stripos($userAgent, 'iPhone') !== false)
{
$this->mobile = true;
$this->platform = self::IPHONE;
// Let's look at the specific mobile options in the iOS space.
if (stripos($userAgent, 'iPad') !== false)
{
$this->platform = self::IPAD;
}
elseif (stripos($userAgent, 'iPod') !== false)
{
$this->platform = self::IPOD;
}
}
// In case where iPhone is not mentioed in iPad user agent string
elseif (stripos($userAgent, 'iPad') !== false)
{
$this->mobile = true;
$this->platform = self::IPAD;
}
// In case where iPhone is not mentioed in iPod user agent string
elseif (stripos($userAgent, 'iPod') !== false)
{
$this->mobile = true;
$this->platform = self::IPOD;
}
// This has to come after the iPhone check because mac strings are also present in iOS devices.
elseif (preg_match('/macintosh|mac os x/i', $userAgent))
{
$this->platform = self::MAC;
}
elseif (stripos($userAgent, 'Blackberry') !== false)
{
$this->mobile = true;
$this->platform = self::BLACKBERRY;
}
elseif (stripos($userAgent, 'Android') !== false)
{
$this->mobile = true;
$this->platform = self::ANDROID;
/**
* Attempt to distinguish between Android phones and tablets
* There is no totally foolproof method but certain rules almost always hold
* Android 3.x is only used for tablets
* Some devices and browsers encourage users to change their UA string to include Tablet.
* Google encourages manufacturers to exclude the string Mobile from tablet device UA strings.
* In some modes Kindle Android devices include the string Mobile but they include the string Silk.
*/
if (stripos($userAgent, 'Android 3') !== false || stripos($userAgent, 'Tablet') !== false
|| stripos($userAgent, 'Mobile') === false || stripos($userAgent, 'Silk') !== false )
{
$this->platform = self::ANDROIDTABLET;
}
}
elseif (stripos($userAgent, 'Linux') !== false)
{
$this->platform = self::LINUX;
}
// Mark this detection routine as run.
$this->detection['platform'] = true;
}
/**
* Determines if the browser is a robot or not.
*
* @param string $userAgent The user-agent string to parse.
*
* @return void
*
* @since 12.3
*/
protected function detectRobot($userAgent)
{
if (preg_match('/http|bot|robot|spider|crawler|curl|^$/i', $userAgent))
{
$this->robot = true;
}
else
{
$this->robot = false;
}
$this->detection['robot'] = true;
}
}

View File

@ -0,0 +1 @@
<!DOCTYPE html><title></title>

View File

@ -0,0 +1,153 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Application
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* Class to define an abstract Web application router.
*
* @package Joomla.Platform
* @subpackage Application
* @since 12.2
*/
abstract class JApplicationWebRouter
{
/**
* @var JApplicationWeb The web application on whose behalf we are routing the request.
* @since 12.2
*/
protected $app;
/**
* @var string The default page controller name for an empty route.
* @since 12.2
*/
protected $default;
/**
* @var string Controller class name prefix for creating controller objects by name.
* @since 12.2
*/
protected $controllerPrefix;
/**
* @var JInput An input object from which to derive the route.
* @since 12.2
*/
protected $input;
/**
* Constructor.
*
* @param JApplicationWeb $app The web application on whose behalf we are routing the request.
* @param JInput $input An optional input object from which to derive the route. If none
* is given than the input from the application object will be used.
*
* @since 12.2
*/
public function __construct(JApplicationWeb $app, JInput $input = null)
{
$this->app = $app;
$this->input = ($input === null) ? $this->app->input : $input;
}
/**
* Find and execute the appropriate controller based on a given route.
*
* @param string $route The route string for which to find and execute a controller.
*
* @return mixed The return value of the controller executed
*
* @since 12.2
* @throws InvalidArgumentException
* @throws RuntimeException
*/
public function execute($route)
{
// Get the controller name based on the route patterns and requested route.
$name = $this->parseRoute($route);
// Get the controller object by name.
$controller = $this->fetchController($name);
// Execute the controller.
return $controller->execute();
}
/**
* Set the controller name prefix.
*
* @param string $prefix Controller class name prefix for creating controller objects by name.
*
* @return JApplicationWebRouter This object for method chaining.
*
* @since 12.2
*/
public function setControllerPrefix($prefix)
{
$this->controllerPrefix = (string) $prefix;
return $this;
}
/**
* Set the default controller name.
*
* @param string $name The default page controller name for an empty route.
*
* @return JApplicationWebRouter This object for method chaining.
*
* @since 12.2
*/
public function setDefaultController($name)
{
$this->default = (string) $name;
return $this;
}
/**
* Parse the given route and return the name of a controller mapped to the given route.
*
* @param string $route The route string for which to find and execute a controller.
*
* @return string The controller name for the given route excluding prefix.
*
* @since 12.2
* @throws InvalidArgumentException
*/
abstract protected function parseRoute($route);
/**
* Get a JController object for a given name.
*
* @param string $name The controller name (excluding prefix) for which to fetch and instance.
*
* @return JController
*
* @since 12.2
* @throws RuntimeException
*/
protected function fetchController($name)
{
// Derive the controller class name.
$class = $this->controllerPrefix . ucfirst($name);
// If the controller class does not exist panic.
if (!class_exists($class) || !is_subclass_of($class, 'JController'))
{
throw new RuntimeException(sprintf('Unable to locate controller `%s`.', $class), 404);
}
// Instantiate the controller.
$controller = new $class($this->input, $this->app);
return $controller;
}
}

View File

@ -0,0 +1,177 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Application
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* Basic Web application router class for the Joomla Platform.
*
* @package Joomla.Platform
* @subpackage Application
* @since 12.2
*/
class JApplicationWebRouterBase extends JApplicationWebRouter
{
/**
* @var array An array of rules, each rule being an associative array('regex'=> $regex, 'vars' => $vars, 'controller' => $controller)
* for routing the request.
* @since 12.2
*/
protected $maps = array();
/**
* Add a route map to the router. If the pattern already exists it will be overwritten.
*
* @param string $pattern The route pattern to use for matching.
* @param string $controller The controller name to map to the given pattern.
*
* @return JApplicationWebRouter This object for method chaining.
*
* @since 12.2
*/
public function addMap($pattern, $controller)
{
// Sanitize and explode the pattern.
$pattern = explode('/', trim(parse_url((string) $pattern, PHP_URL_PATH), ' /'));
// Prepare the route variables
$vars = array();
// Initialize regular expression
$regex = array();
// Loop on each segment
foreach ($pattern as $segment)
{
// Match a splat with no variable.
if ($segment == '*')
{
$regex[] = '.*';
}
// Match a splat and capture the data to a named variable.
elseif ($segment[0] == '*')
{
$vars[] = substr($segment, 1);
$regex[] = '(.*)';
}
// Match an escaped splat segment.
elseif ($segment[0] == '\\' && $segment[1] == '*')
{
$regex[] = '\*' . preg_quote(substr($segment, 2));
}
// Match an unnamed variable without capture.
elseif ($segment == ':')
{
$regex[] = '[^/]*';
}
// Match a named variable and capture the data.
elseif ($segment[0] == ':')
{
$vars[] = substr($segment, 1);
$regex[] = '([^/]*)';
}
// Match a segment with an escaped variable character prefix.
elseif ($segment[0] == '\\' && $segment[1] == ':')
{
$regex[] = preg_quote(substr($segment, 1));
}
// Match the standard segment.
else
{
$regex[] = preg_quote($segment);
}
}
$this->maps[] = array(
'regex' => chr(1) . '^' . implode('/', $regex) . '$' . chr(1),
'vars' => $vars,
'controller' => (string) $controller
);
return $this;
}
/**
* Add a route map to the router. If the pattern already exists it will be overwritten.
*
* @param array $maps A list of route maps to add to the router as $pattern => $controller.
*
* @return JApplicationWebRouter This object for method chaining.
*
* @since 12.2
*/
public function addMaps($maps)
{
foreach ($maps as $pattern => $controller)
{
$this->addMap($pattern, $controller);
}
return $this;
}
/**
* Parse the given route and return the name of a controller mapped to the given route.
*
* @param string $route The route string for which to find and execute a controller.
*
* @return string The controller name for the given route excluding prefix.
*
* @since 12.2
* @throws InvalidArgumentException
*/
protected function parseRoute($route)
{
$controller = false;
// Trim the query string off.
$route = preg_replace('/([^?]*).*/u', '\1', $route);
// Sanitize and explode the route.
$route = trim(parse_url($route, PHP_URL_PATH), ' /');
// If the route is empty then simply return the default route. No parsing necessary.
if ($route == '')
{
return $this->default;
}
// Iterate through all of the known route maps looking for a match.
foreach ($this->maps as $rule)
{
if (preg_match($rule['regex'], $route, $matches))
{
// If we have gotten this far then we have a positive match.
$controller = $rule['controller'];
// Time to set the input variables.
// We are only going to set them if they don't already exist to avoid overwriting things.
foreach ($rule['vars'] as $i => $var)
{
$this->input->def($var, $matches[$i + 1]);
// Don't forget to do an explicit set on the GET superglobal.
$this->input->get->def($var, $matches[$i + 1]);
}
$this->input->def('_rawRoute', $route);
break;
}
}
// We were unable to find a route match for the request. Panic.
if (!$controller)
{
throw new InvalidArgumentException(sprintf('Unable to handle request for route `%s`.', $route), 404);
}
return $controller;
}
}

View File

@ -0,0 +1 @@
<!DOCTYPE html><title></title>

View File

@ -0,0 +1,142 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Application
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* RESTful Web application router class for the Joomla Platform.
*
* @package Joomla.Platform
* @subpackage Application
* @since 12.2
*/
class JApplicationWebRouterRest extends JApplicationWebRouterBase
{
/**
* @var boolean A boolean allowing to pass _method as parameter in POST requests
*
* @since 12.2
*/
protected $methodInPostRequest = false;
/**
* @var array An array of HTTP Method => controller suffix pairs for routing the request.
* @since 12.2
*/
protected $suffixMap = array(
'GET' => 'Get',
'POST' => 'Create',
'PUT' => 'Update',
'PATCH' => 'Update',
'DELETE' => 'Delete',
'HEAD' => 'Head',
'OPTIONS' => 'Options'
);
/**
* Find and execute the appropriate controller based on a given route.
*
* @param string $route The route string for which to find and execute a controller.
*
* @return void
*
* @since 12.2
* @throws InvalidArgumentException
* @throws RuntimeException
*/
public function execute($route)
{
// Get the controller name based on the route patterns and requested route.
$name = $this->parseRoute($route);
// Append the HTTP method based suffix.
$name .= $this->fetchControllerSuffix();
// Get the controller object by name.
$controller = $this->fetchController($name);
// Execute the controller.
$controller->execute();
}
/**
* Set a controller class suffix for a given HTTP method.
*
* @param string $method The HTTP method for which to set the class suffix.
* @param string $suffix The class suffix to use when fetching the controller name for a given request.
*
* @return JApplicationWebRouter This object for method chaining.
*
* @since 12.2
*/
public function setHttpMethodSuffix($method, $suffix)
{
$this->suffixMap[strtoupper((string) $method)] = (string) $suffix;
return $this;
}
/**
* Set to allow or not method in POST request
*
* @param boolean $value A boolean to allow or not method in POST request
*
* @return void
*
* @since 12.2
*/
public function setMethodInPostRequest($value)
{
$this->methodInPostRequest = $value;
}
/**
* Get the property to allow or not method in POST request
*
* @return boolean
*
* @since 12.2
*/
public function isMethodInPostRequest()
{
return $this->methodInPostRequest;
}
/**
* Get the controller class suffix string.
*
* @return string
*
* @since 12.2
* @throws RuntimeException
*/
protected function fetchControllerSuffix()
{
// Validate that we have a map to handle the given HTTP method.
if (!isset($this->suffixMap[$this->input->getMethod()]))
{
throw new RuntimeException(sprintf('Unable to support the HTTP method `%s`.', $this->input->getMethod()), 404);
}
// Check if request method is POST
if ( $this->methodInPostRequest == true && strcmp(strtoupper($this->input->server->getMethod()), 'POST') === 0)
{
// Get the method from input
$postMethod = $this->input->get->getWord('_method');
// Validate that we have a map to handle the given HTTP method from input
if ($postMethod && isset($this->suffixMap[strtoupper($postMethod)]))
{
return ucfirst($this->suffixMap[strtoupper($postMethod)]);
}
}
return ucfirst($this->suffixMap[$this->input->getMethod()]);
}
}

View File

@ -0,0 +1,198 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Archive
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
jimport('joomla.filesystem.file');
jimport('joomla.filesystem.folder');
/**
* An Archive handling class
*
* @package Joomla.Platform
* @subpackage Archive
* @since 11.1
*/
class JArchive
{
/**
* @var array The array of instantiated archive adapters.
* @since 12.1
*/
protected static $adapters = array();
/**
* Extract an archive file to a directory.
*
* @param string $archivename The name of the archive file
* @param string $extractdir Directory to unpack into
*
* @return boolean True for success
*
* @since 11.1
* @throws InvalidArgumentException
*/
public static function extract($archivename, $extractdir)
{
$untar = false;
$result = false;
$ext = JFile::getExt(strtolower($archivename));
// Check if a tar is embedded...gzip/bzip2 can just be plain files!
if (JFile::getExt(JFile::stripExt(strtolower($archivename))) == 'tar')
{
$untar = true;
}
switch ($ext)
{
case 'zip':
$adapter = self::getAdapter('zip');
if ($adapter)
{
$result = $adapter->extract($archivename, $extractdir);
}
break;
case 'tar':
$adapter = self::getAdapter('tar');
if ($adapter)
{
$result = $adapter->extract($archivename, $extractdir);
}
break;
case 'tgz':
// This format is a tarball gzip'd
$untar = true;
case 'gz':
case 'gzip':
// This may just be an individual file (e.g. sql script)
$adapter = self::getAdapter('gzip');
if ($adapter)
{
$config = JFactory::getConfig();
$tmpfname = $config->get('tmp_path') . '/' . uniqid('gzip');
$gzresult = $adapter->extract($archivename, $tmpfname);
if ($gzresult instanceof Exception)
{
@unlink($tmpfname);
return false;
}
if ($untar)
{
// Try to untar the file
$tadapter = self::getAdapter('tar');
if ($tadapter)
{
$result = $tadapter->extract($tmpfname, $extractdir);
}
}
else
{
$path = JPath::clean($extractdir);
JFolder::create($path);
$result = JFile::copy($tmpfname, $path . '/' . JFile::stripExt(basename(strtolower($archivename))), null, 1);
}
@unlink($tmpfname);
}
break;
case 'tbz2':
// This format is a tarball bzip2'd
$untar = true;
case 'bz2':
case 'bzip2':
// This may just be an individual file (e.g. sql script)
$adapter = self::getAdapter('bzip2');
if ($adapter)
{
$config = JFactory::getConfig();
$tmpfname = $config->get('tmp_path') . '/' . uniqid('bzip2');
$bzresult = $adapter->extract($archivename, $tmpfname);
if ($bzresult instanceof Exception)
{
@unlink($tmpfname);
return false;
}
if ($untar)
{
// Try to untar the file
$tadapter = self::getAdapter('tar');
if ($tadapter)
{
$result = $tadapter->extract($tmpfname, $extractdir);
}
}
else
{
$path = JPath::clean($extractdir);
JFolder::create($path);
$result = JFile::copy($tmpfname, $path . '/' . JFile::stripExt(basename(strtolower($archivename))), null, 1);
}
@unlink($tmpfname);
}
break;
default:
throw new InvalidArgumentException('Unknown Archive Type');
}
if (!$result || $result instanceof Exception)
{
return false;
}
return true;
}
/**
* Get a file compression adapter.
*
* @param string $type The type of adapter (bzip2|gzip|tar|zip).
*
* @return JArchiveExtractable Adapter for the requested type
*
* @since 11.1
* @throws UnexpectedValueException
*/
public static function getAdapter($type)
{
if (!isset(self::$adapters[$type]))
{
// Try to load the adapter object
$class = 'JArchive' . ucfirst($type);
if (!class_exists($class))
{
throw new UnexpectedValueException('Unable to load archive', 500);
}
self::$adapters[$type] = new $class;
}
return self::$adapters[$type];
}
}

View File

@ -0,0 +1,181 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Archive
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
jimport('joomla.filesystem.stream');
/**
* Bzip2 format adapter for the JArchive class
*
* @package Joomla.Platform
* @subpackage Archive
* @since 11.1
*/
class JArchiveBzip2 implements JArchiveExtractable
{
/**
* Bzip2 file data buffer
*
* @var string
* @since 11.1
*/
private $_data = null;
/**
* Extract a Bzip2 compressed file to a given path
*
* @param string $archive Path to Bzip2 archive to extract
* @param string $destination Path to extract archive to
* @param array $options Extraction options [unused]
*
* @return boolean True if successful
*
* @since 11.1
* @throws RuntimeException
*/
public function extract($archive, $destination, array $options = array ())
{
$this->_data = null;
if (!extension_loaded('bz2'))
{
if (class_exists('JError'))
{
return JError::raiseWarning(100, 'The bz2 extension is not available.');
}
else
{
throw new RuntimeException('The bz2 extension is not available.');
}
}
if (!isset($options['use_streams']) || $options['use_streams'] == false)
{
// Old style: read the whole file and then parse it
$this->_data = file_get_contents($archive);
if (!$this->_data)
{
if (class_exists('JError'))
{
return JError::raiseWarning(100, 'Unable to read archive');
}
else
{
throw new RuntimeException('Unable to read archive');
}
}
$buffer = bzdecompress($this->_data);
unset($this->_data);
if (empty($buffer))
{
if (class_exists('JError'))
{
return JError::raiseWarning(100, 'Unable to decompress data');
}
else
{
throw new RuntimeException('Unable to decompress data');
}
}
if (JFile::write($destination, $buffer) === false)
{
if (class_exists('JError'))
{
return JError::raiseWarning(100, 'Unable to write archive');
}
else
{
throw new RuntimeException('Unable to write archive');
}
}
}
else
{
// New style! streams!
$input = JFactory::getStream();
// Use bzip
$input->set('processingmethod', 'bz');
if (!$input->open($archive))
{
if (class_exists('JError'))
{
return JError::raiseWarning(100, 'Unable to read archive (bz2)');
}
else
{
throw new RuntimeException('Unable to read archive (bz2)');
}
}
$output = JFactory::getStream();
if (!$output->open($destination, 'w'))
{
$input->close();
if (class_exists('JError'))
{
return JError::raiseWarning(100, 'Unable to write archive (bz2)');
}
else
{
throw new RuntimeException('Unable to write archive (bz2)');
}
}
do
{
$this->_data = $input->read($input->get('chunksize', 8196));
if ($this->_data)
{
if (!$output->write($this->_data))
{
$input->close();
if (class_exists('JError'))
{
return JError::raiseWarning(100, 'Unable to write archive (bz2)');
}
else
{
throw new RuntimeException('Unable to write archive (bz2)');
}
}
}
}
while ($this->_data);
$output->close();
$input->close();
}
return true;
}
/**
* Tests whether this adapter can unpack files on this computer.
*
* @return boolean True if supported
*
* @since 11.3
*/
public static function isSupported()
{
return extension_loaded('bz2');
}
}

View File

@ -0,0 +1,42 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Archive
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* Archieve class interface
*
* @package Joomla.Platform
* @subpackage Archive
* @since 12.1
*/
interface JArchiveExtractable
{
/**
* Extract a compressed file to a given path
*
* @param string $archive Path to archive to extract
* @param string $destination Path to extract archive to
* @param array $options Extraction options [may be unused]
*
* @return boolean True if successful
*
* @since 12.1
*/
public function extract($archive, $destination, array $options = array());
/**
* Tests whether this adapter can unpack files on this computer.
*
* @return boolean True if supported
*
* @since 12.1
*/
public static function isSupported();
}

View File

@ -0,0 +1,247 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Archive
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* Gzip format adapter for the JArchive class
*
* This class is inspired from and draws heavily in code and concept from the Compress package of
* The Horde Project <http://www.horde.org>
*
* @contributor Michael Slusarz <slusarz@horde.org>
* @contributor Michael Cochrane <mike@graftonhall.co.nz>
*
* @package Joomla.Platform
* @subpackage Archive
* @since 11.1
*/
class JArchiveGzip implements JArchiveExtractable
{
/**
* Gzip file flags.
*
* @var array
* @since 11.1
*/
private $_flags = array('FTEXT' => 0x01, 'FHCRC' => 0x02, 'FEXTRA' => 0x04, 'FNAME' => 0x08, 'FCOMMENT' => 0x10);
/**
* Gzip file data buffer
*
* @var string
* @since 11.1
*/
private $_data = null;
/**
* Extract a Gzip compressed file to a given path
*
* @param string $archive Path to ZIP archive to extract
* @param string $destination Path to extract archive to
* @param array $options Extraction options [unused]
*
* @return boolean True if successful
*
* @since 11.1
* @throws RuntimeException
*/
public function extract($archive, $destination, array $options = array ())
{
$this->_data = null;
if (!extension_loaded('zlib'))
{
if (class_exists('JError'))
{
return JError::raiseWarning(100, 'The zlib extension is not available.');
}
else
{
throw new RuntimeException('The zlib extension is not available.');
}
}
if (!isset($options['use_streams']) || $options['use_streams'] == false)
{
$this->_data = file_get_contents($archive);
if (!$this->_data)
{
if (class_exists('JError'))
{
return JError::raiseWarning(100, 'Unable to read archive');
}
else
{
throw new RuntimeException('Unable to read archive');
}
}
$position = $this->_getFilePosition();
$buffer = gzinflate(substr($this->_data, $position, strlen($this->_data) - $position));
if (empty($buffer))
{
if (class_exists('JError'))
{
return JError::raiseWarning(100, 'Unable to decompress data');
}
else
{
throw new RuntimeException('Unable to decompress data');
}
}
if (JFile::write($destination, $buffer) === false)
{
if (class_exists('JError'))
{
return JError::raiseWarning(100, 'Unable to write archive');
}
else
{
throw new RuntimeException('Unable to write archive');
}
}
}
else
{
// New style! streams!
$input = JFactory::getStream();
// Use gz
$input->set('processingmethod', 'gz');
if (!$input->open($archive))
{
if (class_exists('JError'))
{
return JError::raiseWarning(100, 'Unable to read archive (gz)');
}
else
{
throw new RuntimeException('Unable to read archive (gz)');
}
}
$output = JFactory::getStream();
if (!$output->open($destination, 'w'))
{
$input->close();
if (class_exists('JError'))
{
return JError::raiseWarning(100, 'Unable to write archive (gz)');
}
else
{
throw new RuntimeException('Unable to write archive (gz)');
}
}
do
{
$this->_data = $input->read($input->get('chunksize', 8196));
if ($this->_data)
{
if (!$output->write($this->_data))
{
$input->close();
if (class_exists('JError'))
{
return JError::raiseWarning(100, 'Unable to write file (gz)');
}
else
{
throw new RuntimeException('Unable to write file (gz)');
}
}
}
}
while ($this->_data);
$output->close();
$input->close();
}
return true;
}
/**
* Tests whether this adapter can unpack files on this computer.
*
* @return boolean True if supported
*
* @since 11.3
*/
public static function isSupported()
{
return extension_loaded('zlib');
}
/**
* Get file data offset for archive
*
* @return integer Data position marker for archive
*
* @since 11.1
* @throws RuntimeException
*/
public function _getFilePosition()
{
// Gzipped file... unpack it first
$position = 0;
$info = @ unpack('CCM/CFLG/VTime/CXFL/COS', substr($this->_data, $position + 2));
if (!$info)
{
if (class_exists('JError'))
{
return JError::raiseWarning(100, 'Unable to decompress data.');
}
else
{
throw new RuntimeException('Unable to decompress data.');
}
}
$position += 10;
if ($info['FLG'] & $this->_flags['FEXTRA'])
{
$XLEN = unpack('vLength', substr($this->_data, $position + 0, 2));
$XLEN = $XLEN['Length'];
$position += $XLEN + 2;
}
if ($info['FLG'] & $this->_flags['FNAME'])
{
$filenamePos = strpos($this->_data, "\x0", $position);
$position = $filenamePos + 1;
}
if ($info['FLG'] & $this->_flags['FCOMMENT'])
{
$commentPos = strpos($this->_data, "\x0", $position);
$position = $commentPos + 1;
}
if ($info['FLG'] & $this->_flags['FHCRC'])
{
$hcrc = unpack('vCRC', substr($this->_data, $position + 0, 2));
$hcrc = $hcrc['CRC'];
$position += 2;
}
return $position;
}
}

View File

@ -0,0 +1 @@
<!DOCTYPE html><title></title>

View File

@ -0,0 +1,221 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Archive
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
jimport('joomla.filesystem.folder');
/**
* Tar format adapter for the JArchive class
*
* This class is inspired from and draws heavily in code and concept from the Compress package of
* The Horde Project <http://www.horde.org>
*
* @contributor Michael Slusarz <slusarz@horde.org>
* @contributor Michael Cochrane <mike@graftonhall.co.nz>
*
* @package Joomla.Platform
* @subpackage Archive
* @since 11.1
*/
class JArchiveTar implements JArchiveExtractable
{
/**
* Tar file types.
*
* @var array
* @since 11.1
*/
private $_types = array(
0x0 => 'Unix file',
0x30 => 'File',
0x31 => 'Link',
0x32 => 'Symbolic link',
0x33 => 'Character special file',
0x34 => 'Block special file',
0x35 => 'Directory',
0x36 => 'FIFO special file',
0x37 => 'Contiguous file');
/**
* Tar file data buffer
*
* @var string
* @since 11.1
*/
private $_data = null;
/**
* Tar file metadata array
*
* @var array
* @since 11.1
*/
private $_metadata = null;
/**
* Extract a ZIP compressed file to a given path
*
* @param string $archive Path to ZIP archive to extract
* @param string $destination Path to extract archive into
* @param array $options Extraction options [unused]
*
* @return boolean True if successful
*
* @throws RuntimeException
* @since 11.1
*/
public function extract($archive, $destination, array $options = array())
{
$this->_data = null;
$this->_metadata = null;
$this->_data = file_get_contents($archive);
if (!$this->_data)
{
if (class_exists('JError'))
{
return JError::raiseWarning(100, 'Unable to read archive');
}
else
{
throw new RuntimeException('Unable to read archive');
}
}
$this->_getTarInfo($this->_data);
for ($i = 0, $n = count($this->_metadata); $i < $n; $i++)
{
$type = strtolower($this->_metadata[$i]['type']);
if ($type == 'file' || $type == 'unix file')
{
$buffer = $this->_metadata[$i]['data'];
$path = JPath::clean($destination . '/' . $this->_metadata[$i]['name']);
// Make sure the destination folder exists
if (!JFolder::create(dirname($path)))
{
if (class_exists('JError'))
{
return JError::raiseWarning(100, 'Unable to create destination');
}
else
{
throw new RuntimeException('Unable to create destination');
}
}
if (JFile::write($path, $buffer) === false)
{
if (class_exists('JError'))
{
return JError::raiseWarning(100, 'Unable to write entry');
}
else
{
throw new RuntimeException('Unable to write entry');
}
}
}
}
return true;
}
/**
* Tests whether this adapter can unpack files on this computer.
*
* @return boolean True if supported
*
* @since 11.3
*/
public static function isSupported()
{
return true;
}
/**
* Get the list of files/data from a Tar archive buffer.
*
* @param string &$data The Tar archive buffer.
*
* @return array Archive metadata array
* <pre>
* KEY: Position in the array
* VALUES: 'attr' -- File attributes
* 'data' -- Raw file contents
* 'date' -- File modification time
* 'name' -- Filename
* 'size' -- Original file size
* 'type' -- File type
* </pre>
*
* @since 11.1
*/
protected function _getTarInfo(& $data)
{
$position = 0;
$return_array = array();
while ($position < strlen($data))
{
$info = @unpack(
"a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/a8checksum/Ctypeflag/a100link/a6magic/a2version/a32uname/a32gname/a8devmajor/a8devminor",
substr($data, $position)
);
if (!$info)
{
if (class_exists('JError'))
{
return JError::raiseWarning(100, 'Unable to decompress data');
}
else
{
throw new RuntimeException('Unable to decompress data');
}
}
$position += 512;
$contents = substr($data, $position, octdec($info['size']));
$position += ceil(octdec($info['size']) / 512) * 512;
if ($info['filename'])
{
$file = array(
'attr' => null,
'data' => null,
'date' => octdec($info['mtime']),
'name' => trim($info['filename']),
'size' => octdec($info['size']),
'type' => isset($this->_types[$info['typeflag']]) ? $this->_types[$info['typeflag']] : null);
if (($info['typeflag'] == 0) || ($info['typeflag'] == 0x30) || ($info['typeflag'] == 0x35))
{
/* File or folder. */
$file['data'] = $contents;
$mode = hexdec(substr($info['mode'], 4, 3));
$file['attr'] = (($info['typeflag'] == 0x35) ? 'd' : '-') . (($mode & 0x400) ? 'r' : '-') . (($mode & 0x200) ? 'w' : '-') .
(($mode & 0x100) ? 'x' : '-') . (($mode & 0x040) ? 'r' : '-') . (($mode & 0x020) ? 'w' : '-') . (($mode & 0x010) ? 'x' : '-') .
(($mode & 0x004) ? 'r' : '-') . (($mode & 0x002) ? 'w' : '-') . (($mode & 0x001) ? 'x' : '-');
}
else
{
/* Some other type. */
}
$return_array[] = $file;
}
}
$this->_metadata = $return_array;
return true;
}
}

View File

@ -0,0 +1,717 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Archive
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
jimport('joomla.filesystem.folder');
/**
* ZIP format adapter for the JArchive class
*
* The ZIP compression code is partially based on code from:
* Eric Mueller <eric@themepark.com>
* http://www.zend.com/codex.php?id=535&single=1
*
* Deins125 <webmaster@atlant.ru>
* http://www.zend.com/codex.php?id=470&single=1
*
* The ZIP compression date code is partially based on code from
* Peter Listiak <mlady@users.sourceforge.net>
*
* This class is inspired from and draws heavily in code and concept from the Compress package of
* The Horde Project <http://www.horde.org>
*
* @contributor Chuck Hagenbuch <chuck@horde.org>
* @contributor Michael Slusarz <slusarz@horde.org>
* @contributor Michael Cochrane <mike@graftonhall.co.nz>
*
* @package Joomla.Platform
* @subpackage Archive
* @since 11.1
*/
class JArchiveZip implements JArchiveExtractable
{
/**
* ZIP compression methods.
*
* @var array
* @since 11.1
*/
private $_methods = array(0x0 => 'None', 0x1 => 'Shrunk', 0x2 => 'Super Fast', 0x3 => 'Fast', 0x4 => 'Normal', 0x5 => 'Maximum', 0x6 => 'Imploded',
0x8 => 'Deflated');
/**
* Beginning of central directory record.
*
* @var string
* @since 11.1
*/
private $_ctrlDirHeader = "\x50\x4b\x01\x02";
/**
* End of central directory record.
*
* @var string
* @since 11.1
*/
private $_ctrlDirEnd = "\x50\x4b\x05\x06\x00\x00\x00\x00";
/**
* Beginning of file contents.
*
* @var string
* @since 11.1
*/
private $_fileHeader = "\x50\x4b\x03\x04";
/**
* ZIP file data buffer
*
* @var string
* @since 11.1
*/
private $_data = null;
/**
* ZIP file metadata array
*
* @var array
* @since 11.1
*/
private $_metadata = null;
/**
* Create a ZIP compressed file from an array of file data.
*
* @param string $archive Path to save archive.
* @param array $files Array of files to add to archive.
*
* @return boolean True if successful.
*
* @since 11.1
*
* @todo Finish Implementation
*/
public function create($archive, $files)
{
$contents = array();
$ctrldir = array();
foreach ($files as $file)
{
$this->_addToZIPFile($file, $contents, $ctrldir);
}
return $this->_createZIPFile($contents, $ctrldir, $archive);
}
/**
* Extract a ZIP compressed file to a given path
*
* @param string $archive Path to ZIP archive to extract
* @param string $destination Path to extract archive into
* @param array $options Extraction options [unused]
*
* @return boolean True if successful
*
* @since 11.1
* @throws RuntimeException
*/
public function extract($archive, $destination, array $options = array())
{
if (!is_file($archive))
{
if (class_exists('JError'))
{
return JError::raiseWarning(100, 'Archive does not exist');
}
else
{
throw new RuntimeException('Archive does not exist');
}
}
if ($this->hasNativeSupport())
{
return $this->extractNative($archive, $destination);
}
else
{
return $this->extractCustom($archive, $destination);
}
}
/**
* Tests whether this adapter can unpack files on this computer.
*
* @return boolean True if supported
*
* @since 11.3
*/
public static function isSupported()
{
return (self::hasNativeSupport() || extension_loaded('zlib'));
}
/**
* Method to determine if the server has native zip support for faster handling
*
* @return boolean True if php has native ZIP support
*
* @since 11.1
*/
public static function hasNativeSupport()
{
return (function_exists('zip_open') && function_exists('zip_read'));
}
/**
* Checks to see if the data is a valid ZIP file.
*
* @param string &$data ZIP archive data buffer.
*
* @return boolean True if valid, false if invalid.
*
* @since 11.1
*/
public function checkZipData(&$data)
{
if (strpos($data, $this->_fileHeader) === false)
{
return false;
}
else
{
return true;
}
}
/**
* Extract a ZIP compressed file to a given path using a php based algorithm that only requires zlib support
*
* @param string $archive Path to ZIP archive to extract.
* @param string $destination Path to extract archive into.
*
* @return mixed True if successful
*
* @since 11.1
* @throws RuntimeException
*/
protected function extractCustom($archive, $destination)
{
$this->_data = null;
$this->_metadata = null;
if (!extension_loaded('zlib'))
{
if (class_exists('JError'))
{
return JError::raiseWarning(100, 'Zlib not supported');
}
else
{
throw new RuntimeException('Zlib not supported');
}
}
$this->_data = file_get_contents($archive);
if (!$this->_data)
{
if (class_exists('JError'))
{
return JError::raiseWarning(100, 'Unable to read archive (zip)');
}
else
{
throw new RuntimeException('Unable to read archive (zip)');
}
}
if (!$this->_readZipInfo($this->_data))
{
if (class_exists('JError'))
{
return JError::raiseWarning(100, 'Get ZIP Information failed');
}
else
{
throw new RuntimeException('Get ZIP Information failed');
}
}
for ($i = 0, $n = count($this->_metadata); $i < $n; $i++)
{
$lastPathCharacter = substr($this->_metadata[$i]['name'], -1, 1);
if ($lastPathCharacter !== '/' && $lastPathCharacter !== '\\')
{
$buffer = $this->_getFileData($i);
$path = JPath::clean($destination . '/' . $this->_metadata[$i]['name']);
// Make sure the destination folder exists
if (!JFolder::create(dirname($path)))
{
if (class_exists('JError'))
{
return JError::raiseWarning(100, 'Unable to create destination');
}
else
{
throw new RuntimeException('Unable to create destination');
}
}
if (JFile::write($path, $buffer) === false)
{
if (class_exists('JError'))
{
return JError::raiseWarning(100, 'Unable to write entry');
}
else
{
throw new RuntimeException('Unable to write entry');
}
}
}
}
return true;
}
/**
* Extract a ZIP compressed file to a given path using native php api calls for speed
*
* @param string $archive Path to ZIP archive to extract
* @param string $destination Path to extract archive into
*
* @return boolean True on success
*
* @since 11.1
* @throws RuntimeException
*/
protected function extractNative($archive, $destination)
{
$zip = zip_open($archive);
if (is_resource($zip))
{
// Make sure the destination folder exists
if (!JFolder::create($destination))
{
if (class_exists('JError'))
{
return JError::raiseWarning(100, 'Unable to create destination');
}
else
{
throw new RuntimeException('Unable to create destination');
}
}
// Read files in the archive
while ($file = @zip_read($zip))
{
if (zip_entry_open($zip, $file, "r"))
{
if (substr(zip_entry_name($file), strlen(zip_entry_name($file)) - 1) != "/")
{
$buffer = zip_entry_read($file, zip_entry_filesize($file));
if (JFile::write($destination . '/' . zip_entry_name($file), $buffer) === false)
{
if (class_exists('JError'))
{
return JError::raiseWarning(100, 'Unable to write entry');
}
else
{
throw new RuntimeException('Unable to write entry');
}
}
zip_entry_close($file);
}
}
else
{
if (class_exists('JError'))
{
return JError::raiseWarning(100, 'Unable to read entry');
}
else
{
throw new RuntimeException('Unable to read entry');
}
}
}
@zip_close($zip);
}
else
{
if (class_exists('JError'))
{
return JError::raiseWarning(100, 'Unable to open archive');
}
else
{
throw new RuntimeException('Unable to open archive');
}
}
return true;
}
/**
* Get the list of files/data from a ZIP archive buffer.
*
* <pre>
* KEY: Position in zipfile
* VALUES: 'attr' -- File attributes
* 'crc' -- CRC checksum
* 'csize' -- Compressed file size
* 'date' -- File modification time
* 'name' -- Filename
* 'method'-- Compression method
* 'size' -- Original file size
* 'type' -- File type
* </pre>
*
* @param string &$data The ZIP archive buffer.
*
* @return boolean True on success
*
* @since 11.1
* @throws RuntimeException
*/
private function _readZipInfo(&$data)
{
$entries = array();
// Find the last central directory header entry
$fhLast = strpos($data, $this->_ctrlDirEnd);
do
{
$last = $fhLast;
}
while (($fhLast = strpos($data, $this->_ctrlDirEnd, $fhLast + 1)) !== false);
// Find the central directory offset
$offset = 0;
if ($last)
{
$endOfCentralDirectory = unpack(
'vNumberOfDisk/vNoOfDiskWithStartOfCentralDirectory/vNoOfCentralDirectoryEntriesOnDisk/' .
'vTotalCentralDirectoryEntries/VSizeOfCentralDirectory/VCentralDirectoryOffset/vCommentLength',
substr($data, $last + 4)
);
$offset = $endOfCentralDirectory['CentralDirectoryOffset'];
}
// Get details from central directory structure.
$fhStart = strpos($data, $this->_ctrlDirHeader, $offset);
$dataLength = strlen($data);
do
{
if ($dataLength < $fhStart + 31)
{
if (class_exists('JError'))
{
return JError::raiseWarning(100, 'Invalid Zip Data');
}
else
{
throw new RuntimeException('Invalid Zip Data');
}
}
$info = unpack('vMethod/VTime/VCRC32/VCompressed/VUncompressed/vLength', substr($data, $fhStart + 10, 20));
$name = substr($data, $fhStart + 46, $info['Length']);
$entries[$name] = array(
'attr' => null,
'crc' => sprintf("%08s", dechex($info['CRC32'])),
'csize' => $info['Compressed'],
'date' => null,
'_dataStart' => null,
'name' => $name,
'method' => $this->_methods[$info['Method']],
'_method' => $info['Method'],
'size' => $info['Uncompressed'],
'type' => null
);
$entries[$name]['date'] = mktime(
(($info['Time'] >> 11) & 0x1f),
(($info['Time'] >> 5) & 0x3f),
(($info['Time'] << 1) & 0x3e),
(($info['Time'] >> 21) & 0x07),
(($info['Time'] >> 16) & 0x1f),
((($info['Time'] >> 25) & 0x7f) + 1980)
);
if ($dataLength < $fhStart + 43)
{
if (class_exists('JError'))
{
return JError::raiseWarning(100, 'Invalid ZIP data');
}
else
{
throw new RuntimeException('Invalid ZIP data');
}
}
$info = unpack('vInternal/VExternal/VOffset', substr($data, $fhStart + 36, 10));
$entries[$name]['type'] = ($info['Internal'] & 0x01) ? 'text' : 'binary';
$entries[$name]['attr'] = (($info['External'] & 0x10) ? 'D' : '-') . (($info['External'] & 0x20) ? 'A' : '-')
. (($info['External'] & 0x03) ? 'S' : '-') . (($info['External'] & 0x02) ? 'H' : '-') . (($info['External'] & 0x01) ? 'R' : '-');
$entries[$name]['offset'] = $info['Offset'];
// Get details from local file header since we have the offset
$lfhStart = strpos($data, $this->_fileHeader, $entries[$name]['offset']);
if ($dataLength < $lfhStart + 34)
{
if (class_exists('JError'))
{
return JError::raiseWarning(100, 'Invalid Zip Data');
}
else
{
throw new RuntimeException('Invalid Zip Data');
}
}
$info = unpack('vMethod/VTime/VCRC32/VCompressed/VUncompressed/vLength/vExtraLength', substr($data, $lfhStart + 8, 25));
$name = substr($data, $lfhStart + 30, $info['Length']);
$entries[$name]['_dataStart'] = $lfhStart + 30 + $info['Length'] + $info['ExtraLength'];
// Bump the max execution time because not using the built in php zip libs makes this process slow.
@set_time_limit(ini_get('max_execution_time'));
}
while ((($fhStart = strpos($data, $this->_ctrlDirHeader, $fhStart + 46)) !== false));
$this->_metadata = array_values($entries);
return true;
}
/**
* Returns the file data for a file by offsest in the ZIP archive
*
* @param integer $key The position of the file in the archive.
*
* @return string Uncompressed file data buffer.
*
* @since 11.1
*/
private function _getFileData($key)
{
if ($this->_metadata[$key]['_method'] == 0x8)
{
return gzinflate(substr($this->_data, $this->_metadata[$key]['_dataStart'], $this->_metadata[$key]['csize']));
}
elseif ($this->_metadata[$key]['_method'] == 0x0)
{
/* Files that aren't compressed. */
return substr($this->_data, $this->_metadata[$key]['_dataStart'], $this->_metadata[$key]['csize']);
}
elseif ($this->_metadata[$key]['_method'] == 0x12)
{
// If bz2 extension is loaded use it
if (extension_loaded('bz2'))
{
return bzdecompress(substr($this->_data, $this->_metadata[$key]['_dataStart'], $this->_metadata[$key]['csize']));
}
}
return '';
}
/**
* Converts a UNIX timestamp to a 4-byte DOS date and time format
* (date in high 2-bytes, time in low 2-bytes allowing magnitude
* comparison).
*
* @param int $unixtime The current UNIX timestamp.
*
* @return int The current date in a 4-byte DOS format.
*
* @since 11.1
*/
protected function _unix2DOSTime($unixtime = null)
{
$timearray = (is_null($unixtime)) ? getdate() : getdate($unixtime);
if ($timearray['year'] < 1980)
{
$timearray['year'] = 1980;
$timearray['mon'] = 1;
$timearray['mday'] = 1;
$timearray['hours'] = 0;
$timearray['minutes'] = 0;
$timearray['seconds'] = 0;
}
return (($timearray['year'] - 1980) << 25) | ($timearray['mon'] << 21) | ($timearray['mday'] << 16) | ($timearray['hours'] << 11) |
($timearray['minutes'] << 5) | ($timearray['seconds'] >> 1);
}
/**
* Adds a "file" to the ZIP archive.
*
* @param array &$file File data array to add
* @param array &$contents An array of existing zipped files.
* @param array &$ctrldir An array of central directory information.
*
* @return void
*
* @since 11.1
*
* @todo Review and finish implementation
*/
private function _addToZIPFile(array &$file, array &$contents, array &$ctrldir)
{
$data = &$file['data'];
$name = str_replace('\\', '/', $file['name']);
/* See if time/date information has been provided. */
$ftime = null;
if (isset($file['time']))
{
$ftime = $file['time'];
}
// Get the hex time.
$dtime = dechex($this->_unix2DosTime($ftime));
$hexdtime = chr(hexdec($dtime[6] . $dtime[7])) . chr(hexdec($dtime[4] . $dtime[5])) . chr(hexdec($dtime[2] . $dtime[3]))
. chr(hexdec($dtime[0] . $dtime[1]));
/* Begin creating the ZIP data. */
$fr = $this->_fileHeader;
/* Version needed to extract. */
$fr .= "\x14\x00";
/* General purpose bit flag. */
$fr .= "\x00\x00";
/* Compression method. */
$fr .= "\x08\x00";
/* Last modification time/date. */
$fr .= $hexdtime;
/* "Local file header" segment. */
$unc_len = strlen($data);
$crc = crc32($data);
$zdata = gzcompress($data);
$zdata = substr(substr($zdata, 0, strlen($zdata) - 4), 2);
$c_len = strlen($zdata);
/* CRC 32 information. */
$fr .= pack('V', $crc);
/* Compressed filesize. */
$fr .= pack('V', $c_len);
/* Uncompressed filesize. */
$fr .= pack('V', $unc_len);
/* Length of filename. */
$fr .= pack('v', strlen($name));
/* Extra field length. */
$fr .= pack('v', 0);
/* File name. */
$fr .= $name;
/* "File data" segment. */
$fr .= $zdata;
/* Add this entry to array. */
$old_offset = strlen(implode('', $contents));
$contents[] = &$fr;
/* Add to central directory record. */
$cdrec = $this->_ctrlDirHeader;
/* Version made by. */
$cdrec .= "\x00\x00";
/* Version needed to extract */
$cdrec .= "\x14\x00";
/* General purpose bit flag */
$cdrec .= "\x00\x00";
/* Compression method */
$cdrec .= "\x08\x00";
/* Last mod time/date. */
$cdrec .= $hexdtime;
/* CRC 32 information. */
$cdrec .= pack('V', $crc);
/* Compressed filesize. */
$cdrec .= pack('V', $c_len);
/* Uncompressed filesize. */
$cdrec .= pack('V', $unc_len);
/* Length of filename. */
$cdrec .= pack('v', strlen($name));
/* Extra field length. */
$cdrec .= pack('v', 0);
/* File comment length. */
$cdrec .= pack('v', 0);
/* Disk number start. */
$cdrec .= pack('v', 0);
/* Internal file attributes. */
$cdrec .= pack('v', 0);
/* External file attributes -'archive' bit set. */
$cdrec .= pack('V', 32);
/* Relative offset of local header. */
$cdrec .= pack('V', $old_offset);
/* File name. */
$cdrec .= $name;
/* Optional extra field, file comment goes here. */
/* Save to central directory array. */
$ctrldir[] = &$cdrec;
}
/**
* Creates the ZIP file.
*
* Official ZIP file format: http://www.pkware.com/appnote.txt
*
* @param array &$contents An array of existing zipped files.
* @param array &$ctrlDir An array of central directory information.
* @param string $path The path to store the archive.
*
* @return boolean True if successful
*
* @since 11.1
*
* @todo Review and finish implementation
*/
private function _createZIPFile(array &$contents, array &$ctrlDir, $path)
{
$data = implode('', $contents);
$dir = implode('', $ctrlDir);
$buffer = $data . $dir . $this->_ctrlDirEnd . /* Total # of entries "on this disk". */
pack('v', count($ctrlDir)) . /* Total # of entries overall. */
pack('v', count($ctrlDir)) . /* Size of central directory. */
pack('V', strlen($dir)) . /* Offset to start of central dir. */
pack('V', strlen($data)) . /* ZIP file comment length. */
"\x00\x00";
if (JFile::write($path, $buffer) === false)
{
return false;
}
else
{
return true;
}
}
}

View File

@ -0,0 +1,196 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Base
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* Adapter Class
* Retains common adapter pattern functions
* Class harvested from joomla.installer.installer
*
* @package Joomla.Platform
* @subpackage Base
* @since 11.1
*/
class JAdapter extends JObject
{
/**
* Associative array of adapters
*
* @var array
* @since 11.1
*/
protected $_adapters = array();
/**
* Adapter Folder
* @var string
* @since 11.1
*/
protected $_adapterfolder = 'adapters';
/**
* @var string Adapter Class Prefix
* @since 11.1
*/
protected $_classprefix = 'J';
/**
* Base Path for the adapter instance
*
* @var string
* @since 11.1
*/
protected $_basepath = null;
/**
* Database Connector Object
*
* @var JDatabaseDriver
* @since 11.1
*/
protected $_db;
/**
* Constructor
*
* @param string $basepath Base Path of the adapters
* @param string $classprefix Class prefix of adapters
* @param string $adapterfolder Name of folder to append to base path
*
* @since 11.1
*/
public function __construct($basepath, $classprefix = null, $adapterfolder = null)
{
$this->_basepath = $basepath;
$this->_classprefix = $classprefix ? $classprefix : 'J';
$this->_adapterfolder = $adapterfolder ? $adapterfolder : 'adapters';
$this->_db = JFactory::getDbo();
}
/**
* Get the database connector object
*
* @return JDatabaseDriver Database connector object
*
* @since 11.1
*/
public function getDBO()
{
return $this->_db;
}
/**
* Set an adapter by name
*
* @param string $name Adapter name
* @param object &$adapter Adapter object
* @param array $options Adapter options
*
* @return boolean True if successful
*
* @since 11.1
*/
public function setAdapter($name, &$adapter = null, $options = array())
{
if (!is_object($adapter))
{
$fullpath = $this->_basepath . '/' . $this->_adapterfolder . '/' . strtolower($name) . '.php';
if (!file_exists($fullpath))
{
return false;
}
// Try to load the adapter object
require_once $fullpath;
$class = $this->_classprefix . ucfirst($name);
if (!class_exists($class))
{
return false;
}
$adapter = new $class($this, $this->_db, $options);
}
$this->_adapters[$name] = &$adapter;
return true;
}
/**
* Return an adapter.
*
* @param string $name Name of adapter to return
* @param array $options Adapter options
*
* @return object Adapter of type 'name' or false
*
* @since 11.1
*/
public function getAdapter($name, $options = array())
{
if (!array_key_exists($name, $this->_adapters))
{
if (!$this->setAdapter($name, $options))
{
$false = false;
return $false;
}
}
return $this->_adapters[$name];
}
/**
* Loads all adapters.
*
* @param array $options Adapter options
*
* @return void
*
* @since 11.1
*/
public function loadAllAdapters($options = array())
{
$files = new DirectoryIterator($this->_basepath . '/' . $this->_adapterfolder);
foreach ($files as $file)
{
$fileName = $file->getFilename();
// Only load for php files.
// Note: DirectoryIterator::getExtension only available PHP >= 5.3.6
if (!$file->isFile() || substr($fileName, strrpos($fileName, '.') + 1) != 'php')
{
continue;
}
// Try to load the adapter object
require_once $this->_basepath . '/' . $this->_adapterfolder . '/' . $fileName;
// Derive the class name from the filename.
$name = str_ireplace('.php', '', ucfirst(trim($fileName)));
$class = $this->_classprefix . ucfirst($name);
if (!class_exists($class))
{
// Skip to next one
continue;
}
$adapter = new $class($this, $this->_db, $options);
$this->_adapters[$name] = clone $adapter;
}
}
}

View File

@ -0,0 +1,69 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Base
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* Adapter Instance Class
*
* @package Joomla.Platform
* @subpackage Base
* @since 11.1
*/
class JAdapterInstance extends JObject
{
/**
* Parent
*
* @var JAdapter
* @since 11.1
*/
protected $parent = null;
/**
* Database
*
* @var JDatabaseDriver
* @since 11.1
*/
protected $db = null;
/**
* Constructor
*
* @param JAdapter $parent Parent object
* @param JDatabaseDriver $db Database object
* @param array $options Configuration Options
*
* @since 11.1
*/
public function __construct(JAdapter $parent, JDatabaseDriver $db, array $options = array())
{
// Set the properties from the options array that is passed in
$this->setProperties($options);
// Set the parent and db in case $options for some reason overrides it.
$this->parent = $parent;
// Pull in the global dbo in case something happened to it.
$this->db = $db ?: JFactory::getDbo();
}
/**
* Retrieves the parent object
*
* @return JAdapter parent
*
* @since 11.1
*/
public function getParent()
{
return $this->parent;
}
}

View File

@ -0,0 +1 @@
<!DOCTYPE html><title></title>

726
libraries/joomla/cache/cache.php vendored Normal file
View File

@ -0,0 +1,726 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Cache
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* Joomla! Cache base object
*
* @package Joomla.Platform
* @subpackage Cache
* @since 11.1
*/
class JCache
{
/**
* @var object Storage handler
* @since 11.1
*/
public static $_handler = array();
/**
* @var array Options
* @since 11.1
*/
public $_options;
/**
* Constructor
*
* @param array $options options
*
* @since 11.1
*/
public function __construct($options)
{
$conf = JFactory::getConfig();
$this->_options = array(
'cachebase' => $conf->get('cache_path', JPATH_CACHE),
'lifetime' => (int) $conf->get('cachetime'),
'language' => $conf->get('language', 'en-GB'),
'storage' => $conf->get('cache_handler', ''),
'defaultgroup' => 'default',
'locking' => true,
'locktime' => 15,
'checkTime' => true,
'caching' => ($conf->get('caching') >= 1) ? true : false);
// Overwrite default options with given options
foreach ($options as $option => $value)
{
if (isset($options[$option]) && $options[$option] !== '')
{
$this->_options[$option] = $options[$option];
}
}
if (empty($this->_options['storage']))
{
$this->_options['caching'] = false;
}
}
/**
* Returns a reference to a cache adapter object, always creating it
*
* @param string $type The cache object type to instantiate
* @param array $options The array of options
*
* @return JCache A JCache object
*
* @since 11.1
*/
public static function getInstance($type = 'output', $options = array())
{
return JCacheController::getInstance($type, $options);
}
/**
* Get the storage handlers
*
* @return array An array of available storage handlers
*
* @since 11.1
*/
public static function getStores()
{
$handlers = array();
// Get an iterator and loop trough the driver classes.
$iterator = new DirectoryIterator(__DIR__ . '/storage');
foreach ($iterator as $file)
{
$fileName = $file->getFilename();
// Only load for php files.
// Note: DirectoryIterator::getExtension only available PHP >= 5.3.6
if (!$file->isFile()
|| substr($fileName, strrpos($fileName, '.') + 1) != 'php'
|| $fileName == 'helper.php')
{
continue;
}
// Derive the class name from the type.
$class = str_ireplace('.php', '', 'JCacheStorage' . ucfirst(trim($fileName)));
// If the class doesn't exist we have nothing left to do but look at the next type. We did our best.
if (!class_exists($class))
{
continue;
}
// Sweet! Our class exists, so now we just need to know if it passes its test method.
if ($class::isSupported())
{
// Connector names should not have file extensions.
$handlers[] = str_ireplace('.php', '', $fileName);
}
}
return $handlers;
}
/**
* Set caching enabled state
*
* @param boolean $enabled True to enable caching
*
* @return void
*
* @since 11.1
*/
public function setCaching($enabled)
{
$this->_options['caching'] = $enabled;
}
/**
* Get caching state
*
* @return boolean Caching state
*
* @since 11.1
*/
public function getCaching()
{
return $this->_options['caching'];
}
/**
* Set cache lifetime
*
* @param integer $lt Cache lifetime
*
* @return void
*
* @since 11.1
*/
public function setLifeTime($lt)
{
$this->_options['lifetime'] = $lt;
}
/**
* Get cached data by id and group
*
* @param string $id The cache data id
* @param string $group The cache data group
*
* @return mixed boolean False on failure or a cached data string
*
* @since 11.1
*/
public function get($id, $group = null)
{
// Get the default group
$group = ($group) ? $group : $this->_options['defaultgroup'];
// Get the storage
$handler = $this->_getStorage();
if (!($handler instanceof Exception) && $this->_options['caching'])
{
return $handler->get($id, $group, $this->_options['checkTime']);
}
return false;
}
/**
* Get a list of all cached data
*
* @return mixed Boolean false on failure or an object with a list of cache groups and data
*
* @since 11.1
*/
public function getAll()
{
// Get the storage
$handler = $this->_getStorage();
if (!($handler instanceof Exception) && $this->_options['caching'])
{
return $handler->getAll();
}
return false;
}
/**
* Store the cached data by id and group
*
* @param mixed $data The data to store
* @param string $id The cache data id
* @param string $group The cache data group
*
* @return boolean True if cache stored
*
* @since 11.1
*/
public function store($data, $id, $group = null)
{
// Get the default group
$group = ($group) ? $group : $this->_options['defaultgroup'];
// Get the storage and store the cached data
$handler = $this->_getStorage();
if (!($handler instanceof Exception) && $this->_options['caching'])
{
$handler->_lifetime = $this->_options['lifetime'];
return $handler->store($id, $group, $data);
}
return false;
}
/**
* Remove a cached data entry by id and group
*
* @param string $id The cache data id
* @param string $group The cache data group
*
* @return boolean True on success, false otherwise
*
* @since 11.1
*/
public function remove($id, $group = null)
{
// Get the default group
$group = ($group) ? $group : $this->_options['defaultgroup'];
// Get the storage
$handler = $this->_getStorage();
if (!($handler instanceof Exception))
{
return $handler->remove($id, $group);
}
return false;
}
/**
* Clean cache for a group given a mode.
*
* group mode : cleans all cache in the group
* notgroup mode : cleans all cache not in the group
*
* @param string $group The cache data group
* @param string $mode The mode for cleaning cache [group|notgroup]
*
* @return boolean True on success, false otherwise
*
* @since 11.1
*/
public function clean($group = null, $mode = 'group')
{
// Get the default group
$group = ($group) ? $group : $this->_options['defaultgroup'];
// Get the storage handler
$handler = $this->_getStorage();
if (!($handler instanceof Exception))
{
return $handler->clean($group, $mode);
}
return false;
}
/**
* Garbage collect expired cache data
*
* @return boolean True on success, false otherwise.
*
* @since 11.1
*/
public function gc()
{
// Get the storage handler
$handler = $this->_getStorage();
if (!($handler instanceof Exception))
{
return $handler->gc();
}
return false;
}
/**
* Set lock flag on cached item
*
* @param string $id The cache data id
* @param string $group The cache data group
* @param string $locktime The default locktime for locking the cache.
*
* @return object Properties are lock and locklooped
*
* @since 11.1
*/
public function lock($id, $group = null, $locktime = null)
{
$returning = new stdClass;
$returning->locklooped = false;
// Get the default group
$group = ($group) ? $group : $this->_options['defaultgroup'];
// Get the default locktime
$locktime = ($locktime) ? $locktime : $this->_options['locktime'];
// Allow storage handlers to perform locking on their own
// NOTE drivers with lock need also unlock or unlocking will fail because of false $id
$handler = $this->_getStorage();
if (!($handler instanceof Exception) && $this->_options['locking'] == true && $this->_options['caching'] == true)
{
$locked = $handler->lock($id, $group, $locktime);
if ($locked !== false)
{
return $locked;
}
}
// Fallback
$curentlifetime = $this->_options['lifetime'];
// Set lifetime to locktime for storing in children
$this->_options['lifetime'] = $locktime;
$looptime = $locktime * 10;
$id2 = $id . '_lock';
if ($this->_options['locking'] == true && $this->_options['caching'] == true)
{
$data_lock = $this->get($id2, $group);
}
else
{
$data_lock = false;
$returning->locked = false;
}
if ($data_lock !== false)
{
$lock_counter = 0;
// Loop until you find that the lock has been released.
// That implies that data get from other thread has finished
while ($data_lock !== false)
{
if ($lock_counter > $looptime)
{
$returning->locked = false;
$returning->locklooped = true;
break;
}
usleep(100);
$data_lock = $this->get($id2, $group);
$lock_counter++;
}
}
if ($this->_options['locking'] == true && $this->_options['caching'] == true)
{
$returning->locked = $this->store(1, $id2, $group);
}
// Revert lifetime to previous one
$this->_options['lifetime'] = $curentlifetime;
return $returning;
}
/**
* Unset lock flag on cached item
*
* @param string $id The cache data id
* @param string $group The cache data group
*
* @return boolean True on success, false otherwise.
*
* @since 11.1
*/
public function unlock($id, $group = null)
{
$unlock = false;
// Get the default group
$group = ($group) ? $group : $this->_options['defaultgroup'];
// Allow handlers to perform unlocking on their own
$handler = $this->_getStorage();
if (!($handler instanceof Exception) && $this->_options['caching'])
{
$unlocked = $handler->unlock($id, $group);
if ($unlocked !== false)
{
return $unlocked;
}
}
// Fallback
if ($this->_options['caching'])
{
$unlock = $this->remove($id . '_lock', $group);
}
return $unlock;
}
/**
* Get the cache storage handler
*
* @return JCacheStorage A JCacheStorage object
*
* @since 11.1
*/
public function &_getStorage()
{
$hash = md5(serialize($this->_options));
if (isset(self::$_handler[$hash]))
{
return self::$_handler[$hash];
}
self::$_handler[$hash] = JCacheStorage::getInstance($this->_options['storage'], $this->_options);
return self::$_handler[$hash];
}
/**
* Perform workarounds on retrieved cached data
*
* @param string $data Cached data
* @param array $options Array of options
*
* @return string Body of cached data
*
* @since 11.1
*/
public static function getWorkarounds($data, $options = array())
{
$app = JFactory::getApplication();
$document = JFactory::getDocument();
$body = null;
// Get the document head out of the cache.
if (isset($options['mergehead']) && $options['mergehead'] == 1 && isset($data['head']) && !empty($data['head']))
{
$document->mergeHeadData($data['head']);
}
elseif (isset($data['head']) && method_exists($document, 'setHeadData'))
{
$document->setHeadData($data['head']);
}
// If the pathway buffer is set in the cache data, get it.
if (isset($data['pathway']) && is_array($data['pathway']))
{
// Push the pathway data into the pathway object.
$pathway = $app->getPathWay();
$pathway->setPathway($data['pathway']);
}
// @todo check if the following is needed, seems like it should be in page cache
// If a module buffer is set in the cache data, get it.
if (isset($data['module']) && is_array($data['module']))
{
// Iterate through the module positions and push them into the document buffer.
foreach ($data['module'] as $name => $contents)
{
$document->setBuffer($contents, 'module', $name);
}
}
// Set cached headers.
if (isset($data['headers']) && $data['headers'])
{
foreach($data['headers'] as $header)
{
JResponse::setHeader($header['name'], $header['value']);
}
}
// The following code searches for a token in the cached page and replaces it with the
// proper token.
if (isset($data['body']))
{
$token = JSession::getFormToken();
$search = '#<input type="hidden" name="[0-9a-f]{32}" value="1" />#';
$replacement = '<input type="hidden" name="' . $token . '" value="1" />';
$data['body'] = preg_replace($search, $replacement, $data['body']);
$body = $data['body'];
}
// Get the document body out of the cache.
return $body;
}
/**
* Create workarounded data to be cached
*
* @param string $data Cached data
* @param array $options Array of options
*
* @return string Data to be cached
*
* @since 11.1
*/
public static function setWorkarounds($data, $options = array())
{
$loptions = array(
'nopathway' => 0,
'nohead' => 0,
'nomodules' => 0,
'modulemode' => 0,
);
if (isset($options['nopathway']))
{
$loptions['nopathway'] = $options['nopathway'];
}
if (isset($options['nohead']))
{
$loptions['nohead'] = $options['nohead'];
}
if (isset($options['nomodules']))
{
$loptions['nomodules'] = $options['nomodules'];
}
if (isset($options['modulemode']))
{
$loptions['modulemode'] = $options['modulemode'];
}
$app = JFactory::getApplication();
$document = JFactory::getDocument();
if ($loptions['nomodules'] != 1)
{
// Get the modules buffer before component execution.
$buffer1 = $document->getBuffer();
if (!is_array($buffer1))
{
$buffer1 = array();
}
// Make sure the module buffer is an array.
if (!isset($buffer1['module']) || !is_array($buffer1['module']))
{
$buffer1['module'] = array();
}
}
// View body data
$cached['body'] = $data;
// Document head data
if ($loptions['nohead'] != 1 && method_exists($document, 'getHeadData'))
{
if ($loptions['modulemode'] == 1)
{
$headnow = $document->getHeadData();
$unset = array('title', 'description', 'link', 'links', 'metaTags');
foreach ($unset as $un)
{
unset($headnow[$un]);
unset($options['headerbefore'][$un]);
}
$cached['head'] = array();
// Only store what this module has added
foreach ($headnow as $now => $value)
{
if (isset($options['headerbefore'][$now]))
{
// We have to serialize the content of the arrays because the may contain other arrays which is a notice in PHP 5.4 and newer
$nowvalue = array_map('serialize', $headnow[$now]);
$beforevalue = array_map('serialize', $options['headerbefore'][$now]);
$newvalue = array_diff_assoc($nowvalue, $beforevalue);
$newvalue = array_map('unserialize', $newvalue);
}
else
{
$newvalue = $headnow[$now];
}
if (!empty($newvalue))
{
$cached['head'][$now] = $newvalue;
}
}
}
else
{
$cached['head'] = $document->getHeadData();
}
}
// Pathway data
if ($app->isSite() && $loptions['nopathway'] != 1)
{
$pathway = $app->getPathWay();
$cached['pathway'] = isset($data['pathway']) ? $data['pathway'] : $pathway->getPathway();
}
if ($loptions['nomodules'] != 1)
{
// @todo Check if the following is needed, seems like it should be in page cache
// Get the module buffer after component execution.
$buffer2 = $document->getBuffer();
if (!is_array($buffer2))
{
$buffer2 = array();
}
// Make sure the module buffer is an array.
if (!isset($buffer2['module']) || !is_array($buffer2['module']))
{
$buffer2['module'] = array();
}
// Compare the second module buffer against the first buffer.
$cached['module'] = array_diff_assoc($buffer2['module'], $buffer1['module']);
}
// Headers data
if (isset($options['headers']) && $options['headers'])
{
$cached['headers'] = JResponse::getHeaders();
}
return $cached;
}
/**
* Create safe id for cached data from url parameters set by plugins and framework
*
* @return string md5 encoded cacheid
*
* @since 11.1
*/
public static function makeId()
{
$app = JFactory::getApplication();
// Get url parameters set by plugins
if (!empty($app->registeredurlparams))
{
$registeredurlparams = $app->registeredurlparams;
}
// Platform defaults
$registeredurlparams->format = 'WORD';
$registeredurlparams->option = 'WORD';
$registeredurlparams->view = 'WORD';
$registeredurlparams->layout = 'WORD';
$registeredurlparams->tpl = 'CMD';
$registeredurlparams->id = 'INT';
$safeuriaddon = new stdClass;
foreach ($registeredurlparams as $key => $value)
{
$safeuriaddon->$key = $app->input->get($key, null, $value);
}
return md5(serialize($safeuriaddon));
}
/**
* Add a directory where JCache should search for handlers. You may
* either pass a string or an array of directories.
*
* @param string $path A path to search.
*
* @return array An array with directory elements
*
* @since 11.1
*/
public static function addIncludePath($path = '')
{
static $paths;
if (!isset($paths))
{
$paths = array();
}
if (!empty($path) && !in_array($path, $paths))
{
jimport('joomla.filesystem.path');
array_unshift($paths, JPath::clean($path));
}
return $paths;
}
}

235
libraries/joomla/cache/controller.php vendored Normal file
View File

@ -0,0 +1,235 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Cache
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* Public cache handler
*
* @package Joomla.Platform
* @subpackage Cache
* @since 11.1
*/
class JCacheController
{
/**
* @var JCache
* @since 11.1
*/
public $cache;
/**
* @var array Array of options
* @since 11.1
*/
public $options;
/**
* Constructor
*
* @param array $options Array of options
*
* @since 11.1
*/
public function __construct($options)
{
$this->cache = new JCache($options);
$this->options = & $this->cache->_options;
// Overwrite default options with given options
foreach ($options as $option => $value)
{
if (isset($options[$option]))
{
$this->options[$option] = $options[$option];
}
}
}
/**
* Magic method to proxy JCacheControllerMethods
*
* @param string $name Name of the function
* @param array $arguments Array of arguments for the function
*
* @return mixed
*
* @since 11.1
*/
public function __call($name, $arguments)
{
$nazaj = call_user_func_array(array($this->cache, $name), $arguments);
return $nazaj;
}
/**
* Returns a reference to a cache adapter object, always creating it
*
* @param string $type The cache object type to instantiate; default is output.
* @param array $options Array of options
*
* @return JCache A JCache object
*
* @since 11.1
* @throws RuntimeException
*/
public static function getInstance($type = 'output', $options = array())
{
self::addIncludePath(JPATH_PLATFORM . '/joomla/cache/controller');
$type = strtolower(preg_replace('/[^A-Z0-9_\.-]/i', '', $type));
$class = 'JCacheController' . ucfirst($type);
if (!class_exists($class))
{
// Search for the class file in the JCache include paths.
jimport('joomla.filesystem.path');
if ($path = JPath::find(self::addIncludePath(), strtolower($type) . '.php'))
{
include_once $path;
}
else
{
throw new RuntimeException('Unable to load Cache Controller: ' . $type, 500);
}
}
return new $class($options);
}
/**
* Set caching enabled state
*
* @param boolean $enabled True to enable caching
*
* @return void
*
* @since 11.1
*/
public function setCaching($enabled)
{
$this->cache->setCaching($enabled);
}
/**
* Set cache lifetime
*
* @param integer $lt Cache lifetime
*
* @return void
*
* @since 11.1
*/
public function setLifeTime($lt)
{
$this->cache->setLifeTime($lt);
}
/**
* Add a directory where JCache should search for controllers. You may
* either pass a string or an array of directories.
*
* @param string $path A path to search.
*
* @return array An array with directory elements
*
* @since 11.1
*/
public static function addIncludePath($path = '')
{
static $paths;
if (!isset($paths))
{
$paths = array();
}
if (!empty($path) && !in_array($path, $paths))
{
jimport('joomla.filesystem.path');
array_unshift($paths, JPath::clean($path));
}
return $paths;
}
/**
* Get stored cached data by id and group
*
* @param string $id The cache data id
* @param string $group The cache data group
*
* @return mixed False on no result, cached object otherwise
*
* @since 11.1
*/
public function get($id, $group = null)
{
$data = $this->cache->get($id, $group);
if ($data === false)
{
$locktest = new stdClass;
$locktest->locked = null;
$locktest->locklooped = null;
$locktest = $this->cache->lock($id, $group);
if ($locktest->locked == true && $locktest->locklooped == true)
{
$data = $this->cache->get($id, $group);
}
if ($locktest->locked == true)
{
$this->cache->unlock($id, $group);
}
}
// Check again because we might get it from second attempt
if ($data !== false)
{
// Trim to fix unserialize errors
$data = unserialize(trim($data));
}
return $data;
}
/**
* Store data to cache by id and group
*
* @param mixed $data The data to store
* @param string $id The cache data id
* @param string $group The cache data group
* @param boolean $wrkarounds True to use wrkarounds
*
* @return boolean True if cache stored
*
* @since 11.1
*/
public function store($data, $id, $group = null, $wrkarounds = true)
{
$locktest = new stdClass;
$locktest->locked = null;
$locktest->locklooped = null;
$locktest = $this->cache->lock($id, $group);
if ($locktest->locked == false && $locktest->locklooped == true)
{
$locktest = $this->cache->lock($id, $group);
}
$sucess = $this->cache->store(serialize($data), $id, $group);
if ($locktest->locked == true)
{
$this->cache->unlock($id, $group);
}
return $sucess;
}
}

View File

@ -0,0 +1,205 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Cache
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* Joomla! Cache callback type object
*
* @package Joomla.Platform
* @subpackage Cache
* @since 11.1
*/
class JCacheControllerCallback extends JCacheController
{
/**
* Executes a cacheable callback if not found in cache else returns cached output and result
*
* Since arguments to this function are read with func_get_args you can pass any number of
* arguments to this method
* as long as the first argument passed is the callback definition.
*
* The callback definition can be in several forms:
* - Standard PHP Callback array see <http://php.net/callback> [recommended]
* - Function name as a string eg. 'foo' for function foo()
* - Static method name as a string eg. 'MyClass::myMethod' for method myMethod() of class MyClass
*
* @return mixed Result of the callback
*
* @since 11.1
*/
public function call()
{
// Get callback and arguments
$args = func_get_args();
$callback = array_shift($args);
return $this->get($callback, $args);
}
/**
* Executes a cacheable callback if not found in cache else returns cached output and result
*
* @param mixed $callback Callback or string shorthand for a callback
* @param array $args Callback arguments
* @param string $id Cache id
* @param boolean $wrkarounds True to use wrkarounds
* @param array $woptions Workaround options
*
* @return mixed Result of the callback
*
* @since 11.1
*/
public function get($callback, $args = array(), $id = false, $wrkarounds = false, $woptions = array())
{
// Normalize callback
if (is_array($callback))
{
// We have a standard php callback array -- do nothing
}
elseif (strstr($callback, '::'))
{
// This is shorthand for a static method callback classname::methodname
list ($class, $method) = explode('::', $callback);
$callback = array(trim($class), trim($method));
}
elseif (strstr($callback, '->'))
{
/*
* This is a really not so smart way of doing this... we provide this for backward compatability but this
* WILL! disappear in a future version. If you are using this syntax change your code to use the standard
* PHP callback array syntax: <http://php.net/callback>
*
* We have to use some silly global notation to pull it off and this is very unreliable
*/
list ($object_123456789, $method) = explode('->', $callback);
global $$object_123456789;
$callback = array($$object_123456789, $method);
}
else
{
// We have just a standard function -- do nothing
}
if (!$id)
{
// Generate an ID
$id = $this->_makeId($callback, $args);
}
$data = $this->cache->get($id);
$locktest = new stdClass;
$locktest->locked = null;
$locktest->locklooped = null;
if ($data === false)
{
$locktest = $this->cache->lock($id);
if ($locktest->locked == true && $locktest->locklooped == true)
{
$data = $this->cache->get($id);
}
}
$coptions = array();
if ($data !== false)
{
$cached = unserialize(trim($data));
$coptions['mergehead'] = isset($woptions['mergehead']) ? $woptions['mergehead'] : 0;
$output = ($wrkarounds == false) ? $cached['output'] : JCache::getWorkarounds($cached['output'], $coptions);
$result = $cached['result'];
if ($locktest->locked == true)
{
$this->cache->unlock($id);
}
}
else
{
if (!is_array($args))
{
$Args = !empty($args) ? array(&$args) : array();
}
else
{
$Args = &$args;
}
if ($locktest->locked == false)
{
$locktest = $this->cache->lock($id);
}
if (isset($woptions['modulemode']) && $woptions['modulemode'] == 1)
{
$document = JFactory::getDocument();
$coptions['modulemode'] = 1;
$coptions['headerbefore'] = $document->getHeadData();
}
else
{
$coptions['modulemode'] = 0;
}
ob_start();
ob_implicit_flush(false);
$result = call_user_func_array($callback, $Args);
$output = ob_get_contents();
ob_end_clean();
$cached = array();
$coptions['nopathway'] = isset($woptions['nopathway']) ? $woptions['nopathway'] : 1;
$coptions['nohead'] = isset($woptions['nohead']) ? $woptions['nohead'] : 1;
$coptions['nomodules'] = isset($woptions['nomodules']) ? $woptions['nomodules'] : 1;
$cached['output'] = ($wrkarounds == false) ? $output : JCache::setWorkarounds($output, $coptions);
$cached['result'] = $result;
// Store the cache data
$this->cache->store(serialize($cached), $id);
if ($locktest->locked == true)
{
$this->cache->unlock($id);
}
}
echo $output;
return $result;
}
/**
* Generate a callback cache id
*
* @param callback $callback Callback to cache
* @param array $args Arguments to the callback method to cache
*
* @return string MD5 Hash : function cache id
*
* @since 11.1
*/
protected function _makeId($callback, $args)
{
if (is_array($callback) && is_object($callback[0]))
{
$vars = get_object_vars($callback[0]);
$vars[] = strtolower(get_class($callback[0]));
$callback[0] = $vars;
}
return md5(serialize(array($callback, $args)));
}
}

View File

@ -0,0 +1 @@
<!DOCTYPE html><title></title>

View File

@ -0,0 +1,122 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Cache
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* Joomla Cache output type object
*
* @package Joomla.Platform
* @subpackage Cache
* @since 11.1
*/
class JCacheControllerOutput extends JCacheController
{
/**
* @since 11.1
*/
protected $_id;
/**
* @since 11.1
*/
protected $_group;
/**
* @since 11.1
*/
protected $_locktest = null;
/**
* Start the cache
*
* @param string $id The cache data id
* @param string $group The cache data group
*
* @return boolean True if the cache is hit (false else)
*
* @since 11.1
*/
public function start($id, $group = null)
{
// If we have data in cache use that.
$data = $this->cache->get($id, $group);
$this->_locktest = new stdClass;
$this->_locktest->locked = null;
$this->_locktest->locklooped = null;
if ($data === false)
{
$this->_locktest = $this->cache->lock($id, $group);
if ($this->_locktest->locked == true && $this->_locktest->locklooped == true)
{
$data = $this->cache->get($id, $group);
}
}
if ($data !== false)
{
$data = unserialize(trim($data));
echo $data;
if ($this->_locktest->locked == true)
{
$this->cache->unlock($id, $group);
}
return true;
}
else
{
// Nothing in cache... let's start the output buffer and start collecting data for next time.
if ($this->_locktest->locked == false)
{
$this->_locktest = $this->cache->lock($id, $group);
}
ob_start();
ob_implicit_flush(false);
// Set id and group placeholders
$this->_id = $id;
$this->_group = $group;
return false;
}
}
/**
* Stop the cache buffer and store the cached data
*
* @return boolean True if cache stored
*
* @since 11.1
*/
public function end()
{
// Get data from output buffer and echo it
$data = ob_get_contents();
ob_end_clean();
echo $data;
// Get id and group and reset them placeholders
$id = $this->_id;
$group = $this->_group;
$this->_id = null;
$this->_group = null;
// Get the storage handler and store the cached data
$ret = $this->cache->store(serialize($data), $id, $group);
if ($this->_locktest->locked == true)
{
$this->cache->unlock($id, $group);
}
return $ret;
}
}

View File

@ -0,0 +1,211 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Cache
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* Joomla! Cache page type object
*
* @package Joomla.Platform
* @subpackage Cache
* @since 11.1
*/
class JCacheControllerPage extends JCacheController
{
/**
* @var integer ID property for the cache page object.
* @since 11.1
*/
protected $_id;
/**
* @var string Cache group
* @since 11.1
*/
protected $_group;
/**
* @var object Cache lock test
* @since 11.1
*/
protected $_locktest = null;
/**
* Get the cached page data
*
* @param string $id The cache data id
* @param string $group The cache data group
*
* @return boolean True if the cache is hit (false else)
*
* @since 11.1
*/
public function get($id = false, $group = 'page')
{
// If an id is not given, generate it from the request
if ($id == false)
{
$id = $this->_makeId();
}
// If the etag matches the page id ... set a no change header and exit : utilize browser cache
if (!headers_sent() && isset($_SERVER['HTTP_IF_NONE_MATCH']))
{
$etag = stripslashes($_SERVER['HTTP_IF_NONE_MATCH']);
if ($etag == $id)
{
$browserCache = isset($this->options['browsercache']) ? $this->options['browsercache'] : false;
if ($browserCache)
{
$this->_noChange();
}
}
}
// We got a cache hit... set the etag header and echo the page data
$data = $this->cache->get($id, $group);
$this->_locktest = new stdClass;
$this->_locktest->locked = null;
$this->_locktest->locklooped = null;
if ($data === false)
{
$this->_locktest = $this->cache->lock($id, $group);
if ($this->_locktest->locked == true && $this->_locktest->locklooped == true)
{
$data = $this->cache->get($id, $group);
}
}
if ($data !== false)
{
$data = unserialize(trim($data));
$data = JCache::getWorkarounds($data);
$this->_setEtag($id);
if ($this->_locktest->locked == true)
{
$this->cache->unlock($id, $group);
}
return $data;
}
// Set id and group placeholders
$this->_id = $id;
$this->_group = $group;
return false;
}
/**
* Stop the cache buffer and store the cached data
*
* @param mixed $data The data to store
* @param string $id The cache data id
* @param string $group The cache data group
* @param boolean $wrkarounds True to use wrkarounds
*
* @return boolean True if cache stored
*
* @since 11.1
*/
public function store($data, $id, $group = null, $wrkarounds = true)
{
// Get page data from JResponse
if (empty($data))
{
$data = JResponse::getBody();
}
// Get id and group and reset the placeholders
if (empty($id))
{
$id = $this->_id;
}
if (empty($group))
{
$group = $this->_group;
}
// Only attempt to store if page data exists
if ($data)
{
if ($wrkarounds) {
$data = JCache::setWorkarounds($data, array(
'nopathway' => 1,
'nohead' => 1,
'nomodules' => 1,
'headers' => true
));
}
if ($this->_locktest->locked == false)
{
$this->_locktest = $this->cache->lock($id, $group);
}
$sucess = $this->cache->store(serialize($data), $id, $group);
if ($this->_locktest->locked == true)
{
$this->cache->unlock($id, $group);
}
return $sucess;
}
return false;
}
/**
* Generate a page cache id
*
* @return string MD5 Hash : page cache id
*
* @since 11.1
* @todo Discuss whether this should be coupled to a data hash or a request
* hash ... perhaps hashed with a serialized request
*/
protected function _makeId()
{
return JCache::makeId();
}
/**
* There is no change in page data so send an
* unmodified header and die gracefully
*
* @return void
*
* @since 11.1
*/
protected function _noChange()
{
$app = JFactory::getApplication();
// Send not modified header and exit gracefully
header('HTTP/1.x 304 Not Modified', true);
$app->close();
}
/**
* Set the ETag header in the response
*
* @param string $etag The entity tag (etag) to set
*
* @return void
*
* @since 11.1
*/
protected function _setEtag($etag)
{
JResponse::setHeader('ETag', $etag, true);
}
}

View File

@ -0,0 +1,135 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Cache
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* Joomla! Cache view type object
*
* @package Joomla.Platform
* @subpackage Cache
* @since 11.1
*/
class JCacheControllerView extends JCacheController
{
/**
* Get the cached view data
*
* @param object &$view The view object to cache output for
* @param string $method The method name of the view method to cache output for
* @param string $id The cache data id
* @param boolean $wrkarounds True to enable workarounds.
*
* @return boolean True if the cache is hit (false else)
*
* @since 11.1
*/
public function get( $view, $method = 'display' , $id = false, $wrkarounds = true )
{
// If an id is not given generate it from the request
if ($id == false)
{
$id = $this->_makeId($view, $method);
}
$data = $this->cache->get($id);
$locktest = new stdClass;
$locktest->locked = null;
$locktest->locklooped = null;
if ($data === false)
{
$locktest = $this->cache->lock($id, null);
// If the loop is completed and returned true it means the lock has been set.
// If looped is true try to get the cached data again; it could exist now.
if ($locktest->locked == true && $locktest->locklooped == true)
{
$data = $this->cache->get($id);
}
// False means that locking is either turned off or maxtime has been exceeded.
// Execute the view.
}
if ($data !== false)
{
$data = unserialize(trim($data));
if ($wrkarounds === true)
{
echo JCache::getWorkarounds($data);
}
else
{
// No workarounds, so all data is stored in one piece
echo (isset($data)) ? $data : null;
}
if ($locktest->locked == true)
{
$this->cache->unlock($id);
}
return true;
}
/*
* No hit so we have to execute the view
*/
if (method_exists($view, $method))
{
// If previous lock failed try again
if ($locktest->locked == false)
{
$locktest = $this->cache->lock($id);
}
// Capture and echo output
ob_start();
ob_implicit_flush(false);
$view->$method();
$data = ob_get_contents();
ob_end_clean();
echo $data;
/*
* For a view we have a special case. We need to cache not only the output from the view, but the state
* of the document head after the view has been rendered. This will allow us to properly cache any attached
* scripts or stylesheets or links or any other modifications that the view has made to the document object
*/
$cached = $wrkarounds == true ? JCache::setWorkarounds($data) : $data;
// Store the cache data
$this->cache->store(serialize($cached), $id);
if ($locktest->locked == true)
{
$this->cache->unlock($id);
}
}
return false;
}
/**
* Generate a view cache id.
*
* @param object &$view The view object to cache output for
* @param string $method The method name to cache for the view object
*
* @return string MD5 Hash : view cache id
*
* @since 11.1
*/
protected function _makeId(&$view, $method)
{
return md5(serialize(array(JCache::makeId(), get_class($view), $method)));
}
}

1
libraries/joomla/cache/index.html vendored Normal file
View File

@ -0,0 +1 @@
<!DOCTYPE html><title></title>

346
libraries/joomla/cache/storage.php vendored Normal file
View File

@ -0,0 +1,346 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Cache
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* Abstract cache storage handler
*
* @package Joomla.Platform
* @subpackage Cache
* @since 11.1
*/
class JCacheStorage
{
/**
* @var string Rawname
* @since 11.1
*/
protected $rawname;
/**
* @var datetime Now
* @since 11.1
*/
public $_now;
/**
* @var integer Cache lifetime
* @since 11.1
*/
public $_lifetime;
/**
* @var boolean Locking
* @since 11.1
*/
public $_locking;
/**
* @var string Language
* @since 11.1
*/
public $_language;
/**
* @var string Application name.
* @since 11.1
*/
public $_application;
/**
* @var string Hash
* @since 11.1
*/
public $_hash;
/**
* Constructor
*
* @param array $options Optional parameters
*
* @since 11.1
*/
public function __construct($options = array())
{
$config = JFactory::getConfig();
$this->_hash = md5($config->get('secret'));
$this->_application = (isset($options['application'])) ? $options['application'] : null;
$this->_language = (isset($options['language'])) ? $options['language'] : 'en-GB';
$this->_locking = (isset($options['locking'])) ? $options['locking'] : true;
$this->_lifetime = (isset($options['lifetime'])) ? $options['lifetime'] * 60 : $config->get('cachetime') * 60;
$this->_now = (isset($options['now'])) ? $options['now'] : time();
// Set time threshold value. If the lifetime is not set, default to 60 (0 is BAD)
// _threshold is now available ONLY as a legacy (it's deprecated). It's no longer used in the core.
if (empty($this->_lifetime))
{
$this->_threshold = $this->_now - 60;
$this->_lifetime = 60;
}
else
{
$this->_threshold = $this->_now - $this->_lifetime;
}
}
/**
* Returns a cache storage handler object, only creating it
* if it doesn't already exist.
*
* @param string $handler The cache storage handler to instantiate
* @param array $options Array of handler options
*
* @return JCacheStorage A JCacheStorage instance
*
* @since 11.1
* @throws UnexpectedValueException
* @throws RuntimeException
*/
public static function getInstance($handler = null, $options = array())
{
static $now = null;
self::addIncludePath(JPATH_PLATFORM . '/joomla/cache/storage');
if (!isset($handler))
{
$conf = JFactory::getConfig();
$handler = $conf->get('cache_handler');
if (empty($handler))
{
throw new UnexpectedValueException('Cache Storage Handler not set.');
}
}
if (is_null($now))
{
$now = time();
}
$options['now'] = $now;
// We can't cache this since options may change...
$handler = strtolower(preg_replace('/[^A-Z0-9_\.-]/i', '', $handler));
$class = 'JCacheStorage' . ucfirst($handler);
if (!class_exists($class))
{
// Search for the class file in the JCacheStorage include paths.
jimport('joomla.filesystem.path');
if ($path = JPath::find(self::addIncludePath(), strtolower($handler) . '.php'))
{
include_once $path;
}
else
{
throw new RuntimeException(sprintf('Unable to load Cache Storage: %s', $handler));
}
}
return new $class($options);
}
/**
* Get cached data by id and group
*
* @param string $id The cache data id
* @param string $group The cache data group
* @param boolean $checkTime True to verify cache time expiration threshold
*
* @return mixed Boolean false on failure or a cached data object
*
* @since 11.1
*/
public function get($id, $group, $checkTime = true)
{
return false;
}
/**
* Get all cached data
*
* @return mixed Boolean false on failure or a cached data object
*
* @since 11.1
* @todo Review this method. The docblock doesn't fit what it actually does.
*/
public function getAll()
{
if (!class_exists('JCacheStorageHelper', false))
{
include_once JPATH_PLATFORM . '/joomla/cache/storage/helper.php';
}
return;
}
/**
* Store the data to cache by id and group
*
* @param string $id The cache data id
* @param string $group The cache data group
* @param string $data The data to store in cache
*
* @return boolean True on success, false otherwise
*
* @since 11.1
*/
public function store($id, $group, $data)
{
return true;
}
/**
* Remove a cached data entry by id and group
*
* @param string $id The cache data id
* @param string $group The cache data group
*
* @return boolean True on success, false otherwise
*
* @since 11.1
*/
public function remove($id, $group)
{
return true;
}
/**
* Clean cache for a group given a mode.
*
* @param string $group The cache data group
* @param string $mode The mode for cleaning cache [group|notgroup]
* group mode : cleans all cache in the group
* notgroup mode : cleans all cache not in the group
*
* @return boolean True on success, false otherwise
*
* @since 11.1
*/
public function clean($group, $mode = null)
{
return true;
}
/**
* Garbage collect expired cache data
*
* @return boolean True on success, false otherwise.
*
* @since 11.1
*/
public function gc()
{
return true;
}
/**
* Test to see if the storage handler is available.
*
* @return boolean True on success, false otherwise
*
* @since 12.1.
*/
public static function isSupported()
{
return true;
}
/**
* Test to see if the storage handler is available.
*
* @return boolean True on success, false otherwise.
*
* @since 11.1
* @deprecated 12.3 (Platform) & 4.0 (CMS)
*/
public static function test()
{
JLog::add('JCacheStorage::test() is deprecated. Use JCacheStorage::isSupported() instead.', JLog::WARNING, 'deprecated');
return static::isSupported();
}
/**
* Lock cached item
*
* @param string $id The cache data id
* @param string $group The cache data group
* @param integer $locktime Cached item max lock time
*
* @return boolean True on success, false otherwise.
*
* @since 11.1
*/
public function lock($id, $group, $locktime)
{
return false;
}
/**
* Unlock cached item
*
* @param string $id The cache data id
* @param string $group The cache data group
*
* @return boolean True on success, false otherwise.
*
* @since 11.1
*/
public function unlock($id, $group = null)
{
return false;
}
/**
* Get a cache_id string from an id/group pair
*
* @param string $id The cache data id
* @param string $group The cache data group
*
* @return string The cache_id string
*
* @since 11.1
*/
protected function _getCacheId($id, $group)
{
$name = md5($this->_application . '-' . $id . '-' . $this->_language);
$this->rawname = $this->_hash . '-' . $name;
return $this->_hash . '-cache-' . $group . '-' . $name;
}
/**
* Add a directory where JCacheStorage should search for handlers. You may
* either pass a string or an array of directories.
*
* @param string $path A path to search.
*
* @return array An array with directory elements
*
* @since 11.1
*/
public static function addIncludePath($path = '')
{
static $paths;
if (!isset($paths))
{
$paths = array();
}
if (!empty($path) && !in_array($path, $paths))
{
jimport('joomla.filesystem.path');
array_unshift($paths, JPath::clean($path));
}
return $paths;
}
}

248
libraries/joomla/cache/storage/apc.php vendored Normal file
View File

@ -0,0 +1,248 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Cache
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* APC cache storage handler
*
* @package Joomla.Platform
* @subpackage Cache
* @see http://php.net/manual/en/book.apc.php
* @since 11.1
*/
class JCacheStorageApc extends JCacheStorage
{
/**
* Get cached data from APC by id and group
*
* @param string $id The cache data id
* @param string $group The cache data group
* @param boolean $checkTime True to verify cache time expiration threshold
*
* @return mixed Boolean False on failure or a cached data string
*
* @since 11.1
*/
public function get($id, $group, $checkTime = true)
{
$cache_id = $this->_getCacheId($id, $group);
return apc_fetch($cache_id);
}
/**
* Get all cached data
*
* @return array data
*
* @since 11.1
*/
public function getAll()
{
parent::getAll();
$allinfo = apc_cache_info('user');
$keys = $allinfo['cache_list'];
$secret = $this->_hash;
$data = array();
foreach ($keys as $key)
{
$name = $key['info'];
$namearr = explode('-', $name);
if ($namearr !== false && $namearr[0] == $secret && $namearr[1] == 'cache')
{
$group = $namearr[2];
if (!isset($data[$group]))
{
$item = new JCacheStorageHelper($group);
}
else
{
$item = $data[$group];
}
$item->updateSize($key['mem_size'] / 1024);
$data[$group] = $item;
}
}
return $data;
}
/**
* Store the data to APC by id and group
*
* @param string $id The cache data id
* @param string $group The cache data group
* @param string $data The data to store in cache
*
* @return boolean True on success, false otherwise
*
* @since 11.1
*/
public function store($id, $group, $data)
{
$cache_id = $this->_getCacheId($id, $group);
return apc_store($cache_id, $data, $this->_lifetime);
}
/**
* Remove a cached data entry by id and group
*
* @param string $id The cache data id
* @param string $group The cache data group
*
* @return boolean True on success, false otherwise
*
* @since 11.1
*/
public function remove($id, $group)
{
$cache_id = $this->_getCacheId($id, $group);
return apc_delete($cache_id);
}
/**
* Clean cache for a group given a mode.
*
* group mode : cleans all cache in the group
* notgroup mode : cleans all cache not in the group
*
* @param string $group The cache data group
* @param string $mode The mode for cleaning cache [group|notgroup]
*
* @return boolean True on success, false otherwise
*
* @since 11.1
*/
public function clean($group, $mode = null)
{
$allinfo = apc_cache_info('user');
$keys = $allinfo['cache_list'];
$secret = $this->_hash;
foreach ($keys as $key)
{
if (strpos($key['info'], $secret . '-cache-' . $group . '-') === 0 xor $mode != 'group')
{
apc_delete($key['info']);
}
}
return true;
}
/**
* Force garbage collect expired cache data as items are removed only on fetch!
*
* @return boolean True on success, false otherwise.
*
* @since 11.1
*/
public function gc()
{
$allinfo = apc_cache_info('user');
$keys = $allinfo['cache_list'];
$secret = $this->_hash;
foreach ($keys as $key)
{
if (strpos($key['info'], $secret . '-cache-'))
{
apc_fetch($key['info']);
}
}
}
/**
* Test to see if the cache storage is available.
*
* @return boolean True on success, false otherwise.
*
* @since 12.1
*/
public static function isSupported()
{
return extension_loaded('apc');
}
/**
* Lock cached item - override parent as this is more efficient
*
* @param string $id The cache data id
* @param string $group The cache data group
* @param integer $locktime Cached item max lock time
*
* @return object Properties are lock and locklooped
*
* @since 11.1
*/
public function lock($id, $group, $locktime)
{
$returning = new stdClass;
$returning->locklooped = false;
$looptime = $locktime * 10;
$cache_id = $this->_getCacheId($id, $group) . '_lock';
$data_lock = apc_add($cache_id, 1, $locktime);
if ($data_lock === false)
{
$lock_counter = 0;
// Loop until you find that the lock has been released.
// That implies that data get from other thread has finished
while ($data_lock === false)
{
if ($lock_counter > $looptime)
{
$returning->locked = false;
$returning->locklooped = true;
break;
}
usleep(100);
$data_lock = apc_add($cache_id, 1, $locktime);
$lock_counter++;
}
}
$returning->locked = $data_lock;
return $returning;
}
/**
* Unlock cached item - override parent for cacheid compatibility with lock
*
* @param string $id The cache data id
* @param string $group The cache data group
*
* @return boolean True on success, false otherwise.
*
* @since 11.1
*/
public function unlock($id, $group = null)
{
$cache_id = $this->_getCacheId($id, $group) . '_lock';
$unlock = apc_delete($cache_id);
return $unlock;
}
}

View File

@ -0,0 +1,336 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Cache
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* Cache lite storage handler
*
* @package Joomla.Platform
* @subpackage Cache
* @see http://pear.php.net/package/Cache_Lite/
* @since 11.1
*/
class JCacheStorageCachelite extends JCacheStorage
{
/**
* @var object
* @since 11.1
*/
protected static $CacheLiteInstance = null;
/**
* @var
* @since 11.1
*/
protected $_root;
/**
* Constructor
*
* @param array $options Optional parameters.
*
* @since 11.1
*/
public function __construct($options = array())
{
parent::__construct($options);
$this->_root = $options['cachebase'];
$cloptions = array(
'cacheDir' => $this->_root . '/',
'lifeTime' => $this->_lifetime,
'fileLocking' => $this->_locking,
'automaticCleaningFactor' => isset($options['autoclean']) ? $options['autoclean'] : 200,
'fileNameProtection' => false,
'hashedDirectoryLevel' => 0,
'caching' => $options['caching']);
if (self::$CacheLiteInstance === null)
{
$this->initCache($cloptions);
}
}
/**
* Instantiates the appropriate CacheLite object.
* Only initializes the engine if it does not already exist.
* Note this is a protected method
*
* @param array $cloptions optional parameters
*
* @return object
*
* @since 11.1
*/
protected function initCache($cloptions)
{
require_once 'Cache/Lite.php';
self::$CacheLiteInstance = new Cache_Lite($cloptions);
return self::$CacheLiteInstance;
}
/**
* Get cached data from a file by id and group
*
* @param string $id The cache data id.
* @param string $group The cache data group.
* @param boolean $checkTime True to verify cache time expiration threshold.
*
* @return mixed Boolean false on failure or a cached data string.
*
* @since 11.1
*/
public function get($id, $group, $checkTime = true)
{
self::$CacheLiteInstance->setOption('cacheDir', $this->_root . '/' . $group . '/');
$this->_getCacheId($id, $group);
$data = self::$CacheLiteInstance->get($this->rawname, $group);
return $data;
}
/**
* Get all cached data
*
* @return array
*
* @since 11.1
*/
public function getAll()
{
parent::getAll();
$path = $this->_root;
$folders = new DirectoryIterator($path);
$data = array();
foreach ($folders as $folder)
{
if (!$folder->isDir() || $folder->isDot())
{
continue;
}
$foldername = $folder->getFilename();
$files = new DirectoryIterator($path . '/' . $foldername);
$item = new JCacheStorageHelper($foldername);
foreach ($files as $file)
{
if (!$file->isFile())
{
continue;
}
$filename = $file->getFilename();
$item->updateSize(filesize($path . '/' . $foldername . '/' . $filename) / 1024);
}
$data[$foldername] = $item;
}
return $data;
}
/**
* Store the data to a file by id and group
*
* @param string $id The cache data id.
* @param string $group The cache data group.
* @param string $data The data to store in cache.
*
* @return boolean True on success, false otherwise
*
* @since 11.1
*/
public function store($id, $group, $data)
{
$dir = $this->_root . '/' . $group;
// If the folder doesn't exist try to create it
if (!is_dir($dir))
{
// Make sure the index file is there
$indexFile = $dir . '/index.html';
@mkdir($dir) && file_put_contents($indexFile, '<!DOCTYPE html><title></title>');
}
// Make sure the folder exists
if (!is_dir($dir))
{
return false;
}
self::$CacheLiteInstance->setOption('cacheDir', $this->_root . '/' . $group . '/');
$this->_getCacheId($id, $group);
$success = self::$CacheLiteInstance->save($data, $this->rawname, $group);
if ($success == true)
{
return $success;
}
else
{
return false;
}
}
/**
* Remove a cached data file by id and group
*
* @param string $id The cache data id
* @param string $group The cache data group
*
* @return boolean True on success, false otherwise
*
* @since 11.1
*/
public function remove($id, $group)
{
self::$CacheLiteInstance->setOption('cacheDir', $this->_root . '/' . $group . '/');
$this->_getCacheId($id, $group);
$success = self::$CacheLiteInstance->remove($this->rawname, $group);
if ($success == true)
{
return $success;
}
else
{
return false;
}
}
/**
* Clean cache for a group given a mode.
*
* @param string $group The cache data group.
* @param string $mode The mode for cleaning cache [group|notgroup].
* group mode : cleans all cache in the group
* notgroup mode : cleans all cache not in the group
*
* @return boolean True on success, false otherwise.
*
* @since 11.1
*/
public function clean($group, $mode = null)
{
jimport('joomla.filesystem.folder');
switch ($mode)
{
case 'notgroup':
$clmode = 'notingroup';
$success = self::$CacheLiteInstance->clean($group, $clmode);
break;
case 'group':
if (is_dir($this->_root . '/' . $group))
{
$clmode = $group;
self::$CacheLiteInstance->setOption('cacheDir', $this->_root . '/' . $group . '/');
$success = self::$CacheLiteInstance->clean($group, $clmode);
JFolder::delete($this->_root . '/' . $group);
}
else
{
$success = true;
}
break;
default:
if (is_dir($this->_root . '/' . $group))
{
$clmode = $group;
self::$CacheLiteInstance->setOption('cacheDir', $this->_root . '/' . $group . '/');
$success = self::$CacheLiteInstance->clean($group, $clmode);
}
else
{
$success = true;
}
break;
}
if ($success == true)
{
return $success;
}
else
{
return false;
}
}
/**
* Garbage collect expired cache data
*
* @return boolean True on success, false otherwise.
*
* @since 11.1
*/
public function gc()
{
$result = true;
self::$CacheLiteInstance->setOption('automaticCleaningFactor', 1);
self::$CacheLiteInstance->setOption('hashedDirectoryLevel', 1);
$success1 = self::$CacheLiteInstance->_cleanDir($this->_root . '/', false, 'old');
if (!($dh = opendir($this->_root . '/')))
{
return false;
}
while ($file = readdir($dh))
{
if (($file != '.') && ($file != '..') && ($file != '.svn'))
{
$file2 = $this->_root . '/' . $file;
if (is_dir($file2))
{
$result = ($result && (self::$CacheLiteInstance->_cleanDir($file2 . '/', false, 'old')));
}
}
}
$success = ($success1 && $result);
return $success;
}
/**
* Test to see if the cache storage is available.
*
* @return boolean True on success, false otherwise.
*
* @since 12.1
*/
public static function isSupported()
{
@include_once 'Cache/Lite.php';
if (class_exists('Cache_Lite'))
{
return true;
}
else
{
return false;
}
}
}

689
libraries/joomla/cache/storage/file.php vendored Normal file
View File

@ -0,0 +1,689 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Cache
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* File cache storage handler
*
* @package Joomla.Platform
* @subpackage Cache
* @since 11.1
*/
class JCacheStorageFile extends JCacheStorage
{
/**
* @var string
* @since 11.1
*/
protected $_root;
/**
* Constructor
*
* @param array $options Optional parameters
*
* @since 11.1
*/
public function __construct($options = array())
{
parent::__construct($options);
$this->_root = $options['cachebase'];
}
// NOTE: raw php calls are up to 100 times faster than JFile or JFolder
/**
* Get cached data from a file by id and group
*
* @param string $id The cache data id
* @param string $group The cache data group
* @param boolean $checkTime True to verify cache time expiration threshold
*
* @return mixed Boolean false on failure or a cached data string
*
* @since 11.1
*/
public function get($id, $group, $checkTime = true)
{
$data = false;
$path = $this->_getFilePath($id, $group);
if ($checkTime == false || ($checkTime == true && $this->_checkExpire($id, $group) === true))
{
if (file_exists($path))
{
$data = file_get_contents($path);
if ($data)
{
// Remove the initial die() statement
$data = str_replace('<?php die("Access Denied"); ?>#x#', '', $data);
}
}
return $data;
}
else
{
return false;
}
}
/**
* Get all cached data
*
* @return array The cached data
*
* @since 11.1
*/
public function getAll()
{
parent::getAll();
$path = $this->_root;
$folders = $this->_folders($path);
$data = array();
foreach ($folders as $folder)
{
$files = $this->_filesInFolder($path . '/' . $folder);
$item = new JCacheStorageHelper($folder);
foreach ($files as $file)
{
$item->updateSize(filesize($path . '/' . $folder . '/' . $file) / 1024);
}
$data[$folder] = $item;
}
return $data;
}
/**
* Store the data to a file by id and group
*
* @param string $id The cache data id
* @param string $group The cache data group
* @param string $data The data to store in cache
*
* @return boolean True on success, false otherwise
*
* @since 11.1
*/
public function store($id, $group, $data)
{
$written = false;
$path = $this->_getFilePath($id, $group);
$die = '<?php die("Access Denied"); ?>#x#';
// Prepend a die string
$data = $die . $data;
$_fileopen = @fopen($path, "wb");
if ($_fileopen)
{
$len = strlen($data);
@fwrite($_fileopen, $data, $len);
$written = true;
}
// Data integrity check
if ($written && ($data == file_get_contents($path)))
{
return true;
}
else
{
return false;
}
}
/**
* Remove a cached data file by id and group
*
* @param string $id The cache data id
* @param string $group The cache data group
*
* @return boolean True on success, false otherwise
*
* @since 11.1
*/
public function remove($id, $group)
{
$path = $this->_getFilePath($id, $group);
if (!@unlink($path))
{
return false;
}
return true;
}
/**
* Clean cache for a group given a mode.
*
* @param string $group The cache data group
* @param string $mode The mode for cleaning cache [group|notgroup]
* group mode : cleans all cache in the group
* notgroup mode : cleans all cache not in the group
*
* @return boolean True on success, false otherwise
*
* @since 11.1
*/
public function clean($group, $mode = null)
{
$return = true;
$folder = $group;
if (trim($folder) == '')
{
$mode = 'notgroup';
}
switch ($mode)
{
case 'notgroup':
$folders = $this->_folders($this->_root);
for ($i = 0, $n = count($folders); $i < $n; $i++)
{
if ($folders[$i] != $folder)
{
$return |= $this->_deleteFolder($this->_root . '/' . $folders[$i]);
}
}
break;
case 'group':
default:
if (is_dir($this->_root . '/' . $folder))
{
$return = $this->_deleteFolder($this->_root . '/' . $folder);
}
break;
}
return $return;
}
/**
* Garbage collect expired cache data
*
* @return boolean True on success, false otherwise.
*
* @since 11.1
*/
public function gc()
{
$result = true;
// Files older than lifeTime get deleted from cache
$files = $this->_filesInFolder($this->_root, '', true, true, array('.svn', 'CVS', '.DS_Store', '__MACOSX', 'index.html'));
foreach ($files as $file)
{
$time = @filemtime($file);
if (($time + $this->_lifetime) < $this->_now || empty($time))
{
$result |= @unlink($file);
}
}
return $result;
}
/**
* Test to see if the cache storage is available.
*
* @return boolean True on success, false otherwise.
*
* @since 12.1
*/
public static function isSupported()
{
$conf = JFactory::getConfig();
return is_writable($conf->get('cache_path', JPATH_CACHE));
}
/**
* Lock cached item
*
* @param string $id The cache data id
* @param string $group The cache data group
* @param integer $locktime Cached item max lock time
*
* @return boolean True on success, false otherwise.
*
* @since 11.1
*/
public function lock($id, $group, $locktime)
{
$returning = new stdClass;
$returning->locklooped = false;
$looptime = $locktime * 10;
$path = $this->_getFilePath($id, $group);
$_fileopen = @fopen($path, "r+b");
if ($_fileopen)
{
$data_lock = @flock($_fileopen, LOCK_EX);
}
else
{
$data_lock = false;
}
if ($data_lock === false)
{
$lock_counter = 0;
// Loop until you find that the lock has been released.
// That implies that data get from other thread has finished
while ($data_lock === false)
{
if ($lock_counter > $looptime)
{
$returning->locked = false;
$returning->locklooped = true;
break;
}
usleep(100);
$data_lock = @flock($_fileopen, LOCK_EX);
$lock_counter++;
}
}
$returning->locked = $data_lock;
return $returning;
}
/**
* Unlock cached item
*
* @param string $id The cache data id
* @param string $group The cache data group
*
* @return boolean True on success, false otherwise.
*
* @since 11.1
*/
public function unlock($id, $group = null)
{
$path = $this->_getFilePath($id, $group);
$_fileopen = @fopen($path, "r+b");
if ($_fileopen)
{
$ret = @flock($_fileopen, LOCK_UN);
@fclose($_fileopen);
}
return $ret;
}
/**
* Check to make sure cache is still valid, if not, delete it.
*
* @param string $id Cache key to expire.
* @param string $group The cache data group.
*
* @return boolean False if not valid
*
* @since 11.1
*/
protected function _checkExpire($id, $group)
{
$path = $this->_getFilePath($id, $group);
// Check prune period
if (file_exists($path))
{
$time = @filemtime($path);
if (($time + $this->_lifetime) < $this->_now || empty($time))
{
@unlink($path);
return false;
}
return true;
}
return false;
}
/**
* Get a cache file path from an id/group pair
*
* @param string $id The cache data id
* @param string $group The cache data group
*
* @return string The cache file path
*
* @since 11.1
*/
protected function _getFilePath($id, $group)
{
$name = $this->_getCacheId($id, $group);
$dir = $this->_root . '/' . $group;
// If the folder doesn't exist try to create it
if (!is_dir($dir))
{
// Make sure the index file is there
$indexFile = $dir . '/index.html';
@ mkdir($dir) && file_put_contents($indexFile, '<!DOCTYPE html><title></title>');
}
// Make sure the folder exists
if (!is_dir($dir))
{
return false;
}
return $dir . '/' . $name . '.php';
}
/**
* Quickly delete a folder of files
*
* @param string $path The path to the folder to delete.
*
* @return boolean True on success.
*
* @since 11.1
*/
protected function _deleteFolder($path)
{
// Sanity check
if (!$path || !is_dir($path) || empty($this->_root))
{
// Bad programmer! Bad Bad programmer!
JLog::add('JCacheStorageFile::_deleteFolder ' . JText::_('JLIB_FILESYSTEM_ERROR_DELETE_BASE_DIRECTORY'), JLog::WARNING, 'jerror');
return false;
}
$path = $this->_cleanPath($path);
// Check to make sure path is inside cache folder, we do not want to delete Joomla root!
$pos = strpos($path, $this->_cleanPath($this->_root));
if ($pos === false || $pos > 0)
{
JLog::add('JCacheStorageFile::_deleteFolder' . JText::sprintf('JLIB_FILESYSTEM_ERROR_PATH_IS_NOT_A_FOLDER', $path), JLog::WARNING, 'jerror');
return false;
}
// Remove all the files in folder if they exist; disable all filtering
$files = $this->_filesInFolder($path, '.', false, true, array(), array());
if (!empty($files) && !is_array($files))
{
if (@unlink($files) !== true)
{
return false;
}
}
elseif (!empty($files) && is_array($files))
{
foreach ($files as $file)
{
$file = $this->_cleanPath($file);
// In case of restricted permissions we zap it one way or the other
// as long as the owner is either the webserver or the ftp
if (@unlink($file))
{
// Do nothing
}
else
{
$filename = basename($file);
JLog::add('JCacheStorageFile::_deleteFolder' . JText::sprintf('JLIB_FILESYSTEM_DELETE_FAILED', $filename), JLog::WARNING, 'jerror');
return false;
}
}
}
// Remove sub-folders of folder; disable all filtering
$folders = $this->_folders($path, '.', false, true, array(), array());
foreach ($folders as $folder)
{
if (is_link($folder))
{
// Don't descend into linked directories, just delete the link.
if (@unlink($folder) !== true)
{
return false;
}
}
elseif ($this->_deleteFolder($folder) !== true)
{
return false;
}
}
// In case of restricted permissions we zap it one way or the other
// as long as the owner is either the webserver or the ftp
if (@rmdir($path))
{
$ret = true;
}
else
{
JLog::add('JCacheStorageFile::_deleteFolder' . JText::sprintf('JLIB_FILESYSTEM_ERROR_FOLDER_DELETE', $path), JLog::WARNING, 'jerror');
$ret = false;
}
return $ret;
}
/**
* Function to strip additional / or \ in a path name
*
* @param string $path The path to clean
* @param string $ds Directory separator (optional)
*
* @return string The cleaned path
*
* @since 11.1
*/
protected function _cleanPath($path, $ds = DIRECTORY_SEPARATOR)
{
$path = trim($path);
if (empty($path))
{
$path = $this->_root;
}
else
{
// Remove double slashes and backslahses and convert all slashes and backslashes to DIRECTORY_SEPARATOR
$path = preg_replace('#[/\\\\]+#', $ds, $path);
}
return $path;
}
/**
* Utility function to quickly read the files in a folder.
*
* @param string $path The path of the folder to read.
* @param string $filter A filter for file names.
* @param mixed $recurse True to recursively search into sub-folders, or an
* integer to specify the maximum depth.
* @param boolean $fullpath True to return the full path to the file.
* @param array $exclude Array with names of files which should not be shown in
* the result.
* @param array $excludefilter Array of folder names to exclude
*
* @return array Files in the given folder.
*
* @since 11.1
*/
protected function _filesInFolder($path, $filter = '.', $recurse = false, $fullpath = false
, $exclude = array('.svn', 'CVS', '.DS_Store', '__MACOSX'), $excludefilter = array('^\..*', '.*~'))
{
$arr = array();
// Check to make sure the path valid and clean
$path = $this->_cleanPath($path);
// Is the path a folder?
if (!is_dir($path))
{
JLog::add('JCacheStorageFile::_filesInFolder' . JText::sprintf('JLIB_FILESYSTEM_ERROR_PATH_IS_NOT_A_FOLDER', $path), JLog::WARNING, 'jerror');
return false;
}
// Read the source directory.
if (!($handle = @opendir($path)))
{
return $arr;
}
if (count($excludefilter))
{
$excludefilter = '/(' . implode('|', $excludefilter) . ')/';
}
else
{
$excludefilter = '';
}
while (($file = readdir($handle)) !== false)
{
if (($file != '.') && ($file != '..') && (!in_array($file, $exclude)) && (!$excludefilter || !preg_match($excludefilter, $file)))
{
$dir = $path . '/' . $file;
$isDir = is_dir($dir);
if ($isDir)
{
if ($recurse)
{
if (is_int($recurse))
{
$arr2 = $this->_filesInFolder($dir, $filter, $recurse - 1, $fullpath);
}
else
{
$arr2 = $this->_filesInFolder($dir, $filter, $recurse, $fullpath);
}
$arr = array_merge($arr, $arr2);
}
}
else
{
if (preg_match("/$filter/", $file))
{
if ($fullpath)
{
$arr[] = $path . '/' . $file;
}
else
{
$arr[] = $file;
}
}
}
}
}
closedir($handle);
return $arr;
}
/**
* Utility function to read the folders in a folder.
*
* @param string $path The path of the folder to read.
* @param string $filter A filter for folder names.
* @param mixed $recurse True to recursively search into sub-folders, or an integer to specify the maximum depth.
* @param boolean $fullpath True to return the full path to the folders.
* @param array $exclude Array with names of folders which should not be shown in the result.
* @param array $excludefilter Array with regular expressions matching folders which should not be shown in the result.
*
* @return array Folders in the given folder.
*
* @since 11.1
*/
protected function _folders($path, $filter = '.', $recurse = false, $fullpath = false
, $exclude = array('.svn', 'CVS', '.DS_Store', '__MACOSX'), $excludefilter = array('^\..*'))
{
$arr = array();
// Check to make sure the path valid and clean
$path = $this->_cleanPath($path);
// Is the path a folder?
if (!is_dir($path))
{
JLog::add('JCacheStorageFile::_folders' . JText::sprintf('JLIB_FILESYSTEM_ERROR_PATH_IS_NOT_A_FOLDER', $path), JLog::WARNING, 'jerror');
return false;
}
// Read the source directory
if (!($handle = @opendir($path)))
{
return $arr;
}
if (count($excludefilter))
{
$excludefilter_string = '/(' . implode('|', $excludefilter) . ')/';
}
else
{
$excludefilter_string = '';
}
while (($file = readdir($handle)) !== false)
{
if (($file != '.') && ($file != '..')
&& (!in_array($file, $exclude))
&& (empty($excludefilter_string) || !preg_match($excludefilter_string, $file)))
{
$dir = $path . '/' . $file;
$isDir = is_dir($dir);
if ($isDir)
{
// Removes filtered directories
if (preg_match("/$filter/", $file))
{
if ($fullpath)
{
$arr[] = $dir;
}
else
{
$arr[] = $file;
}
}
if ($recurse)
{
if (is_int($recurse))
{
$arr2 = $this->_folders($dir, $filter, $recurse - 1, $fullpath, $exclude, $excludefilter);
}
else
{
$arr2 = $this->_folders($dir, $filter, $recurse, $fullpath, $exclude, $excludefilter);
}
$arr = array_merge($arr, $arr2);
}
}
}
}
closedir($handle);
return $arr;
}
}

View File

@ -0,0 +1,71 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Cache
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* Cache storage helper functions.
*
* @package Joomla.Platform
* @subpackage Cache
* @since 11.1
*/
class JCacheStorageHelper
{
/**
* Cache data group
*
* @var string
* @since 11.1
*/
public $group = '';
/**
* Cached item size
*
* @var string
* @since 11.1
*/
public $size = 0;
/**
* Counter
*
* @var integer
* @since 11.1
*/
public $count = 0;
/**
* Constructor
*
* @param string $group The cache data group
*
* @since 11.1
*/
public function __construct($group)
{
$this->group = $group;
}
/**
* Increase cache items count.
*
* @param string $size Cached item size
*
* @return void
*
* @since 11.1
*/
public function updateSize($size)
{
$this->size = number_format($this->size + $size, 2);
$this->count++;
}
}

View File

@ -0,0 +1 @@
<!DOCTYPE html><title></title>

View File

@ -0,0 +1,485 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Cache
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* Memcache cache storage handler
*
* @package Joomla.Platform
* @subpackage Cache
* @see http://php.net/manual/en/book.memcache.php
* @since 11.1
*/
class JCacheStorageMemcache extends JCacheStorage
{
/**
* @var Memcache
* @since 11.1
*/
protected static $_db = null;
/**
* @var boolean
* @since 11.1
*/
protected $_persistent = false;
/**
* @var
* @since 11.1
*/
protected $_compress = 0;
/**
* Constructor
*
* @param array $options Optional parameters.
*
* @since 11.1
*/
public function __construct($options = array())
{
parent::__construct($options);
if (self::$_db === null)
{
$this->getConnection();
}
}
/**
* Return memcache connection object
*
* @return object memcache connection object
*
* @since 11.1
* @throws RuntimeException
*/
protected function getConnection()
{
if ((extension_loaded('memcache') && class_exists('Memcache')) != true)
{
return false;
}
$config = JFactory::getConfig();
$this->_persistent = $config->get('memcache_persist', true);
$this->_compress = $config->get('memcache_compress', false) == false ? 0 : MEMCACHE_COMPRESSED;
/*
* This will be an array of loveliness
* @todo: multiple servers
* $servers = (isset($params['servers'])) ? $params['servers'] : array();
*/
$server = array();
$server['host'] = $config->get('memcache_server_host', 'localhost');
$server['port'] = $config->get('memcache_server_port', 11211);
// Create the memcache connection
self::$_db = new Memcache;
self::$_db->addServer($server['host'], $server['port'], $this->_persistent);
$memcachetest = @self::$_db->connect($server['host'], $server['port']);
if ($memcachetest == false)
{
throw new RuntimeException('Could not connect to memcache server', 404);
}
// Memcahed has no list keys, we do our own accounting, initialise key index
if (self::$_db->get($this->_hash . '-index') === false)
{
$empty = array();
self::$_db->set($this->_hash . '-index', $empty, $this->_compress, 0);
}
return;
}
/**
* Get cached data from memcache by id and group
*
* @param string $id The cache data id
* @param string $group The cache data group
* @param boolean $checkTime True to verify cache time expiration threshold
*
* @return mixed Boolean false on failure or a cached data string
*
* @since 11.1
*/
public function get($id, $group, $checkTime = true)
{
$cache_id = $this->_getCacheId($id, $group);
$back = self::$_db->get($cache_id);
return $back;
}
/**
* Get all cached data
*
* @return array data
*
* @since 11.1
*/
public function getAll()
{
parent::getAll();
$keys = self::$_db->get($this->_hash . '-index');
$secret = $this->_hash;
$data = array();
if (!empty($keys))
{
foreach ($keys as $key)
{
if (empty($key))
{
continue;
}
$namearr = explode('-', $key->name);
if ($namearr !== false && $namearr[0] == $secret && $namearr[1] == 'cache')
{
$group = $namearr[2];
if (!isset($data[$group]))
{
$item = new JCacheStorageHelper($group);
}
else
{
$item = $data[$group];
}
$item->updateSize($key->size / 1024);
$data[$group] = $item;
}
}
}
return $data;
}
/**
* Store the data to memcache by id and group
*
* @param string $id The cache data id
* @param string $group The cache data group
* @param string $data The data to store in cache
*
* @return boolean True on success, false otherwise
*
* @since 11.1
*/
public function store($id, $group, $data)
{
$cache_id = $this->_getCacheId($id, $group);
if (!$this->lockindex())
{
return false;
}
$index = self::$_db->get($this->_hash . '-index');
if ($index === false)
{
$index = array();
}
$tmparr = new stdClass;
$tmparr->name = $cache_id;
$tmparr->size = strlen($data);
$config = JFactory::getConfig();
$lifetime = (int) $config->get('cachetime', 15);
if ($this->_lifetime == $lifetime)
{
$this->_lifetime = $lifetime * 60;
}
$index[] = $tmparr;
self::$_db->replace($this->_hash . '-index', $index, 0, 0);
$this->unlockindex();
// Prevent double writes, write only if it doesn't exist else replace
if (!self::$_db->replace($cache_id, $data, $this->_compress, $this->_lifetime))
{
self::$_db->set($cache_id, $data, $this->_compress, $this->_lifetime);
}
return true;
}
/**
* Remove a cached data entry by id and group
*
* @param string $id The cache data id
* @param string $group The cache data group
*
* @return boolean True on success, false otherwise
*
* @since 11.1
*/
public function remove($id, $group)
{
$cache_id = $this->_getCacheId($id, $group);
if (!$this->lockindex())
{
return false;
}
$index = self::$_db->get($this->_hash . '-index');
if ($index === false)
{
$index = array();
}
foreach ($index as $key => $value)
{
if ($value->name == $cache_id)
{
unset($index[$key]);
}
break;
}
self::$_db->replace($this->_hash . '-index', $index, 0, 0);
$this->unlockindex();
return self::$_db->delete($cache_id);
}
/**
* Clean cache for a group given a mode.
*
* @param string $group The cache data group
* @param string $mode The mode for cleaning cache [group|notgroup]
* group mode : cleans all cache in the group
* notgroup mode : cleans all cache not in the group
*
* @return boolean True on success, false otherwise
*
* @since 11.1
*/
public function clean($group, $mode = null)
{
if (!$this->lockindex())
{
return false;
}
$index = self::$_db->get($this->_hash . '-index');
if ($index === false)
{
$index = array();
}
$secret = $this->_hash;
foreach ($index as $key => $value)
{
if (strpos($value->name, $secret . '-cache-' . $group . '-') === 0 xor $mode != 'group')
{
self::$_db->delete($value->name, 0);
unset($index[$key]);
}
}
self::$_db->replace($this->_hash . '-index', $index, 0, 0);
$this->unlockindex();
return true;
}
/**
* Test to see if the cache storage is available.
*
* @return boolean True on success, false otherwise.
*
* @since 12.1
*/
public static function isSupported()
{
if ((extension_loaded('memcache') && class_exists('Memcache')) != true)
{
return false;
}
$config = JFactory::getConfig();
$host = $config->get('memcache_server_host', 'localhost');
$port = $config->get('memcache_server_port', 11211);
$memcache = new Memcache;
$memcachetest = @$memcache->connect($host, $port);
if (!$memcachetest)
{
return false;
}
else
{
return true;
}
}
/**
* Lock cached item - override parent as this is more efficient
*
* @param string $id The cache data id
* @param string $group The cache data group
* @param integer $locktime Cached item max lock time
*
* @return boolean True on success, false otherwise.
*
* @since 11.1
*/
public function lock($id, $group, $locktime)
{
$returning = new stdClass;
$returning->locklooped = false;
$looptime = $locktime * 10;
$cache_id = $this->_getCacheId($id, $group);
if (!$this->lockindex())
{
return false;
}
$index = self::$_db->get($this->_hash . '-index');
if ($index === false)
{
$index = array();
}
$tmparr = new stdClass;
$tmparr->name = $cache_id;
$tmparr->size = 1;
$index[] = $tmparr;
self::$_db->replace($this->_hash . '-index', $index, 0, 0);
$this->unlockindex();
$data_lock = self::$_db->add($cache_id . '_lock', 1, false, $locktime);
if ($data_lock === false)
{
$lock_counter = 0;
// Loop until you find that the lock has been released.
// That implies that data get from other thread has finished
while ($data_lock === false)
{
if ($lock_counter > $looptime)
{
$returning->locked = false;
$returning->locklooped = true;
break;
}
usleep(100);
$data_lock = self::$_db->add($cache_id . '_lock', 1, false, $locktime);
$lock_counter++;
}
}
$returning->locked = $data_lock;
return $returning;
}
/**
* Unlock cached item - override parent for cacheid compatibility with lock
*
* @param string $id The cache data id
* @param string $group The cache data group
*
* @return boolean True on success, false otherwise.
*
* @since 11.1
*/
public function unlock($id, $group = null)
{
$cache_id = $this->_getCacheId($id, $group) . '_lock';
if (!$this->lockindex())
{
return false;
}
$index = self::$_db->get($this->_hash . '-index');
if ($index === false)
{
$index = array();
}
foreach ($index as $key => $value)
{
if ($value->name == $cache_id)
{
unset($index[$key]);
}
break;
}
self::$_db->replace($this->_hash . '-index', $index, 0, 0);
$this->unlockindex();
return self::$_db->delete($cache_id);
}
/**
* Lock cache index
*
* @return boolean True on success, false otherwise.
*
* @since 11.1
*/
protected function lockindex()
{
$looptime = 300;
$data_lock = self::$_db->add($this->_hash . '-index_lock', 1, false, 30);
if ($data_lock === false)
{
$lock_counter = 0;
// Loop until you find that the lock has been released. that implies that data get from other thread has finished
while ($data_lock === false)
{
if ($lock_counter > $looptime)
{
return false;
break;
}
usleep(100);
$data_lock = self::$_db->add($this->_hash . '-index_lock', 1, false, 30);
$lock_counter++;
}
}
return true;
}
/**
* Unlock cache index
*
* @return boolean True on success, false otherwise.
*
* @since 11.1
*/
protected function unlockindex()
{
return self::$_db->delete($this->_hash . '-index_lock');
}
}

View File

@ -0,0 +1,489 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Cache
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* Memcached cache storage handler
*
* @package Joomla.Platform
* @subpackage Cache
* @see http://php.net/manual/en/book.memcached.php
* @since 12.1
*/
class JCacheStorageMemcached extends JCacheStorage
{
/**
* @var Memcached
* @since 12.1
*/
protected static $_db = null;
/**
* @var boolean
* @since 12.1
*/
protected $_persistent = false;
/**
* @var
* @since 12.1
*/
protected $_compress = 0;
/**
* Constructor
*
* @param array $options Optional parameters.
*
* @since 12.1
*/
public function __construct($options = array())
{
parent::__construct($options);
if (self::$_db === null)
{
$this->getConnection();
}
}
/**
* Return memcached connection object
*
* @return object memcached connection object
*
* @since 12.1
* @throws RuntimeException
*/
protected function getConnection()
{
if ((extension_loaded('memcached') && class_exists('Memcached')) != true)
{
return false;
}
$config = JFactory::getConfig();
$this->_persistent = $config->get('memcache_persist', true);
$this->_compress = $config->get('memcache_compress', false) == false ? 0 : Memcached::OPT_COMPRESSION;
/*
* This will be an array of loveliness
* @todo: multiple servers
* $servers = (isset($params['servers'])) ? $params['servers'] : array();
*/
$server = array();
$server['host'] = $config->get('memcache_server_host', 'localhost');
$server['port'] = $config->get('memcache_server_port', 11211);
// Create the memcache connection
if ($this->_persistent)
{
$session = JFactory::getSession();
self::$_db = new Memcached($session->getId());
}
else
{
self::$_db = new Memcached;
}
$memcachedtest = self::$_db->addServer($server['host'], $server['port']);
if ($memcachedtest == false)
{
throw new RuntimeException('Could not connect to memcached server', 404);
}
self::$_db->setOption(Memcached::OPT_COMPRESSION, $this->_compress);
// Memcached has no list keys, we do our own accounting, initialise key index
if (self::$_db->get($this->_hash . '-index') === false)
{
$empty = array();
self::$_db->set($this->_hash . '-index', $empty, 0);
}
return;
}
/**
* Get cached data from memcached by id and group
*
* @param string $id The cache data id
* @param string $group The cache data group
* @param boolean $checkTime True to verify cache time expiration threshold
*
* @return mixed Boolean false on failure or a cached data string
*
* @since 12.1
*/
public function get($id, $group, $checkTime = true)
{
$cache_id = $this->_getCacheId($id, $group);
$back = self::$_db->get($cache_id);
return $back;
}
/**
* Get all cached data
*
* @return array data
*
* @since 12.1
*/
public function getAll()
{
parent::getAll();
$keys = self::$_db->get($this->_hash . '-index');
$secret = $this->_hash;
$data = array();
if (!empty($keys) && is_array($keys))
{
foreach ($keys as $key)
{
if (empty($key))
{
continue;
}
$namearr = explode('-', $key->name);
if ($namearr !== false && $namearr[0] == $secret && $namearr[1] == 'cache')
{
$group = $namearr[2];
if (!isset($data[$group]))
{
$item = new JCacheStorageHelper($group);
}
else
{
$item = $data[$group];
}
$item->updateSize($key->size / 1024);
$data[$group] = $item;
}
}
}
return $data;
}
/**
* Store the data to memcached by id and group
*
* @param string $id The cache data id
* @param string $group The cache data group
* @param string $data The data to store in cache
*
* @return boolean True on success, false otherwise
*
* @since 12.1
*/
public function store($id, $group, $data)
{
$cache_id = $this->_getCacheId($id, $group);
if (!$this->lockindex())
{
return false;
}
$index = self::$_db->get($this->_hash . '-index');
if ($index === false)
{
$index = array();
}
$tmparr = new stdClass;
$tmparr->name = $cache_id;
$tmparr->size = strlen($data);
$index[] = $tmparr;
self::$_db->replace($this->_hash . '-index', $index, 0);
$this->unlockindex();
// Prevent double writes, write only if it doesn't exist else replace
if (!self::$_db->replace($cache_id, $data, $this->_lifetime))
{
self::$_db->set($cache_id, $data, $this->_lifetime);
}
return true;
}
/**
* Remove a cached data entry by id and group
*
* @param string $id The cache data id
* @param string $group The cache data group
*
* @return boolean True on success, false otherwise
*
* @since 12.1
*/
public function remove($id, $group)
{
$cache_id = $this->_getCacheId($id, $group);
if (!$this->lockindex())
{
return false;
}
$index = self::$_db->get($this->_hash . '-index');
if ($index === false)
{
$index = array();
}
foreach ($index as $key => $value)
{
if ($value->name == $cache_id)
{
unset($index[$key]);
}
break;
}
self::$_db->replace($this->_hash . '-index', $index, 0);
$this->unlockindex();
return self::$_db->delete($cache_id);
}
/**
* Clean cache for a group given a mode.
*
* @param string $group The cache data group
* @param string $mode The mode for cleaning cache [group|notgroup]
* group mode : cleans all cache in the group
* notgroup mode : cleans all cache not in the group
*
* @return boolean True on success, false otherwise
*
* @since 12.1
*/
public function clean($group, $mode = null)
{
if (!$this->lockindex())
{
return false;
}
$index = self::$_db->get($this->_hash . '-index');
if ($index === false)
{
$index = array();
}
$secret = $this->_hash;
foreach ($index as $key => $value)
{
if (strpos($value->name, $secret . '-cache-' . $group . '-') === 0 xor $mode != 'group')
{
self::$_db->delete($value->name, 0);
unset($index[$key]);
}
}
self::$_db->replace($this->_hash . '-index', $index, 0);
$this->unlockindex();
return true;
}
/**
* Test to see if the cache storage is available.
*
* @return boolean True on success, false otherwise.
*
* @since 12.1
*/
public static function isSupported()
{
if ((extension_loaded('memcached') && class_exists('Memcached')) != true)
{
return false;
}
$config = JFactory::getConfig();
$host = $config->get('memcache_server_host', 'localhost');
$port = $config->get('memcache_server_port', 11211);
$memcached = new Memcached;
$memcachedtest = @$memcached->addServer($host, $port);
if (!$memcachedtest)
{
return false;
}
else
{
return true;
}
}
/**
* Lock cached item - override parent as this is more efficient
*
* @param string $id The cache data id
* @param string $group The cache data group
* @param integer $locktime Cached item max lock time
*
* @return boolean True on success, false otherwise.
*
* @since 12.1
*/
public function lock($id, $group, $locktime)
{
$returning = new stdClass;
$returning->locklooped = false;
$looptime = $locktime * 10;
$cache_id = $this->_getCacheId($id, $group);
if (!$this->lockindex())
{
return false;
}
$index = self::$_db->get($this->_hash . '-index');
if ($index === false)
{
$index = array();
}
$tmparr = new stdClass;
$tmparr->name = $cache_id;
$tmparr->size = 1;
$index[] = $tmparr;
self::$_db->replace($this->_hash . '-index', $index, 0);
$this->unlockindex();
$data_lock = self::$_db->add($cache_id . '_lock', 1, $locktime);
if ($data_lock === false)
{
$lock_counter = 0;
// Loop until you find that the lock has been released.
// That implies that data get from other thread has finished
while ($data_lock === false)
{
if ($lock_counter > $looptime)
{
$returning->locked = false;
$returning->locklooped = true;
break;
}
usleep(100);
$data_lock = self::$_db->add($cache_id . '_lock', 1, $locktime);
$lock_counter++;
}
}
$returning->locked = $data_lock;
return $returning;
}
/**
* Unlock cached item - override parent for cacheid compatibility with lock
*
* @param string $id The cache data id
* @param string $group The cache data group
*
* @return boolean True on success, false otherwise.
*
* @since 12.1
*/
public function unlock($id, $group = null)
{
$cache_id = $this->_getCacheId($id, $group) . '_lock';
if (!$this->lockindex())
{
return false;
}
$index = self::$_db->get($this->_hash . '-index');
if ($index === false)
{
$index = array();
}
foreach ($index as $key => $value)
{
if ($value->name == $cache_id)
{
unset($index[$key]);
}
break;
}
self::$_db->replace($this->_hash . '-index', $index, 0);
$this->unlockindex();
return self::$_db->delete($cache_id);
}
/**
* Lock cache index
*
* @return boolean True on success, false otherwise.
*
* @since 12.1
*/
protected function lockindex()
{
$looptime = 300;
$data_lock = self::$_db->add($this->_hash . '-index_lock', 1, 30);
if ($data_lock === false)
{
$lock_counter = 0;
// Loop until you find that the lock has been released. that implies that data get from other thread has finished
while ($data_lock === false)
{
if ($lock_counter > $looptime)
{
return false;
break;
}
usleep(100);
$data_lock = self::$_db->add($this->_hash . '-index_lock', 1, 30);
$lock_counter++;
}
}
return true;
}
/**
* Unlock cache index
*
* @return boolean True on success, false otherwise.
*
* @since 12.1
*/
protected function unlockindex()
{
return self::$_db->delete($this->_hash . '-index_lock');
}
}

View File

@ -0,0 +1,194 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Cache
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* WINCACHE cache storage handler
*
* @package Joomla.Platform
* @subpackage Cache
* @see http://php.net/manual/en/book.wincache.php
* @since 11.1
*/
class JCacheStorageWincache extends JCacheStorage
{
/**
* Constructor
*
* @param array $options Optional parameters.
*
* @since 11.1
*/
public function __construct($options = array())
{
parent::__construct($options);
}
/**
* Get cached data from WINCACHE by id and group
*
* @param string $id The cache data id
* @param string $group The cache data group
* @param boolean $checkTime True to verify cache time expiration threshold
*
* @return mixed Boolean false on failure or a cached data string
*
* @since 11.1
*/
public function get($id, $group, $checkTime = true)
{
$cache_id = $this->_getCacheId($id, $group);
$cache_content = wincache_ucache_get($cache_id);
return $cache_content;
}
/**
* Get all cached data
*
* @return array data
*
* @since 11.1
*/
public function getAll()
{
parent::getAll();
$allinfo = wincache_ucache_info();
$keys = $allinfo['cache_entries'];
$secret = $this->_hash;
$data = array();
foreach ($keys as $key)
{
$name = $key['key_name'];
$namearr = explode('-', $name);
if ($namearr !== false && $namearr[0] == $secret && $namearr[1] == 'cache')
{
$group = $namearr[2];
if (!isset($data[$group]))
{
$item = new JCacheStorageHelper($group);
}
else
{
$item = $data[$group];
}
if (isset($key['value_size']))
{
$item->updateSize($key['value_size'] / 1024);
}
else
{
// Dummy, WINCACHE version is too low.
$item->updateSize(1);
}
$data[$group] = $item;
}
}
return $data;
}
/**
* Store the data to WINCACHE by id and group
*
* @param string $id The cache data id
* @param string $group The cache data group
* @param string $data The data to store in cache
*
* @return boolean True on success, false otherwise
*
* @since 11.1
*/
public function store($id, $group, $data)
{
$cache_id = $this->_getCacheId($id, $group);
return wincache_ucache_set($cache_id, $data, $this->_lifetime);
}
/**
* Remove a cached data entry by id and group
*
* @param string $id The cache data id
* @param string $group The cache data group
*
* @return boolean True on success, false otherwise
*
* @since 11.1
*/
public function remove($id, $group)
{
$cache_id = $this->_getCacheId($id, $group);
return wincache_ucache_delete($cache_id);
}
/**
* Clean cache for a group given a mode.
*
* @param string $group The cache data group
* @param string $mode The mode for cleaning cache [group|notgroup]
* group mode : cleans all cache in the group
* notgroup mode : cleans all cache not in the group
*
* @return boolean True on success, false otherwise
*
* @since 11.1
*/
public function clean($group, $mode = null)
{
$allinfo = wincache_ucache_info();
$keys = $allinfo['cache_entries'];
$secret = $this->_hash;
foreach ($keys as $key)
{
if (strpos($key['key_name'], $secret . '-cache-' . $group . '-') === 0 xor $mode != 'group')
{
wincache_ucache_delete($key['key_name']);
}
}
return true;
}
/**
* Force garbage collect expired cache data as items are removed only on get/add/delete/info etc
*
* @return boolean True on success, false otherwise.
*
* @since 11.1
*/
public function gc()
{
$allinfo = wincache_ucache_info();
$keys = $allinfo['cache_entries'];
$secret = $this->_hash;
foreach ($keys as $key)
{
if (strpos($key['key_name'], $secret . '-cache-'))
{
wincache_ucache_get($key['key_name']);
}
}
}
/**
* Test to see if the cache storage is available.
*
* @return boolean True on success, false otherwise.
*
* @since 12.1
*/
public static function isSupported()
{
$test = extension_loaded('wincache') && function_exists('wincache_ucache_get') && !strcmp(ini_get('wincache.ucenabled'), '1');
return $test;
}
}

View File

@ -0,0 +1,209 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Cache
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* XCache cache storage handler
*
* @package Joomla.Platform
* @subpackage Cache
* @link http://xcache.lighttpd.net/
* @since 11.1
*/
class JCacheStorageXcache extends JCacheStorage
{
/**
* Get cached data by id and group
*
* @param string $id The cache data id
* @param string $group The cache data group
* @param boolean $checkTime True to verify cache time expiration threshold
*
* @return mixed Boolean false on failure or a cached data string
*
* @since 11.1
*/
public function get($id, $group, $checkTime = true)
{
$cache_id = $this->_getCacheId($id, $group);
$cache_content = xcache_get($cache_id);
if ($cache_content === null)
{
return false;
}
return $cache_content;
}
/**
* Get all cached data
*
* This requires the php.ini setting xcache.admin.enable_auth = Off.
*
* @return array data
*
* @since 11.1
*/
public function getAll()
{
parent::getAll();
$allinfo = xcache_list(XC_TYPE_VAR, 0);
$keys = $allinfo['cache_list'];
$secret = $this->_hash;
$data = array();
foreach ($keys as $key)
{
$namearr = explode('-', $key['name']);
if ($namearr !== false && $namearr[0] == $secret && $namearr[1] == 'cache')
{
$group = $namearr[2];
if (!isset($data[$group]))
{
$item = new JCacheStorageHelper($group);
}
else
{
$item = $data[$group];
}
$item->updateSize($key['size'] / 1024);
$data[$group] = $item;
}
}
return $data;
}
/**
* Store the data by id and group
*
* @param string $id The cache data id
* @param string $group The cache data group
* @param string $data The data to store in cache
*
* @return boolean True on success, false otherwise
*
* @since 11.1
*/
public function store($id, $group, $data)
{
$cache_id = $this->_getCacheId($id, $group);
$store = xcache_set($cache_id, $data, $this->_lifetime);
return $store;
}
/**
* Remove a cached data entry by id and group
*
* @param string $id The cache data id
* @param string $group The cache data group
*
* @return boolean True on success, false otherwise
*
* @since 11.1
*/
public function remove($id, $group)
{
$cache_id = $this->_getCacheId($id, $group);
if (!xcache_isset($cache_id))
{
return true;
}
return xcache_unset($cache_id);
}
/**
* Clean cache for a group given a mode.
*
* This requires the php.ini setting xcache.admin.enable_auth = Off.
*
* @param string $group The cache data group
* @param string $mode The mode for cleaning cache [group|notgroup]
* group mode : cleans all cache in the group
* notgroup mode : cleans all cache not in the group
*
* @return boolean True on success, false otherwise
*
* @since 11.1
*/
public function clean($group, $mode = null)
{
$allinfo = xcache_list(XC_TYPE_VAR, 0);
$keys = $allinfo['cache_list'];
$secret = $this->_hash;
foreach ($keys as $key)
{
if (strpos($key['name'], $secret . '-cache-' . $group . '-') === 0 xor $mode != 'group')
{
xcache_unset($key['name']);
}
}
return true;
}
/**
* Garbage collect expired cache data
*
* This is a dummy, since xcache has built in garbage collector, turn it
* on in php.ini by changing default xcache.gc_interval setting from
* 0 to 3600 (=1 hour)
*
* @return boolean True on success, false otherwise.
*
* @since 11.1
*/
public function gc()
{
/*
$now = time();
$cachecount = xcache_count(XC_TYPE_VAR);
for ($i = 0; $i < $cachecount; $i ++) {
$allinfo = xcache_list(XC_TYPE_VAR, $i);
$keys = $allinfo ['cache_list'];
foreach($keys as $key) {
if (strstr($key['name'], $this->_hash)) {
if (($key['ctime'] + $this->_lifetime ) < $this->_now) xcache_unset($key['name']);
}
}
}
*/
return true;
}
/**
* Test to see if the cache storage is available.
*
* @return boolean True on success, false otherwise.
*
* @since 12.1
*/
public static function isSupported()
{
return (extension_loaded('xcache'));
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,240 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Client
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* Client helper class
*
* @package Joomla.Platform
* @subpackage Client
* @since 11.1
*/
class JClientHelper
{
/**
* Method to return the array of client layer configuration options
*
* @param string $client Client name, currently only 'ftp' is supported
* @param boolean $force Forces re-creation of the login credentials. Set this to
* true if login credentials in the session storage have changed
*
* @return array Client layer configuration options, consisting of at least
* these fields: enabled, host, port, user, pass, root
*
* @since 11.1
*/
public static function getCredentials($client, $force = false)
{
static $credentials = array();
$client = strtolower($client);
if (!isset($credentials[$client]) || $force)
{
$config = JFactory::getConfig();
// Fetch the client layer configuration options for the specific client
switch ($client)
{
case 'ftp':
$options = array(
'enabled' => $config->get('ftp_enable'),
'host' => $config->get('ftp_host'),
'port' => $config->get('ftp_port'),
'user' => $config->get('ftp_user'),
'pass' => $config->get('ftp_pass'),
'root' => $config->get('ftp_root'));
break;
default:
$options = array('enabled' => false, 'host' => '', 'port' => '', 'user' => '', 'pass' => '', 'root' => '');
break;
}
// If user and pass are not set in global config lets see if they are in the session
if ($options['enabled'] == true && ($options['user'] == '' || $options['pass'] == ''))
{
$session = JFactory::getSession();
$options['user'] = $session->get($client . '.user', null, 'JClientHelper');
$options['pass'] = $session->get($client . '.pass', null, 'JClientHelper');
}
// If user or pass are missing, disable this client
if ($options['user'] == '' || $options['pass'] == '')
{
$options['enabled'] = false;
}
// Save the credentials for later use
$credentials[$client] = $options;
}
return $credentials[$client];
}
/**
* Method to set client login credentials
*
* @param string $client Client name, currently only 'ftp' is supported
* @param string $user Username
* @param string $pass Password
*
* @return boolean True if the given login credentials have been set and are valid
*
* @since 11.1
*/
public static function setCredentials($client, $user, $pass)
{
$return = false;
$client = strtolower($client);
// Test if the given credentials are valid
switch ($client)
{
case 'ftp':
$config = JFactory::getConfig();
$options = array('enabled' => $config->get('ftp_enable'), 'host' => $config->get('ftp_host'), 'port' => $config->get('ftp_port'));
if ($options['enabled'])
{
$ftp = JClientFtp::getInstance($options['host'], $options['port']);
// Test the connection and try to log in
if ($ftp->isConnected())
{
if ($ftp->login($user, $pass))
{
$return = true;
}
$ftp->quit();
}
}
break;
default:
break;
}
if ($return)
{
// Save valid credentials to the session
$session = JFactory::getSession();
$session->set($client . '.user', $user, 'JClientHelper');
$session->set($client . '.pass', $pass, 'JClientHelper');
// Force re-creation of the data saved within JClientHelper::getCredentials()
self::getCredentials($client, true);
}
return $return;
}
/**
* Method to determine if client login credentials are present
*
* @param string $client Client name, currently only 'ftp' is supported
*
* @return boolean True if login credentials are available
*
* @since 11.1
*/
public static function hasCredentials($client)
{
$return = false;
$client = strtolower($client);
// Get (unmodified) credentials for this client
switch ($client)
{
case 'ftp':
$config = JFactory::getConfig();
$options = array('enabled' => $config->get('ftp_enable'), 'user' => $config->get('ftp_user'), 'pass' => $config->get('ftp_pass'));
break;
default:
$options = array('enabled' => false, 'user' => '', 'pass' => '');
break;
}
if ($options['enabled'] == false)
{
// The client is disabled in global config, so let's pretend we are OK
$return = true;
}
elseif ($options['user'] != '' && $options['pass'] != '')
{
// Login credentials are available in global config
$return = true;
}
else
{
// Check if login credentials are available in the session
$session = JFactory::getSession();
$user = $session->get($client . '.user', null, 'JClientHelper');
$pass = $session->get($client . '.pass', null, 'JClientHelper');
if ($user != '' && $pass != '')
{
$return = true;
}
}
return $return;
}
/**
* Determine whether input fields for client settings need to be shown
*
* If valid credentials were passed along with the request, they are saved to the session.
* This functions returns an exception if invalid credentials have been given or if the
* connection to the server failed for some other reason.
*
* @param string $client The name of the client.
*
* @return mixed True, if FTP settings; JError if using legacy tree.
*
* @since 11.1
* @throws InvalidArgumentException if credentials invalid
*/
public static function setCredentialsFromRequest($client)
{
// Determine whether FTP credentials have been passed along with the current request
$input = JFactory::getApplication()->input;
$user = $input->post->getString('username', null);
$pass = $input->post->getString('password', null);
if ($user != '' && $pass != '')
{
// Add credentials to the session
if (self::setCredentials($client, $user, $pass))
{
$return = false;
}
else
{
if (class_exists('JError'))
{
$return = JError::raiseWarning('SOME_ERROR_CODE', JText::_('JLIB_CLIENT_ERROR_HELPER_SETCREDENTIALSFROMREQUEST_FAILED'));
}
else
{
throw new InvalidArgumentException('Invalid user credentials');
}
}
}
else
{
// Just determine if the FTP input fields need to be shown
$return = !self::hasCredentials('ftp');
}
return $return;
}
}

View File

@ -0,0 +1 @@
<!DOCTYPE html><title></title>

View File

@ -0,0 +1,683 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Client
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* LDAP client class
*
* @package Joomla.Platform
* @subpackage Client
* @since 12.1
*/
class JClientLdap
{
/**
* @var string Hostname of LDAP server
* @since 12.1
*/
public $host = null;
/**
* @var bool Authorization Method to use
* @since 12.1
*/
public $auth_method = null;
/**
* @var int Port of LDAP server
* @since 12.1
*/
public $port = null;
/**
* @var string Base DN (e.g. o=MyDir)
* @since 12.1
*/
public $base_dn = null;
/**
* @var string User DN (e.g. cn=Users,o=MyDir)
* @since 12.1
*/
public $users_dn = null;
/**
* @var string Search String
* @since 12.1
*/
public $search_string = null;
/**
* @var boolean Use LDAP Version 3
* @since 12.1
*/
public $use_ldapV3 = null;
/**
* @var boolean No referrals (server transfers)
* @since 11.1
*/
public $no_referrals = null;
/**
* @var boolean Negotiate TLS (encrypted communications)
* @since 12.1
*/
public $negotiate_tls = null;
/**
* @var string Username to connect to server
* @since 12.1
*/
public $username = null;
/**
*
* @var string Password to connect to server
* @since 12.1
*/
public $password = null;
/**
* @var mixed LDAP Resource Identifier
* @since 12.1
*/
private $_resource = null;
/**
*
* @var string Current DN
* @since 12.1
*/
private $_dn = null;
/**
* Constructor
*
* @param object $configObj An object of configuration variables
*
* @since 12.1
*/
public function __construct($configObj = null)
{
if (is_object($configObj))
{
$vars = get_class_vars(get_class($this));
foreach (array_keys($vars) as $var)
{
if (substr($var, 0, 1) != '_')
{
$param = $configObj->get($var);
if ($param)
{
$this->$var = $param;
}
}
}
}
}
/**
* Connect to server
*
* @return boolean True if successful
*
* @since 12.1
*/
public function connect()
{
if ($this->host == '')
{
return false;
}
$this->_resource = @ ldap_connect($this->host, $this->port);
if ($this->_resource)
{
if ($this->use_ldapV3)
{
if (!@ldap_set_option($this->_resource, LDAP_OPT_PROTOCOL_VERSION, 3))
{
return false;
}
}
if (!@ldap_set_option($this->_resource, LDAP_OPT_REFERRALS, (int) $this->no_referrals))
{
return false;
}
if ($this->negotiate_tls)
{
if (!@ldap_start_tls($this->_resource))
{
return false;
}
}
return true;
}
else
{
return false;
}
}
/**
* Close the connection
*
* @return void
*
* @since 12.1
*/
public function close()
{
@ ldap_close($this->_resource);
}
/**
* Sets the DN with some template replacements
*
* @param string $username The username
* @param string $nosub ...
*
* @return void
*
* @since 12.1
*/
public function setDN($username, $nosub = 0)
{
if ($this->users_dn == '' || $nosub)
{
$this->_dn = $username;
}
elseif (strlen($username))
{
$this->_dn = str_replace('[username]', $username, $this->users_dn);
}
else
{
$this->_dn = '';
}
}
/**
* Get the DN
*
* @return string The current dn
*
* @since 12.1
*/
public function getDN()
{
return $this->_dn;
}
/**
* Anonymously binds to LDAP directory
*
* @return array
*
* @since 12.1
*/
public function anonymous_bind()
{
$bindResult = @ldap_bind($this->_resource);
return $bindResult;
}
/**
* Binds to the LDAP directory
*
* @param string $username The username
* @param string $password The password
* @param string $nosub ...
*
* @return boolean
*
* @since 12.1
*/
public function bind($username = null, $password = null, $nosub = 0)
{
if (is_null($username))
{
$username = $this->username;
}
if (is_null($password))
{
$password = $this->password;
}
$this->setDN($username, $nosub);
$bindResult = @ldap_bind($this->_resource, $this->getDN(), $password);
return $bindResult;
}
/**
* Perform an LDAP search using comma separated search strings
*
* @param string $search search string of search values
*
* @return array Search results
*
* @since 12.1
*/
public function simple_search($search)
{
$results = explode(';', $search);
foreach ($results as $key => $result)
{
$results[$key] = '(' . $result . ')';
}
return $this->search($results);
}
/**
* Performs an LDAP search
*
* @param array $filters Search Filters (array of strings)
* @param string $dnoverride DN Override
* @param array $attributes An array of attributes to return (if empty, all fields are returned).
*
* @return array Multidimensional array of results
*
* @since 12.1
*/
public function search(array $filters, $dnoverride = null, array $attributes = array())
{
$result = array();
if ($dnoverride)
{
$dn = $dnoverride;
}
else
{
$dn = $this->base_dn;
}
$resource = $this->_resource;
foreach ($filters as $search_filter)
{
$search_result = @ldap_search($resource, $dn, $search_filter, $attributes);
if ($search_result && ($count = @ldap_count_entries($resource, $search_result)) > 0)
{
for ($i = 0; $i < $count; $i++)
{
$result[$i] = array();
if (!$i)
{
$firstentry = @ldap_first_entry($resource, $search_result);
}
else
{
$firstentry = @ldap_next_entry($resource, $firstentry);
}
// Load user-specified attributes
$result_array = @ldap_get_attributes($resource, $firstentry);
// LDAP returns an array of arrays, fit this into attributes result array
foreach ($result_array as $ki => $ai)
{
if (is_array($ai))
{
$subcount = $ai['count'];
$result[$i][$ki] = array();
for ($k = 0; $k < $subcount; $k++)
{
$result[$i][$ki][$k] = $ai[$k];
}
}
}
$result[$i]['dn'] = @ldap_get_dn($resource, $firstentry);
}
}
}
return $result;
}
/**
* Replace an entry and return a true or false result
*
* @param string $dn The DN which contains the attribute you want to replace
* @param string $attribute The attribute values you want to replace
*
* @return mixed result of comparison (true, false, -1 on error)
*
* @since 12.1
*/
public function replace($dn, $attribute)
{
return @ldap_mod_replace($this->_resource, $dn, $attribute);
}
/**
* Modifies an entry and return a true or false result
*
* @param string $dn The DN which contains the attribute you want to modify
* @param string $attribute The attribute values you want to modify
*
* @return mixed result of comparison (true, false, -1 on error)
*
* @since 12.1
*/
public function modify($dn, $attribute)
{
return @ldap_modify($this->_resource, $dn, $attribute);
}
/**
* Removes attribute value from given dn and return a true or false result
*
* @param string $dn The DN which contains the attribute you want to remove
* @param string $attribute The attribute values you want to remove
*
* @return mixed result of comparison (true, false, -1 on error)
*
* @since 12.1
*/
public function remove($dn, $attribute)
{
$resource = $this->_resource;
return @ldap_mod_del($resource, $dn, $attribute);
}
/**
* Compare an entry and return a true or false result
*
* @param string $dn The DN which contains the attribute you want to compare
* @param string $attribute The attribute whose value you want to compare
* @param string $value The value you want to check against the LDAP attribute
*
* @return mixed result of comparison (true, false, -1 on error)
*
* @since 12.1
*/
public function compare($dn, $attribute, $value)
{
return @ldap_compare($this->_resource, $dn, $attribute, $value);
}
/**
* Read all or specified attributes of given dn
*
* @param string $dn The DN of the object you want to read
*
* @return mixed array of attributes or -1 on error
*
* @since 12.1
*/
public function read($dn)
{
$base = substr($dn, strpos($dn, ',') + 1);
$cn = substr($dn, 0, strpos($dn, ','));
$result = @ldap_read($this->_resource, $base, $cn);
if ($result)
{
return @ldap_get_entries($this->_resource, $result);
}
else
{
return $result;
}
}
/**
* Deletes a given DN from the tree
*
* @param string $dn The DN of the object you want to delete
*
* @return boolean Result of operation
*
* @since 12.1
*/
public function delete($dn)
{
return @ldap_delete($this->_resource, $dn);
}
/**
* Create a new DN
*
* @param string $dn The DN where you want to put the object
* @param array $entries An array of arrays describing the object to add
*
* @return boolean Result of operation
*
* @since 12.1
*/
public function create($dn, array $entries)
{
return @ldap_add($this->_resource, $dn, $entries);
}
/**
* Add an attribute to the given DN
* Note: DN has to exist already
*
* @param string $dn The DN of the entry to add the attribute
* @param array $entry An array of arrays with attributes to add
*
* @return boolean Result of operation
*
* @since 12.1
*/
public function add($dn, array $entry)
{
return @ldap_mod_add($this->_resource, $dn, $entry);
}
/**
* Rename the entry
*
* @param string $dn The DN of the entry at the moment
* @param string $newdn The DN of the entry should be (only cn=newvalue)
* @param string $newparent The full DN of the parent (null by default)
* @param boolean $deleteolddn Delete the old values (default)
*
* @return boolean Result of operation
*
* @since 12.1
*/
public function rename($dn, $newdn, $newparent, $deleteolddn)
{
return @ldap_rename($this->_resource, $dn, $newdn, $newparent, $deleteolddn);
}
/**
* Returns the error message
*
* @return string error message
*
* @since 12.1
*/
public function getErrorMsg()
{
return @ldap_error($this->_resource);
}
/**
* Converts a dot notation IP address to net address (e.g. for Netware, etc)
*
* @param string $ip IP Address (e.g. xxx.xxx.xxx.xxx)
*
* @return string Net address
*
* @since 12.1
*/
public static function ipToNetAddress($ip)
{
$parts = explode('.', $ip);
$address = '1#';
foreach ($parts as $int)
{
$tmp = dechex($int);
if (strlen($tmp) != 2)
{
$tmp = '0' . $tmp;
}
$address .= '\\' . $tmp;
}
return $address;
}
/**
* Extract readable network address from the LDAP encoded networkAddress attribute.
*
* Please keep this document block and author attribution in place.
*
* Novell Docs, see: http://developer.novell.com/ndk/doc/ndslib/schm_enu/data/sdk5624.html#sdk5624
* for Address types: http://developer.novell.com/ndk/doc/ndslib/index.html?page=/ndk/doc/ndslib/schm_enu/data/sdk4170.html
* LDAP Format, String:
* taggedData = uint32String "#" octetstring
* byte 0 = uint32String = Address Type: 0= IPX Address; 1 = IP Address
* byte 1 = char = "#" - separator
* byte 2+ = octetstring - the ordinal value of the address
* Note: with eDirectory 8.6.2, the IP address (type 1) returns
* correctly, however, an IPX address does not seem to. eDir 8.7 may correct this.
* Enhancement made by Merijn van de Schoot:
* If addresstype is 8 (UDP) or 9 (TCP) do some additional parsing like still returning the IP address
*
* @param string $networkaddress The network address
*
* @return array
*
* @author Jay Burrell, Systems & Networks, Mississippi State University
* @since 12.1
*/
public static function LDAPNetAddr($networkaddress)
{
$addr = "";
$addrtype = (int) substr($networkaddress, 0, 1);
// Throw away bytes 0 and 1 which should be the addrtype and the "#" separator
$networkaddress = substr($networkaddress, 2);
if (($addrtype == 8) || ($addrtype = 9))
{
// TODO 1.6: If UDP or TCP, (TODO fill addrport and) strip portnumber information from address
$networkaddress = substr($networkaddress, (strlen($networkaddress) - 4));
}
$addrtypes = array(
'IPX',
'IP',
'SDLC',
'Token Ring',
'OSI',
'AppleTalk',
'NetBEUI',
'Socket',
'UDP',
'TCP',
'UDP6',
'TCP6',
'Reserved (12)',
'URL',
'Count');
$len = strlen($networkaddress);
if ($len > 0)
{
for ($i = 0; $i < $len; $i++)
{
$byte = substr($networkaddress, $i, 1);
$addr .= ord($byte);
if (($addrtype == 1) || ($addrtype == 8) || ($addrtype = 9))
{
// Dot separate IP addresses...
$addr .= ".";
}
}
if (($addrtype == 1) || ($addrtype == 8) || ($addrtype = 9))
{
// Strip last period from end of $addr
$addr = substr($addr, 0, strlen($addr) - 1);
}
}
else
{
$addr .= JText::_('JLIB_CLIENT_ERROR_LDAP_ADDRESS_NOT_AVAILABLE');
}
return array('protocol' => $addrtypes[$addrtype], 'address' => $addr);
}
/**
* Generates a LDAP compatible password
*
* @param string $password Clear text password to encrypt
* @param string $type Type of password hash, either md5 or SHA
*
* @return string Encrypted password
*
* @since 12.1
*/
public static function generatePassword($password, $type = 'md5')
{
switch (strtolower($type))
{
case 'sha':
$userpassword = '{SHA}' . base64_encode(pack('H*', sha1($password)));
break;
case 'md5':
default:
$userpassword = '{MD5}' . base64_encode(pack('H*', md5($password)));
break;
}
return $userpassword;
}
}
/**
* Deprecated class placeholder. You should use JClientLdap instead.
*
* @package Joomla.Platform
* @subpackage Client
* @since 11.1
* @deprecated 12.3 (Platform) & 4.0 (CMS)
*/
class JLDAP extends JClientLdap
{
/**
* Constructor
*
* @param object $configObj An object of configuration variables
*
* @since 11.1
*/
public function __construct($configObj = null)
{
JLog::add('JLDAP is deprecated. Use JClientLdap instead.', JLog::WARNING, 'deprecated');
parent::__construct($configObj);
}
}

View File

@ -0,0 +1,137 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Controller
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* Joomla Platform Base Controller Class
*
* @package Joomla.Platform
* @subpackage Controller
* @since 12.1
*/
abstract class JControllerBase implements JController
{
/**
* The application object.
*
* @var JApplicationBase
* @since 12.1
*/
protected $app;
/**
* The input object.
*
* @var JInput
* @since 12.1
*/
protected $input;
/**
* Instantiate the controller.
*
* @param JInput $input The input object.
* @param JApplicationBase $app The application object.
*
* @since 12.1
*/
public function __construct(JInput $input = null, JApplicationBase $app = null)
{
// Setup dependencies.
$this->app = isset($app) ? $app : $this->loadApplication();
$this->input = isset($input) ? $input : $this->loadInput();
}
/**
* Get the application object.
*
* @return JApplicationBase The application object.
*
* @since 12.1
*/
public function getApplication()
{
return $this->app;
}
/**
* Get the input object.
*
* @return JInput The input object.
*
* @since 12.1
*/
public function getInput()
{
return $this->input;
}
/**
* Serialize the controller.
*
* @return string The serialized controller.
*
* @since 12.1
*/
public function serialize()
{
return serialize($this->input);
}
/**
* Unserialize the controller.
*
* @param string $input The serialized controller.
*
* @return JController Supports chaining.
*
* @since 12.1
* @throws UnexpectedValueException if input is not the right class.
*/
public function unserialize($input)
{
// Setup dependencies.
$this->app = $this->loadApplication();
// Unserialize the input.
$this->input = unserialize($input);
if (!($this->input instanceof JInput))
{
throw new UnexpectedValueException(sprintf('%s::unserialize would not accept a `%s`.', get_class($this), gettype($this->input)));
}
return $this;
}
/**
* Load the application object.
*
* @return JApplicationBase The application object.
*
* @since 12.1
*/
protected function loadApplication()
{
return JFactory::getApplication();
}
/**
* Load the input object.
*
* @return JInput The input object.
*
* @since 12.1
*/
protected function loadInput()
{
return $this->app->input;
}
}

View File

@ -0,0 +1,51 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Controller
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* Joomla Platform Controller Interface
*
* @package Joomla.Platform
* @subpackage Controller
* @since 12.1
*/
interface JController extends Serializable
{
/**
* Execute the controller.
*
* @return boolean True if controller finished execution, false if the controller did not
* finish execution. A controller might return false if some precondition for
* the controller to run has not been satisfied.
*
* @since 12.1
* @throws LogicException
* @throws RuntimeException
*/
public function execute();
/**
* Get the application object.
*
* @return JApplicationBase The application object.
*
* @since 12.1
*/
public function getApplication();
/**
* Get the input object.
*
* @return JInput The input object.
*
* @since 12.1
*/
public function getInput();
}

View File

@ -0,0 +1 @@
<!DOCTYPE html><title></title>

View File

@ -0,0 +1,55 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Crypt
*
* @copyright Copyright (C) 2005 - 2011 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* JCrypt cipher interface.
*
* @package Joomla.Platform
* @subpackage Crypt
* @since 12.1
*/
interface JCryptCipher
{
/**
* Method to decrypt a data string.
*
* @param string $data The encrypted string to decrypt.
* @param JCryptKey $key The key[/pair] object to use for decryption.
*
* @return string The decrypted data string.
*
* @since 12.1
*/
public function decrypt($data, JCryptKey $key);
/**
* Method to encrypt a data string.
*
* @param string $data The data string to encrypt.
* @param JCryptKey $key The key[/pair] object to use for encryption.
*
* @return string The encrypted data string.
*
* @since 12.1
*/
public function encrypt($data, JCryptKey $key);
/**
* Method to generate a new encryption key[/pair] object.
*
* @param array $options Key generation options.
*
* @return JCryptKey
*
* @since 12.1
*/
public function generateKey(array $options = array());
}

View File

@ -0,0 +1,40 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Crypt
*
* @copyright Copyright (C) 2005 - 2011 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* JCrypt cipher for Triple DES encryption, decryption and key generation.
*
* @package Joomla.Platform
* @subpackage Crypt
* @since 12.1
*/
class JCryptCipher3DES extends JCryptCipherMcrypt
{
/**
* @var integer The mcrypt cipher constant.
* @see http://www.php.net/manual/en/mcrypt.ciphers.php
* @since 12.1
*/
protected $type = MCRYPT_3DES;
/**
* @var integer The mcrypt block cipher mode.
* @see http://www.php.net/manual/en/mcrypt.constants.php
* @since 12.1
*/
protected $mode = MCRYPT_MODE_CBC;
/**
* @var string The JCrypt key type for validation.
* @since 12.1
*/
protected $keyType = '3des';
}

View File

@ -0,0 +1,40 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Crypt
*
* @copyright Copyright (C) 2005 - 2011 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* JCrypt cipher for Blowfish encryption, decryption and key generation.
*
* @package Joomla.Platform
* @subpackage Crypt
* @since 12.1
*/
class JCryptCipherBlowfish extends JCryptCipherMcrypt
{
/**
* @var integer The mcrypt cipher constant.
* @see http://www.php.net/manual/en/mcrypt.ciphers.php
* @since 12.1
*/
protected $type = MCRYPT_BLOWFISH;
/**
* @var integer The mcrypt block cipher mode.
* @see http://www.php.net/manual/en/mcrypt.constants.php
* @since 12.1
*/
protected $mode = MCRYPT_MODE_CBC;
/**
* @var string The JCrypt key type for validation.
* @since 12.1
*/
protected $keyType = 'blowfish';
}

View File

@ -0,0 +1 @@
<!DOCTYPE html><title></title>

View File

@ -0,0 +1,175 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Crypt
*
* @copyright Copyright (C) 2005 - 2011 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* JCrypt cipher for mcrypt algorithm encryption, decryption and key generation.
*
* @package Joomla.Platform
* @subpackage Crypt
* @since 12.1
*/
abstract class JCryptCipherMcrypt implements JCryptCipher
{
/**
* @var integer The mcrypt cipher constant.
* @see http://www.php.net/manual/en/mcrypt.ciphers.php
* @since 12.1
*/
protected $type;
/**
* @var integer The mcrypt block cipher mode.
* @see http://www.php.net/manual/en/mcrypt.constants.php
* @since 12.1
*/
protected $mode;
/**
* @var string The JCrypt key type for validation.
* @since 12.1
*/
protected $keyType;
/**
* Constructor.
*
* @since 12.1
* @throws RuntimeException
*/
public function __construct()
{
if (!is_callable('mcrypt_encrypt'))
{
throw new RuntimeException('The mcrypt extension is not available.');
}
}
/**
* Method to decrypt a data string.
*
* @param string $data The encrypted string to decrypt.
* @param JCryptKey $key The key object to use for decryption.
*
* @return string The decrypted data string.
*
* @since 12.1
*/
public function decrypt($data, JCryptKey $key)
{
// Validate key.
if ($key->type != $this->keyType)
{
throw new InvalidArgumentException('Invalid key of type: ' . $key->type . '. Expected ' . $this->keyType . '.');
}
// Decrypt the data.
$decrypted = trim(mcrypt_decrypt($this->type, $key->private, $data, $this->mode, $key->public));
return $decrypted;
}
/**
* Method to encrypt a data string.
*
* @param string $data The data string to encrypt.
* @param JCryptKey $key The key object to use for encryption.
*
* @return string The encrypted data string.
*
* @since 12.1
*/
public function encrypt($data, JCryptKey $key)
{
// Validate key.
if ($key->type != $this->keyType)
{
throw new InvalidArgumentException('Invalid key of type: ' . $key->type . '. Expected ' . $this->keyType . '.');
}
// Encrypt the data.
$encrypted = mcrypt_encrypt($this->type, $key->private, $data, $this->mode, $key->public);
return $encrypted;
}
/**
* Method to generate a new encryption key object.
*
* @param array $options Key generation options.
*
* @return JCryptKey
*
* @since 12.1
*/
public function generateKey(array $options = array())
{
// Create the new encryption key object.
$key = new JCryptKey($this->keyType);
// Generate an initialisation vector based on the algorithm.
$key->public = mcrypt_create_iv(mcrypt_get_iv_size($this->type, $this->mode));
// Get the salt and password setup.
$salt = (isset($options['salt'])) ? $options['salt'] : substr(pack("h*", md5(mt_rand())), 0, 16);
$password = (isset($options['password'])) ? $options['password'] : 'J00ml4R0ck$!';
// Generate the derived key.
$key->private = $this->pbkdf2($password, $salt, mcrypt_get_key_size($this->type, $this->mode));
return $key;
}
/**
* PBKDF2 Implementation for deriving keys.
*
* @param string $p Password
* @param string $s Salt
* @param integer $kl Key length
* @param integer $c Iteration count
* @param string $a Hash algorithm
*
* @return string The derived key.
*
* @see http://en.wikipedia.org/wiki/PBKDF2
* @see http://www.ietf.org/rfc/rfc2898.txt
* @since 12.1
*/
public function pbkdf2($p, $s, $kl, $c = 10000, $a = 'sha256')
{
// Hash length.
$hl = strlen(hash($a, null, true));
// Key blocks to compute.
$kb = ceil($kl / $hl);
// Derived key.
$dk = '';
// Create the key.
for ($block = 1; $block <= $kb; $block++)
{
// Initial hash for this block.
$ib = $b = hash_hmac($a, $s . pack('N', $block), $p, true);
// Perform block iterations.
for ($i = 1; $i < $c; $i++)
{
$ib ^= ($b = hash_hmac($a, $b, $p, true));
}
// Append the iterated block.
$dk .= $ib;
}
// Return derived key of correct length.
return substr($dk, 0, $kl);
}
}

View File

@ -0,0 +1,40 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Crypt
*
* @copyright Copyright (C) 2005 - 2011 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* JCrypt cipher for Rijndael 256 encryption, decryption and key generation.
*
* @package Joomla.Platform
* @subpackage Crypt
* @since 12.1
*/
class JCryptCipherRijndael256 extends JCryptCipherMcrypt
{
/**
* @var integer The mcrypt cipher constant.
* @see http://www.php.net/manual/en/mcrypt.ciphers.php
* @since 12.1
*/
protected $type = MCRYPT_RIJNDAEL_256;
/**
* @var integer The mcrypt block cipher mode.
* @see http://www.php.net/manual/en/mcrypt.constants.php
* @since 12.1
*/
protected $mode = MCRYPT_MODE_CBC;
/**
* @var string The JCrypt key type for validation.
* @since 12.1
*/
protected $keyType = 'rijndael256';
}

View File

@ -0,0 +1,284 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Crypt
*
* @copyright Copyright (C) 2005 - 2011 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* JCrypt cipher for Simple encryption, decryption and key generation.
*
* @package Joomla.Platform
* @subpackage Crypt
* @since 12.1
*/
class JCryptCipherSimple implements JCryptCipher
{
/**
* Method to decrypt a data string.
*
* @param string $data The encrypted string to decrypt.
* @param JCryptKey $key The key[/pair] object to use for decryption.
*
* @return string The decrypted data string.
*
* @since 12.1
* @throws InvalidArgumentException
*/
public function decrypt($data, JCryptKey $key)
{
// Validate key.
if ($key->type != 'simple')
{
throw new InvalidArgumentException('Invalid key of type: ' . $key->type . '. Expected simple.');
}
$decrypted = '';
$tmp = $key->public;
// Convert the HEX input into an array of integers and get the number of characters.
$chars = $this->_hexToIntArray($data);
$charCount = count($chars);
// Repeat the key as many times as necessary to ensure that the key is at least as long as the input.
for ($i = 0; $i < $charCount; $i = strlen($tmp))
{
$tmp = $tmp . $tmp;
}
// Get the XOR values between the ASCII values of the input and key characters for all input offsets.
for ($i = 0; $i < $charCount; $i++)
{
$decrypted .= chr($chars[$i] ^ ord($tmp[$i]));
}
return $decrypted;
}
/**
* Method to encrypt a data string.
*
* @param string $data The data string to encrypt.
* @param JCryptKey $key The key[/pair] object to use for encryption.
*
* @return string The encrypted data string.
*
* @since 12.1
* @throws InvalidArgumentException
*/
public function encrypt($data, JCryptKey $key)
{
// Validate key.
if ($key->type != 'simple')
{
throw new InvalidArgumentException('Invalid key of type: ' . $key->type . '. Expected simple.');
}
$encrypted = '';
$tmp = $key->private;
// Split up the input into a character array and get the number of characters.
$chars = preg_split('//', $data, -1, PREG_SPLIT_NO_EMPTY);
$charCount = count($chars);
// Repeat the key as many times as necessary to ensure that the key is at least as long as the input.
for ($i = 0; $i < $charCount; $i = strlen($tmp))
{
$tmp = $tmp . $tmp;
}
// Get the XOR values between the ASCII values of the input and key characters for all input offsets.
for ($i = 0; $i < $charCount; $i++)
{
$encrypted .= $this->_intToHex(ord($tmp[$i]) ^ ord($chars[$i]));
}
return $encrypted;
}
/**
* Method to generate a new encryption key[/pair] object.
*
* @param array $options Key generation options.
*
* @return JCryptKey
*
* @since 12.1
*/
public function generateKey(array $options = array())
{
// Create the new encryption key[/pair] object.
$key = new JCryptKey('simple');
// Just a random key of a given length.
$key->private = $this->_getRandomKey();
$key->public = $key->private;
return $key;
}
/**
* Method to generate a random key of a given length.
*
* @param integer $length The length of the key to generate.
*
* @return string
*
* @since 12.1
*/
private function _getRandomKey($length = 256)
{
$key = '';
$salt = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$saltLength = strlen($salt);
// Build the random key.
for ($i = 0; $i < $length; $i++)
{
$key .= $salt[mt_rand(0, $saltLength - 1)];
}
return $key;
}
/**
* Convert hex to an integer
*
* @param string $s The hex string to convert.
* @param integer $i The offset?
*
* @return integer
*
* @since 11.1
*/
private function _hexToInt($s, $i)
{
$j = (int) $i * 2;
$k = 0;
$s1 = (string) $s;
// Get the character at position $j.
$c = substr($s1, $j, 1);
// Get the character at position $j + 1.
$c1 = substr($s1, $j + 1, 1);
switch ($c)
{
case 'A':
$k += 160;
break;
case 'B':
$k += 176;
break;
case 'C':
$k += 192;
break;
case 'D':
$k += 208;
break;
case 'E':
$k += 224;
break;
case 'F':
$k += 240;
break;
case ' ':
$k += 0;
break;
default:
(int) $k = $k + (16 * (int) $c);
break;
}
switch ($c1)
{
case 'A':
$k += 10;
break;
case 'B':
$k += 11;
break;
case 'C':
$k += 12;
break;
case 'D':
$k += 13;
break;
case 'E':
$k += 14;
break;
case 'F':
$k += 15;
break;
case ' ':
$k += 0;
break;
default:
$k += (int) $c1;
break;
}
return $k;
}
/**
* Convert hex to an array of integers
*
* @param string $hex The hex string to convert to an integer array.
*
* @return array An array of integers.
*
* @since 11.1
*/
private function _hexToIntArray($hex)
{
$array = array();
$j = (int) strlen($hex) / 2;
for ($i = 0; $i < $j; $i++)
{
$array[$i] = (int) $this->_hexToInt($hex, $i);
}
return $array;
}
/**
* Convert an integer to a hexadecimal string.
*
* @param integer $i An integer value to convert to a hex string.
*
* @return string
*
* @since 11.1
*/
private function _intToHex($i)
{
// Sanitize the input.
$i = (int) $i;
// Get the first character of the hexadecimal string if there is one.
$j = (int) ($i / 16);
if ($j === 0)
{
$s = ' ';
}
else
{
$s = strtoupper(dechex($j));
}
// Get the second character of the hexadecimal string.
$k = $i - $j * 16;
$s = $s . strtoupper(dechex($k));
return $s;
}
}

View File

@ -0,0 +1,252 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Crypt
*
* @copyright Copyright (C) 2005 - 2011 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* JCrypt is a Joomla Platform class for handling basic encryption/decryption of data.
*
* @package Joomla.Platform
* @subpackage Crypt
* @since 12.1
*/
class JCrypt
{
/**
* @var JCryptCipher The encryption cipher object.
* @since 12.1
*/
private $_cipher;
/**
* @var JCryptKey The encryption key[/pair)].
* @since 12.1
*/
private $_key;
/**
* Object Constructor takes an optional key to be used for encryption/decryption. If no key is given then the
* secret word from the configuration object is used.
*
* @param JCryptCipher $cipher The encryption cipher object.
* @param JCryptKey $key The encryption key[/pair)].
*
* @since 12.1
*/
public function __construct(JCryptCipher $cipher = null, JCryptKey $key = null)
{
// Set the encryption key[/pair)].
$this->_key = $key;
// Set the encryption cipher.
$this->_cipher = isset($cipher) ? $cipher : new JCryptCipherSimple;
}
/**
* Method to decrypt a data string.
*
* @param string $data The encrypted string to decrypt.
*
* @return string The decrypted data string.
*
* @since 12.1
*/
public function decrypt($data)
{
return $this->_cipher->decrypt($data, $this->_key);
}
/**
* Method to encrypt a data string.
*
* @param string $data The data string to encrypt.
*
* @return string The encrypted data string.
*
* @since 12.1
*/
public function encrypt($data)
{
return $this->_cipher->encrypt($data, $this->_key);
}
/**
* Method to generate a new encryption key[/pair] object.
*
* @param array $options Key generation options.
*
* @return JCryptKey
*
* @since 12.1
*/
public function generateKey(array $options = array())
{
return $this->_cipher->generateKey($options);
}
/**
* Method to set the encryption key[/pair] object.
*
* @param JCryptKey $key The key object to set.
*
* @return JCrypt
*
* @since 12.1
*/
public function setKey(JCryptKey $key)
{
$this->_key = $key;
return $this;
}
/**
* Generate random bytes.
*
* @param integer $length Length of the random data to generate
*
* @return string Random binary data
*
* @since 12.1
*/
public static function genRandomBytes($length = 16)
{
$sslStr = '';
/*
* if a secure randomness generator exists and we don't
* have a buggy PHP version use it.
*/
if (function_exists('openssl_random_pseudo_bytes')
&& (version_compare(PHP_VERSION, '5.3.4') >= 0 || IS_WIN))
{
$sslStr = openssl_random_pseudo_bytes($length, $strong);
if ($strong)
{
return $sslStr;
}
}
/*
* Collect any entropy available in the system along with a number
* of time measurements of operating system randomness.
*/
$bitsPerRound = 2;
$maxTimeMicro = 400;
$shaHashLength = 20;
$randomStr = '';
$total = $length;
// Check if we can use /dev/urandom.
$urandom = false;
$handle = null;
// This is PHP 5.3.3 and up
if (function_exists('stream_set_read_buffer') && @is_readable('/dev/urandom'))
{
$handle = @fopen('/dev/urandom', 'rb');
if ($handle)
{
$urandom = true;
}
}
while ($length > strlen($randomStr))
{
$bytes = ($total > $shaHashLength)? $shaHashLength : $total;
$total -= $bytes;
/*
* Collect any entropy available from the PHP system and filesystem.
* If we have ssl data that isn't strong, we use it once.
*/
$entropy = rand() . uniqid(mt_rand(), true) . $sslStr;
$entropy .= implode('', @fstat(fopen(__FILE__, 'r')));
$entropy .= memory_get_usage();
$sslStr = '';
if ($urandom)
{
stream_set_read_buffer($handle, 0);
$entropy .= @fread($handle, $bytes);
}
else
{
/*
* There is no external source of entropy so we repeat calls
* to mt_rand until we are assured there's real randomness in
* the result.
*
* Measure the time that the operations will take on average.
*/
$samples = 3;
$duration = 0;
for ($pass = 0; $pass < $samples; ++$pass)
{
$microStart = microtime(true) * 1000000;
$hash = sha1(mt_rand(), true);
for ($count = 0; $count < 50; ++$count)
{
$hash = sha1($hash, true);
}
$microEnd = microtime(true) * 1000000;
$entropy .= $microStart . $microEnd;
if ($microStart >= $microEnd)
{
$microEnd += 1000000;
}
$duration += $microEnd - $microStart;
}
$duration = $duration / $samples;
/*
* Based on the average time, determine the total rounds so that
* the total running time is bounded to a reasonable number.
*/
$rounds = (int) (($maxTimeMicro / $duration) * 50);
/*
* Take additional measurements. On average we can expect
* at least $bitsPerRound bits of entropy from each measurement.
*/
$iter = $bytes * (int) ceil(8 / $bitsPerRound);
for ($pass = 0; $pass < $iter; ++$pass)
{
$microStart = microtime(true);
$hash = sha1(mt_rand(), true);
for ($count = 0; $count < $rounds; ++$count)
{
$hash = sha1($hash, true);
}
$entropy .= $microStart . microtime(true);
}
}
$randomStr .= sha1($entropy, true);
}
if ($urandom)
{
@fclose($handle);
}
return substr($randomStr, 0, $length);
}
}

View File

@ -0,0 +1 @@
<!DOCTYPE html><title></title>

View File

@ -0,0 +1,80 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Crypt
*
* @copyright Copyright (C) 2005 - 2011 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* Encryption key object for the Joomla Platform.
*
* @property-read string $type The key type.
*
* @package Joomla.Platform
* @subpackage Crypt
* @since 12.1
*/
class JCryptKey
{
/**
* @var string The private key.
* @since 12.1
*/
public $private;
/**
* @var string The public key.
* @since 12.1
*/
public $public;
/**
* @var string The key type.
* @since 12.1
*/
protected $type;
/**
* Constructor.
*
* @param string $type The key type.
* @param string $private The private key.
* @param string $public The public key.
*
* @since 12.1
*/
public function __construct($type, $private = null, $public = null)
{
// Set the key type.
$this->type = (string) $type;
// Set the optional public/private key strings.
$this->private = isset($private) ? (string) $private : null;
$this->public = isset($public) ? (string) $public : null;
}
/**
* Magic method to return some protected property values.
*
* @param string $name The name of the property to return.
*
* @return mixed
*
* @since 12.1
*/
public function __get($name)
{
if ($name == 'type')
{
return $this->type;
}
else
{
trigger_error('Cannot access property ' . __CLASS__ . '::' . $name, E_USER_WARNING);
}
}
}

View File

@ -0,0 +1,72 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Crypt
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* Joomla Platform Password Hashing Interface
*
* @package Joomla.Platform
* @subpackage Crypt
* @since 12.2
*/
interface JCryptPassword
{
const BLOWFISH = '$2y$';
const JOOMLA = 'Joomla';
const PBKDF = '$pbkdf$';
const MD5 = '$1$';
/**
* Creates a password hash
*
* @param string $password The password to hash.
* @param string $type The type of hash. This determines the prefix of the hashing function.
*
* @return string The hashed password.
*
* @since 12.2
*/
public function create($password, $type = null);
/**
* Verifies a password hash
*
* @param string $password The password to verify.
* @param string $hash The password hash to check.
*
* @return boolean True if the password is valid, false otherwise.
*
* @since 12.2
*/
public function verify($password, $hash);
/**
* Sets a default prefix
*
* @param string $type The prefix to set as default
*
* @return void
*
* @since 12.3
*/
public function setDefaultType($type);
/**
* Gets the default type
*
* @return void
*
* @since 12.3
*/
public function getDefaultType();
}

View File

@ -0,0 +1 @@
<!DOCTYPE html><title></title>

View File

@ -0,0 +1,190 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Crypt
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* Joomla Platform Password Crypter
*
* @package Joomla.Platform
* @subpackage Crypt
* @since 12.2
*/
class JCryptPasswordSimple implements JCryptPassword
{
/**
* @var integer The cost parameter for hashing algorithms.
* @since 12.2
*/
protected $cost = 10;
/**
* @var string The default hash type
* @since 12.3
*/
protected $defaultType = '$2y$';
/**
* Creates a password hash
*
* @param string $password The password to hash.
* @param string $type The hash type.
*
* @return string The hashed password.
*
* @since 12.2
* @throws InvalidArgumentException
*/
public function create($password, $type = null)
{
if (empty($type))
{
$type = $this->defaultType;
}
switch ($type)
{
case '$2a$':
case JCryptPassword::BLOWFISH:
if (version_compare(PHP_VERSION, '5.3.7') >= 0)
{
$type = '$2y$';
}
else
{
$type = '$2a$';
}
$salt = $type . str_pad($this->cost, 2, '0', STR_PAD_LEFT) . '$' . $this->getSalt(22);
return crypt($password, $salt);
case JCryptPassword::MD5:
$salt = $this->getSalt(12);
$salt = '$1$' . $salt;
return crypt($password, $salt);
case JCryptPassword::JOOMLA:
$salt = $this->getSalt(32);
return md5($password . $salt) . ':' . $salt;
default:
throw new InvalidArgumentException(sprintf('Hash type %s is not supported', $type));
break;
}
}
/**
* Sets the cost parameter for the generated hash for algorithms that use a cost factor.
*
* @param integer $cost The new cost value.
*
* @return void
*
* @since 12.2
*/
public function setCost($cost)
{
$this->cost = $cost;
}
/**
* Generates a salt of specified length. The salt consists of characters in the set [./0-9A-Za-z].
*
* @param integer $length The number of characters to return.
*
* @return string The string of random characters.
*
* @since 12.2
*/
protected function getSalt($length)
{
$bytes = ceil($length * 6 / 8);
$randomData = str_replace('+', '.', base64_encode(JCrypt::genRandomBytes($bytes)));
return substr($randomData, 0, $length);
}
/**
* Verifies a password hash
*
* @param string $password The password to verify.
* @param string $hash The password hash to check.
*
* @return boolean True if the password is valid, false otherwise.
*
* @since 12.2
*/
public function verify($password, $hash)
{
// Check if the hash is a blowfish hash.
if (substr($hash, 0, 4) == '$2a$' || substr($hash, 0, 4) == '$2y$')
{
if (version_compare(PHP_VERSION, '5.3.7') >= 0)
{
$type = '$2y$';
}
else
{
$type = '$2a$';
}
$hash = $type . substr($hash, 4);
return (crypt($password, $hash) === $hash);
}
// Check if the hash is an MD5 hash.
if (substr($hash, 0, 3) == '$1$')
{
return (crypt($password, $hash) === $hash);
}
// Check if the hash is a Joomla hash.
if (preg_match('#[a-z0-9]{32}:[A-Za-z0-9]{32}#', $hash) === 1)
{
return md5($password . substr($hash, 33)) == substr($hash, 0, 32);
}
return false;
}
/**
* Sets a default type
*
* @param string $type The value to set as default.
*
* @return void
*
* @since 12.3
*/
public function setDefaultType($type)
{
if (!empty($type))
{
$this->defaultType = $type;
}
}
/**
* Gets the default type
*
* @return string $type The default type
*
* @since 12.3
*/
public function getDefaultType()
{
return $this->defaultType;
}
}

View File

@ -0,0 +1,332 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Data
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* JData is a class that is used to store data but allowing you to access the data
* by mimicking the way PHP handles class properties.
*
* @package Joomla.Platform
* @subpackage Data
* @since 12.3
*/
class JData implements JDataDumpable, IteratorAggregate, JsonSerializable, Countable
{
/**
* The data properties.
*
* @var array
* @since 12.3
*/
private $_properties = array();
/**
* The class constructor.
*
* @param mixed $properties Either an associative array or another object
* by which to set the initial properties of the new object.
*
* @since 12.3
* @throws InvalidArgumentException
*/
public function __construct($properties = array())
{
// Check the properties input.
if (!empty($properties))
{
// Bind the properties.
$this->bind($properties);
}
}
/**
* The magic get method is used to get a data property.
*
* This method is a public proxy for the protected getProperty method.
*
* Note: Magic __get does not allow recursive calls. This can be tricky because the error generated by recursing into
* __get is "Undefined property: {CLASS}::{PROPERTY}" which is misleading. This is relevant for this class because
* requesting a non-visible property can trigger a call to a sub-function. If that references the property directly in
* the object, it will cause a recursion into __get.
*
* @param string $property The name of the data property.
*
* @return mixed The value of the data property, or null if the data property does not exist.
*
* @see JData::getProperty()
* @since 12.3
*/
public function __get($property)
{
return $this->getProperty($property);
}
/**
* The magic isset method is used to check the state of an object property.
*
* @param string $property The name of the data property.
*
* @return boolean True if set, otherwise false is returned.
*
* @since 12.3
*/
public function __isset($property)
{
return isset($this->_properties[$property]);
}
/**
* The magic set method is used to set a data property.
*
* This is a public proxy for the protected setProperty method.
*
* @param string $property The name of the data property.
* @param mixed $value The value to give the data property.
*
* @return void
*
* @see JData::setProperty()
* @since 12.3
*/
public function __set($property, $value)
{
$this->setProperty($property, $value);
}
/**
* The magic unset method is used to unset a data property.
*
* @param string $property The name of the data property.
*
* @return void
*
* @since 12.3
*/
public function __unset($property)
{
unset($this->_properties[$property]);
}
/**
* Binds an array or object to this object.
*
* @param mixed $properties An associative array of properties or an object.
* @param boolean $updateNulls True to bind null values, false to ignore null values.
*
* @return JData Returns itself to allow chaining.
*
* @since 12.3
* @throws InvalidArgumentException
*/
public function bind($properties, $updateNulls = true)
{
// Check the properties data type.
if (!is_array($properties) && !is_object($properties))
{
throw new InvalidArgumentException(sprintf('%s(%s)', __METHOD__, gettype($properties)));
}
// Check if the object is traversable.
if ($properties instanceof Traversable)
{
// Convert iterator to array.
$properties = iterator_to_array($properties);
}
// Check if the object needs to be converted to an array.
elseif (is_object($properties))
{
// Convert properties to an array.
$properties = (array) $properties;
}
// Bind the properties.
foreach ($properties as $property => $value)
{
// Check if the value is null and should be bound.
if ($value === null && !$updateNulls)
{
continue;
}
// Set the property.
$this->setProperty($property, $value);
}
return $this;
}
/**
* Dumps the data properties into a stdClass object, recursively if appropriate.
*
* @param integer $depth The maximum depth of recursion (default = 3).
* For example, a depth of 0 will return a stdClass with all the properties in native
* form. A depth of 1 will recurse into the first level of properties only.
* @param SplObjectStorage $dumped An array of already serialized objects that is used to avoid infinite loops.
*
* @return stdClass The data properties as a simple PHP stdClass object.
*
* @since 12.3
*/
public function dump($depth = 3, SplObjectStorage $dumped = null)
{
// Check if we should initialise the recursion tracker.
if ($dumped === null)
{
$dumped = new SplObjectStorage;
}
// Add this object to the dumped stack.
$dumped->attach($this);
// Setup a container.
$dump = new stdClass;
// Dump all object properties.
foreach (array_keys($this->_properties) as $property)
{
// Get the property.
$dump->$property = $this->dumpProperty($property, $depth, $dumped);
}
return $dump;
}
/**
* Gets this object represented as an ArrayIterator.
*
* This allows the data properties to be access via a foreach statement.
*
* @return ArrayIterator This object represented as an ArrayIterator.
*
* @see IteratorAggregate::getIterator()
* @since 12.3
*/
public function getIterator()
{
return new ArrayIterator($this->dump(0));
}
/**
* Gets the data properties in a form that can be serialised to JSON format.
*
* @return string An object that can be serialised by json_encode().
*
* @since 12.3
*/
public function jsonSerialize()
{
return $this->dump();
}
/**
* Dumps a data property.
*
* If recursion is set, this method will dump any object implementing JDumpable (like JData and JDataSet); it will
* convert a JDate object to a string; and it will convert a JRegistry to an object.
*
* @param string $property The name of the data property.
* @param integer $depth The current depth of recursion (a value of 0 will ignore recursion).
* @param SplObjectStorage $dumped An array of already serialized objects that is used to avoid infinite loops.
*
* @return mixed The value of the dumped property.
*
* @since 12.3
*/
protected function dumpProperty($property, $depth, SplObjectStorage $dumped)
{
$value = $this->getProperty($property);
if ($depth > 0)
{
// Check if the object is also an dumpable object.
if ($value instanceof JDataDumpable)
{
// Do not dump the property if it has already been dumped.
if (!$dumped->contains($value))
{
$value = $value->dump($depth - 1, $dumped);
}
}
// Check if the object is a date.
if ($value instanceof JDate)
{
$value = (string) $value;
}
// Check if the object is a registry.
elseif ($value instanceof JRegistry)
{
$value = $value->toObject();
}
}
return $value;
}
/**
* Gets a data property.
*
* @param string $property The name of the data property.
*
* @return mixed The value of the data property.
*
* @see JData::__get()
* @since 12.3
*/
protected function getProperty($property)
{
// Get the raw value.
$value = array_key_exists($property, $this->_properties) ? $this->_properties[$property] : null;
return $value;
}
/**
* Sets a data property.
*
* If the name of the property starts with a null byte, this method will return null.
*
* @param string $property The name of the data property.
* @param mixed $value The value to give the data property.
*
* @return mixed The value of the data property.
*
* @see JData::__set()
* @since 12.3
*/
protected function setProperty($property, $value)
{
/*
* Check if the property starts with a null byte. If so, discard it because a later attempt to try to access it
* can cause a fatal error. See http://us3.php.net/manual/en/language.types.array.php#language.types.array.casting
*/
if (strpos($property, "\0") === 0)
{
return null;
}
// Set the value.
$this->_properties[$property] = $value;
return $value;
}
/**
* Count the number of data properties.
*
* @return integer The number of data properties.
*
* @since 12.3
*/
public function count()
{
return count($this->_properties);
}
}

View File

@ -0,0 +1,34 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Data
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* An interface to define if an object is dumpable.
*
* @package Joomla.Platform
* @subpackage Data
* @since 12.3
*/
interface JDataDumpable
{
/**
* Dumps the object properties into a stdClass object, recursively if appropriate.
*
* @param integer $depth The maximum depth of recursion.
* For example, a depth of 0 will return a stdClass with all the properties in native
* form. A depth of 1 will recurse into the first level of properties only.
* @param SplObjectStorage $dumped An array of already serialized objects that is used to avoid infinite loops.
*
* @return stdClass The data properties as a simple PHP stdClass object.
*
* @since 12.3
*/
public function dump($depth = 3, SplObjectStorage $dumped = null);
}

View File

@ -0,0 +1 @@
<!DOCTYPE html><title></title>

View File

@ -0,0 +1,518 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Data
*
* @copyright Copyright (C) 2005 - 2011 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* JDataSet is a collection class that allows the developer to operate on a set of JData objects as if they were in a
* typical PHP array.
*
* @package Joomla.Platform
* @subpackage Data
* @since 12.3
*/
class JDataSet implements JDataDumpable, ArrayAccess, Countable, Iterator
{
/**
* The current position of the iterator.
*
* @var integer
* @since 12.3
*/
private $_current = false;
/**
* The iterator objects.
*
* @var array
* @since 12.3
*/
private $_objects = array();
/**
* The class constructor.
*
* @param array $objects An array of JData objects to bind to the data set.
*
* @since 12.3
* @throws InvalidArgumentException if an object is not an instance of JData.
*/
public function __construct(array $objects = array())
{
// Set the objects.
$this->_initialise($objects);
}
/**
* The magic call method is used to call object methods using the iterator.
*
* Example: $array = $objectList->foo('bar');
*
* The object list will iterate over its objects and see if each object has a callable 'foo' method.
* If so, it will pass the argument list and assemble any return values. If an object does not have
* a callable method no return value is recorded.
* The keys of the objects and the result array are maintained.
*
* @param string $method The name of the method called.
* @param array $arguments The arguments of the method called.
*
* @return array An array of values returned by the methods called on the objects in the data set.
*
* @since 12.3
*/
public function __call($method, $arguments = array())
{
$return = array();
// Iterate through the objects.
foreach ($this->_objects as $key => $object)
{
// Create the object callback.
$callback = array($object, $method);
// Check if the callback is callable.
if (is_callable($callback))
{
// Call the method for the object.
$return[$key] = call_user_func_array($callback, $arguments);
}
}
return $return;
}
/**
* The magic get method is used to get a list of properties from the objects in the data set.
*
* Example: $array = $dataSet->foo;
*
* This will return a column of the values of the 'foo' property in all the objects
* (or values determined by custom property setters in the individual JData's).
* The result array will contain an entry for each object in the list (compared to __call which may not).
* The keys of the objects and the result array are maintained.
*
* @param string $property The name of the data property.
*
* @return array An associative array of the values.
*
* @since 12.3
*/
public function __get($property)
{
$return = array();
// Iterate through the objects.
foreach ($this->_objects as $key => $object)
{
// Get the property.
$return[$key] = $object->$property;
}
return $return;
}
/**
* The magic isset method is used to check the state of an object property using the iterator.
*
* Example: $array = isset($objectList->foo);
*
* @param string $property The name of the property.
*
* @return boolean True if the property is set in any of the objects in the data set.
*
* @since 12.3
*/
public function __isset($property)
{
$return = array();
// Iterate through the objects.
foreach ($this->_objects as $object)
{
// Check the property.
$return[] = isset($object->$property);
}
return in_array(true, $return, true) ? true : false;
}
/**
* The magic set method is used to set an object property using the iterator.
*
* Example: $objectList->foo = 'bar';
*
* This will set the 'foo' property to 'bar' in all of the objects
* (or a value determined by custom property setters in the JData).
*
* @param string $property The name of the property.
* @param mixed $value The value to give the data property.
*
* @return void
*
* @since 12.3
*/
public function __set($property, $value)
{
// Iterate through the objects.
foreach ($this->_objects as $object)
{
// Set the property.
$object->$property = $value;
}
}
/**
* The magic unset method is used to unset an object property using the iterator.
*
* Example: unset($objectList->foo);
*
* This will unset all of the 'foo' properties in the list of JData's.
*
* @param string $property The name of the property.
*
* @return void
*
* @since 12.3
*/
public function __unset($property)
{
// Iterate through the objects.
foreach ($this->_objects as $object)
{
unset($object->$property);
}
}
/**
* Gets the number of data objects in the set.
*
* @return integer The number of objects.
*
* @since 12.3
*/
public function count()
{
return count($this->_objects);
}
/**
* Clears the objects in the data set.
*
* @return JDataSet Returns itself to allow chaining.
*
* @since 12.3
*/
public function clear()
{
$this->_objects = array();
$this->rewind();
return $this;
}
/**
* Get the current data object in the set.
*
* @return JData The current object, or false if the array is empty or the pointer is beyond the end of the elements.
*
* @since 12.3
*/
public function current()
{
return is_scalar($this->_current) ? $this->_objects[$this->_current] : false;
}
/**
* Dumps the data object in the set, recursively if appropriate.
*
* @param integer $depth The maximum depth of recursion (default = 3).
* For example, a depth of 0 will return a stdClass with all the properties in native
* form. A depth of 1 will recurse into the first level of properties only.
* @param SplObjectStorage $dumped An array of already serialized objects that is used to avoid infinite loops.
*
* @return array An associative array of the date objects in the set, dumped as a simple PHP stdClass object.
*
* @see JData::dump()
* @since 12.3
*/
public function dump($depth = 3, SplObjectStorage $dumped = null)
{
// Check if we should initialise the recursion tracker.
if ($dumped === null)
{
$dumped = new SplObjectStorage;
}
// Add this object to the dumped stack.
$dumped->attach($this);
$objects = array();
// Make sure that we have not reached our maximum depth.
if ($depth > 0)
{
// Handle JSON serialization recursively.
foreach ($this->_objects as $key => $object)
{
$objects[$key] = $object->dump($depth, $dumped);
}
}
return $objects;
}
/**
* Gets the data set in a form that can be serialised to JSON format.
*
* Note that this method will not return an associative array, otherwise it would be encoded into an object.
* JSON decoders do not consistently maintain the order of associative keys, whereas they do maintain the order of arrays.
*
* @param mixed $serialized An array of objects that have already been serialized that is used to infinite loops
* (null on first call).
*
* @return array An array that can be serialised by json_encode().
*
* @since 12.3
*/
public function jsonSerialize($serialized = null)
{
// Check if we should initialise the recursion tracker.
if ($serialized === null)
{
$serialized = array();
}
// Add this object to the serialized stack.
$serialized[] = spl_object_hash($this);
$return = array();
// Iterate through the objects.
foreach ($this->_objects as $object)
{
// Call the method for the object.
$return[] = $object->jsonSerialize($serialized);
}
return $return;
}
/**
* Gets the key of the current object in the iterator.
*
* @return scalar The object key on success; null on failure.
*
* @since 12.3
*/
public function key()
{
return $this->_current;
}
/**
* Gets the array of keys for all the objects in the iterator (emulates array_keys).
*
* @return array The array of keys
*
* @since 12.3
*/
public function keys()
{
return array_keys($this->_objects);
}
/**
* Advances the iterator to the next object in the iterator.
*
* @return void
*
* @since 12.3
*/
public function next()
{
// Get the object offsets.
$keys = $this->keys();
// Check if _current has been set to false but offsetUnset.
if ($this->_current === false && isset($keys[0]))
{
// This is a special case where offsetUnset was used in a foreach loop and the first element was unset.
$this->_current = $keys[0];
}
else
{
// Get the current key.
$position = array_search($this->_current, $keys);
// Check if there is an object after the current object.
if ($position !== false && isset($keys[$position + 1]))
{
// Get the next id.
$this->_current = $keys[$position + 1];
}
else
{
// That was the last object or the internal properties have become corrupted.
$this->_current = null;
}
}
}
/**
* Checks whether an offset exists in the iterator.
*
* @param mixed $offset The object offset.
*
* @return boolean True if the object exists, false otherwise.
*
* @since 12.3
*/
public function offsetExists($offset)
{
return isset($this->_objects[$offset]);
}
/**
* Gets an offset in the iterator.
*
* @param mixed $offset The object offset.
*
* @return JData The object if it exists, null otherwise.
*
* @since 12.3
*/
public function offsetGet($offset)
{
return isset($this->_objects[$offset]) ? $this->_objects[$offset] : null;
}
/**
* Sets an offset in the iterator.
*
* @param mixed $offset The object offset.
* @param JData $object The object object.
*
* @return void
*
* @since 12.3
* @throws InvalidArgumentException if an object is not an instance of JData.
*/
public function offsetSet($offset, $object)
{
// Check if the object is a JData object.
if (!($object instanceof JData))
{
throw new InvalidArgumentException(sprintf('%s("%s", *%s*)', __METHOD__, $offset, gettype($object)));
}
// Set the offset.
$this->_objects[$offset] = $object;
}
/**
* Unsets an offset in the iterator.
*
* @param mixed $offset The object offset.
*
* @return void
*
* @since 12.3
*/
public function offsetUnset($offset)
{
if (!$this->offsetExists($offset))
{
// Do nothing if the offset does not exist.
return;
}
// Check for special handling of unsetting the current position.
if ($offset == $this->_current)
{
// Get the current position.
$keys = $this->keys();
$position = array_search($this->_current, $keys);
// Check if there is an object before the current object.
if ($position > 0)
{
// Move the current position back one.
$this->_current = $keys[$position - 1];
}
else
{
// We are at the start of the keys AND let's assume we are in a foreach loop and `next` is going to be called.
$this->_current = false;
}
}
unset($this->_objects[$offset]);
}
/**
* Rewinds the iterator to the first object.
*
* @return void
*
* @since 12.3
*/
public function rewind()
{
// Set the current position to the first object.
if (empty($this->_objects))
{
$this->_current = false;
}
else
{
$keys = $this->keys();
$this->_current = array_shift($keys);
}
}
/**
* Validates the iterator.
*
* @return boolean True if valid, false otherwise.
*
* @since 12.3
*/
public function valid()
{
// Check the current position.
if (!is_scalar($this->_current) || !isset($this->_objects[$this->_current]))
{
return false;
}
return true;
}
/**
* Initialises the list with an array of objects.
*
* @param array $input An array of objects.
*
* @return void
*
* @since 12.3
* @throws InvalidArgumentException if an object is not an instance of JData.
*/
private function _initialise(array $input = array())
{
foreach ($input as $key => $object)
{
if (!is_null($object))
{
$this->offsetSet($key, $object);
}
}
$this->rewind();
}
}

View File

@ -0,0 +1,174 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Database
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* Database connector class.
*
* @package Joomla.Platform
* @subpackage Database
* @since 11.1
* @deprecated 13.3 (Platform) & 4.0 (CMS)
*/
abstract class JDatabase
{
/**
* Execute the SQL statement.
*
* @return mixed A database cursor resource on success, boolean false on failure.
*
* @since 11.1
* @throws RuntimeException
* @deprecated 13.1 (Platform) & 4.0 (CMS)
*/
public function query()
{
JLog::add('JDatabase::query() is deprecated, use JDatabaseDriver::execute() instead.', JLog::WARNING, 'deprecated');
return $this->execute();
}
/**
* Get a list of available database connectors. The list will only be populated with connectors that both
* the class exists and the static test method returns true. This gives us the ability to have a multitude
* of connector classes that are self-aware as to whether or not they are able to be used on a given system.
*
* @return array An array of available database connectors.
*
* @since 11.1
* @deprecated 13.1 (Platform) & 4.0 (CMS)
*/
public static function getConnectors()
{
JLog::add('JDatabase::getConnectors() is deprecated, use JDatabaseDriver::getConnectors() instead.', JLog::WARNING, 'deprecated');
return JDatabaseDriver::getConnectors();
}
/**
* Gets the error message from the database connection.
*
* @param boolean $escaped True to escape the message string for use in JavaScript.
*
* @return string The error message for the most recent query.
*
* @deprecated 13.3 (Platform) & 4.0 (CMS)
* @since 11.1
*/
public function getErrorMsg($escaped = false)
{
JLog::add('JDatabase::getErrorMsg() is deprecated, use exception handling instead.', JLog::WARNING, 'deprecated');
if ($escaped)
{
return addslashes($this->errorMsg);
}
else
{
return $this->errorMsg;
}
}
/**
* Gets the error number from the database connection.
*
* @return integer The error number for the most recent query.
*
* @since 11.1
* @deprecated 13.3 (Platform) & 4.0 (CMS)
*/
public function getErrorNum()
{
JLog::add('JDatabase::getErrorNum() is deprecated, use exception handling instead.', JLog::WARNING, 'deprecated');
return $this->errorNum;
}
/**
* Method to return a JDatabaseDriver instance based on the given options. There are three global options and then
* the rest are specific to the database driver. The 'driver' option defines which JDatabaseDriver class is
* used for the connection -- the default is 'mysqli'. The 'database' option determines which database is to
* be used for the connection. The 'select' option determines whether the connector should automatically select
* the chosen database.
*
* Instances are unique to the given options and new objects are only created when a unique options array is
* passed into the method. This ensures that we don't end up with unnecessary database connection resources.
*
* @param array $options Parameters to be passed to the database driver.
*
* @return JDatabaseDriver A database object.
*
* @since 11.1
* @deprecated 13.1 (Platform) & 4.0 (CMS)
*/
public static function getInstance($options = array())
{
JLog::add('JDatabase::getInstance() is deprecated, use JDatabaseDriver::getInstance() instead.', JLog::WARNING, 'deprecated');
return JDatabaseDriver::getInstance($options);
}
/**
* Splits a string of multiple queries into an array of individual queries.
*
* @param string $query Input SQL string with which to split into individual queries.
*
* @return array The queries from the input string separated into an array.
*
* @since 11.1
* @deprecated 13.1 (Platform) & 4.0 (CMS)
*/
public static function splitSql($query)
{
JLog::add('JDatabase::splitSql() is deprecated, use JDatabaseDriver::splitSql() instead.', JLog::WARNING, 'deprecated');
return JDatabaseDriver::splitSql($query);
}
/**
* Return the most recent error message for the database connector.
*
* @param boolean $showSQL True to display the SQL statement sent to the database as well as the error.
*
* @return string The error message for the most recent query.
*
* @since 11.1
* @deprecated 13.3 (Platform) & 4.0 (CMS)
*/
public function stderr($showSQL = false)
{
JLog::add('JDatabase::stderr() is deprecated.', JLog::WARNING, 'deprecated');
if ($this->errorNum != 0)
{
return JText::sprintf('JLIB_DATABASE_ERROR_FUNCTION_FAILED', $this->errorNum, $this->errorMsg)
. ($showSQL ? "<br />SQL = <pre>$this->sql</pre>" : '');
}
else
{
return JText::_('JLIB_DATABASE_FUNCTION_NOERROR');
}
}
/**
* Test to see if the connector is available.
*
* @return boolean True on success, false otherwise.
*
* @since 11.1
* @deprecated 12.3 (Platform) & 4.0 (CMS) - Use JDatabaseDriver::isSupported() instead.
*/
public static function test()
{
JLog::add('JDatabase::test() is deprecated. Use JDatabaseDriver::isSupported() instead.', JLog::WARNING, 'deprecated');
return static::isSupported();
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1 @@
<!DOCTYPE html><title></title>

View File

@ -0,0 +1,463 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Database
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* MySQL database driver
*
* @package Joomla.Platform
* @subpackage Database
* @see http://dev.mysql.com/doc/
* @since 12.1
*/
class JDatabaseDriverMysql extends JDatabaseDriverMysqli
{
/**
* The name of the database driver.
*
* @var string
* @since 12.1
*/
public $name = 'mysql';
/**
* Constructor.
*
* @param array $options Array of database options with keys: host, user, password, database, select.
*
* @since 12.1
*/
public function __construct($options)
{
// Get some basic values from the options.
$options['host'] = (isset($options['host'])) ? $options['host'] : 'localhost';
$options['user'] = (isset($options['user'])) ? $options['user'] : 'root';
$options['password'] = (isset($options['password'])) ? $options['password'] : '';
$options['database'] = (isset($options['database'])) ? $options['database'] : '';
$options['select'] = (isset($options['select'])) ? (bool) $options['select'] : true;
// Finalize initialisation.
parent::__construct($options);
}
/**
* Destructor.
*
* @since 12.1
*/
public function __destruct()
{
$this->disconnect();
}
/**
* Connects to the database if needed.
*
* @return void Returns void if the database connected successfully.
*
* @since 12.1
* @throws RuntimeException
*/
public function connect()
{
if ($this->connection)
{
return;
}
// Make sure the MySQL extension for PHP is installed and enabled.
if (!function_exists('mysql_connect'))
{
throw new RuntimeException('Could not connect to MySQL.');
}
// Attempt to connect to the server.
if (!($this->connection = @ mysql_connect($this->options['host'], $this->options['user'], $this->options['password'], true)))
{
throw new RuntimeException('Could not connect to MySQL.');
}
// Set sql_mode to non_strict mode
mysql_query("SET @@SESSION.sql_mode = '';", $this->connection);
// If auto-select is enabled select the given database.
if ($this->options['select'] && !empty($this->options['database']))
{
$this->select($this->options['database']);
}
// Set charactersets (needed for MySQL 4.1.2+).
$this->setUTF();
// Turn MySQL profiling ON in debug mode:
if ($this->debug && $this->hasProfiling())
{
mysql_query("SET profiling = 1;", $this->connection);
}
}
/**
* Disconnects the database.
*
* @return void
*
* @since 12.1
*/
public function disconnect()
{
// Close the connection.
if (is_resource($this->connection))
{
foreach ($this->disconnectHandlers as $h)
{
call_user_func_array($h, array( &$this));
}
mysql_close($this->connection);
}
$this->connection = null;
}
/**
* Method to escape a string for usage in an SQL statement.
*
* @param string $text The string to be escaped.
* @param boolean $extra Optional parameter to provide extra escaping.
*
* @return string The escaped string.
*
* @since 12.1
*/
public function escape($text, $extra = false)
{
$this->connect();
$result = mysql_real_escape_string($text, $this->getConnection());
if ($extra)
{
$result = addcslashes($result, '%_');
}
return $result;
}
/**
* Test to see if the MySQL connector is available.
*
* @return boolean True on success, false otherwise.
*
* @since 12.1
*/
public static function isSupported()
{
return (function_exists('mysql_connect'));
}
/**
* Determines if the connection to the server is active.
*
* @return boolean True if connected to the database engine.
*
* @since 12.1
*/
public function connected()
{
if (is_resource($this->connection))
{
return @mysql_ping($this->connection);
}
return false;
}
/**
* Get the number of affected rows for the previous executed SQL statement.
*
* @return integer The number of affected rows.
*
* @since 12.1
*/
public function getAffectedRows()
{
$this->connect();
return mysql_affected_rows($this->connection);
}
/**
* Get the number of returned rows for the previous executed SQL statement.
*
* @param resource $cursor An optional database cursor resource to extract the row count from.
*
* @return integer The number of returned rows.
*
* @since 12.1
*/
public function getNumRows($cursor = null)
{
$this->connect();
return mysql_num_rows($cursor ? $cursor : $this->cursor);
}
/**
* Get the version of the database connector.
*
* @return string The database connector version.
*
* @since 12.1
*/
public function getVersion()
{
$this->connect();
return mysql_get_server_info($this->connection);
}
/**
* Method to get the auto-incremented value from the last INSERT statement.
*
* @return integer The value of the auto-increment field from the last inserted row.
*
* @since 12.1
*/
public function insertid()
{
$this->connect();
return mysql_insert_id($this->connection);
}
/**
* Execute the SQL statement.
*
* @return mixed A database cursor resource on success, boolean false on failure.
*
* @since 12.1
* @throws RuntimeException
*/
public function execute()
{
$this->connect();
if (!is_resource($this->connection))
{
JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database');
throw new RuntimeException($this->errorMsg, $this->errorNum);
}
// Take a local copy so that we don't modify the original query and cause issues later
$query = $this->replacePrefix((string) $this->sql);
if ($this->limit > 0 || $this->offset > 0)
{
$query .= ' LIMIT ' . $this->offset . ', ' . $this->limit;
}
// Increment the query counter.
$this->count++;
// Reset the error values.
$this->errorNum = 0;
$this->errorMsg = '';
// If debugging is enabled then let's log the query.
if ($this->debug)
{
// Add the query to the object queue.
$this->log[] = $query;
JLog::add($query, JLog::DEBUG, 'databasequery');
$this->timings[] = microtime(true);
}
// Execute the query. Error suppression is used here to prevent warnings/notices that the connection has been lost.
$this->cursor = @mysql_query($query, $this->connection);
if ($this->debug)
{
$this->timings[] = microtime(true);
if (defined('DEBUG_BACKTRACE_IGNORE_ARGS'))
{
$this->callStacks[] = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
}
else
{
$this->callStacks[] = debug_backtrace();
}
}
// If an error occurred handle it.
if (!$this->cursor)
{
// Check if the server was disconnected.
if (!$this->connected())
{
try
{
// Attempt to reconnect.
$this->connection = null;
$this->connect();
}
// If connect fails, ignore that exception and throw the normal exception.
catch (RuntimeException $e)
{
// Get the error number and message.
$this->errorNum = (int) mysql_errno($this->connection);
$this->errorMsg = (string) mysql_error($this->connection) . ' SQL=' . $query;
// Throw the normal query exception.
JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'databasequery');
throw new RuntimeException($this->errorMsg, $this->errorNum);
}
// Since we were able to reconnect, run the query again.
return $this->execute();
}
// The server was not disconnected.
else
{
// Get the error number and message.
$this->errorNum = (int) mysql_errno($this->connection);
$this->errorMsg = (string) mysql_error($this->connection) . ' SQL=' . $query;
// Throw the normal query exception.
JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'databasequery');
throw new RuntimeException($this->errorMsg, $this->errorNum);
}
}
return $this->cursor;
}
/**
* Select a database for use.
*
* @param string $database The name of the database to select for use.
*
* @return boolean True if the database was successfully selected.
*
* @since 12.1
* @throws RuntimeException
*/
public function select($database)
{
$this->connect();
if (!$database)
{
return false;
}
if (!mysql_select_db($database, $this->connection))
{
throw new RuntimeException('Could not connect to database');
}
return true;
}
/**
* Set the connection to use UTF-8 character encoding.
*
* @return boolean True on success.
*
* @since 12.1
*/
public function setUTF()
{
$this->connect();
return mysql_set_charset('utf8', $this->connection);
}
/**
* Method to fetch a row from the result set cursor as an array.
*
* @param mixed $cursor The optional result set cursor from which to fetch the row.
*
* @return mixed Either the next row from the result set or false if there are no more rows.
*
* @since 12.1
*/
protected function fetchArray($cursor = null)
{
return mysql_fetch_row($cursor ? $cursor : $this->cursor);
}
/**
* Method to fetch a row from the result set cursor as an associative array.
*
* @param mixed $cursor The optional result set cursor from which to fetch the row.
*
* @return mixed Either the next row from the result set or false if there are no more rows.
*
* @since 12.1
*/
protected function fetchAssoc($cursor = null)
{
return mysql_fetch_assoc($cursor ? $cursor : $this->cursor);
}
/**
* Method to fetch a row from the result set cursor as an object.
*
* @param mixed $cursor The optional result set cursor from which to fetch the row.
* @param string $class The class name to use for the returned row object.
*
* @return mixed Either the next row from the result set or false if there are no more rows.
*
* @since 12.1
*/
protected function fetchObject($cursor = null, $class = 'stdClass')
{
return mysql_fetch_object($cursor ? $cursor : $this->cursor, $class);
}
/**
* Method to free up the memory used for the result set.
*
* @param mixed $cursor The optional result set cursor from which to fetch the row.
*
* @return void
*
* @since 12.1
*/
protected function freeResult($cursor = null)
{
mysql_free_result($cursor ? $cursor : $this->cursor);
}
/**
* Internal function to check if profiling is available
*
* @return boolean
*
* @since 3.1.3
*/
private function hasProfiling()
{
try
{
$res = mysql_query("SHOW VARIABLES LIKE 'have_profiling'", $this->connection);
$row = mysql_fetch_assoc($res);
return isset($row);
}
catch (Exception $e)
{
return false;
}
}
}

View File

@ -0,0 +1,834 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Database
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* MySQLi database driver
*
* @package Joomla.Platform
* @subpackage Database
* @see http://php.net/manual/en/book.mysqli.php
* @since 12.1
*/
class JDatabaseDriverMysqli extends JDatabaseDriver
{
/**
* The name of the database driver.
*
* @var string
* @since 12.1
*/
public $name = 'mysqli';
/**
* The character(s) used to quote SQL statement names such as table names or field names,
* etc. The child classes should define this as necessary. If a single character string the
* same character is used for both sides of the quoted name, else the first character will be
* used for the opening quote and the second for the closing quote.
*
* @var string
* @since 12.2
*/
protected $nameQuote = '`';
/**
* The null or zero representation of a timestamp for the database driver. This should be
* defined in child classes to hold the appropriate value for the engine.
*
* @var string
* @since 12.2
*/
protected $nullDate = '0000-00-00 00:00:00';
/**
* @var string The minimum supported database version.
* @since 12.2
*/
protected static $dbMinimum = '5.0.4';
/**
* Constructor.
*
* @param array $options List of options used to configure the connection
*
* @since 12.1
*/
public function __construct($options)
{
// Get some basic values from the options.
$options['host'] = (isset($options['host'])) ? $options['host'] : 'localhost';
$options['user'] = (isset($options['user'])) ? $options['user'] : 'root';
$options['password'] = (isset($options['password'])) ? $options['password'] : '';
$options['database'] = (isset($options['database'])) ? $options['database'] : '';
$options['select'] = (isset($options['select'])) ? (bool) $options['select'] : true;
$options['port'] = null;
$options['socket'] = null;
// Finalize initialisation.
parent::__construct($options);
}
/**
* Destructor.
*
* @since 12.1
*/
public function __destruct()
{
$this->disconnect();
}
/**
* Connects to the database if needed.
*
* @return void Returns void if the database connected successfully.
*
* @since 12.1
* @throws RuntimeException
*/
public function connect()
{
if ($this->connection)
{
return;
}
/*
* Unlike mysql_connect(), mysqli_connect() takes the port and socket as separate arguments. Therefore, we
* have to extract them from the host string.
*/
$tmp = substr(strstr($this->options['host'], ':'), 1);
if (!empty($tmp))
{
// Get the port number or socket name
if (is_numeric($tmp))
{
$this->options['port'] = $tmp;
}
else
{
$this->options['socket'] = $tmp;
}
// Extract the host name only
$this->options['host'] = substr($this->options['host'], 0, strlen($this->options['host']) - (strlen($tmp) + 1));
// This will take care of the following notation: ":3306"
if ($this->options['host'] == '')
{
$this->options['host'] = 'localhost';
}
}
// Make sure the MySQLi extension for PHP is installed and enabled.
if (!function_exists('mysqli_connect'))
{
throw new RuntimeException('The MySQL adapter mysqli is not available');
}
$this->connection = @mysqli_connect(
$this->options['host'], $this->options['user'], $this->options['password'], null, $this->options['port'], $this->options['socket']
);
// Attempt to connect to the server.
if (!$this->connection)
{
throw new RuntimeException('Could not connect to MySQL.');
}
// Set sql_mode to non_strict mode
mysqli_query($this->connection, "SET @@SESSION.sql_mode = '';");
// If auto-select is enabled select the given database.
if ($this->options['select'] && !empty($this->options['database']))
{
$this->select($this->options['database']);
}
// Set charactersets (needed for MySQL 4.1.2+).
$this->setUTF();
// Turn MySQL profiling ON in debug mode:
if ($this->debug && $this->hasProfiling())
{
mysqli_query($this->connection, "SET profiling_history_size = 100;");
mysqli_query($this->connection, "SET profiling = 1;");
}
}
/**
* Disconnects the database.
*
* @return void
*
* @since 12.1
*/
public function disconnect()
{
// Close the connection.
if ($this->connection)
{
foreach ($this->disconnectHandlers as $h)
{
call_user_func_array($h, array( &$this));
}
mysqli_close($this->connection);
}
$this->connection = null;
}
/**
* Method to escape a string for usage in an SQL statement.
*
* @param string $text The string to be escaped.
* @param boolean $extra Optional parameter to provide extra escaping.
*
* @return string The escaped string.
*
* @since 12.1
*/
public function escape($text, $extra = false)
{
$this->connect();
$result = mysqli_real_escape_string($this->getConnection(), $text);
if ($extra)
{
$result = addcslashes($result, '%_');
}
return $result;
}
/**
* Test to see if the MySQL connector is available.
*
* @return boolean True on success, false otherwise.
*
* @since 12.1
*/
public static function isSupported()
{
return (function_exists('mysqli_connect'));
}
/**
* Determines if the connection to the server is active.
*
* @return boolean True if connected to the database engine.
*
* @since 12.1
*/
public function connected()
{
if (is_object($this->connection))
{
return mysqli_ping($this->connection);
}
return false;
}
/**
* Drops a table from the database.
*
* @param string $tableName The name of the database table to drop.
* @param boolean $ifExists Optionally specify that the table must exist before it is dropped.
*
* @return JDatabaseDriverMysqli Returns this object to support chaining.
*
* @since 12.2
* @throws RuntimeException
*/
public function dropTable($tableName, $ifExists = true)
{
$this->connect();
$query = $this->getQuery(true);
$this->setQuery('DROP TABLE ' . ($ifExists ? 'IF EXISTS ' : '') . $query->quoteName($tableName));
$this->execute();
return $this;
}
/**
* Get the number of affected rows for the previous executed SQL statement.
*
* @return integer The number of affected rows.
*
* @since 12.1
*/
public function getAffectedRows()
{
$this->connect();
return mysqli_affected_rows($this->connection);
}
/**
* Method to get the database collation in use by sampling a text field of a table in the database.
*
* @return mixed The collation in use by the database (string) or boolean false if not supported.
*
* @since 12.2
* @throws RuntimeException
*/
public function getCollation()
{
$this->connect();
$tables = $this->getTableList();
$this->setQuery('SHOW FULL COLUMNS FROM ' . $tables[0]);
$array = $this->loadAssocList();
foreach ($array as $field)
{
if (!is_null($field['Collation']))
{
return $field['Collation'];
}
}
return null;
}
/**
* Get the number of returned rows for the previous executed SQL statement.
*
* @param resource $cursor An optional database cursor resource to extract the row count from.
*
* @return integer The number of returned rows.
*
* @since 12.1
*/
public function getNumRows($cursor = null)
{
return mysqli_num_rows($cursor ? $cursor : $this->cursor);
}
/**
* Shows the table CREATE statement that creates the given tables.
*
* @param mixed $tables A table name or a list of table names.
*
* @return array A list of the create SQL for the tables.
*
* @since 12.1
* @throws RuntimeException
*/
public function getTableCreate($tables)
{
$this->connect();
$result = array();
// Sanitize input to an array and iterate over the list.
settype($tables, 'array');
foreach ($tables as $table)
{
// Set the query to get the table CREATE statement.
$this->setQuery('SHOW CREATE table ' . $this->quoteName($this->escape($table)));
$row = $this->loadRow();
// Populate the result array based on the create statements.
$result[$table] = $row[1];
}
return $result;
}
/**
* Retrieves field information about a given table.
*
* @param string $table The name of the database table.
* @param boolean $typeOnly True to only return field types.
*
* @return array An array of fields for the database table.
*
* @since 12.2
* @throws RuntimeException
*/
public function getTableColumns($table, $typeOnly = true)
{
$this->connect();
$result = array();
// Set the query to get the table fields statement.
$this->setQuery('SHOW FULL COLUMNS FROM ' . $this->quoteName($this->escape($table)));
$fields = $this->loadObjectList();
// If we only want the type as the value add just that to the list.
if ($typeOnly)
{
foreach ($fields as $field)
{
$result[$field->Field] = preg_replace("/[(0-9)]/", '', $field->Type);
}
}
// If we want the whole field data object add that to the list.
else
{
foreach ($fields as $field)
{
$result[$field->Field] = $field;
}
}
return $result;
}
/**
* Get the details list of keys for a table.
*
* @param string $table The name of the table.
*
* @return array An array of the column specification for the table.
*
* @since 12.2
* @throws RuntimeException
*/
public function getTableKeys($table)
{
$this->connect();
// Get the details columns information.
$this->setQuery('SHOW KEYS FROM ' . $this->quoteName($table));
$keys = $this->loadObjectList();
return $keys;
}
/**
* Method to get an array of all tables in the database.
*
* @return array An array of all the tables in the database.
*
* @since 12.2
* @throws RuntimeException
*/
public function getTableList()
{
$this->connect();
// Set the query to get the tables statement.
$this->setQuery('SHOW TABLES');
$tables = $this->loadColumn();
return $tables;
}
/**
* Get the version of the database connector.
*
* @return string The database connector version.
*
* @since 12.1
*/
public function getVersion()
{
$this->connect();
return mysqli_get_server_info($this->connection);
}
/**
* Method to get the auto-incremented value from the last INSERT statement.
*
* @return mixed The value of the auto-increment field from the last inserted row.
* If the value is greater than maximal int value, it will return a string.
*
* @since 12.1
*/
public function insertid()
{
$this->connect();
return mysqli_insert_id($this->connection);
}
/**
* Locks a table in the database.
*
* @param string $table The name of the table to unlock.
*
* @return JDatabaseDriverMysqli Returns this object to support chaining.
*
* @since 12.2
* @throws RuntimeException
*/
public function lockTable($table)
{
$this->setQuery('LOCK TABLES ' . $this->quoteName($table) . ' WRITE')->execute();
return $this;
}
/**
* Execute the SQL statement.
*
* @return mixed A database cursor resource on success, boolean false on failure.
*
* @since 12.1
* @throws RuntimeException
*/
public function execute()
{
$this->connect();
if (!is_object($this->connection))
{
JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'database');
throw new RuntimeException($this->errorMsg, $this->errorNum);
}
// Take a local copy so that we don't modify the original query and cause issues later
$query = $this->replacePrefix((string) $this->sql);
if ($this->limit > 0 || $this->offset > 0)
{
$query .= ' LIMIT ' . $this->offset . ', ' . $this->limit;
}
// Increment the query counter.
$this->count++;
// Reset the error values.
$this->errorNum = 0;
$this->errorMsg = '';
$memoryBefore = null;
// If debugging is enabled then let's log the query.
if ($this->debug)
{
// Add the query to the object queue.
$this->log[] = $query;
JLog::add($query, JLog::DEBUG, 'databasequery');
$this->timings[] = microtime(true);
if (is_object($this->cursor))
{
$this->freeResult();
}
$memoryBefore = memory_get_usage();
}
// Execute the query. Error suppression is used here to prevent warnings/notices that the connection has been lost.
$this->cursor = @mysqli_query($this->connection, $query);
if ($this->debug)
{
$this->timings[] = microtime(true);
if (defined('DEBUG_BACKTRACE_IGNORE_ARGS'))
{
$this->callStacks[] = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
}
else
{
$this->callStacks[] = debug_backtrace();
}
$this->callStacks[count($this->callStacks) - 1][0]['memory'] = array($memoryBefore, memory_get_usage(), is_object($this->cursor) ? $this->getNumRows() : null);
}
// If an error occurred handle it.
if (!$this->cursor)
{
$this->errorNum = (int) mysqli_errno($this->connection);
$this->errorMsg = (string) mysqli_error($this->connection) . ' SQL=' . $query;
// Check if the server was disconnected.
if (!$this->connected())
{
try
{
// Attempt to reconnect.
$this->connection = null;
$this->connect();
}
// If connect fails, ignore that exception and throw the normal exception.
catch (RuntimeException $e)
{
JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'databasequery');
throw new RuntimeException($this->errorMsg, $this->errorNum);
}
// Since we were able to reconnect, run the query again.
return $this->execute();
}
// The server was not disconnected.
else
{
JLog::add(JText::sprintf('JLIB_DATABASE_QUERY_FAILED', $this->errorNum, $this->errorMsg), JLog::ERROR, 'databasequery');
throw new RuntimeException($this->errorMsg, $this->errorNum);
}
}
return $this->cursor;
}
/**
* Renames a table in the database.
*
* @param string $oldTable The name of the table to be renamed
* @param string $newTable The new name for the table.
* @param string $backup Not used by MySQL.
* @param string $prefix Not used by MySQL.
*
* @return JDatabaseDriverMysqli Returns this object to support chaining.
*
* @since 12.2
* @throws RuntimeException
*/
public function renameTable($oldTable, $newTable, $backup = null, $prefix = null)
{
$this->setQuery('RENAME TABLE ' . $oldTable . ' TO ' . $newTable)->execute();
return $this;
}
/**
* Select a database for use.
*
* @param string $database The name of the database to select for use.
*
* @return boolean True if the database was successfully selected.
*
* @since 12.1
* @throws RuntimeException
*/
public function select($database)
{
$this->connect();
if (!$database)
{
return false;
}
if (!mysqli_select_db($this->connection, $database))
{
throw new RuntimeException('Could not connect to database.');
}
return true;
}
/**
* Set the connection to use UTF-8 character encoding.
*
* @return boolean True on success.
*
* @since 12.1
*/
public function setUTF()
{
$this->connect();
return $this->connection->set_charset('utf8');
}
/**
* Method to commit a transaction.
*
* @param boolean $toSavepoint If true, commit to the last savepoint.
*
* @return void
*
* @since 12.2
* @throws RuntimeException
*/
public function transactionCommit($toSavepoint = false)
{
$this->connect();
if (!$toSavepoint || $this->transactionDepth <= 1)
{
if ($this->setQuery('COMMIT')->execute())
{
$this->transactionDepth = 0;
}
return;
}
$this->transactionDepth--;
}
/**
* Method to roll back a transaction.
*
* @param boolean $toSavepoint If true, rollback to the last savepoint.
*
* @return void
*
* @since 12.2
* @throws RuntimeException
*/
public function transactionRollback($toSavepoint = false)
{
$this->connect();
if (!$toSavepoint || $this->transactionDepth <= 1)
{
if ($this->setQuery('ROLLBACK')->execute())
{
$this->transactionDepth = 0;
}
return;
}
$savepoint = 'SP_' . ($this->transactionDepth - 1);
$this->setQuery('ROLLBACK TO SAVEPOINT ' . $this->quoteName($savepoint));
if ($this->execute())
{
$this->transactionDepth--;
}
}
/**
* Method to initialize a transaction.
*
* @param boolean $asSavepoint If true and a transaction is already active, a savepoint will be created.
*
* @return void
*
* @since 12.2
* @throws RuntimeException
*/
public function transactionStart($asSavepoint = false)
{
$this->connect();
if (!$asSavepoint || !$this->transactionDepth)
{
if ($this->setQuery('START TRANSACTION')->execute())
{
$this->transactionDepth = 1;
}
return;
}
$savepoint = 'SP_' . $this->transactionDepth;
$this->setQuery('SAVEPOINT ' . $this->quoteName($savepoint));
if ($this->execute())
{
$this->transactionDepth++;
}
}
/**
* Method to fetch a row from the result set cursor as an array.
*
* @param mixed $cursor The optional result set cursor from which to fetch the row.
*
* @return mixed Either the next row from the result set or false if there are no more rows.
*
* @since 12.1
*/
protected function fetchArray($cursor = null)
{
return mysqli_fetch_row($cursor ? $cursor : $this->cursor);
}
/**
* Method to fetch a row from the result set cursor as an associative array.
*
* @param mixed $cursor The optional result set cursor from which to fetch the row.
*
* @return mixed Either the next row from the result set or false if there are no more rows.
*
* @since 12.1
*/
protected function fetchAssoc($cursor = null)
{
return mysqli_fetch_assoc($cursor ? $cursor : $this->cursor);
}
/**
* Method to fetch a row from the result set cursor as an object.
*
* @param mixed $cursor The optional result set cursor from which to fetch the row.
* @param string $class The class name to use for the returned row object.
*
* @return mixed Either the next row from the result set or false if there are no more rows.
*
* @since 12.1
*/
protected function fetchObject($cursor = null, $class = 'stdClass')
{
return mysqli_fetch_object($cursor ? $cursor : $this->cursor, $class);
}
/**
* Method to free up the memory used for the result set.
*
* @param mixed $cursor The optional result set cursor from which to fetch the row.
*
* @return void
*
* @since 12.1
*/
protected function freeResult($cursor = null)
{
mysqli_free_result($cursor ? $cursor : $this->cursor);
if ((! $cursor) || ($cursor === $this->cursor))
{
$this->cursor = null;
}
}
/**
* Unlocks tables in the database.
*
* @return JDatabaseDriverMysqli Returns this object to support chaining.
*
* @since 12.1
* @throws RuntimeException
*/
public function unlockTables()
{
$this->setQuery('UNLOCK TABLES')->execute();
return $this;
}
/**
* Internal function to check if profiling is available
*
* @return boolean
*
* @since 3.1.3
*/
private function hasProfiling()
{
try
{
$res = mysqli_query($this->connection, "SHOW VARIABLES LIKE 'have_profiling'");
$row = mysqli_fetch_assoc($res);
return isset($row);
}
catch (Exception $e)
{
return false;
}
}
}

View File

@ -0,0 +1,680 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Database
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* Oracle database driver
*
* @package Joomla.Platform
* @subpackage Database
* @see http://php.net/pdo
* @since 12.1
*/
class JDatabaseDriverOracle extends JDatabaseDriverPdo
{
/**
* The name of the database driver.
*
* @var string
* @since 12.1
*/
public $name = 'oracle';
/**
* The character(s) used to quote SQL statement names such as table names or field names,
* etc. The child classes should define this as necessary. If a single character string the
* same character is used for both sides of the quoted name, else the first character will be
* used for the opening quote and the second for the closing quote.
*
* @var string
* @since 12.1
*/
protected $nameQuote = '"';
/**
* Returns the current dateformat
*
* @var string
* @since 12.1
*/
protected $dateformat;
/**
* Returns the current character set
*
* @var string
* @since 12.1
*/
protected $charset;
/**
* Constructor.
*
* @param array $options List of options used to configure the connection
*
* @since 12.1
*/
public function __construct($options)
{
$options['driver'] = 'oci';
$options['charset'] = (isset($options['charset'])) ? $options['charset'] : 'AL32UTF8';
$options['dateformat'] = (isset($options['dateformat'])) ? $options['dateformat'] : 'RRRR-MM-DD HH24:MI:SS';
$this->charset = $options['charset'];
$this->dateformat = $options['dateformat'];
// Finalize initialisation
parent::__construct($options);
}
/**
* Destructor.
*
* @since 12.1
*/
public function __destruct()
{
$this->freeResult();
unset($this->connection);
}
/**
* Connects to the database if needed.
*
* @return void Returns void if the database connected successfully.
*
* @since 12.1
* @throws RuntimeException
*/
public function connect()
{
if ($this->connection)
{
return;
}
parent::connect();
if (isset($this->options['schema']))
{
$this->setQuery('ALTER SESSION SET CURRENT_SCHEMA = ' . $this->quoteName($this->options['schema']))->execute();
}
$this->setDateFormat($this->dateformat);
}
/**
* Disconnects the database.
*
* @return void
*
* @since 12.1
*/
public function disconnect()
{
// Close the connection.
$this->freeResult();
unset($this->connection);
}
/**
* Drops a table from the database.
*
* Note: The IF EXISTS flag is unused in the Oracle driver.
*
* @param string $tableName The name of the database table to drop.
* @param boolean $ifExists Optionally specify that the table must exist before it is dropped.
*
* @return JDatabaseDriverOracle Returns this object to support chaining.
*
* @since 12.1
*/
public function dropTable($tableName, $ifExists = true)
{
$this->connect();
$query = $this->getQuery(true)
->setQuery('DROP TABLE :tableName');
$query->bind(':tableName', $tableName);
$this->setQuery($query);
$this->execute();
return $this;
}
/**
* Method to get the database collation in use by sampling a text field of a table in the database.
*
* @return mixed The collation in use by the database or boolean false if not supported.
*
* @since 12.1
*/
public function getCollation()
{
return $this->charset;
}
/**
* Get a query to run and verify the database is operational.
*
* @return string The query to check the health of the DB.
*
* @since 12.2
*/
public function getConnectedQuery()
{
return 'SELECT 1 FROM dual';
}
/**
* Returns the current date format
* This method should be useful in the case that
* somebody actually wants to use a different
* date format and needs to check what the current
* one is to see if it needs to be changed.
*
* @return string The current date format
*
* @since 12.1
*/
public function getDateFormat()
{
return $this->dateformat;
}
/**
* Shows the table CREATE statement that creates the given tables.
*
* Note: You must have the correct privileges before this method
* will return usable results!
*
* @param mixed $tables A table name or a list of table names.
*
* @return array A list of the create SQL for the tables.
*
* @since 12.1
* @throws RuntimeException
*/
public function getTableCreate($tables)
{
$this->connect();
$result = array();
$query = $this->getQuery(true)
->select('dbms_metadata.get_ddl(:type, :tableName)')
->from('dual')
->bind(':type', 'TABLE');
// Sanitize input to an array and iterate over the list.
settype($tables, 'array');
foreach ($tables as $table)
{
$query->bind(':tableName', $table);
$this->setQuery($query);
$statement = (string) $this->loadResult();
$result[$table] = $statement;
}
return $result;
}
/**
* Retrieves field information about a given table.
*
* @param string $table The name of the database table.
* @param boolean $typeOnly True to only return field types.
*
* @return array An array of fields for the database table.
*
* @since 12.1
* @throws RuntimeException
*/
public function getTableColumns($table, $typeOnly = true)
{
$this->connect();
$columns = array();
$query = $this->getQuery(true);
$fieldCasing = $this->getOption(PDO::ATTR_CASE);
$this->setOption(PDO::ATTR_CASE, PDO::CASE_UPPER);
$table = strtoupper($table);
$query->select('*');
$query->from('ALL_TAB_COLUMNS');
$query->where('table_name = :tableName');
$prefixedTable = str_replace('#__', strtoupper($this->tablePrefix), $table);
$query->bind(':tableName', $prefixedTable);
$this->setQuery($query);
$fields = $this->loadObjectList();
if ($typeOnly)
{
foreach ($fields as $field)
{
$columns[$field->COLUMN_NAME] = $field->DATA_TYPE;
}
}
else
{
foreach ($fields as $field)
{
$columns[$field->COLUMN_NAME] = $field;
$columns[$field->COLUMN_NAME]->Default = null;
}
}
$this->setOption(PDO::ATTR_CASE, $fieldCasing);
return $columns;
}
/**
* Get the details list of keys for a table.
*
* @param string $table The name of the table.
*
* @return array An array of the column specification for the table.
*
* @since 12.1
* @throws RuntimeException
*/
public function getTableKeys($table)
{
$this->connect();
$query = $this->getQuery(true);
$fieldCasing = $this->getOption(PDO::ATTR_CASE);
$this->setOption(PDO::ATTR_CASE, PDO::CASE_UPPER);
$table = strtoupper($table);
$query->select('*')
->from('ALL_CONSTRAINTS')
->where('table_name = :tableName')
->bind(':tableName', $table);
$this->setQuery($query);
$keys = $this->loadObjectList();
$this->setOption(PDO::ATTR_CASE, $fieldCasing);
return $keys;
}
/**
* Method to get an array of all tables in the database (schema).
*
* @param string $databaseName The database (schema) name
* @param boolean $includeDatabaseName Whether to include the schema name in the results
*
* @return array An array of all the tables in the database.
*
* @since 12.1
* @throws RuntimeException
*/
public function getTableList($databaseName = null, $includeDatabaseName = false)
{
$this->connect();
$query = $this->getQuery(true);
if ($includeDatabaseName)
{
$query->select('owner, table_name');
}
else
{
$query->select('table_name');
}
$query->from('all_tables');
if ($databaseName)
{
$query->where('owner = :database')
->bind(':database', $databaseName);
}
$query->order('table_name');
$this->setQuery($query);
if ($includeDatabaseName)
{
$tables = $this->loadAssocList();
}
else
{
$tables = $this->loadColumn();
}
return $tables;
}
/**
* Get the version of the database connector.
*
* @return string The database connector version.
*
* @since 12.1
*/
public function getVersion()
{
$this->connect();
$this->setQuery("select value from nls_database_parameters where parameter = 'NLS_RDBMS_VERSION'");
return $this->loadResult();
}
/**
* Select a database for use.
*
* @param string $database The name of the database to select for use.
*
* @return boolean True if the database was successfully selected.
*
* @since 12.1
* @throws RuntimeException
*/
public function select($database)
{
$this->connect();
return true;
}
/**
* Sets the Oracle Date Format for the session
* Default date format for Oracle is = DD-MON-RR
* The default date format for this driver is:
* 'RRRR-MM-DD HH24:MI:SS' since it is the format
* that matches the MySQL one used within most Joomla
* tables.
*
* @param string $dateFormat Oracle Date Format String
*
* @return boolean
*
* @since 12.1
*/
public function setDateFormat($dateFormat = 'DD-MON-RR')
{
$this->connect();
$this->setQuery("ALTER SESSION SET NLS_DATE_FORMAT = '$dateFormat'");
if (!$this->execute())
{
return false;
}
$this->setQuery("ALTER SESSION SET NLS_TIMESTAMP_FORMAT = '$dateFormat'");
if (!$this->execute())
{
return false;
}
$this->dateformat = $dateFormat;
return true;
}
/**
* Set the connection to use UTF-8 character encoding.
*
* Returns false automatically for the Oracle driver since
* you can only set the character set when the connection
* is created.
*
* @return boolean True on success.
*
* @since 12.1
*/
public function setUTF()
{
return false;
}
/**
* Locks a table in the database.
*
* @param string $table The name of the table to unlock.
*
* @return JDatabaseDriverOracle Returns this object to support chaining.
*
* @since 12.1
* @throws RuntimeException
*/
public function lockTable($table)
{
$this->setQuery('LOCK TABLE ' . $this->quoteName($table) . ' IN EXCLUSIVE MODE')->execute();
return $this;
}
/**
* Renames a table in the database.
*
* @param string $oldTable The name of the table to be renamed
* @param string $newTable The new name for the table.
* @param string $backup Not used by Oracle.
* @param string $prefix Not used by Oracle.
*
* @return JDatabaseDriverOracle Returns this object to support chaining.
*
* @since 12.1
* @throws RuntimeException
*/
public function renameTable($oldTable, $newTable, $backup = null, $prefix = null)
{
$this->setQuery('RENAME ' . $oldTable . ' TO ' . $newTable)->execute();
return $this;
}
/**
* Unlocks tables in the database.
*
* @return JDatabaseDriverOracle Returns this object to support chaining.
*
* @since 12.1
* @throws RuntimeException
*/
public function unlockTables()
{
$this->setQuery('COMMIT')->execute();
return $this;
}
/**
* Test to see if the PDO ODBC connector is available.
*
* @return boolean True on success, false otherwise.
*
* @since 12.1
*/
public static function isSupported()
{
return class_exists('PDO') && in_array('oci', PDO::getAvailableDrivers());
}
/**
* This function replaces a string identifier <var>$prefix</var> with the string held is the
* <var>tablePrefix</var> class variable.
*
* @param string $query The SQL statement to prepare.
* @param string $prefix The common table prefix.
*
* @return string The processed SQL statement.
*
* @since 11.1
*/
public function replacePrefix($query, $prefix = '#__')
{
$startPos = 0;
$quoteChar = "'";
$literal = '';
$query = trim($query);
$n = strlen($query);
while ($startPos < $n)
{
$ip = strpos($query, $prefix, $startPos);
if ($ip === false)
{
break;
}
$j = strpos($query, "'", $startPos);
if ($j === false)
{
$j = $n;
}
$literal .= str_replace($prefix, $this->tablePrefix, substr($query, $startPos, $j - $startPos));
$startPos = $j;
$j = $startPos + 1;
if ($j >= $n)
{
break;
}
// Quote comes first, find end of quote
while (true)
{
$k = strpos($query, $quoteChar, $j);
$escaped = false;
if ($k === false)
{
break;
}
$l = $k - 1;
while ($l >= 0 && $query{$l} == '\\')
{
$l--;
$escaped = !$escaped;
}
if ($escaped)
{
$j = $k + 1;
continue;
}
break;
}
if ($k === false)
{
// Error in the query - no end quote; ignore it
break;
}
$literal .= substr($query, $startPos, $k - $startPos + 1);
$startPos = $k + 1;
}
if ($startPos < $n)
{
$literal .= substr($query, $startPos, $n - $startPos);
}
return $literal;
}
/**
* Method to commit a transaction.
*
* @param boolean $toSavepoint If true, commit to the last savepoint.
*
* @return void
*
* @since 12.3
* @throws RuntimeException
*/
public function transactionCommit($toSavepoint = false)
{
$this->connect();
if (!$toSavepoint || $this->transactionDepth <= 1)
{
parent::transactionCommit($toSavepoint);
}
else
{
$this->transactionDepth--;
}
}
/**
* Method to roll back a transaction.
*
* @param boolean $toSavepoint If true, rollback to the last savepoint.
*
* @return void
*
* @since 12.3
* @throws RuntimeException
*/
public function transactionRollback($toSavepoint = false)
{
$this->connect();
if (!$toSavepoint || $this->transactionDepth <= 1)
{
parent::transactionRollback($toSavepoint);
}
else
{
$savepoint = 'SP_' . ($this->transactionDepth - 1);
$this->setQuery('ROLLBACK TO SAVEPOINT ' . $this->quoteName($savepoint));
if ($this->execute())
{
$this->transactionDepth--;
}
}
}
/**
* Method to initialize a transaction.
*
* @param boolean $asSavepoint If true and a transaction is already active, a savepoint will be created.
*
* @return void
*
* @since 12.3
* @throws RuntimeException
*/
public function transactionStart($asSavepoint = false)
{
$this->connect();
if (!$asSavepoint || !$this->transactionDepth)
{
return parent::transactionStart($asSavepoint);
}
$savepoint = 'SP_' . $this->transactionDepth;
$this->setQuery('SAVEPOINT ' . $this->quoteName($savepoint));
if ($this->execute())
{
$this->transactionDepth++;
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,29 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Database
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* SQL Server database driver
*
* @package Joomla.Platform
* @subpackage Database
* @see http://msdn.microsoft.com/en-us/library/ee336279.aspx
* @since 12.1
*/
class JDatabaseDriverSqlazure extends JDatabaseDriverSqlsrv
{
/**
* The name of the database driver.
*
* @var string
* @since 12.1
*/
public $name = 'sqlzure';
}

View File

@ -0,0 +1,464 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Database
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* SQLite database driver
*
* @package Joomla.Platform
* @subpackage Database
* @see http://php.net/pdo
* @since 12.1
*/
class JDatabaseDriverSqlite extends JDatabaseDriverPdo
{
/**
* The name of the database driver.
*
* @var string
* @since 12.1
*/
public $name = 'sqlite';
/**
* The character(s) used to quote SQL statement names such as table names or field names,
* etc. The child classes should define this as necessary. If a single character string the
* same character is used for both sides of the quoted name, else the first character will be
* used for the opening quote and the second for the closing quote.
*
* @var string
* @since 12.1
*/
protected $nameQuote = '`';
/**
* Destructor.
*
* @since 12.1
*/
public function __destruct()
{
$this->freeResult();
unset($this->connection);
}
/**
* Disconnects the database.
*
* @return void
*
* @since 12.1
*/
public function disconnect()
{
$this->freeResult();
unset($this->connection);
}
/**
* Drops a table from the database.
*
* @param string $tableName The name of the database table to drop.
* @param boolean $ifExists Optionally specify that the table must exist before it is dropped.
*
* @return JDatabaseDriverSqlite Returns this object to support chaining.
*
* @since 12.1
*/
public function dropTable($tableName, $ifExists = true)
{
$this->connect();
$query = $this->getQuery(true);
$this->setQuery('DROP TABLE ' . ($ifExists ? 'IF EXISTS ' : '') . $query->quoteName($tableName));
$this->execute();
return $this;
}
/**
* Method to escape a string for usage in an SQLite statement.
*
* Note: Using query objects with bound variables is
* preferable to the below.
*
* @param string $text The string to be escaped.
* @param boolean $extra Unused optional parameter to provide extra escaping.
*
* @return string The escaped string.
*
* @since 12.1
*/
public function escape($text, $extra = false)
{
if (is_int($text) || is_float($text))
{
return $text;
}
return SQLite3::escapeString($text);
}
/**
* Method to get the database collation in use by sampling a text field of a table in the database.
*
* @return mixed The collation in use by the database or boolean false if not supported.
*
* @since 12.1
*/
public function getCollation()
{
return $this->charset;
}
/**
* Shows the table CREATE statement that creates the given tables.
*
* Note: Doesn't appear to have support in SQLite
*
* @param mixed $tables A table name or a list of table names.
*
* @return array A list of the create SQL for the tables.
*
* @since 12.1
* @throws RuntimeException
*/
public function getTableCreate($tables)
{
$this->connect();
// Sanitize input to an array and iterate over the list.
settype($tables, 'array');
return $tables;
}
/**
* Retrieves field information about a given table.
*
* @param string $table The name of the database table.
* @param boolean $typeOnly True to only return field types.
*
* @return array An array of fields for the database table.
*
* @since 12.1
* @throws RuntimeException
*/
public function getTableColumns($table, $typeOnly = true)
{
$this->connect();
$columns = array();
$query = $this->getQuery(true);
$fieldCasing = $this->getOption(PDO::ATTR_CASE);
$this->setOption(PDO::ATTR_CASE, PDO::CASE_UPPER);
$table = strtoupper($table);
$query->setQuery('pragma table_info(' . $table . ')');
$this->setQuery($query);
$fields = $this->loadObjectList();
if ($typeOnly)
{
foreach ($fields as $field)
{
$columns[$field->NAME] = $field->TYPE;
}
}
else
{
foreach ($fields as $field)
{
// Do some dirty translation to MySQL output.
// TODO: Come up with and implement a standard across databases.
$columns[$field->NAME] = (object) array(
'Field' => $field->NAME,
'Type' => $field->TYPE,
'Null' => ($field->NOTNULL == '1' ? 'NO' : 'YES'),
'Default' => $field->DFLT_VALUE,
'Key' => ($field->PK == '1' ? 'PRI' : '')
);
}
}
$this->setOption(PDO::ATTR_CASE, $fieldCasing);
return $columns;
}
/**
* Get the details list of keys for a table.
*
* @param string $table The name of the table.
*
* @return array An array of the column specification for the table.
*
* @since 12.1
* @throws RuntimeException
*/
public function getTableKeys($table)
{
$this->connect();
$keys = array();
$query = $this->getQuery(true);
$fieldCasing = $this->getOption(PDO::ATTR_CASE);
$this->setOption(PDO::ATTR_CASE, PDO::CASE_UPPER);
$table = strtoupper($table);
$query->setQuery('pragma table_info( ' . $table . ')');
// $query->bind(':tableName', $table);
$this->setQuery($query);
$rows = $this->loadObjectList();
foreach ($rows as $column)
{
if ($column->PK == 1)
{
$keys[$column->NAME] = $column;
}
}
$this->setOption(PDO::ATTR_CASE, $fieldCasing);
return $keys;
}
/**
* Method to get an array of all tables in the database (schema).
*
* @return array An array of all the tables in the database.
*
* @since 12.1
* @throws RuntimeException
*/
public function getTableList()
{
$this->connect();
$type = 'table';
$query->$this->getQuery(true)
->select('name')
->from('sqlite_master')
->where('type = :type')
->bind(':type', $type)
->order('name');
$this->setQuery($query);
$tables = $this->loadColumn();
return $tables;
}
/**
* Get the version of the database connector.
*
* @return string The database connector version.
*
* @since 12.1
*/
public function getVersion()
{
$this->connect();
$this->setQuery("SELECT sqlite_version()");
return $this->loadResult();
}
/**
* Select a database for use.
*
* @param string $database The name of the database to select for use.
*
* @return boolean True if the database was successfully selected.
*
* @since 12.1
* @throws RuntimeException
*/
public function select($database)
{
$this->connect();
return true;
}
/**
* Set the connection to use UTF-8 character encoding.
*
* Returns false automatically for the Oracle driver since
* you can only set the character set when the connection
* is created.
*
* @return boolean True on success.
*
* @since 12.1
*/
public function setUTF()
{
$this->connect();
return false;
}
/**
* Locks a table in the database.
*
* @param string $table The name of the table to unlock.
*
* @return JDatabaseDriverSqlite Returns this object to support chaining.
*
* @since 12.1
* @throws RuntimeException
*/
public function lockTable($table)
{
return $this;
}
/**
* Renames a table in the database.
*
* @param string $oldTable The name of the table to be renamed
* @param string $newTable The new name for the table.
* @param string $backup Not used by Sqlite.
* @param string $prefix Not used by Sqlite.
*
* @return JDatabaseDriverSqlite Returns this object to support chaining.
*
* @since 12.1
* @throws RuntimeException
*/
public function renameTable($oldTable, $newTable, $backup = null, $prefix = null)
{
$this->setQuery('ALTER TABLE ' . $oldTable . ' RENAME TO ' . $newTable)->execute();
return $this;
}
/**
* Unlocks tables in the database.
*
* @return JDatabaseDriverSqlite Returns this object to support chaining.
*
* @since 12.1
* @throws RuntimeException
*/
public function unlockTables()
{
return $this;
}
/**
* Test to see if the PDO ODBC connector is available.
*
* @return boolean True on success, false otherwise.
*
* @since 12.1
*/
public static function isSupported()
{
return class_exists('PDO') && in_array('sqlite', PDO::getAvailableDrivers());
}
/**
* Method to commit a transaction.
*
* @param boolean $toSavepoint If true, commit to the last savepoint.
*
* @return void
*
* @since 12.3
* @throws RuntimeException
*/
public function transactionCommit($toSavepoint = false)
{
$this->connect();
if (!$toSavepoint || $this->transactionDepth <= 1)
{
parent::transactionCommit($toSavepoint);
}
else
{
$this->transactionDepth--;
}
}
/**
* Method to roll back a transaction.
*
* @param boolean $toSavepoint If true, rollback to the last savepoint.
*
* @return void
*
* @since 12.3
* @throws RuntimeException
*/
public function transactionRollback($toSavepoint = false)
{
$this->connect();
if (!$toSavepoint || $this->transactionDepth <= 1)
{
parent::transactionRollback($toSavepoint);
}
else
{
$savepoint = 'SP_' . ($this->transactionDepth - 1);
$this->setQuery('ROLLBACK TO ' . $this->quoteName($savepoint));
if ($this->execute())
{
$this->transactionDepth--;
}
}
}
/**
* Method to initialize a transaction.
*
* @param boolean $asSavepoint If true and a transaction is already active, a savepoint will be created.
*
* @return void
*
* @since 12.3
* @throws RuntimeException
*/
public function transactionStart($asSavepoint = false)
{
$this->connect();
if (!$asSavepoint || !$this->transactionDepth)
{
parent::transactionStart($asSavepoint);
}
$savepoint = 'SP_' . $this->transactionDepth;
$this->setQuery('SAVEPOINT ' . $this->quoteName($savepoint));
if ($this->execute())
{
$this->transactionDepth++;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,22 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Database
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* Joomla Platform Database Exporter Class
*
* @package Joomla.Platform
* @subpackage Database
* @since 12.1
*/
abstract class JDatabaseExporter
{
}

View File

@ -0,0 +1 @@
<!DOCTYPE html><title></title>

View File

@ -0,0 +1,62 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Database
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* MySQL export driver.
*
* @package Joomla.Platform
* @subpackage Database
* @since 11.1
*/
class JDatabaseExporterMysql extends JDatabaseExporterMysqli
{
/**
* Checks if all data and options are in order prior to exporting.
*
* @return JDatabaseExporterMySQL Method supports chaining.
*
* @since 11.1
*
* @throws Exception if an error is encountered.
*/
public function check()
{
// Check if the db connector has been set.
if (!($this->db instanceof JDatabaseDriverMysql))
{
throw new Exception('JPLATFORM_ERROR_DATABASE_CONNECTOR_WRONG_TYPE');
}
// Check if the tables have been specified.
if (empty($this->from))
{
throw new Exception('JPLATFORM_ERROR_NO_TABLES_SPECIFIED');
}
return $this;
}
/**
* Sets the database connector to use for exporting structure and/or data from MySQL.
*
* @param JDatabaseDriverMysql $db The database connector.
*
* @return JDatabaseExporterMysql Method supports chaining.
*
* @since 11.1
*/
public function setDbo(JDatabaseDriverMysql $db)
{
$this->db = $db;
return $this;
}
}

View File

@ -0,0 +1,287 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Database
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* MySQLi export driver.
*
* @package Joomla.Platform
* @subpackage Database
* @since 11.1
*/
class JDatabaseExporterMysqli extends JDatabaseExporter
{
/**
* An array of cached data.
*
* @var array
* @since 11.1
*/
protected $cache = array();
/**
* The database connector to use for exporting structure and/or data.
*
* @var JDatabaseDriverMysql
* @since 11.1
*/
protected $db = null;
/**
* An array input sources (table names).
*
* @var array
* @since 11.1
*/
protected $from = array();
/**
* The type of output format (xml).
*
* @var string
* @since 11.1
*/
protected $asFormat = 'xml';
/**
* An array of options for the exporter.
*
* @var object
* @since 11.1
*/
protected $options = null;
/**
* Constructor.
*
* Sets up the default options for the exporter.
*
* @since 11.1
*/
public function __construct()
{
$this->options = new stdClass;
$this->cache = array('columns' => array(), 'keys' => array());
// Set up the class defaults:
// Export with only structure
$this->withStructure();
// Export as xml.
$this->asXml();
// Default destination is a string using $output = (string) $exporter;
}
/**
* Magic function to exports the data to a string.
*
* @return string
*
* @since 11.1
* @throws Exception if an error is encountered.
*/
public function __toString()
{
// Check everything is ok to run first.
$this->check();
return $this->buildXml();
}
/**
* Set the output option for the exporter to XML format.
*
* @return JDatabaseExporterMySQL Method supports chaining.
*
* @since 11.1
*/
public function asXml()
{
$this->asFormat = 'xml';
return $this;
}
/**
* Builds the XML data for the tables to export.
*
* @return string An XML string
*
* @since 11.1
* @throws Exception if an error occurs.
*/
protected function buildXml()
{
$buffer = array();
$buffer[] = '<?xml version="1.0"?>';
$buffer[] = '<mysqldump xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">';
$buffer[] = ' <database name="">';
$buffer = array_merge($buffer, $this->buildXmlStructure());
$buffer[] = ' </database>';
$buffer[] = '</mysqldump>';
return implode("\n", $buffer);
}
/**
* Builds the XML structure to export.
*
* @return array An array of XML lines (strings).
*
* @since 11.1
* @throws Exception if an error occurs.
*/
protected function buildXmlStructure()
{
$buffer = array();
foreach ($this->from as $table)
{
// Replace the magic prefix if found.
$table = $this->getGenericTableName($table);
// Get the details columns information.
$fields = $this->db->getTableColumns($table, false);
$keys = $this->db->getTableKeys($table);
$buffer[] = ' <table_structure name="' . $table . '">';
foreach ($fields as $field)
{
$buffer[] = ' <field Field="' . $field->Field . '"' . ' Type="' . $field->Type . '"' . ' Null="' . $field->Null . '"' . ' Key="' .
$field->Key . '"' . (isset($field->Default) ? ' Default="' . $field->Default . '"' : '') . ' Extra="' . $field->Extra . '"' .
' />';
}
foreach ($keys as $key)
{
$buffer[] = ' <key Table="' . $table . '"' . ' Non_unique="' . $key->Non_unique . '"' . ' Key_name="' . $key->Key_name . '"' .
' Seq_in_index="' . $key->Seq_in_index . '"' . ' Column_name="' . $key->Column_name . '"' . ' Collation="' . $key->Collation . '"' .
' Null="' . $key->Null . '"' . ' Index_type="' . $key->Index_type . '"' . ' Comment="' . htmlspecialchars($key->Comment) . '"' .
' />';
}
$buffer[] = ' </table_structure>';
}
return $buffer;
}
/**
* Checks if all data and options are in order prior to exporting.
*
* @return JDatabaseExporterMysqli Method supports chaining.
*
* @since 11.1
* @throws Exception if an error is encountered.
*/
public function check()
{
// Check if the db connector has been set.
if (!($this->db instanceof JDatabaseDriverMysqli))
{
throw new Exception('JPLATFORM_ERROR_DATABASE_CONNECTOR_WRONG_TYPE');
}
// Check if the tables have been specified.
if (empty($this->from))
{
throw new Exception('JPLATFORM_ERROR_NO_TABLES_SPECIFIED');
}
return $this;
}
/**
* Get the generic name of the table, converting the database prefix to the wildcard string.
*
* @param string $table The name of the table.
*
* @return string The name of the table with the database prefix replaced with #__.
*
* @since 11.1
*/
protected function getGenericTableName($table)
{
// TODO Incorporate into parent class and use $this.
$prefix = $this->db->getPrefix();
// Replace the magic prefix if found.
$table = preg_replace("|^$prefix|", '#__', $table);
return $table;
}
/**
* Specifies a list of table names to export.
*
* @param mixed $from The name of a single table, or an array of the table names to export.
*
* @return JDatabaseExporterMysql Method supports chaining.
*
* @since 11.1
* @throws Exception if input is not a string or array.
*/
public function from($from)
{
if (is_string($from))
{
$this->from = array($from);
}
elseif (is_array($from))
{
$this->from = $from;
}
else
{
throw new Exception('JPLATFORM_ERROR_INPUT_REQUIRES_STRING_OR_ARRAY');
}
return $this;
}
/**
* Sets the database connector to use for exporting structure and/or data from MySQL.
*
* @param JDatabaseDriverMysqli $db The database connector.
*
* @return JDatabaseExporterMysqli Method supports chaining.
*
* @since 11.1
*/
public function setDbo(JDatabaseDriverMysqli $db)
{
$this->db = $db;
return $this;
}
/**
* Sets an internal option to export the structure of the input table(s).
*
* @param boolean $setting True to export the structure, false to not.
*
* @return JDatabaseExporterMysql Method supports chaining.
*
* @since 11.1
*/
public function withStructure($setting = true)
{
$this->options->withStructure = (boolean) $setting;
return $this;
}
}

View File

@ -0,0 +1,302 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Database
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* PostgreSQL export driver.
*
* @package Joomla.Platform
* @subpackage Database
* @since 12.1
*/
class JDatabaseExporterPostgresql extends JDatabaseExporter
{
/**
* An array of cached data.
*
* @var array
* @since 12.1
*/
protected $cache = array();
/**
* The database connector to use for exporting structure and/or data.
*
* @var JDatabaseDriverPostgresql
* @since 12.1
*/
protected $db = null;
/**
* An array input sources (table names).
*
* @var array
* @since 12.1
*/
protected $from = array();
/**
* The type of output format (xml).
*
* @var string
* @since 12.1
*/
protected $asFormat = 'xml';
/**
* An array of options for the exporter.
*
* @var object
* @since 12.1
*/
protected $options = null;
/**
* Constructor.
*
* Sets up the default options for the exporter.
*
* @since 12.1
*/
public function __construct()
{
$this->options = new stdClass;
$this->cache = array('columns' => array(), 'keys' => array());
// Set up the class defaults:
// Export with only structure
$this->withStructure();
// Export as xml.
$this->asXml();
// Default destination is a string using $output = (string) $exporter;
}
/**
* Magic function to exports the data to a string.
*
* @return string
*
* @since 12.1
* @throws Exception if an error is encountered.
*/
public function __toString()
{
// Check everything is ok to run first.
$this->check();
return $this->buildXml();
}
/**
* Set the output option for the exporter to XML format.
*
* @return JDatabaseExporterPostgresql Method supports chaining.
*
* @since 12.1
*/
public function asXml()
{
$this->asFormat = 'xml';
return $this;
}
/**
* Builds the XML data for the tables to export.
*
* @return string An XML string
*
* @since 12.1
* @throws Exception if an error occurs.
*/
protected function buildXml()
{
$buffer = array();
$buffer[] = '<?xml version="1.0"?>';
$buffer[] = '<postgresqldump xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">';
$buffer[] = ' <database name="">';
$buffer = array_merge($buffer, $this->buildXmlStructure());
$buffer[] = ' </database>';
$buffer[] = '</postgresqldump>';
return implode("\n", $buffer);
}
/**
* Builds the XML structure to export.
*
* @return array An array of XML lines (strings).
*
* @since 12.1
* @throws Exception if an error occurs.
*/
protected function buildXmlStructure()
{
$buffer = array();
foreach ($this->from as $table)
{
// Replace the magic prefix if found.
$table = $this->getGenericTableName($table);
// Get the details columns information.
$fields = $this->db->getTableColumns($table, false);
$keys = $this->db->getTableKeys($table);
$sequences = $this->db->getTableSequences($table);
$buffer[] = ' <table_structure name="' . $table . '">';
foreach ($sequences as $sequence)
{
if (version_compare($this->db->getVersion(), '9.1.0') < 0)
{
$sequence->start_value = null;
}
$buffer[] = ' <sequence Name="' . $sequence->sequence . '"' . ' Schema="' . $sequence->schema . '"' .
' Table="' . $sequence->table . '"' . ' Column="' . $sequence->column . '"' . ' Type="' . $sequence->data_type . '"' .
' Start_Value="' . $sequence->start_value . '"' . ' Min_Value="' . $sequence->minimum_value . '"' .
' Max_Value="' . $sequence->maximum_value . '"' . ' Increment="' . $sequence->increment . '"' .
' Cycle_option="' . $sequence->cycle_option . '"' .
' />';
}
foreach ($fields as $field)
{
$buffer[] = ' <field Field="' . $field->column_name . '"' . ' Type="' . $field->type . '"' . ' Null="' . $field->null . '"' .
(isset($field->default) ? ' Default="' . $field->default . '"' : '') . ' Comments="' . $field->comments . '"' .
' />';
}
foreach ($keys as $key)
{
$buffer[] = ' <key Index="' . $key->idxName . '"' . ' is_primary="' . $key->isPrimary . '"' . ' is_unique="' . $key->isUnique . '"' .
' Query="' . $key->Query . '" />';
}
$buffer[] = ' </table_structure>';
}
return $buffer;
}
/**
* Checks if all data and options are in order prior to exporting.
*
* @return JDatabaseExporterPostgresql Method supports chaining.
*
* @since 12.1
*
* @throws Exception if an error is encountered.
*/
public function check()
{
// Check if the db connector has been set.
if (!($this->db instanceof JDatabaseDriverPostgresql))
{
throw new Exception('JPLATFORM_ERROR_DATABASE_CONNECTOR_WRONG_TYPE');
}
// Check if the tables have been specified.
if (empty($this->from))
{
throw new Exception('JPLATFORM_ERROR_NO_TABLES_SPECIFIED');
}
return $this;
}
/**
* Get the generic name of the table, converting the database prefix to the wildcard string.
*
* @param string $table The name of the table.
*
* @return string The name of the table with the database prefix replaced with #__.
*
* @since 12.1
*/
protected function getGenericTableName($table)
{
// TODO Incorporate into parent class and use $this.
$prefix = $this->db->getPrefix();
// Replace the magic prefix if found.
$table = preg_replace("|^$prefix|", '#__', $table);
return $table;
}
/**
* Specifies a list of table names to export.
*
* @param mixed $from The name of a single table, or an array of the table names to export.
*
* @return JDatabaseExporterPostgresql Method supports chaining.
*
* @since 12.1
* @throws Exception if input is not a string or array.
*/
public function from($from)
{
if (is_string($from))
{
$this->from = array($from);
}
elseif (is_array($from))
{
$this->from = $from;
}
else
{
throw new Exception('JPLATFORM_ERROR_INPUT_REQUIRES_STRING_OR_ARRAY');
}
return $this;
}
/**
* Sets the database connector to use for exporting structure and/or data from PostgreSQL.
*
* @param JDatabaseDriverPostgresql $db The database connector.
*
* @return JDatabaseExporterPostgresql Method supports chaining.
*
* @since 12.1
*/
public function setDbo(JDatabaseDriverPostgresql $db)
{
$this->db = $db;
return $this;
}
/**
* Sets an internal option to export the structure of the input table(s).
*
* @param boolean $setting True to export the structure, false to not.
*
* @return JDatabaseExporterPostgresql Method supports chaining.
*
* @since 12.1
*/
public function withStructure($setting = true)
{
$this->options->withStructure = (boolean) $setting;
return $this;
}
}

View File

@ -0,0 +1,192 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Database
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* Joomla Platform Database Factory class
*
* @package Joomla.Platform
* @subpackage Database
* @since 12.1
*/
class JDatabaseFactory
{
/**
* Contains the current JDatabaseFactory instance
*
* @var JDatabaseFactory
* @since 12.1
*/
private static $_instance = null;
/**
* Method to return a JDatabaseDriver instance based on the given options. There are three global options and then
* the rest are specific to the database driver. The 'database' option determines which database is to
* be used for the connection. The 'select' option determines whether the connector should automatically select
* the chosen database.
*
* Instances are unique to the given options and new objects are only created when a unique options array is
* passed into the method. This ensures that we don't end up with unnecessary database connection resources.
*
* @param string $name Name of the database driver you'd like to instantiate
* @param array $options Parameters to be passed to the database driver.
*
* @return JDatabaseDriver A database driver object.
*
* @since 12.1
* @throws RuntimeException
*/
public function getDriver($name = 'mysqli', $options = array())
{
// Sanitize the database connector options.
$options['driver'] = preg_replace('/[^A-Z0-9_\.-]/i', '', $name);
$options['database'] = (isset($options['database'])) ? $options['database'] : null;
$options['select'] = (isset($options['select'])) ? $options['select'] : true;
// Derive the class name from the driver.
$class = 'JDatabaseDriver' . ucfirst(strtolower($options['driver']));
// If the class still doesn't exist we have nothing left to do but throw an exception. We did our best.
if (!class_exists($class))
{
throw new RuntimeException(sprintf('Unable to load Database Driver: %s', $options['driver']));
}
// Create our new JDatabaseDriver connector based on the options given.
try
{
$instance = new $class($options);
}
catch (RuntimeException $e)
{
throw new RuntimeException(sprintf('Unable to connect to the Database: %s', $e->getMessage()));
}
return $instance;
}
/**
* Gets an exporter class object.
*
* @param string $name Name of the driver you want an exporter for.
* @param JDatabaseDriver $db Optional JDatabaseDriver instance
*
* @return JDatabaseExporter An exporter object.
*
* @since 12.1
* @throws RuntimeException
*/
public function getExporter($name, JDatabaseDriver $db = null)
{
// Derive the class name from the driver.
$class = 'JDatabaseExporter' . ucfirst(strtolower($name));
// Make sure we have an exporter class for this driver.
if (!class_exists($class))
{
// If it doesn't exist we are at an impasse so throw an exception.
throw new RuntimeException('Database Exporter not found.');
}
$o = new $class;
if ($db instanceof JDatabaseDriver)
{
$o->setDbo($db);
}
return $o;
}
/**
* Gets an importer class object.
*
* @param string $name Name of the driver you want an importer for.
* @param JDatabaseDriver $db Optional JDatabaseDriver instance
*
* @return JDatabaseImporter An importer object.
*
* @since 12.1
* @throws RuntimeException
*/
public function getImporter($name, JDatabaseDriver $db = null)
{
// Derive the class name from the driver.
$class = 'JDatabaseImporter' . ucfirst(strtolower($name));
// Make sure we have an importer class for this driver.
if (!class_exists($class))
{
// If it doesn't exist we are at an impasse so throw an exception.
throw new RuntimeException('Database importer not found.');
}
$o = new $class;
if ($db instanceof JDatabaseDriver)
{
$o->setDbo($db);
}
return $o;
}
/**
* Gets an instance of the factory object.
*
* @return JDatabaseFactory
*
* @since 12.1
*/
public static function getInstance()
{
return self::$_instance ? self::$_instance : new JDatabaseFactory;
}
/**
* Get the current query object or a new JDatabaseQuery object.
*
* @param string $name Name of the driver you want an query object for.
* @param JDatabaseDriver $db Optional JDatabaseDriver instance
*
* @return JDatabaseQuery The current query object or a new object extending the JDatabaseQuery class.
*
* @since 12.1
* @throws RuntimeException
*/
public function getQuery($name, JDatabaseDriver $db = null)
{
// Derive the class name from the driver.
$class = 'JDatabaseQuery' . ucfirst(strtolower($name));
// Make sure we have a query class for this driver.
if (!class_exists($class))
{
// If it doesn't exist we are at an impasse so throw an exception.
throw new RuntimeException('Database Query class not found');
}
return new $class($db);
}
/**
* Gets an instance of a factory object to return on subsequent calls of getInstance.
*
* @param JDatabaseFactory $instance A JDatabaseFactory object.
*
* @return void
*
* @since 12.1
*/
public static function setInstance(JDatabaseFactory $instance = null)
{
self::$_instance = $instance;
}
}

View File

@ -0,0 +1,22 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Database
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* Joomla Platform Database Importer Class
*
* @package Joomla.Platform
* @subpackage Database
* @since 12.1
*/
abstract class JDatabaseImporter
{
}

View File

@ -0,0 +1 @@
<!DOCTYPE html><title></title>

View File

@ -0,0 +1,61 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Database
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* MySQL import driver.
*
* @package Joomla.Platform
* @subpackage Database
* @since 11.1
*/
class JDatabaseImporterMysql extends JDatabaseImporterMysqli
{
/**
* Checks if all data and options are in order prior to exporting.
*
* @return JDatabaseImporterMysql Method supports chaining.
*
* @since 11.1
* @throws Exception if an error is encountered.
*/
public function check()
{
// Check if the db connector has been set.
if (!($this->db instanceof JDatabaseDriverMysql))
{
throw new Exception('JPLATFORM_ERROR_DATABASE_CONNECTOR_WRONG_TYPE');
}
// Check if the tables have been specified.
if (empty($this->from))
{
throw new Exception('JPLATFORM_ERROR_NO_TABLES_SPECIFIED');
}
return $this;
}
/**
* Sets the database connector to use for exporting structure and/or data from MySQL.
*
* @param JDatabaseDriverMysql $db The database connector.
*
* @return JDatabaseImporterMysql Method supports chaining.
*
* @since 11.1
*/
public function setDbo(JDatabaseDriverMysql $db)
{
$this->db = $db;
return $this;
}
}

View File

@ -0,0 +1,646 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Database
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* MySQLi import driver.
*
* @package Joomla.Platform
* @subpackage Database
* @since 11.1
*/
class JDatabaseImporterMysqli extends JDatabaseImporter
{
/**
* @var array An array of cached data.
* @since 11.1
*/
protected $cache = array();
/**
* The database connector to use for exporting structure and/or data.
*
* @var JDatabaseDriverMysql
* @since 11.1
*/
protected $db = null;
/**
* The input source.
*
* @var mixed
* @since 11.1
*/
protected $from = array();
/**
* The type of input format (XML).
*
* @var string
* @since 11.1
*/
protected $asFormat = 'xml';
/**
* An array of options for the exporter.
*
* @var object
* @since 11.1
*/
protected $options = null;
/**
* Constructor.
*
* Sets up the default options for the exporter.
*
* @since 11.1
*/
public function __construct()
{
$this->options = new stdClass;
$this->cache = array('columns' => array(), 'keys' => array());
// Set up the class defaults:
// Import with only structure
$this->withStructure();
// Export as XML.
$this->asXml();
// Default destination is a string using $output = (string) $exporter;
}
/**
* Set the output option for the exporter to XML format.
*
* @return JDatabaseImporterMysql Method supports chaining.
*
* @since 11.1
*/
public function asXml()
{
$this->asFormat = 'xml';
return $this;
}
/**
* Checks if all data and options are in order prior to exporting.
*
* @return JDatabaseImporterMysqli Method supports chaining.
*
* @since 11.1
* @throws Exception if an error is encountered.
*/
public function check()
{
// Check if the db connector has been set.
if (!($this->db instanceof JDatabaseDriverMysqli))
{
throw new Exception('JPLATFORM_ERROR_DATABASE_CONNECTOR_WRONG_TYPE');
}
// Check if the tables have been specified.
if (empty($this->from))
{
throw new Exception('JPLATFORM_ERROR_NO_TABLES_SPECIFIED');
}
return $this;
}
/**
* Specifies the data source to import.
*
* @param mixed $from The data source to import.
*
* @return JDatabaseImporterMysql Method supports chaining.
*
* @since 11.1
*/
public function from($from)
{
$this->from = $from;
return $this;
}
/**
* Get the SQL syntax to add a column.
*
* @param string $table The table name.
* @param SimpleXMLElement $field The XML field definition.
*
* @return string
*
* @since 11.1
*/
protected function getAddColumnSQL($table, SimpleXMLElement $field)
{
return 'ALTER TABLE ' . $this->db->quoteName($table) . ' ADD COLUMN ' . $this->getColumnSQL($field);
}
/**
* Get the SQL syntax to add a key.
*
* @param string $table The table name.
* @param array $keys An array of the fields pertaining to this key.
*
* @return string
*
* @since 11.1
*/
protected function getAddKeySQL($table, $keys)
{
return 'ALTER TABLE ' . $this->db->quoteName($table) . ' ADD ' . $this->getKeySQL($keys);
}
/**
* Get alters for table if there is a difference.
*
* @param SimpleXMLElement $structure The XML structure pf the table.
*
* @return array
*
* @since 11.1
*/
protected function getAlterTableSQL(SimpleXMLElement $structure)
{
$table = $this->getRealTableName($structure['name']);
$oldFields = $this->db->getTableColumns($table);
$oldKeys = $this->db->getTableKeys($table);
$alters = array();
// Get the fields and keys from the XML that we are aiming for.
$newFields = $structure->xpath('field');
$newKeys = $structure->xpath('key');
// Loop through each field in the new structure.
foreach ($newFields as $field)
{
$fName = (string) $field['Field'];
if (isset($oldFields[$fName]))
{
// The field exists, check it's the same.
$column = $oldFields[$fName];
// Test whether there is a change.
$change = ((string) $field['Type'] != $column->Type) || ((string) $field['Null'] != $column->Null)
|| ((string) $field['Default'] != $column->Default) || ((string) $field['Extra'] != $column->Extra);
if ($change)
{
$alters[] = $this->getChangeColumnSQL($table, $field);
}
// Unset this field so that what we have left are fields that need to be removed.
unset($oldFields[$fName]);
}
else
{
// The field is new.
$alters[] = $this->getAddColumnSQL($table, $field);
}
}
// Any columns left are orphans
foreach ($oldFields as $name => $column)
{
// Delete the column.
$alters[] = $this->getDropColumnSQL($table, $name);
}
// Get the lookups for the old and new keys.
$oldLookup = $this->getKeyLookup($oldKeys);
$newLookup = $this->getKeyLookup($newKeys);
// Loop through each key in the new structure.
foreach ($newLookup as $name => $keys)
{
// Check if there are keys on this field in the existing table.
if (isset($oldLookup[$name]))
{
$same = true;
$newCount = count($newLookup[$name]);
$oldCount = count($oldLookup[$name]);
// There is a key on this field in the old and new tables. Are they the same?
if ($newCount == $oldCount)
{
// Need to loop through each key and do a fine grained check.
for ($i = 0; $i < $newCount; $i++)
{
$same = (((string) $newLookup[$name][$i]['Non_unique'] == $oldLookup[$name][$i]->Non_unique)
&& ((string) $newLookup[$name][$i]['Column_name'] == $oldLookup[$name][$i]->Column_name)
&& ((string) $newLookup[$name][$i]['Seq_in_index'] == $oldLookup[$name][$i]->Seq_in_index)
&& ((string) $newLookup[$name][$i]['Collation'] == $oldLookup[$name][$i]->Collation)
&& ((string) $newLookup[$name][$i]['Index_type'] == $oldLookup[$name][$i]->Index_type));
/*
Debug.
echo '<pre>';
echo '<br />Non_unique: '.
((string) $newLookup[$name][$i]['Non_unique'] == $oldLookup[$name][$i]->Non_unique ? 'Pass' : 'Fail').' '.
(string) $newLookup[$name][$i]['Non_unique'].' vs '.$oldLookup[$name][$i]->Non_unique;
echo '<br />Column_name: '.
((string) $newLookup[$name][$i]['Column_name'] == $oldLookup[$name][$i]->Column_name ? 'Pass' : 'Fail').' '.
(string) $newLookup[$name][$i]['Column_name'].' vs '.$oldLookup[$name][$i]->Column_name;
echo '<br />Seq_in_index: '.
((string) $newLookup[$name][$i]['Seq_in_index'] == $oldLookup[$name][$i]->Seq_in_index ? 'Pass' : 'Fail').' '.
(string) $newLookup[$name][$i]['Seq_in_index'].' vs '.$oldLookup[$name][$i]->Seq_in_index;
echo '<br />Collation: '.
((string) $newLookup[$name][$i]['Collation'] == $oldLookup[$name][$i]->Collation ? 'Pass' : 'Fail').' '.
(string) $newLookup[$name][$i]['Collation'].' vs '.$oldLookup[$name][$i]->Collation;
echo '<br />Index_type: '.
((string) $newLookup[$name][$i]['Index_type'] == $oldLookup[$name][$i]->Index_type ? 'Pass' : 'Fail').' '.
(string) $newLookup[$name][$i]['Index_type'].' vs '.$oldLookup[$name][$i]->Index_type;
echo '<br />Same = '.($same ? 'true' : 'false');
echo '</pre>';
*/
if (!$same)
{
// Break out of the loop. No need to check further.
break;
}
}
}
else
{
// Count is different, just drop and add.
$same = false;
}
if (!$same)
{
$alters[] = $this->getDropKeySQL($table, $name);
$alters[] = $this->getAddKeySQL($table, $keys);
}
// Unset this field so that what we have left are fields that need to be removed.
unset($oldLookup[$name]);
}
else
{
// This is a new key.
$alters[] = $this->getAddKeySQL($table, $keys);
}
}
// Any keys left are orphans.
foreach ($oldLookup as $name => $keys)
{
if (strtoupper($name) == 'PRIMARY')
{
$alters[] = $this->getDropPrimaryKeySQL($table);
}
else
{
$alters[] = $this->getDropKeySQL($table, $name);
}
}
return $alters;
}
/**
* Get the syntax to alter a column.
*
* @param string $table The name of the database table to alter.
* @param SimpleXMLElement $field The XML definition for the field.
*
* @return string
*
* @since 11.1
*/
protected function getChangeColumnSQL($table, SimpleXMLElement $field)
{
return 'ALTER TABLE ' . $this->db->quoteName($table) . ' CHANGE COLUMN ' . $this->db->quoteName((string) $field['Field']) . ' '
. $this->getColumnSQL($field);
}
/**
* Get the SQL syntax for a single column that would be included in a table create or alter statement.
*
* @param SimpleXMLElement $field The XML field definition.
*
* @return string
*
* @since 11.1
*/
protected function getColumnSQL(SimpleXMLElement $field)
{
// TODO Incorporate into parent class and use $this.
$blobs = array('text', 'smalltext', 'mediumtext', 'largetext');
$fName = (string) $field['Field'];
$fType = (string) $field['Type'];
$fNull = (string) $field['Null'];
$fDefault = isset($field['Default']) ? (string) $field['Default'] : null;
$fExtra = (string) $field['Extra'];
$query = $this->db->quoteName($fName) . ' ' . $fType;
if ($fNull == 'NO')
{
if (in_array($fType, $blobs) || $fDefault === null)
{
$query .= ' NOT NULL';
}
else
{
// TODO Don't quote numeric values.
$query .= ' NOT NULL DEFAULT ' . $this->db->quote($fDefault);
}
}
else
{
if ($fDefault === null)
{
$query .= ' DEFAULT NULL';
}
else
{
// TODO Don't quote numeric values.
$query .= ' DEFAULT ' . $this->db->quote($fDefault);
}
}
if ($fExtra)
{
$query .= ' ' . strtoupper($fExtra);
}
return $query;
}
/**
* Get the SQL syntax to drop a column.
*
* @param string $table The table name.
* @param string $name The name of the field to drop.
*
* @return string
*
* @since 11.1
*/
protected function getDropColumnSQL($table, $name)
{
return 'ALTER TABLE ' . $this->db->quoteName($table) . ' DROP COLUMN ' . $this->db->quoteName($name);
}
/**
* Get the SQL syntax to drop a key.
*
* @param string $table The table name.
* @param string $name The name of the key to drop.
*
* @return string
*
* @since 11.1
*/
protected function getDropKeySQL($table, $name)
{
return 'ALTER TABLE ' . $this->db->quoteName($table) . ' DROP KEY ' . $this->db->quoteName($name);
}
/**
* Get the SQL syntax to drop a key.
*
* @param string $table The table name.
*
* @return string
*
* @since 11.1
*/
protected function getDropPrimaryKeySQL($table)
{
return 'ALTER TABLE ' . $this->db->quoteName($table) . ' DROP PRIMARY KEY';
}
/**
* Get the details list of keys for a table.
*
* @param array $keys An array of objects that comprise the keys for the table.
*
* @return array The lookup array. array({key name} => array(object, ...))
*
* @since 11.1
* @throws Exception
*/
protected function getKeyLookup($keys)
{
// First pass, create a lookup of the keys.
$lookup = array();
foreach ($keys as $key)
{
if ($key instanceof SimpleXMLElement)
{
$kName = (string) $key['Key_name'];
}
else
{
$kName = $key->Key_name;
}
if (empty($lookup[$kName]))
{
$lookup[$kName] = array();
}
$lookup[$kName][] = $key;
}
return $lookup;
}
/**
* Get the SQL syntax for a key.
*
* @param array $columns An array of SimpleXMLElement objects comprising the key.
*
* @return string
*
* @since 11.1
*/
protected function getKeySQL($columns)
{
// TODO Error checking on array and element types.
$kNonUnique = (string) $columns[0]['Non_unique'];
$kName = (string) $columns[0]['Key_name'];
$kColumn = (string) $columns[0]['Column_name'];
$prefix = '';
if ($kName == 'PRIMARY')
{
$prefix = 'PRIMARY ';
}
elseif ($kNonUnique == 0)
{
$prefix = 'UNIQUE ';
}
$nColumns = count($columns);
$kColumns = array();
if ($nColumns == 1)
{
$kColumns[] = $this->db->quoteName($kColumn);
}
else
{
foreach ($columns as $column)
{
$kColumns[] = (string) $column['Column_name'];
}
}
$query = $prefix . 'KEY ' . ($kName != 'PRIMARY' ? $this->db->quoteName($kName) : '') . ' (' . implode(',', $kColumns) . ')';
return $query;
}
/**
* Get the real name of the table, converting the prefix wildcard string if present.
*
* @param string $table The name of the table.
*
* @return string The real name of the table.
*
* @since 11.1
*/
protected function getRealTableName($table)
{
// TODO Incorporate into parent class and use $this.
$prefix = $this->db->getPrefix();
// Replace the magic prefix if found.
$table = preg_replace('|^#__|', $prefix, $table);
return $table;
}
/**
* Merges the incoming structure definition with the existing structure.
*
* @return void
*
* @note Currently only supports XML format.
* @since 11.1
* @throws Exception on error.
* @todo If it's not XML convert to XML first.
*/
protected function mergeStructure()
{
$prefix = $this->db->getPrefix();
$tables = $this->db->getTableList();
if ($this->from instanceof SimpleXMLElement)
{
$xml = $this->from;
}
else
{
$xml = new SimpleXMLElement($this->from);
}
// Get all the table definitions.
$xmlTables = $xml->xpath('database/table_structure');
foreach ($xmlTables as $table)
{
// Convert the magic prefix into the real table name.
$tableName = (string) $table['name'];
$tableName = preg_replace('|^#__|', $prefix, $tableName);
if (in_array($tableName, $tables))
{
// The table already exists. Now check if there is any difference.
if ($queries = $this->getAlterTableSQL($xml->database->table_structure))
{
// Run the queries to upgrade the data structure.
foreach ($queries as $query)
{
$this->db->setQuery($query);
try
{
$this->db->execute();
}
catch (RuntimeException $e)
{
$this->addLog('Fail: ' . $this->db->getQuery());
throw $e;
}
$this->addLog('Pass: ' . $this->db->getQuery());
}
}
}
else
{
// This is a new table.
$query = $this->xmlToCreate($table);
$this->db->setQuery($query);
try
{
$this->db->execute();
}
catch (RuntimeException $e)
{
$this->addLog('Fail: ' . $this->db->getQuery());
throw $e;
}
$this->addLog('Pass: ' . $this->db->getQuery());
}
}
}
/**
* Sets the database connector to use for exporting structure and/or data from MySQL.
*
* @param JDatabaseDriverMysqli $db The database connector.
*
* @return JDatabaseImporterMysqli Method supports chaining.
*
* @since 11.1
*/
public function setDbo(JDatabaseDriverMysqli $db)
{
$this->db = $db;
return $this;
}
/**
* Sets an internal option to merge the structure based on the input data.
*
* @param boolean $setting True to export the structure, false to not.
*
* @return JDatabaseImporterMysql Method supports chaining.
*
* @since 11.1
*/
public function withStructure($setting = true)
{
$this->options->withStructure = (boolean) $setting;
return $this;
}
}

View File

@ -0,0 +1,780 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Database
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* PostgreSQL import driver.
*
* @package Joomla.Platform
* @subpackage Database
* @since 12.1
*/
class JDatabaseImporterPostgresql extends JDatabaseImporter
{
/**
* @var array An array of cached data.
* @since 12.1
*/
protected $cache = array();
/**
* The database connector to use for exporting structure and/or data.
*
* @var JDatabaseDriverPostgresql
* @since 12.1
*/
protected $db = null;
/**
* The input source.
*
* @var mixed
* @since 12.1
*/
protected $from = array();
/**
* The type of input format (XML).
*
* @var string
* @since 12.1
*/
protected $asFormat = 'xml';
/**
* An array of options for the exporter.
*
* @var object
* @since 12.1
*/
protected $options = null;
/**
* Constructor.
*
* Sets up the default options for the exporter.
*
* @since 12.1
*/
public function __construct()
{
$this->options = new stdClass;
$this->cache = array('columns' => array(), 'keys' => array());
// Set up the class defaults:
// Import with only structure
$this->withStructure();
// Export as XML.
$this->asXml();
// Default destination is a string using $output = (string) $exporter;
}
/**
* Set the output option for the exporter to XML format.
*
* @return JDatabaseImporterPostgresql Method supports chaining.
*
* @since 12.1
*/
public function asXml()
{
$this->asFormat = 'xml';
return $this;
}
/**
* Checks if all data and options are in order prior to exporting.
*
* @return JDatabaseImporterPostgresql Method supports chaining.
*
* @since 12.1
* @throws Exception if an error is encountered.
*/
public function check()
{
// Check if the db connector has been set.
if (!($this->db instanceof JDatabaseDriverPostgresql))
{
throw new Exception('JPLATFORM_ERROR_DATABASE_CONNECTOR_WRONG_TYPE');
}
// Check if the tables have been specified.
if (empty($this->from))
{
throw new Exception('JPLATFORM_ERROR_NO_TABLES_SPECIFIED');
}
return $this;
}
/**
* Specifies the data source to import.
*
* @param mixed $from The data source to import.
*
* @return JDatabaseImporterPostgresql Method supports chaining.
*
* @since 12.1
*/
public function from($from)
{
$this->from = $from;
return $this;
}
/**
* Get the SQL syntax to add a column.
*
* @param string $table The table name.
* @param SimpleXMLElement $field The XML field definition.
*
* @return string
*
* @since 12.1
*/
protected function getAddColumnSQL($table, SimpleXMLElement $field)
{
return 'ALTER TABLE ' . $this->db->quoteName($table) . ' ADD COLUMN ' . $this->getColumnSQL($field);
}
/**
* Get the SQL syntax to add an index.
*
* @param SimpleXMLElement $field The XML index definition.
*
* @return string
*
* @since 12.1
*/
protected function getAddIndexSQL(SimpleXMLElement $field)
{
return (string) $field['Query'];
}
/**
* Get alters for table if there is a difference.
*
* @param SimpleXMLElement $structure The XML structure of the table.
*
* @return array
*
* @since 12.1
*/
protected function getAlterTableSQL(SimpleXMLElement $structure)
{
$table = $this->getRealTableName($structure['name']);
$oldFields = $this->db->getTableColumns($table);
$oldKeys = $this->db->getTableKeys($table);
$oldSequence = $this->db->getTableSequences($table);
$alters = array();
// Get the fields and keys from the XML that we are aiming for.
$newFields = $structure->xpath('field');
$newKeys = $structure->xpath('key');
$newSequence = $structure->xpath('sequence');
/* Sequence section */
$oldSeq = $this->getSeqLookup($oldSequence);
$newSequenceLook = $this->getSeqLookup($newSequence);
foreach ($newSequenceLook as $kSeqName => $vSeq)
{
if (isset($oldSeq[$kSeqName]))
{
// The field exists, check it's the same.
$column = $oldSeq[$kSeqName][0];
/* For older database version that doesn't support these fields use default values */
if (version_compare($this->db->getVersion(), '9.1.0') < 0)
{
$column->Min_Value = '1';
$column->Max_Value = '9223372036854775807';
$column->Increment = '1';
$column->Cycle_option = 'NO';
$column->Start_Value = '1';
}
// Test whether there is a change.
$change = ((string) $vSeq[0]['Type'] != $column->Type) || ((string) $vSeq[0]['Start_Value'] != $column->Start_Value)
|| ((string) $vSeq[0]['Min_Value'] != $column->Min_Value) || ((string) $vSeq[0]['Max_Value'] != $column->Max_Value)
|| ((string) $vSeq[0]['Increment'] != $column->Increment) || ((string) $vSeq[0]['Cycle_option'] != $column->Cycle_option)
|| ((string) $vSeq[0]['Table'] != $column->Table) || ((string) $vSeq[0]['Column'] != $column->Column)
|| ((string) $vSeq[0]['Schema'] != $column->Schema) || ((string) $vSeq[0]['Name'] != $column->Name);
if ($change)
{
$alters[] = $this->getChangeSequenceSQL($kSeqName, $vSeq);
}
// Unset this field so that what we have left are fields that need to be removed.
unset($oldSeq[$kSeqName]);
}
else
{
// The sequence is new
$alters[] = $this->getAddSequenceSQL($newSequenceLook[$kSeqName][0]);
}
}
// Any sequences left are orphans
foreach ($oldSeq as $name => $column)
{
// Delete the sequence.
$alters[] = $this->getDropSequenceSQL($name);
}
/* Field section */
// Loop through each field in the new structure.
foreach ($newFields as $field)
{
$fName = (string) $field['Field'];
if (isset($oldFields[$fName]))
{
// The field exists, check it's the same.
$column = $oldFields[$fName];
// Test whether there is a change.
$change = ((string) $field['Type'] != $column->Type) || ((string) $field['Null'] != $column->Null)
|| ((string) $field['Default'] != $column->Default);
if ($change)
{
$alters[] = $this->getChangeColumnSQL($table, $field);
}
// Unset this field so that what we have left are fields that need to be removed.
unset($oldFields[$fName]);
}
else
{
// The field is new.
$alters[] = $this->getAddColumnSQL($table, $field);
}
}
// Any columns left are orphans
foreach ($oldFields as $name => $column)
{
// Delete the column.
$alters[] = $this->getDropColumnSQL($table, $name);
}
/* Index section */
// Get the lookups for the old and new keys
$oldLookup = $this->getIdxLookup($oldKeys);
$newLookup = $this->getIdxLookup($newKeys);
// Loop through each key in the new structure.
foreach ($newLookup as $name => $keys)
{
// Check if there are keys on this field in the existing table.
if (isset($oldLookup[$name]))
{
$same = true;
$newCount = count($newLookup[$name]);
$oldCount = count($oldLookup[$name]);
// There is a key on this field in the old and new tables. Are they the same?
if ($newCount == $oldCount)
{
for ($i = 0; $i < $newCount; $i++)
{
// Check only query field -> different query means different index
$same = ((string) $newLookup[$name][$i]['Query'] == $oldLookup[$name][$i]->Query);
if (!$same)
{
// Break out of the loop. No need to check further.
break;
}
}
}
else
{
// Count is different, just drop and add.
$same = false;
}
if (!$same)
{
$alters[] = $this->getDropIndexSQL($name);
$alters[] = (string) $newLookup[$name][0]['Query'];
}
// Unset this field so that what we have left are fields that need to be removed.
unset($oldLookup[$name]);
}
else
{
// This is a new key.
$alters[] = (string) $newLookup[$name][0]['Query'];
}
}
// Any keys left are orphans.
foreach ($oldLookup as $name => $keys)
{
if ($oldLookup[$name][0]->is_primary == 'TRUE')
{
$alters[] = $this->getDropPrimaryKeySQL($table, $oldLookup[$name][0]->Index);
}
else
{
$alters[] = $this->getDropIndexSQL($name);
}
}
return $alters;
}
/**
* Get the SQL syntax to drop a sequence.
*
* @param string $name The name of the sequence to drop.
*
* @return string
*
* @since 12.1
*/
protected function getDropSequenceSQL($name)
{
return 'DROP SEQUENCE ' . $this->db->quoteName($name);
}
/**
* Get the syntax to add a sequence.
*
* @param SimpleXMLElement $field The XML definition for the sequence.
*
* @return string
*
* @since 12.1
*/
protected function getAddSequenceSQL($field)
{
/* For older database version that doesn't support these fields use default values */
if (version_compare($this->db->getVersion(), '9.1.0') < 0)
{
$field['Min_Value'] = '1';
$field['Max_Value'] = '9223372036854775807';
$field['Increment'] = '1';
$field['Cycle_option'] = 'NO';
$field['Start_Value'] = '1';
}
return 'CREATE SEQUENCE ' . (string) $field['Name'] .
' INCREMENT BY ' . (string) $field['Increment'] . ' MINVALUE ' . $field['Min_Value'] .
' MAXVALUE ' . (string) $field['Max_Value'] . ' START ' . (string) $field['Start_Value'] .
(((string) $field['Cycle_option'] == 'NO') ? ' NO' : '') . ' CYCLE' .
' OWNED BY ' . $this->db->quoteName((string) $field['Schema'] . '.' . (string) $field['Table'] . '.' . (string) $field['Column']);
}
/**
* Get the syntax to alter a sequence.
*
* @param SimpleXMLElement $field The XML definition for the sequence.
*
* @return string
*
* @since 12.1
*/
protected function getChangeSequenceSQL($field)
{
/* For older database version that doesn't support these fields use default values */
if (version_compare($this->db->getVersion(), '9.1.0') < 0)
{
$field['Min_Value'] = '1';
$field['Max_Value'] = '9223372036854775807';
$field['Increment'] = '1';
$field['Cycle_option'] = 'NO';
$field['Start_Value'] = '1';
}
return 'ALTER SEQUENCE ' . (string) $field['Name'] .
' INCREMENT BY ' . (string) $field['Increment'] . ' MINVALUE ' . (string) $field['Min_Value'] .
' MAXVALUE ' . (string) $field['Max_Value'] . ' START ' . (string) $field['Start_Value'] .
' OWNED BY ' . $this->db->quoteName((string) $field['Schema'] . '.' . (string) $field['Table'] . '.' . (string) $field['Column']);
}
/**
* Get the syntax to alter a column.
*
* @param string $table The name of the database table to alter.
* @param SimpleXMLElement $field The XML definition for the field.
*
* @return string
*
* @since 12.1
*/
protected function getChangeColumnSQL($table, SimpleXMLElement $field)
{
return 'ALTER TABLE ' . $this->db->quoteName($table) . ' ALTER COLUMN ' . $this->db->quoteName((string) $field['Field']) . ' '
. $this->getAlterColumnSQL($table, $field);
}
/**
* Get the SQL syntax for a single column that would be included in a table create statement.
*
* @param string $table The name of the database table to alter.
* @param SimpleXMLElement $field The XML field definition.
*
* @return string
*
* @since 12.1
*/
protected function getAlterColumnSQL($table, $field)
{
// TODO Incorporate into parent class and use $this.
$blobs = array('text', 'smalltext', 'mediumtext', 'largetext');
$fName = (string) $field['Field'];
$fType = (string) $field['Type'];
$fNull = (string) $field['Null'];
$fDefault = (isset($field['Default']) && $field['Default'] != 'NULL' ) ?
preg_match('/^[0-9]$/', $field['Default']) ? $field['Default'] : $this->db->quote((string) $field['Default'])
: null;
$query = ' TYPE ' . $fType;
if ($fNull == 'NO')
{
if (in_array($fType, $blobs) || $fDefault === null)
{
$query .= ",\nALTER COLUMN " . $this->db->quoteName($fName) . ' SET NOT NULL' .
",\nALTER COLUMN " . $this->db->quoteName($fName) . ' DROP DEFAULT';
}
else
{
$query .= ",\nALTER COLUMN " . $this->db->quoteName($fName) . ' SET NOT NULL' .
",\nALTER COLUMN " . $this->db->quoteName($fName) . ' SET DEFAULT ' . $fDefault;
}
}
else
{
if ($fDefault !== null)
{
$query .= ",\nALTER COLUMN " . $this->db->quoteName($fName) . ' DROP NOT NULL' .
",\nALTER COLUMN " . $this->db->quoteName($fName) . ' SET DEFAULT ' . $fDefault;
}
}
/* sequence was created in other function, here is associated a default value but not yet owner */
if (strpos($fDefault, 'nextval') !== false)
{
$query .= ";\nALTER SEQUENCE " . $this->db->quoteName($table . '_' . $fName . '_seq') . ' OWNED BY ' . $this->db->quoteName($table . '.' . $fName);
}
return $query;
}
/**
* Get the SQL syntax for a single column that would be included in a table create statement.
*
* @param SimpleXMLElement $field The XML field definition.
*
* @return string
*
* @since 12.1
*/
protected function getColumnSQL(SimpleXMLElement $field)
{
// TODO Incorporate into parent class and use $this.
$blobs = array('text', 'smalltext', 'mediumtext', 'largetext');
$fName = (string) $field['Field'];
$fType = (string) $field['Type'];
$fNull = (string) $field['Null'];
$fDefault = (isset($field['Default']) && $field['Default'] != 'NULL' ) ?
preg_match('/^[0-9]$/', $field['Default']) ? $field['Default'] : $this->db->quote((string) $field['Default'])
: null;
/* nextval() as default value means that type field is serial */
if (strpos($fDefault, 'nextval') !== false)
{
$query = $this->db->quoteName($fName) . ' SERIAL';
}
else
{
$query = $this->db->quoteName($fName) . ' ' . $fType;
if ($fNull == 'NO')
{
if (in_array($fType, $blobs) || $fDefault === null)
{
$query .= ' NOT NULL';
}
else
{
$query .= ' NOT NULL DEFAULT ' . $fDefault;
}
}
else
{
if ($fDefault !== null)
{
$query .= ' DEFAULT ' . $fDefault;
}
}
}
return $query;
}
/**
* Get the SQL syntax to drop a column.
*
* @param string $table The table name.
* @param string $name The name of the field to drop.
*
* @return string
*
* @since 12.1
*/
protected function getDropColumnSQL($table, $name)
{
return 'ALTER TABLE ' . $this->db->quoteName($table) . ' DROP COLUMN ' . $this->db->quoteName($name);
}
/**
* Get the SQL syntax to drop an index.
*
* @param string $name The name of the key to drop.
*
* @return string
*
* @since 12.1
*/
protected function getDropIndexSQL($name)
{
return 'DROP INDEX ' . $this->db->quoteName($name);
}
/**
* Get the SQL syntax to drop a key.
*
* @param string $table The table name.
* @param string $name The constraint name.
*
* @return string
*
* @since 12.1
*/
protected function getDropPrimaryKeySQL($table, $name)
{
return 'ALTER TABLE ONLY ' . $this->db->quoteName($table) . ' DROP CONSTRAINT ' . $this->db->quoteName($name);
}
/**
* Get the details list of keys for a table.
*
* @param array $keys An array of objects that comprise the keys for the table.
*
* @return array The lookup array. array({key name} => array(object, ...))
*
* @since 12.1
* @throws Exception
*/
protected function getIdxLookup($keys)
{
// First pass, create a lookup of the keys.
$lookup = array();
foreach ($keys as $key)
{
if ($key instanceof SimpleXMLElement)
{
$kName = (string) $key['Index'];
}
else
{
$kName = $key->Index;
}
if (empty($lookup[$kName]))
{
$lookup[$kName] = array();
}
$lookup[$kName][] = $key;
}
return $lookup;
}
/**
* Get the details list of sequences for a table.
*
* @param array $sequences An array of objects that comprise the sequences for the table.
*
* @return array The lookup array. array({key name} => array(object, ...))
*
* @since 12.1
* @throws Exception
*/
protected function getSeqLookup($sequences)
{
// First pass, create a lookup of the keys.
$lookup = array();
foreach ($sequences as $seq)
{
if ($seq instanceof SimpleXMLElement)
{
$sName = (string) $seq['Name'];
}
else
{
$sName = $seq->Name;
}
if (empty($lookup[$sName]))
{
$lookup[$sName] = array();
}
$lookup[$sName][] = $seq;
}
return $lookup;
}
/**
* Get the real name of the table, converting the prefix wildcard string if present.
*
* @param string $table The name of the table.
*
* @return string The real name of the table.
*
* @since 12.1
*/
protected function getRealTableName($table)
{
// TODO Incorporate into parent class and use $this.
$prefix = $this->db->getPrefix();
// Replace the magic prefix if found.
$table = preg_replace('|^#__|', $prefix, $table);
return $table;
}
/**
* Merges the incoming structure definition with the existing structure.
*
* @return void
*
* @note Currently only supports XML format.
* @since 12.1
* @throws Exception on error.
* @todo If it's not XML convert to XML first.
*/
protected function mergeStructure()
{
$prefix = $this->db->getPrefix();
$tables = $this->db->getTableList();
if ($this->from instanceof SimpleXMLElement)
{
$xml = $this->from;
}
else
{
$xml = new SimpleXMLElement($this->from);
}
// Get all the table definitions.
$xmlTables = $xml->xpath('database/table_structure');
foreach ($xmlTables as $table)
{
// Convert the magic prefix into the real table name.
$tableName = (string) $table['name'];
$tableName = preg_replace('|^#__|', $prefix, $tableName);
if (in_array($tableName, $tables))
{
// The table already exists. Now check if there is any difference.
if ($queries = $this->getAlterTableSQL($xml->database->table_structure))
{
// Run the queries to upgrade the data structure.
foreach ($queries as $query)
{
$this->db->setQuery($query);
try
{
$this->db->execute();
}
catch (RuntimeException $e)
{
$this->addLog('Fail: ' . $this->db->getQuery());
throw $e;
}
$this->addLog('Pass: ' . $this->db->getQuery());
}
}
}
else
{
// This is a new table.
$query = $this->xmlToCreate($table);
$this->db->setQuery($query);
try
{
$this->db->execute();
}
catch (RuntimeException $e)
{
$this->addLog('Fail: ' . $this->db->getQuery());
throw $e;
}
$this->addLog('Pass: ' . $this->db->getQuery());
}
}
}
/**
* Sets the database connector to use for exporting structure and/or data from PostgreSQL.
*
* @param JDatabaseDriverPostgresql $db The database connector.
*
* @return JDatabaseImporterPostgresql Method supports chaining.
*
* @since 12.1
*/
public function setDbo(JDatabaseDriverPostgresql $db)
{
$this->db = $db;
return $this;
}
/**
* Sets an internal option to merge the structure based on the input data.
*
* @param boolean $setting True to export the structure, false to not.
*
* @return JDatabaseImporterPostgresql Method supports chaining.
*
* @since 12.1
*/
public function withStructure($setting = true)
{
$this->options->withStructure = (boolean) $setting;
return $this;
}
}

View File

@ -0,0 +1 @@
<!DOCTYPE html><title></title>

View File

@ -0,0 +1,205 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Database
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* Joomla Platform Database Driver Class
*
* @package Joomla.Platform
* @subpackage Database
* @since 12.1
*/
abstract class JDatabaseIterator implements Countable, Iterator
{
/**
* The database cursor.
*
* @var mixed
* @since 12.1
*/
protected $cursor;
/**
* The class of object to create.
*
* @var string
* @since 12.1
*/
protected $class;
/**
* The name of the column to use for the key of the database record.
*
* @var mixed
* @since 12.1
*/
private $_column;
/**
* The current database record.
*
* @var mixed
* @since 12.1
*/
private $_current;
/**
* A numeric or string key for the current database record.
*
* @var scalar
* @since 12.1
*/
private $_key;
/**
* The number of fetched records.
*
* @var integer
* @since 12.1
*/
private $_fetched = 0;
/**
* Database iterator constructor.
*
* @param mixed $cursor The database cursor.
* @param string $column An option column to use as the iterator key.
* @param string $class The class of object that is returned.
*
* @throws InvalidArgumentException
*/
public function __construct($cursor, $column = null, $class = 'stdClass')
{
if (!class_exists($class))
{
throw new InvalidArgumentException(sprintf('new %s(*%s*, cursor)', get_class($this), gettype($class)));
}
$this->cursor = $cursor;
$this->class = $class;
$this->_column = $column;
$this->_fetched = 0;
$this->next();
}
/**
* Database iterator destructor.
*
* @since 12.1
*/
public function __destruct()
{
if ($this->cursor)
{
$this->freeResult($this->cursor);
}
}
/**
* The current element in the iterator.
*
* @return object
*
* @see Iterator::current()
* @since 12.1
*/
public function current()
{
return $this->_current;
}
/**
* The key of the current element in the iterator.
*
* @return scalar
*
* @see Iterator::key()
* @since 12.1
*/
public function key()
{
return $this->_key;
}
/**
* Moves forward to the next result from the SQL query.
*
* @return void
*
* @see Iterator::next()
* @since 12.1
*/
public function next()
{
// Set the default key as being the number of fetched object
$this->_key = $this->_fetched;
// Try to get an object
$this->_current = $this->fetchObject();
// If an object has been found
if ($this->_current)
{
// Set the key as being the indexed column (if it exists)
if (isset($this->_current->{$this->_column}))
{
$this->_key = $this->_current->{$this->_column};
}
// Update the number of fetched object
$this->_fetched++;
}
}
/**
* Rewinds the iterator.
*
* This iterator cannot be rewound.
*
* @return void
*
* @see Iterator::rewind()
* @since 12.1
*/
public function rewind()
{
}
/**
* Checks if the current position of the iterator is valid.
*
* @return boolean
*
* @see Iterator::valid()
* @since 12.1
*/
public function valid()
{
return (boolean) $this->_current;
}
/**
* Method to fetch a row from the result set cursor as an object.
*
* @return mixed Either the next row from the result set or false if there are no more rows.
*
* @since 12.1
*/
abstract protected function fetchObject();
/**
* Method to free up the memory used for the result set.
*
* @return void
*
* @since 12.1
*/
abstract protected function freeResult();
}

View File

@ -0,0 +1,21 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Database
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* SQL azure database iterator.
*
* @package Joomla.Platform
* @subpackage Database
* @since 12.1
*/
class JDatabaseIteratorAzure extends JDatabaseIteratorSqlsrv
{
}

View File

@ -0,0 +1 @@
<!DOCTYPE html><title></title>

View File

@ -0,0 +1,58 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Database
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* MySQL database iterator.
*
* @package Joomla.Platform
* @subpackage Database
* @see http://dev.mysql.com/doc/
* @since 12.1
*/
class JDatabaseIteratorMysql extends JDatabaseIterator
{
/**
* Get the number of rows in the result set for the executed SQL given by the cursor.
*
* @return integer The number of rows in the result set.
*
* @since 12.1
* @see Countable::count()
*/
public function count()
{
return mysql_num_rows($this->cursor);
}
/**
* Method to fetch a row from the result set cursor as an object.
*
* @return mixed Either the next row from the result set or false if there are no more rows.
*
* @since 12.1
*/
protected function fetchObject()
{
return mysql_fetch_object($this->cursor, $this->class);
}
/**
* Method to free up the memory used for the result set.
*
* @return void
*
* @since 12.1
*/
protected function freeResult()
{
mysql_free_result($this->cursor);
}
}

View File

@ -0,0 +1,57 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Database
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* MySQLi database iterator.
*
* @package Joomla.Platform
* @subpackage Database
* @since 12.1
*/
class JDatabaseIteratorMysqli extends JDatabaseIterator
{
/**
* Get the number of rows in the result set for the executed SQL given by the cursor.
*
* @return integer The number of rows in the result set.
*
* @since 12.1
* @see Countable::count()
*/
public function count()
{
return mysqli_num_rows($this->cursor);
}
/**
* Method to fetch a row from the result set cursor as an object.
*
* @return mixed Either the next row from the result set or false if there are no more rows.
*
* @since 12.1
*/
protected function fetchObject()
{
return mysqli_fetch_object($this->cursor, $this->class);
}
/**
* Method to free up the memory used for the result set.
*
* @return void
*
* @since 12.1
*/
protected function freeResult()
{
mysqli_free_result($this->cursor);
}
}

View File

@ -0,0 +1,21 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Database
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* Oracle database iterator.
*
* @package Joomla.Platform
* @subpackage Database
* @since 12.1
*/
class JDatabaseIteratorOracle extends JDatabaseIteratorPdo
{
}

View File

@ -0,0 +1,74 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Database
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* PDO database iterator.
*
* @package Joomla.Platform
* @subpackage Database
* @since 12.1
*/
class JDatabaseIteratorPdo extends JDatabaseIterator
{
/**
* Get the number of rows in the result set for the executed SQL given by the cursor.
*
* @return integer The number of rows in the result set.
*
* @since 12.1
* @see Countable::count()
*/
public function count()
{
if (!empty($this->cursor) && $this->cursor instanceof PDOStatement)
{
return $this->cursor->rowCount();
}
else
{
return 0;
}
}
/**
* Method to fetch a row from the result set cursor as an object.
*
* @return mixed Either the next row from the result set or false if there are no more rows.
*
* @since 12.1
*/
protected function fetchObject()
{
if (!empty($this->cursor) && $this->cursor instanceof PDOStatement)
{
return $this->cursor->fetchObject($this->class);
}
else
{
return false;
}
}
/**
* Method to free up the memory used for the result set.
*
* @return void
*
* @since 12.1
*/
protected function freeResult()
{
if (!empty($this->cursor) && $this->cursor instanceof PDOStatement)
{
$this->cursor->closeCursor();
}
}
}

View File

@ -0,0 +1,21 @@
<?php
/**
* @package Joomla.Platform
* @subpackage Database
*
* @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
defined('JPATH_PLATFORM') or die;
/**
* SQLite database iterator.
*
* @package Joomla.Platform
* @subpackage Database
* @since 12.1
*/
class JDatabaseIteratorSqlite extends JDatabaseIteratorPdo
{
}

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