ArrayObject vs Array Access

7 11 2012

Floral Swirl

By Aster_Tataricus.JPG: Pascalou petit derivative work: Nevit Dilmen (Aster_Tataricus.JPG) [CC-BY-SA-3.0-2.5-2.0-1.0 (http://creativecommons.org/licenses/by-sa/3.0) or GFDL (http://www.gnu.org/copyleft/fdl.html)%5D, via Wikimedia Commons

I recently dusted off one of my older PHP books, PHP Hacks (O’reilly), something that I’m sure at the time must have seemed like a most compelling purchase.  My copy is 7 years old — would it have anything pertinent to say after all these years?  As I  read a particular code example,  I  kept thinking about what a great convenience the Standard PHP Library (SPL) is.  Instead of having to write the code yourself to handle an object as if it were an array, you can use either the ArrayObject class or else use the Array Access interface.  If you follow the example in the PHP Hacks, pp 224-226, it might seem like an awful lot of code just to be able to iterate through an object’s properties as if they were elements of an array.  I rewrote the code to take advantage of the SPL ArrayObject class.  This class allows you to use array notation and to iterate an object with a foreach loop because it implements some useful interfaces, such as Traversable and ArrayAccess (see http://php.net/manual/en/class.arrayobject.php for more details).

<?php
interface Listener {
     public function invoke( $data );
}
class SimpleListenerList extends ArrayObject {
public function execute( $data ) {
 foreach( $this as $listener ){
      $listener->invoke( $data );
 }
 }
}//end class

class SimpleListener implements Listener {
    private $v;
    public function __construct( $v) {
       $this->v = $v;
    }
    public function invoke( $data ) {
       echo( $this->v." invoked with '$data' \n" );
   }
   public function __toString() {
       return $this->v;
   }
 }
 $sll = new SimpleListenerList();
 $sll[] = new SimpleListener("alpha");
 $sll[] = new SimpleListener("beta");
 $sll[] = new SimpleListener("gamma");
 echo "The simple listeners are as follows:";
 foreach($sll as $l) {
    print $l; // __toString gets activated here
    print "\n";
 }
echo '<br>';
$sll->execute("Some interesting data");

Unlike the example in PHP Hacks, ArrayObject is a class that already has implemented everything you need to use an object just as if it were an array. In the above example, SimpleListenerList is a class which inheritsa lot of methods from ArrayObject, and it has one of its own that simply has each listener become invoked. The execute method of SimpleListenerList contains a foreach loop that works because the object has a traversable interface.  As the loop iterates over the list of simple listener objects, each one calls its invoke method and passes some data.  The method prints a string that informs the user what the value is of its property $v and what data was passed. The application starts out by creating an object that is a simple listener list.  Next three simple listeners are created and assigned to the list as if it were an array. Note, each listener is initialized with a different value.  The name of each listener next appears.  Then the ball really starting rolling when the list object executes after having received “Some interesting data”.

I wondered about whether I could also use  the ArrayAccess interface for this same purpose.  It’s possible, but it requires much more code and  iteration of the list object is unfeasible  using a foreach loop.  Here’s the code I used:

<?php
interface Listener {
   public function invoke( $data );
}
class SimpleListenerList implements ArrayAccess {
   private $container = array();
   public function execute( $data ) {
     foreach( $this->container as $listener ){
       $listener->invoke( $data );
     }
   }
   public function offsetSet($offset, $value) {
     if (is_null($offset)) { $this->container[] = $value;
     }
     else
     {
       $this->container[$offset] = $value;
     }
   }
   public function offsetExists($offset) {
     return isset($this->container[$offset]);
   }
   public function offsetUnset($offset) {
      unset($this->container[$offset]);
   }
   public function offsetGet($offset) {
   return isset($this->container[$offset]) ? $this->container[$offset] : null;
   }
}//end class
class SimpleListener implements Listener {
   private $v;
   public function __construct( $v) {
     $this->v = $v;
   }
   public function invoke( $data ) {
     echo( $this->v." invoked with '$data' \n" );
   }
   public function __toString() {
     return $this->v;
   }
}
$list_container = new SimpleListenerList();
$list_container[] = new SimpleListener("alpha");
$list_container[] = new SimpleListener("beta");
$list_container[] = new SimpleListener("gamma");
for ($i=0, $max=3; $i < $max; $i++) {
   print $list_container[$i]; // __toString gets activated here
   print "\n";
}
$list_container->execute("Something to talk about");

This last example requires far more code than the previous example, and in the end the code is less flexible. All the methods of the ArrayAccess Interface you have to implement yourself. The result is that you end up with limited functionality because the class implementing ArrayAccess is not traversable, so you can’t use a foreach loop. Not unless you decide to have your class implement the Traverable Interface which means of course implementing additional methods, such as current(), key(), rewind(), etc. Because the object is not traversable, one has to use a for-loop to have the list cause each listener to invoke. If you want an object with maximum utility to be handled like an array, I suggest creating a class that extends ArrayObject. On the other hand, if you want to work with an object like an array and you are only interested in using array notation with the object and possibly unsetting an object’s property or checking for its existence, then ArrayAccess might be a better choice, particularly if you need to customize the way object properties are read or set/unset. This would also be a good choice if you want the code to do somehting special in case a property does not exist.

Note, the code in PHP Hacks at the aforementioned specified pages is incidentally still good — I was able to run it under PHP5.4.

Happy coding!

This work is licensed under a Creative Commons License

Advertisements

Actions

Information

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s




%d bloggers like this: