看手册说define定义的常量只允许:
仅允许标量和 null。标量的类型是 integer, float,string 或者 boolean。 也能够定义常量值的类型为 resource ,但并不推荐这么做,可能会导致未知状况的发生。
今天阅读php源码,发现define的第二个参数其实也可以是一个对象。
先贴一段示例:
<span class</span><span A {
</span><span public</span> <span function</span><span __toString() {
</span><span return</span> 'bar'<span ;
}
}
</span><span $a</span> = <span new</span><span A();
</span><span define</span>('foo', <span $a</span><span );
</span><span echo</span> foo;<br />// 输出bar接着来看看php中的define究竟是如何实现的:
<span ZEND_FUNCTION(define)
{
</span><span char</span> *<span name;
</span><span int</span><span name_len;
zval </span>*<span val;
zval </span>*val_free =<span NULL;
zend_bool non_cs </span>= <span 0</span><span ;
</span><span int</span> case_sensitive =<span CONST_CS;
zend_constant c;
</span><span //</span><span 接收3个参数,string,zval,bool</span>
<span if</span> (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, <span "</span><span sz|b</span><span "</span>, &name, &name_len, &val, &non_cs) ==<span FAILURE) {
</span><span return</span><span ;
}
</span><span //</span><span 是否大小写敏感</span>
<span if</span><span (non_cs) {
case_sensitive </span>= <span 0</span><span ;
}
</span><span //</span><span 如果define类常量,则报错</span>
<span if</span> (zend_memnstr(name, <span "</span><span ::</span><span "</span>, <span sizeof</span>(<span "</span><span ::</span><span "</span>) - <span 1</span>, name +<span name_len)) {
zend_error(E_WARNING, </span><span "</span><span Class constants cannot be defined or redefined</span><span "</span><span );
RETURN_FALSE;
}
</span><span //</span><span 获取真正的值,用val保存</span>
<span repeat:
</span><span switch</span><span (Z_TYPE_P(val)) {
</span><span case</span><span IS_LONG:
</span><span case</span><span IS_DOUBLE:
</span><span case</span><span IS_STRING:
</span><span case</span><span IS_BOOL:
</span><span case</span><span IS_RESOURCE:
</span><span case</span><span IS_NULL:
</span><span break</span><span ;
</span><span case</span><span IS_OBJECT:
</span><span if</span> (!<span val_free) {
</span><span if</span> (Z_OBJ_HT_P(val)-><span get</span><span ) {
val_free </span>= val = Z_OBJ_HT_P(val)-><span get</span><span (val TSRMLS_CC);
</span><span goto</span><span repeat;
} </span><span else</span> <span if</span> (Z_OBJ_HT_P(val)-><span cast_object) {
ALLOC_INIT_ZVAL(val_free);
</span><span if</span> (Z_OBJ_HT_P(val)->cast_object(val, val_free, IS_STRING TSRMLS_CC) ==<span SUCCESS) {
val </span>=<span val_free;
</span><span break</span><span ;
}
}
}
</span><span /*</span><span no break </span><span */</span>
<span default</span><span :
zend_error(E_WARNING,</span><span "</span><span Constants may only evaluate to scalar values</span><span "</span><span );
</span><span if</span><span (val_free) {
zval_ptr_dtor(</span>&<span val_free);
}
RETURN_FALSE;
}
</span><span //</span><span 构建常量</span>
c.value = *<span val;
zval_copy_ctor(</span>&<span c.value);
</span><span if</span><span (val_free) {
zval_ptr_dtor(</span>&<span val_free);
}
c.flags </span>= case_sensitive; <span /*</span><span non persistent </span><span */ // 如果大小写不敏感,则为0,敏感则为1</span><span
c.name </span>=<span zend_strndup(name, name_len);
c.name_len </span>= name_len+<span 1</span><span ;
c.module_number </span>=<span PHP_USER_CONSTANT; <span // 标注非内核常量,而是用户定义的常量</span>
</span><span //</span><span 注册常量</span>
<span if</span> (zend_register_constant(&c TSRMLS_CC) ==<span SUCCESS) {
RETURN_TRUE;
} </span><span else</span><span {
RETURN_FALSE;
}
}</span>注意以repeat开始的一段循环,还用到了goto语句T_T
立即学习“PHP免费学习笔记(深入)”;
这段代码的作用为:
如何将object成6个类型之一呢?从代码上看有2种手段:
<span if</span> (Z_OBJ_HT_P(val)-><span get</span><span ) {
val_free </span>= val = Z_OBJ_HT_P(val)-><span get</span><span (val TSRMLS_CC);
</span><span goto</span><span repeat;
}<br />// __toString()方法会在cast_object中被调用
</span><span else</span> <span if</span> (Z_OBJ_HT_P(val)-><span cast_object) {
ALLOC_INIT_ZVAL(val_free);
</span><span if</span> (Z_OBJ_HT_P(val)->cast_object(val, val_free, IS_STRING TSRMLS_CC) ==<span SUCCESS)
{
val </span>=<span val_free;
</span><span break</span><span ;
}
}</span>1,Z_OBJ_HT_P(val)->get ,宏展开之后为(*val).value.obj.handlers->get
2,Z_OBJ_HT_P(val)->cast_object,宏展开之后为(*val).value.obj.handlers->cast_object
handlers是一个包含很多函数指针的结构体,具体定义参见_zend_object_handlers 。该结构体中的函数指针均用于操作object,比如读取/修改对象属性、获取/调用对象方法等等...get和cast_object也是其中之一。
对于一般的对象,php提供了标准的cast_object函数zend_std_cast_object_tostring,代码位于php-src/zend/zend-object-handlers.c中:
ZEND_API <span int</span> zend_std_cast_object_tostring(zval *readobj, zval *writeobj, <span int</span> type TSRMLS_DC) <span /*</span><span {{{ </span><span */</span><span
{
zval </span>*<span retval;
zend_class_entry </span>*<span ce;
</span><span switch</span><span (type) {
</span><span case</span><span IS_STRING:
ce </span>=<span Z_OBJCE_P(readobj);
</span><span //</span><span 如果用户的class中定义了__toString,则尝试调用</span>
<span if</span> (ce->__tostring &&<span
(zend_call_method_with_0_params(</span>&readobj, ce, &ce->__tostring, <span "</span><span __tostring</span><span "</span>, &retval) ||<span EG(exception))) {
……
}
</span><span return</span><span FAILURE;
……
}
</span><span return</span><span FAILURE;
}</span>从上述具体实现来看,默认的cast_object就是去寻找class中的__tostring方法然后调用...
回到刚开始的例子,('foo',
<span define</span>('PHP_INT_MAX', 1); <span //</span><span 返回FALSE</span>
<span define</span>('FOO', 1); <span //</span><span 返回TRUE</span>
<span define</span>('FOO', 2); <span //</span><span 返回FALSE</span>上面代码包含了两种情况,一是我们尝试重新定义php内核的预定义常量,比如PHP_INT_MAX,这显然会失败。第二种情况是我们曾经在代码的某个位置定义过了一个常量FOO,然后又在接下来的程序中再次定义它,这也会造成失败。因此,在编码时最好将所有需要定义的常量写在一起,以免造成name重复。
对其name不做任何要求,当然也不需要name是一个合法的php变量名。因此,我们可以让define的常量取一些稀奇古怪的名称。例如:
<span define</span>('>_<', 123); <span //</span><span 返回TRUE</span>
<span echo</span> >_<; <span // </span><span syntax error</span>不过如果定义了这样的常量,是没法直接使用的,会报语法错误。正确的使用方法如下:
<span define</span>('>_<', 123); <span //</span><span 返回TRUE</span>
<span echo</span> <span constant</span>('>_<'); <span //</span><span 输出123</span>
PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号