每天一個 PHP 語法-變數使用及內部實現

ifelse發表於2020-04-12

說明

這裡基於php7.2.5進行測試,php7之後內部結構變化應該不是太大,但與php5.X有差別。

使用

變數宣告及使用

$a = "hello"; // string
echo is_string($a); // 1
$b = 1; // int
echo is_int($b); // 1
$c = [1]; // array
$d = true; // bool

弱型別

區別於C/JAVA/GO等強型別語言,PHP在宣告變數的時候不需要指定變數的型別,程式碼在執行的時候會自動去解析變數的型別。

如何實現

儲存變數的底層結構zval、zend_value(zend_types.h)

struct _zval_struct {
    zend_value        value;            /* value 變數值放在這裡 */
    union {
        struct {
            ZEND_ENDIAN_LOHI_4(
                zend_uchar    type,            /* active type 變數型別  stirng/true/false/array */
                zend_uchar    type_flags,
                zend_uchar    const_flags,
                zend_uchar    reserved)        /* call info for EX(This) */
        } v;
        uint32_t type_info;
    } u1;
    union {
        uint32_t     next;                 /* hash collision chain */
        uint32_t     cache_slot;           /* literal cache slot */
        uint32_t     lineno;               /* line number (for ast nodes) */
        uint32_t     num_args;             /* arguments number for EX(This) */
        uint32_t     fe_pos;               /* foreach position */
        uint32_t     fe_iter_idx;          /* foreach iterator index */
        uint32_t     access_flags;         /* class constant access flags */
        uint32_t     property_guard;       /* single property guard */
        uint32_t     extra;                /* not further specified */
    } u2;
};

// zend_value
typedef union _zend_value {
    zend_long         lval;                /* long value 這裡儲存int的值 */
    double            dval;                /* double value */
    zend_refcounted  *counted; /* 引用計數 用於垃圾回收 */
    zend_string      *str; /* 字串 */
    zend_array       *arr;
    zend_object      *obj;
    zend_resource    *res;
    zend_reference   *ref;
    zend_ast_ref     *ast;
    zval             *zv;
    void             *ptr;
    zend_class_entry *ce;
    zend_function    *func;
    struct {
        uint32_t w1;
        uint32_t w2;
    } ww;
} zend_value;


// zval.u1.v1.type的型別的定義
/* regular data types */
#define IS_UNDEF                    0
#define IS_NULL                        1 空 
#define IS_FALSE                    2 false
#define IS_TRUE                        3
#define IS_LONG                        4
#define IS_DOUBLE                    5
#define IS_STRING                    6 字串
#define IS_ARRAY                    7
#define IS_OBJECT                    8
#define IS_RESOURCE                    9
#define IS_REFERENCE                10

/* constant expressions */
#define IS_CONSTANT                    11
#define IS_CONSTANT_AST                12

/* fake types */
#define _IS_BOOL                    13
#define IS_CALLABLE                    14
#define IS_ITERABLE                    19
#define IS_VOID                        18

/* internal types */
#define IS_INDIRECT                 15
#define IS_PTR                        17
#define _IS_ERROR                    20

我們可以先看一下zval.u1.v.type 與 zend_value其他的元素可以暫時忽略。

zval是存放變數的結構體,變數的值則放在zend_value中,zend_value是一個union型別,也就是共用體,特點就是可以存放多種型別的資料,但同時只有一個種型別可以有值。

$a = "hello";

那麼PHP程式碼在編譯、執行的時候透過詞法語法解析,將值1生成一個zval結構體,zval.u1.type就是IS_STRING型別,值儲存在zend_value中的str也就是zend_string中。

總結

弱型別的實現是基於zend_value共用體實現,變數型別在程式碼解析的時候根據語法去解析生成的。

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章