* @implements CollectionInterface */ abstract class AbstractCollection extends Arrayy implements CollectionInterface { /** * @var bool */ protected $checkPropertyTypes = true; /** * @var bool */ protected $checkPropertiesMismatch = false; /** * @var bool */ protected $checkForMissingPropertiesInConstructor = true; /** * Constructs a collection object of the specified type, optionally with the * specified data. * * @param mixed $data *

* The initial items to store in the collection. *

* @param string $iteratorClass optional

* You can overwrite the ArrayyIterator, but mostly you don't * need this option. *

* @param bool $checkPropertiesInConstructor optional

* You need to extend the "Arrayy"-class and you need to set * the $checkPropertiesMismatchInConstructor class property * to * true, otherwise this option didn't not work anyway. *

* * @phpstan-param array|\Arrayy\Arrayy|\Closure():array|mixed $data * @phpstan-param class-string<\Arrayy\ArrayyIterator> $iteratorClass */ public function __construct( $data = [], string $iteratorClass = ArrayyIterator::class, bool $checkPropertiesInConstructor = true ) { $type = $this->getType(); $type = self::convertIntoTypeCheckArray($type); $this->properties = $type; // cast into array, if needed if ( !\is_array($data) && !($data instanceof \Traversable) && !($data instanceof \Closure) ) { $data = [$data]; } parent::__construct( $data, $iteratorClass, $checkPropertiesInConstructor ); } /** * Append a (key) + value to the current array. * * @param mixed $value * @param mixed $key * * @return $this *

(Mutable) Return this CollectionInterface object, with the appended values.

* * @phpstan-param T|static $value * @phpstan-param TKey|null $key * @phpstan-return static */ public function append($value, $key = null): Arrayy { if ( $value instanceof self && !$value instanceof TypeInterface ) { foreach ($value as $valueTmp) { parent::append($valueTmp, $key); } return $this; } /** @phpstan-ignore-next-line | special? */ $return = parent::append($value, $key); $this->array = $return->array; $this->generator = null; return $this; } /** * Assigns a value to the specified offset + check the type. * * @param int|string|null $offset * @param mixed $value * * @return void * * @phpstan-param T $value */ #[\ReturnTypeWillChange] public function offsetSet($offset, $value) { if ( $value instanceof self && !$value instanceof TypeInterface ) { foreach ($value as $valueTmp) { parent::offsetSet($offset, $valueTmp); } return; } parent::offsetSet($offset, $value); } /** * Prepend a (key) + value to the current array. * * @param mixed $value * @param mixed $key * * @return $this *

(Mutable) Return this CollectionInterface object, with the prepended value.

* * @phpstan-param T|static $value * @phpstan-param TKey|null $key * @phpstan-return static */ public function prepend($value, $key = null): Arrayy { if ( $value instanceof self && !$value instanceof TypeInterface ) { foreach ($value as $valueTmp) { parent::prepend($valueTmp, $key); } return $this; } /** @phpstan-ignore-next-line | special? */ $return = parent::prepend($value, $key); $this->array = $return->array; $this->generator = null; return $this; } /** * {@inheritdoc} */ public function column(string $keyOrPropertyOrMethod): array { // init $temp = []; foreach ($this->getGenerator() as $item) { $temp[] = $this->extractValue($item, $keyOrPropertyOrMethod); } return $temp; } /** * @return array * * @phpstan-return array */ public function getCollection(): array { return $this->getArray(); } /** * The type (FQCN) associated with this collection. * * @return string|string[]|TypeCheckArray|TypeCheckInterface[] * * @phpstan-return string|string[]|class-string|class-string[]|TypeCheckArray|TypeCheckInterface[] */ abstract public function getType(); /** * Merge current items and items of given collections into a new one. * * @param CollectionInterface|static ...$collections *

The collections to merge.

* *@throws \InvalidArgumentException if any of the given collections are not of the same type * * @return $this * * @phpstan-param CollectionInterface ...$collections * @phpstan-return static */ public function merge(CollectionInterface ...$collections): self { foreach ($collections as $collection) { foreach ($collection as $item) { $this->append($item); } } return $this; } /** * Creates an CollectionInterface object. * * @param mixed $data * @param string $iteratorClass * @param bool $checkPropertiesInConstructor * * @return static *

(Immutable) Returns an new instance of the CollectionInterface object.

* * @template TKeyCreate as int|string * @template TCreate * * @phpstan-param array $data * @phpstan-param class-string<\Arrayy\ArrayyIterator> $iteratorClass * @phpstan-return static * * @psalm-mutation-free */ public static function create( $data = [], string $iteratorClass = ArrayyIterator::class, bool $checkPropertiesInConstructor = true ) { return new static( $data, $iteratorClass, $checkPropertiesInConstructor ); } /** * @param string $json * * @return static *

(Immutable) Returns an new instance of the CollectionInterface object.

* * @phpstan-return static * * @psalm-mutation-free */ public static function createFromJsonMapper(string $json) { // init $return = static::create(); $jsonObject = \json_decode($json, false); $mapper = new \Arrayy\Mapper\Json(); $mapper->undefinedPropertyHandler = static function ($object, $key, $jsonValue) use ($return) { if ($return->checkForMissingPropertiesInConstructor) { throw new \TypeError('Property mismatch - input: ' . \print_r(['key' => $key, 'jsonValue' => $jsonValue], true) . ' for object: ' . \get_class($object)); } }; $type = $return->getType(); if ( \is_string($type) && \class_exists($type) ) { if (\is_array($jsonObject)) { foreach ($jsonObject as $jsonObjectSingle) { $collectionData = $mapper->map($jsonObjectSingle, $type); $return->add($collectionData); } } else { $collectionData = $mapper->map($jsonObject, $type); $return->add($collectionData); } } else { foreach ($jsonObject as $key => $jsonValue) { $return->add($jsonValue, $key); } } /** @phpstan-var static */ return $return; } /** * Internal mechanic of set method. * * @param int|string|null $key * @param mixed $value * @param bool $checkProperties * * @return bool */ protected function internalSet( $key, &$value, bool $checkProperties = true ): bool { if ( $value instanceof self && !$value instanceof TypeInterface ) { foreach ($value as $valueTmp) { parent::internalSet( $key, $valueTmp, $checkProperties ); } return true; } return parent::internalSet( $key, $value, $checkProperties ); } /** * @param string|string[]|TypeCheckArray|TypeCheckInterface[]|null $type * * @return TypeCheckArray * * @phpstan-param null|string|string[]|class-string|class-string[]|TypeCheckArray|array|mixed $type * @phpstan-return TypeCheckArray */ protected static function convertIntoTypeCheckArray($type): TypeCheckArray { $is_array = false; if ( \is_scalar($type) || $is_array = \is_array($type) ) { $type = TypeCheckArray::create( [ Arrayy::ARRAYY_HELPER_TYPES_FOR_ALL_PROPERTIES => new TypeCheckSimple($is_array ? $type : (string) $type), ] ); } return $type; } }