Tuesday, March 2, 2010

Dynamically adding methods in PHP :: Base Object :: Part 3

If you are a seasoned javascript developer, coding in PHP must feel like riding a bike in a straight jacket. Some of the features that you miss so much may still be added. If not through direct syntax, but through your own ingenuity.

In this article we will enable dynamic methods. Worth mentioning that this feature will only work in PHP 5.3 since closures were added only at that point.

So, add the following method to your class Object:

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);
    }
}
Now you can add dynamic methods almost like in JavaScript:
$oMyObject = new Object();
$oMyObject->myNewMethod = function($owner, $someparam) { print $someparam; };
$oMyObject->myNewMethod('Yay! Javascript-style methods!');
Note the extra "$owner" parameter. It is reserved to use instead of "$this" in this implementation. Closure implementation in php is still not smart enough to accept $this from the scope of the object they are called from. So, now you are like: "huh? oooh! yeah! ...but...". Take a moment to digest it. Breathe. To answer some questions that may be forming in your mind: Yes, it does all the things a method should do: 1. As I mentioned, use $owner instead of $this, and it will be provided for you. Say if you have a protected or private property $myPrivateProp, you can access it like so:
$oMyObject->getPrivateProp = function($owner) { return $owner->myPrivateProp; };
2. Pass by reference is supported if you specify it in both places: definition and call. Like so:
$ref = 1;
    $oMyObject->test = function($owner, &$ref) { return $ref = 2; };
    print $ref; // outputs: 1
    print $oMyObject->test(&$ref);
    print $ref; // outputs: 2

No comments: