看手册说define定义的常量只允许:
仅允许标量和 null。标量的类型是 integer, float,string 或者 boolean。 也能够定义常量值的类型为 resource ,但并不推荐这么做,可能会导致未知状况的发生。
今天阅读php源码,发现define的第二个参数其实也可以是一个对象。
先贴一段示例:
<span class><span a><span public> <span function><span __tostring><span return> 'bar'<span><span> = <span new><span a><span define>('foo', <span><span><span echo> foo;<br>// 输出bar</span></span></span></span></span></span></span></span></span></span></span></span></span></span>接着来看看php中的define究竟是如何实现的:
立即学习“PHP免费学习笔记(深入)”;
<span zend_function><span char> *<span name><span int><span name_len zval>*<span val zval>*val_free =<span null zend_bool non_cs>= <span><span><span int> case_sensitive =<span const_cs zend_constant c><span><span>
<span if> (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, <span><span sz><span>, &name, &name_len, &val, &non_cs) ==<span failure><span return><span><span><span>
<span if><span case_sensitive>= <span><span><span><span>
<span if> (zend_memnstr(name, <span><span ::><span>, <span sizeof>(<span><span ::><span>) - <span>, name +<span name_len zend_error><span><span class constants cannot be defined or redefined><span><span return_false><span><span>
<span repeat:><span switch><span><span case><span is_long:><span case><span is_double:><span case><span is_string:><span case><span is_bool:><span case><span is_resource:><span case><span is_null:><span break><span><span case><span is_object:><span if> (!<span val_free><span if> (Z_OBJ_HT_P(val)-><span get><span val_free>= val = Z_OBJ_HT_P(val)-><span get><span tsrmls_cc><span goto><span repeat><span else> <span if> (Z_OBJ_HT_P(val)-><span cast_object alloc_init_zval><span if> (Z_OBJ_HT_P(val)->cast_object(val, val_free, IS_STRING TSRMLS_CC) ==<span success val>=<span val_free><span break><span><span><span no break><span>
<span default><span : zend_error><span><span constants may only evaluate to scalar values><span><span><span if><span zval_ptr_dtor>&<span val_free return_false><span><span>
c.value = *<span val zval_copy_ctor>&<span c.value><span if><span zval_ptr_dtor>&<span val_free c.flags>= case_sensitive; <span><span non persistent><span><span c.name>=<span zend_strndup name_len c.name_len>= name_len+<span><span c.module_number>=<span php_user_constant>
</span><span><span>
<span if> (zend_register_constant(&c TSRMLS_CC) ==<span success return_true><span else><span return_false></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>注意以repeat开始的一段循环,还用到了goto语句T_T
这段代码的作用为:
- 对于int,float,string,bool,resource,null,则实际定义的常量时直接使用这些值
- 对于object,则需要将object转成上述6个类型之一(如果转型之后依然是object,则继续转型)
如何将object成6个类型之一呢?从代码上看有2种手段:
<span if> (Z_OBJ_HT_P(val)-><span get><span val_free>= val = Z_OBJ_HT_P(val)-><span get><span tsrmls_cc><span goto><span repeat></span>// __toString()方法会在cast_object中被调用 </span><span else> <span if> (Z_OBJ_HT_P(val)-><span cast_object alloc_init_zval><span if> (Z_OBJ_HT_P(val)->cast_object(val, val_free, IS_STRING TSRMLS_CC) ==<span success val>=<span val_free><span break><span></span></span></span></span></span></span></span></span></span></span></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> zend_std_cast_object_tostring(zval *readobj, zval *writeobj, <span int> type TSRMLS_DC) <span><span><span><span zval>*<span retval zend_class_entry>*<span ce><span switch><span><span case><span is_string: ce>=<span z_objce_p><span><span>
<span if> (ce->__tostring &&<span>&readobj, ce, &ce->__tostring, <span><span __tostring><span>, &retval) ||<span eg><span return><span failure><span return><span failure></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>从上述具体实现来看,默认的cast_object就是去寻找class中的__tostring方法然后调用...
PHP网络编程技术详解由浅入深,全面、系统地介绍了PHP开发技术,并提供了大量实例,供读者实战演练。另外,笔者专门为本书录制了相应的配套教学视频,以帮助读者更好地学习本书内容。这些视频和书中的实例源代码一起收录于配书光盘中。本书共分4篇。第1篇是PHP准备篇,介绍了PHP的优势、开发环境及安装;第2篇是PHP基础篇,介绍了PHP中的常量与变量、运算符与表达式、流程控制以及函数;第3篇是进阶篇,介绍
回到刚开始的例子,('foo',
<span define>('PHP_INT_MAX', 1); <span><span>
<span define>('FOO', 1); <span><span>
<span define>('FOO', 2); <span><span></span></span></span></span></span></span></span></span></span>上面代码包含了两种情况,一是我们尝试重新定义php内核的预定义常量,比如PHP_INT_MAX,这显然会失败。第二种情况是我们曾经在代码的某个位置定义过了一个常量FOO,然后又在接下来的程序中再次定义它,这也会造成失败。因此,在编码时最好将所有需要定义的常量写在一起,以免造成name重复。
2,常量名没有限制
对其name不做任何要求,当然也不需要name是一个合法的php变量名。因此,我们可以让define的常量取一些稀奇古怪的名称。例如:
<span define>('>_<', 123); <span><span>
<span echo> >_<; <span><span syntax error></span></span></span></span></span></span>不过如果定义了这样的常量,是没法直接使用的,会报语法错误。正确的使用方法如下:
<span define>('>_<', 123); <span><span>
<span echo> <span constant>('>_<'); <span><span></span></span></span></span></span></span></span>










