Использование PHP итераторов - PullRequest
3 голосов
/ 08 ноября 2011

друзей.Я знаю, здесь уже есть много вопросов об этих итераторах.Я кое-что прочитал, и я не новичок ... но мой разум несколько застрял на этом.Пожалуйста, помогите мне понять, как я использую итераторы на практике.

Предположим, у меня есть объект ORM, который может выбирать экземпляры из базы данных.И один экземпляр содержит поля и может вставлять, uodate и т. Д. Как обычно.Я хочу перебрать все объекты типа, но так как их может быть много, я предпочитаю выбирать их по «страницам».Мой код:

$limit = 100;
$offset = 0;
do
{
  $recs = $orm->select($filter, $sorting, $limit , $offset);
  $offset += $limit;
  foreach ($recs as $rec)
  {
    // doing something with record
  }
}
while (count($recs) == $limit);

Мне кажется, что здесь подходит парадигма итератора, но какой интерфейс лучше реализовать в этом случае или, может быть, какой-то базовый класс SPL?1009 * В идеале приведенный выше код с итератором может выглядеть следующим образом:

$iterator = new ORMPagedIterator($ormobject, $filter, $sorting);
foreach ($iterator as $rec)
{
  // do something with record
}

Например, все это постраничное поведение находится внутри итератора.

1 Ответ

4 голосов
/ 08 ноября 2011

Я бы использовал Итератор, который перебирает другой Итератор и запрашивает следующий Итератор, как только он достигает конца предыдущего Итератора ... хорошо, звучит сложнее, чем на самом деле:

<?php
$limit = 100;
$offset = 0;

$iter = new NextIteratorCallbackIterator(function($i) use ($orm, $limit, &$offset) {
    printf("selecting next bunch at offset %d\n", $offset);
    $recs = $orm->select($filter, $sorting, $limit , $offset);
    $offset += $limit;
    if ($recs) {
        return new ArrayIterator($recs);
    }
    return null; // end reached
});

foreach ($iter as $rec) {
    // do something with record
}
?>

А вот пример реализации этого NextIteratorCallbackIterator:

<?php
class NextIteratorCallbackIterator implements Iterator {
    private $_iterator = null;
    private $_count = 0;
    private $_callback;

    public function __construct($callback) {
        if (!is_callable($callback)) {
            throw new Exception(__CLASS__.": callback must be callable");
        }
        $this->_callback = $callback;
    }

    public function current() {
        return $this->_iterator !== null ? $this->_iterator->current() : null;
    }

    public function key() {
        return $this->_iterator !== null ? $this->_iterator->key() : null;
    }

    public function next() {
        $tryNext = ($this->_iterator === null);
        do {
            if ($tryNext) {
                $tryNext = false;
                $this->_iterator = call_user_func($this->_callback, ++$this->_count);
            }
            elseif ($this->_iterator !== null) {
                $this->_iterator->next();
                if ($this->_iterator->valid() == false) {
                    $tryNext = true;
                }
            }
        } while ($tryNext);
    }

    public function rewind() {
        $this->_iterator = call_user_func($this->_callback, $this->_count = 0);
    }

    public function valid () {
        return $this->_iterator !== null;
    }
}
?>

ОБНОВЛЕНИЕ: Ваш ORMPagedIterator может быть реализован с использованием NextIteratorCallbackIterator так же просто, как:

<?php
class ORMPagedIterator implements IteratorAggregate {
    function __construct($orm, $filter, $sorting, $chunksize = 100) {
        $this->orm = $orm;
        $this->filter = $filter;
        $this->sorting = $sorting;
        $this->chunksize = $chunksize;
    }

    function iteratorNext($i) {
        $offset = $this->chunksize * $i;
        $recs = $this->orm->select($this->filter, $this->sorting, $this->chunksize, $offset);
        if ($recs) {
            return new ArrayIterator($recs);
        }
        return null; // end reached
    }

    function getIterator() {
        return new NextIteratorCallbackIterator(array($this,"iteratorNext"));
    }
}
?>
...