Приглашаем посетить
Русская библиотека (biblioteka-rus.ru)

Hack 53. Turn Any Object into an Array

Previous
Table of Contents
Next

Hack 53. Turn Any Object into an Array

Hack 53. Turn Any Object into an Array Hack 53. Turn Any Object into an Array

Use the Iterator interface in PHP 5 to turn any object into an array.

If you have ever used the DOM interface to read or write XML in PHP, you're already familiar with the DOMNodeList interface. Many methods in the DOM return an array of nodes. That array is implemented by the DOMNodeList object. To read the node list, you have to write code like this:

	$dl = $doc->getElementsByTagName( "foo" );
	for( $i = 0; $i < $dl->length; $i++ )
	{
		$n = $dl->item( $i );
		…
	}

That's kind of unfortunate, isn't it, since PHP has that beautiful foreach operator that gives access to arrays with almost no potential for messing things up. Wouldn't it be great if the interface to DOM looked more like this?

	foreach($doc->getElementsByTagName( "foo" ) as $n ) {
		…
	}

That is a lot cleaner and far less error prone.

Thanks to the additions in PHP 5, we can now allow foreach to work on any object, simply by having that class implement the Iterator interface. In this hack, I'll show how to implement an Observer pattern [Hack #67] using the Iterator interface.

6.4.1. The Code

Save the code in Example 6-5 as iterator.php.

Example 6-5. A class that uses PHP 5's new Iterator interface
<?php
interface Listener
{
	public function invoke( $caller, $data );
}

class ListenerList implements Iterator
{
	private $listeners = array();

	public function _ _construct()
	{	
	}

	public function add( $listener )
	{
		$this->listeners []= $listener;
	}

	public function invoke( $caller, $data )
	{
		foreach( $this as $listener )
	{
		$listener->invoke( $caller, $data );
	}
}

	public function rewind()
	{
		reset($this->listeners);
	}
	
	public function current()
	{
		return current($this->listeners);
	}

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

	public function next()
	{
		return next($this->listeners);
	}

	public function valid()
	{
		return ( $this->current() !== false );
	}

	}
	class SimpleListener implements Listener
	{	
		private $v;
		public function _ _construct( $v ) { $this->v = $v; }
		public function invoke( $caller, $data )
	{
		echo( $this->v." invoked with with '$data'\n" );
	}
		public function _ _tostring() { return "Listener ".$this->v; }
	}

	$ll = new ListenerList();

	$ll->add( new SimpleListener( "a" ) );
	$ll->add( new SimpleListener( "b" ) );
	$ll->add( new SimpleListener( "c" ) );
	print("Listeners:\n\n");
	foreach( $ll as $listener )
{
	print( $listener );
	print( "\n" );
}

print("\nInvoking Listeners:\n\n");
$ll->invoke( null, "Some data" );
>?

The first section of the code defines a Listener interface for objects that are to be registered with ListenerList. The second part defines ListenerList, which is just a wrapper around an array with the addition of the add() and invoke() methods. The other methods all implement the Iterator interface. SimpleListener is just an implementation of the listener that prints when called.

Figure 6-5 shows the model for the code in this hack. ListenerList contains zero or more objects that implement the Listener interface. SimpleListener implements the Listener interface and just prints out a message whenever it's invoked.

Figure 6-5. The UML for ListenerList
Hack 53. Turn Any Object into an Array


6.4.2. Running the Hack

You run this hack on the command line using the command-line interpreter:

	% php iterator.php
	Listeners:
	Listener a
	Listener b
	Listener c

	Invoking Listeners:

	a invoked with with 'Some data'
	b invoked with with 'Some data'
	c invoked with with 'Some data'
	%

If you look at the end of the code from Example 6-5, you will see the tests that output here. The first test iterates through the list with a foreach statement. You see the result of this at the top of the run. The second section shows the result of the invoke() method being called on the ListenerList object.

The great thing about the Iterator interface is that you can now pass around complex interfaces in any case where you could only previously use arrays. Those array interfaces will still work, but now you can have additional methods as well.

6.4.3. See Also


Previous
Table of Contents
Next