define常量   
               添加时间:2013-6-27 点击量: 
 
              看手册说define定义的常量只容许:
仅容许标量和 null。标量的类型是 integer, float,string 或者 boolean。 也可以或许定义常量值的类型为 resource ,但并不推荐这么做,可能会导致未知状况的产生。
今天浏览php源码,发明define的第二个参数其实也可所以一个对象。
先贴一段示例:
class A {
    public function __toString() {
        return bar;
    }
}
¥a = new A();
define(foo, ¥a);
echo foo;
// 输出bar
接着来看看php中的define毕竟是如何实现的:
ZEND_FUNCTION(define)
{
    char name;
    int name_len;
    zval val;
    zval val_free = NULL;
    zend_bool non_cs = 0;
    int case_sensitive = CT_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_CTANT;
    
    // 注册常量
    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种手段:
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, val_free, 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中:
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。
原来,再大的房子,再大的床,没有相爱的人陪伴,都只是冰冷的物质。而如果身边有爱人陪伴,即使房子小,床小,也觉得无关紧要,因为这些物质上面有了爱的温度,成了家的元素。—— 何珞《婚房》#书摘#
                     
                  
     
  
 
    
    
看手册说define定义的常量只容许:
仅容许标量和 null。标量的类型是 integer, float,string 或者 boolean。 也可以或许定义常量值的类型为 resource ,但并不推荐这么做,可能会导致未知状况的产生。
今天浏览php源码,发明define的第二个参数其实也可所以一个对象。
先贴一段示例:
class A {
public function __toString() {
return bar;
}
}
¥a = new A();
define(foo, ¥a);
echo foo;
// 输出bar
接着来看看php中的define毕竟是如何实现的:
ZEND_FUNCTION(define)
{
char name;
int name_len;
zval val;
zval val_free = NULL;
zend_bool non_cs = 0;
int case_sensitive = CT_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_CTANT;
// 注册常量
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种手段:
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, val_free, 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中:
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。
原来,再大的房子,再大的床,没有相爱的人陪伴,都只是冰冷的物质。而如果身边有爱人陪伴,即使房子小,床小,也觉得无关紧要,因为这些物质上面有了爱的温度,成了家的元素。—— 何珞《婚房》#书摘#



