使用引用来布置简单的函数是一种好习惯

Jim*_*988 5 php pass-by-reference

我有一个功能:

$query = "SELECT * from lol";
database_query( $query );
Run Code Online (Sandbox Code Playgroud)

考虑到$query在database_query函数中永远不会改变它,使用指针是一个好习惯,$query这样函数不需要为传入的值的新迭代分配更多的内存吗?

function database_query( &$query ){
    //do stuff that does not affect $query
}
Run Code Online (Sandbox Code Playgroud)

Tim*_*per 6

不,不要这样做.如果在函数内部更改了non-pass-by-reference参数的值("copy on write"),PHP将只创建该字符串的另一个副本.没有理由通过将参数作为参考来让人们阅读您的代码错误的印象.

此外,引用不是指针.


Ben*_*ill -2

事实证明这是一个非常有趣的问题,我花了一个半小时阅读有关 PHP 及其如何处理引用的内容(感谢 Tim Cooper 提供的链接让我开始)。

要回答你的问题,是的 - 当你调用函数时使用这样的引用是一个很好的做法。通过使用引用,您将使用更少的资源 - 引用变量没有“写入时复制”。这是一些证据:

<?php
function noref_nowrite($var_a){
    echo '<h3>NOT Using a Reference/Not Changing Data</h3>';
    echo '<p>'. xdebug_debug_zval('var_a') .'</p>';
    echo '<p>'. debug_zval_dump($var_a) .'</p>';
    echo '<p>$var_a = '. $var_a .' and $GLOBALS[a] = '. $GLOBALS['a'] .'</p>';
}
function noref_write($var_a){
    $var_a++;
    echo '<h3>NOT Using a Reference/Changing Data</h3>';
    echo '<p>'. xdebug_debug_zval('var_a') .'</p>';
    echo '<p>'. debug_zval_dump($var_a) .'</p>';
    echo '<p>$var_a = '. $var_a .' and $GLOBALS[a] = '. $GLOBALS['a'] .'</p>';
}

function ref_nowrite(&$var_a){
    echo '<h3>Using a Reference/Not Changing Data</h3>';
    echo '<p>'. xdebug_debug_zval('var_a') .'</p>';
    echo '<p>'. debug_zval_dump($var_a) .'</p>';
    echo '<p>$var_a = '. $var_a .' and $GLOBALS[a] = '. $GLOBALS['a'] .'</p>';
}
function ref_write(&$var_a){
    $var_a++;
    echo '<h3>Using a Reference/Changing Data</h3>';
    echo '<p>'. xdebug_debug_zval('var_a') .'</p>';
    echo '<p>'. debug_zval_dump($var_a) .'</p>';
    echo '<p>$var_a = '. $var_a .' and $GLOBALS[a] = '. $GLOBALS['a'] .'</p>';
}

$a = 5;
noref_nowrite($a);
noref_write($a);
ref_nowrite($a);
ref_write($a);
?>
Run Code Online (Sandbox Code Playgroud)

如果将上述代码复制/粘贴到 PHP 页面并执行它,您将看到以下内容:

NOT Using a Reference/Not Changing Data
var_a: (refcount=3, is_ref=0)=5
long(5) refcount(4)

$var_a = 5 and $GLOBALS[a] = 5


NOT Using a Reference/Changing Data
var_a: (refcount=1, is_ref=0)=6
long(6) refcount(2)
$var_a = 6 and $GLOBALS[a] = 5

Using a Reference/Not Changing Data
var_a: (refcount=3, is_ref=1)=5
long(5) refcount(1)
$var_a = 5 and $GLOBALS[a] = 5

Using a Reference/Changing Data
var_a: (refcount=3, is_ref=1)=6
long(6) refcount(1)
$var_a = 6 and $GLOBALS[a] = 6
Run Code Online (Sandbox Code Playgroud)

所以我们这里有四个基本测试。我创建一个全局变量 ($a) 并为其指定值 5。

当我调用 noref_nowrite 函数时,我们看到 XDebug 计数为 3 个引用,而 PHP 的内置函数计数为 4 个。有趣的是,PHP 在内部对此进行了优化,因此它实际上就像调用 ref_nowrite 函数,因为 PHP 使 $var_a 成为对 $GLOBALS['a'] 的引用。

当我调用 noref_write 函数时,我们看到引用计数下降到 1(如果查看 PHP 的内置函数,则为 2)。为什么?因为这就是“写时复制”问题发生的地方。在我们递增 $var_a 之前,PHP 在内部使用 $var_a 作为对 $a 的引用,但是当我们更改值时,我们强制 PHP 复制该变量,以便它可以递增。所以此时 $var_a 不再是对 $a 的引用,而是更改为引用它自己的数据。

ref_nowrite 函数显示的结果不明确。仅凭这一点我们无法证明任何事情。然而 ref_write 函数告诉我们,XDebug 说我们正在处理一个引用变量 (is_ref=1),最重要的是,我们看到在增加 $var_a 后,全局变量 $a 的值也发生了变化 - 这意味着 $var_a 和$GLOBALS['a'] 肯定指向内存中的同一位置。这意味着更改 $var_a 不会触发“写入时复制”情况 - 而且也不应该触发,因为我们正在处理引用。

尝试一下这个来说服自己,这里还有一些阅读内容:

检测 PHP 变量是否是引用/被引用(我认为 ircmaxell 有一个深思熟虑的答案)

https://www.php.net/debug-zval-dump

XDebug 文档:http://xdebug.org/docs/display

PHP 参考文献的作用:https ://www.php.net/manual/en/language.references.whatdo.php

PHP 引用计数基础知识:https://www.php.net/manual/en/features.gc.refcounting-basics.php

  • 我不太确定这样做的目的是什么,因为问题不在于 PHP 是否使用引用变量执行写时复制(显然没有),而是将参数传递为如果该值未更改,请参考。您自己的测试表明,PHP 在未修改时确实会复制变量数据,因此不应将变量作为引用传递。这只是创建了不明确的代码。 (2认同)