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

发布于 2024-11-14 07:55:30 字数 983 浏览 2 评论 0原文

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

文档说了一些简洁的话,例如“请注意,call_user_func() 的参数不是通过引用传递的。”我认为 PHP 开发人员在这种情况下禁用该功能有某种原因。

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

编辑:

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

<?php

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

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

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

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

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

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

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

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

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

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?

EDIT:

In order to clarify this, here is an example.

<?php

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

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

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

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

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

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.

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

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

冷了相思 2024-11-21 07:55:30

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

当您开始将引用传递给函数时,事情开始变得有点棘手 - 显然,目的是能够修改原始变量。很久以前,调用时传递引用(将引用传递到不期望的函数中的能力)已被弃用,因为不知道自己正在处理引用的函数可能会“意外地” ' 修改输入。把它带到另一个层次,如果该函数调用第二个函数,它本身并不期望引用......那么一切最终都会断开连接。它可能可以工作,但不能保证,并且可能在某些 PHP 版本中出现问题。

这就是 call_user_func() 发挥作用的地方。假设您向其中传递一个引用(并获取关联的调用时传递引用警告)。然后,您的引用将绑定到一个新变量 - call_user_func() 本身的参数。然后,当您的目标函数被调用时,其参数不会绑定到您期望的位置。它们根本不受原始参数的约束。它们绑定到 call_user_func() 声明中的局部变量。 call_user_func_array() 也需要谨慎。将引用放入数组单元格中可能会很麻烦 - 因为 PHP 使用“写时复制”语义传递该数组,因此您无法确定该数组是否不会在您下面进行修改,并且副本也不会得到与原始参考分离。

我见过的最有见地的解释(它帮助我了解参考资料)是在 PHP“通过参考传递”手册的评论中:

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

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

认真思考这一点,您将对 PHP 参考文献有更深入的了解(如果可以的话,您也会更有动力避开)。

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.

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.

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.

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

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.

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).

放血 2024-11-21 07:55:30

更新的答案:

您可以使用:

call_user_func('more', &$count)

达到与以下相同的效果:

call_user_func_array('more', array(&$count))

因此,我相信(毫无根据) call_user_func 只是编译器时间的捷径。 (即它在编译时被替换为后者)

为了给出我对您实际问题的看法“为什么call_user_func设计成这样工作?”:

它可能属于与“为什么是”相同的行一些方法strstr和其他str_replace?为什么是数组函数haystack、needle和字符串函数needle、haystack

这是因为 PHP 是由许多人设计的不同的人,在很长一段时间内,并且当时没有严格的标准

原始答案:

您也必须确保将数组内的变量设置为引用。

并记下 array(&$t) 部分:

function test(&$t) {
    $t++;
    echo '$t is '.$t.' inside function'.PHP_EOL;
}

$t = 0;
echo '$t is '.$t.' in global scope'.PHP_EOL;

test($t);

$t++;
echo '$t is '.$t.' in global scope'.PHP_EOL;

call_user_func_array('test', array(&$t));

$t++;
echo '$t is '.$t.' in global scope'.PHP_EOL;

应该输出:

$t is 0 in global scope
$t is 1 inside function
$t is 2 in global scope
$t is 3 inside function
$t is 4 in global scope

Updated Answer:

You can use:

call_user_func('more', &$count)

to achieve the same effect as:

call_user_func_array('more', array(&$count))

For this reason I believe (unfoundedly) that call_user_func is just a compiler time short cut. (i.e. it gets replaced with the later at compile time)

To give my view on you actual question "Why was call_user_func designed to work this way?":

It probably falls under the same lines as "Why is some methods strstr and other str_replace?, why is array functions haystack, needle and string functions needle, haystack?

Its because PHP was designed, by many different people, over a long period of time, and with no strict standards in place at the time.

Original Answer:

You must make sure you set the variable inside the array to a reference as well.

Try this and take note of the array(&$t) part:

function test(&$t) {
    $t++;
    echo '$t is '.$t.' inside function'.PHP_EOL;
}

$t = 0;
echo '$t is '.$t.' in global scope'.PHP_EOL;

test($t);

$t++;
echo '$t is '.$t.' in global scope'.PHP_EOL;

call_user_func_array('test', array(&$t));

$t++;
echo '$t is '.$t.' in global scope'.PHP_EOL;

Should output:

$t is 0 in global scope
$t is 1 inside function
$t is 2 in global scope
$t is 3 inside function
$t is 4 in global scope
不再见 2024-11-21 07:55:30

另一种可能的方法 - 引用语法保持“正确”的方式:

$data = 'some data';
$func = 'more';
$func($more);

function more(&$data) {
  // Do something with $data here...
}

Another possible way - the by-reference syntax stays the 'right' way:

$data = 'some data';
$func = 'more';
$func($more);

function more(&$data) {
  // Do something with $data here...
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文