Sure we can. Unfortunately, the __staticCall, __staticGet and __staticSet are not implemented in PHP 5.3, so we will have to improvise while still keeping the syntax pretty.
I am not going to list all the multiple modifications I did to the base object class, I will just dump the whole thing here, take a look:
class Object {
/**************** PRIVATE ****************/
/**************** PROTECTED **************/
protected static $_aClassProperties = array();
protected $_aProperties = array();
/**************** STATIC ****************/
public static function construct() {
$aArgs = func_get_args();
$nArgCount = count($aArgs);
$aArgTokens = array();
for ($i=0; $i<$nArgCount; $i++) {
$aArgTokens[] = '$aArgs['.$i.']';
}
$sArgTokens = implode(',', $aArgTokens);
$sEval = 'return new '.get_called_class().'('.$sArgTokens.');';
return eval($sEval);
}
/**************** PUBLIC ****************/
public function __construct() {
if (!isset(self::$_aClassProperties[get_called_class()])) {
self::$_aClassProperties[get_called_class()] = array();
}
$this->extend(self::$_aClassProperties[get_called_class()]);
}
public function __call($sMethod, $aArgs) {
if (isset($this->_aProperties[$sMethod])) {
$f = $this->_aProperties[$sMethod];
$nArgCount = count($aArgs);
$aArgTokens = array('$this');
for ($i=0; $i<$nArgCount; $i++) {
$aArgTokens[] = '$aArgs['.$i.']';
}
$sArgTokens = implode(',', $aArgTokens);
$sEval = 'return $f('.$sArgTokens.');';
return eval($sEval);
} else {
throw new Exception('Method '.$sMethod.' does not exist in class '.get_class($this));
}
}
public function __get($sProperty) {
return isset($this->_aProperties[$sProperty])
? $this->_aProperties[$sProperty]
: null;
}
public function __set($sProperty, $mValue) {
$this->_aProperties[$sProperty] = $mValue;
return $mValue;
}
public function extend($mBase) {
$sClass = get_called_class();
if (!isset(self::$_aClassProperties[$sClass])) {
self::$_aClassProperties[$sClass] = array();
}
if ($this) { // object call
if (!is_array($mBase)) {
$mBase = $mBase->_aProperties;
}
$this->_aProperties = array_merge(
$this->_aProperties,
$mBase
);
} else { // static call
if (is_string($mBase)) { // class name is supplied
$mBase = self::$_aClassProperties[$mBase];
}
self::$_aClassProperties[$sClass] = array_merge(
self::$_aClassProperties[$sClass],
$mBase
);
}
}
}
Suffice it to say that I have made the following things possible:
class A extends Object {};
A::extend(array(
'method3' => function($owner) { print 'method3'; }
));
class B extends Object {};
B::extend('A');
$o1 = Object::construct();
$o1->method1 = function($owner) { return 'method1'; };
$o2 = B::construct();
$o2->method2 = function($owner) { return 'method2'; };
$o2->extend($o1);
print $o2->method1(); // output: method1
print $o2->method2(); // output: method2
print $o2->method3(); // output: method3
To rephrase the above: now we can use the same extend() method to respond to static calls. Specifically two types of static calls extend([string ClassName]) and extend([array aProperties]).
All the properties extended at class level are copied to the object when it is instantiated. Of course if a constructor is overridden in subclass, it would have to call parent::__construct().
Cool?
1 comment:
Post a Comment