tiistai 6. elokuuta 2013

PHP: Magic methods and ArrayObject

On of the irritating sides of PHP is using lot of functions over methods. People familiar with javascript programming knows how easy it is for example to filter, split or join arrays. They are done with methods, while in PHP they are done with functions. Another thing which people often complain is irregular order of parameters in functions, for example array_map takes callback as first argument and an array as second, while array_walk takes an array as first argument and callback as second.

When programming with Javascript I like the fact that an array is an object, so much, that I created similar array type of object in PHP. PHP is branded to be dynamic language, in this post I test how dynamic the language can be by creating Javascript-like arrayobject. I created a class which extends ArrayObject class. ArrayObject gives ability to objects to act as array (they can go through foreach etc). With magic methods, I was able to implement all array_* functions as methods for arrayobject.

class Arr extends ArrayObject {
private $container=array();
public function __construct($arr) {
parent::__construct($arr);
$this->container=$arr;
}
public function getContainer() {
return $this->container;
}
    public function offsetSet($offset, $value) {
        if (is_null($offset)) {
            $this->container[] = $value;
        } else {
            $this->container[$offset] = $value;
        }
parent::offsetSet($offset, $value);
    }
    public function offsetUnset($offset) {
        unset($this->container[$offset]);
parent::offsetUnset($offset);
    }
public function __call($name, $args) {
if(function_exists('array_'.$name)) {
foreach($args as $key=> $ar) {
if(is_object($ar) && get_class($ar)=='Arr')
$args[$key]=$ar->getContainer();
}
set_error_handler(function($a,$b,$c,$d) {throw new Exception($b);});
try {
$ref=new ReflectionFunction('array_'.$name);
if(current($ref->getParameters())->isPassedByReference()) //walk
$newargs=array(&$this->container);
else
$newargs=array((array)$this);
foreach($args as $arg) {
$newargs[]=$arg;
}
$r=call_user_func_array('array_'.$name,$newargs);
} catch (Exception $e) {
array_splice( $args, 2, 0, array((array)$this));
$r=call_user_func_array('array_'.$name,$args);
}
restore_error_handler();
if(is_array($r))
return new Arr($r);
return $r;
}
}
 }


To use the class, all we need to do is wrap an array with Arr class:

$fruits = new Arr(array("lemon", "orange", "banana", "apple"));

Now we have $fruits arrayobject, which have all "array_*" functions as method. There are over seventy array_* functions which we can now use as method. Lets see how to use it:

$fruits->change_key_case(CASE_UPPER)->filter()->walk(function($value,$key) {
     echo $key.': '.$value."\r\n";
});

In example above, all keys in $fruits array are changed to uppercase, then empty members are filtered out, finally a function is applied to all members of an array. Now lets compare how same would be done with native array

array_walk(array_filter(array_change_key_case($fruits)),function($value,$key) {
 echo $key.': '.$value."\r\n";
 })

This seems rather interesting, as 49 lines of code change the way and idea how PHP code can be written. PHP is dynamic language after all.

Ei kommentteja:

Lähetä kommentti