My JavaScript book is out! Don't miss the opportunity to upgrade your beginner or average dev skills.

Sunday, April 06, 2008

PHP - JavaScript like Object class

As I've wrote in last post, there's some JavaScript feature I would like to have in PHP too.
This time we will use a basic implementation of JavaScript Object constructor in PHP.
What we need to start is this class, based on SPL ArrayAccess interface.

class Object extends stdClass implements ArrayAccess {

// (C) Andrea Giammarchi - webreflection.blogspot.com - Mit Style License

// static public methods
static public function create(){
return new Object;
}
static public function parseJSON($json){
return self::create()->extend(json_decode($json));
}
static public function parseSource($source){
return self::create()->extend(unserialize($source));
}

// basic JavaScript like methods
public function extend(){
for($i = 0, $length = count($arguments = func_get_args()); $i < $length; $i++)
foreach($arguments[$i] as $key => $value)
$this->$key = $value;
return $this;
}
public function toJSONString(){
return json_encode($this);
}
public function toSource(){
return serialize($this);
}

// ArrayAccess interface methods
public function offsetExists($key){
return isset($this->$key);
}
public function offsetGet($key){
return $this->$key;
}
public function offsetSet($key, $value){
$this->$key = $value;
}
public function offsetUnset($key){
unset($this->$key);
}
}

The main goal of this class is to have a JS like literal object, and in this reduced version, with best possible performances for this kind of purpose.
Here is some example:

$o = new Object;
$o->test = "test";
echo $o->test === $o['test']; // 1
$o['other_test'] = 123;
echo $o->other_test; // 123

These instances are simple as useful and could be used instead of associative arrays.
The class contains 3 public static methods to perform common task during client/server interactions.

// factory pattern
$o = Object::create();

// factory with serialized string
$o = Object::parseSource(serialize(array('A'=>'A')));
echo $o->A; // A

// factory with JSON string
$o = Object::parseJSON('{"B":"B"}');
echo $o->B; // B


One of the common PHP error is to access to an associative array propery sending undefined constants instead of strings.

$a = array('A'=>'A');
echo $a[A]; // notice, defined constant possible ambiguity

// factory + extend
$o = Object::create()->extend($a);

// we have two ways to access to the same property
echo $o->A; // OK, output is A
echo $o['A']; // OK again ...

Of course, using associative wrong way to retrieve a property will cause a notice error again, but having the common instance "->" operator, why should we cause that notice?

Another interesting thing could be the usage of dynamic instances, and the ability to add methods (not possible with associative arrays) or use current one to share, save, or send these instances.

$me = new Object;
$me->name = 'Andrea';
$me->surname = 'Giammarchi';
$me->age = 29; // ... still ...

// simple interaction
echo '
';
foreach($me as $key => $value)
echo $key."\t".$value.PHP_EOL;
echo '
';


// or JSON / PHP serializzation
echo // {"name":"Andrea","surname":"Giammarchi","age":29}
$me->toJSONString().
PHP_EOL.
// O:6:"Object":3:{s:4:"name";s:6:"Andrea";s:7:"surname";s:10:"Giammarchi";s:3:"age";i:29;}
$me->toSource();

To have these functionalities in every day applications, we could think about this simple task:

$result = array();
$query = mysql_unbuffered_query(
'SELECT t.name AS "name", t.surname AS "surname", t.age AS "age" FROM table t',
$connection
);
while(@$row = mysql_fetch_assoc($query))
$result[] = Object::create()->extend($row);
echo 'First person name is '.$result[0]->name;


Of course, you can find a lot of different common situation where this kind of class could be useful, don't you?

No comments: