是否可以存储对对象方法的引用?

问题描述 投票:0回答:4

假设此类代码:

class Foo {
    function method() {
        echo 'works';
    }
}

有什么方法可以存储对

method
实例的
Foo
方法的引用吗?

我只是在试验和摆弄,我的目标是检查 PHP 是否允许调用

$FooInstance->method()
而无需每次都写
$FooInstance->
。我知道我可以为此编写一个函数包装器,但我更感兴趣的是获取对实例方法的引用。

例如,这个伪代码理论上会将

$foo->method
存储在
$method
变量中:

$foo = new Foo();
$method = $foo->method; //Undefined property: Foo::$method 
$method();

显然,由于

method
是一种方法,而我没有用
()
来调用它,解释器认为我正在寻找一个属性,因此这是行不通的。

我已经阅读了返回引用,但示例仅展示了如何返回对变量的引用,而不是方法。

因此,我调整了代码以将匿名函数存储在变量中并返回它:

class Foo {
    function &method() {
        $fn = function() {
            echo 'works';
        };
        return $fn;
    }
}

$foo = new Foo();
$method = &$foo->method();
$method();

这可行,但相当难看。另外,没有一种巧妙的方法可以一次性调用它,因为这似乎需要在调用之前将返回的函数存储在变量中:

$foo->method()();
($foo->method())();
是语法错误。

另外,我尝试直接返回匿名函数而不将其存储在变量中,但随后我收到以下通知:

注意:只有变量引用才应该通过引用返回

这是否意味着返回/存储对类实例方法的引用是不可能的/不鼓励的,或者我是否忽略了某些东西?


更新: 如果有必要,我不介意添加 getter,目标只是获得对该方法的引用。我什至尝试过:

class Foo {
    var $fn = function() {
        echo 'works';
    };
    function &method() {
        return $this->fn;
    }
}

但是从

unexpected 'function' (T_FUNCTION)
错误来看,我相信 PHP 明智地不允许属性存储函数。

我开始相信,如果不使用像

eval()
那样的丑陋黑客,我的目标就不容易实现。

php
4个回答
9
投票

是的。您必须使用具有两个值的数组:类实例(如果调用静态方法,则为类名称的字符串)和字符串形式的方法名称。这记录在 Callbacks Man page:

实例化对象的方法作为数组传递,其中包含索引 0 处的对象和索引 1 处的方法名称。

演示(键盘):

<?php
class Something {
    public function abc() {
        echo 'called';
    }
}

$some = new Something;

$meth = array($some, 'abc');

$meth(); // 'called'

请注意,这也适用于需要回调的内置程序(Codepad):

class Filter {
    public function doFilter($value) {
        return $value !== 3;
    }
}

$filter = new Filter;
$test = array(1,2,3,4,5);
var_dump(array_filter($test, array($filter, 'doFilter'))); // 'array(1,2,4,5)'

对于静态方法 - 请注意

'Filter'
而不是类的实例作为数组中的第一个元素 (Codepad):

class Filter {
    public static function doFilter($value) {
        return $value !== 3;
    }
}

$test = array(1,2,3,4,5);

var_dump(array_filter($test, array('Filter', 'doFilter'))); // 'array(1,2,4,5)'
// -------- or -----------
var_dump(array_filter($test, 'Filter::doFilter')); // As of PHP 5.2.3

4
投票

是的,可以。 PHP 有一个“可调用”伪类型,实际上,它要么只是一个字符串,要么是一个数组。有几个函数(我想到了

usort
)接受“回调”类型的参数:事实上,它们只需要一个函数名称,或一个对象方法对。

没错,字符串是可调用的:

$fn = "strlen";
$fn("string"); // returns 6

如上所述,也可以使用数组作为回调。在这种情况下,第一个元素必须是对象,第二个参数必须是方法名称:

$obj = new Foo();
$fn = array($obj, "method");
$fn(); // calls $obj->method()

以前,您必须使用

call_user_func
来调用它们,但最近版本中的语法糖使得可以直接对变量执行调用。

您可以在“可调用”文档页面阅读更多内容。


0
投票

不,据我所知,不可能在 PHP 中存储对方法的引用。将对象/类名称和方法名称存储在数组中是可行的,但它只是一个数组,没有任何特殊含义。您可以随意使用数组,例如:

$ref = [new My_Class(), "x"];
// all is fine here ...
$ref();
// but this also valid, now the 'reference' points to My_Other_Class::x()
// do you expect real reference to behave like this?
$ref[0] = new My_Other_Class();
$ref();
// this is also valid syntax, but it throws fatal error
$ref[0] = 1; 
$ref();
// let's assume My_Class::y() is a protected method, this won't work outside My_Class
$ref = [new My_Class(), 'y'];
$ref();
  • 由于将方法名称存储为字符串而导致松散语法检查,因此很容易出错。
  • 您无法通过这种方式可靠地传递对私有或受保护方法的引用(除非您从已经具有对该方法的正确访问权限的上下文中调用该引用)。

我个人更喜欢使用 lambda:

$ref = function() use($my_object) { $my_object->x(); }

如果您从内部执行此操作

$my_object
,由于可以访问
$this
,它会变得不那么笨重:

$ref = function() { $this->x(); } 
  • 这适用于受保护/私有方法
  • 在 IDE 中进行语法检查(错误更少)
  • 不幸的是它不太简洁

0
投票

从 PHP 8.1.0 开始,有 一流的可调用语法

class Foo {

  function method() {
    echo 'works' . PHP_EOL;
  }

  function methodWithArguments($arg) {
    echo "works: $arg";
  }

}

$foo = new Foo();

$methodRef = $foo->method(...);
$methodRef();

$methodWithArgumentsRef = $foo->methodWithArgument(...);
$methodWithArgumentsRef('abc');
© www.soinside.com 2019 - 2024. All rights reserved.