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


Posted in PHP onNovember 04, 2013

看手册说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 相关文章推荐
使用zend studio for eclipse不能激活代码提示功能的解决办法
Oct 11 PHP
PHP 调试工具Debug Tools
Apr 30 PHP
php中批量修改文件后缀名的函数代码
Oct 23 PHP
smarty巧妙处理iframe中内容页的代码
Mar 07 PHP
PHP数组无限分级数据的层级化处理代码
Dec 29 PHP
PHP操作Memcache实例介绍
Jun 14 PHP
PHP中func_get_args(),func_get_arg(),func_num_args()的区别
Sep 30 PHP
ini_set的用法介绍
Jan 07 PHP
php获取给定日期相差天数的方法分析
Feb 20 PHP
php实现购物车功能(以大苹果购物网为例)
Mar 09 PHP
PHP微信PC二维码登陆的实现思路
Jul 13 PHP
PHP文件后缀不强制为.php方法
Mar 31 PHP
Linux编译升级php的详细方法
Nov 04 #PHP
php获取操作系统语言代码
Nov 04 #PHP
Php header()函数语法及使用代码
Nov 04 #PHP
php配置php-fpm启动参数及配置详解
Nov 04 #PHP
mac下安装nginx和php
Nov 04 #PHP
php使用curl模拟登录后采集页面的例子
Nov 04 #PHP
在PHP上显示JFreechart画的统计图方法
Nov 03 #PHP
You might like
解决PHP在DOS命令行下却无法链接MySQL的技术笔记
2010/12/29 PHP
PHP生成不重复标识符的方法
2014/11/21 PHP
PHP后期静态绑定之self::限制实例分析
2018/12/21 PHP
php输出文字乱码的解决方法
2019/10/04 PHP
Javascript 更新 JavaScript 数组的 uniq 方法
2008/01/23 Javascript
JQuery Ajax 跨域访问的解决方案
2010/03/12 Javascript
javascript的数据类型、字面量、变量介绍
2012/05/23 Javascript
几种延迟加载JS代码的方法加快网页的访问速度
2013/10/12 Javascript
jquery插件tooltipv顶部淡入淡出效果使用示例
2013/12/05 Javascript
原生js和jQuery写的网页选项卡特效对比
2015/04/27 Javascript
JS基于Mootools实现的个性菜单效果代码
2015/10/21 Javascript
JavaScript实现搜索框的自动完成功能(一)
2016/02/25 Javascript
实例讲解javascript实现异步图片上传方法
2017/12/05 Javascript
如何手动实现es5中的bind方法详解
2018/12/07 Javascript
基于jquery实现的tab选项卡功能示例【附源码下载】
2019/06/10 jQuery
微信小程序实现批量倒计时功能
2020/11/01 Javascript
vue中如何实现后台管理系统的权限控制的方法步骤
2019/09/05 Javascript
在Python的Flask框架中实现全文搜索功能
2015/04/20 Python
使用Python程序抓取新浪在国内的所有IP的教程
2015/05/04 Python
Python使用gensim计算文档相似性
2016/04/10 Python
python下载图片实现方法(超简单)
2017/07/21 Python
利用python求相邻数的方法示例
2017/08/18 Python
Python网络编程之TCP套接字简单用法示例
2018/04/09 Python
Python 保持登录状态进行接口测试的方法示例
2019/08/06 Python
python实现两个字典合并,两个list合并
2019/12/02 Python
python如何通过twisted搭建socket服务
2020/02/03 Python
django 读取图片到页面实例
2020/03/27 Python
jupyter notebook 使用过程中python莫名崩溃的原因及解决方式
2020/04/10 Python
django queryset 去重 .distinct()说明
2020/05/19 Python
python 字符串的驻留机制及优缺点
2020/06/19 Python
快速创建python 虚拟环境
2020/11/28 Python
文化与传播毕业生求职信
2014/03/09 职场文书
离婚协议书范本及离婚须知
2014/10/15 职场文书
2015年小学数学教师个人工作总结
2015/05/25 职场文书
学校2016年圣诞节活动总结
2016/03/31 职场文书
Python各协议下socket黏包问题原理
2022/04/12 Python