What could be better than chainable operators in jQuery, .addClass(”beautiful”).show(”slow”)-style? Almost nothing, but adding those to PHP would make code cleaner and life easier.

At first sight, implementing these looks almost impossible, but after a quick look at extensions we notice that SPL could offer something similar, if combined with magic methods. Let’s implement ArrayAccess interface and see how it works:

$fruits = new Chain(array(
           new String('apple'),
           new String('mango'),
           new String('kiwi')
)); 

echo "First fruit: ";
echo $fruits[0];
echo "\n"; 

echo "All fruits: ";
echo $fruits->toUpperCase()->join();
echo "\n";

Outputs:

First fruit: apple
All fruits: APPLE, MANGO, KIWI

Beautiful, eh?

Here is a complete listing:


class Chain implements ArrayAccess {
    private $items = array(); 

    public function __construct($items = array()) {
      if (!is_array($items)) return;
      $this->items = $items;
    } 

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

    public function __call($method, $args) {
      if (count($this->items)
            && !method_exists($this->items[0], $method)) {
        throw new BadMethodCallException();
      } 

      $returnChain = new Chain();
      foreach($this->items as $item) {
        $returnChain->add(call_user_func_array(array($item, $method), $args));
      }
      return $returnChain;
    } 

    public function rewind() {
      reset($this->items);
    } 

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

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

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

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

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

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

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

    // convenience method
    public function join($separator = ', ') {
      return implode($this->items, $separator);
    }
} 

class String{
    private $s; 

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

    public function toUpperCase() {
      $this->s = strtoupper($this->s);
      return $this;
    } 

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

} 

$fruits = new Chain(array(
              new String('apple'),
              new String('mango'),
              new String('kiwi')
)); 

echo "First fruit: ";
echo $fruits[0];
echo "\n"; 

echo "All fruits: ";
echo $fruits->toUpperCase()->join();
echo "\n";