diff --git a/src/Interfaces/ArrayViewInterface.php b/src/Interfaces/ArrayViewInterface.php index d248ec5..3385219 100644 --- a/src/Interfaces/ArrayViewInterface.php +++ b/src/Interfaces/ArrayViewInterface.php @@ -85,12 +85,14 @@ public function count(): int; /** * @param numeric|string|ArraySelectorInterface $offset + * * @return bool */ public function offsetExists($offset): bool; /** * @param numeric|string|ArraySelectorInterface $offset + * * @return T|array */ #[\ReturnTypeWillChange] @@ -99,12 +101,14 @@ public function offsetGet($offset); /** * @param numeric|string|ArraySelectorInterface $offset * @param T|array|ArrayViewInterface $value + * * @return void */ public function offsetSet($offset, $value): void; /** * @param numeric|string|ArraySelectorInterface $offset + * * @return void */ public function offsetUnset($offset): void; diff --git a/src/Traits/ArrayViewAccessTrait.php b/src/Traits/ArrayViewAccessTrait.php new file mode 100644 index 0000000..5611717 --- /dev/null +++ b/src/Traits/ArrayViewAccessTrait.php @@ -0,0 +1,126 @@ +numericOffsetExists($offset); + } + + if (\is_string($offset) && Slice::isSlice($offset)) { + return true; + } + + if ($offset instanceof ArraySelectorInterface) { + return true; + } + + return false; + } + + /** + * @param numeric|string|ArraySelectorInterface $offset + * + * @return T|array + * + * {@inheritDoc} + */ + #[\ReturnTypeWillChange] + public function offsetGet($offset) + { + /** @var mixed $offset */ + if (\is_numeric($offset)) { + if (!$this->numericOffsetExists($offset)) { + throw new IndexError("Index {$offset} is out of range."); + } + return $this->source[$this->convertIndex(\intval($offset))]; + } + + if (\is_string($offset) && Slice::isSlice($offset)) { + return $this->subview(new SliceSelector($offset))->toArray(); + } + + if ($offset instanceof ArraySelectorInterface) { + return $this->subview($offset)->toArray(); + } + + $strOffset = \is_scalar($offset) ? \strval($offset) : \gettype($offset); + throw new KeyError("Invalid key: \"{$strOffset}\"."); + } + + /** + * @param numeric|string|ArraySelectorInterface $offset + * @param T|array|ArrayViewInterface $value + * + * @return void + * + * {@inheritDoc} + */ + public function offsetSet($offset, $value): void + { + /** @var mixed $offset */ + if ($this->isReadonly()) { + throw new ReadonlyError("Cannot modify a readonly view."); + } + + if (\is_numeric($offset)) { + if (!$this->numericOffsetExists($offset)) { + throw new IndexError("Index {$offset} is out of range."); + } + + // @phpstan-ignore-next-line + $this->source[$this->convertIndex(\intval($offset))] = $value; + return; + } + + if (\is_string($offset) && Slice::isSlice($offset)) { + /** @var array|ArrayViewInterface $value */ + $this->subview(new SliceSelector($offset))->set($value); + return; + } + + if ($offset instanceof ArraySelectorInterface) { + $this->subview($offset)->set($value); + return; + } + + $strOffset = \is_scalar($offset) ? \strval($offset) : \gettype($offset); + throw new KeyError("Invalid key: \"{$strOffset}\"."); + } + + /** + * @param numeric|string|ArraySelectorInterface $offset + * + * @return void + * + * @throws NotSupportedError + * + * {@inheritDoc} + */ + public function offsetUnset($offset): void + { + throw new NotSupportedError(); + } +} diff --git a/src/Views/ArrayView.php b/src/Views/ArrayView.php index 9809387..ede4cb3 100644 --- a/src/Views/ArrayView.php +++ b/src/Views/ArrayView.php @@ -5,17 +5,14 @@ namespace Smoren\ArrayView\Views; use Smoren\ArrayView\Exceptions\IndexError; -use Smoren\ArrayView\Exceptions\KeyError; use Smoren\ArrayView\Exceptions\SizeError; -use Smoren\ArrayView\Exceptions\NotSupportedError; use Smoren\ArrayView\Exceptions\ReadonlyError; use Smoren\ArrayView\Exceptions\ValueError; -use Smoren\ArrayView\Interfaces\ArraySelectorInterface; use Smoren\ArrayView\Interfaces\ArrayViewInterface; use Smoren\ArrayView\Interfaces\MaskSelectorInterface; use Smoren\ArrayView\Selectors\MaskSelector; use Smoren\ArrayView\Selectors\SliceSelector; -use Smoren\ArrayView\Structs\Slice; +use Smoren\ArrayView\Traits\ArrayViewAccessTrait; use Smoren\ArrayView\Util; /** @@ -25,6 +22,11 @@ */ class ArrayView implements ArrayViewInterface { + /** + * @use ArrayViewAccessTrait + */ + use ArrayViewAccessTrait; + /** * @var array|ArrayViewInterface */ @@ -124,7 +126,8 @@ public function subview($selector, bool $readonly = null): ArrayViewInterface */ public function apply(callable $mapper): self { - for ($i = 0; $i < \count($this); $i++) { + $size = \count($this); + for ($i = 0; $i < $size; $i++) { /** @var T $item */ $item = $this[$i]; $this[$i] = $mapper($item, $i); @@ -151,7 +154,8 @@ public function applyWith($data, callable $mapper): self $dataView = ArrayView::toView($data); - for ($i = 0; $i < \count($this); $i++) { + $size = \count($this); + for ($i = 0; $i < $size; $i++) { /** @var T $lhs */ $lhs = $this[$i]; /** @var U $rhs */ @@ -170,7 +174,8 @@ public function applyWith($data, callable $mapper): self public function set($newValues): self { if (!\is_array($newValues) && !($newValues instanceof ArrayViewInterface)) { - for ($i = 0; $i < \count($this); $i++) { + $size = \count($this); + for ($i = 0; $i < $size; $i++) { $this[$i] = $newValues; } return $this; @@ -183,7 +188,8 @@ public function set($newValues): self $newValuesView = ArrayView::toView($newValues); - for ($i = 0; $i < \count($this); $i++) { + $size = \count($this); + for ($i = 0; $i < $size; $i++) { $this[$i] = $newValuesView[$i]; } @@ -195,7 +201,8 @@ public function set($newValues): self */ public function getIterator(): \Generator { - for ($i = 0; $i < \count($this); $i++) { + $size = \count($this); + for ($i = 0; $i < $size; $i++) { /** @var T $item */ $item = $this[$i]; yield $item; @@ -210,97 +217,6 @@ public function isReadonly(): bool return $this->readonly; } - /** - * {@inheritDoc} - */ - public function offsetExists($offset): bool - { - if (\is_numeric($offset)) { - return $this->numericOffsetExists($offset); - } - - if (\is_string($offset) && Slice::isSlice($offset)) { - return true; - } - - if ($offset instanceof ArraySelectorInterface) { - return true; - } - - return false; - } - - /** - * {@inheritDoc} - */ - #[\ReturnTypeWillChange] - public function offsetGet($offset) - { - /** @var mixed $offset */ - if (\is_numeric($offset)) { - if (!$this->numericOffsetExists($offset)) { - throw new IndexError("Index {$offset} is out of range."); - } - return $this->source[$this->convertIndex(\intval($offset))]; - } - - if (\is_string($offset) && Slice::isSlice($offset)) { - return $this->subview(new SliceSelector($offset))->toArray(); - } - - if ($offset instanceof ArraySelectorInterface) { - return $this->subview($offset)->toArray(); - } - - $strOffset = \is_scalar($offset) ? \strval($offset) : \gettype($offset); - throw new KeyError("Invalid key: \"{$strOffset}\"."); - } - - /** - * {@inheritDoc} - */ - public function offsetSet($offset, $value): void - { - /** @var mixed $offset */ - if ($this->isReadonly()) { - throw new ReadonlyError("Cannot modify a readonly view."); - } - - if (\is_numeric($offset)) { - if (!$this->numericOffsetExists($offset)) { - throw new IndexError("Index {$offset} is out of range."); - } - - // @phpstan-ignore-next-line - $this->source[$this->convertIndex(\intval($offset))] = $value; - return; - } - - if (\is_string($offset) && Slice::isSlice($offset)) { - /** @var array|ArrayViewInterface $value */ - $this->subview(new SliceSelector($offset))->set($value); - return; - } - - if ($offset instanceof ArraySelectorInterface) { - $this->subview($offset)->set($value); - return; - } - - $strOffset = \is_scalar($offset) ? \strval($offset) : \gettype($offset); - throw new KeyError("Invalid key: \"{$strOffset}\"."); - } - - /** - * @throws NotSupportedError - * - * {@inheritDoc} - */ - public function offsetUnset($offset): void - { - throw new NotSupportedError(); - } - /** * {@inheritDoc} */