PHP5: Predefined Interfaces

Note: This article serves as preliminary for SPL Interfaces article to be published later on.

I wanted to provide good overview of (highly under-used) Standard PHP Library (SPL) starting with interfaces. However, without firstly discussing predefined interfaces, which come bundled with each and every PHP distribution, discussion of SPL-provided ones seemed to be incomplete.

To be covered in this article:

 
The Traversable Interface

Basically this interface is used to mark that object of the class can be used in foreach i.e. it could be traversed. This interface is internal in a sense that you cannot use it directly in your scripts, instead you have to implement either Iterator or IteratorAggregate interfaces (which extend the Traversable). Since the interface is not implementable within your PHP classes, it’s synopsis does not enforce any methods:

Traversable
{}

 
The Iterator Interface

Extends the Traversable, this interface can be used to produce objects that can iterate themselves. As a rule of thumb: if your object holds number of some items, and you need to access them individually in foreach, Iterator (or IteratorAggregate for that matter) is the good bid. Here is the synopsis:

Iterator extends Traversable {
    /* Methods */
    abstract public mixed current ( void )  // return current element's value
    abstract public scalar key ( void )     // return current element's key
    abstract public void next ( void )      // move internal pointer forward
    abstract public void rewind ( void )    // move to the beginning
    abstract public boolean valid ( void )  // check if current position is valid
}

To illustrate, here is sample Iterator implementation and usage:

class Item
{
    protected $title = null;

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

    public function __toString()
    {
        return $this->title;
    }
}

class Items implements Iterator
{
    /**
     * List of contained items
     * @var array
     */
    protected $items = array();

    /**
     * Current iterator position
     * @var int
     */
    protected $pos = 0;

    public function add(Item $item)
    {
        $this->items[] = $item;
        return $this;
    }

    /**
     * Iterator methods
     */
    public function current()
    {
        return $this->items[$this->pos];
    }

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

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

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

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

$items = new Items();
$items->add(new Item('milk'))
      ->add(new Item('butter'))
      ->add(new Item('bread'));

foreach($items as $key => $item) {
    printf('%d - %s ', $key, $item);
}
// Output:
// 0 - milk
// 1 - butter
// 2 - bread

Pretty simple, isn’t it? Granted, it takes time to get used to, but once you do, you would never look back.

 
The IteratorAggregate Interface

PHP5 comes with a lot of predefined iterators (and SPL adds to the list), so you rarely need to write your own. For situations when there’s already suitable iterator for your object (either pre-build or written by you), there’s little sense to implement Iterator interface, instead it’s just enough to implement the IteratorAggregate, which allows you to use external iterators. Here is interface synopsis:

IteratorAggregate extends Traversable {
    /* Methods */
    abstract public Traversable getIterator( void )
}

As you see, all you have to do is to provide getIterator() method, which would return suitable instance of iterator to be used for traversal:

class Numbers implements IteratorAggregate
{
    protected $numbers = array(1, 2, 3);

    public function getIterator()
    {
        return new ArrayIterator($this->numbers);
    }
}

$nums = new Numbers();
foreach($nums as $k=>$v) {
    printf('%d - %d ', $k, $v);
}

// Output:
//0 - 1
//1 - 2
//2 - 3

I used predefined ArrayIterator iterator, which allows to traverse arrays and objects. You can use your own iterator as well (the only requirement is that it implements on Traversable).

 
The ArrayAccess Interface

One of the coolest interfaces that come with PHP5, ArrayAccess allows you to interact with your objects as if they were arrays. If you are familiar with C++, it’s like overloading [] operator (well, almost). Anyway, here is interface synopsis:

ArrayAccess {
    // whether an offset exists
    abstract public boolean offsetExists( mixed $offset )
    // returns the value at specified offset
    abstract public mixed offsetGet( mixed $offset )
    // assigns a value to the specified offset
    abstract public void offsetSet( mixed $offset , mixed $value )
    // unsets an offset
    abstract public void offsetUnset( mixed $offset )
}

Don’t worry if interface doesn’t seem simple at first, everything would clear up once you review the example:

class Config implements ArrayAccess
{
    protected $options = array(
        'username' => 'torio',
        'uri'      => 'http://www.phpmag.ru/'
    );

    /**
     * ArrayAccess implementation
     */
    public function offsetExists($offset)
    {
        return isset($this->options[$offset]);
    }

    public function offsetGet($offset )
    {
        return $this->options[$offset];
    }

    public function offsetSet($offset, $value )
    {
        $this->options[$offset] = $value;
    }

    public function offsetUnset($offset )
    {
        unset($this->options[$offset]);
    }
}

$config = new Config();
if(isset($config['uri'])) { // offsetExists() is called internally
    printf('URI: %s ', $config['uri']);   // offsetGet()
}
$config['username'] = 'vic';                    // offsetSet()
printf('Username: %s', $config['username']);

unset($config['uri']);                          // offsetUnset()
if(!isset($config['uri'])) {
    echo 'Offset deleted..';
}

// Output:
// URI: http://www.phpmag.ru/
// Username: vic
// Offset deleted..

Note: As of PHP 5.3 it is possible to return the value at a given offset by reference.

 
The Serializable Interface

This is the last of predefined interfaces, and is actually quite a simple one (if you are familiar with data serialization). If you want your object to control its serialization/unserialization process you can either use magic methods __sleep()/__wakeup() or, more explicitly, just to implement the Serializable interface:

Serializable {
    /* Methods */
    abstract public string serialize( void )
    abstract public mixed unserialize( string $serialized )
}

Here is trivial example:

class Item implements Serializable
{
    protected $title = null;

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

    public function getTitle()
    {
        return $this->title;
    }

    /**
     * Serializable implementation
     */
    public function serialize()
    {
        return serialize(array($this->title));
    }

    public function unserialize($serialized )
    {
        list($title) = unserialize($serialized);
        // this method is invoked as constructor, if you need to use
        // default constructor, invoke it manually:
        $this->__construct($title);
    }

}

$serialized = serialize(new Item('milk'));
$obj = unserialize($serialized);
printf('Title: %s ', $obj->getTitle()); // Title: milk

That’s about it! If you liked the article, consider subscribing to RSS, so that you can follow up the incoming SPL tutorials.

  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • Reddit
  • Technorati
  • email
  • Print
  • DZone
  • eKudos
  • LinkedIn
  • StumbleUpon
  • Tumblr
  • Twitter
, ,
Trackback

6 comments untill now

  1. I think that also SplQueue and SplStack could be defined as interfaces, but there is a lack of interfaces in php: I miss Collection, List, Set, Map…

  2. [...] interfaces presupposes that you know standard interfaces that come build-in with PHP5. So, I wrote preliminary article discussing them – I consider it to be a prerequisite for good understanding of the current [...]

  3. Giorgio,
    SplQueue and SplStack are data-structures, not interfaces i.e. they provide implementation (and a lot of :) ).

    As about missing data-structures, well, it depends on what you knew before coming into PHP – if you have some well-abstracted language under your belt..mm, PHP wouldn’t make you very happy. In essence, it is really simple language that does its job. It’s only with PHP5 any serious attempts started to make PHP meet enterprise (due to its popularity, language needed quite an update to suit the growing practitioners’ needs).

  4. [...] PHP5: Predefined Interfaces [...]

  5. [...] PHP5: Predefined Interfaces [...]

  6. [...] PHP5: Predefined Interfaces [...]

Add your comment now