note 99721 modified in function.create-function by danbrown
| From: | danbrown@php.net | Date: | Wed, 01 Sep 2010 19:37:35 +0000 |
| Subject: | note 99721 modified in function.create-function by danbrown | ||
| References: | 1 | Groups: | php.notes |
| Request: | Send a blank email to php-notes+get-172255@lists.php.net to get a copy of this message | ||
If you create a function that will only be used from an object context (i.e. you want a dynamic
method that can then call methods from the original object, still maintaining access to the
object's runtime values) then you can use the following functions I have created
(ob_lambda_func and ob_lambda) to enable the dynamic function to easily call *public* methods on the
object, in their runtime contexts:
<?php
/* the OB Lambda functions allow a lambda function to call a method from its callee's object */
if(!function_exists('ob_lambda_func')){
function ob_lambda_func($method, $args) {
if((phpversion()+0)<5.1 || (substr(phpversion(),2)+0)<1.1)
die("\nError: This script requires PHP v5.1.1+!\n");
$bt=debug_backtrace();
foreach($bt as $xsp) {
if(isset($xsp['object'])) {
if(!method_exists($xsp['object'], $method)) continue;
return call_user_func_array(array($xsp['object'], $method), $args);
}
}
if(!function_exists($method))
die("\nOB: Internal Error! ($method)");
return call_user_func_array($method, $args);
}}
if(!function_exists('ob_lambda')){
function ob_lambda($method) {
return create_function('','$args=func_get_args(); return
ob_lambda_func('.var_export($method, true).',$args);');
}}
/* Usage:
* For each callable method, store the results of ob_lambda('method_name')
* When a lambda function you call wants to access one of these methods,
* you need to pass it the result of ob_lambda for that method, the easiest
* way is to have one of the lambda functions arguments accept the result
* i.e.: $myFunc=ob_lambda('myMethod');
* $lf1=create_function('$func', '$func( ... args ... )');
* $lf1($myFunc);
* because create_function prepends a \0 to the function name, it is difficult
* to pass the result of ob_lambda to the target function without using an argument
*/
/* here is an example -- lambda function Foo::test can call Foo::Bar and Foo::Baz, and these */
class Foo {
private $bar, $baz, $test;
private $runtimeValue=0;
function __construct() {
$this->runtimeValue=rand();
$this->bar=ob_lambda('Bar');
$this->baz=ob_lambda('Baz');
$this->test=create_function(
'$bar, $baz',
'$bar("Hello, World!"); $baz();'
);
}
function Test() {
$fn=$this->test;
$fn($this->bar, $this->baz);
}
function Bar($a) {
echo "$a: Bar ($this->runtimeValue)\n";
}
function Baz() {
echo "and another! Baz ($this->runtimeValue)\n";
}
}
$foo=new Foo();
// Foo::Bar("Testing Other Method"); <-- this causes fatal error
// (using $this when not in object context)
$foo->Test();
?>
This snippet (with test) shows how the lambda function $foo->test can call Foo::Bar and Foo::Baz
without an explicit reference to the original object. The methods are run from their object context,
and so can access the runtime-modified variable $foo->runtimeValue, which is set to a random
number on construction.
The function definition for $foo->test is the following:
function ($bar, $baz) {
$bar("Hello, World!");
$baz;
}
As you can see, it is clean and simple, with no obvious artifacts from the use of OB Lambda
The expected output should be:
Hello, World!: Bar (440048505)
and another! Baz (440048505)
Or any other random number in place of 440048505
Make sure, before you change your code, that your target server has PHP 5.1.1+, or these functions
won't work and will return an error!
Hope this helps anyone as much as it has me! :)
--was--
If you create a function that will only be used from an object context (i.e. you want a dynamic
method that can then call methods from the original object, still maintaining access to the
object's runtime values) then you can use the following functions I have created
(ob_lambda_func and ob_lambda) to enable the dynamic function to easily call *public* methods on the
object, in their runtime contexts:
<?php
/* the OB Lambda functions allow a lambda function to call a method from its callee's object */
if(!function_exists('ob_lambda_func')){
function ob_lambda_func($method, $args) {
if((phpversion()+0)<5.1 || (substr(phpversion(),2)+0)<1.1)
die("\nError: This script requires PHP v5.1.1+!\n");
$bt=debug_backtrace();
foreach($bt as $xsp) {
if(isset($xsp['object'])) {
if(!method_exists($xsp['object'], $method)) continue;
call_user_func_array(array($xsp['object'], $method), $args);
return;
}
}
if(!function_exists($method))
die("\nOB: Internal Error! ($method)");
call_user_func_array($method, $args);
}}
if(!function_exists('ob_lambda')){
function ob_lambda($method) {
return create_function('','$args=func_get_args();
ob_lambda_func('.var_export($method, true).',$args);');
}}
/* Usage:
* For each callable method, store the results of ob_lambda('method_name')
* When a lambda function you call wants to access one of these methods,
* you need to pass it the result of ob_lambda for that method, the easiest
* way is to have one of the lambda functions arguments accept the result
* i.e.: $myFunc=ob_lambda('myMethod');
* $lf1=create_function('$func', '$func( ... args ... )');
* $lf1($myFunc);
* because create_function prepends a \0 to the function name, it is difficult
* to pass the result of ob_lambda to the target function without using an argument
*/
/* here is an example -- lambda function Foo::test can call Foo::Bar and Foo::Baz, and these */
class Foo {
private $bar, $baz, $test;
private $runtimeValue=0;
function __construct() {
$this->runtimeValue=rand();
$this->bar=ob_lambda('Bar');
$this->baz=ob_lambda('Baz');
$this->test=create_function(
'$bar, $baz',
'$bar("Hello, World!"); $baz();'
);
}
function Test() {
$fn=$this->test;
$fn($this->bar, $this->baz);
}
function Bar($a) {
echo "$a: Bar ($this->runtimeValue)\n";
}
function Baz() {
echo "and another! Baz ($this->runtimeValue)\n";
}
}
$foo=new Foo();
// Foo::Bar("Testing Other Method"); <-- this causes fatal error
// (using $this when not in object context)
$foo->Test();
?>
This snippet (with test) shows how the lambda function $foo->test can call Foo::Bar and Foo::Baz
without an explicit reference to the original object. The methods are run from their object context,
and so can access the runtime-modified variable $foo->runtimeValue, which is set to a random
number on construction.
The function definition for $foo->test is the following:
function ($bar, $baz) {
$bar("Hello, World!");
$baz;
}
As you can see, it is clean and simple, with no obvious artifacts from the use of OB Lambda
The expected output should be:
Hello, World!: Bar (440048505)
and another! Baz (440048505)
Or any other random number in place of 440048505
Make sure, before you change your code, that your target server has PHP 5.1.1+, or these functions
won't work and will return an error!
Hope this helps anyone as much as it has me! :)
http://php.net/manual/en/function.create-function.php