I spent a lot of time thinking, how to emulate some PHP5's object model features in older PHP4. How get rid tons of ampersands in my source codes. How force objects to not copy itself every time.
So, there is solution.
Look at this example:
class MyClass {
var $parent;
var $children = array();
// this is PHP5 constructor
function __construct($parent = null)
{
if ($parent !== null) {
$this->parent = $parent;
$parent->addChild($this);
}
}
function addChild($child)
{
$this->children[] = $child;
}
}
$parent = new MyClass();
$child1 = new MyClass($parent);
$child2 = new MyClass($parent);
In PHP5 it results in:
$parent = MyClass Object ( [parent] => null [children] => Array ( [0] => MyClass Object ( [parent] => MyClass Object *RECURSION* [children] => Array () ) [1] => MyClass Object ( [parent] => MyClass Object *RECURSION* [children] => Array () ) ) )
But in PHP4 $parent->children will remain empty. Because of “copying” object model and PHP5-like constructor.
$parent = MyClass Object ( [parent] => null [children] => Array () )
The trick
Ladies and gentlemen, now I'll force this example to work in PHP4. And it is very easy!
require_once 'php4compat.php';
class MyClass extends compatClass4 {
.....
}
All the magic is hidden in php4compat.php file. You can download it.
How it works? Php4Compat checks PHP version. For PHP5 it creates empty class compatClass4, with no methods or variables, just for compatibility purposes. But in PHP4 it creates “magic” class compatClass4 and function clone().
Class compatClass4 declares PHP4 constructor compatClass4(). Its task is turn each object's variables into references and call PHP5-like constructor __construct().
This way is very simple, but if you don't like “extends” in class definition, you may like this equivalent variant:
class MyClass {
/* PHP 5 constructor */
function __construct()
{
}
.....
/* PHP 4 constructor */
function MyClass()
{
// generate references
foreach ($this as $key => $foo)
$GLOBALS['$$HIDDEN$$'][] = & $this->$key;
// call php5 constructor
$args = func_get_args();
call_user_func_array(array(&$this, '__construct'), $args);
}
}
In other words, the only you need to do is to add this special PHP4-like constructor into class definition. And objects in PHP4 will behave as they in PHP5.
Princip and explanation
It uses this princip:
// notice, this argument is not passed by reference
function changeFirst($arr) {
$arr[0] = 'CHANGED';
}
// array stay unchanged:
$arr = array('first', 'second');
changeFirst($arr);
// $arr = array('first', 'second')
// and now array will be changed
$arr = array('first', 'second');
$x = 'test';
$arr[0] = &$x;
changeFirst($arr);
// $arr = array('CHANGED', 'second')
// now too
$arr = array('first', 'second');
$x = & $arr[0];
changeFirst($arr);
// $arr = array('CHANGED', 'second')
// (in this example you can replace arrays by objects)
Object clonning
PHP4 & PHP5 compatible way is:
....
$parent = new MyClass();
$dolly = clone ($parent);
$child1 = new MyClass($parent);
$child2 = new MyClass($dolly);
....
PHP5 has clone keyword, PHP4 has function clone() – therefore we must use parentheses.
Known limitations
- It is not possible to unset or creates non-declared object's variables
- It is not possible to assign references into object's variables
ad 1)
will not work:
class MyClass extends compatClass4 {
}
$object = new MyClass()
$object->var = 10; // $var is not declared!
correct:
class MyClass extends compatClass4 {
var $var;
}
$object = new MyClass()
$object->var = 10;
Notice: I think it is not serious limitation, because good programmers use only declared variables 🙂
ad 2)
will not work:
$object = new MyClass()
$b = 'La Trine';
$object->var = & $b;
correct:
$object = new MyClass()
$b = & $object->var;
$b = 'La Trine';
or:
$object = new MyClass()
$b = 'La Trine';
$object->var = array( &$b );
That's all folks 🙂