File: /home/dreamlan/public_html/wp-content/plugins/admin-menu-editor/includes/ame-collections.php
<?php
namespace YahnisElsts\AdminMenuEditor\Collections;
use YahnisElsts\AdminMenuEditor\Options\Option;
interface IterableOps {
public function flatMap($callback): self;
public function toArray(): array;
}
class ArrayWrapper implements \IteratorAggregate, \Countable, IterableOps {
private array $array;
public function __construct(array $array) {
$this->array = $array;
}
public function toArray(): array {
return $this->array;
}
public function getIterator(): \Traversable {
return new \ArrayIterator($this->array);
}
/**
* Utility method that simply returns the value passed to it.
*
* @template V
* @param V $value
* @return V Returns $value.
*/
public static function identity($value) {
return $value;
}
public function get($key): Option {
if ( array_key_exists($key, $this->array) ) {
return Option::some($this->array[$key]);
} else {
return Option::none();
}
}
/**
* Creates a new collection by applying the callback to each element in the array.
*
* Note: Unlike array_map(), the callback receives both the value and the key as arguments.
*
* @param callable(mixed $value, int|string $key):mixed $callback
* @return self
*/
public function map(callable $callback): self {
$mapped = [];
foreach ($this->array as $key => $value) {
$mapped[$key] = $callback($value, $key);
}
return new ArrayWrapper($mapped);
}
/**
* Creates a new array by applying the callback to each value in the array.
*
* The callback receives only the value as an argument. Useful for compatibility with array_map()
* and built-in functions.
*
* @param callable(mixed $value):mixed $callback
* @return self
*/
public function mapValues(callable $callback): self {
return new ArrayWrapper(array_map($callback, $this->array));
}
/**
* @template R
* @param callable(mixed $value):IterableOps<R> $callback
* @return ArrayWrapper
*/
public function flatMap($callback = [self::class, 'identity']): ArrayWrapper {
$results = [];
foreach ($this->array as $value) {
$mapped = $callback($value);
if ( is_iterable($mapped) ) {
foreach ($mapped as $item) {
$results[] = $item;
}
} else {
throw new \InvalidArgumentException('Callback must return an iterable.');
}
}
return new ArrayWrapper($results);
}
/**
* Similar to flatMap(), but preserves keys, and the callback receives both the value and
* the key as arguments.
*
* If the callback returns multiple items, only the last one will be kept, and it will be
* associated with the original key.
*
* @param callable(mixed $value, int|string $key):IterableOps<mixed> $callback
* @return ArrayWrapper
*/
public function flatMapWithKeys(callable $callback): ArrayWrapper {
$results = [];
foreach ($this->array as $key => $value) {
$mapped = $callback($value, $key);
if ( is_iterable($mapped) ) {
foreach ($mapped as $item) {
$results[$key] = $item;
}
} else {
throw new \InvalidArgumentException('Callback must return an iterable.');
}
}
return new ArrayWrapper($results);
}
public function intersect($other): self {
if ( $other instanceof ArrayWrapper ) {
$otherArray = $other->toArray();
} else if ( is_array($other) ) {
$otherArray = $other;
} else {
throw new \InvalidArgumentException('Argument must be an array or ArrayWrapper.');
}
$intersected = array_intersect($this->array, $otherArray);
return new ArrayWrapper($intersected);
}
public function diff($other): self {
if ( $other instanceof ArrayWrapper ) {
$otherArray = $other->toArray();
} else if ( is_array($other) ) {
$otherArray = $other;
} else {
throw new \InvalidArgumentException('Argument must be an array or ArrayWrapper.');
}
$diffed = array_diff($this->array, $otherArray);
return new ArrayWrapper($diffed);
}
/**
* Like diff(), but uses strict comparison (===) when comparing values.
*
* @param array|ArrayWrapper $other
* @return self
*/
public function strictDiff($other): self {
if ( $other instanceof ArrayWrapper ) {
$otherArray = $other->toArray();
} else if ( is_array($other) ) {
$otherArray = $other;
} else {
throw new \InvalidArgumentException('Argument must be an array or ArrayWrapper.');
}
$diffed = array_udiff($this->array, $otherArray, function ($a, $b) {
if ( $a === $b ) {
return 0;
}
return strcmp((string)$a, (string)$b);
});
return new ArrayWrapper($diffed);
}
/**
* Filter the array using the provided callback.
*
* Array keys are preserved.
*
* Note: Unfortunately, the argument cannot be type-hinted as callable here because we want a default
* "identity" callback and PHP doesn't seem to have a reasonable way to express that. Arrays like
* ['ClassName', 'method'] and strings like 'ClassName::method' are valid callables, but they get
* rejected as syntax errors.
*
* @param callable $callback
* @return self
*/
public function filterValues($callback = [self::class, 'identity']): self {
$filtered = array_filter($this->array, $callback);
return new ArrayWrapper($filtered);
}
public function rejectValues(callable $callback): self {
$filtered = array_filter($this->array, function ($item) use ($callback) {
return !$callback($item);
});
return new ArrayWrapper($filtered);
}
public function rejectKeys(callable $callback): self {
$filtered = array_filter($this->array, function ($key) use ($callback) {
return !$callback($key);
}, ARRAY_FILTER_USE_KEY);
return new ArrayWrapper($filtered);
}
public function implode($separator): string {
return implode($separator, $this->array);
}
public function pick($keys): self {
if ( $keys instanceof ArrayWrapper ) {
$keys = $keys->toArray();
}
$picked = [];
foreach ($keys as $key) {
if ( array_key_exists($key, $this->array) ) {
$picked[$key] = $this->array[$key];
}
}
return new ArrayWrapper($picked);
}
/**
* Creates a new ArrayWrapper where keys that exist in $source are added to the current array
* only if they do not already exist in the current array.
*
* This is similar to array_merge() with $source as the first argument and the current
* array as the second.
*
* @param array|ArrayWrapper $source
* @return self
*/
public function defaults($source): self {
if ( $source instanceof ArrayWrapper ) {
$source = $source->toArray();
}
$merged = $this->array + $source;
return new ArrayWrapper($merged);
}
public function keys(): self {
return new ArrayWrapper(array_keys($this->array));
}
public function contains($value): bool {
return in_array($value, $this->array, true);
}
public function containsKey($key): bool {
return array_key_exists($key, $this->array);
}
/**
* @param iterable $keys
* @return bool
*/
public function containsAllKeys(iterable $keys): bool {
foreach ($keys as $key) {
if ( !array_key_exists($key, $this->array) ) {
return false;
}
}
return true;
}
public function headOption() {
if ( empty($this->array) ) {
return Option::none();
} else {
$firstKey = array_key_first($this->array);
return Option::some($this->array[$firstKey]);
}
}
public function headOrNull() {
if ( empty($this->array) ) {
return null;
} else {
$firstKey = array_key_first($this->array); //PHP 7.3+ or WP 5.9+
return $this->array[$firstKey];
}
}
/**
* @template A Accumulator type
* @template T Collection element type
*
* @param A $accumulator
* @param callable(A,T):A $callback
* @return A
*/
public function foldLeft($accumulator, callable $callback) {
foreach ($this->array as $value) {
$accumulator = $callback($accumulator, $value);
}
return $accumulator;
}
/**
* Computes the Cartesian product of this array and another array or iterable.
*
* Each element of the resulting array is a two-element array, where the first element
* comes from this array and the second element comes from the other array.
*
* @param array|ArrayWrapper|iterable $other
* @return ArrayWrapper
*/
public function cartesianProduct($other): self {
$result = [];
foreach ($this->array as $value1) {
foreach ($other as $value2) {
$result[] = [$value1, $value2];
}
}
return new ArrayWrapper($result);
}
public function isEmpty(): bool {
return empty($this->array);
}
public function count(): int {
return count($this->array);
}
//region Mutating methods
public function push(...$items): self {
foreach ($items as $item) {
$this->array[] = $item;
}
return $this;
}
public function pop() {
return array_pop($this->array);
}
//endregion
}
function w($value): ArrayWrapper {
if ( is_array($value) ) {
return new ArrayWrapper($value);
} else if ( is_iterable($value) ) {
return new ArrayWrapper(iterator_to_array($value));
} else {
throw new \InvalidArgumentException('Value must be an array or iterable.');
}
}