原創:PHP內核研究常量定義 define/defined

常量

什麼是常量.

常量就是不變的量.
先看看常量的結構

  1. typedef struct _zend_constant { 
  2.         zval value;//zval類型 
  3.         int flags;//標示 是否大小寫敏感 
  4.         char *name;//常量名稱 
  5.         uint name_len;//長度 
  6.         int module_number;//標示是用戶定義的常量 不是系統常量 
  7. } zend_constant; 

PHP定義常量如下

 

  1. <?php 
  2. define('TEST',1); 

很簡單 .定義了一個常量

那在內核裏都做了什麼?

打開 zend/zend_builtin_functions.c  PHP的內置函數都在這裏

找到 ZEND_FUNCTION(define)

代碼如下

 

  1. ZEND_FUNCTION(define) 
  2.         char *name; 
  3.         int name_len; 
  4.         zval *val; 
  5.         zval *val_free = NULL; 
  6.         zend_bool non_cs = 0; 
  7.         int case_sensitive = CONST_CS; 
  8.         zend_constant c;//這個是常量的struct 
  9.   
  10.         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz|b", &name, &name_len, &val, &non_cs) == FAILURE) { 
  11.                 return
  12.         } 
  13.   
  14.         if(non_cs) {//define第三個參數的作用,大小寫是否敏感 如果爲真 大小寫不敏感 
  15.                 case_sensitive = 0; 
  16.         } 
  17.   
  18.         //PHP5.3新特性:類常量,暫不做介紹 
  19.         if (zend_memnstr(name, "::"sizeof("::") - 1, name + name_len)) { 
  20.                 zend_error(E_WARNING, "Class constants cannot be defined or redefined"); 
  21.                 RETURN_FALSE; 
  22.         } 
  23.   
  24. repeat: 
  25.        //類型檢測 
  26.         switch (Z_TYPE_P(val)) { 
  27.                 case IS_LONG: 
  28.                 case IS_DOUBLE: 
  29.                 case IS_STRING: 
  30.                 case IS_BOOL: 
  31.                 case IS_RESOURCE: 
  32.                 case IS_NULL: 
  33.                         break
  34.                 case IS_OBJECT: 
  35.                         if (!val_free) { 
  36.                                 if (Z_OBJ_HT_P(val)->get) { 
  37.                                         val_free = val = Z_OBJ_HT_P(val)->get(val TSRMLS_CC); 
  38.                                         goto repeat; 
  39.                                 } else if (Z_OBJ_HT_P(val)->cast_object) { 
  40.                                         ALLOC_INIT_ZVAL(val_free); 
  41.                                         if (Z_OBJ_HT_P(val)->cast_object(val, val_free, IS_STRING TSRMLS_CC) == SUCCESS) { 
  42.                                                 val = val_free; 
  43.                                                 break
  44.                                         } 
  45.                                 } 
  46.                         } 
  47.                         /* no break */ 
  48.                 default
  49.                         zend_error(E_WARNING,"Constants may only evaluate to scalar values"); 
  50.                         if (val_free) { 
  51.                                 zval_ptr_dtor(&val_free); 
  52.                         } 
  53.                         RETURN_FALSE; 
  54.         } 
  55.   
  56.         c.value = *val; //拷貝常量的值 
  57.         zval_copy_ctor(&c.value);//完全拷貝 因爲val是zval類型,除了value還有其他屬性,用這個宏完全拷貝過來 
  58.         if (val_free) { 
  59.                 zval_ptr_dtor(&val_free); 
  60.         } 
  61.         c.flags = case_sensitive; //大小寫敏感? 
  62.         c.name = zend_strndup(name, name_len); //拷貝name 
  63.         c.name_len = name_len+1; 
  64.         c.module_number = PHP_USER_CONSTANT; //標示是用戶創建的常量而不是系統常量 
  65.         if (zend_register_constant(&c TSRMLS_CC) == SUCCESS) { //添加到常量符號表裏. 
  66.                 RETURN_TRUE; 
  67.         } else { 
  68.                 RETURN_FALSE; 
  69.         } 

既然說了define 那就肯定要說說defined了
defined用來檢測 常量是否已經定義
這個其實很簡單 代碼如下

  1. ZEND_FUNCTION(defined) 
  2.         char *name; 
  3.         int name_len; 
  4.         zval c; 
  5.   
  6.         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &name_len) == FAILURE) { 
  7.                 return
  8.         } 
  9.   
  10.         if (zend_get_constant_ex(name, name_len, &c, NULL, ZEND_FETCH_CLASS_SILENT TSRMLS_CC)) {//找到了 
  11.                 zval_dtor(&c); //釋放zval.value的內存 
  12.                 RETURN_TRUE; 
  13.         } else {//沒找到 
  14.                 RETURN_FALSE; 
  15.         } 

 

主要的工作在 zend_get_constant_ex這個函數裏

 

  1. ZEND_API int zend_get_constant_ex(const char *name, uint name_len, zval *result, zend_class_entry *scope, ulong flags TSRMLS_DC) 
  2.         zend_constant *c; //常量類型 
  3.         int retval = 1; 
  4.         char *colon; 
  5.         zend_class_entry *ce = NULL; 
  6.         char *class_name; 
  7.         zval **ret_constant; 
  8.   
  9.         /* Skip leading \\ */ 
  10.         if (name[0] == '\\') { 
  11.                 name += 1; 
  12.                 name_len -= 1; 
  13.         } 
  14.   
  15.         if ((colon = zend_memrchr(name, ':', name_len)) && 
  16.             colon > name && (*(colon - 1) == ':')) {  //類常量 暫不做介紹 
  17.                 int class_name_len = colon - name - 1; 
  18.                 int const_name_len = name_len - class_name_len - 2; 
  19.                 char *constant_name = colon + 1; 
  20.                 char *lcname; 
  21.   
  22.                 class_name = estrndup(name, class_name_len); 
  23.                 lcname = zend_str_tolower_dup(class_name, class_name_len); 
  24.                 if (!scope) { 
  25.                         if (EG(in_execution)) { 
  26.                                 scope = EG(scope); 
  27.                         } else { 
  28.                                 scope = CG(active_class_entry); 
  29.                         } 
  30.                 } 
  31.   
  32.                 if (class_name_len == sizeof("self")-1 && 
  33.                     !memcmp(lcname, "self"sizeof("self")-1)) { 
  34.                         if (scope) { 
  35.                                 ce = scope; 
  36.                         } else { 
  37.                                 zend_error(E_ERROR, "Cannot access self:: when no class scope is active"); 
  38.                                 retval = 0; 
  39.                         } 
  40.                         efree(lcname); 
  41.                 } else if (class_name_len == sizeof("parent")-1 && 
  42.                            !memcmp(lcname, "parent"sizeof("parent")-1)) { 
  43.                         if (!scope) { 
  44.                                 zend_error(E_ERROR, "Cannot access parent:: when no class scope is active"); 
  45.                         } else if (!scope->parent) { 
  46.                                 zend_error(E_ERROR, "Cannot access parent:: when current class scope has no parent"); 
  47.                         } else { 
  48.                                 ce = scope->parent; 
  49.                         } 
  50.                         efree(lcname); 
  51.                 } else if (class_name_len == sizeof("static")-1 && 
  52.                            !memcmp(lcname, "static"sizeof("static")-1)) { 
  53.                         if (EG(called_scope)) { 
  54.                                 ce = EG(called_scope); 
  55.                         } else { 
  56.                                 zend_error(E_ERROR, "Cannot access static:: when no class scope is active"); 
  57.                         } 
  58.                         efree(lcname); 
  59.                 } else { 
  60.                         efree(lcname); 
  61.                         ce = zend_fetch_class(class_name, class_name_len, flags TSRMLS_CC); 
  62.                 } 
  63.                 if (retval && ce) { 
  64.                         if (zend_hash_find(&ce->constants_table, constant_name, const_name_len+1, (void **) &ret_constant) != SUCCESS) { 
  65.                                 retval = 0; 
  66.                                 if ((flags & ZEND_FETCH_CLASS_SILENT) == 0) { 
  67.                                         zend_error(E_ERROR, "Undefined class constant '%s::%s'", class_name, constant_name); 
  68.                                 } 
  69.                         } 
  70.                 } else if (!ce) { 
  71.                         retval = 0; 
  72.                 } 
  73.                 efree(class_name); 
  74.                 goto finish; 
  75.         } 
  76.   
  77.         //普通常量  colon爲反斜線之前的字符串 
  78.         if ((colon = zend_memrchr(name, '\\', name_len)) != NULL) { 
  79.                 /* compound constant name */ 
  80.                 int prefix_len = colon - name; 
  81.                 int const_name_len = name_len - prefix_len - 1; 
  82.                 char *constant_name = colon + 1; 
  83.                 char *lcname; 
  84.                 int found_const = 0; 
  85.   
  86.                 lcname = zend_str_tolower_dup(name, prefix_len); 
  87.                 /* Check for namespace constant */ 
  88.   
  89.                 /* Concatenate lowercase namespace name and constant name */ 
  90.                 lcname = erealloc(lcname, prefix_len + 1 + const_name_len + 1); 
  91.                 lcname[prefix_len] = '\\'
  92.                 memcpy(lcname + prefix_len + 1, constant_name, const_name_len + 1); 
  93.     //查找常量 
  94.                 if (zend_hash_find(EG(zend_constants), lcname, prefix_len + 1 + const_name_len + 1, (void **) &c) == SUCCESS) { 
  95.                         found_const = 1;//找到了 
  96.                 } else {//沒找到 
  97.                        //轉換爲小寫重新查找 
  98.                         zend_str_tolower(lcname + prefix_len + 1, const_name_len); 
  99.                         if (zend_hash_find(EG(zend_constants), lcname, prefix_len + 1 + const_name_len + 1, (void **) &c) == SUCCESS) {//找到了 
  100.                                 if ((c->flags & CONST_CS) == 0) { 
  101.                                         found_const = 1; 
  102.                                 } 
  103.                         } 
  104.                 } 
  105.                 efree(lcname); 
  106.                 if(found_const) { 
  107.                         *result = c->value; 
  108.                         zval_update_constant_ex(&result, (void*)1, NULL TSRMLS_CC); 
  109.                         zval_copy_ctor(result); 
  110.                         Z_SET_REFCOUNT_P(result, 1); 
  111.                         Z_UNSET_ISREF_P(result); 
  112.                         return 1; 
  113.                 } 
  114.                 /* name requires runtime resolution, need to check non-namespaced name */ 
  115.                 if ((flags & IS_CONSTANT_UNQUALIFIED) != 0) { 
  116.                         name = constant_name; 
  117.                         name_len = const_name_len; 
  118.                         return zend_get_constant(name, name_len, result TSRMLS_CC); 
  119.                 } 
  120.                 retval = 0; 
  121. finish: 
  122.                 if (retval) { 
  123.                         zval_update_constant_ex(ret_constant, (void*)1, ce TSRMLS_CC); 
  124.                         *result = **ret_constant; 
  125.                         zval_copy_ctor(result); 
  126.                         INIT_PZVAL(result); 
  127.                 } 
  128.   
  129.                 return retval; 
  130.         } 
  131.         return zend_get_constant(name, name_len, result TSRMLS_CC); 

define就是將創建的常量 放在EG(zend_constants)裏
defined就是在EG(zend_constants)去遞歸查找

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章