php define的第二个参数使用方法
php define的第二个参数使用方法
发布时间:2016-12-29 来源:查字典编辑
摘要:看手册说define定义的常量只允许:仅允许标量和null。标量的类型是integer,float,string或者boolean。也能够定...

看手册说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。

推荐文章
猜你喜欢
附近的人在看
推荐阅读
拓展阅读
相关阅读
网友关注
最新php教程学习
热门php教程学习
编程开发子分类