php define的第二个参数使用方法
程序员文章站
2022-06-11 15:49:40
看手册说define定义的常量只允许:仅允许标量和 null。标量的类型是 integer, float,string 或者 boolean。 也能够定义常量值的类型为 r...
看手册说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 = 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种手段:
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。
仅允许标量和 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 = 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种手段:
复制代码 代码如下:
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。
上一篇: 产品推广策划的“四要素”
下一篇: ts实现忽略大小写的字符串排序的两种方法