( ! ) Deprecated: Function set_magic_quotes_runtime() is deprecated in /home/charles/public_html/blarg/index.php on line 17
Call Stack
#TimeMemoryFunctionLocation
10.0001638544{main}( )../index.php:0

( ! ) Deprecated: Function set_magic_quotes_runtime() is deprecated in /home/charles/public_html/blarg/mysql.inc.php on line 7
Call Stack
#TimeMemoryFunctionLocation
10.0001638544{main}( )../index.php:0
20.0006640248include_once( '/home/charles/public_html/blarg/lib.inc.php' )../index.php:29
30.0007648664include_once( '/home/charles/public_html/blarg/mysql.inc.php' )../lib.inc.php:7
Plehnetoid - The Blog of of Charles Capps
 

The PHP5 ArrayObject

Work Fri Dec 29, 2006 @ 17:20 Pacific

It's a rare time when I can actually praise PHP. As a native Perl hacker, I've come to hate many aspects of the language because they just seem so horribly wrong. That isn't to say that there aren't things that are done right, it's just that the irritations are more than the benefits.

Here's a prime example. PHP5 allows what it calls overloading of object methods and properties. It's not what I'm used to thinking of when I hear the term "overload," but it's pretty interesting.

The long and short of it is that you can have code be called whenever:
1) A method is called that has not been defined
2) A property is accessed that does not exist or has not been declared public.

There's a high degree of utility here, if it's done right. An actual example that we use at work is using __get and __set to keep track of property changes, so that when the object is saved to the database, we can log what changed without actually comparing the object to itself, or keeping revisioning within the object.

Unfortunately PHP, logically, doesn't do it right. When the property is an array, __set is never called. This is because __get (or direct access) will return the array by reference, so changing an index in the array is done directly.

I'd previously blown off the Standard PHP Library as nothing more than OO wankery that was mostly useless in day to day coding. And while I still maintain that opinion to some extent, it does contain one gem that can fix the __set problem: ArrayObject.

An ArrayObject is not unlike a Perl tied array. There are four really important methods - offsetExists, offsetGet, offsetSet, and offsetUnset. There are a handful of others, but those are the interesting ones. The object is treated as an honest to goodness array by all PHP functions, and can be accessed directly as an array. Any additional methods can also be accessed directly through the normal object syntax.

(For additional fun, you can define its own __get and __set that call offsetGet and offsetSet so you can refer to each index in the array using both object syntax and array syntax.)

So how does this solve the __set problem? Simple -- override the default offsetSet method with some variety of logging, and add a method to fetch the changelog.

class LoggingArray extends ArrayObject {

private $log = array();

function offsetSet($index, $value) {
$old_value = $this->offsetGet($index);
if($old_value != $value)
$this->log[$index] = $old_value;
return parent::offsetSet($index, $value);
} // end offsetSet

function get_log() {
$log_array = array();
foreach($this->log as $k => $v) {
$log_array[] = "'$k' changed from '$v' to '{$this->offsetGet($k)}'";
} // end foreach
return join("\n", $log_array);
} // end get_log

} // end CC_Array

class Example {
public $test;
function __construct() {
$this->test = new LoggingArray;
} // end __construct
} // end Example

$x = new Example;
$x->test[0] = 2;
$x->test["hi"] = "there";
echo $x->test->get_log();


It'll be interesting to see how or if this will work in practice, and what kind of overhead is introduced by all those method calls.

[]

Comments:
Software, Content, and Design © 2001-2004 Charles Capps