primo commit

This commit is contained in:
2024-12-17 17:34:10 +01:00
commit e650f8df99
16435 changed files with 2451012 additions and 0 deletions

View 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;
}
}

View 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;
}
}

View 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) . ')';
}
}