Sophisticated Object Iterators in PHP

Share this article

In my previous post, Simple Object Iterators in PHP
, we discovered how to iterate over array items defined within an object using a foreach loop. However, what if you need to iterate over items which are not stored in an array, e.g. records from a database or lines of text read from a file? For the following example, we’ll create a class which:
  1. counts the number of users who are logged in to a web application by retrieving records from a database
  2. creates a collection of “user” objects which can be iterated with a foreach loop.
Ultimately, we want to be able to run simple code such as:

$loggedin = new LoggedIn();

// count users
echo '<p>', count($loggedin), ' user(s) are currently logged in:</p>';

// iterate over users
foreach ($loggedin as $rec => $user) {
	echo
		'Record ', $rec,
		':   ID=', $user->id, 
		' Email=', $user->email,
		'<br/>';
}
note: Recordsets are traversable
PDO recordsets are already traversable so why wrap that functionality within a class? The benefits are encapsulation and reuse. A developer using the LoggedIn class doesn’t need to worry about implementation details such as database connections or table structures. If user or login details were moved to another system, you could change the LoggedIn class without affecting other code. In addition, class functionality could be extended without affecting code which uses it, e.g. to fetch further user details, write to a log file, raise security alerts, etc.
First, we’ll define a basic class which stores the ID and email address for a single user. An instance of this class will be returned when we iterate over logged in users:

class User
{
	public $id, $email;
}
We now require a LoggedIn class which handles a collection of users who have logged in. Note that the class will implement both the Countable and Iterator interfaces:

class LoggedIn implements Countable, Iterator
{

	private $rec;		// database recordset
	private $cursor;		// number of current record
	private $item;		// current user in the collection
The constructor will immediately call a private FetchLoggedIn() method which runs a database query:

	// constructor
	public function __construct() {
		$this->FetchLoggedIn();
	}
	
	
	// fetch logged in user records
	private function FetchLoggedIn() {
	
		$this->cursor = -1;
		$this->item = null;
	
		// find logged-in users
		$db = new PDO('mysql:host=localhost;dbname=dbname', 'dbuser', 'dbpass');
		$this->rec = $db->Prepare(
			'SELECT id, email FROM `user` WHERE loggedin=1;', 
			array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY)
		);
		
		// convert returned records to a User object
		$this->rec->setFetchMode(PDO::FETCH_INTO, new User());
		
		// run query
		$this->rec->execute();
	
	}
Note:
  • $this->cursor is the current record number (zero based). It’s initially set to -1 because we haven’t retrieved any records.
  • $this->item will hold the current User object.
  • $this->rec is the set of records (if any) returned from our SQL query.
  • The $this->rec->setFetchMode line sets the default PDO fetch mode. When we fetch a row, a new instance of the User class is returned with the $id and $email properties mapped to the record.
Since our class implements Countable, we must create a public method named count()
which returns the number items in the collection — in this case, the number of rows returned by our SQL query:

	public function count() {
		return $this->rec->rowCount();
	}
We now require 5 public methods which implement Iterator functionality. The first is confusingly named rewind(). It is called when the foreach loop starts and should “rewind” to the first item in the collection:

	public function rewind() {
		if ($this->cursor >= 0) $this->FetchLoggedIn();
		$this->next();
	}
This code checks whether one or more records has already been returned. If it has, we run the SQL query again since we’ve created a forward-only recordset. The following line calls next() — our second Iterator method:

	public function next() {
		$this->cursor++;
		$this->item = $this->rec->fetch(PDO::FETCH_ORI_NEXT);
	}
This method is called during each iteration of the foreach loop. It increments $this->cursor, fetches the next row from the recordset, and creates a new User object which is assigned to $this->item. The next two Iterator methods are named current() and key()
which return the current collection item (a User object) and record number respectively:

	public function current() {
		return $this->item;
	}


	public function key() {
		return $this->cursor;
	}
Finally, we require a public valid() method. This is called immediately after the foreach loop executes rewind() or next() — it must return true if an item is available:

	public function valid() {
		return ($this->cursor < $this->count());
    }
// end of class definition
}
Our class is complete and we can count or iterate over logged-in users using the code at the top of this article. I hope you found this series useful and consider Iterators the next time you create an object which contains a collection of items.

Frequently Asked Questions (FAQs) about PHP Object Iterators

What is the basic concept of PHP Object Iterators?

PHP Object Iterators are a part of the SPL (Standard PHP Library) and they provide an easy way to iterate over different types of data. They are essentially a way to loop over data in PHP. The Iterator interface provides five methods: current(), key(), next(), rewind(), and valid(). These methods allow you to retrieve the current element, get the key of the current element, move forward to the next element, rewind the Iterator to the first element, and check if the current position is valid, respectively.

How do I implement the Iterator interface in PHP?

To implement the Iterator interface in PHP, you need to use the keyword ‘implements’ followed by the name of the interface, which is ‘Iterator’. Then, you need to define the five methods that the Iterator interface requires. Here’s a basic example:

class MyIterator implements Iterator {
private $items = array();
private $pointer = 0;

public function __construct($items) {
$this->items = array_values($items);
}

public function current() {
return $this->items[$this->pointer];
}

public function key() {
return $this->pointer;
}

public function next() {
$this->pointer++;
}

public function rewind() {
$this->pointer = 0;
}

public function valid() {
return isset($this->items[$this->pointer]);
}
}

How can I iterate over an object’s properties in PHP?

To iterate over an object’s properties in PHP, you can use a foreach loop. The object must be an instance of a class that implements the Iterator interface. Here’s an example:

class MyClass {
public $property1 = "Hello";
public $property2 = "World";
}

$object = new MyClass();

foreach($object as $key => $value) {
echo "$key => $value\n";
}

What is the difference between Iterator and IteratorAggregate interface in PHP?

The Iterator interface requires you to implement five methods: current(), key(), next(), rewind(), and valid(). On the other hand, the IteratorAggregate interface only requires you to implement one method: getIterator(). This method should return an instance of a class that implements the Iterator interface. The IteratorAggregate interface is useful when you want to expose an internal list or array for iteration.

How can I filter the elements that I iterate over with PHP Object Iterators?

You can use the FilterIterator class to filter the elements that you iterate over. This class requires you to implement the accept() method, which should return a boolean indicating whether the current element should be returned. Here’s an example:

class MyFilterIterator extends FilterIterator {
public function accept() {
return $this->current() > 10;
}
}

$iterator = new MyFilterIterator(new ArrayIterator(array(1, 20, 30, 40)));

foreach($iterator as $value) {
echo "$value\n";
}

How can I sort the elements that I iterate over with PHP Object Iterators?

You can use the ArrayObject class to sort the elements that you iterate over. This class provides the asort() method, which sorts the elements and maintains index association. Here’s an example:

$arrayObject = new ArrayObject(array(3, 2, 1));
$arrayObject->asort();

foreach($arrayObject as $value) {
echo "$value\n";
}

How can I limit the number of elements that I iterate over with PHP Object Iterators?

You can use the LimitIterator class to limit the number of elements that you iterate over. This class allows you to specify the offset and count in the constructor. Here’s an example:

$iterator = new LimitIterator(new ArrayIterator(range(1, 100)), 10, 20);

foreach($iterator as $value) {
echo "$value\n";
}

How can I iterate over multiple iterators at the same time with PHP Object Iterators?

You can use the MultipleIterator class to iterate over multiple iterators at the same time. This class allows you to attach multiple iterators and iterate over them simultaneously. Here’s an example:

$iterator1 = new ArrayIterator(array(1, 2, 3));
$iterator2 = new ArrayIterator(array(4, 5, 6));

$multipleIterator = new MultipleIterator();
$multipleIterator->attachIterator($iterator1);
$multipleIterator->attachIterator($iterator2);

foreach($multipleIterator as $values) {
list($value1, $value2) = $values;
echo "$value1, $value2\n";
}

How can I iterate over a directory with PHP Object Iterators?

You can use the DirectoryIterator class to iterate over a directory. This class provides methods to retrieve information about the files and directories. Here’s an example:

$iterator = new DirectoryIterator('/path/to/directory');

foreach($iterator as $fileInfo) {
if($fileInfo->isFile()) {
echo $fileInfo->getFilename() . "\n";
}
}

How can I iterate over a CSV file with PHP Object Iterators?

You can use the SplFileObject class to iterate over a CSV file. This class provides the fgetcsv() method, which returns an array containing the fields read. Here’s an example:

$file = new SplFileObject('/path/to/file.csv');

while(!$file->eof()) {
$fields = $file->fgetcsv();
print_r($fields);
}

Craig BucklerCraig Buckler
View Author

Craig is a freelance UK web consultant who built his first page for IE2.0 in 1995. Since that time he's been advocating standards, accessibility, and best-practice HTML5 techniques. He's created enterprise specifications, websites and online applications for companies and organisations including the UK Parliament, the European Parliament, the Department of Energy & Climate Change, Microsoft, and more. He's written more than 1,000 articles for SitePoint and you can find him @craigbuckler.

classforeachiteratorloopobjectPHP
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week