Viktors Rotanovs home

jQuery-style Chainability in PHP, Using SPL

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:

<?php
$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’s complete source code:

<?php
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";
?>
blog comments powered by Disqus
Fork me on GitHub