1. 程式人生 > >php5.6與php7 不同總結(陸續補充)

php5.6與php7 不同總結(陸續補充)

php5.6的雜湊表比較噁心,php7也對雜湊表進行了改造,先介紹下php5.6的雜湊表

原來大家都清楚,我們看一下更細的一部分,如何更新插入:

static zend_never_inline zval **_get_zval_cv_lookup_BP_VAR_W(zval ***ptr, zend_uint var TSRMLS_DC)
{
	zend_compiled_variable *cv = &CV_DEF_OF(var);

	if (!EG(active_symbol_table)) {
		Z_ADDREF(EG(uninitialized_zval));
		*ptr = (zval**)EX_CV_NUM(EG(current_execute_data), EG(active_op_array)->last_var + var);
		**ptr = &EG(uninitialized_zval);
	} else if (zend_hash_quick_find(EG(active_symbol_table), cv->name, cv->name_len+1, cv->hash_value, (void **)ptr)==FAILURE) {
		Z_ADDREF(EG(uninitialized_zval));
		zend_hash_quick_update(EG(active_symbol_table), cv->name, cv->name_len+1, cv->hash_value, &EG(uninitialized_zval_ptr), sizeof(zval *), (void **)ptr);
	}
	return *ptr;
}

注意一下
ZEND_API int _zend_hash_quick_add_or_update(HashTable *ht, const char *arKey, uint nKeyLength, ulong h, void *pData, uint nDataSize, void **pDest, int flag ZEND_FILE_LINE_DC);
zend_hash_quick_update(EG(active_symbol_table), cv->name, cv->name_len+1, cv->hash_value, &EG(uninitialized_zval_ptr), sizeof
(zval *), (void **)ptr);

首先要注意看的是&EG(uninitialized_zval_ptr)是一個**zval結構指標, 把它存進去的目的是初始化,也就是讓hash表結構指標有所指向,一個固定位置,後續會根據pDest指標進行賦值。

其次是pDest是二級指標,為什麼會是二級指標,因為c語言函式傳遞都是值傳遞,要改變指標值只能將指標地址傳入。

但是雖然是二級指標,事實呼叫的卻是用的三級真值***ptr,那是因為符號表變數存的永遠是**zval結構,加上ptr變數地址,所以三級就是這樣來的。

#define UPDATE_DATA(ht, p, pData, nDataSize)											\
	if (nDataSize == sizeof(void*)) {													\
		if ((p)->pData != &(p)->pDataPtr) {												\
			pefree_rel((p)->pData, (ht)->persistent);									\
		}																				\
		memcpy(&(p)->pDataPtr, pData, sizeof(void *));									\
		(p)->pData = &(p)->pDataPtr;													\
	} else {																			\
		if ((p)->pData == &(p)->pDataPtr) {												\
			(p)->pData = (void *) pemalloc_rel(nDataSize, (ht)->persistent);			\
			(p)->pDataPtr=NULL;															\
		} else {																		\
			(p)->pData = (void *) perealloc_rel((p)->pData, nDataSize, (ht)->persistent);	\
			/* (p)->pDataPtr is already NULL so no need to initialize it */				\
		}																				\
		memcpy((p)->pData, pData, nDataSize);											\
	}

開始把memcpy函式搞錯了,是指標指向的內容進行拷貝,所以p->pDataPtr存放的是一級指標*zval

具體看下面畫的很難看的圖


接下來是php7的資料結構:

struct _zend_string {
	zend_refcounted_h gc;
	zend_ulong        h;                /* hash value */
	size_t            len;
	char              val[1];
};

typedef struct _Bucket {
	zval              val;
	zend_ulong        h;                /* hash value (or numeric index)   */
	zend_string      *key;              /* string key or NULL for numerics */
} Bucket;

typedef struct _zend_array HashTable;

struct _zend_array {
	zend_refcounted_h gc;
	union {
		struct {
			ZEND_ENDIAN_LOHI_4(
				zend_uchar    flags,
				zend_uchar    nApplyCount,
				zend_uchar    nIteratorsCount,
				zend_uchar    reserve)
		} v;
		uint32_t flags;
	} u;
	uint32_t          nTableMask;
	Bucket           *arData;
	uint32_t          nNumUsed;
	uint32_t          nNumOfElements;
	uint32_t          nTableSize;
	uint32_t          nInternalPointer;
	zend_long         nNextFreeElement;
	dtor_func_t       pDestructor;
};

/*
 * HashTable Data Layout
 * =====================
 *
 *                 +=============================+
 *                 | HT_HASH(ht, ht->nTableMask) |
 *                 | ...                         |
 *                 | HT_HASH(ht, -1)             |
 *                 +-----------------------------+
 * ht->arData ---> | Bucket[0]                   |
 *                 | ...                         |
 *                 | Bucket[ht->nTableSize-1]    |
 *                 +=============================+
 */
ht->arData僅是一級指標,分為上下兩部分,上部分是索引根據hash值偏移獲取下半部分的index,從而拿到內容
<pre style="font-family: NSimSun; font-size: 16px; background: white;"><span style="color:blue;">#define</span> HT_HASH_EX(data, idx) \
	((uint32_t*)(data))[(int32_t)(idx)]
根據hash值獲取int型別的偏移量,通常idx為負數見初始化:
static void zend_always_inline zend_hash_real_init_ex(HashTable *ht, int packed)
{
	HT_ASSERT(GC_REFCOUNT(ht) == 1);
	ZEND_ASSERT(!((ht)->u.flags & HASH_FLAG_INITIALIZED));
	if (packed) {
		HT_SET_DATA_ADDR(ht, pemalloc(HT_SIZE(ht), (ht)->u.flags & HASH_FLAG_PERSISTENT));
		(ht)->u.flags |= HASH_FLAG_INITIALIZED | HASH_FLAG_PACKED;
		HT_HASH_RESET_PACKED(ht);
	} else {
		(ht)->nTableMask = -(ht)->nTableSize;
		HT_SET_DATA_ADDR(ht, pemalloc(HT_SIZE(ht), (ht)->u.flags & HASH_FLAG_PERSISTENT));
		(ht)->u.flags |= HASH_FLAG_INITIALIZED;
		if (EXPECTED(ht->nTableMask == -8)) {
			Bucket *arData = ht->arData;

			HT_HASH_EX(arData, -8) = -1;
			HT_HASH_EX(arData, -7) = -1;
			HT_HASH_EX(arData, -6) = -1;
			HT_HASH_EX(arData, -5) = -1;
			HT_HASH_EX(arData, -4) = -1;
			HT_HASH_EX(arData, -3) = -1;
			HT_HASH_EX(arData, -2) = -1;
			HT_HASH_EX(arData, -1) = -1;
		} else {
			HT_HASH_RESET(ht);
		}
	}
}


# define HT_HASH_TO_BUCKET_EX(data, idx) \
	((Bucket*)((char*)(data) + (idx)))
根據上面獲取的偏移量獲取bucket指標

可見增加元素的方法

add_to_hash:
	HANDLE_BLOCK_INTERRUPTIONS();
	idx = ht->nNumUsed++;
	ht->nNumOfElements++;
	if (ht->nInternalPointer == HT_INVALID_IDX) {
		ht->nInternalPointer = idx;
	}
	zend_hash_iterators_update(ht, HT_INVALID_IDX, idx);
	p = ht->arData + idx;
	p->key = key;
	if (!ZSTR_IS_INTERNED(key)) {
		zend_string_addref(key);
		ht->u.flags &= ~HASH_FLAG_STATIC_KEYS;
		zend_string_hash_val(key);
	}
	p->h = h = ZSTR_H(key);
	ZVAL_COPY_VALUE(&p->val, pData);
	nIndex = h | ht->nTableMask;
	Z_NEXT(p->val) = HT_HASH(ht, nIndex);
	HT_HASH(ht, nIndex) = HT_IDX_TO_HASH(idx);
	HANDLE_UNBLOCK_INTERRUPTIONS();


首先zval元素的位置是順序儲存的,就是ht->arData的下半部分,然後通過hash算出nIndex位移即上部分,首先將上部分的初始位置(-1)賦值給下半部分zval的next屬性,然後賦值為下部分的位置(1),如此迴圈,當加入一個新的hash相同的資料時,此時將1賦值給新的zval的next,上部分的值為2。

2  $var = 1

php7解析過程 :

compile_file -> zendparse(yyparse) -> zend_compile_top_stmt -> zend_compile_stmt
-> zend_compile_assign -> zend_compile_simple_var -> zend_try_compile_cv -> zend_compile_simple_var_no_cv
獲取opcode

相關推薦

php5.6php7 不同總結(陸續補充)

php5.6的雜湊表比較噁心,php7也對雜湊表進行了改造,先介紹下php5.6的雜湊表 原來大家都清楚,我們看一下更細的一部分,如何更新插入: static zend_never_inline zval **_get_zval_cv_lookup_BP_VAR_W(zv

自我記錄:C語言編寫程式碼可能發生的問題注意事項【陸續補充

前言:該文章原創,不僅針對新手還是老手,均有一定幫助。若有錯誤地方,請不惜賜教。主要結合《C與指標》這本書,後續看過《C專家程式設計》、《C語言的XXX個問題》等書後會繼續補充。【非計算機類學生,目前更新進度緩慢】 編寫程式碼前的注意: ①、程式設計風格影響程式碼的可讀性,這像是一個人的臉,

isset在php5.6-和php7.0+的一些差異

今天在公司實現一個模組功能時寫了如下程式碼: class ProductCategory { const TYPES = [ 1 =&gt; 'type1', 2 =&gt; 'type2', ]; public func

PHP5.6PHP7.0區別 PHP5.6PHP7.0區別

PHP5.6 和PHP7.0區別   1. PHP7.0 比PHP5.6效能提升了兩倍。 2.PHP7.0全面一致支援64位。 3.PHP7.0之前出現的致命錯誤,都改成了丟擲異常。 4.增加了空結合操作符(??)。效果相當於三元運算子。 5.PHP7

PHP多版本共存:php5.6php5.5共存

續: php多版本共存,需要使用php-fpm來執行php方便,這樣更方便配置和管理 準備工作: 建立PHP5.6的相關目錄,不要與已安裝的php5.5的目錄相同 mkdir /usr/local/php56 安裝目錄 mkdir /etc/ph

基於MAC的 LNMP環境搭建, PHP5.6PHP7.2.3 雜記

sudo ./configure --prefix=/Users/heweijun/www/server/php --with-config-file-path=/Users/heweijun/www/server/php/etc --enable-fpm --with-fpm-user=www --wit

PHP5.6PHP7.0區別

1. PHP7.0 比PHP5.6效能提升了兩倍。2.PHP7.0全面一致支援64位。3.PHP7.0之前出現的致命錯誤,都改成了丟擲異常。4.增加了空結合操作符(??)。效果相當於三元運算子。5.PHP7.0新增了函式的返回型別宣告。6.PHP7.0新增了標量型別宣告。  PHP 7 中的函式的形參型別宣告

Ubuntu 16.04 PHP5.6PHP7.2共存 LNMP

因為公司專案跑PHP5.6,而自己學習需要PHP7.2,所以需要這兩個版本共存.LNMP的搭建 首先搭建的是5.6的版本 不影響 在此基礎上再安裝一個PHP7.2ppa 方式安裝 php7.2 :sudo apt-get install software-properties

Apache2.4 php7.1.6的鏈接

pin php png 4.2 啟動 .cn ica 1-1 image 首先Apache已經安裝成功,在瀏覽器中能夠打開再下載php 我的Apache安裝版本為Apache2.4.26 x64 vc14 所以我php也應該是vc14編譯的 php下載地址為 http://

PHP7有沒有你們說的那麽牛逼(php7.1 和 php5.6 橫向對比) 轉載

ber bug maria 現在 bin grep creat -h incr 轉自 https://www.jianshu.com/p/5baa78646a79 PHP7來一發 PHP7正式發布到現在已經一年半了,剛出道就號稱比舊版本快了幾倍,各種開源框架或系統運行

ccentos 7下安裝php5.6並使用nginx + php-fpm部署多個不同端口網站

png .net 又一 介紹 htm sea tip 編輯 端口 作為一個的勤雜工,近期因公司內部信息化的需求,給新進員工提供基礎的知識培訓和介紹,也為了給公司內部建立一個溝通交流的平臺,百度找了開源的百科系統HDwiki和開源的問答系統Tipask問答系統,蛋痛的這兩套系

php5.5和php7.2 方括號賦值的不同區別結果 (織夢升級到php7除錯後臺無法刪除欄目的問題)

php5.5和php7.2 方括號賦值的不同區別結果  織夢升級到php7除錯後臺無法刪除欄目的問題程式碼簡化例子如下: <?php /** * */ class TestName { var $string; var $arr; function __con

CentOS6.8中原php5.6升級成php7.2方法

因為laravel框架的基本要求,必須將原伺服器上的php5.6升級成php7.2才可以。以下是自己的操作步驟。 yum remove php* 參照右邊連結: intall-php7-in-centos6 操作 因為上文中沒有安裝php-fpm所以還要執行yum

linux下編譯安裝php7(相容現有的php5.6版本)

1.首先去php官網下載一個php7版本原始碼包 http://php.net/downloads.php,我這下載的是php7.2.13版本.  2.使用ftp或者linux的rz命令將包上傳到linux下,開始進行編譯安裝. 3.解壓安裝包 # tar -zxvf   

PHP5.6將退出版本之爭,PHP7的時代將全面來臨(PHP7.3)

PHP背景:       PHP5.6作為PHP5的最後一個版本,也是目前國內使用最廣泛的PHP版本,PHP 5.6始於2014年。其第一個測試版PHP 5.6 alpha 1版於2014年1月釋出。隨機產生了第一個由國人(鳥哥,惠新宸

Ubuntu16.04 PHP7 TO PHP5.6

PHP7 TO PHP5.6 remove PHP installed List the installed php pkg root@VM-13-184-ubuntu:~# dpkg -l | grep php7 ii libapache2-mo

ubuntu 16 php7 降級 php5.6

sudo dpkg -l | grep php| awk '{print $2}' |tr "\n" " " sudo apt-get install aptitude sudo aptitude purge `dpkg -l | grep php| awk '{print

ubuntu 使用apt-get install 安裝php5.6--php7

使用ppa增加源: $ sudo apt-get install python-software-properties $ sudo add-apt-repository ppa:ondrej/php $ sudo apt-get update $ sud

Centos 7 搭建LAMP(apache2.4+php5.6/php7+mysql)

1、下載apache 服務 yum -y install httpd 開啟apache  systemctl start httpd 設定開啟自啟動 systemctl enbale httpd2、安裝mysql  yum install mariadb mariadb-se

ubuntu解除安裝php7並安裝php5.6記錄

    ubuntu16.04版本從預設源安裝的php版本為7.x版本,我們都知道php7.0已經捨棄了很多舊版本的函式等內容,這對舊系統來說是致命的,那麼,我們就有了安裝舊版php的需求,而同一主機安裝兩個版本的php,如果不做配置會報錯。那麼,我們就需要:一、解除安裝ph