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