primo commit
This commit is contained in:
585
libraries/f0f/utils/array/array.php
Normal file
585
libraries/f0f/utils/array/array.php
Normal file
@ -0,0 +1,585 @@
|
||||
<?php
|
||||
/**
|
||||
* @package FrameworkOnFramework
|
||||
* @subpackage utils
|
||||
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved.
|
||||
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
||||
*/
|
||||
|
||||
defined('F0F_INCLUDED') or die;
|
||||
|
||||
/**
|
||||
* A utility class to handle array manipulation.
|
||||
*
|
||||
* Based on the JArrayHelper class as found in Joomla! 3.2.0
|
||||
*/
|
||||
abstract class F0FUtilsArray
|
||||
{
|
||||
/**
|
||||
* Option to perform case-sensitive sorts.
|
||||
*
|
||||
* @var mixed Boolean or array of booleans.
|
||||
*/
|
||||
protected static $sortCase;
|
||||
|
||||
/**
|
||||
* Option to set the sort direction.
|
||||
*
|
||||
* @var mixed Integer or array of integers.
|
||||
*/
|
||||
protected static $sortDirection;
|
||||
|
||||
/**
|
||||
* Option to set the object key to sort on.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected static $sortKey;
|
||||
|
||||
/**
|
||||
* Option to perform a language aware sort.
|
||||
*
|
||||
* @var mixed Boolean or array of booleans.
|
||||
*/
|
||||
protected static $sortLocale;
|
||||
|
||||
/**
|
||||
* Function to convert array to integer values
|
||||
*
|
||||
* @param array &$array The source array to convert
|
||||
* @param mixed $default A default value (int|array) to assign if $array is not an array
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function toInteger(&$array, $default = null)
|
||||
{
|
||||
if (is_array($array))
|
||||
{
|
||||
foreach ($array as $i => $v)
|
||||
{
|
||||
$array[$i] = (int) $v;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($default === null)
|
||||
{
|
||||
$array = array();
|
||||
}
|
||||
elseif (is_array($default))
|
||||
{
|
||||
self::toInteger($default, null);
|
||||
$array = $default;
|
||||
}
|
||||
else
|
||||
{
|
||||
$array = array((int) $default);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function to map an array to a stdClass object.
|
||||
*
|
||||
* @param array &$array The array to map.
|
||||
* @param string $class Name of the class to create
|
||||
*
|
||||
* @return object The object mapped from the given array
|
||||
*/
|
||||
public static function toObject(&$array, $class = 'stdClass')
|
||||
{
|
||||
$obj = null;
|
||||
|
||||
if (is_array($array))
|
||||
{
|
||||
$obj = new $class;
|
||||
|
||||
foreach ($array as $k => $v)
|
||||
{
|
||||
if (is_array($v))
|
||||
{
|
||||
$obj->$k = self::toObject($v, $class);
|
||||
}
|
||||
else
|
||||
{
|
||||
$obj->$k = $v;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function to map an array to a string.
|
||||
*
|
||||
* @param array $array The array to map.
|
||||
* @param string $inner_glue The glue (optional, defaults to '=') between the key and the value.
|
||||
* @param string $outer_glue The glue (optional, defaults to ' ') between array elements.
|
||||
* @param boolean $keepOuterKey True if final key should be kept.
|
||||
*
|
||||
* @return string The string mapped from the given array
|
||||
*/
|
||||
public static function toString($array = null, $inner_glue = '=', $outer_glue = ' ', $keepOuterKey = false)
|
||||
{
|
||||
$output = array();
|
||||
|
||||
if (is_array($array))
|
||||
{
|
||||
foreach ($array as $key => $item)
|
||||
{
|
||||
if (is_array($item))
|
||||
{
|
||||
if ($keepOuterKey)
|
||||
{
|
||||
$output[] = $key;
|
||||
}
|
||||
// This is value is an array, go and do it again!
|
||||
$output[] = self::toString($item, $inner_glue, $outer_glue, $keepOuterKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
$output[] = $key . $inner_glue . '"' . $item . '"';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return implode($outer_glue, $output);
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function to map an object to an array
|
||||
*
|
||||
* @param object $p_obj The source object
|
||||
* @param boolean $recurse True to recurse through multi-level objects
|
||||
* @param string $regex An optional regular expression to match on field names
|
||||
*
|
||||
* @return array The array mapped from the given object
|
||||
*/
|
||||
public static function fromObject($p_obj, $recurse = true, $regex = null)
|
||||
{
|
||||
if (is_object($p_obj))
|
||||
{
|
||||
return self::_fromObject($p_obj, $recurse, $regex);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function to map an object or array to an array
|
||||
*
|
||||
* @param mixed $item The source object or array
|
||||
* @param boolean $recurse True to recurse through multi-level objects
|
||||
* @param string $regex An optional regular expression to match on field names
|
||||
*
|
||||
* @return array The array mapped from the given object
|
||||
*/
|
||||
protected static function _fromObject($item, $recurse, $regex)
|
||||
{
|
||||
if (is_object($item))
|
||||
{
|
||||
$result = array();
|
||||
|
||||
foreach (get_object_vars($item) as $k => $v)
|
||||
{
|
||||
if (!$regex || preg_match($regex, $k))
|
||||
{
|
||||
if ($recurse)
|
||||
{
|
||||
$result[$k] = self::_fromObject($v, $recurse, $regex);
|
||||
}
|
||||
else
|
||||
{
|
||||
$result[$k] = $v;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
elseif (is_array($item))
|
||||
{
|
||||
$result = array();
|
||||
|
||||
foreach ($item as $k => $v)
|
||||
{
|
||||
$result[$k] = self::_fromObject($v, $recurse, $regex);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$result = $item;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts a column from an array of arrays or objects
|
||||
*
|
||||
* @param array &$array The source array
|
||||
* @param string $index The index of the column or name of object property
|
||||
*
|
||||
* @return array Column of values from the source array
|
||||
*/
|
||||
public static function getColumn(&$array, $index)
|
||||
{
|
||||
$result = array();
|
||||
|
||||
if (is_array($array))
|
||||
{
|
||||
foreach ($array as &$item)
|
||||
{
|
||||
if (is_array($item) && isset($item[$index]))
|
||||
{
|
||||
$result[] = $item[$index];
|
||||
}
|
||||
elseif (is_object($item) && isset($item->$index))
|
||||
{
|
||||
$result[] = $item->$index;
|
||||
}
|
||||
// Else ignore the entry
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function to return a value from a named array or a specified default
|
||||
*
|
||||
* @param array &$array A named array
|
||||
* @param string $name The key to search for
|
||||
* @param mixed $default The default value to give if no key found
|
||||
* @param string $type Return type for the variable (INT, FLOAT, STRING, WORD, BOOLEAN, ARRAY)
|
||||
*
|
||||
* @return mixed The value from the source array
|
||||
*/
|
||||
public static function getValue(&$array, $name, $default = null, $type = '')
|
||||
{
|
||||
$result = null;
|
||||
|
||||
if (isset($array[$name]))
|
||||
{
|
||||
$result = $array[$name];
|
||||
}
|
||||
|
||||
// Handle the default case
|
||||
if (is_null($result))
|
||||
{
|
||||
$result = $default;
|
||||
}
|
||||
|
||||
// Handle the type constraint
|
||||
switch (strtoupper($type))
|
||||
{
|
||||
case 'INT':
|
||||
case 'INTEGER':
|
||||
// Only use the first integer value
|
||||
@preg_match('/-?[0-9]+/', $result, $matches);
|
||||
$result = @(int) $matches[0];
|
||||
break;
|
||||
|
||||
case 'FLOAT':
|
||||
case 'DOUBLE':
|
||||
// Only use the first floating point value
|
||||
@preg_match('/-?[0-9]+(\.[0-9]+)?/', $result, $matches);
|
||||
$result = @(float) $matches[0];
|
||||
break;
|
||||
|
||||
case 'BOOL':
|
||||
case 'BOOLEAN':
|
||||
$result = (bool) $result;
|
||||
break;
|
||||
|
||||
case 'ARRAY':
|
||||
if (!is_array($result))
|
||||
{
|
||||
$result = array($result);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'STRING':
|
||||
$result = (string) $result;
|
||||
break;
|
||||
|
||||
case 'WORD':
|
||||
$result = (string) preg_replace('#\W#', '', $result);
|
||||
break;
|
||||
|
||||
case 'NONE':
|
||||
default:
|
||||
// No casting necessary
|
||||
break;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes an associative array of arrays and inverts the array keys to values using the array values as keys.
|
||||
*
|
||||
* Example:
|
||||
* $input = array(
|
||||
* 'New' => array('1000', '1500', '1750'),
|
||||
* 'Used' => array('3000', '4000', '5000', '6000')
|
||||
* );
|
||||
* $output = F0FUtilsArray::invert($input);
|
||||
*
|
||||
* Output would be equal to:
|
||||
* $output = array(
|
||||
* '1000' => 'New',
|
||||
* '1500' => 'New',
|
||||
* '1750' => 'New',
|
||||
* '3000' => 'Used',
|
||||
* '4000' => 'Used',
|
||||
* '5000' => 'Used',
|
||||
* '6000' => 'Used'
|
||||
* );
|
||||
*
|
||||
* @param array $array The source array.
|
||||
*
|
||||
* @return array The inverted array.
|
||||
*/
|
||||
public static function invert($array)
|
||||
{
|
||||
$return = array();
|
||||
|
||||
foreach ($array as $base => $values)
|
||||
{
|
||||
if (!is_array($values))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($values as $key)
|
||||
{
|
||||
// If the key isn't scalar then ignore it.
|
||||
if (is_scalar($key))
|
||||
{
|
||||
$return[$key] = $base;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to determine if an array is an associative array.
|
||||
*
|
||||
* @param array $array An array to test.
|
||||
*
|
||||
* @return boolean True if the array is an associative array.
|
||||
*/
|
||||
public static function isAssociative($array)
|
||||
{
|
||||
if (is_array($array))
|
||||
{
|
||||
foreach (array_keys($array) as $k => $v)
|
||||
{
|
||||
if ($k !== $v)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pivots an array to create a reverse lookup of an array of scalars, arrays or objects.
|
||||
*
|
||||
* @param array $source The source array.
|
||||
* @param string $key Where the elements of the source array are objects or arrays, the key to pivot on.
|
||||
*
|
||||
* @return array An array of arrays pivoted either on the value of the keys, or an individual key of an object or array.
|
||||
*/
|
||||
public static function pivot($source, $key = null)
|
||||
{
|
||||
$result = array();
|
||||
$counter = array();
|
||||
|
||||
foreach ($source as $index => $value)
|
||||
{
|
||||
// Determine the name of the pivot key, and its value.
|
||||
if (is_array($value))
|
||||
{
|
||||
// If the key does not exist, ignore it.
|
||||
if (!isset($value[$key]))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$resultKey = $value[$key];
|
||||
$resultValue = &$source[$index];
|
||||
}
|
||||
elseif (is_object($value))
|
||||
{
|
||||
// If the key does not exist, ignore it.
|
||||
if (!isset($value->$key))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$resultKey = $value->$key;
|
||||
$resultValue = &$source[$index];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Just a scalar value.
|
||||
$resultKey = $value;
|
||||
$resultValue = $index;
|
||||
}
|
||||
|
||||
// The counter tracks how many times a key has been used.
|
||||
if (empty($counter[$resultKey]))
|
||||
{
|
||||
// The first time around we just assign the value to the key.
|
||||
$result[$resultKey] = $resultValue;
|
||||
$counter[$resultKey] = 1;
|
||||
}
|
||||
elseif ($counter[$resultKey] == 1)
|
||||
{
|
||||
// If there is a second time, we convert the value into an array.
|
||||
$result[$resultKey] = array(
|
||||
$result[$resultKey],
|
||||
$resultValue,
|
||||
);
|
||||
$counter[$resultKey]++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// After the second time, no need to track any more. Just append to the existing array.
|
||||
$result[$resultKey][] = $resultValue;
|
||||
}
|
||||
}
|
||||
|
||||
unset($counter);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function to sort an array of objects on a given field
|
||||
*
|
||||
* @param array &$a An array of objects
|
||||
* @param mixed $k The key (string) or a array of key to sort on
|
||||
* @param mixed $direction Direction (integer) or an array of direction to sort in [1 = Ascending] [-1 = Descending]
|
||||
* @param mixed $caseSensitive Boolean or array of booleans to let sort occur case sensitive or insensitive
|
||||
* @param mixed $locale Boolean or array of booleans to let sort occur using the locale language or not
|
||||
*
|
||||
* @return array The sorted array of objects
|
||||
*/
|
||||
public static function sortObjects(&$a, $k, $direction = 1, $caseSensitive = true, $locale = false)
|
||||
{
|
||||
if (!is_array($locale) || !is_array($locale[0]))
|
||||
{
|
||||
$locale = array($locale);
|
||||
}
|
||||
|
||||
self::$sortCase = (array) $caseSensitive;
|
||||
self::$sortDirection = (array) $direction;
|
||||
self::$sortKey = (array) $k;
|
||||
self::$sortLocale = $locale;
|
||||
|
||||
usort($a, array(__CLASS__, '_sortObjects'));
|
||||
|
||||
self::$sortCase = null;
|
||||
self::$sortDirection = null;
|
||||
self::$sortKey = null;
|
||||
self::$sortLocale = null;
|
||||
|
||||
return $a;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback function for sorting an array of objects on a key
|
||||
*
|
||||
* @param array &$a An array of objects
|
||||
* @param array &$b An array of objects
|
||||
*
|
||||
* @return integer Comparison status
|
||||
*
|
||||
* @see F0FUtilsArray::sortObjects()
|
||||
*/
|
||||
protected static function _sortObjects(&$a, &$b)
|
||||
{
|
||||
$key = self::$sortKey;
|
||||
|
||||
for ($i = 0, $count = count($key); $i < $count; $i++)
|
||||
{
|
||||
if (isset(self::$sortDirection[$i]))
|
||||
{
|
||||
$direction = self::$sortDirection[$i];
|
||||
}
|
||||
|
||||
if (isset(self::$sortCase[$i]))
|
||||
{
|
||||
$caseSensitive = self::$sortCase[$i];
|
||||
}
|
||||
|
||||
if (isset(self::$sortLocale[$i]))
|
||||
{
|
||||
$locale = self::$sortLocale[$i];
|
||||
}
|
||||
|
||||
$va = $a->{$key[$i]};
|
||||
$vb = $b->{$key[$i]};
|
||||
|
||||
if ((is_bool($va) || is_numeric($va)) && (is_bool($vb) || is_numeric($vb)))
|
||||
{
|
||||
$cmp = $va - $vb;
|
||||
}
|
||||
elseif ($caseSensitive)
|
||||
{
|
||||
$cmp = JString::strcmp($va, $vb, $locale);
|
||||
}
|
||||
else
|
||||
{
|
||||
$cmp = JString::strcasecmp($va, $vb, $locale);
|
||||
}
|
||||
|
||||
if ($cmp > 0)
|
||||
{
|
||||
|
||||
return $direction;
|
||||
}
|
||||
|
||||
if ($cmp < 0)
|
||||
{
|
||||
return -$direction;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Multidimensional array safe unique test
|
||||
*
|
||||
* @param array $myArray The array to make unique.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @see http://php.net/manual/en/function.array-unique.php
|
||||
*/
|
||||
public static function arrayUnique($myArray)
|
||||
{
|
||||
if (!is_array($myArray))
|
||||
{
|
||||
return $myArray;
|
||||
}
|
||||
|
||||
foreach ($myArray as &$myvalue)
|
||||
{
|
||||
$myvalue = serialize($myvalue);
|
||||
}
|
||||
|
||||
$myArray = array_unique($myArray);
|
||||
|
||||
foreach ($myArray as &$myvalue)
|
||||
{
|
||||
$myvalue = unserialize($myvalue);
|
||||
}
|
||||
|
||||
return $myArray;
|
||||
}
|
||||
}
|
||||
85
libraries/f0f/utils/cache/cleaner.php
vendored
Normal file
85
libraries/f0f/utils/cache/cleaner.php
vendored
Normal file
@ -0,0 +1,85 @@
|
||||
<?php
|
||||
/**
|
||||
* @package FrameworkOnFramework
|
||||
* @subpackage utils
|
||||
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved.
|
||||
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
||||
*/
|
||||
|
||||
defined('F0F_INCLUDED') or die;
|
||||
|
||||
/**
|
||||
* A utility class to help you quickly clean the Joomla! cache
|
||||
*/
|
||||
class F0FUtilsCacheCleaner
|
||||
{
|
||||
/**
|
||||
* Clears the com_modules and com_plugins cache. You need to call this whenever you alter the publish state or
|
||||
* parameters of a module or plugin from your code.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function clearPluginsAndModulesCache()
|
||||
{
|
||||
self::clearPluginsCache();
|
||||
self::clearModulesCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the com_plugins cache. You need to call this whenever you alter the publish state or parameters of a
|
||||
* plugin from your code.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function clearPluginsCache()
|
||||
{
|
||||
self::clearCacheGroups(array('com_plugins'), array(0,1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the com_modules cache. You need to call this whenever you alter the publish state or parameters of a
|
||||
* module from your code.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function clearModulesCache()
|
||||
{
|
||||
self::clearCacheGroups(array('com_modules'), array(0,1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the specified cache groups.
|
||||
*
|
||||
* @param array $clearGroups Which cache groups to clear. Usually this is com_yourcomponent to clear your
|
||||
* component's cache.
|
||||
* @param array $cacheClients Which cache clients to clear. 0 is the back-end, 1 is the front-end. If you do not
|
||||
* specify anything, both cache clients will be cleared.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function clearCacheGroups(array $clearGroups, array $cacheClients = array(0, 1))
|
||||
{
|
||||
$conf = JFactory::getConfig();
|
||||
|
||||
foreach ($clearGroups as $group)
|
||||
{
|
||||
foreach ($cacheClients as $client_id)
|
||||
{
|
||||
try
|
||||
{
|
||||
$options = array(
|
||||
'defaultgroup' => $group,
|
||||
'cachebase' => ($client_id) ? JPATH_ADMINISTRATOR . '/cache' : $conf->get('cache_path', JPATH_SITE . '/cache')
|
||||
);
|
||||
|
||||
$cache = JCache::getInstance('callback', $options);
|
||||
$cache->clean();
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
// suck it up
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
89
libraries/f0f/utils/config/helper.php
Normal file
89
libraries/f0f/utils/config/helper.php
Normal file
@ -0,0 +1,89 @@
|
||||
<?php
|
||||
/**
|
||||
* @package FrameworkOnFramework
|
||||
* @subpackage utils
|
||||
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved.
|
||||
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
||||
*/
|
||||
|
||||
defined('F0F_INCLUDED') or die;
|
||||
|
||||
/**
|
||||
* A utility class to help you fetch component parameters without going through JComponentHelper
|
||||
*/
|
||||
class F0FUtilsConfigHelper
|
||||
{
|
||||
/**
|
||||
* Caches the component parameters without going through JComponentHelper. This is necessary since JComponentHelper
|
||||
* cannot be reset or updated once you update parameters in the database.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $componentParams = array();
|
||||
|
||||
/**
|
||||
* Loads the component's configuration parameters so they can be accessed by getComponentConfigurationValue
|
||||
*
|
||||
* @param string $component The component for loading the parameters
|
||||
* @param bool $force Should I force-reload the configuration information?
|
||||
*/
|
||||
public final static function loadComponentConfig($component, $force = false)
|
||||
{
|
||||
if (isset(self::$componentParams[$component]) && !is_null(self::$componentParams[$component]) && !$force)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$db = F0FPlatform::getInstance()->getDbo();
|
||||
|
||||
$sql = $db->getQuery(true)
|
||||
->select($db->qn('params'))
|
||||
->from($db->qn('#__extensions'))
|
||||
->where($db->qn('type') . ' = ' . $db->q('component'))
|
||||
->where($db->qn('element') . " = " . $db->q($component));
|
||||
$db->setQuery($sql);
|
||||
$config_ini = $db->loadResult();
|
||||
|
||||
// OK, Joomla! 1.6 stores values JSON-encoded so, what do I do? Right!
|
||||
$config_ini = trim($config_ini);
|
||||
|
||||
if ((substr($config_ini, 0, 1) == '{') && substr($config_ini, -1) == '}')
|
||||
{
|
||||
$config_ini = json_decode($config_ini, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
$config_ini = F0FUtilsIniParser::parse_ini_file($config_ini, false, true);
|
||||
}
|
||||
|
||||
if (is_null($config_ini) || empty($config_ini))
|
||||
{
|
||||
$config_ini = array();
|
||||
}
|
||||
|
||||
self::$componentParams[$component] = $config_ini;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the value of a component configuration parameter without going through JComponentHelper
|
||||
*
|
||||
* @param string $component The component for loading the parameter value
|
||||
* @param string $key The key to retrieve
|
||||
* @param mixed $default The default value to use in case the key is missing
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public final static function getComponentConfigurationValue($component, $key, $default = null)
|
||||
{
|
||||
self::loadComponentConfig($component, false);
|
||||
|
||||
if (array_key_exists($key, self::$componentParams[$component]))
|
||||
{
|
||||
return self::$componentParams[$component][$key];
|
||||
}
|
||||
else
|
||||
{
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
}
|
||||
272
libraries/f0f/utils/filescheck/filescheck.php
Normal file
272
libraries/f0f/utils/filescheck/filescheck.php
Normal file
@ -0,0 +1,272 @@
|
||||
<?php
|
||||
/**
|
||||
* @package FrameworkOnFramework
|
||||
* @subpackage utils
|
||||
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved.
|
||||
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
||||
*/
|
||||
|
||||
defined('F0F_INCLUDED') or die;
|
||||
|
||||
/**
|
||||
* A utility class to check that your extension's files are not missing and have not been tampered with.
|
||||
*
|
||||
* You need a file called fileslist.php in your component's administrator root directory with the following contents:
|
||||
*
|
||||
* $phpFileChecker = array(
|
||||
* 'version' => 'revCEE2DAB',
|
||||
* 'date' => '2014-10-16',
|
||||
* 'directories' => array(
|
||||
* 'administrator/components/com_foobar',
|
||||
* ....
|
||||
* ),
|
||||
* 'files' => array(
|
||||
* 'administrator/components/com_foobar/access.xml' => array('705', '09aa0351a316bf011ecc8c1145134761', 'b95f00c7b49a07a60570dc674f2497c45c4e7152'),
|
||||
* ....
|
||||
* )
|
||||
* );
|
||||
*
|
||||
* All directory and file paths are relative to the site's root
|
||||
*
|
||||
* The directories array is a list of
|
||||
*/
|
||||
class F0FUtilsFilescheck
|
||||
{
|
||||
/** @var string The name of the component */
|
||||
protected $option = '';
|
||||
|
||||
/** @var string Current component version */
|
||||
protected $version = null;
|
||||
|
||||
/** @var string Current component release date */
|
||||
protected $date = null;
|
||||
|
||||
/** @var array List of files to check as filepath => (filesize, md5, sha1) */
|
||||
protected $fileList = array();
|
||||
|
||||
/** @var array List of directories to check that exist */
|
||||
protected $dirList = array();
|
||||
|
||||
/** @var bool Is the reported component version different than the version of the #__extensions table? */
|
||||
protected $wrongComponentVersion = false;
|
||||
|
||||
/** @var bool Is the fileslist.php reporting a version different than the reported component version? */
|
||||
protected $wrongFilesVersion = false;
|
||||
|
||||
/**
|
||||
* Create and initialise the object
|
||||
*
|
||||
* @param string $option Component name, e.g. com_foobar
|
||||
* @param string $version The current component version, as reported by the component
|
||||
* @param string $date The current component release date, as reported by the component
|
||||
*/
|
||||
public function __construct($option, $version, $date)
|
||||
{
|
||||
// Initialise from parameters
|
||||
$this->option = $option;
|
||||
$this->version = $version;
|
||||
$this->date = $date;
|
||||
|
||||
// Retrieve the date and version from the #__extensions table
|
||||
$db = F0FPlatform::getInstance()->getDbo();
|
||||
$query = $db->getQuery(true)->select('*')->from($db->qn('#__extensions'))
|
||||
->where($db->qn('element') . ' = ' . $db->q($this->option))
|
||||
->where($db->qn('type') . ' = ' . $db->q('component'));
|
||||
$extension = $db->setQuery($query)->loadObject();
|
||||
|
||||
// Check the version and date against those from #__extensions. I hate heavily nested IFs as much as the next
|
||||
// guy, but what can you do...
|
||||
if (!is_null($extension))
|
||||
{
|
||||
$manifestCache = $extension->manifest_cache;
|
||||
|
||||
if (!empty($manifestCache))
|
||||
{
|
||||
$manifestCache = json_decode($manifestCache, true);
|
||||
|
||||
if (is_array($manifestCache) && isset($manifestCache['creationDate']) && isset($manifestCache['version']))
|
||||
{
|
||||
// Make sure the fileslist.php version and date match the component's version
|
||||
if ($this->version != $manifestCache['version'])
|
||||
{
|
||||
$this->wrongComponentVersion = true;
|
||||
}
|
||||
|
||||
if ($this->date != $manifestCache['creationDate'])
|
||||
{
|
||||
$this->wrongComponentVersion = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Try to load the fileslist.php file from the component's back-end root
|
||||
$filePath = JPATH_ADMINISTRATOR . '/components/' . $this->option . '/fileslist.php';
|
||||
|
||||
if (!file_exists($filePath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
include $filePath;
|
||||
|
||||
// Make sure the fileslist.php version and date match the component's version
|
||||
if ($this->version != $phpFileChecker['version'])
|
||||
{
|
||||
$this->wrongFilesVersion = true;
|
||||
}
|
||||
|
||||
if ($this->date != $phpFileChecker['date'])
|
||||
{
|
||||
$this->wrongFilesVersion = true;
|
||||
}
|
||||
|
||||
// Initialise the files and directories lists
|
||||
$this->fileList = $phpFileChecker['files'];
|
||||
$this->dirList = $phpFileChecker['directories'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the reported component version different than the version of the #__extensions table?
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isWrongComponentVersion()
|
||||
{
|
||||
return $this->wrongComponentVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the fileslist.php reporting a version different than the reported component version?
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isWrongFilesVersion()
|
||||
{
|
||||
return $this->wrongFilesVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a fast check of file and folders. If even one of the files/folders doesn't exist, or even one file has
|
||||
* the wrong file size it will return false.
|
||||
*
|
||||
* @return bool False when there are mismatched files and directories
|
||||
*/
|
||||
public function fastCheck()
|
||||
{
|
||||
// Check that all directories exist
|
||||
foreach ($this->dirList as $directory)
|
||||
{
|
||||
$directory = JPATH_ROOT . '/' . $directory;
|
||||
|
||||
if (!@is_dir($directory))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check that all files exist and have the right size
|
||||
foreach ($this->fileList as $filePath => $fileData)
|
||||
{
|
||||
$filePath = JPATH_ROOT . '/' . $filePath;
|
||||
|
||||
if (!@file_exists($filePath))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$fileSize = @filesize($filePath);
|
||||
|
||||
if ($fileSize != $fileData[0])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a slow, thorough check of all files and folders (including MD5/SHA1 sum checks)
|
||||
*
|
||||
* @param int $idx The index from where to start
|
||||
*
|
||||
* @return array Progress report
|
||||
*/
|
||||
public function slowCheck($idx = 0)
|
||||
{
|
||||
$ret = array(
|
||||
'done' => false,
|
||||
'files' => array(),
|
||||
'folders' => array(),
|
||||
'idx' => $idx
|
||||
);
|
||||
|
||||
$totalFiles = count($this->fileList);
|
||||
$totalFolders = count($this->dirList);
|
||||
$fileKeys = array_keys($this->fileList);
|
||||
|
||||
$timer = new F0FUtilsTimer(3.0, 75.0);
|
||||
|
||||
while ($timer->getTimeLeft() && (($idx < $totalFiles) || ($idx < $totalFolders)))
|
||||
{
|
||||
if ($idx < $totalFolders)
|
||||
{
|
||||
$directory = JPATH_ROOT . '/' . $this->dirList[$idx];
|
||||
|
||||
if (!@is_dir($directory))
|
||||
{
|
||||
$ret['folders'][] = $directory;
|
||||
}
|
||||
}
|
||||
|
||||
if ($idx < $totalFiles)
|
||||
{
|
||||
$fileKey = $fileKeys[$idx];
|
||||
$filePath = JPATH_ROOT . '/' . $fileKey;
|
||||
$fileData = $this->fileList[$fileKey];
|
||||
|
||||
if (!@file_exists($filePath))
|
||||
{
|
||||
$ret['files'][] = $fileKey . ' (missing)';
|
||||
}
|
||||
elseif (@filesize($filePath) != $fileData[0])
|
||||
{
|
||||
$ret['files'][] = $fileKey . ' (size ' . @filesize($filePath) . ' ≠ ' . $fileData[0] . ')';
|
||||
}
|
||||
else
|
||||
{
|
||||
if (function_exists('sha1_file'))
|
||||
{
|
||||
$fileSha1 = @sha1_file($filePath);
|
||||
|
||||
if ($fileSha1 != $fileData[2])
|
||||
{
|
||||
$ret['files'][] = $fileKey . ' (SHA1 ' . $fileSha1 . ' ≠ ' . $fileData[2] . ')';
|
||||
}
|
||||
}
|
||||
elseif (function_exists('md5_file'))
|
||||
{
|
||||
$fileMd5 = @md5_file($filePath);
|
||||
|
||||
if ($fileMd5 != $fileData[1])
|
||||
{
|
||||
$ret['files'][] = $fileKey . ' (MD5 ' . $fileMd5 . ' ≠ ' . $fileData[1] . ')';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$idx++;
|
||||
}
|
||||
|
||||
if (($idx >= $totalFiles) && ($idx >= $totalFolders))
|
||||
{
|
||||
$ret['done'] = true;
|
||||
}
|
||||
|
||||
$ret['idx'] = $idx;
|
||||
|
||||
return $ret;
|
||||
}
|
||||
}
|
||||
199
libraries/f0f/utils/ini/parser.php
Normal file
199
libraries/f0f/utils/ini/parser.php
Normal file
@ -0,0 +1,199 @@
|
||||
<?php
|
||||
/**
|
||||
* @package FrameworkOnFramework
|
||||
* @subpackage utils
|
||||
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved.
|
||||
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
||||
*/
|
||||
|
||||
defined('F0F_INCLUDED') or die;
|
||||
|
||||
/**
|
||||
* A utility class to parse INI files. This monstrosity is only required because some impossibly misguided individuals
|
||||
* who misrepresent themselves as hosts have disabled PHP's parse_ini_file() function for "security reasons". Apparently
|
||||
* their blatant ignorance doesn't allow them to discern between the innocuous parse_ini_file and the _potentially_
|
||||
* dangerous ini_set functions, leading them to disable the former and let the latter enabled. In other words, THIS
|
||||
* CLASS IS HERE TO FIX STUPID.
|
||||
*/
|
||||
class F0FUtilsIniParser
|
||||
{
|
||||
/**
|
||||
* Parse an INI file and return an associative array.
|
||||
*
|
||||
* @param string $file The file to process
|
||||
* @param bool $process_sections True to also process INI sections
|
||||
*
|
||||
* @return array An associative array of sections, keys and values
|
||||
*/
|
||||
public static function parse_ini_file($file, $process_sections, $rawdata = false)
|
||||
{
|
||||
$isMoronHostFile = !function_exists('parse_ini_file');
|
||||
$isMoronHostString = !function_exists('parse_ini_string');
|
||||
|
||||
if ($rawdata)
|
||||
{
|
||||
if ($isMoronHostString)
|
||||
{
|
||||
return self::parse_ini_file_php($file, $process_sections, $rawdata);
|
||||
}
|
||||
else
|
||||
{
|
||||
return parse_ini_string($file, $process_sections);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($isMoronHostFile)
|
||||
{
|
||||
return self::parse_ini_file_php($file, $process_sections);
|
||||
}
|
||||
else
|
||||
{
|
||||
return parse_ini_file($file, $process_sections);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A PHP based INI file parser.
|
||||
*
|
||||
* Thanks to asohn ~at~ aircanopy ~dot~ net for posting this handy function on
|
||||
* the parse_ini_file page on http://gr.php.net/parse_ini_file
|
||||
*
|
||||
* @param string $file Filename to process
|
||||
* @param bool $process_sections True to also process INI sections
|
||||
* @param bool $rawdata If true, the $file contains raw INI data, not a filename
|
||||
*
|
||||
* @return array An associative array of sections, keys and values
|
||||
*/
|
||||
static function parse_ini_file_php($file, $process_sections = false, $rawdata = false)
|
||||
{
|
||||
$process_sections = ($process_sections !== true) ? false : true;
|
||||
|
||||
if (!$rawdata)
|
||||
{
|
||||
$ini = file($file);
|
||||
}
|
||||
else
|
||||
{
|
||||
$file = str_replace("\r", "", $file);
|
||||
$ini = explode("\n", $file);
|
||||
}
|
||||
|
||||
if (count($ini) == 0)
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
$sections = array();
|
||||
$values = array();
|
||||
$result = array();
|
||||
$globals = array();
|
||||
$i = 0;
|
||||
foreach ($ini as $line)
|
||||
{
|
||||
$line = trim($line);
|
||||
$line = str_replace("\t", " ", $line);
|
||||
|
||||
// Comments
|
||||
if (!preg_match('/^[a-zA-Z0-9[]/', $line))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Sections
|
||||
if ($line{0} == '[')
|
||||
{
|
||||
$tmp = explode(']', $line);
|
||||
$sections[] = trim(substr($tmp[0], 1));
|
||||
$i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Key-value pair
|
||||
$lineParts = explode('=', $line, 2);
|
||||
if (count($lineParts) != 2)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
$key = trim($lineParts[0]);
|
||||
$value = trim($lineParts[1]);
|
||||
unset($lineParts);
|
||||
|
||||
if (strstr($value, ";"))
|
||||
{
|
||||
$tmp = explode(';', $value);
|
||||
if (count($tmp) == 2)
|
||||
{
|
||||
if ((($value{0} != '"') && ($value{0} != "'")) ||
|
||||
preg_match('/^".*"\s*;/', $value) || preg_match('/^".*;[^"]*$/', $value) ||
|
||||
preg_match("/^'.*'\s*;/", $value) || preg_match("/^'.*;[^']*$/", $value)
|
||||
)
|
||||
{
|
||||
$value = $tmp[0];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($value{0} == '"')
|
||||
{
|
||||
$value = preg_replace('/^"(.*)".*/', '$1', $value);
|
||||
}
|
||||
elseif ($value{0} == "'")
|
||||
{
|
||||
$value = preg_replace("/^'(.*)'.*/", '$1', $value);
|
||||
}
|
||||
else
|
||||
{
|
||||
$value = $tmp[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
$value = trim($value);
|
||||
$value = trim($value, "'\"");
|
||||
|
||||
if ($i == 0)
|
||||
{
|
||||
if (substr($line, -1, 2) == '[]')
|
||||
{
|
||||
$globals[$key][] = $value;
|
||||
}
|
||||
else
|
||||
{
|
||||
$globals[$key] = $value;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (substr($line, -1, 2) == '[]')
|
||||
{
|
||||
$values[$i - 1][$key][] = $value;
|
||||
}
|
||||
else
|
||||
{
|
||||
$values[$i - 1][$key] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for ($j = 0; $j < $i; $j++)
|
||||
{
|
||||
if ($process_sections === true)
|
||||
{
|
||||
if (isset($sections[$j]) && isset($values[$j]))
|
||||
{
|
||||
$result[$sections[$j]] = $values[$j];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isset($values[$j]))
|
||||
{
|
||||
$result[] = $values[$j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $result + $globals;
|
||||
}
|
||||
}
|
||||
2397
libraries/f0f/utils/installscript/installscript.php
Normal file
2397
libraries/f0f/utils/installscript/installscript.php
Normal file
File diff suppressed because it is too large
Load Diff
523
libraries/f0f/utils/ip/ip.php
Normal file
523
libraries/f0f/utils/ip/ip.php
Normal file
@ -0,0 +1,523 @@
|
||||
<?php
|
||||
/**
|
||||
* @package FrameworkOnFramework
|
||||
* @subpackage utils
|
||||
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved.
|
||||
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
||||
*/
|
||||
|
||||
defined('F0F_INCLUDED') or die;
|
||||
|
||||
/**
|
||||
* IP address utilities
|
||||
*/
|
||||
abstract class F0FUtilsIp
|
||||
{
|
||||
/** @var string The IP address of the current visitor */
|
||||
protected static $ip = null;
|
||||
|
||||
/**
|
||||
* Should I allow IP overrides through X-Forwarded-For or Client-Ip HTTP headers?
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected static $allowIpOverrides = true;
|
||||
|
||||
/**
|
||||
* Get the current visitor's IP address
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getIp()
|
||||
{
|
||||
if (is_null(static::$ip))
|
||||
{
|
||||
$ip = self::detectAndCleanIP();
|
||||
|
||||
if (!empty($ip) && ($ip != '0.0.0.0') && function_exists('inet_pton') && function_exists('inet_ntop'))
|
||||
{
|
||||
$myIP = @inet_pton($ip);
|
||||
|
||||
if ($myIP !== false)
|
||||
{
|
||||
$ip = inet_ntop($myIP);
|
||||
}
|
||||
}
|
||||
|
||||
static::setIp($ip);
|
||||
}
|
||||
|
||||
return static::$ip;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the IP address of the current visitor
|
||||
*
|
||||
* @param string $ip
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function setIp($ip)
|
||||
{
|
||||
static::$ip = $ip;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is it an IPv6 IP address?
|
||||
*
|
||||
* @param string $ip An IPv4 or IPv6 address
|
||||
*
|
||||
* @return boolean True if it's IPv6
|
||||
*/
|
||||
public static function isIPv6($ip)
|
||||
{
|
||||
if (strstr($ip, ':'))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an IP is contained in a list of IPs or IP expressions
|
||||
*
|
||||
* @param string $ip The IPv4/IPv6 address to check
|
||||
* @param array|string $ipTable An IP expression (or a comma-separated or array list of IP expressions) to check against
|
||||
*
|
||||
* @return null|boolean True if it's in the list
|
||||
*/
|
||||
public static function IPinList($ip, $ipTable = '')
|
||||
{
|
||||
// No point proceeding with an empty IP list
|
||||
if (empty($ipTable))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the IP list is not an array, convert it to an array
|
||||
if (!is_array($ipTable))
|
||||
{
|
||||
if (strpos($ipTable, ',') !== false)
|
||||
{
|
||||
$ipTable = explode(',', $ipTable);
|
||||
$ipTable = array_map(function($x) { return trim($x); }, $ipTable);
|
||||
}
|
||||
else
|
||||
{
|
||||
$ipTable = trim($ipTable);
|
||||
$ipTable = array($ipTable);
|
||||
}
|
||||
}
|
||||
|
||||
// If no IP address is found, return false
|
||||
if ($ip == '0.0.0.0')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// If no IP is given, return false
|
||||
if (empty($ip))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Sanity check
|
||||
if (!function_exists('inet_pton'))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the IP's in_adds representation
|
||||
$myIP = @inet_pton($ip);
|
||||
|
||||
// If the IP is in an unrecognisable format, quite
|
||||
if ($myIP === false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$ipv6 = self::isIPv6($ip);
|
||||
|
||||
foreach ($ipTable as $ipExpression)
|
||||
{
|
||||
$ipExpression = trim($ipExpression);
|
||||
|
||||
// Inclusive IP range, i.e. 123.123.123.123-124.125.126.127
|
||||
if (strstr($ipExpression, '-'))
|
||||
{
|
||||
list($from, $to) = explode('-', $ipExpression, 2);
|
||||
|
||||
if ($ipv6 && (!self::isIPv6($from) || !self::isIPv6($to)))
|
||||
{
|
||||
// Do not apply IPv4 filtering on an IPv6 address
|
||||
continue;
|
||||
}
|
||||
elseif (!$ipv6 && (self::isIPv6($from) || self::isIPv6($to)))
|
||||
{
|
||||
// Do not apply IPv6 filtering on an IPv4 address
|
||||
continue;
|
||||
}
|
||||
|
||||
$from = @inet_pton(trim($from));
|
||||
$to = @inet_pton(trim($to));
|
||||
|
||||
// Sanity check
|
||||
if (($from === false) || ($to === false))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Swap from/to if they're in the wrong order
|
||||
if ($from > $to)
|
||||
{
|
||||
list($from, $to) = array($to, $from);
|
||||
}
|
||||
|
||||
if (($myIP >= $from) && ($myIP <= $to))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// Netmask or CIDR provided
|
||||
elseif (strstr($ipExpression, '/'))
|
||||
{
|
||||
$binaryip = self::inet_to_bits($myIP);
|
||||
|
||||
list($net, $maskbits) = explode('/', $ipExpression, 2);
|
||||
if ($ipv6 && !self::isIPv6($net))
|
||||
{
|
||||
// Do not apply IPv4 filtering on an IPv6 address
|
||||
continue;
|
||||
}
|
||||
elseif (!$ipv6 && self::isIPv6($net))
|
||||
{
|
||||
// Do not apply IPv6 filtering on an IPv4 address
|
||||
continue;
|
||||
}
|
||||
elseif ($ipv6 && strstr($maskbits, ':'))
|
||||
{
|
||||
// Perform an IPv6 CIDR check
|
||||
if (self::checkIPv6CIDR($myIP, $ipExpression))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// If we didn't match it proceed to the next expression
|
||||
continue;
|
||||
}
|
||||
elseif (!$ipv6 && strstr($maskbits, '.'))
|
||||
{
|
||||
// Convert IPv4 netmask to CIDR
|
||||
$long = ip2long($maskbits);
|
||||
$base = ip2long('255.255.255.255');
|
||||
$maskbits = 32 - log(($long ^ $base) + 1, 2);
|
||||
}
|
||||
|
||||
// Convert network IP to in_addr representation
|
||||
$net = @inet_pton($net);
|
||||
|
||||
// Sanity check
|
||||
if ($net === false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get the network's binary representation
|
||||
$binarynet = self::inet_to_bits($net);
|
||||
$expectedNumberOfBits = $ipv6 ? 128 : 24;
|
||||
$binarynet = str_pad($binarynet, $expectedNumberOfBits, '0', STR_PAD_RIGHT);
|
||||
|
||||
// Check the corresponding bits of the IP and the network
|
||||
$ip_net_bits = substr($binaryip, 0, $maskbits);
|
||||
$net_bits = substr($binarynet, 0, $maskbits);
|
||||
|
||||
if ($ip_net_bits == $net_bits)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// IPv6: Only single IPs are supported
|
||||
if ($ipv6)
|
||||
{
|
||||
$ipExpression = trim($ipExpression);
|
||||
|
||||
if (!self::isIPv6($ipExpression))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$ipCheck = @inet_pton($ipExpression);
|
||||
if ($ipCheck === false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($ipCheck == $myIP)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Standard IPv4 address, i.e. 123.123.123.123 or partial IP address, i.e. 123.[123.][123.][123]
|
||||
$dots = 0;
|
||||
if (substr($ipExpression, -1) == '.')
|
||||
{
|
||||
// Partial IP address. Convert to CIDR and re-match
|
||||
foreach (count_chars($ipExpression, 1) as $i => $val)
|
||||
{
|
||||
if ($i == 46)
|
||||
{
|
||||
$dots = $val;
|
||||
}
|
||||
}
|
||||
switch ($dots)
|
||||
{
|
||||
case 1:
|
||||
$netmask = '255.0.0.0';
|
||||
$ipExpression .= '0.0.0';
|
||||
break;
|
||||
|
||||
case 2:
|
||||
$netmask = '255.255.0.0';
|
||||
$ipExpression .= '0.0';
|
||||
break;
|
||||
|
||||
case 3:
|
||||
$netmask = '255.255.255.0';
|
||||
$ipExpression .= '0';
|
||||
break;
|
||||
|
||||
default:
|
||||
$dots = 0;
|
||||
}
|
||||
|
||||
if ($dots)
|
||||
{
|
||||
$binaryip = self::inet_to_bits($myIP);
|
||||
|
||||
// Convert netmask to CIDR
|
||||
$long = ip2long($netmask);
|
||||
$base = ip2long('255.255.255.255');
|
||||
$maskbits = 32 - log(($long ^ $base) + 1, 2);
|
||||
|
||||
$net = @inet_pton($ipExpression);
|
||||
|
||||
// Sanity check
|
||||
if ($net === false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get the network's binary representation
|
||||
$binarynet = self::inet_to_bits($net);
|
||||
$expectedNumberOfBits = $ipv6 ? 128 : 24;
|
||||
$binarynet = str_pad($binarynet, $expectedNumberOfBits, '0', STR_PAD_RIGHT);
|
||||
|
||||
// Check the corresponding bits of the IP and the network
|
||||
$ip_net_bits = substr($binaryip, 0, $maskbits);
|
||||
$net_bits = substr($binarynet, 0, $maskbits);
|
||||
|
||||
if ($ip_net_bits == $net_bits)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!$dots)
|
||||
{
|
||||
$ip = @inet_pton(trim($ipExpression));
|
||||
if ($ip == $myIP)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Works around the REMOTE_ADDR not containing the user's IP
|
||||
*/
|
||||
public static function workaroundIPIssues()
|
||||
{
|
||||
$ip = self::getIp();
|
||||
|
||||
if ($_SERVER['REMOTE_ADDR'] == $ip)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (array_key_exists('REMOTE_ADDR', $_SERVER))
|
||||
{
|
||||
$_SERVER['FOF_REMOTE_ADDR'] = $_SERVER['REMOTE_ADDR'];
|
||||
}
|
||||
elseif (function_exists('getenv'))
|
||||
{
|
||||
if (getenv('REMOTE_ADDR'))
|
||||
{
|
||||
$_SERVER['FOF_REMOTE_ADDR'] = getenv('REMOTE_ADDR');
|
||||
}
|
||||
}
|
||||
|
||||
$_SERVER['REMOTE_ADDR'] = $ip;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should I allow the remote client's IP to be overridden by an X-Forwarded-For or Client-Ip HTTP header?
|
||||
*
|
||||
* @param bool $newState True to allow the override
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function setAllowIpOverrides($newState)
|
||||
{
|
||||
self::$allowIpOverrides = $newState ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the visitor's IP address. Automatically handles reverse proxies
|
||||
* reporting the IPs of intermediate devices, like load balancers. Examples:
|
||||
* https://www.akeebabackup.com/support/admin-tools/13743-double-ip-adresses-in-security-exception-log-warnings.html
|
||||
* http://stackoverflow.com/questions/2422395/why-is-request-envremote-addr-returning-two-ips
|
||||
* The solution used is assuming that the last IP address is the external one.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected static function detectAndCleanIP()
|
||||
{
|
||||
$ip = self::detectIP();
|
||||
|
||||
if ((strstr($ip, ',') !== false) || (strstr($ip, ' ') !== false))
|
||||
{
|
||||
$ip = str_replace(' ', ',', $ip);
|
||||
$ip = str_replace(',,', ',', $ip);
|
||||
$ips = explode(',', $ip);
|
||||
$ip = '';
|
||||
while (empty($ip) && !empty($ips))
|
||||
{
|
||||
$ip = array_pop($ips);
|
||||
$ip = trim($ip);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$ip = trim($ip);
|
||||
}
|
||||
|
||||
return $ip;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the visitor's IP address
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected static function detectIP()
|
||||
{
|
||||
// Normally the $_SERVER superglobal is set
|
||||
if (isset($_SERVER))
|
||||
{
|
||||
// Do we have an x-forwarded-for HTTP header (e.g. NginX)?
|
||||
if (self::$allowIpOverrides && array_key_exists('HTTP_X_FORWARDED_FOR', $_SERVER))
|
||||
{
|
||||
return $_SERVER['HTTP_X_FORWARDED_FOR'];
|
||||
}
|
||||
|
||||
// Do we have a client-ip header (e.g. non-transparent proxy)?
|
||||
if (self::$allowIpOverrides && array_key_exists('HTTP_CLIENT_IP', $_SERVER))
|
||||
{
|
||||
return $_SERVER['HTTP_CLIENT_IP'];
|
||||
}
|
||||
|
||||
// Normal, non-proxied server or server behind a transparent proxy
|
||||
return $_SERVER['REMOTE_ADDR'];
|
||||
}
|
||||
|
||||
// This part is executed on PHP running as CGI, or on SAPIs which do
|
||||
// not set the $_SERVER superglobal
|
||||
// If getenv() is disabled, you're screwed
|
||||
if (!function_exists('getenv'))
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
// Do we have an x-forwarded-for HTTP header?
|
||||
if (self::$allowIpOverrides && getenv('HTTP_X_FORWARDED_FOR'))
|
||||
{
|
||||
return getenv('HTTP_X_FORWARDED_FOR');
|
||||
}
|
||||
|
||||
// Do we have a client-ip header?
|
||||
if (self::$allowIpOverrides && getenv('HTTP_CLIENT_IP'))
|
||||
{
|
||||
return getenv('HTTP_CLIENT_IP');
|
||||
}
|
||||
|
||||
// Normal, non-proxied server or server behind a transparent proxy
|
||||
if (getenv('REMOTE_ADDR'))
|
||||
{
|
||||
return getenv('REMOTE_ADDR');
|
||||
}
|
||||
|
||||
// Catch-all case for broken servers, apparently
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts inet_pton output to bits string
|
||||
*
|
||||
* @param string $inet The in_addr representation of an IPv4 or IPv6 address
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected static function inet_to_bits($inet)
|
||||
{
|
||||
if (strlen($inet) == 4)
|
||||
{
|
||||
$unpacked = unpack('A4', $inet);
|
||||
}
|
||||
else
|
||||
{
|
||||
$unpacked = unpack('A16', $inet);
|
||||
}
|
||||
$unpacked = str_split($unpacked[1]);
|
||||
$binaryip = '';
|
||||
|
||||
foreach ($unpacked as $char)
|
||||
{
|
||||
$binaryip .= str_pad(decbin(ord($char)), 8, '0', STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
return $binaryip;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an IPv6 address $ip is part of the IPv6 CIDR block $cidrnet
|
||||
*
|
||||
* @param string $ip The IPv6 address to check, e.g. 21DA:00D3:0000:2F3B:02AC:00FF:FE28:9C5A
|
||||
* @param string $cidrnet The IPv6 CIDR block, e.g. 21DA:00D3:0000:2F3B::/64
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected static function checkIPv6CIDR($ip, $cidrnet)
|
||||
{
|
||||
$ip = inet_pton($ip);
|
||||
$binaryip=self::inet_to_bits($ip);
|
||||
|
||||
list($net,$maskbits)=explode('/',$cidrnet);
|
||||
$net=inet_pton($net);
|
||||
$binarynet=self::inet_to_bits($net);
|
||||
|
||||
$ip_net_bits=substr($binaryip,0,$maskbits);
|
||||
$net_bits =substr($binarynet,0,$maskbits);
|
||||
|
||||
return $ip_net_bits === $net_bits;
|
||||
}
|
||||
}
|
||||
202
libraries/f0f/utils/object/object.php
Normal file
202
libraries/f0f/utils/object/object.php
Normal file
@ -0,0 +1,202 @@
|
||||
<?php
|
||||
/**
|
||||
* @package FrameworkOnFramework
|
||||
* @subpackage utils
|
||||
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved.
|
||||
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
||||
*/
|
||||
|
||||
defined('F0F_INCLUDED') or die;
|
||||
|
||||
/**
|
||||
* Temporary class for backwards compatibility. You should not be using this
|
||||
* in your code. It is currently present to handle the validation error stack
|
||||
* for F0FTable::check() and will be removed in an upcoming version.
|
||||
*
|
||||
* This class is based on JObject as found in Joomla! 3.2.1
|
||||
*
|
||||
* @deprecated 2.1
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class F0FUtilsObject
|
||||
{
|
||||
/**
|
||||
* An array of error messages or Exception objects.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_errors = array();
|
||||
|
||||
/**
|
||||
* Class constructor, overridden in descendant classes.
|
||||
*
|
||||
* @param mixed $properties Either and associative array or another
|
||||
* object to set the initial properties of the object.
|
||||
*/
|
||||
public function __construct($properties = null)
|
||||
{
|
||||
if ($properties !== null)
|
||||
{
|
||||
$this->setProperties($properties);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic method to convert the object to a string gracefully.
|
||||
*
|
||||
* @return string The classname.
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return get_class($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a default value if not alreay assigned
|
||||
*
|
||||
* @param string $property The name of the property.
|
||||
* @param mixed $default The default value.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function def($property, $default = null)
|
||||
{
|
||||
$value = $this->get($property, $default);
|
||||
return $this->set($property, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a property of the object or the default value if the property is not set.
|
||||
*
|
||||
* @param string $property The name of the property.
|
||||
* @param mixed $default The default value.
|
||||
*
|
||||
* @return mixed The value of the property.
|
||||
*/
|
||||
public function get($property, $default = null)
|
||||
{
|
||||
if (isset($this->$property))
|
||||
{
|
||||
return $this->$property;
|
||||
}
|
||||
return $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an associative array of object properties.
|
||||
*
|
||||
* @param boolean $public If true, returns only the public properties.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getProperties($public = true)
|
||||
{
|
||||
$vars = get_object_vars($this);
|
||||
if ($public)
|
||||
{
|
||||
foreach ($vars as $key => $value)
|
||||
{
|
||||
if ('_' == substr($key, 0, 1))
|
||||
{
|
||||
unset($vars[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $vars;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the most recent error message.
|
||||
*
|
||||
* @param integer $i Option error index.
|
||||
* @param boolean $toString Indicates if JError objects should return their error message.
|
||||
*
|
||||
* @return string Error message
|
||||
*/
|
||||
public function getError($i = null, $toString = true)
|
||||
{
|
||||
// Find the error
|
||||
if ($i === null)
|
||||
{
|
||||
// Default, return the last message
|
||||
$error = end($this->_errors);
|
||||
}
|
||||
elseif (!array_key_exists($i, $this->_errors))
|
||||
{
|
||||
// If $i has been specified but does not exist, return false
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
$error = $this->_errors[$i];
|
||||
}
|
||||
|
||||
// Check if only the string is requested
|
||||
if ($error instanceof Exception && $toString)
|
||||
{
|
||||
return (string) $error;
|
||||
}
|
||||
|
||||
return $error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all errors, if any.
|
||||
*
|
||||
* @return array Array of error messages or JErrors.
|
||||
*/
|
||||
public function getErrors()
|
||||
{
|
||||
return $this->_errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies a property of the object, creating it if it does not already exist.
|
||||
*
|
||||
* @param string $property The name of the property.
|
||||
* @param mixed $value The value of the property to set.
|
||||
*
|
||||
* @return mixed Previous value of the property.
|
||||
*/
|
||||
public function set($property, $value = null)
|
||||
{
|
||||
$previous = isset($this->$property) ? $this->$property : null;
|
||||
$this->$property = $value;
|
||||
return $previous;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the object properties based on a named array/hash.
|
||||
*
|
||||
* @param mixed $properties Either an associative array or another object.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function setProperties($properties)
|
||||
{
|
||||
if (is_array($properties) || is_object($properties))
|
||||
{
|
||||
foreach ((array) $properties as $k => $v)
|
||||
{
|
||||
// Use the set function which might be overridden.
|
||||
$this->set($k, $v);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an error message.
|
||||
*
|
||||
* @param string $error Error message.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setError($error)
|
||||
{
|
||||
array_push($this->_errors, $error);
|
||||
}
|
||||
}
|
||||
277
libraries/f0f/utils/observable/dispatcher.php
Normal file
277
libraries/f0f/utils/observable/dispatcher.php
Normal file
@ -0,0 +1,277 @@
|
||||
<?php
|
||||
/**
|
||||
* @package FrameworkOnFramework
|
||||
* @subpackage utils
|
||||
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved.
|
||||
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
||||
*/
|
||||
|
||||
defined('F0F_INCLUDED') or die;
|
||||
|
||||
/**
|
||||
* Class to handle dispatching of events.
|
||||
*
|
||||
* This is the Observable part of the Observer design pattern
|
||||
* for the event architecture.
|
||||
*
|
||||
* This class is based on JEventDispatcher as found in Joomla! 3.2.0
|
||||
*/
|
||||
class F0FUtilsObservableDispatcher extends F0FUtilsObject
|
||||
{
|
||||
/**
|
||||
* An array of Observer objects to notify
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_observers = array();
|
||||
|
||||
/**
|
||||
* The state of the observable object
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
protected $_state = null;
|
||||
|
||||
/**
|
||||
* A multi dimensional array of [function][] = key for observers
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_methods = array();
|
||||
|
||||
/**
|
||||
* Stores the singleton instance of the dispatcher.
|
||||
*
|
||||
* @var F0FUtilsObservableDispatcher
|
||||
*/
|
||||
protected static $instance = null;
|
||||
|
||||
/**
|
||||
* Returns the global Event Dispatcher object, only creating it
|
||||
* if it doesn't already exist.
|
||||
*
|
||||
* @return F0FUtilsObservableDispatcher The EventDispatcher object.
|
||||
*/
|
||||
public static function getInstance()
|
||||
{
|
||||
if (self::$instance === null)
|
||||
{
|
||||
self::$instance = new static;
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the state of the F0FUtilsObservableDispatcher object
|
||||
*
|
||||
* @return mixed The state of the object.
|
||||
*/
|
||||
public function getState()
|
||||
{
|
||||
return $this->_state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers an event handler to the event dispatcher
|
||||
*
|
||||
* @param string $event Name of the event to register handler for
|
||||
* @param string $handler Name of the event handler
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function register($event, $handler)
|
||||
{
|
||||
// Are we dealing with a class or callback type handler?
|
||||
if (is_callable($handler))
|
||||
{
|
||||
// Ok, function type event handler... let's attach it.
|
||||
$method = array('event' => $event, 'handler' => $handler);
|
||||
$this->attach($method);
|
||||
}
|
||||
elseif (class_exists($handler))
|
||||
{
|
||||
// Ok, class type event handler... let's instantiate and attach it.
|
||||
$this->attach(new $handler($this));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidArgumentException('Invalid event handler.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers an event by dispatching arguments to all observers that handle
|
||||
* the event and returning their return values.
|
||||
*
|
||||
* @param string $event The event to trigger.
|
||||
* @param array $args An array of arguments.
|
||||
*
|
||||
* @return array An array of results from each function call.
|
||||
*/
|
||||
public function trigger($event, $args = array())
|
||||
{
|
||||
$result = array();
|
||||
|
||||
/*
|
||||
* If no arguments were passed, we still need to pass an empty array to
|
||||
* the call_user_func_array function.
|
||||
*/
|
||||
$args = (array) $args;
|
||||
|
||||
$event = strtolower($event);
|
||||
|
||||
// Check if any plugins are attached to the event.
|
||||
if (!isset($this->_methods[$event]) || empty($this->_methods[$event]))
|
||||
{
|
||||
// No Plugins Associated To Event!
|
||||
return $result;
|
||||
}
|
||||
|
||||
// Loop through all plugins having a method matching our event
|
||||
foreach ($this->_methods[$event] as $key)
|
||||
{
|
||||
// Check if the plugin is present.
|
||||
if (!isset($this->_observers[$key]))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Fire the event for an object based observer.
|
||||
if (is_object($this->_observers[$key]))
|
||||
{
|
||||
$args['event'] = $event;
|
||||
$value = $this->_observers[$key]->update($args);
|
||||
}
|
||||
// Fire the event for a function based observer.
|
||||
elseif (is_array($this->_observers[$key]))
|
||||
{
|
||||
$value = call_user_func_array($this->_observers[$key]['handler'], $args);
|
||||
}
|
||||
|
||||
if (isset($value))
|
||||
{
|
||||
$result[] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach an observer object
|
||||
*
|
||||
* @param object $observer An observer object to attach
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function attach($observer)
|
||||
{
|
||||
if (is_array($observer))
|
||||
{
|
||||
if (!isset($observer['handler']) || !isset($observer['event']) || !is_callable($observer['handler']))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure we haven't already attached this array as an observer
|
||||
foreach ($this->_observers as $check)
|
||||
{
|
||||
if (is_array($check) && $check['event'] == $observer['event'] && $check['handler'] == $observer['handler'])
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$this->_observers[] = $observer;
|
||||
end($this->_observers);
|
||||
$methods = array($observer['event']);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!($observer instanceof F0FUtilsObservableEvent))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure we haven't already attached this object as an observer
|
||||
$class = get_class($observer);
|
||||
|
||||
foreach ($this->_observers as $check)
|
||||
{
|
||||
if ($check instanceof $class)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$this->_observers[] = $observer;
|
||||
|
||||
// Required in PHP 7 since foreach() doesn't advance the internal array counter, see http://php.net/manual/en/migration70.incompatible.php
|
||||
end($this->_observers);
|
||||
|
||||
$methods = array();
|
||||
|
||||
foreach(get_class_methods($observer) as $obs_method)
|
||||
{
|
||||
// Magic methods are not allowed
|
||||
if(strpos('__', $obs_method) === 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$methods[] = $obs_method;
|
||||
}
|
||||
|
||||
//$methods = get_class_methods($observer);
|
||||
}
|
||||
|
||||
$key = key($this->_observers);
|
||||
|
||||
foreach ($methods as $method)
|
||||
{
|
||||
$method = strtolower($method);
|
||||
|
||||
if (!isset($this->_methods[$method]))
|
||||
{
|
||||
$this->_methods[$method] = array();
|
||||
}
|
||||
|
||||
$this->_methods[$method][] = $key;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Detach an observer object
|
||||
*
|
||||
* @param object $observer An observer object to detach.
|
||||
*
|
||||
* @return boolean True if the observer object was detached.
|
||||
*/
|
||||
public function detach($observer)
|
||||
{
|
||||
$retval = false;
|
||||
|
||||
$key = array_search($observer, $this->_observers);
|
||||
|
||||
if ($key !== false)
|
||||
{
|
||||
unset($this->_observers[$key]);
|
||||
$retval = true;
|
||||
|
||||
foreach ($this->_methods as &$method)
|
||||
{
|
||||
$k = array_search($key, $method);
|
||||
|
||||
if ($k !== false)
|
||||
{
|
||||
unset($method[$k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $retval;
|
||||
}
|
||||
}
|
||||
70
libraries/f0f/utils/observable/event.php
Normal file
70
libraries/f0f/utils/observable/event.php
Normal file
@ -0,0 +1,70 @@
|
||||
<?php
|
||||
/**
|
||||
* @package FrameworkOnFramework
|
||||
* @subpackage utils
|
||||
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved.
|
||||
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
||||
*/
|
||||
|
||||
defined('F0F_INCLUDED') or die;
|
||||
|
||||
/**
|
||||
* Defines an observable event.
|
||||
*
|
||||
* This class is based on JEvent as found in Joomla! 3.2.0
|
||||
*/
|
||||
abstract class F0FUtilsObservableEvent extends F0FUtilsObject
|
||||
{
|
||||
/**
|
||||
* Event object to observe.
|
||||
*
|
||||
* @var object
|
||||
*/
|
||||
protected $_subject = null;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param object &$subject The object to observe.
|
||||
*/
|
||||
public function __construct(&$subject)
|
||||
{
|
||||
// Register the observer ($this) so we can be notified
|
||||
$subject->attach($this);
|
||||
|
||||
// Set the subject to observe
|
||||
$this->_subject = &$subject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to trigger events.
|
||||
* The method first generates the even from the argument array. Then it unsets the argument
|
||||
* since the argument has no bearing on the event handler.
|
||||
* If the method exists it is called and returns its return value. If it does not exist it
|
||||
* returns null.
|
||||
*
|
||||
* @param array &$args Arguments
|
||||
*
|
||||
* @return mixed Routine return value
|
||||
*/
|
||||
public function update(&$args)
|
||||
{
|
||||
// First let's get the event from the argument array. Next we will unset the
|
||||
// event argument as it has no bearing on the method to handle the event.
|
||||
$event = $args['event'];
|
||||
unset($args['event']);
|
||||
|
||||
/*
|
||||
* If the method to handle an event exists, call it and return its return
|
||||
* value. If it does not exist, return null.
|
||||
*/
|
||||
if (method_exists($this, $event))
|
||||
{
|
||||
return call_user_func_array(array($this, $event), $args);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
87
libraries/f0f/utils/timer/timer.php
Normal file
87
libraries/f0f/utils/timer/timer.php
Normal file
@ -0,0 +1,87 @@
|
||||
<?php
|
||||
/**
|
||||
* @package FrameworkOnFramework
|
||||
* @subpackage utils
|
||||
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved.
|
||||
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
||||
*/
|
||||
|
||||
defined('F0F_INCLUDED') or die;
|
||||
|
||||
/**
|
||||
* An execution timer monitor class
|
||||
*/
|
||||
class F0FUtilsTimer
|
||||
{
|
||||
/** @var float Maximum execution time allowance */
|
||||
private $max_exec_time = null;
|
||||
|
||||
/** @var float Timestamp of execution start */
|
||||
private $start_time = null;
|
||||
|
||||
/**
|
||||
* Public constructor, creates the timer object and calculates the execution
|
||||
* time limits.
|
||||
*
|
||||
* @param float $max_exec_time Maximum execution time allowance
|
||||
* @param float $runtime_bias Execution time bias (expressed as % of $max_exec_time)
|
||||
*/
|
||||
public function __construct($max_exec_time = 5.0, $runtime_bias = 75.0)
|
||||
{
|
||||
// Initialize start time
|
||||
$this->start_time = $this->microtime_float();
|
||||
|
||||
$this->max_exec_time = $max_exec_time * $runtime_bias / 100.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wake-up function to reset internal timer when we get unserialized
|
||||
*/
|
||||
public function __wakeup()
|
||||
{
|
||||
// Re-initialize start time on wake-up
|
||||
$this->start_time = $this->microtime_float();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of seconds left, before we hit the "must break" threshold. Negative
|
||||
* values mean that we have already crossed that threshold.
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function getTimeLeft()
|
||||
{
|
||||
return $this->max_exec_time - $this->getRunningTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the time elapsed since object creation/unserialization, effectively
|
||||
* how long we are running
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function getRunningTime()
|
||||
{
|
||||
return $this->microtime_float() - $this->start_time;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current timestamp in decimal seconds
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
private function microtime_float()
|
||||
{
|
||||
list($usec, $sec) = explode(" ", microtime());
|
||||
return ((float)$usec + (float)$sec);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the timer
|
||||
*/
|
||||
public function resetTime()
|
||||
{
|
||||
$this->start_time = $this->microtime_float();
|
||||
}
|
||||
|
||||
}
|
||||
371
libraries/f0f/utils/update/collection.php
Normal file
371
libraries/f0f/utils/update/collection.php
Normal file
@ -0,0 +1,371 @@
|
||||
<?php
|
||||
/**
|
||||
* @package FrameworkOnFramework
|
||||
* @subpackage utils
|
||||
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved.
|
||||
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
||||
*/
|
||||
|
||||
// Protect from unauthorized access
|
||||
defined('F0F_INCLUDED') or die;
|
||||
|
||||
/**
|
||||
* A helper class to read and parse "collection" update XML files over the web
|
||||
*/
|
||||
class F0FUtilsUpdateCollection
|
||||
{
|
||||
/**
|
||||
* Reads a "collection" XML update source and returns the complete tree of categories
|
||||
* and extensions applicable for platform version $jVersion
|
||||
*
|
||||
* @param string $url The collection XML update source URL to read from
|
||||
* @param string $jVersion Joomla! version to fetch updates for, or null to use JVERSION
|
||||
*
|
||||
* @return array A list of update sources applicable to $jVersion
|
||||
*/
|
||||
public function getAllUpdates($url, $jVersion = null)
|
||||
{
|
||||
// Get the target platform
|
||||
if (is_null($jVersion))
|
||||
{
|
||||
$jVersion = JVERSION;
|
||||
}
|
||||
|
||||
// Initialise return value
|
||||
$updates = array(
|
||||
'metadata' => array(
|
||||
'name' => '',
|
||||
'description' => '',
|
||||
),
|
||||
'categories' => array(),
|
||||
'extensions' => array(),
|
||||
);
|
||||
|
||||
// Download and parse the XML file
|
||||
$donwloader = new F0FDownload();
|
||||
$xmlSource = $donwloader->getFromURL($url);
|
||||
|
||||
try
|
||||
{
|
||||
$xml = new SimpleXMLElement($xmlSource, LIBXML_NONET);
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
return $updates;
|
||||
}
|
||||
|
||||
// Sanity check
|
||||
if (($xml->getName() != 'extensionset'))
|
||||
{
|
||||
unset($xml);
|
||||
|
||||
return $updates;
|
||||
}
|
||||
|
||||
// Initialise return value with the stream metadata (name, description)
|
||||
$rootAttributes = $xml->attributes();
|
||||
foreach ($rootAttributes as $k => $v)
|
||||
{
|
||||
$updates['metadata'][$k] = (string)$v;
|
||||
}
|
||||
|
||||
// Initialise the raw list of updates
|
||||
$rawUpdates = array(
|
||||
'categories' => array(),
|
||||
'extensions' => array(),
|
||||
);
|
||||
|
||||
// Segregate the raw list to a hierarchy of extension and category entries
|
||||
/** @var SimpleXMLElement $extension */
|
||||
foreach ($xml->children() as $extension)
|
||||
{
|
||||
switch ($extension->getName())
|
||||
{
|
||||
case 'category':
|
||||
// These are the parameters we expect in a category
|
||||
$params = array(
|
||||
'name' => '',
|
||||
'description' => '',
|
||||
'category' => '',
|
||||
'ref' => '',
|
||||
'targetplatformversion' => $jVersion,
|
||||
);
|
||||
|
||||
// These are the attributes of the element
|
||||
$attributes = $extension->attributes();
|
||||
|
||||
// Merge them all
|
||||
foreach ($attributes as $k => $v)
|
||||
{
|
||||
$params[$k] = (string)$v;
|
||||
}
|
||||
|
||||
// We can't have a category with an empty category name
|
||||
if (empty($params['category']))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// We can't have a category with an empty ref
|
||||
if (empty($params['ref']))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (empty($params['description']))
|
||||
{
|
||||
$params['description'] = $params['category'];
|
||||
}
|
||||
|
||||
if (!array_key_exists($params['category'], $rawUpdates['categories']))
|
||||
{
|
||||
$rawUpdates['categories'][$params['category']] = array();
|
||||
}
|
||||
|
||||
$rawUpdates['categories'][$params['category']][] = $params;
|
||||
|
||||
break;
|
||||
|
||||
case 'extension':
|
||||
// These are the parameters we expect in a category
|
||||
$params = array(
|
||||
'element' => '',
|
||||
'type' => '',
|
||||
'version' => '',
|
||||
'name' => '',
|
||||
'detailsurl' => '',
|
||||
'targetplatformversion' => $jVersion,
|
||||
);
|
||||
|
||||
// These are the attributes of the element
|
||||
$attributes = $extension->attributes();
|
||||
|
||||
// Merge them all
|
||||
foreach ($attributes as $k => $v)
|
||||
{
|
||||
$params[$k] = (string)$v;
|
||||
}
|
||||
|
||||
// We can't have an extension with an empty element
|
||||
if (empty($params['element']))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// We can't have an extension with an empty type
|
||||
if (empty($params['type']))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// We can't have an extension with an empty version
|
||||
if (empty($params['version']))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (empty($params['name']))
|
||||
{
|
||||
$params['name'] = $params['element'] . ' ' . $params['version'];
|
||||
}
|
||||
|
||||
if (!array_key_exists($params['type'], $rawUpdates['extensions']))
|
||||
{
|
||||
$rawUpdates['extensions'][$params['type']] = array();
|
||||
}
|
||||
|
||||
if (!array_key_exists($params['element'], $rawUpdates['extensions'][$params['type']]))
|
||||
{
|
||||
$rawUpdates['extensions'][$params['type']][$params['element']] = array();
|
||||
}
|
||||
|
||||
$rawUpdates['extensions'][$params['type']][$params['element']][] = $params;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
unset($xml);
|
||||
|
||||
if (!empty($rawUpdates['categories']))
|
||||
{
|
||||
foreach ($rawUpdates['categories'] as $category => $entries)
|
||||
{
|
||||
$update = $this->filterListByPlatform($entries, $jVersion);
|
||||
$updates['categories'][$category] = $update;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($rawUpdates['extensions']))
|
||||
{
|
||||
foreach ($rawUpdates['extensions'] as $type => $extensions)
|
||||
{
|
||||
$updates['extensions'][$type] = array();
|
||||
|
||||
if (!empty($extensions))
|
||||
{
|
||||
foreach ($extensions as $element => $entries)
|
||||
{
|
||||
$update = $this->filterListByPlatform($entries, $jVersion);
|
||||
$updates['extensions'][$type][$element] = $update;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $updates;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters a list of updates, returning only those available for the
|
||||
* specified platform version $jVersion
|
||||
*
|
||||
* @param array $updates An array containing update definitions (categories or extensions)
|
||||
* @param string $jVersion Joomla! version to fetch updates for, or null to use JVERSION
|
||||
*
|
||||
* @return array|null The update definition that is compatible, or null if none is compatible
|
||||
*/
|
||||
private function filterListByPlatform($updates, $jVersion = null)
|
||||
{
|
||||
// Get the target platform
|
||||
if (is_null($jVersion))
|
||||
{
|
||||
$jVersion = JVERSION;
|
||||
}
|
||||
|
||||
$versionParts = explode('.', $jVersion, 4);
|
||||
$platformVersionMajor = $versionParts[0];
|
||||
$platformVersionMinor = (count($versionParts) > 1) ? $platformVersionMajor . '.' . $versionParts[1] : $platformVersionMajor;
|
||||
$platformVersionNormal = (count($versionParts) > 2) ? $platformVersionMinor . '.' . $versionParts[2] : $platformVersionMinor;
|
||||
$platformVersionFull = (count($versionParts) > 3) ? $platformVersionNormal . '.' . $versionParts[3] : $platformVersionNormal;
|
||||
|
||||
$pickedExtension = null;
|
||||
$pickedSpecificity = -1;
|
||||
|
||||
foreach ($updates as $update)
|
||||
{
|
||||
// Test the target platform
|
||||
$targetPlatform = (string)$update['targetplatformversion'];
|
||||
|
||||
if ($targetPlatform === $platformVersionFull)
|
||||
{
|
||||
$pickedExtension = $update;
|
||||
$pickedSpecificity = 4;
|
||||
}
|
||||
elseif (($targetPlatform === $platformVersionNormal) && ($pickedSpecificity <= 3))
|
||||
{
|
||||
$pickedExtension = $update;
|
||||
$pickedSpecificity = 3;
|
||||
}
|
||||
elseif (($targetPlatform === $platformVersionMinor) && ($pickedSpecificity <= 2))
|
||||
{
|
||||
$pickedExtension = $update;
|
||||
$pickedSpecificity = 2;
|
||||
}
|
||||
elseif (($targetPlatform === $platformVersionMajor) && ($pickedSpecificity <= 1))
|
||||
{
|
||||
$pickedExtension = $update;
|
||||
$pickedSpecificity = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return $pickedExtension;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns only the category definitions of a collection
|
||||
*
|
||||
* @param string $url The URL of the collection update source
|
||||
* @param string $jVersion Joomla! version to fetch updates for, or null to use JVERSION
|
||||
*
|
||||
* @return array An array of category update definitions
|
||||
*/
|
||||
public function getCategories($url, $jVersion = null)
|
||||
{
|
||||
$allUpdates = $this->getAllUpdates($url, $jVersion);
|
||||
|
||||
return $allUpdates['categories'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the update source for a specific category
|
||||
*
|
||||
* @param string $url The URL of the collection update source
|
||||
* @param string $category The category name you want to get the update source URL of
|
||||
* @param string $jVersion Joomla! version to fetch updates for, or null to use JVERSION
|
||||
*
|
||||
* @return string|null The update stream URL, or null if it's not found
|
||||
*/
|
||||
public function getCategoryUpdateSource($url, $category, $jVersion = null)
|
||||
{
|
||||
$allUpdates = $this->getAllUpdates($url, $jVersion);
|
||||
|
||||
if (array_key_exists($category, $allUpdates['categories']))
|
||||
{
|
||||
return $allUpdates['categories'][$category]['ref'];
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of updates for extensions only, optionally of a specific type
|
||||
*
|
||||
* @param string $url The URL of the collection update source
|
||||
* @param string $type The extension type you want to get the update source URL of, empty to get all
|
||||
* extension types
|
||||
* @param string $jVersion Joomla! version to fetch updates for, or null to use JVERSION
|
||||
*
|
||||
* @return array|null An array of extension update definitions or null if none is found
|
||||
*/
|
||||
public function getExtensions($url, $type = null, $jVersion = null)
|
||||
{
|
||||
$allUpdates = $this->getAllUpdates($url, $jVersion);
|
||||
|
||||
if (empty($type))
|
||||
{
|
||||
return $allUpdates['extensions'];
|
||||
}
|
||||
elseif (array_key_exists($type, $allUpdates['extensions']))
|
||||
{
|
||||
return $allUpdates['extensions'][$type];
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the update source URL for a specific extension, based on the type and element, e.g.
|
||||
* type=file and element=joomla is Joomla! itself.
|
||||
*
|
||||
* @param string $url The URL of the collection update source
|
||||
* @param string $type The extension type you want to get the update source URL of
|
||||
* @param string $element The extension element you want to get the update source URL of
|
||||
* @param string $jVersion Joomla! version to fetch updates for, or null to use JVERSION
|
||||
*
|
||||
* @return string|null The update source URL or null if the extension is not found
|
||||
*/
|
||||
public function getExtensionUpdateSource($url, $type, $element, $jVersion = null)
|
||||
{
|
||||
$allUpdates = $this->getExtensions($url, $type, $jVersion);
|
||||
|
||||
if (empty($allUpdates))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
elseif (array_key_exists($element, $allUpdates))
|
||||
{
|
||||
return $allUpdates[$element]['detailsurl'];
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
128
libraries/f0f/utils/update/extension.php
Normal file
128
libraries/f0f/utils/update/extension.php
Normal file
@ -0,0 +1,128 @@
|
||||
<?php
|
||||
/**
|
||||
* @package FrameworkOnFramework
|
||||
* @subpackage utils
|
||||
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved.
|
||||
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
||||
*/
|
||||
|
||||
// Protect from unauthorized access
|
||||
defined('F0F_INCLUDED') or die;
|
||||
|
||||
/**
|
||||
* A helper class to read and parse "extension" update XML files over the web
|
||||
*/
|
||||
class F0FUtilsUpdateExtension
|
||||
{
|
||||
/**
|
||||
* Reads an "extension" XML update source and returns all listed update entries.
|
||||
*
|
||||
* If you have a "collection" XML update source you should do something like this:
|
||||
* $collection = new F0FUtilsUpdateCollection();
|
||||
* $extensionUpdateURL = $collection->getExtensionUpdateSource($url, 'component', 'com_foobar', JVERSION);
|
||||
* $extension = new F0FUtilsUpdateExtension();
|
||||
* $updates = $extension->getUpdatesFromExtension($extensionUpdateURL);
|
||||
*
|
||||
* @param string $url The extension XML update source URL to read from
|
||||
*
|
||||
* @return array An array of update entries
|
||||
*/
|
||||
public function getUpdatesFromExtension($url)
|
||||
{
|
||||
// Initialise
|
||||
$ret = array();
|
||||
|
||||
// Get and parse the XML source
|
||||
$downloader = new F0FDownload();
|
||||
$xmlSource = $downloader->getFromURL($url);
|
||||
|
||||
try
|
||||
{
|
||||
$xml = new SimpleXMLElement($xmlSource, LIBXML_NONET);
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
return $ret;
|
||||
}
|
||||
|
||||
// Sanity check
|
||||
if (($xml->getName() != 'updates'))
|
||||
{
|
||||
unset($xml);
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
// Let's populate the list of updates
|
||||
/** @var SimpleXMLElement $update */
|
||||
foreach ($xml->children() as $update)
|
||||
{
|
||||
// Sanity check
|
||||
if ($update->getName() != 'update')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$entry = array(
|
||||
'infourl' => array('title' => '', 'url' => ''),
|
||||
'downloads' => array(),
|
||||
'tags' => array(),
|
||||
'targetplatform' => array(),
|
||||
);
|
||||
|
||||
$properties = get_object_vars($update);
|
||||
|
||||
foreach ($properties as $nodeName => $nodeContent)
|
||||
{
|
||||
switch ($nodeName)
|
||||
{
|
||||
default:
|
||||
$entry[$nodeName] = $nodeContent;
|
||||
break;
|
||||
|
||||
case 'infourl':
|
||||
case 'downloads':
|
||||
case 'tags':
|
||||
case 'targetplatform':
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$infourlNode = $update->xpath('infourl');
|
||||
$entry['infourl']['title'] = (string)$infourlNode[0]['title'];
|
||||
$entry['infourl']['url'] = (string)$infourlNode[0];
|
||||
|
||||
$downloadNodes = $update->xpath('downloads/downloadurl');
|
||||
foreach ($downloadNodes as $downloadNode)
|
||||
{
|
||||
$entry['downloads'][] = array(
|
||||
'type' => (string)$downloadNode['type'],
|
||||
'format' => (string)$downloadNode['format'],
|
||||
'url' => (string)$downloadNode,
|
||||
);
|
||||
}
|
||||
|
||||
$tagNodes = $update->xpath('tags/tag');
|
||||
foreach ($tagNodes as $tagNode)
|
||||
{
|
||||
$entry['tags'][] = (string)$tagNode;
|
||||
}
|
||||
|
||||
/** @var SimpleXMLElement $targetPlatformNode */
|
||||
$targetPlatformNode = $update->xpath('targetplatform');
|
||||
|
||||
$entry['targetplatform']['name'] = (string)$targetPlatformNode[0]['name'];
|
||||
$entry['targetplatform']['version'] = (string)$targetPlatformNode[0]['version'];
|
||||
$client = $targetPlatformNode[0]->xpath('client');
|
||||
$entry['targetplatform']['client'] = (is_array($client) && count($client)) ? (string)$client[0] : '';
|
||||
$folder = $targetPlatformNode[0]->xpath('folder');
|
||||
$entry['targetplatform']['folder'] = is_array($folder) && count($folder) ? (string)$folder[0] : '';
|
||||
|
||||
$ret[] = $entry;
|
||||
}
|
||||
|
||||
unset($xml);
|
||||
|
||||
return $ret;
|
||||
}
|
||||
}
|
||||
625
libraries/f0f/utils/update/joomla.php
Normal file
625
libraries/f0f/utils/update/joomla.php
Normal file
@ -0,0 +1,625 @@
|
||||
<?php
|
||||
/**
|
||||
* @package FrameworkOnFramework
|
||||
* @subpackage utils
|
||||
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved.
|
||||
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
||||
*/
|
||||
|
||||
// Protect from unauthorized access
|
||||
defined('F0F_INCLUDED') or die;
|
||||
|
||||
/**
|
||||
* A helper class which provides update information for the Joomla! CMS itself. This is slightly different than the
|
||||
* regular "extension" files as we need to know if a Joomla! version is STS, LTS, testing, current and so on.
|
||||
*/
|
||||
class F0FUtilsUpdateJoomla extends F0FUtilsUpdateExtension
|
||||
{
|
||||
/**
|
||||
* The source for LTS updates
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected static $lts_url = 'http://update.joomla.org/core/list.xml';
|
||||
|
||||
/**
|
||||
* The source for STS updates
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected static $sts_url = 'http://update.joomla.org/core/sts/list_sts.xml';
|
||||
|
||||
/**
|
||||
* The source for test release updates
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected static $test_url = 'http://update.joomla.org/core/test/list_test.xml';
|
||||
|
||||
/**
|
||||
* Reads an "extension" XML update source and returns all listed update entries.
|
||||
*
|
||||
* If you have a "collection" XML update source you should do something like this:
|
||||
* $collection = new CmsupdateHelperCollection();
|
||||
* $extensionUpdateURL = $collection->getExtensionUpdateSource($url, 'component', 'com_foobar', JVERSION);
|
||||
* $extension = new CmsupdateHelperExtension();
|
||||
* $updates = $extension->getUpdatesFromExtension($extensionUpdateURL);
|
||||
*
|
||||
* @param string $url The extension XML update source URL to read from
|
||||
*
|
||||
* @return array An array of update entries
|
||||
*/
|
||||
public function getUpdatesFromExtension($url)
|
||||
{
|
||||
// Initialise
|
||||
$ret = array();
|
||||
|
||||
// Get and parse the XML source
|
||||
$downloader = new F0FDownload();
|
||||
$xmlSource = $downloader->getFromURL($url);
|
||||
|
||||
try
|
||||
{
|
||||
$xml = new SimpleXMLElement($xmlSource, LIBXML_NONET);
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
return $ret;
|
||||
}
|
||||
|
||||
// Sanity check
|
||||
if (($xml->getName() != 'updates'))
|
||||
{
|
||||
unset($xml);
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
// Let's populate the list of updates
|
||||
/** @var SimpleXMLElement $update */
|
||||
foreach ($xml->children() as $update)
|
||||
{
|
||||
// Sanity check
|
||||
if ($update->getName() != 'update')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$entry = array(
|
||||
'infourl' => array('title' => '', 'url' => ''),
|
||||
'downloads' => array(),
|
||||
'tags' => array(),
|
||||
'targetplatform' => array(),
|
||||
);
|
||||
|
||||
$properties = get_object_vars($update);
|
||||
|
||||
foreach ($properties as $nodeName => $nodeContent)
|
||||
{
|
||||
switch ($nodeName)
|
||||
{
|
||||
default:
|
||||
$entry[ $nodeName ] = $nodeContent;
|
||||
break;
|
||||
|
||||
case 'infourl':
|
||||
case 'downloads':
|
||||
case 'tags':
|
||||
case 'targetplatform':
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$infourlNode = $update->xpath('infourl');
|
||||
$entry['infourl']['title'] = (string) $infourlNode[0]['title'];
|
||||
$entry['infourl']['url'] = (string) $infourlNode[0];
|
||||
|
||||
$downloadNodes = $update->xpath('downloads/downloadurl');
|
||||
foreach ($downloadNodes as $downloadNode)
|
||||
{
|
||||
$entry['downloads'][] = array(
|
||||
'type' => (string) $downloadNode['type'],
|
||||
'format' => (string) $downloadNode['format'],
|
||||
'url' => (string) $downloadNode,
|
||||
);
|
||||
}
|
||||
|
||||
$tagNodes = $update->xpath('tags/tag');
|
||||
foreach ($tagNodes as $tagNode)
|
||||
{
|
||||
$entry['tags'][] = (string) $tagNode;
|
||||
}
|
||||
|
||||
/** @var SimpleXMLElement[] $targetPlatformNode */
|
||||
$targetPlatformNode = $update->xpath('targetplatform');
|
||||
|
||||
$entry['targetplatform']['name'] = (string) $targetPlatformNode[0]['name'];
|
||||
$entry['targetplatform']['version'] = (string) $targetPlatformNode[0]['version'];
|
||||
$client = $targetPlatformNode[0]->xpath('client');
|
||||
$entry['targetplatform']['client'] = (is_array($client) && count($client)) ? (string) $client[0] : '';
|
||||
$folder = $targetPlatformNode[0]->xpath('folder');
|
||||
$entry['targetplatform']['folder'] = is_array($folder) && count($folder) ? (string) $folder[0] : '';
|
||||
|
||||
$ret[] = $entry;
|
||||
}
|
||||
|
||||
unset($xml);
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a "collection" XML update source and picks the correct source URL
|
||||
* for the extension update source.
|
||||
*
|
||||
* @param string $url The collection XML update source URL to read from
|
||||
* @param string $jVersion Joomla! version to fetch updates for, or null to use JVERSION
|
||||
*
|
||||
* @return string The URL of the extension update source, or empty if no updates are provided / fetching failed
|
||||
*/
|
||||
public function getUpdateSourceFromCollection($url, $jVersion = null)
|
||||
{
|
||||
$provider = new F0FUtilsUpdateCollection();
|
||||
|
||||
return $provider->getExtensionUpdateSource($url, 'file', 'joomla', $jVersion);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the properties of a version: STS/LTS, normal or testing
|
||||
*
|
||||
* @param string $jVersion The version number to check
|
||||
* @param string $currentVersion The current Joomla! version number
|
||||
*
|
||||
* @return array The properties analysis
|
||||
*/
|
||||
public function getVersionProperties($jVersion, $currentVersion = null)
|
||||
{
|
||||
// Initialise
|
||||
$ret = array(
|
||||
'lts' => true,
|
||||
// Is this an LTS release? False means STS.
|
||||
'current' => false,
|
||||
// Is this a release in the $currentVersion branch?
|
||||
'upgrade' => 'none',
|
||||
// Upgrade relation of $jVersion to $currentVersion: 'none' (can't upgrade), 'lts' (next or current LTS), 'sts' (next or current STS) or 'current' (same release, no upgrade available)
|
||||
'testing' => false,
|
||||
// Is this a testing (alpha, beta, RC) release?
|
||||
);
|
||||
|
||||
// Get the current version if none is defined
|
||||
if (is_null($currentVersion))
|
||||
{
|
||||
$currentVersion = JVERSION;
|
||||
}
|
||||
|
||||
// Sanitise version numbers
|
||||
$sameVersion = $jVersion == $currentVersion;
|
||||
$jVersion = $this->sanitiseVersion($jVersion);
|
||||
$currentVersion = $this->sanitiseVersion($currentVersion);
|
||||
$sameVersion = $sameVersion || ($jVersion == $currentVersion);
|
||||
|
||||
// Get the base version
|
||||
$baseVersion = substr($jVersion, 0, 3);
|
||||
|
||||
// Get the minimum and maximum current version numbers
|
||||
$current_minimum = substr($currentVersion, 0, 3);
|
||||
$current_maximum = $current_minimum . '.9999';
|
||||
|
||||
// Initialise STS/LTS version numbers
|
||||
$sts_minimum = false;
|
||||
$sts_maximum = false;
|
||||
$lts_minimum = false;
|
||||
|
||||
// Is it an LTS or STS release?
|
||||
switch ($baseVersion)
|
||||
{
|
||||
case '1.5':
|
||||
$ret['lts'] = true;
|
||||
break;
|
||||
|
||||
case '1.6':
|
||||
$ret['lts'] = false;
|
||||
$sts_minimum = '1.7';
|
||||
$sts_maximum = '1.7.999';
|
||||
$lts_minimum = '2.5';
|
||||
break;
|
||||
|
||||
case '1.7':
|
||||
$ret['lts'] = false;
|
||||
$sts_minimum = false;
|
||||
$lts_minimum = '2.5';
|
||||
break;
|
||||
|
||||
case '2.5':
|
||||
$ret['lts'] = true;
|
||||
$sts_minimum = false;
|
||||
$lts_minimum = '2.5';
|
||||
break;
|
||||
|
||||
default:
|
||||
$majorVersion = (int) substr($jVersion, 0, 1);
|
||||
//$minorVersion = (int) substr($jVersion, 2, 1);
|
||||
|
||||
$ret['lts'] = true;
|
||||
$sts_minimum = false;
|
||||
$lts_minimum = $majorVersion . '.0';
|
||||
break;
|
||||
}
|
||||
|
||||
// Is it a current release?
|
||||
if (version_compare($jVersion, $current_minimum, 'ge') && version_compare($jVersion, $current_maximum, 'le'))
|
||||
{
|
||||
$ret['current'] = true;
|
||||
}
|
||||
|
||||
// Is this a testing release?
|
||||
$versionParts = explode('.', $jVersion);
|
||||
$lastVersionPart = array_pop($versionParts);
|
||||
|
||||
if (in_array(substr($lastVersionPart, 0, 1), array('a', 'b')))
|
||||
{
|
||||
$ret['testing'] = true;
|
||||
}
|
||||
elseif (substr($lastVersionPart, 0, 2) == 'rc')
|
||||
{
|
||||
$ret['testing'] = true;
|
||||
}
|
||||
elseif (substr($lastVersionPart, 0, 3) == 'dev')
|
||||
{
|
||||
$ret['testing'] = true;
|
||||
}
|
||||
|
||||
// Find the upgrade relation of $jVersion to $currentVersion
|
||||
if (version_compare($jVersion, $currentVersion, 'eq'))
|
||||
{
|
||||
$ret['upgrade'] = 'current';
|
||||
}
|
||||
elseif (($sts_minimum !== false) && version_compare($jVersion, $sts_minimum, 'ge') && version_compare($jVersion, $sts_maximum, 'le'))
|
||||
{
|
||||
$ret['upgrade'] = 'sts';
|
||||
}
|
||||
elseif (($lts_minimum !== false) && version_compare($jVersion, $lts_minimum, 'ge'))
|
||||
{
|
||||
$ret['upgrade'] = 'lts';
|
||||
}
|
||||
elseif ($baseVersion == $current_minimum)
|
||||
{
|
||||
$ret['upgrade'] = $ret['lts'] ? 'lts' : 'sts';
|
||||
}
|
||||
else
|
||||
{
|
||||
$ret['upgrade'] = 'none';
|
||||
}
|
||||
|
||||
if ($sameVersion)
|
||||
{
|
||||
$ret['upgrade'] = 'none';
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Filters a list of updates, making sure they apply to the specifed CMS
|
||||
* release.
|
||||
*
|
||||
* @param array $updates A list of update records returned by the getUpdatesFromExtension method
|
||||
* @param string $jVersion The current Joomla! version number
|
||||
*
|
||||
* @return array A filtered list of updates. Each update record also includes version relevance information.
|
||||
*/
|
||||
public function filterApplicableUpdates($updates, $jVersion = null)
|
||||
{
|
||||
if (empty($jVersion))
|
||||
{
|
||||
$jVersion = JVERSION;
|
||||
}
|
||||
|
||||
$versionParts = explode('.', $jVersion, 4);
|
||||
$platformVersionMajor = $versionParts[0];
|
||||
$platformVersionMinor = $platformVersionMajor . '.' . $versionParts[1];
|
||||
$platformVersionNormal = $platformVersionMinor . '.' . $versionParts[2];
|
||||
//$platformVersionFull = (count($versionParts) > 3) ? $platformVersionNormal . '.' . $versionParts[3] : $platformVersionNormal;
|
||||
|
||||
$ret = array();
|
||||
|
||||
foreach ($updates as $update)
|
||||
{
|
||||
// Check each update for platform match
|
||||
if (strtolower($update['targetplatform']['name']) != 'joomla')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$targetPlatformVersion = $update['targetplatform']['version'];
|
||||
|
||||
if (!preg_match('/' . $targetPlatformVersion . '/', $platformVersionMinor))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get some information from the version number
|
||||
$updateVersion = $update['version'];
|
||||
$versionProperties = $this->getVersionProperties($updateVersion, $jVersion);
|
||||
|
||||
if ($versionProperties['upgrade'] == 'none')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// The XML files are ill-maintained. Maybe we already have this update?
|
||||
if (!array_key_exists($updateVersion, $ret))
|
||||
{
|
||||
$ret[ $updateVersion ] = array_merge($update, $versionProperties);
|
||||
}
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Joomla! has a lousy track record in naming its alpha, beta and release
|
||||
* candidate releases. The convention used seems to be "what the hell the
|
||||
* current package maintainer thinks looks better". This method tries to
|
||||
* figure out what was in the mind of the maintainer and translate the
|
||||
* funky version number to an actual PHP-format version string.
|
||||
*
|
||||
* @param string $version The whatever-format version number
|
||||
*
|
||||
* @return string A standard formatted version number
|
||||
*/
|
||||
public function sanitiseVersion($version)
|
||||
{
|
||||
$test = strtolower($version);
|
||||
$alphaQualifierPosition = strpos($test, 'alpha-');
|
||||
$betaQualifierPosition = strpos($test, 'beta-');
|
||||
$betaQualifierPosition2 = strpos($test, '-beta');
|
||||
$rcQualifierPosition = strpos($test, 'rc-');
|
||||
$rcQualifierPosition2 = strpos($test, '-rc');
|
||||
$rcQualifierPosition3 = strpos($test, 'rc');
|
||||
$devQualifiedPosition = strpos($test, 'dev');
|
||||
|
||||
if ($alphaQualifierPosition !== false)
|
||||
{
|
||||
$betaRevision = substr($test, $alphaQualifierPosition + 6);
|
||||
if (!$betaRevision)
|
||||
{
|
||||
$betaRevision = 1;
|
||||
}
|
||||
$test = substr($test, 0, $alphaQualifierPosition) . '.a' . $betaRevision;
|
||||
}
|
||||
elseif ($betaQualifierPosition !== false)
|
||||
{
|
||||
$betaRevision = substr($test, $betaQualifierPosition + 5);
|
||||
if (!$betaRevision)
|
||||
{
|
||||
$betaRevision = 1;
|
||||
}
|
||||
$test = substr($test, 0, $betaQualifierPosition) . '.b' . $betaRevision;
|
||||
}
|
||||
elseif ($betaQualifierPosition2 !== false)
|
||||
{
|
||||
$betaRevision = substr($test, $betaQualifierPosition2 + 5);
|
||||
|
||||
if (!$betaRevision)
|
||||
{
|
||||
$betaRevision = 1;
|
||||
}
|
||||
|
||||
$test = substr($test, 0, $betaQualifierPosition2) . '.b' . $betaRevision;
|
||||
}
|
||||
elseif ($rcQualifierPosition !== false)
|
||||
{
|
||||
$betaRevision = substr($test, $rcQualifierPosition + 5);
|
||||
if (!$betaRevision)
|
||||
{
|
||||
$betaRevision = 1;
|
||||
}
|
||||
$test = substr($test, 0, $rcQualifierPosition) . '.rc' . $betaRevision;
|
||||
}
|
||||
elseif ($rcQualifierPosition2 !== false)
|
||||
{
|
||||
$betaRevision = substr($test, $rcQualifierPosition2 + 3);
|
||||
|
||||
if (!$betaRevision)
|
||||
{
|
||||
$betaRevision = 1;
|
||||
}
|
||||
|
||||
$test = substr($test, 0, $rcQualifierPosition2) . '.rc' . $betaRevision;
|
||||
}
|
||||
elseif ($rcQualifierPosition3 !== false)
|
||||
{
|
||||
$betaRevision = substr($test, $rcQualifierPosition3 + 5);
|
||||
|
||||
if (!$betaRevision)
|
||||
{
|
||||
$betaRevision = 1;
|
||||
}
|
||||
|
||||
$test = substr($test, 0, $rcQualifierPosition3) . '.rc' . $betaRevision;
|
||||
}
|
||||
elseif ($devQualifiedPosition !== false)
|
||||
{
|
||||
$betaRevision = substr($test, $devQualifiedPosition + 6);
|
||||
if (!$betaRevision)
|
||||
{
|
||||
$betaRevision = '';
|
||||
}
|
||||
$test = substr($test, 0, $devQualifiedPosition) . '.dev' . $betaRevision;
|
||||
}
|
||||
|
||||
return $test;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads the list of all updates available for the specified Joomla! version
|
||||
* from the network.
|
||||
*
|
||||
* @param array $sources The enabled sources to look into
|
||||
* @param string $jVersion The Joomla! version we are checking updates for
|
||||
*
|
||||
* @return array A list of updates for the installed, current, lts and sts versions
|
||||
*/
|
||||
public function getUpdates($sources = array(), $jVersion = null)
|
||||
{
|
||||
// Make sure we have a valid list of sources
|
||||
if (empty($sources) || !is_array($sources))
|
||||
{
|
||||
$sources = array();
|
||||
}
|
||||
|
||||
$defaultSources = array('lts' => true, 'sts' => true, 'test' => true, 'custom' => '');
|
||||
|
||||
$sources = array_merge($defaultSources, $sources);
|
||||
|
||||
// Use the current JVERSION if none is specified
|
||||
if (empty($jVersion))
|
||||
{
|
||||
$jVersion = JVERSION;
|
||||
}
|
||||
|
||||
// Get the current branch' min/max versions
|
||||
$versionParts = explode('.', $jVersion, 4);
|
||||
$currentMinVersion = $versionParts[0] . '.' . $versionParts[1];
|
||||
$currentMaxVersion = $versionParts[0] . '.' . $versionParts[1] . '.9999';
|
||||
|
||||
|
||||
// Retrieve all updates
|
||||
$allUpdates = array();
|
||||
|
||||
foreach ($sources as $source => $value)
|
||||
{
|
||||
if (($value === false) || empty($value))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
switch ($source)
|
||||
{
|
||||
case 'lts':
|
||||
$url = self::$lts_url;
|
||||
break;
|
||||
|
||||
case 'sts':
|
||||
$url = self::$sts_url;
|
||||
break;
|
||||
|
||||
case 'test':
|
||||
$url = self::$test_url;
|
||||
break;
|
||||
|
||||
default:
|
||||
case 'custom':
|
||||
$url = $value;
|
||||
break;
|
||||
}
|
||||
|
||||
$url = $this->getUpdateSourceFromCollection($url, $jVersion);
|
||||
|
||||
if (!empty($url))
|
||||
{
|
||||
$updates = $this->getUpdatesFromExtension($url);
|
||||
|
||||
if (!empty($updates))
|
||||
{
|
||||
$applicableUpdates = $this->filterApplicableUpdates($updates, $jVersion);
|
||||
|
||||
if (!empty($applicableUpdates))
|
||||
{
|
||||
$allUpdates = array_merge($allUpdates, $applicableUpdates);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$ret = array(
|
||||
// Currently installed version (used to reinstall, if available)
|
||||
'installed' => array(
|
||||
'version' => '',
|
||||
'package' => '',
|
||||
'infourl' => '',
|
||||
),
|
||||
// Current branch
|
||||
'current' => array(
|
||||
'version' => '',
|
||||
'package' => '',
|
||||
'infourl' => '',
|
||||
),
|
||||
// Upgrade to STS release
|
||||
'sts' => array(
|
||||
'version' => '',
|
||||
'package' => '',
|
||||
'infourl' => '',
|
||||
),
|
||||
// Upgrade to LTS release
|
||||
'lts' => array(
|
||||
'version' => '',
|
||||
'package' => '',
|
||||
'infourl' => '',
|
||||
),
|
||||
// Upgrade to LTS release
|
||||
'test' => array(
|
||||
'version' => '',
|
||||
'package' => '',
|
||||
'infourl' => '',
|
||||
),
|
||||
);
|
||||
|
||||
foreach ($allUpdates as $update)
|
||||
{
|
||||
$sections = array();
|
||||
|
||||
if ($update['upgrade'] == 'current')
|
||||
{
|
||||
$sections[0] = 'installed';
|
||||
}
|
||||
elseif (version_compare($update['version'], $currentMinVersion, 'ge') && version_compare($update['version'], $currentMaxVersion, 'le'))
|
||||
{
|
||||
$sections[0] = 'current';
|
||||
}
|
||||
else
|
||||
{
|
||||
$sections[0] = '';
|
||||
}
|
||||
|
||||
$sections[1] = $update['lts'] ? 'lts' : 'sts';
|
||||
|
||||
if ($update['testing'])
|
||||
{
|
||||
$sections = array('test');
|
||||
}
|
||||
|
||||
foreach ($sections as $section)
|
||||
{
|
||||
if (empty($section))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$existingVersionForSection = $ret[ $section ]['version'];
|
||||
|
||||
if (empty($existingVersionForSection))
|
||||
{
|
||||
$existingVersionForSection = '0.0.0';
|
||||
}
|
||||
|
||||
if (version_compare($update['version'], $existingVersionForSection, 'ge'))
|
||||
{
|
||||
$ret[ $section ]['version'] = $update['version'];
|
||||
$ret[ $section ]['package'] = $update['downloads'][0]['url'];
|
||||
$ret[ $section ]['infourl'] = $update['infourl']['url'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Catch the case when the latest current branch version is the installed version (up to date site)
|
||||
if (empty($ret['current']['version']) && !empty($ret['installed']['version']))
|
||||
{
|
||||
$ret['current'] = $ret['installed'];
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
}
|
||||
1206
libraries/f0f/utils/update/update.php
Normal file
1206
libraries/f0f/utils/update/update.php
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user