我有一个功能:
$query = "SELECT * from lol";
database_query( $query );
考虑到$query
永远不会在 database_query 函数中更改,使用指针是一种好习惯,$query
这样函数就不需要为传入的值的新迭代分配更多内存?
function database_query( &$query ){
//do stuff that does not affect $query
}
我有一个功能:
$query = "SELECT * from lol";
database_query( $query );
考虑到$query
永远不会在 database_query 函数中更改,使用指针是一种好习惯,$query
这样函数就不需要为传入的值的新迭代分配更多内存?
function database_query( &$query ){
//do stuff that does not affect $query
}
不,不要这样做。如果在函数内部更改了 non-pass-by-reference 参数的值( “写时复制”),PHP 只会创建字符串的另一个副本。没有理由通过使参数成为引用来让阅读您的代码的人对您的函数正在执行的操作产生错误的印象。
此外,引用不是指针。
的值$query
是一个字符串。字符串没有内部可变状态——在 PHP 中“更改”包含字符串的变量的唯一方法是为其分配一个新字符串(或通过引用将其传递给一个函数,该函数然后为其分配一个新字符串, ETC。)。因此,当您这样做时$foo = $query;
,任何合理的实现都会简单地将指针复制到内部字符数组,而不是复制实际字符,因为它们无法更改。因此,即使您不知道 PHP 具有写时复制功能,您也可以得出结论,按值传递字符串并不昂贵。
事实证明这是一个非常有趣的问题,我在过去的一个半小时里阅读了有关 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);
?>
如果您将上述代码复制/粘贴到 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
所以我们这里有四个基本测试。我创建了一个全局变量 ($a) 并将其赋值为 5。
当我调用 noref_nowrite 函数时,我们看到 XDebug 计数为 3 个引用,而 PHP 的内置函数计数为 4。有趣的是,PHP 在内部对此进行了优化,这就像调用 ref_nowrite 函数一样,因为 PHP 使 $var_a 成为对 $GLOBALS['a'] 的引用.
当我调用 noref_write 函数时,我们看到 refcount 下降到 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 有一个深思熟虑的答案)
http://us2.php.net/debug-zval-dump
XDebug 文档:http: //xdebug.org/docs/display
PHP 参考资料的作用:http: //us3.php.net/manual/en/language.references.whatdo.php
PHP 参考计数基础:http ://us3.php.net/manual/en/features.gc.refcounting-basics.php