PHP中define常量的实现

https://blog.csdn.net/yongcto/article/details/17366425?utm_source=blogxgwz4

在PHP中,常量的名字是一个简单的标识符,执行周期中不能改变,并且默认情况下是大小写敏感的。通常常量总是大写的。
注:define中的第三个参数可以设置是否常量名大小写敏感


一、常量的内部结构
[cpp] view plaincopy
typedef struct _zend_constant { 
    zval value; /* zval结构,PHP内部变量的存储结构 */ 
    int flags;  /* 常量的标记如 CONST_PERSISTENT | CONST_CS */ 
    char *name; /* 常量名称 */ 
    uint name_len;  /* 常量名称字符长度 */ 
    int module_number;  /* 模块号 */ 
} zend_constant; 

二、PHP中定义常量
[php] view plaincopy
<?php 
define('DATABASE', 'MYSQL'); 
define('DATABASE_USER', 'ROOT'); 
define('DATABASE_PASSWORD', 'PASSWORD'); 

三、分析opcode
# php -dvld.active=1 index.php
打印出opchode,如下图

注,查看opcode扩展VLD安装可以阅读 《通过VLD扩展分析PHP opcode》
这里介绍出现的了三条指令,SEND_VAL、DO_FCALL、RETURN
SEND_VAL:传递参数的固定值,操作码65
RETURN:从函数中返回结果,操作码62
DO_FCALL:调用函数,操作码60
更多指令说明:http://php.net/manual/en/internals2.opcodes.list.php


四、通过ZEND VM执行opcode
[cpp] view plaincopy
void zif_define(int ht, zval *return_value, zval **return_value_ptr, zval *this_ptr, int return_value_used ) 
 
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;    //PHP中常量对应的结构体变量 
 
    /*
     * sz|b function define(string, zval [, boolean])
     * 这个参数表示定义接收的参数,s为字符串,z为zval,b为boolean
     * */ 
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz|b", &name, &name_len, &val, &non_cs) == FAILURE) { 
        return; 
    } 
 
    if(non_cs) { 
        case_sensitive = 0; 
    } 
 
    /* class constant, check if there is name and make sure class is valid & exists */ 
    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; 
        //上面,允许integer, float,string 或者 boolean和 null 
        case IS_OBJECT: 
            /* 如果传入的是对象, 通过判断是否定义__tostring()魔术方法,重新设置val值
             * 可以看出,define已经接受对象作为参数,前提条件是定义了__tostring(),并且返回的值也是integer, float,string 或者 boolean和 null
             * 此时,这里的版本是php5.3.8
             * */ 
            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); 
    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; 
    } 


---------------------
作者:zkaipmoo
来源:CSDN
原文:https://blog.csdn.net/yongcto/article/details/17366425
版权声明:本文为博主原创文章,转载请附上博文链接!

posted @ 2018-12-07 16:40  精灵的泪  阅读(13)  评论(0)    收藏  举报