Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions src/kim/present/lib/arrayutils/ArrayUtils.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@
namespace kim\present\lib\arrayutils;

use ArrayObject;
use Closure;
use ReflectionException;
use ReflectionFunction;
use ReflectionMethod;
use Traversable;
use kim\present\lib\arrayutils\traits\ConcatTrait;
use kim\present\lib\arrayutils\traits\IterationTrait;
Expand All @@ -38,7 +42,9 @@
use kim\present\lib\arrayutils\traits\StackTrait;

use function is_array;
use function is_string;
use function iterator_to_array;
use function strpos;

/**
* Class ArrayUtils is provides a method to fancy manipulate an array
Expand All @@ -64,6 +70,9 @@ class ArrayUtils extends ArrayObject{
use SortTrait;
use StackTrait;

/** @var ArrayUtils|null Empty instance that is cloned to create results without calling the constructor */
private static $blank = null;

/** Creates a new, shallow-copied ArrayUtils instance from an iterable */
public function __construct(iterable $iterable, int $flags = 0, string $iteratorClass = "ArrayIterator"){
parent::__construct(is_array($iterable) ? $iterable : self::toArray($iterable), $flags, $iteratorClass);
Expand Down Expand Up @@ -123,6 +132,41 @@ public static function toArray(iterable $iterable) : array{
return iterator_to_array($iterable, true);
}

/**
* Creates an empty instance without running the constructor
*
* The userland constructor forwards its argument to ArrayObject, which makes ArrayObject copy the whole array.
* Filling a clone with exchangeArray() takes the array over without copying it.
*/
private static function blank() : ArrayUtils{
if(self::$blank === null){
self::$blank = new self([]);
}
return clone self::$blank;
}

/**
* Returns how many arguments the callback accepts (3 if it cannot be determined or is variadic)
* Iteration methods use this to pass only the arguments the callback declares,
* which lets them delegate to native functions such as array_map() and array_filter()
*/
private static function callbackArity(callable $callback) : int{
try{
if($callback instanceof Closure || (is_string($callback) && strpos($callback, "::") === false)){
$reflection = new ReflectionFunction($callback);
}elseif(is_array($callback)){
$reflection = new ReflectionMethod($callback[0], $callback[1]);
}elseif(is_string($callback)){
$reflection = new ReflectionMethod($callback);
}else{
$reflection = new ReflectionMethod($callback, "__invoke");
}
return $reflection->isVariadic() ? 3 : $reflection->getNumberOfParameters();
}catch(ReflectionException $_){
return 3;
}
}

/** Exchange the array for another one */
public function exchange(array $array) : ArrayUtils{
$this->exchangeArray($array);
Expand Down
46 changes: 38 additions & 8 deletions src/kim/present/lib/arrayutils/traits/ConcatTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
*
* @noinspection PhpUnused
* @noinspection PhpDocSignatureIsNotCompleteInspection
* @generated by tools/generate-traits.py - do not edit by hand, edit the spec in that script instead
*/

declare(strict_types=1);
Expand All @@ -35,7 +36,7 @@
/**
* Methods that join arrays together (concat, merge, replace, flat)
*
* Requires ArrayUtils::toArray() and ArrayUtils::mapToArray() of the class using this trait
* Requires ArrayUtils::toArray(), ArrayUtils::mapToArray(), ArrayUtils::blank() and ArrayUtils::callbackArity() of the class using this trait
*/
trait ConcatTrait{
/**
Expand All @@ -59,7 +60,9 @@ public function concatAs(...$values) : array{
* Same as concat(), but operates on the given iterable and returns a new ArrayUtils
*/
public static function concatFrom(iterable $from, ...$values) : ArrayUtils{
return new self(array_merge((is_array($from) ? $from : self::toArray($from)), ...self::mapToArray($values)));
$utils = self::blank();
$utils->exchangeArray(array_merge((is_array($from) ? $from : self::toArray($from)), ...self::mapToArray($values)));
return $utils;
}

/**
Expand Down Expand Up @@ -90,7 +93,9 @@ public function concatSoftAs(...$values) : array{
* Same as concatSoft(), but operates on the given iterable and returns a new ArrayUtils
*/
public static function concatSoftFrom(iterable $from, ...$values) : ArrayUtils{
return new self(self::concatSoftFromAs($from, ...$values));
$utils = self::blank();
$utils->exchangeArray(self::concatSoftFromAs($from, ...$values));
return $utils;
}

/**
Expand Down Expand Up @@ -125,7 +130,9 @@ public function mergeAs(...$values) : array{
* Same as merge(), but operates on the given iterable and returns a new ArrayUtils
*/
public static function mergeFrom(iterable $from, ...$values) : ArrayUtils{
return new self(array_merge((is_array($from) ? $from : self::toArray($from)), ...self::mapToArray($values)));
$utils = self::blank();
$utils->exchangeArray(array_merge((is_array($from) ? $from : self::toArray($from)), ...self::mapToArray($values)));
return $utils;
}

/**
Expand Down Expand Up @@ -156,7 +163,9 @@ public function mergeSoftAs(...$values) : array{
* Same as mergeSoft(), but operates on the given iterable and returns a new ArrayUtils
*/
public static function mergeSoftFrom(iterable $from, ...$values) : ArrayUtils{
return new self(self::mergeSoftFromAs($from, ...$values));
$utils = self::blank();
$utils->exchangeArray(self::mergeSoftFromAs($from, ...$values));
return $utils;
}

/**
Expand Down Expand Up @@ -188,7 +197,9 @@ public function replaceAs(iterable ...$iterables) : array{
* Same as replace(), but operates on the given iterable and returns a new ArrayUtils
*/
public static function replaceFrom(iterable $from, iterable ...$iterables) : ArrayUtils{
return new self(array_replace_recursive((is_array($from) ? $from : self::toArray($from)), ...self::mapToArray($iterables)));
$utils = self::blank();
$utils->exchangeArray(array_replace_recursive((is_array($from) ? $from : self::toArray($from)), ...self::mapToArray($iterables)));
return $utils;
}

/**
Expand Down Expand Up @@ -219,7 +230,9 @@ public function flatAs(int $dept = 1) : array{
* Same as flat(), but operates on the given iterable and returns a new ArrayUtils
*/
public static function flatFrom(iterable $from, int $dept = 1) : ArrayUtils{
return new self(self::flatFromAs($from, $dept));
$utils = self::blank();
$utils->exchangeArray(self::flatFromAs($from, $dept));
return $utils;
}

/**
Expand Down Expand Up @@ -258,14 +271,31 @@ public function flatMapAs(callable $callback) : array{
* Same as flatMap(), but operates on the given iterable and returns a new ArrayUtils
*/
public static function flatMapFrom(iterable $from, callable $callback) : ArrayUtils{
return new self(self::flatMapFromAs($from, $callback));
$utils = self::blank();
$utils->exchangeArray(self::flatMapFromAs($from, $callback));
return $utils;
}

/**
* Same as flatMapFrom(), but returns a plain array
*/
public static function flatMapFromAs(iterable $from, callable $callback) : array{
$array = is_array($from) ? $from : self::toArray($from);
$arity = self::callbackArity($callback);
if($arity <= 1){
$parts = [];
foreach($array as $key => $value){
$parts[] = (array) $callback($value);
}
return $parts ? array_merge(...$parts) : [];
}
if($arity <= 2){
$parts = [];
foreach($array as $key => $value){
$parts[] = (array) $callback($value, $key);
}
return $parts ? array_merge(...$parts) : [];
}
$parts = [];
foreach($array as $key => $value){
$parts[] = (array) $callback($value, $key, $array);
Expand Down
Loading