parent
be64559bd7
commit
80dd6d3545
@ -1,17 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace jrosset\ArrayClasses;
|
||||
|
||||
/**
|
||||
* An array
|
||||
*/
|
||||
class ArrayClass extends ImmutableArrayClass implements IArrayClass {
|
||||
use TInternalArray;
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function toImmutable (): IImmutableArrayClass {
|
||||
return new ImmutableArrayClass($this->array, $this->throwsForNonExistentElement());
|
||||
}
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace jrosset\ArrayClasses;
|
||||
|
||||
/**
|
||||
* Interface for array classes
|
||||
*
|
||||
* @see IImmutableArrayClass Immutable version
|
||||
*/
|
||||
interface IArrayClass extends IImmutableArrayClass {
|
||||
/**
|
||||
* Create or replace a cell
|
||||
*
|
||||
* @param mixed $cellName The cell name/offset
|
||||
* @param mixed $cellValue The cell new value
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function set ($cellName, $cellValue): self;
|
||||
/**
|
||||
* Add a cell (at the end)
|
||||
*
|
||||
* @param mixed $cellValue The cell new value
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function push ($cellValue): self;
|
||||
/**
|
||||
* Remove a cell
|
||||
*
|
||||
* @param mixed $cellName The cell name/offset
|
||||
* @param bool $throwsForNonExistentElement Throws an exception for nonexistent elements ?
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function del ($cellName, ?bool $throwsForNonExistentElement = null): self;
|
||||
|
||||
/**
|
||||
* Create an immutable copy
|
||||
*
|
||||
* @return IImmutableArrayClass The immutable copy
|
||||
*/
|
||||
public function toImmutable (): IImmutableArrayClass;
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace jrosset\ArrayClasses;
|
||||
|
||||
use ArrayAccess;
|
||||
use Countable;
|
||||
use IteratorAggregate;
|
||||
use JsonSerializable;
|
||||
use Serializable;
|
||||
|
||||
/**
|
||||
* Interface for immutable array classes
|
||||
*
|
||||
* @see IArrayClass Mutable version
|
||||
*/
|
||||
interface IImmutableArrayClass extends IteratorAggregate, JsonSerializable, Serializable, Countable, ArrayAccess, IArrayCast {
|
||||
/**
|
||||
* Check if a cell exists
|
||||
*
|
||||
* @param mixed $cellName The cell name/offset
|
||||
*
|
||||
* @return bool Is the cell exists ?
|
||||
*/
|
||||
public function has ($cellName): bool;
|
||||
/**
|
||||
* Get the value of a cell
|
||||
*
|
||||
* @param mixed $cellName The cell name/offset
|
||||
* @param bool $throwsForNonExistentElement Throws an exception for nonexistent elements ?
|
||||
*
|
||||
* @return mixed The cell value
|
||||
*/
|
||||
public function get ($cellName, ?bool $throwsForNonExistentElement = null);
|
||||
|
||||
/**
|
||||
* Create a mutable copy
|
||||
*
|
||||
* @return IArrayClass The mutable copy
|
||||
*/
|
||||
public function toMutable (): IArrayClass;
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace jrosset\ArrayClasses;
|
||||
|
||||
/**
|
||||
* An immutable array
|
||||
*/
|
||||
class ImmutableArrayClass implements IImmutableArrayClass {
|
||||
use TImmutableInternalArray;
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function toMutable (): IArrayClass {
|
||||
return new ArrayClass($this->array, $this->throwsForNonExistentElement());
|
||||
}
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace jrosset\ArrayClasses;
|
||||
|
||||
use LogicException;
|
||||
|
||||
/**
|
||||
* Exception thrown when trying to modify an immutable element
|
||||
*/
|
||||
class ImmutableException extends LogicException {
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace jrosset\ArrayClasses;
|
||||
|
||||
/**
|
||||
* An immutable array with insensitive case cell name
|
||||
*/
|
||||
class ImmutableInsensitiveCaseArrayClass implements IImmutableArrayClass {
|
||||
use TImmutableInternalArray {
|
||||
__construct as private __construct__TImmutableInternalArray;
|
||||
has as private has__TImmutableInternalArray;
|
||||
get as private get__TImmutableInternalArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function __construct ($initial = null, bool $throwsForNonExistentElement = true) {
|
||||
$this->__construct__TImmutableInternalArray($initial, $throwsForNonExistentElement);
|
||||
|
||||
$arrayNew = [];
|
||||
foreach ($this->array as $key => $value) {
|
||||
$arrayNew[mb_strtolower($key)] = $value;
|
||||
}
|
||||
$this->array = $arrayNew;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function has ($cellName): bool {
|
||||
return $this->has__TImmutableInternalArray(mb_strtolower($cellName));
|
||||
}
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function get ($cellName, ?bool $throwsForNonExistentElement = null) {
|
||||
return $this->get__TImmutableInternalArray(mb_strtolower($cellName), $throwsForNonExistentElement);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function toMutable (): IArrayClass {
|
||||
return new InsensitiveCaseArrayClass($this->array, $this->throwsForNonExistentElement());
|
||||
}
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace jrosset\ArrayClasses;
|
||||
|
||||
/**
|
||||
* An array with insensitive case cell name
|
||||
*/
|
||||
class InsensitiveCaseArrayClass extends ImmutableInsensitiveCaseArrayClass implements IArrayClass {
|
||||
use TInternalArray {
|
||||
set as private set__TInternalArray;
|
||||
del as private del__TInternalArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function set ($cellName, $cellValue): self {
|
||||
return $this->set__TInternalArray(mb_strtolower($cellName), $cellValue);
|
||||
}
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function del ($cellName, ?bool $throwsForNonExistentElement = null): self {
|
||||
return $this->del__TInternalArray(mb_strtolower($cellName), $throwsForNonExistentElement);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function toImmutable (): IImmutableArrayClass {
|
||||
return new ImmutableInsensitiveCaseArrayClass($this->array, $this->throwsForNonExistentElement());
|
||||
}
|
||||
}
|
@ -1,159 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace jrosset\ArrayClasses;
|
||||
|
||||
use ArrayIterator;
|
||||
use InvalidArgumentException;
|
||||
use OutOfRangeException;
|
||||
use Traversable;
|
||||
|
||||
trait TImmutableInternalArray {
|
||||
/**
|
||||
* @var array The internal array
|
||||
*/
|
||||
protected array $array = [];
|
||||
/**
|
||||
* @var bool Throws an exception for nonexistent elements ?
|
||||
*/
|
||||
private bool $throwsForNonExistentElement = true;
|
||||
|
||||
/**
|
||||
* Initialize the array
|
||||
*
|
||||
* @param IArrayCast|array|null $initial The initial value
|
||||
* @param bool $throwsForNonExistentElement Throws an exception for nonexistent elements ?
|
||||
*/
|
||||
public function __construct ($initial = null, bool $throwsForNonExistentElement = true) {
|
||||
$this->setThrowsForNonExistentElement($throwsForNonExistentElement);
|
||||
|
||||
if (is_array($initial)) {
|
||||
$this->array = $initial;
|
||||
}
|
||||
elseif (is_null($initial)) {
|
||||
$this->array = [];
|
||||
}
|
||||
elseif ($initial instanceof IArrayCast) {
|
||||
$this->array = $initial->toArray();
|
||||
}
|
||||
else {
|
||||
throw new InvalidArgumentException('The initial value is not valid: null, array or ' . IArrayCast::class);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Set information to dump
|
||||
*
|
||||
* @return array The information to dump
|
||||
*
|
||||
* @see https://www.php.net/manual/function.var-dump.php
|
||||
*/
|
||||
public function __debugInfo (): array {
|
||||
return [
|
||||
'array' => $this->array,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function has ($cellName): bool {
|
||||
return array_key_exists($cellName, $this->array);
|
||||
}
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function get ($cellName, ?bool $throwsForNonExistentElement = null) {
|
||||
if ($this->has($cellName)) {
|
||||
return $this->array[$cellName];
|
||||
}
|
||||
if ($throwsForNonExistentElement ?? $this->throwsForNonExistentElement()) {
|
||||
throw new OutOfRangeException();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function toArray (): array {
|
||||
return $this->array;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function count (): int {
|
||||
return count($this->array);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getIterator (): Traversable {
|
||||
return new ArrayIterator($this->array);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function offsetExists ($offset): bool {
|
||||
return $this->has($offset);
|
||||
}
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function offsetGet ($offset) {
|
||||
return $this->get($offset);
|
||||
}
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function offsetSet ($offset, $value): void {
|
||||
throw new ImmutableException();
|
||||
}
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function offsetUnset ($offset): void {
|
||||
throw new ImmutableException();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function serialize (): string {
|
||||
return serialize($this->array);
|
||||
}
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function unserialize ($data): void {
|
||||
$this->array = unserialize($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function jsonSerialize () {
|
||||
return $this->array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws an exception for nonexistent elements ?
|
||||
*
|
||||
* @return bool Throws an exception for nonexistent elements ?
|
||||
*/
|
||||
public function throwsForNonExistentElement (): bool {
|
||||
return $this->throwsForNonExistentElement;
|
||||
}
|
||||
/**
|
||||
* Set if throws an exception for nonexistent elements
|
||||
*
|
||||
* @param bool $throwsForNonExistentElement Throws an exception for nonexistent elements ?
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setThrowsForNonExistentElement (bool $throwsForNonExistentElement): self {
|
||||
$this->throwsForNonExistentElement = $throwsForNonExistentElement;
|
||||
return $this;
|
||||
}
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace jrosset\ArrayClasses;
|
||||
|
||||
use OutOfRangeException;
|
||||
|
||||
trait TInternalArray {
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function set ($cellName, $cellValue): self {
|
||||
$this->array[$cellName] = $cellValue;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function push ($cellValue): self {
|
||||
return $this->set($this->count(), $cellValue);
|
||||
}
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function del ($cellName, ?bool $throwsForNonExistentElement = null): self {
|
||||
if ($throwsForNonExistentElement ?? $this->throwsForNonExistentElement() && !$this->has($cellName)) {
|
||||
throw new OutOfRangeException();
|
||||
}
|
||||
unset($this->array[$cellName]);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function offsetSet ($offset, $value): void {
|
||||
$this->set($offset, $value);
|
||||
}
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function offsetUnset ($offset): void {
|
||||
$this->del($offset);
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace jrosset\Collections;
|
||||
|
||||
/**
|
||||
* A collection
|
||||
*
|
||||
* @psalm-template TKey of array-key
|
||||
* @template-covariant TValue
|
||||
* @template-extends ImmutableCollection<TKey, TValue>
|
||||
* @template-implements ICollection<TKey, TValue>
|
||||
*/
|
||||
class Collection extends ImmutableCollection implements ICollection {
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function set ($key, $value): self {
|
||||
return $this->_set($key, $value);
|
||||
}
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function add ($value): self {
|
||||
$this->elements[] = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function clear (): self {
|
||||
$this->_initialize();
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function remove ($key): self {
|
||||
unset($this->elements[$this->_normalizeKey($key)]);
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function removeValue ($value, bool $strict = false): self {
|
||||
foreach ($this->elements as $currentKey => $currentValue) {
|
||||
if (($strict && $value === $currentValue) || (!$strict && $value == $currentValue)) {
|
||||
unset($this->elements[$currentKey]);
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
}
|
@ -1,15 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace jrosset\ArrayClasses;
|
||||
namespace jrosset\Collections;
|
||||
|
||||
/**
|
||||
* Interface for PHP-native array cast
|
||||
*
|
||||
* @psalm-template TKey of array-key
|
||||
* @template-covariant TValue
|
||||
*/
|
||||
interface IArrayCast {
|
||||
/**
|
||||
* Transform to a PHP-native array
|
||||
*
|
||||
* @return array The native array
|
||||
* @psalm-return array<TKey, TValue>
|
||||
*/
|
||||
public function toArray (): array;
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace jrosset\Collections;
|
||||
|
||||
/**
|
||||
* Interface for a collection
|
||||
*
|
||||
* @psalm-template TKey of array-key
|
||||
* @template-covariant TValue
|
||||
* @template-extends IImmutableCollection<TKey, TValue>
|
||||
*/
|
||||
interface ICollection extends IImmutableCollection {
|
||||
/**
|
||||
* Set a value to a key
|
||||
*
|
||||
* @param array-key $key The key
|
||||
* @param mixed $value The value
|
||||
*
|
||||
* @psalm-param TKey $key
|
||||
* @psalm-param TValue $value
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function set ($key, $value): self;
|
||||
/**
|
||||
* Add a value
|
||||
*
|
||||
* @param mixed $value The value
|
||||
*
|
||||
* @psalm-param TValue $value
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function add ($value): self;
|
||||
|
||||
/**
|
||||
* Empties the collection
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function clear (): self;
|
||||
/**
|
||||
* Delete a key
|
||||
*
|
||||
* @param array-key $key The key
|
||||
*
|
||||
* @psalm-param TKey $key
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function remove ($key): self;
|
||||
/**
|
||||
* Delete all instances of a value
|
||||
*
|
||||
* @param mixed $value The value
|
||||
* @param bool $strict Strict comparison ?
|
||||
*
|
||||
* @psalm-param TValue $value
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function removeValue ($value, bool $strict = false): self;
|
||||
}
|
@ -0,0 +1,127 @@
|
||||
<?php
|
||||
|
||||
namespace jrosset\Collections;
|
||||
|
||||
use ArrayAccess;
|
||||
use Closure;
|
||||
use Countable;
|
||||
use IteratorAggregate;
|
||||
use JsonSerializable;
|
||||
use Serializable;
|
||||
|
||||
/**
|
||||
* Interface for an immutable (read-only) collection
|
||||
*
|
||||
* @psalm-template TKey of array-key
|
||||
* @template-covariant TValue
|
||||
* @template-extends IteratorAggregate<TKey, TValue>
|
||||
* @template-extends ArrayAccess<TKey, TValue>
|
||||
* @template-extends IArrayCast<TKey, TValue>
|
||||
*/
|
||||
interface IImmutableCollection extends IteratorAggregate, JsonSerializable, Serializable, Countable, ArrayAccess, IArrayCast {
|
||||
/**
|
||||
* Checks if the collection is empty
|
||||
*
|
||||
* @return bool TRUE if the collection is empty, FALSE otherwise
|
||||
*/
|
||||
public function empty (): bool;
|
||||
|
||||
/**
|
||||
* Checks if a key exists
|
||||
*
|
||||
* @param array-key $key The key
|
||||
*
|
||||
* @psalm-param TKey $key
|
||||
*
|
||||
* @return bool TRUE if the key exists, FALSE otherwise
|
||||
*/
|
||||
public function exists ($key): bool;
|
||||
/**
|
||||
* Checks if contains a value
|
||||
*
|
||||
* @param mixed $value The value
|
||||
* @param bool $strict Strict comparison ?
|
||||
*
|
||||
* @psalm-param TValue $value
|
||||
*
|
||||
* @return bool TRUE if the value exists, FALSE otherwise
|
||||
*/
|
||||
public function contains ($value, bool $strict = false): bool;
|
||||
|
||||
/**
|
||||
* Get the value of a key or null if not found
|
||||
*
|
||||
* @param array-key $key The key
|
||||
*
|
||||
* @psalm-param TKey $key
|
||||
*
|
||||
* @return mixed The value
|
||||
* @psalm-return TValue|null
|
||||
*/
|
||||
public function get ($key);
|
||||
/**
|
||||
* Get the first key of a value or null if not found
|
||||
*
|
||||
* @param mixed $value The value
|
||||
* @param bool $strict Strict comparison ?
|
||||
*
|
||||
* @psalm-param TValue $value
|
||||
*
|
||||
* @return array-key
|
||||
* @psalm-return TKey|null
|
||||
*/
|
||||
public function key ($value, bool $strict = false);
|
||||
|
||||
/**
|
||||
* Extract a slice of the collection
|
||||
*
|
||||
* Preserves the keys
|
||||
*
|
||||
* @param int $offset The start offset
|
||||
* @param int|null $length The maximum length. Null if until end of the collection
|
||||
*
|
||||
* @return static
|
||||
* @psalm-return static<TKey, TValue>
|
||||
*/
|
||||
public function slice (int $offset, ?int $length = null): IImmutableCollection;
|
||||
|
||||
/**
|
||||
* Get a collection of all elements that satisfy predicate $filter
|
||||
*
|
||||
* @param Closure $filter The filtering predicate
|
||||
*
|
||||
* @psalm-param Closure(TKey, TValue):bool $filter
|
||||
*
|
||||
* @return static The result collection
|
||||
* @psalm-return static<TKey, TValue>
|
||||
*/
|
||||
public function filter (Closure $filter): IImmutableCollection;
|
||||
/**
|
||||
* A new collection with $process applied on all elements
|
||||
*
|
||||
* @psalm-template TResultValue
|
||||
*
|
||||
* @param Closure $process The process function to apply on each element
|
||||
*
|
||||
* @psalm-param Closure(TKey, TValue): TResultValue $process
|
||||
*
|
||||
* @return static
|
||||
* @psalm-return static<TKey, TResultValue>
|
||||
*/
|
||||
public function map (Closure $process): IImmutableCollection;
|
||||
|
||||
/**
|
||||
* The list of all keys
|
||||
*
|
||||
* @return static The list of all keys
|
||||
* @psalm-return static<int, TKey>
|
||||
*/
|
||||
public function keys (): IImmutableCollection;
|
||||
/**
|
||||
* The list of all values
|
||||
*
|
||||
* @return static The list of all values
|
||||
* @psalm-return static<int, TValue>
|
||||
*/
|
||||
public function values (): IImmutableCollection;
|
||||
}
|
@ -0,0 +1,271 @@
|
||||
<?php
|
||||
|
||||
namespace jrosset\Collections;
|
||||
|
||||
use ArrayIterator;
|
||||
use Closure;
|
||||
use InvalidArgumentException;
|
||||
use JsonException;
|
||||
use Traversable;
|
||||
|
||||
/**
|
||||
* An immutable collection
|
||||
*
|
||||
* @psalm-template TKey of array-key
|
||||
* @template-covariant TValue
|
||||
* @template-implements IImmutableCollection<TKey, TValue>
|
||||
*/
|
||||
class ImmutableCollection implements IImmutableCollection {
|
||||
/**
|
||||
* @var array The internal array of elements
|
||||
* @psalm-var array<TKey, TValue>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
protected array $elements;
|
||||
|
||||
/**
|
||||
* Initialise a new collection
|
||||
*
|
||||
* @param array|Traversable|null $other The initial values
|
||||
*
|
||||
* @psalm-param array<TKey, TValue>|Traversable<TKey, TValue>|null
|
||||
*
|
||||
* @throws InvalidArgumentException If the initial values aren't valid
|
||||
*/
|
||||
public function __construct ($other = null) {
|
||||
$this->_initialize($other);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise the internals elements
|
||||
*
|
||||
* @param array|Traversable|null $other The initial values
|
||||
*
|
||||
* @psalm-param array<TKey, TValue>|Traversable<TKey, TValue>|null
|
||||
*
|
||||
* @throws InvalidArgumentException If the initial values aren't valid
|
||||
* @internal
|
||||
*
|
||||
*/
|
||||
protected function _initialize ($other = null) {
|
||||
$this->elements = [];
|
||||
if ($other !== null) {
|
||||
if (!is_array($other) && !$other instanceof Traversable) {
|
||||
throw new InvalidArgumentException('The initial values must be an array or a ' . IArrayCast::class . ' instance');
|
||||
}
|
||||
|
||||
foreach ($other as $key => $value) {
|
||||
$this->_set($key, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Internally set a value to a key
|
||||
*
|
||||
* @param array-key $key The key
|
||||
* @param mixed $value The value
|
||||
*
|
||||
* @psalm-param TKey $key
|
||||
* @psalm-param TValue $value
|
||||
*
|
||||
* @return $this
|
||||
* @internal
|
||||
*
|
||||
*/
|
||||
protected function _set ($key, $value): self {
|
||||
$this->elements[$this->_normalizeKey($key)] = $value;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Internally add a value
|
||||
*
|
||||
* @param mixed $value The value
|
||||
*
|
||||
* @psalm-param TValue $value
|
||||
*
|
||||
* @return $this
|
||||
* @internal
|
||||
*
|
||||
*/
|
||||
protected function _add ($value): self {
|
||||
$this->elements[] = $value;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Normalize a key
|
||||
*
|
||||
* @param array-key $key The key to normalize
|
||||
*
|
||||
* @psalm-param TKey $key
|
||||
*
|
||||
* @return array-key The normalized key
|
||||
* @psalm-return TKey
|
||||
*/
|
||||
protected function _normalizeKey ($key) {
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getIterator () {
|
||||
return new ArrayIterator($this->elements);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function offsetExists ($offset): bool {
|
||||
return $this->exists($offset);
|
||||
}
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function offsetGet ($offset) {
|
||||
return $this->get($offset);
|
||||
}
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function offsetSet ($offset, $value) {
|
||||
throw new ImmutableException();
|
||||
}
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function offsetUnset ($offset) {
|
||||
throw new ImmutableException();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function serialize (): ?string {
|
||||
return serialize($this->elements);
|
||||
}
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function unserialize ($data) {
|
||||
$this->_initialize(unserialize($data));
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function toArray (): array {
|
||||
return $this->elements;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function count (): int {
|
||||
return count($this->elements);
|
||||
}
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function empty (): bool {
|
||||
return $this->count() === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function exists ($key): bool {
|
||||
return isset($this->elements[$this->_normalizeKey($key)]);
|
||||
}
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function contains ($value, bool $strict = false): bool {
|
||||
return in_array($value, $this->elements, $strict);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function get ($key) {
|
||||
return $this->elements[$this->_normalizeKey($key)] ?? null;
|
||||
}
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function key ($value, bool $strict = false) {
|
||||
foreach ($this->elements as $currentKey => $currentValue) {
|
||||
if (($strict && $value === $currentValue) || (!$strict && $value == $currentValue)) {
|
||||
return $currentKey;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function slice (int $offset, ?int $length = null): IImmutableCollection {
|
||||
$output = new static();
|
||||
|
||||
$currentIndex = 0;
|
||||
foreach ($this->elements as $key => $value) {
|
||||
if ($currentIndex++ < $offset) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$output->_set($key, $value);
|
||||
if ($output->count() >= $length) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function filter (Closure $filter): IImmutableCollection {
|
||||
$output = new static();
|
||||
foreach ($this->elements as $key => $value) {
|
||||
if (!$filter($key, $value)) {
|
||||
continue;
|
||||
}
|
||||
$output->_set($key, $value);
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function map (Closure $process): IImmutableCollection {
|
||||
$output = new static();
|
||||
foreach ($this->elements as $key => $value) {
|
||||
$output->_set($key, $process($key, $value));
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function keys (): IImmutableCollection {
|
||||
return new static(array_keys($this->elements));
|
||||
}
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function values (): IImmutableCollection {
|
||||
return new static(array_values($this->elements));
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*
|
||||
* @throws JsonException
|
||||
*/
|
||||
public function jsonSerialize () {
|
||||
return json_encode($this->elements, JSON_THROW_ON_ERROR);
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace jrosset\Collections;
|
||||
|
||||
use LogicException;
|
||||
|
||||
/**
|
||||
* Exception thrown when trying to modify an immutable collection
|
||||
*/
|
||||
class ImmutableException extends LogicException {
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace jrosset\Collections;
|
||||
|
||||
/**
|
||||
* A collection with insensitive case keys
|
||||
*
|
||||
* @psalm-template TKey of array-key
|
||||
* @template-covariant TValue
|
||||
* @template-extends Collection<TKey, TValue>
|
||||
*/
|
||||
class InsensitiveCaseKeyCollection extends Collection {
|
||||
use TInsensitiveCaseKey;
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace jrosset\Collections;
|
||||
|
||||
/**
|
||||
* An immutable collection with insensitive case keys
|
||||
*
|
||||
* @psalm-template TKey of array-key
|
||||
* @template-covariant TValue
|
||||
* @template-extends ImmutableCollection<TKey, TValue>
|
||||
*/
|
||||
class InsensitiveCaseKeyImmutableCollection extends ImmutableCollection implements IImmutableCollection {
|
||||
use TInsensitiveCaseKey;
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace jrosset\Collections;
|
||||
|
||||
/**
|
||||
* Implementation for insensitive case key
|
||||
*/
|
||||
trait TInsensitiveCaseKey {
|
||||
/**
|
||||
* Normalize a key
|
||||
*
|
||||
* @param array-key $key The key to normalize
|
||||
*
|
||||
* @return array-key The normalized key
|
||||
*/
|
||||
protected function _normalizeKey ($key) {
|
||||
return mb_strtolower($key);
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
use jrosset\Collections\InsensitiveCaseKeyCollection;
|
||||
use jrosset\Collections\InsensitiveCaseKeyImmutableCollection;
|
||||
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
$readOnlyCollection = new InsensitiveCaseKeyImmutableCollection(
|
||||
[
|
||||
'Foo' => 'bar',
|
||||
'foo' => 'foobar',
|
||||
]
|
||||
);
|
||||
|
||||
echo 'Size : ' . $readOnlyCollection->count() . PHP_EOL;
|
||||
echo 'Foo = ' . $readOnlyCollection->get('Foo') . PHP_EOL;
|
||||
echo '-----' . PHP_EOL;
|
||||
|
||||
$collection = new InsensitiveCaseKeyCollection($readOnlyCollection);
|
||||
$collection->add(28);
|
||||
echo 'Size : ' . $collection->count() . PHP_EOL;
|
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
/** @noinspection PhpIllegalPsrClassPathInspection */
|
||||
|
||||
use jrosset\Collections\Collection;
|
||||
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
class Measure {
|
||||
private int $value;
|
||||
private string $unit;
|
||||
|
||||
public function __construct (int $value, string $unit = 's') {
|
||||
$this->value = $value;
|
||||
$this->unit = $unit;
|
||||
}
|
||||
|
||||
public function asString (): string {
|
||||
return $this->value . $this->unit;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection<int, Measure>
|
||||
*/
|
||||
function getMeasures (): Collection {
|
||||
$measures = new Collection();
|
||||
$measures->add(new Measure(38));
|
||||
$measures->add(new Measure(27));
|
||||
$measures->add(new Measure(34));
|
||||
|
||||
return $measures;
|
||||
}
|
||||
|
||||
echo 'Measures :' . PHP_EOL;
|
||||
foreach (getMeasures() as $no => $measure) {
|
||||
echo "\t#" . $no . ' = ' . $measure->asString() . PHP_EOL;
|
||||
}
|
Loading…
Reference in New Issue