之前一直没看过php中是如何定义一个常量的,今天空闲的时候看了下,发现其机制也很简单
在php中定义常量 有define,和const两个方法
区别是define是个函数
const是个结构
define不能用于类中
可以用define 定义一个表达式,
const不能定义为一个表达式
<?php const ABC="def"; echo ABC; ?>
const ABC="def"对应的opcode为 ZEND_DECLARE_CONST ,其实常说的opcode是zend_op这个结构体里的一个属性,叫opcode, 就是一个数字,只不过以宏的形式展现
static int ZEND_FASTCALL ZEND_DECLARE_CONST_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE zval *name; zval *val; zend_constant c; SAVE_OPLINE(); name = opline->op1.zv; val = opline->op2.zv; if ((Z_TYPE_P(val) & IS_CONSTANT_TYPE_MASK) == IS_CONSTANT || Z_TYPE_P(val) == IS_CONSTANT_ARRAY) { /// } else { INIT_PZVAL_COPY(&c.value, val); zval_copy_ctor(&c.value); } c.flags = CONST_CS; /* non persistent, case sensetive */ c.name = IS_INTERNED(Z_STRVAL_P(name)) ? Z_STRVAL_P(name) : zend_strndup(Z_STRVAL_P(name), Z_STRLEN_P(name)); c.name_len = Z_STRLEN_P(name)+1; c.module_number = PHP_USER_CONSTANT; if (zend_register_constant(&c TSRMLS_CC) == FAILURE) { } CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); }
最终放到的是EG(zend_constant)这个hashtable里
ZEND_API int zend_register_constant(zend_constant *c TSRMLS_DC) { char *lowercase_name = NULL; char *name; int ret = SUCCESS; ulong chash = 0; if (!(c->flags & CONST_CS)) { /* keep in mind that c->name_len already contains the ' ' */ lowercase_name = estrndup(c->name, c->name_len-1); zend_str_tolower(lowercase_name, c->name_len-1); lowercase_name = (char*)zend_new_interned_string(lowercase_name, c->name_len, 1 TSRMLS_CC); name = lowercase_name; chash = IS_INTERNED(lowercase_name) ? INTERNED_HASH(lowercase_name) : 0; } else { } if (chash == 0) { chash = zend_hash_func(name, c->name_len); } /* Check if the user is trying to define the internal pseudo constant name __COMPILER_HALT_OFFSET__ */ if ((c->name_len == sizeof("__COMPILER_HALT_OFFSET__") && !memcmp(name, "__COMPILER_HALT_OFFSET__", sizeof("__COMPILER_HALT_OFFSET__")-1)) || zend_hash_quick_add(EG(zend_constants), name, c->name_len, chash, (void *) c, sizeof(zend_constant), NULL)==FAILURE) { /// ret = FAILURE; } if (lowercase_name && !IS_INTERNED(lowercase_name)) { efree(lowercase_name); } return ret; }
echo ABC的时候, opcode是ZEND_FETCH_CONSTANT,下面的函数会把取出的数据赋值给 zend_op中的属性result, echo 时,再显示这个result的值
static int ZEND_FASTCALL ZEND_FETCH_CONSTANT_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE SAVE_OPLINE(); if (IS_UNUSED == IS_UNUSED) { zend_constant *c; zval *retval; if (CACHED_PTR(opline->op2.literal->cache_slot)) { c = CACHED_PTR(opline->op2.literal->cache_slot); } else if ((c = zend_quick_get_constant(opline->op2.literal + 1, opline->extended_value TSRMLS_CC)) == NULL) { if ((opline->extended_value & IS_CONSTANT_UNQUALIFIED) != 0) { char *actual = (char *)zend_memrchr(Z_STRVAL_P(opline->op2.zv), '\', Z_STRLEN_P(opline->op2.zv)); if(!actual) { actual = Z_STRVAL_P(opline->op2.zv); } else { actual++; } /* non-qualified constant - allow text substitution */ zend_error(E_NOTICE, "Use of undefined constant %s - assumed '%s'", actual, actual); ZVAL_STRINGL(&EX_T(opline->result.var).tmp_var, actual, Z_STRLEN_P(opline->op2.zv)-(actual - Z_STRVAL_P(opline->op2.zv)), 1); CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); } else { zend_error_noreturn(E_ERROR, "Undefined constant '%s'", Z_STRVAL_P(opline->op2.zv)); } } else { CACHE_PTR(opline->op2.literal->cache_slot, c); } retval = &EX_T(opline->result.var).tmp_var; ZVAL_COPY_VALUE(retval, &c->value); zval_copy_ctor(retval); CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); } }