primo commit
This commit is contained in:
690
libraries/vendor/joomla/utilities/src/ArrayHelper.php
vendored
Normal file
690
libraries/vendor/joomla/utilities/src/ArrayHelper.php
vendored
Normal file
@ -0,0 +1,690 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Part of the Joomla Framework Utilities Package
|
||||
*
|
||||
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
|
||||
* @license GNU General Public License version 2 or later; see LICENSE
|
||||
*/
|
||||
|
||||
namespace Joomla\Utilities;
|
||||
|
||||
use Joomla\String\StringHelper;
|
||||
|
||||
/**
|
||||
* ArrayHelper is an array utility class for doing all sorts of odds and ends with arrays.
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
final class ArrayHelper
|
||||
{
|
||||
/**
|
||||
* Private constructor to prevent instantiation of this class
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
private function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to convert array to integer values
|
||||
*
|
||||
* @param array $array The source array to convert
|
||||
* @param int|array $default A default value to assign if $array is not an array
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
public static function toInteger($array, $default = null)
|
||||
{
|
||||
if (\is_array($array)) {
|
||||
return array_map('intval', $array);
|
||||
}
|
||||
|
||||
if ($default === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (\is_array($default)) {
|
||||
return static::toInteger($default, null);
|
||||
}
|
||||
|
||||
return [(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
|
||||
* @param boolean $recursive Convert also any array inside the main array
|
||||
*
|
||||
* @return object
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
public static function toObject(array $array, $class = 'stdClass', $recursive = true)
|
||||
{
|
||||
$obj = new $class();
|
||||
|
||||
foreach ($array as $k => $v) {
|
||||
if ($recursive && \is_array($v)) {
|
||||
$obj->$k = static::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 $innerGlue The glue (optional, defaults to '=') between the key and the value.
|
||||
* @param string $outerGlue The glue (optional, defaults to ' ') between array elements.
|
||||
* @param boolean $keepOuterKey True if final key should be kept.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
public static function toString(array $array, string $innerGlue = '=', string $outerGlue = ' ', $keepOuterKey = false)
|
||||
{
|
||||
$output = [];
|
||||
|
||||
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[] = static::toString($item, $innerGlue, $outerGlue, $keepOuterKey);
|
||||
} else {
|
||||
$output[] = $key . $innerGlue . '"' . $item . '"';
|
||||
}
|
||||
}
|
||||
|
||||
return implode($outerGlue, $output);
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function to map an object to an array
|
||||
*
|
||||
* @param object $source 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
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
public static function fromObject($source, $recurse = true, $regex = null)
|
||||
{
|
||||
if (\is_object($source) || \is_array($source)) {
|
||||
return self::arrayFromObject($source, $recurse, $regex);
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
private static function arrayFromObject($item, $recurse, $regex)
|
||||
{
|
||||
if (\is_object($item)) {
|
||||
$result = [];
|
||||
|
||||
foreach (get_object_vars($item) as $k => $v) {
|
||||
if (!$regex || preg_match($regex, $k)) {
|
||||
if ($recurse) {
|
||||
$result[$k] = self::arrayFromObject($v, $recurse, $regex);
|
||||
} else {
|
||||
$result[$k] = $v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
if (\is_array($item)) {
|
||||
$result = [];
|
||||
|
||||
foreach ($item as $k => $v) {
|
||||
$result[$k] = self::arrayFromObject($v, $recurse, $regex);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
return $item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a column to an array of arrays or objects
|
||||
*
|
||||
* @param array $array The source array
|
||||
* @param array $column The array to be used as new column
|
||||
* @param string $colName The index of the new column or name of the new object property
|
||||
* @param string $keyCol The index of the column or name of object property to be used for mapping with the new column
|
||||
*
|
||||
* @return array An array with the new column added to the source array
|
||||
*
|
||||
* @since 1.5.0
|
||||
* @see https://www.php.net/manual/en/language.types.array.php
|
||||
*/
|
||||
public static function addColumn(array $array, array $column, $colName, $keyCol = null)
|
||||
{
|
||||
$result = [];
|
||||
|
||||
foreach ($array as $i => $item) {
|
||||
$value = null;
|
||||
|
||||
if (!isset($keyCol)) {
|
||||
$value = static::getValue($column, $i);
|
||||
} else {
|
||||
// Convert object to array
|
||||
$subject = \is_object($item) ? static::fromObject($item) : $item;
|
||||
|
||||
if (isset($subject[$keyCol]) && is_scalar($subject[$keyCol])) {
|
||||
$value = static::getValue($column, $subject[$keyCol]);
|
||||
}
|
||||
}
|
||||
|
||||
// Add the column
|
||||
if (\is_object($item)) {
|
||||
if (isset($colName)) {
|
||||
$item->$colName = $value;
|
||||
}
|
||||
} else {
|
||||
if (isset($colName)) {
|
||||
$item[$colName] = $value;
|
||||
} else {
|
||||
$item[] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
$result[$i] = $item;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a column from an array of arrays or objects
|
||||
*
|
||||
* @param array $array The source array
|
||||
* @param string $colName The index of the column or name of object property to be removed
|
||||
*
|
||||
* @return array Column of values from the source array
|
||||
*
|
||||
* @since 1.5.0
|
||||
* @see https://www.php.net/manual/en/language.types.array.php
|
||||
*/
|
||||
public static function dropColumn(array $array, $colName)
|
||||
{
|
||||
$result = [];
|
||||
|
||||
foreach ($array as $i => $item) {
|
||||
if (\is_object($item) && isset($item->$colName)) {
|
||||
unset($item->$colName);
|
||||
} elseif (\is_array($item) && isset($item[$colName])) {
|
||||
unset($item[$colName]);
|
||||
}
|
||||
|
||||
$result[$i] = $item;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts a column from an array of arrays or objects
|
||||
*
|
||||
* @param array $array The source array
|
||||
* @param string $valueCol The index of the column or name of object property to be used as value
|
||||
* It may also be NULL to return complete arrays or objects (this is
|
||||
* useful together with <var>$keyCol</var> to reindex the array).
|
||||
* @param string $keyCol The index of the column or name of object property to be used as key
|
||||
*
|
||||
* @return array Column of values from the source array
|
||||
*
|
||||
* @since 1.0
|
||||
* @see https://www.php.net/manual/en/language.types.array.php
|
||||
* @see https://www.php.net/manual/en/function.array-column.php
|
||||
*/
|
||||
public static function getColumn(array $array, $valueCol, $keyCol = null)
|
||||
{
|
||||
return \array_reduce(
|
||||
$array,
|
||||
function ($result, $item) use ($keyCol, $valueCol) {
|
||||
$array = \is_object($item) ? get_object_vars($item) : $item;
|
||||
|
||||
if ($valueCol === null) {
|
||||
$value = $item;
|
||||
} else {
|
||||
if (!array_key_exists($valueCol, $array)) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
$value = $array[$valueCol];
|
||||
}
|
||||
|
||||
if ($keyCol !== null && \array_key_exists($keyCol, $array) && \is_scalar($array[$keyCol])) {
|
||||
$result[$array[$keyCol]] = $value;
|
||||
} else {
|
||||
$result[] = $value;
|
||||
}
|
||||
|
||||
return $result;
|
||||
},
|
||||
[]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function to return a value from a named array or a specified default
|
||||
*
|
||||
* @param array|\ArrayAccess $array A named array or object that implements ArrayAccess
|
||||
* @param string $name The key to search for (this can be an array index or a dot separated key sequence as in Registry)
|
||||
* @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
|
||||
*
|
||||
* @since 1.0
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public static function getValue($array, $name, $default = null, string $type = '')
|
||||
{
|
||||
if (!\is_array($array) && !($array instanceof \ArrayAccess)) {
|
||||
throw new \InvalidArgumentException('The object must be an array or an object that implements ArrayAccess');
|
||||
}
|
||||
|
||||
$result = null;
|
||||
|
||||
if (isset($array[$name])) {
|
||||
$result = $array[$name];
|
||||
} elseif (strpos($name, '.')) {
|
||||
list($name, $subset) = explode('.', $name, 2);
|
||||
|
||||
if (isset($array[$name]) && \is_array($array[$name])) {
|
||||
return static::getValue($array[$name], $subset, $default, $type);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle the default case
|
||||
if ($result === null) {
|
||||
$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 = [$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 = ArrayHelper::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
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
public static function invert(array $array)
|
||||
{
|
||||
$return = [];
|
||||
|
||||
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
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
public static function pivot(array $source, $key = null)
|
||||
{
|
||||
$result = [];
|
||||
$counter = [];
|
||||
|
||||
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] = [
|
||||
$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 an array of keys 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
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
public static function sortObjects(array $a, $k, $direction = 1, $caseSensitive = true, $locale = false)
|
||||
{
|
||||
if (!\is_array($locale) || !\is_array($locale[0])) {
|
||||
$locale = [$locale];
|
||||
}
|
||||
|
||||
$sortCase = (array) $caseSensitive;
|
||||
$sortDirection = (array) $direction;
|
||||
$key = (array) $k;
|
||||
$sortLocale = $locale;
|
||||
|
||||
usort(
|
||||
$a,
|
||||
function ($a, $b) use ($sortCase, $sortDirection, $key, $sortLocale) {
|
||||
for ($i = 0, $count = \count($key); $i < $count; $i++) {
|
||||
if (isset($sortDirection[$i])) {
|
||||
$direction = $sortDirection[$i];
|
||||
}
|
||||
|
||||
if (isset($sortCase[$i])) {
|
||||
$caseSensitive = $sortCase[$i];
|
||||
}
|
||||
|
||||
if (isset($sortLocale[$i])) {
|
||||
$locale = $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 = StringHelper::strcmp($va, $vb, $locale);
|
||||
} else {
|
||||
$cmp = StringHelper::strcasecmp($va, $vb, $locale);
|
||||
}
|
||||
|
||||
if ($cmp > 0) {
|
||||
return $direction;
|
||||
}
|
||||
|
||||
if ($cmp < 0) {
|
||||
return -$direction;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
);
|
||||
|
||||
return $a;
|
||||
}
|
||||
|
||||
/**
|
||||
* Multidimensional array safe unique test
|
||||
*
|
||||
* @param array $array The array to make unique.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @see https://www.php.net/manual/en/function.array-unique.php
|
||||
* @since 1.0
|
||||
*/
|
||||
public static function arrayUnique(array $array)
|
||||
{
|
||||
$array = array_map('serialize', $array);
|
||||
$array = array_unique($array);
|
||||
$array = array_map('unserialize', $array);
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* An improved array_search that allows for partial matching of strings values in associative arrays.
|
||||
*
|
||||
* @param string $needle The text to search for within the array.
|
||||
* @param array $haystack Associative array to search in to find $needle.
|
||||
* @param boolean $caseSensitive True to search case sensitive, false otherwise.
|
||||
*
|
||||
* @return mixed Returns the matching array $key if found, otherwise false.
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
public static function arraySearch($needle, array $haystack, $caseSensitive = true)
|
||||
{
|
||||
foreach ($haystack as $key => $value) {
|
||||
$searchFunc = ($caseSensitive) ? 'strpos' : 'stripos';
|
||||
|
||||
if ($searchFunc($value, $needle) === 0) {
|
||||
return $key;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to recursively convert data to a one dimension array.
|
||||
*
|
||||
* @param array|object $array The array or object to convert.
|
||||
* @param string $separator The key separator.
|
||||
* @param string $prefix Last level key prefix.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @since 1.3.0
|
||||
*/
|
||||
public static function flatten($array, $separator = '.', $prefix = '')
|
||||
{
|
||||
if ($array instanceof \Traversable) {
|
||||
$array = iterator_to_array($array);
|
||||
} elseif (\is_object($array)) {
|
||||
$array = get_object_vars($array);
|
||||
}
|
||||
|
||||
$result = [];
|
||||
|
||||
foreach ($array as $k => $v) {
|
||||
$key = $prefix ? $prefix . $separator . $k : $k;
|
||||
|
||||
if (\is_object($v) || \is_array($v)) {
|
||||
$result[] = static::flatten($v, $separator, $key);
|
||||
} else {
|
||||
$result[] = [$key => $v];
|
||||
}
|
||||
}
|
||||
|
||||
return array_merge(...$result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge array recursively.
|
||||
*
|
||||
* @param array ...$args Array list to be merged.
|
||||
*
|
||||
* @return array Merged array.
|
||||
*
|
||||
* @since 2.0.0
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public static function mergeRecursive(...$args): array
|
||||
{
|
||||
$result = [];
|
||||
|
||||
foreach ($args as $i => $array) {
|
||||
if (!\is_array($array)) {
|
||||
throw new \InvalidArgumentException(sprintf('Argument #%d is not an array.', $i + 2));
|
||||
}
|
||||
|
||||
foreach ($array as $key => &$value) {
|
||||
if (\is_array($value) && isset($result[$key]) && \is_array($result[$key])) {
|
||||
$result[$key] = static::mergeRecursive($result [$key], $value);
|
||||
} else {
|
||||
$result[$key] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
514
libraries/vendor/joomla/utilities/src/IpHelper.php
vendored
Normal file
514
libraries/vendor/joomla/utilities/src/IpHelper.php
vendored
Normal file
@ -0,0 +1,514 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Part of the Joomla Framework Utilities Package
|
||||
*
|
||||
* @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
|
||||
* @note This file has been modified by the Joomla! Project and no longer reflects the original work of its author.
|
||||
*/
|
||||
|
||||
namespace Joomla\Utilities;
|
||||
|
||||
/**
|
||||
* IpHelper is a utility class for processing IP addresses
|
||||
*
|
||||
* @since 1.6.0
|
||||
*/
|
||||
final class IpHelper
|
||||
{
|
||||
/**
|
||||
* The IP address of the current visitor
|
||||
*
|
||||
* @var string
|
||||
* @since 1.6.0
|
||||
*/
|
||||
private static $ip = null;
|
||||
|
||||
/**
|
||||
* Should I allow IP overrides through X-Forwarded-For or Client-Ip HTTP headers?
|
||||
*
|
||||
* @var boolean
|
||||
* @since 1.6.0
|
||||
* @note The default value is false in version 2.0+
|
||||
*/
|
||||
private static $allowIpOverrides = false;
|
||||
|
||||
/**
|
||||
* Private constructor to prevent instantiation of this class
|
||||
*
|
||||
* @since 1.6.0
|
||||
*/
|
||||
private function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current visitor's IP address
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @since 1.6.0
|
||||
*/
|
||||
public static function getIp()
|
||||
{
|
||||
if (self::$ip === null) {
|
||||
$ip = static::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 self::$ip;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the IP address of the current visitor
|
||||
*
|
||||
* @param string $ip The visitor's IP address
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 1.6.0
|
||||
*/
|
||||
public static function setIp($ip)
|
||||
{
|
||||
self::$ip = $ip;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is it an IPv6 IP address?
|
||||
*
|
||||
* @param string $ip An IPv4 or IPv6 address
|
||||
*
|
||||
* @return boolean
|
||||
*
|
||||
* @since 1.6.0
|
||||
*/
|
||||
public static function isIPv6($ip)
|
||||
{
|
||||
return strpos($ip, ':') !== 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 boolean
|
||||
*
|
||||
* @since 1.6.0
|
||||
*/
|
||||
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('trim', $ipTable);
|
||||
} else {
|
||||
$ipTable = trim($ipTable);
|
||||
$ipTable = [$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 = static::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 && (!static::isIPv6($from) || !static::isIPv6($to))) {
|
||||
// Do not apply IPv4 filtering on an IPv6 address
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$ipv6 && (static::isIPv6($from) || static::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) = [$to, $from];
|
||||
}
|
||||
|
||||
if (($myIP >= $from) && ($myIP <= $to)) {
|
||||
return true;
|
||||
}
|
||||
} elseif (strstr($ipExpression, '/')) {
|
||||
// Netmask or CIDR provided
|
||||
$binaryip = static::inetToBits($myIP);
|
||||
|
||||
list($net, $maskbits) = explode('/', $ipExpression, 2);
|
||||
|
||||
if ($ipv6 && !static::isIPv6($net)) {
|
||||
// Do not apply IPv4 filtering on an IPv6 address
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$ipv6 && static::isIPv6($net)) {
|
||||
// Do not apply IPv6 filtering on an IPv4 address
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($ipv6 && strstr($maskbits, ':')) {
|
||||
// Perform an IPv6 CIDR check
|
||||
if (static::checkIPv6CIDR($myIP, $ipExpression)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If we didn't match it proceed to the next expression
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$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
|
||||
$expectedNumberOfBits = $ipv6 ? 128 : 24;
|
||||
$binarynet = str_pad(static::inetToBits($net), $expectedNumberOfBits, '0', STR_PAD_RIGHT);
|
||||
|
||||
// Check the corresponding bits of the IP and the network
|
||||
$ipNetBits = substr($binaryip, 0, $maskbits);
|
||||
$netBits = substr($binarynet, 0, $maskbits);
|
||||
|
||||
if ($ipNetBits === $netBits) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
// IPv6: Only single IPs are supported
|
||||
if ($ipv6) {
|
||||
$ipExpression = trim($ipExpression);
|
||||
|
||||
if (!static::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 = static::inetToBits($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
|
||||
$expectedNumberOfBits = $ipv6 ? 128 : 24;
|
||||
$binarynet = str_pad(static::inetToBits($net), $expectedNumberOfBits, '0', STR_PAD_RIGHT);
|
||||
|
||||
// Check the corresponding bits of the IP and the network
|
||||
$ipNetBits = substr($binaryip, 0, $maskbits);
|
||||
$netBits = substr($binarynet, 0, $maskbits);
|
||||
|
||||
if ($ipNetBits === $netBits) {
|
||||
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
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 1.6.0
|
||||
*/
|
||||
public static function workaroundIPIssues()
|
||||
{
|
||||
$ip = static::getIp();
|
||||
|
||||
if (isset($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] === $ip) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isset($_SERVER['REMOTE_ADDR'])) {
|
||||
$_SERVER['JOOMLA_REMOTE_ADDR'] = $_SERVER['REMOTE_ADDR'];
|
||||
} elseif (\function_exists('getenv')) {
|
||||
if (getenv('REMOTE_ADDR')) {
|
||||
$_SERVER['JOOMLA_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 boolean $newState True to allow the override
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 1.6.0
|
||||
*/
|
||||
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
|
||||
* - https://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
|
||||
*
|
||||
* @since 1.6.0
|
||||
*/
|
||||
protected static function detectAndCleanIP()
|
||||
{
|
||||
$ip = static::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_shift($ips);
|
||||
$ip = trim($ip);
|
||||
}
|
||||
} else {
|
||||
$ip = trim($ip);
|
||||
}
|
||||
|
||||
return $ip;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the visitor's IP address
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @since 1.6.0
|
||||
*/
|
||||
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 && isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
|
||||
return $_SERVER['HTTP_X_FORWARDED_FOR'];
|
||||
}
|
||||
|
||||
// Do we have a client-ip header (e.g. non-transparent proxy)?
|
||||
if (self::$allowIpOverrides && isset($_SERVER['HTTP_CLIENT_IP'])) {
|
||||
return $_SERVER['HTTP_CLIENT_IP'];
|
||||
}
|
||||
|
||||
// Normal, non-proxied server or server behind a transparent proxy
|
||||
if (isset($_SERVER['REMOTE_ADDR'])) {
|
||||
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
|
||||
*
|
||||
* @since 1.6.0
|
||||
*/
|
||||
protected static function inetToBits($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 boolean
|
||||
*
|
||||
* @since 1.6.0
|
||||
*/
|
||||
protected static function checkIPv6CIDR($ip, $cidrnet)
|
||||
{
|
||||
$ip = inet_pton($ip);
|
||||
$binaryip = static::inetToBits($ip);
|
||||
|
||||
list($net, $maskbits) = explode('/', $cidrnet);
|
||||
$net = inet_pton($net);
|
||||
$binarynet = static::inetToBits($net);
|
||||
|
||||
$ipNetBits = substr($binaryip, 0, $maskbits);
|
||||
$netBits = substr($binarynet, 0, $maskbits);
|
||||
|
||||
return $ipNetBits === $netBits;
|
||||
}
|
||||
}
|
||||
106
libraries/vendor/joomla/utilities/src/RegEx.php
vendored
Normal file
106
libraries/vendor/joomla/utilities/src/RegEx.php
vendored
Normal file
@ -0,0 +1,106 @@
|
||||
<?php
|
||||
/**
|
||||
* Part of the Joomla Framework Utilities Package
|
||||
*
|
||||
* @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved.
|
||||
* @license GNU General Public License version 2 or later; see LICENSE
|
||||
*/
|
||||
|
||||
namespace Joomla\Utilities;
|
||||
|
||||
/**
|
||||
* Utility class for building complex Regular Expressions
|
||||
*
|
||||
* @since __DEPLOY_VERSION__
|
||||
*/
|
||||
abstract class RegEx
|
||||
{
|
||||
/**
|
||||
* Match the Regular Expression
|
||||
*
|
||||
* @param string $regex The Regular Expression
|
||||
* @param string $subject The string to check
|
||||
*
|
||||
* @return array Captured values
|
||||
*/
|
||||
public static function match($regex, $subject)
|
||||
{
|
||||
$match = array();
|
||||
|
||||
preg_match($regex, $subject, $match);
|
||||
|
||||
return array_filter(
|
||||
$match,
|
||||
static function ($value, $key) {
|
||||
return !is_numeric($key) && !empty($value);
|
||||
},
|
||||
ARRAY_FILTER_USE_BOTH
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign a key to an expression.
|
||||
*
|
||||
* @param string $regex The Regular Expression to match
|
||||
* @param string $as The name of the component, used as index
|
||||
*
|
||||
* @return string The modified Regular Expression
|
||||
*/
|
||||
public static function capture($regex, $as = null)
|
||||
{
|
||||
return '(?P<' . $as . '>' . $regex . ')';
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a 'zero or one' quantifier to an expression.
|
||||
*
|
||||
* @param string $regex The Regular Expression to match
|
||||
*
|
||||
* @return string The modified Regular Expression
|
||||
*/
|
||||
public static function optional($regex)
|
||||
{
|
||||
return '(?:' . $regex . ')?';
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a 'one or more' quantifier to an expression.
|
||||
*
|
||||
* @param string $regex The Regular Expression to match
|
||||
*
|
||||
* @return string The modified Regular Expression
|
||||
*/
|
||||
public static function oneOrMore($regex)
|
||||
{
|
||||
return '(?:' . $regex . ')+';
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a 'zero or more' quantifier to an expression.
|
||||
*
|
||||
* @param string $regex The Regular Expression to match
|
||||
*
|
||||
* @return string The modified Regular Expression
|
||||
*/
|
||||
public static function noneOrMore($regex)
|
||||
{
|
||||
return '(?:' . $regex . ')*';
|
||||
}
|
||||
|
||||
/**
|
||||
* Define a list of alternative expressions.
|
||||
*
|
||||
* @param string|array $regexList A list of Regular Expressions to choose from
|
||||
*
|
||||
* @return string The modified Regular Expression
|
||||
*/
|
||||
public static function anyOf($regexList)
|
||||
{
|
||||
if (is_string($regexList))
|
||||
{
|
||||
$regexList = func_get_args();
|
||||
}
|
||||
|
||||
return '(?:' . implode('|', $regexList) . ')';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user