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 相关文章推荐
如何去掉文章里的 html 语法
Oct 09 PHP
PHP+MySQL 制作简单的留言本
Nov 02 PHP
关于PHP堆栈与列队的学习
Jun 21 PHP
zf框架db类的分页示例分享
Mar 14 PHP
PHP批量生成静态HTML的简单原理和方法
Apr 20 PHP
php Session无效分析资料整理
Nov 29 PHP
php json中文编码为null的解决办法
Dec 14 PHP
swoole和websocket简单聊天室开发
Nov 18 PHP
详解PHP中的8个魔术常量
Jul 06 PHP
PHP如何获取Cookie并实现模拟登录
Jul 16 PHP
phpstorm激活码2020附使用详细教程
Sep 25 PHP
PHP中的异常处理机制深入讲解
Nov 10 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
天使彦史上最神还原,性别曝光的那一刻,百万网友恋爱了
2020/03/02 国漫
window+nginx+php环境配置 附配置搭配说明
2010/12/29 PHP
PHP新手NOTICE错误常见解决方法
2011/12/07 PHP
探讨PHP删除文件夹的三种方法
2013/06/09 PHP
一个简单的PHP验证码实现代码
2014/05/10 PHP
php实现encode64编码类实例
2015/03/24 PHP
php 实现301重定向跳转实例代码
2016/07/18 PHP
php字符串比较函数用法小结(strcmp,strcasecmp,strnatcmp及strnatcasecmp)
2016/07/18 PHP
JS网页播放声音实现代码兼容各种浏览器
2013/09/22 Javascript
JavaScript面向对象编程入门教程
2014/04/16 Javascript
简述JavaScript的正则表达式中test()方法的使用
2015/06/16 Javascript
JS判断元素是否在数组内的实现代码
2016/03/30 Javascript
js实现百度登录框鼠标拖拽效果
2017/03/07 Javascript
用纯Node.JS弹出Windows系统消息提示框实例(MessageBox)
2017/05/17 Javascript
在 Angular 中使用Chart.js 和 ng2-charts的示例代码
2017/08/17 Javascript
webpack3+React 的配置全解
2017/08/21 Javascript
通过nodejs 服务器读取HTML文件渲染到页面的方法
2018/05/17 NodeJs
ES6的解构赋值实例详解
2019/05/06 Javascript
Vue数据双向绑定原理实例解析
2020/05/15 Javascript
python海龟绘图实例教程
2014/07/24 Python
Python常用特殊方法实例总结
2019/03/22 Python
python  文件的基本操作 菜中菜功能的实例代码
2019/07/17 Python
阿里健康大药房:阿里自营网上药店
2017/08/01 全球购物
与世界上最好的跑步专业品牌合作:Fleet Feet
2019/03/22 全球购物
Fanatics官网:运动服装、球衣、运动装备
2020/10/12 全球购物
室内设计专业学生的自我评价分享
2013/11/27 职场文书
客服专员岗位职责
2014/02/28 职场文书
授权委托书怎么写
2014/04/03 职场文书
工业设计毕业生自荐信
2014/04/13 职场文书
搞笑的获奖感言
2014/08/16 职场文书
2014领导班子专题民主生活会对照检查材料思想汇报
2014/09/23 职场文书
2016春季校长开学典礼致辞
2015/11/26 职场文书
用人单位的规章制度,怎样制定才是有效的?
2019/07/09 职场文书
深入探讨opencv图像矫正算法实战
2021/05/21 Python
解决Navicat for Mysql连接报错1251的问题(连接失败)
2021/05/27 MySQL
python热力图实现的完整实例
2022/06/25 Python