PHP底層原理-知其然知其所以然

字號+ 編輯: Snake 修訂: 面向ICU 來源: 卷心菜 2023-09-11 我要說兩句(0)

PHP變量的底層實現

1、PHP變量的底層實現

PHP代碼執行圖解

1.jpg

1.1 變量在内存中的存儲結構

PHP變量是通過zval結構體來存儲的,文档: Zend/zend.h 316行左右

2.jpg

1.2 值的存儲

PHP變量的值是放在zval結構體中的value段中的,文档: Zend/zend.h

3.jpg

1.3 結構體的字段解釋

struct _zval_struct {

/* Variable information */
zvalue_value value;/*變量的值,是個聯合體*/
zend_uint refcount__gc; /*指向次數*/
zend_uchar type;/* 變量類型 */
zend_uchar is_ref__gc; /*是否引用*/

};

//type字段的值爲以下常量

IS_NULL, IS_BOOL,IS_LONG,IS_DOUBLE
IS_STRING,IS_ARRAY,IS_OBJECT
IS_RESOURCE

1.4 聯合體中的值

typedef union _zvalue_value {

long lval;/* long value */
double dval;/* double value */

struct {

char *val;
int len;
} str;

HashTable *ht;/* hash table value */
zend_object_value obj;

} zvalue_value;

聯合中爲什麽只列出了5種值?

NULL不用,zval的type爲IS_NULL即可

Bool以1,0存儲在lval上

resource的type爲resource,其resource的内容用long來標志(資源標記)

1.5 變量的結構圖

4.jpg

1.6 變量的創建

創建變量的步驟: $str = "hello";

1:創建zval結構,並設置其類型 IS_STRING

2:設置其值爲 hello

3:講其加入符號表

{

zval *fooval;
MAKE_STD_ZVAL(fooval);
ZVAL_STRING(fooval, "hello", 1);
ZEND_SET_SYMBOL( EG(active_symbol_table) , "foo" , fooval);

}

1.7 符號表 symbol_table

符號表是什麽?

符號表是一張哈希表,裡面存儲了變量名->變量的zval結構體的地址,

// zend/zend_globals.h 182行

struct _zend_executor_globals {

...
...
HashTable *active_symbol_table; /*活動符號表*/
HashTable symbol_table;/* 全局符號表 */
HashTable included_files;/* files already included */

1.8 符號表與函數

Zend/zend_compiles.h

struct _zend_execute_data {

...
zend_op_array *op_array; //函數的執行步驟
HashTable *symbol_table; // 此函數的符號表地址
zend_class_entry *current_scope;
zval *current_this;
zval *current_object;
...

};

上面這個,是當前函數執行時的符號表

1.9 符號表與作用域

當執行到函數時,會生成函數的"執行環境結構體",包含函數名,參數,執行步驟,所在的類(如果是方法),以及爲這個函數生成一個符號表.符號表統一放在棧上.並把active_symbol_table指向剛産生的符號表

5.jpg

1.10 函數中靜態變量的實現

6.jpg

2.0 常量-常量結構體

結構體 Zend/constants.h 33行

typedef struct _zend_constant {

zval value; //變量結構體
int flags; //標志,是否大小寫敏感等
char *name; //常量名
uint name_len;
int module_number;//模塊名

} zend_constant;

2.1 常量的生成

int zend_register_constant(zend_constant *c TSRMLS_DC) {

...
...
zend_hash_add(EG(zend_constants), name, c->name_len, (void *) c, sizeof(zend_constant), NULL)==FAILURE)
...
...

}

2.2 define函數的實現

define函數當然是調用zend_register_constant聲明的常量 :)

具體如下 Zend/zend_builtin_functions.c

關鍵代碼:

c.value = *val;
zval_copy_ctor(&c.value);

if (val_free) {
zval_ptr_dtor(&val_free);
}

c.flags = case_sensitive; /* non persistent */
c.name = zend_strndup(name, name_len);
c.name_len = name_len+1;
c.module_number = PHP_USER_CONSTANT;

if (zend_register_constant(&c TSRMLS_CC) == SUCCESS) {

RETURN_TRUE;

} else {

RETURN_FALSE;

}

2.3 有趣的測試

1:常量並沒有檢測名字...

define('^_^',"laugh");

2:常量的第2個參數還可以是對象,與手冊上介紹的不同

define('obj',new className());

(當然,對象要有toString方法等著)

2.4 常量爲什麽是全局有效的?

很簡單,常量的哈希表只有一個

EG(zend_constants)

3.0 内存管理與垃圾回收

PHP封裝了對系統内存的請求,不要直接用malloc直接請求内存

8.jpg

3.1 PHP的hashtable太強大

9.jpg

3.2 引用計數

<?php

$a = 1;
$b = $a;

$a,$b的值及類型 都一樣,有必要再申請一個zval結構嗎?

10.jpg

3.3 引用計數解釋

<?php

$a = 1;
$b = $a;

當$a的值賦給$b時,並沒有爲$b生成一個新的zval結構體.而是$b與$a共享一個結構體.


3.4:copy-on-write 寫時複制

<?php

$a = 1;
$b = $a;
$b=6

11.jpg

3.5 引用傳值發生了什麽?

<?php

$a = 1;
$b = &$a;

12.jpg

3.6 引用傳值爲什麽影響了2個值

<?php

$a = 1;
$b = &$a;
$b = 6;

内核修改zval的值時,發現is_ref_gc爲1,則直接修改該value,而不是複制一份.如下圖:

13.jpg

閲完此文,您的感想如何?
  • 有用

    0

  • 沒用

    0

  • 開心

    0

  • 憤怒

    0

  • 可憐

    0

1.如文章侵犯了您的版權,請發郵件通知本站,該文章將在24小時内刪除;
2.本站標注原創的文章,轉發時煩請注明來源;
3.交流群: 2702237 13835667

相關課文
  • mac開發接入微信公衆號接口返回報錯 cURL error 56: SSLRead() return error -9806

  • PHP的換行符是什麽

  • pecl安裝程序時報錯Array and string offset access syntax with curly braces is no longer supported

  • 由於商家傳入的H5交易參數有誤,該筆交易暫時無法完成,請聯繫商家解決

我要說說
網上賓友點評