您好,欢迎来到三六零分类信息网!老站,搜索引擎当天收录,欢迎发信息
免费发信息

php define常量详解

2024/3/29 14:29:44发布7次查看
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 */ // 如果大小写不敏感,则为0,敏感则为1 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。
ps:继续挖掘一点小细节.
1,define有返回值通常我们定义常量直接写成:define('foo', 123); 不过从define的实现上来看,它是有返回值的。根据手册上的描述:
成功时返回 true, 或者在失败时返回 false。
什么情况下define会失败呢?举个例子:
define('php_int_max', 1); // 返回false
define('foo', 1); // 返回true
define('foo', 2); // 返回false
复制代码
上面代码包含了两种情况,一是我们尝试重新定义php内核的预定义常量,比如php_int_max,这显然会失败。第二种情况是我们曾经在代码的某个位置定义过了一个常量foo,然后又在接下来的程序中再次定义它,这也会造成失败。因此,在编码时最好将所有需要定义的常量写在一起,以免造成name重复。
2,常量名没有限制再次回顾一下define的实现,其中仅仅判断name是否为xxx::yyy这种形式。
换句话说,define几乎对其name不做任何要求,当然也不需要name是一个合法的php变量名。因此,我们可以让define的常量取一些稀奇古怪的名称。例如:
define('>_echo >_
复制代码
不过如果定义了这样的常量,是没法直接使用的,会报语法错误。正确的使用方法如下:
define('>_echo constant('>_
复制代码
该用户其它信息

VIP推荐

免费发布信息,免费发布B2B信息网站平台 - 三六零分类信息网 沪ICP备09012988号-2
企业名录