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之第七天
Oct 09 PHP
PHP文件读写操作之文件写入代码
Jan 13 PHP
解析crontab php自动运行的方法
Jun 24 PHP
linux使用crontab实现PHP执行计划定时任务
May 10 PHP
ThinkPHP让分页保持搜索状态的方法
Jul 02 PHP
ThinkPHP独立分组使用的注意事项
Nov 25 PHP
thinkphp中memcache的用法实例
Nov 29 PHP
php正则匹配html中带class的div并选取其中内容的方法
Jan 13 PHP
PHP的swoole扩展安装方法详细教程
May 18 PHP
PHP实现查询手机归属地的方法详解
Apr 28 PHP
laravel自定义分页效果
Jul 23 PHP
PHP实现生成模糊图片的方法示例
Dec 21 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文件向另一个地址post数据,不用表单和隐藏的变量的
2007/03/06 PHP
php二维数组排序方法(array_multisort usort)
2013/12/25 PHP
PHP5.5和之前的版本empty函数的不同之处
2014/06/13 PHP
从零开始学YII2框架(五)快速生成代码工具 Gii 的使用
2014/08/20 PHP
php实现两个数组相加的方法
2015/02/17 PHP
PHP实现原比例生成缩略图的方法
2016/02/03 PHP
Yii2-GridView 中让关联字段带搜索和排序功能示例
2017/01/21 PHP
详解PHP5.6.30与Apache2.4.x配置
2017/06/02 PHP
javascript编程起步(第五课)
2007/02/27 Javascript
Lua表达式和控制结构学习笔记
2014/12/15 Javascript
JavaScript的9种继承实现方式归纳
2015/05/18 Javascript
基于javascript实现随机颜色变化效果
2016/01/14 Javascript
AngularJS 单元测试(二)详解
2016/09/21 Javascript
为什么说JavaScript预解释是一种毫无节操的机制详析
2018/11/18 Javascript
仿iPhone通讯录制作小程序自定义选择组件的实现
2019/05/23 Javascript
详解Vue template 如何支持多个根结点
2020/02/10 Javascript
封装一下vue中的axios示例代码详解
2020/02/16 Javascript
Vue-cli 移动端布局和动画使用详解
2020/08/10 Javascript
分析Python中设计模式之Decorator装饰器模式的要点
2016/03/02 Python
Python数据类型详解(三)元祖:tuple
2016/05/08 Python
TF-IDF与余弦相似性的应用(二) 找出相似文章
2017/12/21 Python
OpenCV图像颜色反转算法详解
2019/05/13 Python
python实现复制文件到指定目录
2019/10/16 Python
Python 实现自动完成A4标签排版打印功能
2020/04/09 Python
Python 为什么推荐蛇形命名法原因浅析
2020/06/18 Python
在CentOS7下安装Python3教程解析
2020/07/09 Python
Python使用Selenium模拟浏览器自动操作功能
2020/09/08 Python
matplotlib设置颜色、标记、线条,让你的图像更加丰富(推荐)
2020/09/25 Python
达拉斯牛仔官方商店:Dallas Cowboys Pro Shop
2018/02/10 全球购物
Solaris操作系统的线程机制
2012/12/23 面试题
项目副经理岗位职责
2013/12/30 职场文书
高二历史教学反思
2014/01/25 职场文书
监察局领导班子四风问题整改措施思想汇报
2014/10/05 职场文书
个人先进事迹材料
2014/12/29 职场文书
放假通知范文
2015/04/14 职场文书
《藏戏》教学反思
2016/02/23 职场文书