程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> 深入理解PHP內核(七)變量及數據類型-常量,深入理解內核

深入理解PHP內核(七)變量及數據類型-常量,深入理解內核

編輯:關於C語言

深入理解PHP內核(七)變量及數據類型-常量,深入理解內核


原文鏈接:http://www.orlion.ga/246/

在PHP中,常量的名字是一個簡單值的標識符,在腳本執行期間該值不能改變。和變量一樣,常量默認為大小寫敏感,但是通常是大寫的。

    常量是在變量的zval結構的基礎上添加了一額外的元素。如下所示為PHP中常量的內部結構。

一、常量的內部結構

typedef struct _zend_constant {
    zval value; /* zval結構,PHP內部變量的存儲結構,在第一小節有說明 */
    int flags;  /* 常量的標記如CONST_PERSISTENT | CONST_CS */
    char *name; /* 常量名稱 */
    uint name_len;  
    int module_number;  /* 模塊號 */
} zend_constant;

    在Zend/zend_constants.h文件的第33行可以看到如上所示的結構定義。在常量的結構中,除了與變量一樣的zval結構,它還包括常量的標記,常量名以及常量所在的模塊號。

    我們來看PHP常量的定義過程:

define('TIPI', 'Thinking In PHP Internal');

    這是一個很常規的常量定義過程,它使用了PHP的內置函數difine(),常量名為TIPI,值為一個字符串,存放在zval結構中。從這個例子出發,看下define定義常量的過程實現。

 

二、define定義常量的過程

 

    define是PHP的內置函數,在Zend/zend_builtin_functions.c文件中定義了此函數的實現。如下所示為部分源碼:

/* {{{ proto bool define(string constant_name, mixed value, boolean 
case_insensitive=false)
   Define a new constant */
ZEND_FUNCTION(define)
{
        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz|b", &name,
                &name_len, &val, &non_cs) == FAILURE) {
                return;
        }
 
        ... // 類常量定義 此不做介紹
         ... // 值類型判斷和處理
 
        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;
        }
}
/* }}} */

    上面的代碼已經對對象和類常量做了簡化處理,其實現基本上是一個將傳遞的參數傳遞給新建的zend_constant結構,並將這個結構體注冊到常量列表中的過程。關於大小寫敏感,函數的第三個參數表示是否大小寫不敏感,默認為false(大小寫敏感)。這個參數最後會賦值給zend_constant結構體的flags字段,其在函數中實現的代碼如下:

zend_bool non_cs = 0;   // 第三個參數的臨時存儲變量
int case_sensitive = CONST_CS;  // 是否大小寫敏感,默認為1
 
if(non_cs) {    // 輸入為真,大小寫不敏感
    case_sensitive = 0;
}
 
c.flags = case_sensitive; // 賦值給結構體字段

    從上面的define函數的實現來看,PHP對於常量的名稱在定義時其實是沒有所謂的限制。

 

三、defined判斷常量是否設置

    

    和define一樣,defined的實現也在Zend/zend_builtin_functions.c文件,其實現時一個讀取參數變量,調用zend_get_constant_ex函數獲取常量的值來判斷常量是否存在的過程。而zend_get_constant_ex函數不僅包括了常規的常量獲取,還包括類常量的獲取,最後是通過zend_get_constant函數獲取常量的值。在zend_get_constant函數中,基本上是通過下面的代碼來獲取常量的值:

zend_hash_find(EG(zend_constants), name, name_len+1, (void **) &c)

    除此之外,只在調用這個函數之前和之後對name有一些特殊的處理。

 

四、標准常量的初始化

     

    以上通過define定義的常量的模塊編號都是PHP_USER_CONSTANT,表示這是用戶定義的常量。除此之外我們在平時使用較多的,如在顯示所有級別錯誤報告時使用的E_ALL常量就有點不同了。我們以cgi模式為例說明標准常量的定義過程。整個調用順序:php_cgi_startup() -> php_module_startup() -> zend_startup() -> zend_register_standard_constant()

void zend_register_standard_constants(TSRMLS_D)
{
    ... // 若常量以REGISTER_MAIN_LONG_CONSTANT設置
    REGISTER_MAIN_LONG_CONSTANT("E_ALL", E_ALL, CONST_PERSISTENT | CONST_CS);
    ...
}

    REGISTER_MAIN_LONG_CONSTANT宏展開是以zend_register_long_constant實現。zend_register_long_constant函數將常量中值的類型,值,名稱及模塊號賦值給新的zend_constant。並調用zend_register_constant添加到全局的常量列表中。

    php_cgi_startup() -> php_module_startup() -> zend_startup() -> zend_register_standart_constant() -> zend_register_constant

ZEND_API void zend_register_long_constant(const char *name, uint name_len,
        long lval, int flags, int module_number TSRMLS_DC)
{
    zend_constant c;
 
    c.value.type = IS_LONG;
    c.value.value.lval = lval;
    c.flags = flags;
    c.name = zend_strndup(name, name_len-1);
    c.name_len = name_len;
    c.module_number = module_number;
    zend_register_constant(&c TSRMLS_CC);
}

    zend_register_constant函數首先根據常量中的c->flags判斷是否區分大小寫,如果不區分,則名字統一為小寫,如果包含"\\",也統一成小寫。否則為定義的名字然後將調用下面的語句將當前常量添加到EG(zend_constant)。EG(zend_constants)是一個HashTable,下面的代碼是將常量添加到這個HashTable中

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

    在php_module_startup函數中,除了zend_startup函數中有注冊標准的常量,它本身通過宏REGISTER_MAIN_LONG_CONSTANT等注冊了一些常量,如:PHP_VERSION,PHP_OS等。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved