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

Is is possible to store a reference to an object method?

提问人:Fabrício Matté 提问时间:5/5/2013 最后编辑:Fabrício Matté 更新时间:3/25/2017 访问量:2183

问:

假设以下类代码:

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

有没有办法存储对实例方法的引用?methodFoo

我只是在试验和摆弄,我的目标是检查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())();

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

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

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


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

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

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

我开始相信,如果不使用丑陋的黑客,我的目标就不容易实现。eval()

PHP的

评论


答:

9赞 Bailey Parker 5/5/2013 #1

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

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

演示 (Codepad):

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

$some = new Something;

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

$meth(); // 'called'

请注意,这也适用于需要回调的内置项(代码板):

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'

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

评论

0赞 Fabrício Matté 5/5/2013
我见过一个字符串变量,但这是我第一次看到数组被调用。也感谢手册页链接。()=]
0赞 Bailey Parker 5/5/2013
@FabrícioMatté 我已更新以添加指向文档页面的链接。它与回调有关。这已经有一段时间了,但我认为它看起来非常丑陋。
0赞 Fabrício Matté 5/5/2013
如果你说这很丑陋,我希望你永远不必通读我的 JavaScript 片段。 再次感谢您的快速回答。;)
0赞 hobbs 5/5/2013
佑。在大多数语言中,执行此操作的普通方法是使用闭包来转换 eta-convert(就像几乎 PHP 一样$meth = function() { $some->abc() })
0赞 Bailey Parker 5/5/2013
@hobbs 同意了哟哟。调用数组(技术上是哈希)对我来说似乎也是不合逻辑的。我刚刚在文档中读到,5.2.3 允许您使用更像文档的语法来引用静态方法。如果这适用于实例(如)就好了,但我想这会与静态常量冲突。call_user_func('Something::someStaticMethod')call_user_func('$instance::someInstanceMethod')
4赞 zneak 5/5/2013 #2

是的, 你可以的。PHP 有一个“可调用”的伪类型,实际上它只是一个字符串或数组。有几个函数(我想到了 usort)接受“回调”类型的参数:事实上,它们只需要一个函数名称,或者一个对象-方法对。

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

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

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

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

以前,您必须使用它们来调用它们,但最新版本中的语法糖可以直接对变量执行调用。call_user_func

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

评论

0赞 Fabrício Matté 5/5/2013
是的,我知道字符串包含函数名称,但这是我第一次看到数组语法。+1 对于详细答案,我会接受这个,但@PhpMyCoder先回答。
0赞 matt 3/25/2017 #3

不,据我所知,不可能在 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();
  • 由于将方法名称存储为字符串,因此容易出现错误,因为语法检查松动。
  • 无法以这种方式可靠地传递对私有方法或受保护方法的引用(除非从已对方法具有适当访问权限的上下文调用引用)。

就我个人而言,我更喜欢使用 lambdas:

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

如果您从内部执行此操作,则由于可以访问以下内容,它会变得不那么笨拙:$my_object$this

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