由php的call_user_func傳reference引發(fā)的思考
更新時(shí)間:2010年07月23日 00:13:17 作者:
由php的call_user_func傳reference引發(fā)的思考,使用call_user_func傳reference的朋友可以參考下。
問(wèn)題的提出
網(wǎng)友bercmisir在院內(nèi)留言,針對(duì)php手冊(cè)中的call_user_func函數(shù)的文檔一事,大致如下:
http://php.net/manual/en/function.call-user-func.php
其中parameter下有這樣一句話:
Note: Note that the parameters for call_user_func() are not passed by reference.
簡(jiǎn)單地翻譯一下,是說(shuō)這個(gè)函數(shù)的參數(shù)是不能依靠引用來(lái)傳遞的。
還有一個(gè)例子:
error_reporting(E_ALL);
function increment(&$var)
{
$var++;
}
$a = 0;
call_user_func('increment', $a);
echo $a."\n";
call_user_func_array('increment', array(&$a)); // You can use this instead before PHP 5.3
echo $a."\n";
?>
輸出是:
0
1
而網(wǎng)友bercmisir的問(wèn)題在于:
call_user_func('increment', $a);輸出是0,而call_user_func('increment', &$a);卻輸出是1,明明說(shuō)不能依靠引用來(lái)傳遞。
尋根溯源
然后再進(jìn)一步尋根溯源,這個(gè)Note的信息其實(shí)是http://bugs.php.net/bug.php?id=24931這個(gè)bug中最后處理的結(jié)果。
并且在call_user_func('increment', &$a);雖然輸出了1的結(jié)果,但一般情況下,會(huì)有一個(gè)警告信息:Deprecated: Call-time pass-by-reference has been deprecated。
這是什么原因呢?
先看一個(gè)例子:
error_reporting(E_ALL);
function increment(&$var)
{
$var++;
}
$x = 1;
increment($x);
echo $x;
?>
結(jié)果為2,并且沒(méi)有類似expected to be a reference, value given的警告信息,相反地,如果將第8行代碼修改為&$x,將得到一個(gè)廢除警告。從而得以驗(yàn)證,其實(shí)PHP在傳遞過(guò)程中,變量會(huì)根據(jù)形參需要的到底是引用還是值來(lái)自行決定傳輸?shù)氖且眠€是值,并不需要顯式地傳遞(相反顯式傳遞是即將被廢除的)。
繼續(xù)深入
http://www.php.net/manual/en/language.references.pass.php
在php手冊(cè)中,介紹引用的傳遞一節(jié),在中間位置有一個(gè)Note說(shuō)到:在函數(shù)調(diào)用時(shí)是不需要傳引用的(也就是上節(jié)所說(shuō)的顯式調(diào)用),在5.3中如果顯式調(diào)用會(huì)出來(lái)一個(gè)廢除警告。
分析源碼
有人說(shuō):在php中寫入,everything is a reference。
查閱php源碼,在./Zend/zend_compile.c的1579行有函數(shù)定義zend_do_pass_param。(php5.2.13)
其中有這樣一句判斷:
if (original_op == ZEND_SEND_REF && !CG(allow_call_time_pass_reference)) {打印廢除警告。}
大概意思就是說(shuō),在傳遞的是引用,并且php.ini的allow_call_time_pass_reference為否的話,打印警告。
再看zend_do_pass_param使用的地方,可以發(fā)現(xiàn)是在parser階段時(shí),根據(jù)參數(shù)ZVAL結(jié)構(gòu)體中元素的定義,來(lái)傳遞到底是var還是value還是reference。(php5.2.13 ./Zend/zend_language_parser.y/c 451/3593)
結(jié)論
引用其實(shí)類似linux里的文件硬鏈接一樣,但和C語(yǔ)言中的指針是不相同的,在parser階段php會(huì)根據(jù)上下文環(huán)境自行判斷是傳引用還是值。而本文所提到的call_user_function并不會(huì)自行判斷傳的是引用還是值。所以前面的例子call_user_function在傳值的時(shí)候不管用,而在傳引用的時(shí)候得出了正確結(jié)果(但其實(shí)還有一個(gè)廢除警告)。
網(wǎng)友bercmisir在院內(nèi)留言,針對(duì)php手冊(cè)中的call_user_func函數(shù)的文檔一事,大致如下:
http://php.net/manual/en/function.call-user-func.php
其中parameter下有這樣一句話:
Note: Note that the parameters for call_user_func() are not passed by reference.
簡(jiǎn)單地翻譯一下,是說(shuō)這個(gè)函數(shù)的參數(shù)是不能依靠引用來(lái)傳遞的。
還有一個(gè)例子:
復(fù)制代碼 代碼如下:
error_reporting(E_ALL);
function increment(&$var)
{
$var++;
}
$a = 0;
call_user_func('increment', $a);
echo $a."\n";
call_user_func_array('increment', array(&$a)); // You can use this instead before PHP 5.3
echo $a."\n";
?>
輸出是:
0
1
而網(wǎng)友bercmisir的問(wèn)題在于:
call_user_func('increment', $a);輸出是0,而call_user_func('increment', &$a);卻輸出是1,明明說(shuō)不能依靠引用來(lái)傳遞。
尋根溯源
然后再進(jìn)一步尋根溯源,這個(gè)Note的信息其實(shí)是http://bugs.php.net/bug.php?id=24931這個(gè)bug中最后處理的結(jié)果。
并且在call_user_func('increment', &$a);雖然輸出了1的結(jié)果,但一般情況下,會(huì)有一個(gè)警告信息:Deprecated: Call-time pass-by-reference has been deprecated。
這是什么原因呢?
先看一個(gè)例子:
復(fù)制代碼 代碼如下:
error_reporting(E_ALL);
function increment(&$var)
{
$var++;
}
$x = 1;
increment($x);
echo $x;
?>
結(jié)果為2,并且沒(méi)有類似expected to be a reference, value given的警告信息,相反地,如果將第8行代碼修改為&$x,將得到一個(gè)廢除警告。從而得以驗(yàn)證,其實(shí)PHP在傳遞過(guò)程中,變量會(huì)根據(jù)形參需要的到底是引用還是值來(lái)自行決定傳輸?shù)氖且眠€是值,并不需要顯式地傳遞(相反顯式傳遞是即將被廢除的)。
繼續(xù)深入
http://www.php.net/manual/en/language.references.pass.php
在php手冊(cè)中,介紹引用的傳遞一節(jié),在中間位置有一個(gè)Note說(shuō)到:在函數(shù)調(diào)用時(shí)是不需要傳引用的(也就是上節(jié)所說(shuō)的顯式調(diào)用),在5.3中如果顯式調(diào)用會(huì)出來(lái)一個(gè)廢除警告。
分析源碼
有人說(shuō):在php中寫入,everything is a reference。
查閱php源碼,在./Zend/zend_compile.c的1579行有函數(shù)定義zend_do_pass_param。(php5.2.13)
其中有這樣一句判斷:
if (original_op == ZEND_SEND_REF && !CG(allow_call_time_pass_reference)) {打印廢除警告。}
大概意思就是說(shuō),在傳遞的是引用,并且php.ini的allow_call_time_pass_reference為否的話,打印警告。
再看zend_do_pass_param使用的地方,可以發(fā)現(xiàn)是在parser階段時(shí),根據(jù)參數(shù)ZVAL結(jié)構(gòu)體中元素的定義,來(lái)傳遞到底是var還是value還是reference。(php5.2.13 ./Zend/zend_language_parser.y/c 451/3593)
結(jié)論
引用其實(shí)類似linux里的文件硬鏈接一樣,但和C語(yǔ)言中的指針是不相同的,在parser階段php會(huì)根據(jù)上下文環(huán)境自行判斷是傳引用還是值。而本文所提到的call_user_function并不會(huì)自行判斷傳的是引用還是值。所以前面的例子call_user_function在傳值的時(shí)候不管用,而在傳引用的時(shí)候得出了正確結(jié)果(但其實(shí)還有一個(gè)廢除警告)。
相關(guān)文章
php數(shù)組一對(duì)一替換實(shí)現(xiàn)代碼
以下方法能實(shí)現(xiàn)匹配關(guān)鍵詞并分別對(duì)關(guān)鍵詞做特殊處理的功能,需要的朋友可以參考下2012-08-08PHP實(shí)現(xiàn)獲取并生成數(shù)據(jù)庫(kù)字典的方法
這篇文章主要介紹了PHP實(shí)現(xiàn)獲取并生成數(shù)據(jù)庫(kù)字典的方法,可實(shí)現(xiàn)讀取數(shù)據(jù)庫(kù)并列出詳細(xì)數(shù)據(jù)庫(kù)信息的功能,需要的朋友可以參考下2016-05-05如何使用Serializable接口來(lái)自定義PHP中類的序列化
這篇文章主要介紹了如何使用Serializable接口來(lái)自定義PHP中類的序列化,幫助大家更好的理解和學(xué)習(xí)使用PHP,感興趣的朋友可以了解下2021-04-04PHP實(shí)現(xiàn)返回JSON和XML的類分享
這篇文章主要給大家分享了一個(gè)使用PHP實(shí)現(xiàn)返回JSON和XML的類,非常實(shí)用,希望大家能夠喜歡2015-01-01PHP中spl_autoload_register()和__autoload()區(qū)別分析
這篇文章主要介紹了spl_autoload_register()和__autoload()區(qū)別,需要的朋友可以參考下2014-05-05PHP簡(jiǎn)單計(jì)算兩個(gè)時(shí)間差的方法示例
這篇文章主要介紹了PHP簡(jiǎn)單計(jì)算兩個(gè)時(shí)間差的方法,結(jié)合具體實(shí)例形式分析了php日期與時(shí)間的轉(zhuǎn)換及數(shù)學(xué)運(yùn)算相關(guān)操作技巧,需要的朋友可以參考下2017-06-06