【问题标题】:Using PHP iterators使用 PHP 迭代器
【发布时间】:2011-11-08 09:16:59
【问题描述】:

朋友。我知道,这些迭代器已经有很多问题了。 我读过一些东西,我不是初学者......但我的想法有点停留在这个上面。请帮助我理解我在实践中如何使用迭代器。

假设,我有一个可以从数据库中选择实例的 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 类?

更新 理想情况下,上面带有迭代器的代码可能如下所示:

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

例如所有逐页行为都在迭代器内。

【问题讨论】:

  • 您可能需要详细说明您的方案。我不明白“我更喜欢按页面选择它们”的意思。此外,请查看 stackoverflow.com/questions/1957133/… 以获取各种 SPL 相关资源。
  • “按页面”表示“按某些部分”,而不是“在一个请求中”。实际上,我现在没有太多时间阅读大量文档,所以,我希望得到一个好的建议。
  • AFAIK 通常在服务器上提取更多数据比多次运行相同查询更容易
  • @max4ever,是的,通常是这样。但是,如果您获取 100000 条记录并将它们包装到对象中,您可能会得到“超出内存限制”。这就是为什么会有“页面”。
  • 那么你的代码看起来不错,我不明白你想强制什么接口以及在哪里?

标签: php iterator spl


【解决方案1】:

我会使用一个迭代器来迭代另一个迭代器,并在它到达前一个迭代器的末尾时请求下一个迭代器......好吧,听起来比实际复杂得多:

<?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"));
    }
}
?>

【讨论】:

  • 我想补充一点,您应该避免选择大偏移量,因为随着偏移量的增加,您的数据库将不得不做越来越多的工作才能获得结果。如果您的结果集按其主键排序,则应使用上一个结果集的最后一个键作为下一个结果集的开始键...这样您的查询就不会变慢...
猜你喜欢
  • 1970-01-01
  • 2013-08-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-05-13
相关资源
最近更新 更多