Initial Commit

This commit is contained in:
David Stone
2024-11-30 18:24:12 -07:00
commit e8f7955c1c
5432 changed files with 1397750 additions and 0 deletions

View File

@ -0,0 +1,319 @@
<?php
namespace WP_Ultimo\Dependencies\Arrch;
/**
* Arrch
*
* @copyright 2012 - 2013
* @author Michael Giuliana
* @link http://rpnzl.com
* @version 1.1.0
* @since 2013/5
* @license MIT License - http://opensource.org/licenses/MIT
*
* A small library of array search and sort methods. Perhaps most
* useful paired with a simple, flat-file storage system, Arrch
* allows psuedo-queries of large arrays.
*
*/
class Arrch
{
/**
* @var arr $defaults The find() method default values.
*/
public static $defaults = array('where' => array(), 'limit' => 0, 'offset' => 0, 'sort_key' => null, 'sort_order' => 'ASC');
/**
* @var str $key_split The string to split keys when checking a deep multidimensional array value.
*/
public static $key_split = '.';
/**
* @var arr $operators A list of valid operators for comparisons.
*/
public static $operators = array('==', '===', '!=', '!==', '>', '<', '>=', '<=', '~', '!~');
/**
* This method combines the functionality of the where() and sort() methods,
* with additional limit and offset parameters. Returns an array of matching
* array items. Will only sort if a sort key is set.
*
* @param arr $data The array of objects or associative arrays to search.
* @param arr $options The query options, see Arrch::$defaults.
* @param misc $key An item's key or index value.
* @return arr The result array.
*/
public static function find(array $data, array $options = array(), $key = 'all')
{
$options = static::merge(static::$defaults, $options);
$data = static::where($data, $options['where']);
$data = $options['sort_key'] ? static::sort($data, $options['sort_key'], $options['sort_order']) : $data;
$data = \array_slice($data, $options['offset'], $options['limit'] == 0 ? null : $options['limit'], \true);
switch ($key) {
case 'all':
return $data;
break;
case 'first':
return \array_shift($data);
break;
case 'last':
return \array_pop($data);
break;
default:
return isset($data[$key]) ? $data[$key] : null;
break;
}
}
/**
* Sorts an array of objects by the specified key.
*
* @param arr $data The array of objects or associative arrays to sort.
* @param str $key The object key or key path to use in sort evaluation.
* @param str $order ASC or DESC.
* @return arr The result array.
*/
public static function sort(array $data, $key, $order = null)
{
\uasort($data, function ($a, $b) use($key) {
$a_val = Arrch::extractValues($a, $key);
$b_val = Arrch::extractValues($b, $key);
// Strings
if (\is_string($a_val) && \is_string($b_val)) {
return \strcasecmp($a_val, $b_val);
} else {
if ($a_val == $b_val) {
return 0;
} else {
return $a_val > $b_val ? 1 : -1;
}
}
});
if (\strtoupper($order ?: Arrch::$defaults['sort_order']) == 'DESC') {
$data = \array_reverse($data, \true);
}
return $data;
}
/**
* Evaluates an object based on an array of conditions
* passed into the function, formatted like so...
*
* array('city', 'arlington');
* array('city', '!=', 'arlington');
*
* @param arr $data The array of objects or associative arrays to search.
* @param arr $conditions The conditions to evaluate against.
* @return arr The result array.
*/
public static function where(array $data, array $conditions = array())
{
foreach ($conditions as $condition) {
if (!\is_array($condition)) {
\trigger_error('Condition ' . \var_export($condition, \true) . ' must be an array!', \E_USER_ERROR);
} else {
\array_map(function ($item, $key) use(&$data, $condition) {
$return = 0;
$operator = \array_key_exists(2, $condition) ? $condition[1] : '===';
$search_value = \array_key_exists(2, $condition) ? $condition[2] : $condition[1];
if (\is_array($condition[0])) {
// array of keys
if (\is_array($search_value)) {
// array of values
foreach ($condition[0] as $prop) {
$value = Arrch::extractValues($item, $prop);
foreach ($search_value as $query_val) {
$return += Arrch::compare(array($value, $operator, $query_val)) ? 1 : 0;
}
}
} else {
// single value
foreach ($condition[0] as $prop) {
$value = Arrch::extractValues($item, $prop);
$return += Arrch::compare(array($value, $operator, $search_value)) ? 1 : 0;
}
}
} elseif (\is_array($search_value)) {
// single key, array of query values
$value = Arrch::extractValues($item, $condition[0]);
$c = 0;
foreach ($search_value as $query_val) {
$c += Arrch::compare(array($value, $operator, $query_val)) ? 1 : 0;
}
// Negate options that don't pass all negative assertions
if (\in_array($operator, array('!==', '!=', '!~')) && $c < \count($search_value)) {
$c = 0;
}
$return += $c;
} else {
// single key, single value
$value = Arrch::extractValues($item, $condition[0]);
$return = Arrch::compare(array($value, $operator, $search_value));
}
// Unset
if (!$return) {
unset($data[$key]);
}
}, $data, \array_keys($data));
}
}
return $data;
}
/**
* Finds the requested value in a multidimensional array or an object and
* returns it.
*
* @param mixed $item The item containing the value.
* @param str $key The key or key path to the desired value.
* @return mixed The found value or null.
*/
public static function extractValues($item, $key)
{
// Array of results
$results = array();
// Cast as array if object
$item = \is_object($item) ? (array) $item : $item;
// Array of keys
$keys = \strstr($key, static::$key_split) ? \explode(static::$key_split, $key) : array($key);
$keys = \array_filter($keys);
// Key count
$key_count = \count($keys);
// key count > 0
if ($key_count > 0) {
$curr_key = \array_shift($keys);
$arrayValues = \array_values($item);
$child = \is_array($item) ? \array_shift($arrayValues) : null;
// it's an array
if (\is_array($item)) {
// Clean non-ASCII keys
foreach ($item as $k => $v) {
$sanitized = \preg_replace('/[^(\\x20-\\x7F)]*/', '', $k);
if ($sanitized !== $k) {
$item[$sanitized] = $v;
unset($item[$k]);
}
}
// it has the keys
if (\array_key_exists($curr_key, $item)) {
$results = \array_merge($results, static::extractValues($item[$curr_key], \implode(static::$key_split, $keys)));
// else it's child has the key
} elseif (\is_array($child) || \is_object($child)) {
if (\array_key_exists($curr_key, $child)) {
foreach ($item as $child) {
$results = \array_merge($results, static::extractValues($child, $key));
}
}
}
}
// no key
} else {
$results[] = $item;
}
return $results;
}
/**
* Runs comparison operations on an array of values formatted like so...
*
* e.g. array($value, $operator, $value)
*
* Returns true or false depending on the outcome of the requested
* comparative operation (<, >, =, etc..).
*
* @param arr $array The comparison array.
* @return bool Whether the operation evaluates to true or false.
*/
public static function compare(array $array)
{
$return = 0;
foreach ($array[0] as $value) {
if ($array[1] === '~') {
// Check if variables loosely match right away
if ($value != $array[2]) {
if (\is_string($value) && \is_string($array[2])) {
$return += \stristr($value, $array[2]) ? 1 : 0;
} elseif (\is_null($value) && \is_null($array[2])) {
$return += 1;
} elseif (\is_array($value) || \is_object($value)) {
$return += \stristr(\var_export($value, \true), $array[2]) ? 1 : 0;
}
} else {
$return += 1;
}
} elseif ($array[1] === '!~') {
if (\is_string($value) && \is_string($array[2])) {
$return += !\stristr($value, $array[2]) ? 1 : 0;
} elseif (\is_null($value) && \is_null($array[2])) {
$return += 0;
} elseif (\is_array($value) || \is_object($value)) {
$return += !\stristr(\var_export($value, \true), $array[2]) ? 1 : 0;
}
} else {
if (\is_array($value) || \is_object($value)) {
\trigger_error('Cannot compare object of type ' . \strtoupper(\gettype($value)) . ' with the "' . $array[1] . '" operator.', \E_USER_WARNING);
}
if (!\in_array($array[1], static::$operators)) {
\trigger_error('Operator "' . $array[1] . '" is not valid.', \E_USER_ERROR);
} else {
switch ($array[1]) {
case '==':
$return += $value == $array[2] ? 1 : 0;
break;
case '===':
$return += $value === $array[2] ? 1 : 0;
break;
case '!=':
$return += $value != $array[2] ? 1 : 0;
break;
case '!==':
$return += $value !== $array[2] ? 1 : 0;
break;
case '>':
$return += $value > $array[2] ? 1 : 0;
break;
case '<':
$return += $value < $array[2] ? 1 : 0;
break;
case '>=':
$return += $value >= $array[2] ? 1 : 0;
break;
case '<=':
$return += $value <= $array[2] ? 1 : 0;
break;
}
}
}
}
return $return;
}
/**
* Recursive array merge for options arrays to eliminate duplicates
* in a smart manner using a variable number of array arguments.
*
* @param arr Various options arrays.
* @return arr The merged array.
*/
public static function merge()
{
$args = \func_get_args();
$where = array();
$options = array();
foreach ($args as $o) {
$options = \array_merge($options, $o);
if (isset($o['where'])) {
if (\count($where) === 0) {
$where = $o['where'];
} else {
foreach ($o['where'] as $oc) {
$matches = 0;
foreach ($where as $wc) {
if ($oc == $wc) {
$matches += 1;
}
}
if ($matches === 0) {
$where[] = $oc;
}
}
}
}
}
$options['where'] = $where;
return $options;
}
}