深入解析php中的foreach問題(4)_PHP教程
推薦:PHP做好防盜鏈的設(shè)置方法盜鏈 是指服務(wù)提供商自己不提供服務(wù)的內(nèi)容,通過技術(shù)手段繞過其它有利益的最終用戶界面(如廣告),直接在自己的 網(wǎng)站上向最終用戶提供其它服務(wù)提供商的服務(wù)內(nèi)容,騙取最終用戶的瀏覽和點(diǎn)擊率。受益者不提供資源或提供很少的資源,而真正的服務(wù)提供商卻得不到任何的收 益
可見$tmp = $arr的本質(zhì)就是將array的指針進(jìn)行復(fù)制,然后將array的refcount自動加1.用圖表達(dá)出此時的內(nèi)存,依然只有一份array數(shù)組:

既然只有一份array,那foreach循環(huán)中修改$tmp的時候,為何$arr沒有跟著改變?
繼續(xù)看PHP源碼中的ZEND_FE_RESET_SPEC_CV_HANDLER函數(shù),這是一個OPCODE HANDLER,它對應(yīng)的OPCODE為FE_RESET。該函數(shù)負(fù)責(zé)在foreach開始之前,將數(shù)組的內(nèi)部指針指向其第一個元素。
static int ZEND_FASTCALL ZEND_FE_RESET_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
zend_op *opline = EX(opline);
zval *array_ptr, **array_ptr_ptr;
HashTable *fe_ht;
zend_object_iterator *iter = NULL;
zend_class_entry *ce = NULL;
zend_bool is_empty = 0;
// 對變量進(jìn)行FE_RESET
if (opline->extended_value & ZEND_FE_RESET_VARIABLE) {
array_ptr_ptr = _get_zval_ptr_ptr_cv(&opline->op1, EX(Ts), BP_VAR_R TSRMLS_CC);
if (array_ptr_ptr == NULL || array_ptr_ptr == &EG(uninitialized_zval_ptr)) {
……
}
// foreach一個object
else if (Z_TYPE_PP(array_ptr_ptr) == IS_OBJECT) {
……
}
else {
// 本例會進(jìn)入該分支
if (Z_TYPE_PP(array_ptr_ptr) == IS_ARRAY) {
// 注意此處的SEPARATE_ZVAL_IF_NOT_REF
// 它會重新復(fù)制一個數(shù)組出來
// 真正分離$tmp和$arr,變成了內(nèi)存中的2個數(shù)組
SEPARATE_ZVAL_IF_NOT_REF(array_ptr_ptr);
if (opline->extended_value & ZEND_FE_FETCH_BYREF) {
Z_SET_ISREF_PP(array_ptr_ptr);
}
}
array_ptr = *array_ptr_ptr;
Z_ADDREF_P(array_ptr);
}
} else {
……
}
// 重置數(shù)組內(nèi)部指針
……
}
從代碼中可以看出,真正執(zhí)行變量分離并不是在賦值語句執(zhí)行的時候,而是推遲到了使用變量的時候,這也是Copy On Write機(jī)制在PHP中的實(shí)現(xiàn)。
FE_RESET之后,內(nèi)存的變化如下:

上 圖解釋了為何foreach并不會對原來的$arr產(chǎn)生影響。至于ref_count以及is_ref的變化情況,感興趣的同學(xué)可以詳細(xì)閱讀 ZEND_FE_RESET_SPEC_CV_HANDLER和ZEND_SWITCH_FREE_SPEC_VAR_HANDLER的具體實(shí)現(xiàn)(均位于 php-src/zend/zend_vm_execute.h中),本文不做詳細(xì)剖析:)
分享:如何使用PHP實(shí)現(xiàn)javascript的escape和unescape函數(shù)前端開發(fā)工程師都知道javascript有編碼函數(shù)escape()和對應(yīng)的解碼函數(shù)unescape(),而php中只有個urlencode和 urldecode,這個編碼和解碼函數(shù)對encodeURI和encodeURIComponent有效,但是對escape的是無效的。 javascript中的escape()函數(shù)和unescape()函數(shù)用戶字符串編碼
- PHPNOW安裝Memcached擴(kuò)展方法詳解
- php記錄頁面代碼執(zhí)行時間
- PHP中獎概率的抽獎算法程序代碼
- apache設(shè)置靜態(tài)文件緩存方法介紹
- php對圖像的各種處理函數(shù)代碼小結(jié)
- PHP 關(guān)于訪問控制的和運(yùn)算符優(yōu)先級介紹
- 關(guān)于PHP語言構(gòu)造器介紹
- php/js獲取客戶端mac地址的實(shí)現(xiàn)代碼
- php5.5新數(shù)組函數(shù)array_column使用
- PHP preg_match的匹配多國語言的技巧
- php 中序列化和json使用介紹
- php采集文章中的圖片獲取替換到本地
- 相關(guān)鏈接:
- 教程說明:
PHP教程-深入解析php中的foreach問題(4)
。