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 相关文章推荐
如何在PHP中使用Oracle数据库(5)
Oct 09 PHP
在线增减.htpasswd内的用户
Oct 09 PHP
一个简易需要注册的留言版程序
Oct 09 PHP
判断php数组是否为索引数组的实现方法
Jun 13 PHP
浅析php中常量,变量的作用域和生存周期
Aug 10 PHP
destoon整合ucenter后注册页面不跳转的解决方法
Jun 21 PHP
PHP版单点登陆实现方案的实例
Nov 17 PHP
PHP全功能无变形图片裁剪操作类与用法示例
Jan 10 PHP
php使用PDO事务配合表格读取大量数据插入操作实现方法
Feb 16 PHP
php使用pecl方式安装扩展操作示例
Aug 12 PHP
php中错误处理操作实例分析
Aug 23 PHP
记Laravel调用Gin接口调用formData上传文件的实现方法
Dec 12 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+mysql事务rollback&commit示例
2010/02/08 PHP
php 生成加密公钥加密私钥实例详解
2017/06/16 PHP
JavaScript 解析读取XML文档 实例代码
2009/07/07 Javascript
jqgrid 表格数据导出实例
2013/11/21 Javascript
AngularJS + Node.js + MongoDB开发的基于高德地图位置的通讯录
2015/01/02 Javascript
javascript中tostring()和valueof()的用法及两者的区别
2015/11/16 Javascript
jQuery实现宽屏图片轮播实例教程
2015/11/24 Javascript
关于JavaScript 原型链的一点个人理解
2016/07/31 Javascript
javascript函数中的3个高级技巧
2016/09/22 Javascript
Vue + Webpack + Vue-loader学习教程之功能介绍篇
2017/03/14 Javascript
vue.js使用3DES加密的方法示例
2018/05/18 Javascript
jquery实现搜索框功能实例详解
2018/07/23 jQuery
浅谈在vue中使用mint-ui swipe遇到的问题
2018/09/27 Javascript
浅谈js中的bind
2019/03/18 Javascript
JQuery属性操作与循环用法示例
2019/05/15 jQuery
细说webpack6 Babel的使用详解
2019/09/26 Javascript
Vue——前端生成二维码的示例
2020/12/19 Vue.js
python时间整形转标准格式的示例分享
2014/02/14 Python
1分钟快速生成用于网页内容提取的xslt
2018/02/23 Python
python 爬虫 批量获取代理ip的实例代码
2018/05/22 Python
python实现维吉尼亚加密法
2019/03/20 Python
pandas实现将日期转换成timestamp
2019/12/07 Python
numpy中生成随机数的几种常用函数(小结)
2020/08/18 Python
把Anaconda中的环境导入到Pycharm里面的方法步骤
2020/10/30 Python
手把手教你实现一个canvas智绘画板的方法
2019/03/04 HTML / CSS
英国领先的杂志订阅网站:Magazine.co.uk
2018/01/25 全球购物
高中生学习的自我评价
2013/12/14 职场文书
高中生的自我鉴定范文
2014/01/24 职场文书
环境工程专业自荐信范文
2014/03/18 职场文书
yy司仪主持词
2014/03/22 职场文书
股份合作协议书
2014/09/10 职场文书
2014年英语教研组工作总结
2014/12/06 职场文书
网聊搭讪开场白
2015/05/28 职场文书
百年校庆宣传标语口号
2015/12/26 职场文书
JavaScript最完整的深浅拷贝实现方式详解
2022/02/28 Javascript
vue如何使用模拟的json数据查看效果
2022/03/31 Vue.js