• 深入理解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等。

  • 相关阅读:
    【干货】如何5分钟内解决实时输入仿真(超简单)
    我们不生产bug,我们只是算法的搬运工——OO第一次作业(踩雷)总结
    OO助教总结
    oo第四次总结作业
    oo第三次总结性作业
    OO第二次总结性作业
    oo第四次作业
    C++学习记录二:
    长沙.NET社区之光
    课程总结
  • 原文地址:https://www.cnblogs.com/orlion/p/5344135.html
Copyright © 2020-2023  润新知