首页 > 应用开发 > IT综合 > PHP 迭代器 (转)

PHP 迭代器 (转)

原创 IT综合 作者:gugu99 时间:2007-12-05 10:10:35 0 删除 编辑
PHP 迭代器 (转)[@more@]


PHP 迭代器

作者: Dejan Bosanac
译者: detrox

PHP arrays are generally a very powerful container. But still, we can easily add a little more fuel to them. Imagine an iterator object as a kind of wrapper around our arrays. What we will try to accomplish here is to create unique interface for traversing arrays and to add a little more control over how our objects are created and finally, to support lazy loading.



Iterator has a very simple and many times seen interface.


function Iterator($array) // Constructor. Takes array to be traversed as a parameter. 构造。使需要遍历的数组作为一个参数
function reset() // Sets internal pointer to the first element 设置内部指针指向第一个元素
function end() // Sets internal pointer to the last element 设置内部指针指向最后一个元素
function seek($position) // Sets internal pointer to desired position 设置内部指针指向一个指定的元素
function next() // Returns next element in the iteration 返回后一个元素
function previous() // Returns previous element in the iteration 返回前一个元素

With the interface like this you can easily perfoall your daily tasks (such as traversing arrays) in any way you want and from any position you want. One advantage of using this approach against native PHP array functions is that you have one interface for all of your array tasks. You will not use foreach() construct in one case, list-each combination in other and yet next() and prev() functions in third any more. One more advantage is that now you can easily position on any particular element and start traversing from there (in any way you want). Here are few code examples:

有了这样的接口,我们就可以在任何时间,任何地点,用我们喜欢的任何方法轻轻松松我们的日常事务(像数组遍历)。使用这个东西相对于PHP本身的数组函数的一个优势就是你有了一个为你的所有数组任务[工作]的接口。你就用不着在一段代码里用foreach()构造,另外一段用list和each的组合,然后还有别的地方一会儿用到next(),一会儿用到perv()。另一个优势是现在你可以简单的定位于一个确定的元素并且可以从这里开始遍历(以你想要得任何方式). 这里有一些例子:

// $array = ?. // initialize the array 初始化数组
$iterator = new Iterator($array);
// traverse the array 遍历数组
while ($elem = $iterator->next()) {
echo $elem;

// traverse array in reverse order 反序便利
while ($elem = $iterator->previous()) {
echo $elem;

// traverse array from fifth element on 从第五元素开始遍历

while ($elem = $iterator->next()) {
echo $elem;

OK, you say, this is all nice but there is nothing I can't do with combination of native PHP functions. Bess the fact that you are accessing all your arrays through unique interface, another (and most important) advantage is that Iterator's object structure allows you to easily expand its functionality.

OK, 你说,这是很不错,但是没有什么我不能使用原始的PHP函数组合来完成的。除了这个事实,你能通过唯一的接口存取你的所有数组,另外(也是最重要的)优势是迭代器的对象结构容许你轻松的扩展它的功能

ObjectIterator interface

Often I have ended up in situations where my object methods had to return an array of some other object as a result. Usually that object is loaded from the database, but could be some other situation such as obtaining objects through some RPC protocol (-RPC, P, ...) or endless other situation combinations that you experience every day. In this article we will focus on the first problem and briefly explain how to empower Iterator for the purpose you'd need.

我经常中断于我的对象方法要返回一些其他对象的数组作为结果[的情况]。通常这钟对象是从读取得,但是也可能是其他状况比如获得一个通过一些RPC (XML-RPC, SOAP,...) 得到的对象或是你每天要经历的无尽的其他情况的组合。在这篇文章中,我们将聚焦在第一个问题并且简要的解释如何去为你的目的实现迭代器。

Suppose that you are develo an address book for some large application. Your address book will work with companies and persons. In addition, companies could have an endless number of employees (that are also kinds of persons). So far we have recognized two objects in our application: Company and Person. Also, it is clear that the company will have method getEmployees() that returns an array of Person objects. There are a number of possible implementations of this method. Here are some usual implementations:

假设你在为一个大型web应用开发一个通讯录。你的通讯录将工作在公司和人上。另外,公司可能有无穷多的雇员(这也是一类人)。现在我们认清了应用程序中的两个对象: 公司和个人。同时也清楚了Company将有一个方法getEmployees()返回一个人对象的数组。这个方法的实现有很多。这里是一些常规的实现方法:

First, you could write a query to collect all the ids of all the company employees. Then you could make an array that contains all the objects and returns this array. This would look something like this (supposing you have a database


function getEmployees() {
$query = " id FROM persons WHERE companyid = $this->companyid";
$stmt = execute_query($query); // creates statement object 创建语句对象
$this->employees = array();

while ($row = fetch_object($stmt) {
$this->employess[$row->id] = new Person($row->id); // object creation 对象创建

return $this->employees;

and the usage would be:


$company = new Company("Datagate");
$employees = $company->getEmployees();
foreach ($employees as $id =>$employee)
$employee->addVacationDays(3); // object usage 对象的使用

OK, these look like fairly obvious solutions. But, it has few big flaws. All objects are created but we don't know if we're going to use them all. There are two performance problems here. First accessing a relational database (for creating these objects) can be very time expensive. And if the company has 500 employees and you need to access data for only 50, that is lot of time wasted. Imagine now, that we are loading these objects through RPC which is even slower. This could seriously affect application performance. Now, even if all objects are needed we don't need them at the same time, we need objects one by one as we are traversing the array. The solution above is a huge waste of res (memory and time).

OK, 这些看上去像是清晰明确的解决方案。但是,它存在一些大的瑕疵。所有的对象都被创建但我们却不知道他们是否都要被使用。这里有两个的问题。首先存取一个关系数据库(为了创建这些对象)时间开销很大。其次,如果公司有500个员工并且你只需要为其中50个存取数据,这将是极大的时间上的浪费。现在想象一下,我们将这些对象通过更缓慢的RPC读取。这将严重的影响应用程序的性能。现在,即使所有的对象都是被需要的我们也不是在同一时间需要他们,我们需要一个接着一个的[处理]对象就像我们在遍历一个数组。上面的解方案是一个巨大的资源(和时间)浪费。

The solution to these performance problems looks so obvious. Let's return just an array of employee ids. The code would look something like this:


function getEmployees() {
$query = "SELECT id FROM persons WHERE companyid = $this->companyid";
$stmt = execute_query($query); // creates statement object
$this->employees = array();
while ($row = fetch_object($stmt) {
$this->employess[$row->id] = $row->id;
return $this->employees;

and the usage would be:


$company = new Company("Datagate");
$employees = $company->getEmployees();
foreach ($employees as $id) {
$employee = new Employee($id); // object creation
$employee->addVacationDays(3); // object usage

This looks fine at the first sight. We have saved time and memory , but another problem has arisen. Suppose that the code for creating Employee object changes, for example you need to add extra argument to the constructor or some extra initialization (these are things that are happening on real projects). In that case you'll need to modify your code in many places (wherever you have used getEmployees() method), And that is a problem.


The third solution is to use an ObjectIterator class that is extended from Iterator. In this example we will see how easy it is to extend Iterator class to serve your purposes. When you are using ObjectIterator your getEmployee() function will stay the same as in second solution. So, we have saved our resources. No unnecessary objects are created and everything looks just fine. Now let's look at the usage code:

第三个解决方案是使用一个对象迭代器类,它迭代器的扩展。在这个例子里我们将看到扩展迭代器为你的目的服务是何等容易。当你在使用对象迭代器时你的 getEmployee()[应该是getEmplyees()吧]函数将保持和第二个解决方案一样。因此,我们节省了我们的资源。没有不需要的对象被创建而且每件事看上去都很好。现在,让我们看看使用代码:

$company = new Company("Datagate");
$iterator = new Iterator($company->getEmployees(), "Employee");
while ($employee = $iterator->next()) {

We see now that the object creation code is hidden in the ObjectIterator class, so it is now easy to support changes.


ObjectIterator implementation

The code for ObjectIterator is quite simple.

* Implements iterator for traversing collection of objects
* for the given array od object identifiers
* @version 1.0
* @author Dejan Bosanac
class ObjectIterator extends Iterator { var $_objectName;
* Constructor
* Calls initialization method (@see Iterator::_initialize())
* and do some specific configuration
* @param array $array array of object ids to be traversed
* @param string $objectName class of objects to be created
* @access public
* 构造函数
* 初始化方法(参考Iterator::_initialize())
* 做一些特殊的
* @参数数组 $array 要遍历的对象id的数组
* @参数字符串 $objectName 要被创建的对象的类
function ObjectIterator($array, $objectName) {
$this->_objectName = $objectName;
* Returns object with given id
* @param Object $id identifier of the object
* @return Object next object in the collection
* @access private
* 用给出的id返回对象
* @参数对象 $id 标示一个对象
* @返回对象 集合里的下一个对象
* @存取 私有
function _fetchObject($id) {
return new $this->_objectName($id);

It has $_objectName class member that represent class of the object that has to be returned by next() and previous() methods. The constructor sets this internal variable and calls an initialization function (defined in Iterator class). The most important thing is the _fetchObject() function. It encapsulates code for object creation. It is called from next() and previous() methods and takes object id as a parameter. So all your object creation code is localized here, thus making it easy to change and expand.


So, here are instructions for creating our new type of iterators. First, make your constructor (which has to have an array as a parameter) which calls _initialize() function from Iterator class. Second, override _fetchObject method to do whatever you have to do to make your objects. And, that would be all.

这里是我们创建新的一类迭代器的方法。第一,制作你的构造函数(它有一个数组作为参数),它调用从迭代器类_initialize()函数。第二,重载 _fetchObject方法去做任何你必须对你的对象做的。这就是全部。

In conclusion

Iterator will not slow down your application in a way that it will need new hardware to run it. It has some overhead, but for that price you get clean and easy readable code that is flexible enough for future software enhancements.



来自 “ ITPUB博客 ” ,链接:,如需转载,请注明出处,否则将追究法律责任。

下一篇: 分頁控制 (转)
请登录后发表评论 登录
  • 博文量
  • 访问量