<?php
declare(strict_types=1);
namespace MotorORM;
use ArrayAccess;
use ArrayIterator;
use Countable;
use IteratorAggregate;
use Traversable;
/**
* Collection
*
* @license Code and contributions have MIT License
* @link https://visavi.net
* @author Alexander Grigorev <[email protected]>
* @version 1.0
*/
class Collection implements Countable, IteratorAggregate, ArrayAccess
{
/**
* Initializes a new collection.
*/
public function __construct(
protected array $elements = [],
) {
$this->elements = array_values($elements);
}
/**
* Creates a new instance from the specified elements.
*
* This method is provided for derived classes to specify how a new
* instance should be created when constructor semantics have changed.
*
* @param array $elements Elements.
*
* @return static
*/
protected function createFrom(array $elements): static
{
return new static($elements);
}
/**
* Gets a native PHP array representation of the collection.
*
* @return array
*/
public function toArray(): array
{
return $this->elements;
}
/**
* Sets the internal iterator to the first element in the collection and returns this element.
*
* @return false|mixed
*/
public function first(): mixed
{
return reset($this->elements);
}
/**
* Sets the internal iterator to the last element in the collection and returns this element.
*
* @return false|mixed
*/
public function last(): mixed
{
return end($this->elements);
}
/**
* Gets the key/index of the element at the current iterator position.
*
* @return int|string|null
*/
public function key(): int|string|null
{
return key($this->elements);
}
/**
* Moves the internal iterator position to the next element and returns this element.
*
* @return false|mixed
*/
public function next(): mixed
{
return next($this->elements);
}
/**
* Gets the element of the collection at the current iterator position.
*
* @return false|mixed
*/
public function current(): mixed
{
return current($this->elements);
}
/**
* Removes the element at the specified index from the collection.
*
* @param string|int $key
*
* @return mixed|null
*/
public function remove(string|int $key): mixed
{
if (! isset($this->elements[$key]) && ! array_key_exists($key, $this->elements)) {
return null;
}
$removed = $this->elements[$key];
unset($this->elements[$key]);
return $removed;
}
/**
* Removes the specified element from the collection, if it is found.
*
* @param mixed $element
*
* @return bool
*/
public function removeElement(mixed $element): bool
{
$key = array_search($element, $this->elements, true);
if ($key === false) {
return false;
}
unset($this->elements[$key]);
return true;
}
/**
* Required by interface ArrayAccess.
*
* @param $offset
*
* @return bool
*/
public function offsetExists($offset): bool
{
return $this->containsKey($offset);
}
/**
* Required by interface ArrayAccess.
*
* @param $offset
*
* @return mixed
*/
public function offsetGet($offset): mixed
{
return $this->get($offset);
}
/**
* Required by interface ArrayAccess.
*
* @param $offset
* @param $value
*
* @return void
*/
public function offsetSet($offset, $value): void
{
if (! isset($offset)) {
$this->add($value);
return;
}
$this->set($offset, $value);
}
/**
* Required by interface ArrayAccess.
*
* @param $offset
*
* @return void
*/
public function offsetUnset($offset): void
{
$this->remove($offset);
}
/**
* Checks whether the collection contains an element with the specified key/index.
*
* @param string|int $key
*
* @return bool
*/
public function containsKey(string|int $key): bool
{
return isset($this->elements[$key]) || array_key_exists($key, $this->elements);
}
/**
* Checks whether an element is contained in the collection.
* This is an O(n) operation, where n is the size of the collection.
*
* @param mixed $element
*
* @return bool
*/
public function contains(mixed $element): bool
{
return in_array($element, $this->elements, true);
}
/**
* @param $element
*
* @return false|int|string
*/
public function indexOf($element): bool|int|string
{
return array_search($element, $this->elements, true);
}
/**
* Gets the element at the specified key/index.
*
* @param string|int $key
*
* @return mixed|null
*/
public function get(string|int $key): mixed
{
return $this->elements[$key] ?? null;
}
/**
* Gets all keys/indices of the collection.
*
* @return array
*/
public function getKeys(): array
{
return array_keys($this->elements);
}
/**
* Gets all values of the collection.
*
* @return array
*/
public function getValues(): array
{
return array_values($this->elements);
}
/**
* @return int
*/
public function count(): int
{
return count($this->elements);
}
/**
* Sets an element in the collection at the specified key/index.
*
* @param string|int $key
* @param mixed $value
*
* @return void
*/
public function set(string|int $key, mixed $value): void
{
$this->elements[$key] = $value;
}
/**
* Adds an element at the end of the collection.
*
* @param mixed $element
*
* @return bool
*/
public function add(mixed $element): bool
{
$this->elements[] = $element;
return true;
}
/**
* Checks whether the collection is empty (contains no elements).
*
* @return bool
*/
public function isEmpty(): bool
{
return empty($this->elements);
}
/**
* Checks whether the collection is not empty.
*
* @return bool
*/
public function isNotEmpty(): bool
{
return ! empty($this->elements);
}
/**
* @return Traversable<int|string, mixed>
*/
public function getIterator(): Traversable
{
return new ArrayIterator($this->elements);
}
/**
* Returns a string representation of this object.
*
* @return string
*/
public function __toString()
{
return self::class . '@' . spl_object_hash($this);
}
/**
* Clears the collection, removing all elements.
*
* @return void
*/
public function clear(): void
{
$this->elements = [];
}
/**
* Extracts a slice of $length elements starting at position $offset from the Collection.
*
* If $length is null it returns all elements from $offset to the end of the Collection.
* Keys have to be preserved by this method. Calling this method will only return the
* selected slice and NOT change the elements contained in the collection slice is called on.
*
* @param int $offset
* @param int|null $length
*
* @return array
*/
public function slice($offset, $length = null)
{
return array_slice($this->elements, $offset, $length, true);
}
/**
* Pluck
*
* @param string $value
* @param string|null $key
*
* @return array
*/
public function pluck(string $value, ?string $key = null)
{
if ($key === null) {
return array_column($this->elements, $value);
}
return array_column($this->elements, $value, $key);
}
}