MBHFramework/structures

View on GitHub
Mbh/Iterator/SliceIterator.php

Summary

Maintainability
A
0 mins
Test Coverage
<?php namespace Mbh\Iterator;

/**
 * MBHFramework
 *
 * @link      https://github.com/MBHFramework/mbh-framework
 * @copyright Copyright (c) 2017 Ulises Jeremias Cornejo Fandos
 * @license   https://github.com/MBHFramework/mbh-framework/blob/master/LICENSE (MIT License)
 */

use ArrayAccess;
use LimitIterator;
use JsonSerializable;
use Countable;
use Iterator;
use InvalidArgumentException;
use RuntimeException;

/**
* Iterator to allow a slice to be used like an array
*/

class SliceIterator extends LimitIterator implements ArrayAccess, Countable, JsonSerializable
{
    protected $count = 0;
    protected $begin = 0;

    const INVALID_INDEX = 'Index invalid or out of range';

    /**
     * Build an iterator over a slice of an ArrayAccess object
     * Unlike a LimitIterator, the $end defines the last index, not the count
     *
     * @param Iterator $iterator An ArrayAccess iterator, e.g. SplFixedArray
     * @param int $begin The starting offset of the slice
     * @param int $end The last index of the slice
     */
    public function __construct(Iterator $iterator, $begin = 0, $end = null)
    {
        if (!(
            $iterator instanceof ArrayAccess
          && $iterator instanceof Countable
        )) {
            throw new InvalidArgumentException('Iterator must be a Countable ArrayAccess');
        }

        $count = count($iterator);

        // Negative begin means start from the end
        if ($begin < 0) {
            $begin = max(0, $count + $begin);
        }

        // If no end set, assume whole array
        if ($end === null) {
            $end = $count;
        } elseif ($end < 0) {
            // Ends counting back from start
            $end = max($begin, $count + $end);
        }

        // Set the size of iterable object, for quick-lookup
        $this->count = max(0, $end - $begin);

        // Need to store the starting offset to adjust by
        $this->begin = $begin;

        // Init as LimitIterator
        parent::__construct($iterator, $this->begin, $this->count);
    }

    /**
     * Rewind, extended for clean results on empty sets
     */
    public function rewind()
    {
        // no need to rewind on empty sets
        if ($this->count > 0) {
            parent::rewind();
        }
    }

    /**
     * Countable
     */
    public function count(): int
    {
        return $this->count;
    }

    /**
     * ArrayAccess
     */
    public function offsetExists($offset)
    {
        return $offset >= 0 && $offset < $this->count;
    }

    public function offsetGet($offset)
    {
        if ($this->offsetExists($offset)) {
            return iterator_to_array($this)->offsetGet($offset + $this->begin);
        } else {
            throw new RuntimeException(self::INVALID_INDEX);
        }
    }

    public function offsetSet($offset, $value)
    {
        return iterator_to_array($this)->offsetSet($offset + $this->begin, $value);
    }

    public function offsetUnset($offset)
    {
        return iterator_to_array($this)->offsetUnset($offset + $this->begin);
    }

    /**
     * JsonSerializable
     */
    public function jsonSerialize()
    {
        return $this->toArray();
    }

    public function toArray()
    {
        return iterator_to_array($this, false);
    }
}