原创:PHP内核研究 常量
作者 斯人 | 发布于 2012 年 1 月 5 日
PHP PHP内核

常量

什么是常量.

常量就是不变的量.
先看看常量的结构

typedef struct _zend_constant {
        zval value;//zval类型
        int flags;//标示 是否大小写敏感
        char *name;//常量名称
        uint name_len;//长度
        int module_number;//标示是用户定义的常量 不是系统常量
} zend_constant;

PHP定义常量如下


<?php

define('TEST',1);

很简单 .定义了一个常量

那在内核里都做了什么?

打开 zend/zend_builtin_functions.c  PHP的内置函数都在这里

找到 ZEND_FUNCTION(define)

代码如下


ZEND_FUNCTION(define)
{
        char *name;
        int name_len;
        zval *val;
        zval *val_free = NULL;
        zend_bool non_cs = 0;
        int case_sensitive = CONST_CS;
        zend_constant c;//这个是常量的struct

        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz|b", &name, &name_len, &val, &non_cs) == FAILURE) {
                return;
        }

        if(non_cs) {//define第三个参数的作用,大小写是否敏感 如果为真 大小写不敏感
                case_sensitive = 0;
        }

        //PHP5.3新特性:类常量,暂不做介绍
        if (zend_memnstr(name, "::", sizeof("::") - 1, name + name_len)) {
                zend_error(E_WARNING, "Class constants cannot be defined or redefined");
                RETURN_FALSE;
        }

repeat:
       //类型检测
        switch (Z_TYPE_P(val)) {
                case IS_LONG:
                case IS_DOUBLE:
                case IS_STRING:
                case IS_BOOL:
                case IS_RESOURCE:
                case IS_NULL:
                        break;
                case IS_OBJECT:
                        if (!val_free) {
                                if (Z_OBJ_HT_P(val)->get) {
                                        val_free = val = Z_OBJ_HT_P(val)->get(val TSRMLS_CC);
                                        goto repeat;
                                } else if (Z_OBJ_HT_P(val)->cast_object) {
                                        ALLOC_INIT_ZVAL(val_free);
                                        if (Z_OBJ_HT_P(val)->cast_object(val, val_free, IS_STRING TSRMLS_CC) == SUCCESS) {
                                                val = val_free;
                                                break;
                                        }
                                }
                        }
                        /* no break */
                default:
                        zend_error(E_WARNING,"Constants may only evaluate to scalar values");
                        if (val_free) {
                                zval_ptr_dtor(&val_free);
                        }
                        RETURN_FALSE;
        }

        c.value = *val; //拷贝常量的值
        zval_copy_ctor(&c.value);//完全拷贝 因为val是zval类型,除了value还有其他属性,用这个宏完全拷贝过来 
        if (val_free) {
                zval_ptr_dtor(&val_free);
        }
        c.flags = case_sensitive; //大小写敏感?
        c.name = zend_strndup(name, name_len); //拷贝name
        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;
        }
}

既然说了define 那就肯定要说说defined了
defined用来检测 常量是否已经定义
这个其实很简单 代码如下

ZEND_FUNCTION(defined)
{
        char *name;
        int name_len;
        zval c;

        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &name_len) == FAILURE) {
                return;
        }

        if (zend_get_constant_ex(name, name_len, &c, NULL, ZEND_FETCH_CLASS_SILENT TSRMLS_CC)) {//找到了
                zval_dtor(&c); //释放zval.value的内存
                RETURN_TRUE;
        } else {//没找到
                RETURN_FALSE;
        }
}

主要的工作在 zend_get_constant_ex这个函数里

ZEND_API int zend_get_constant_ex(const char *name, uint name_len, zval *result, zend_class_entry *scope, ulong flags TSRMLS_DC)
{
        zend_constant *c; //常量类型
        int retval = 1;
        char *colon;
        zend_class_entry *ce = NULL;
        char *class_name;
        zval **ret_constant;

        /* Skip leading \\ */
        if (name[0] == '\\') {
                name += 1;
                name_len -= 1;
        }


        if ((colon = zend_memrchr(name, ':', name_len)) &&
            colon > name && (*(colon - 1) == ':')) {  //类常量 暂不做介绍
                int class_name_len = colon - name - 1;
                int const_name_len = name_len - class_name_len - 2;
                char *constant_name = colon + 1;
                char *lcname;

                class_name = estrndup(name, class_name_len);
                lcname = zend_str_tolower_dup(class_name, class_name_len);
                if (!scope) {
                        if (EG(in_execution)) {
                                scope = EG(scope);
                        } else {
                                scope = CG(active_class_entry);
                        }
                }

                if (class_name_len == sizeof("self")-1 &&
                    !memcmp(lcname, "self", sizeof("self")-1)) {
                        if (scope) {
                                ce = scope;
                        } else {
                                zend_error(E_ERROR, "Cannot access self:: when no class scope is active");
                                retval = 0;
                        }
                        efree(lcname);
                } else if (class_name_len == sizeof("parent")-1 &&
                           !memcmp(lcname, "parent", sizeof("parent")-1)) {
                        if (!scope) {
                                zend_error(E_ERROR, "Cannot access parent:: when no class scope is active");
                        } else if (!scope->parent) {
                                zend_error(E_ERROR, "Cannot access parent:: when current class scope has no parent");
                        } else {
                                ce = scope->parent;
                        }
                        efree(lcname);
                } else if (class_name_len == sizeof("static")-1 &&
                           !memcmp(lcname, "static", sizeof("static")-1)) {
                        if (EG(called_scope)) {
                                ce = EG(called_scope);
                        } else {
                                zend_error(E_ERROR, "Cannot access static:: when no class scope is active");
                        }
                        efree(lcname);
                } else {
                        efree(lcname);
                        ce = zend_fetch_class(class_name, class_name_len, flags TSRMLS_CC);
                }
                if (retval && ce) {
                        if (zend_hash_find(&ce->constants_table, constant_name, const_name_len+1, (void **) &ret_constant) != SUCCESS) {
                                retval = 0;
                                if ((flags & ZEND_FETCH_CLASS_SILENT) == 0) {
                                        zend_error(E_ERROR, "Undefined class constant '%s::%s'", class_name, constant_name);
                                }
                        }
                } else if (!ce) {
                        retval = 0;
                }
                efree(class_name);
                goto finish;
        }

        //普通常量  colon为反斜线之前的字符串
        if ((colon = zend_memrchr(name, '\\', name_len)) != NULL) {
                /* compound constant name */
                int prefix_len = colon - name;
                int const_name_len = name_len - prefix_len - 1;
                char *constant_name = colon + 1;
                char *lcname;
                int found_const = 0;
      
                lcname = zend_str_tolower_dup(name, prefix_len);
                /* Check for namespace constant */

                /* Concatenate lowercase namespace name and constant name */
                lcname = erealloc(lcname, prefix_len + 1 + const_name_len + 1);
                lcname[prefix_len] = '\\';
                memcpy(lcname + prefix_len + 1, constant_name, const_name_len + 1);
    //查找常量
                if (zend_hash_find(EG(zend_constants), lcname, prefix_len + 1 + const_name_len + 1, (void **) &c) == SUCCESS) {
                        found_const = 1;//找到了
                } else {//没找到
                       //转换为小写重新查找
                        zend_str_tolower(lcname + prefix_len + 1, const_name_len);
                        if (zend_hash_find(EG(zend_constants), lcname, prefix_len + 1 + const_name_len + 1, (void **) &c) == SUCCESS) {//找到了
                                if ((c->flags & CONST_CS) == 0) {
                                        found_const = 1;
                                }
                        }
                }
                efree(lcname);
                if(found_const) {
                        *result = c->value;
                        zval_update_constant_ex(&result, (void*)1, NULL TSRMLS_CC);
                        zval_copy_ctor(result);
                        Z_SET_REFCOUNT_P(result, 1);
                        Z_UNSET_ISREF_P(result);
                        return 1;
                }
                /* name requires runtime resolution, need to check non-namespaced name */
                if ((flags & IS_CONSTANT_UNQUALIFIED) != 0) {
                        name = constant_name;
                        name_len = const_name_len;
                        return zend_get_constant(name, name_len, result TSRMLS_CC);
                }
                retval = 0;
finish:
                if (retval) {
                        zval_update_constant_ex(ret_constant, (void*)1, ce TSRMLS_CC);
                        *result = **ret_constant;
                        zval_copy_ctor(result);
                        INIT_PZVAL(result);
                }

                return retval;
        }
        return zend_get_constant(name, name_len, result TSRMLS_CC);
}

define就是将创建的常量 放在EG(zend_constants)里
defined就是在EG(zend_constants)去递归查找

原文出处:http://www.imsiren.com/archives/127