在函数内部获取$this


Getting $this inside function

我在类外部有一个函数,我使用call_user_func从内部调用它,我的问题是我无法访问函数内部的类。如果有一种方法可以使$this在函数中工作,那么我需要一种在不传递参数的情况下访问类的方法。

除了将带有参数的类传递到函数之外的任何建议都将受到赞赏;

function test($arg1)
{
    $this->code = 10; // i know $this won't work here, 
                       // but i need a way to change $code 
                       // property from this function without 
                       // passing class into this function by using arguments
}
class MyClass
{
    public $code;
    public function call()
    {
         call_user_func("test", "argument1");
    }
}

如果你能在类中移动函数,你就可以这样调用它:

call_user_func(array($this, 'the_function'));

如果你有PHP5.3,你可以使用一个闭包:

$that = $this;
$function = function() use ($that) {
    // use $that instead of $this
};
call_user_func($function);

如果你可以添加一个参数,你可以这样做:

call_user_func('the_function', "argument1", $this);

如果函数要求传递$this作为第一个参数:

(如果函数有一个名为$__this的参数,则将$this作为参数传递(

function the_function($__this, $foo, $bar) {
}
$arguments = array('argument1');
$function = new ReflectionFunction('the_function');
$params = $function->getParameters();
if (count($params) > 0 && $params[0]->name == '__this') {
    array_unshift($arguments, $this);
}
$function->invokeArgs($arguments);

你可以对文档注释做同样的操作:

/**
 * @IWantThis
 */
function the_function($__this) {
}
$function = new ReflectionFunction('the_function');
if (preg_match('/@IWantThis/', $function->getDocComment()) { 
    ...

直接有两个选项:将对象传递给函数或将值传递给对象。我不知道是否是函数所做的唯一工作,所以这可能无法解决你的问题,只是在你写的将对象传递给函数不是一个选项时显示了另一种选择:

function test($arg1)
{
    return 10;
}
class MyClass
{
    public $code;
    public function call()
    {
         $this->code  = call_user_func("test", "argument1");
    }
}

如果你不想要其中的任何一个,你必须创建一些上下文,对象和函数,在中操作,以将两者相互连接。

您可以静态或动态地创建上下文。第一种通常与全局变量有关,随着时间的推移,全局变量可能导致代码难以维护,例如:

function test($arg1)
{
     global $object;
     $object->code = 10;
}
class MyClass
{
    public $code;
    public function call()
    {
         global $object;
         $object = $this;
         call_user_func("test", "argument1");
         unset($object);
    }
}

要创建一个更动态的上下文,您需要将所有内容封装到某个上下文中:

class contextCallback
{
    private $object;
    public function __construct($object)
    {
        $this->object = $object;
    }
    public function test()
    {
        $this->object = 10;
    }
}
class MyClass
{
    public $code;
    public function call()
    {
         $callback = new contextCallback($this);
         call_user_func(array($callback, 'test'), "argument1");
         # or:
         $callback->test("argument1");
    }
}

然而,我认为将$this作为参数传递要简单得多,而且代码要少得多。我不知道你为什么需要call_user_func,所以它一直是抽象的。

编辑:作为另一种选择,为了与具体类解耦,当您只需要设置值时,请考虑返回多个值:

function test($arg1)
{         
     $object->code = 10;
     return array($value, $object);
}
class MyClass
{
    public $code;
    public function call()
    {
         list($value, $object) = call_user_func("test", "argument1");
         foreach($object as $var => $value)
             $this->$var = $value;
    }
}

您有两个选择。使该方法成为MyClass对象的方法,或者使该对象成为函数的参数。

如果你有一个函数,你希望它做同样的事情,一次。这意味着,如果函数依赖于某种状态(如$this的值(,那么您需要找到一种方法来获得1。将其设为全局(此处不是选项(或2。将其作为参数传入。

无论如何,在该函数中您不需要$this,您需要一些对象,该对象恰好具有以$code的内容命名的属性(我认为这实际上是一个拼写错误。我认为您指的是$this->code而不是$this->$code(。

您可以使用闭包(php 5.3+(将对象引用"传递"给那些需要它的函数。
但是,您必须区分"旧"函数和"新"扩展函数。新函数仍然只能访问对象的公共属性/方法。

<?php
class MyClass
{
    public $code = 0;
    protected $functions=array();
    public function call($fn)
    {
        if ( is_string($fn) && isset($this->functions[$fn]) ) {
            $fn =   $this->functions[$fn];
        }
        if ( !is_callable($fn) ) {
            die('add error handler here');
        }
        $rv = call_user_func($fn, 'argument1');
    }
    public function loadFunctions($file) {
        $fnInit = require $file;
        $fns = $fnInit($this);
        $this->functions = array_merge($this->functions, $fns);
    }
}
$m = new MyClass;
$m->loadFunctions('ext.php');
$m->call('oldFn');
$m->call('foo');
$m->call('bar');
echo 'and now $m->code is: ', $m->code;

和ext.php:

<?php
function oldFn($arg) { // that's probably all you have to do atm to have a new "extension function" for your class
    echo "oldFn($arg)'n";
}

return function($obj) { // and that's more or less how the new functions would have to be declared/defined
    return array(
        'foo'=>function($arg1) use($obj) {
            echo "foo($arg1) + code: $obj->code'n";
            $obj->code = 1;
        },
        'bar'=>function($arg1) use($obj) {
            echo "bar($arg1) + code: $obj->code'n";
            $obj->code = 2;
        }
    );
};

打印

oldFn(argument1)
foo(argument1) + code: 0
bar(argument1) + code: 1
and now $m->code is: 2

(如果这在某种程度上对你来说可行的话,我可以写更多的文本……但现在不行;-(

call_user_func("test", array($this,"argument1"));

我不是100%,但你应该可以用访问它

function test($arg1)
{
    $arg1[0]->code = 10;
}

runkit是否适合您的需求,特别是runkit_function_redefine