为什么 PHP 的 call_user_func() 函数不支持引用传递?

2022-01-03 00:00:00 reference callback language-design php

为什么像call_user_func()这样的函数处理函数不支持通过引用传递参数?

Why don't the function handling functions like call_user_func() support passing parameters by reference?

文档 说的很简洁,比如请注意,call_user_func() 的参数不是通过引用传递的".在这种情况下,我认为 PHP 开发人员有某种原因禁用该功能.

The docs say terse things like "Note that the parameters for call_user_func() are not passed by reference." I assume the PHP devs had some kind of reason for disabling that capability in this case.

他们是否面临技术限制?它是一种语言设计选择吗?这是怎么发生的?

Were they facing a technical limitation? Was it a language design choice? How did this come about?

为了澄清这一点,这里有一个例子.

In order to clarify this, here is an example.

<?php

function more(&$var){ $var++; }

$count = 0;
print "The count is $count.
";

more($count);
print "The count is $count.
";

call_user_func('more', $count);
print "The count is $count.
";

// Output:
// The count is 0.
// The count is 1.
// The count is 1.

这运行正常;call_user_func 不会通过引用传递 $count,即使 more() 将其声明为引用变量.call_user_func 文档 清楚地表明这是它应该工作的方式.

This is functioning normally; call_user_func does not pass $count by reference, even though more() declared it as a referenced variable. The call_user_func documentation clearly says that this is the way it's supposed to work.

我很清楚我可以通过使用call_user_func_array('more', array(&$count))来获得我需要的效果.

I am well aware that I can get the effect I need by using call_user_func_array('more', array(&$count)).

问题是:为什么 call_user_func 设计要以这种方式工作?通过引用文档说仅函数定义就足以正确通过引用传递参数."call_user_func 的行为是一个例外.为什么?

The question is: why was call_user_func designed to work this way? The passing by reference documentation says that "Function definitions alone are enough to correctly pass the argument by reference." The behavior of call_user_func is an exception to that. Why?

推荐答案

答案深深地嵌入在 PHP 的模型中的引用工作方式中 - 不一定是实现,因为这可能会有很大差异,尤其是在 5.x 版本中.我相信你已经听过这些台词,它们不像 C 指针或 C++ 引用等......基本上当一个变量被分配或绑定时,它可以通过两种方式发生 - 要么通过值(如果新变量绑定到包含旧值副本的新框"),或通过引用(在这种情况下,新变量绑定到与旧值相同的值框).无论我们谈论的是变量、函数参数还是数组中的单元格,都是如此.

The answer is embedded deep down in the way references work in PHP's model - not necessarily the implementation, because that can vary a lot, particularly in the 5.x versions. I'm sure you've heard the lines, they're not like C pointers, or C++ references, etc etc... Basically when a variable is assigned or bound, it can happen in two ways - either by value (in which case the new variable is bound to a new 'box' containing a copy of the old value), or by reference (in which case the new variable is bound to the same value box as the old value). This is true whether we're talking about variables, or function arguments, or cells in arrays.

当您开始将引用传递给函数时,事情开始变得有点麻烦——显然目的是能够修改原始变量.很久以前,调用时传递引用(将引用传递给不期望的函数的能力)被弃用了,因为一个不知道它正在处理引用的函数可能不小心"' 修改输入.把它带到另一个层次,如果该函数调用第二个函数,它本身并不期待引用......那么一切最终都会断开连接.它可能有效,但不能保证,并且在某些 PHP 版本中可能会中断.

Things start to get a bit hairy when you start passing references into functions - obviously the intent is to be able to modify the original variables. Quite some time ago, call-time pass-by-reference (the ability to pass a reference into a function that wasn't expecting one) got deprecated, because a function that wasn't aware it was dealing with a reference might 'accidentally' modify the input. Taking it to another level, if that function calls a second function, that itself wasn't expecting a reference... then everything ends up getting disconnected. It might work, but it's not guaranteed, and may break in some PHP version.

这就是 call_user_func() 的用武之地.假设您将一个引用传递给它(并获得相关的调用时传递引用警告).然后您的引用绑定到一个新变量 - call_user_func() 本身的参数.然后当你的目标函数被调用时,它的参数没有绑定在你期望的地方.它们根本不受原始参数的约束.它们绑定到 call_user_func() 声明中的局部变量.call_user_func_array() 也需要小心.将引用放在数组单元格中可能会很麻烦 - 因为 PHP 使用写时复制"语义传递该数组,所以您无法确定该数组是否不会在您下面被修改,并且副本不会得到与原始参考分离.

This is where call_user_func() comes in. Suppose you pass a reference into it (and get the associated the call-time pass-by-reference warning). Then your reference gets bound to a new variable - the parameters of call_user_func() itself. Then when your target function is called, its parameters are not bound where you expect. They're not bound to the original parameters at all. They're bound to the local variables that are in the call_user_func() declaration. call_user_func_array() requires caution too. Putting a reference in an array cell could be trouble - since PHP passes that array with "copy-on-write" semantics, you can't be sure if the array won't get modified underneath you, and the copy won't get detached from the original reference.

我见过的最有见地的解释(帮助我了解引用)是在 PHP 'passing by reference' 手册的评论中:

The most insightful explanation I've seen (which helped me get my head around references) was in a comment on the PHP 'passing by reference' manual:

http://ca.php.net/manual/en/language.references.pass.php#99549

基本上逻辑是这样的.您将如何编写自己的 call_user_func() 版本?- 然后解释它如何与引用中断,以及当您避免调用时传递引用时它如何失败.换句话说,当您使用 call_user_func 时,调用函数的正确方式(指定值,并让 PHP 从函数声明中决定是传递值还是引用)将不起作用() - 您正在调用两个深层次的函数,第一个通过值,第二个通过引用第一个中的值.

Basically the logic goes like this. How would you write your own version of call_user_func() ? - and then explain how that breaks with references, and how it fails when you avoid call-time pass-by-reference. In other words, the right way to call functions (specify the value, and let PHP decide from the function declaration whether to pass value or reference) isn't going to work when you use call_user_func() - you're calling two functions deep, the first by value, and the second by reference to the values in the first.

深入了解这一点,您将对 PHP 引用有更深入的了解(如果可以,您将更有动力避开这一点).

Get your head around this, and you'll have a much deeper understanding of PHP references (and a much greater motivation to steer clear if you can).

相关文章