php define的第二个参数使用方法

前端之家收集整理的这篇文章主要介绍了php define的第二个参数使用方法前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

看手册说define定义的常量只允许:
仅允许标量和 null。标量的类型是 integer, float,string 或者 boolean。 也能够定义常量值的类型为 resource ,但并不推荐这么做,可能会导致未知状况的发生。
今天阅读PHP源码,发现define的第二个参数其实也可以是一个对象。
先贴一段示例:
<div class="codetitle"><a style="CURSOR: pointer" data="91402" class="copybut" id="copybut91402" onclick="doCopy('code91402')"> 代码如下:

<div class="codebody" id="code91402">
class A {
public function toString() {
return 'bar';
}
}$a = new A();
define('foo',$a);
echo foo;
// 输出bar

接着来看看PHP中的define究竟是如何实现的:
<div class="codetitle"><a style="CURSOR: pointer" data="83116" class="copybut" id="copybut83116" onclick="doCopy('code83116')"> 代码如下:
<div class="codebody" id="code83116">
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; // 接收3个参数,string,zval,bool
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;
} // 如果define类常量,则报错
if (zend_memnstr(name,"::",sizeof("::") - 1,name + name_len)) {
zend_error(E_WARNING,"Class constants cannot be defined or redefined");
RETURN_FALSE;
} // 获取真正的值,用val保存
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;
case IS_OBJECT:
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;
}
}

注意以repeat开始的一段循环,还用到了goto语句T_T
这段代码的作用为:
对于int,float,string,bool,resource,null,则实际定义的常量时直接使用这些值
对于object,则需要将object转成上述6个类型之一(如果转型之后依然是object,则继续转型)
如何将object成6个类型之一呢?从代码上看有2种手段:
<div class="codetitle"><a style="CURSOR: pointer" data="61090" class="copybut" id="copybut61090" onclick="doCopy('code61090')"> 代码如下:
<div class="codebody" id="code61090">
if (Z_OBJ_HT_P(val)->get) {
val_free = val = Z_OBJ_HT_P(val)->get(val TSRMLS_CC);
goto repeat;
}
// toString()方法会在cast_object中被调用
else if (Z_OBJ_HT_P(val)->cast_object) {
ALLOC_INIT_ZVAL(val_free);
if (Z_OBJ_HT_P(val)->cast_object(val,IS_STRING TSRMLS_CC) == SUCCESS)
{
val = val_free;
break;
}
}

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中:
<div class="codetitle"><a style="CURSOR: pointer" data="611" class="copybut" id="copybut611" onclick="doCopy('code611')"> 代码如下:
<div class="codebody" id="code611">
ZEND_API int zend_std_cast_object_tostring(zval readobj,zval writeobj,int type TSRMLS_DC) / {{{ /
{
zval retval;
zend_class_entry
ce; switch (type) {
case IS_STRING:
ce = Z_OBJCE_P(readobj); // 如果用户的class中定义了
toString,则尝试调用
if (ce->
tostring &&
(zend_call_method_with_0_params(&readobj,ce,&ce->tostring,"tostring",&retval) || EG(exception))) {
…… }
return FAILURE;
……
}
return FAILURE;
}

从上述具体实现来看,默认的cast_object就是去寻找class中的tostring方法然后调用
回到刚开始的例子,define(‘foo',$a) ,由于$a是A的实例,并且class A中定义了
toString,因此实际上foo常量就等于toString的返回值bar。

原文链接:https://www.f2er.com/php/25507.html
definedefinedefinephp

猜你在找的PHP相关文章