深入解析php中的foreach問題(4)_PHP教程
推薦:PHP做好防盜鏈的設置方法盜鏈 是指服務提供商自己不提供服務的內容,通過技術手段繞過其它有利益的最終用戶界面(如廣告),直接在自己的 網站上向最終用戶提供其它服務提供商的服務內容,騙取最終用戶的瀏覽和點擊率。受益者不提供資源或提供很少的資源,而真正的服務提供商卻得不到任何的收 益
可見$tmp = $arr的本質就是將array的指針進行復制,然后將array的refcount自動加1.用圖表達出此時的內存,依然只有一份array數組:
既然只有一份array,那foreach循環中修改$tmp的時候,為何$arr沒有跟著改變?
繼續看PHP源碼中的ZEND_FE_RESET_SPEC_CV_HANDLER函數,這是一個OPCODE HANDLER,它對應的OPCODE為FE_RESET。該函數負責在foreach開始之前,將數組的內部指針指向其第一個元素。
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;
// 對變量進行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 {
// 本例會進入該分支
if (Z_TYPE_PP(array_ptr_ptr) == IS_ARRAY) {
// 注意此處的SEPARATE_ZVAL_IF_NOT_REF
// 它會重新復制一個數組出來
// 真正分離$tmp和$arr,變成了內存中的2個數組
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 {
……
}
// 重置數組內部指針
……
}
從代碼中可以看出,真正執行變量分離并不是在賦值語句執行的時候,而是推遲到了使用變量的時候,這也是Copy On Write機制在PHP中的實現。
FE_RESET之后,內存的變化如下:
上 圖解釋了為何foreach并不會對原來的$arr產生影響。至于ref_count以及is_ref的變化情況,感興趣的同學可以詳細閱讀 ZEND_FE_RESET_SPEC_CV_HANDLER和ZEND_SWITCH_FREE_SPEC_VAR_HANDLER的具體實現(均位于 php-src/zend/zend_vm_execute.h中),本文不做詳細剖析:)
分享:如何使用PHP實現javascript的escape和unescape函數前端開發工程師都知道javascript有編碼函數escape()和對應的解碼函數unescape(),而php中只有個urlencode和 urldecode,這個編碼和解碼函數對encodeURI和encodeURIComponent有效,但是對escape的是無效的。 javascript中的escape()函數和unescape()函數用戶字符串編碼
- 相關鏈接:
- 教程說明:
PHP教程-深入解析php中的foreach問題(4)
。