iterator Ітератор (англ. Iterator) належить до класу шаблонів поведінки. Використовується у складових об’єктах. Надає доступ до своїх внутрішніх полів, не розкриваючи їхньої структури.

Найчастіше цей шаблон використовується замість масиву об’єктів, щоб не лише надати доступ до елементів, а й наділити деякою логікою. Це може бути обмеження доступу, сортування або будь-яка інша операція над безліччю об’єктів.

З коробки PHP нам доступний інтерфейс Iterator.

  • Метод Iterator->current() повертає поточний елемент
  • Метод Iterator->next() переміщає покажчик на наступний елемент
  • Метод Iterator->key() повертає індекс поточного елемента
  • Метод Iterator->valid() перевіряє, чи існує поточний елемент
  • Метод Iterator->rewind() переміщує покажчик поточного елемента на перший
  • Worker.php
                  
    <?php
    
    namespace DesignPatterns\Behavioral\Iterator;
    
    /**
     * Class Worker
     * @package DesignPatterns\Behavioral\Iterator
     */
    class Worker
    {
        /**
         * @var string
         */
        protected $name;
    
        /**
         * Worker constructor.
         *
         * @param string $name
         */
        public function __construct(string $name)
        {
            $this->name = $name;
        }
    
        /**
         * @return string
         */
        public function getName(): string
        {
            return $this->name;
        }
    }
    
                  
  • WorkersList.php
                  
    <?php
    
    namespace DesignPatterns\Behavioral\Iterator;
    
    use Countable;
    use Iterator;
    
    /**
     * Class WorkersList
     * @package DesignPatterns\Behavioral\Iterator
     */
    class WorkersList implements Iterator, Countable
    {
    
        /**
         * @var Worker[]
         */
        protected $workers = [];
        /**
         * @var int
         */
        protected $currentIndex = 0;
    
        /**
         * @param Worker $worker
         */
        public function addWorker(Worker $worker)
        {
            $this->workers[] = $worker;
        }
    
        /**
         * @return Worker
         */
        public function current(): Worker
        {
            return $this->workers[$this->currentIndex];
        }
    
        /**
         * @return void
         */
        public function next()
        {
            $this->currentIndex++;
        }
    
        /**
         * @return int
         */
        public function key(): int
        {
            return $this->currentIndex;
        }
    
        /**
         * @return bool
         */
        public function valid(): bool
        {
            return isset($this->workers[$this->currentIndex]);
        }
    
        /**
         * @return void
         */
        public function rewind()
        {
            $this->currentIndex = 0;
        }
    
        /**
         * @return int
         */
        public function count(): int
        {
            return count($this->workers);
        }
    }
    
                  
  • Test case:

    IteratorTest.php
      
    <?php
    
    namespace DesignPatterns\Tests\Behavioral\Iterator;
    
    use DesignPatterns\Behavioral\Iterator\Worker;
    use DesignPatterns\Behavioral\Iterator\WorkersList;
    
    class IteratorTest extends \PHPUnit_Framework_TestCase
    {
        public function testWorkerGetName()
        {
            $name = 'TestName';
    
            $worker = new Worker($name);
            $this->assertEquals($name, $worker->getName());
        }
    
        public function testCountList()
        {
            $ivanWorker = new Worker('Ivan');
            $petrWorker = new Worker('Petr');
            $andreyWorker = new Worker('Andrey');
    
            $workersList = new WorkersList();
    
            $workersList->addWorker($ivanWorker);
            $workersList->addWorker($petrWorker);
            $workersList->addWorker($andreyWorker);
            $this->assertCount(3, $workersList);
        }
    
        public function testWorkersListKey()
        {
            $workersList = new WorkersList();
    
            $this->assertEquals(0, $workersList->key());
    
            $workersList->next();
            $this->assertEquals(1, $workersList->key());
    
            $workersList->next();
            $this->assertEquals(2, $workersList->key());
        }
    
        public function testWorkersListValid()
        {
            $ivanWorker = new Worker('Ivan');
            $petrWorker = new Worker('Petr');
            $andreyWorker = new Worker('Andrey');
    
            $workersList = new WorkersList();
    
            $workersList->addWorker($ivanWorker);
            $workersList->addWorker($petrWorker);
            $workersList->addWorker($andreyWorker);
    
            $this->assertEquals(true, $workersList->valid());
    
            $workersList->next();
            $this->assertEquals(true, $workersList->valid());
    
            $workersList->next();
            $this->assertEquals(true, $workersList->valid());
    
            $workersList->next();
            $this->assertEquals(false, $workersList->valid());
        }
    
        public function testWorkersListResetKey()
        {
            $workersList = new WorkersList();
    
            $this->assertEquals(0, $workersList->key());
    
            $workersList->next();
            $this->assertEquals(1, $workersList->key());
    
            $workersList->next();
            $this->assertEquals(2, $workersList->key());
    
            $workersList->rewind();
            $this->assertEquals(0, $workersList->key());
        }
    
        public function testWorkerListCurrentWorker()
        {
            $ivanWorker = new Worker('Ivan');
            $petrWorker = new Worker('Petr');
            $andreyWorker = new Worker('Andrey');
    
            $workersList = new WorkersList();
    
            $workersList->addWorker($ivanWorker);
            $workersList->addWorker($petrWorker);
            $workersList->addWorker($andreyWorker);
    
            $this->assertSame($ivanWorker, $workersList->current());
            $workersList->next();
            $this->assertSame($petrWorker, $workersList->current());
            $workersList->next();
            $this->assertSame($andreyWorker, $workersList->current());
            $workersList->rewind();
            $this->assertSame($ivanWorker, $workersList->current());
        }
    }