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&MYSQL分页原理及实现
Jan 02 PHP
.htaccess文件保护实例讲解
Feb 06 PHP
比较时间段一与时间段二是否有交集的php函数
May 31 PHP
PHP中计算字符串相似度的函数代码
Dec 29 PHP
基于PHP CURL用法的深入分析
Jun 09 PHP
163的邮件用phpmailer发送(实例详解)
Jun 24 PHP
php获取网页请求状态程序示例
Jun 17 PHP
从零开始学YII2框架(一)通过Composer安装Yii2框架
Aug 20 PHP
php创建桌面快捷方式实现方法
Dec 31 PHP
php判断用户是否关注微信公众号
Jul 22 PHP
PHP实现的登录页面信息提示功能示例
Jul 24 PHP
php-7.3.6 编译安装过程
Feb 11 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连接Oracle for NT 远程数据库
2006/10/09 PHP
php中计算时间差的几种方法
2009/12/31 PHP
PHP生成不重复标识符的方法
2014/11/21 PHP
浅析ThinkPHP缓存之快速缓存(F方法)和动态缓存(S方法)(日常整理)
2015/10/26 PHP
php通过淘宝API查询IP地址归属等信息
2015/12/25 PHP
使用Jquery Aajx访问WCF服务(GET、POST、PUT、DELETE)
2012/03/16 Javascript
javascript针对DOM的应用分析(二)
2012/04/15 Javascript
jQuery操作Select选择的Text和Value(获取/设置/添加/删除)
2013/03/06 Javascript
javascript使用百度地图api和html5特性获取浏览器位置
2014/01/10 Javascript
你未必知道的JavaScript和CSS交互的5种方法
2014/04/02 Javascript
JavaScript类型系统之基本数据类型与包装类型
2016/01/06 Javascript
原生JavaScript实现滚动条效果
2020/03/24 Javascript
JavaScript模版引擎的基本实现方法浅析
2016/02/15 Javascript
JS onkeypress兼容性写法详解
2016/04/27 Javascript
ES6学习教程之块级作用域详解
2017/10/09 Javascript
浅谈VUE-CLI脚手架热更新太慢的原因和解决方法
2018/09/28 Javascript
JavaScript高阶教程之“==”隐藏下的类型转换
2019/04/11 Javascript
vue实现多条件和模糊搜索功能
2019/05/28 Javascript
使用Angular9和TypeScript开发RPG游戏的方法
2020/03/25 Javascript
[05:56]第十六期——新进3大C之小兔基
2014/06/24 DOTA
[52:52]DOTA2上海特级锦标赛C组资格赛#1 OG VS LGD第三局
2016/02/27 DOTA
Python 字典(Dictionary)操作详解
2014/03/11 Python
python3设计模式之简单工厂模式
2017/10/17 Python
python+pillow绘制矩阵盖尔圆简单实例
2018/01/16 Python
TensorFlow入门使用 tf.train.Saver()保存模型
2018/04/24 Python
python调用并链接MATLAB脚本详解
2019/07/05 Python
使用PyQt的QLabel组件实现选定目标框功能的方法示例
2020/05/19 Python
python获取百度热榜链接的实例方法
2020/08/25 Python
降低python版本的操作方法
2020/09/11 Python
Html5页面点击遮罩层背景关闭遮罩层
2020/11/30 HTML / CSS
英国亚马逊官方网站:Amazon.co.uk
2019/08/09 全球购物
EJB的几种类型
2012/08/15 面试题
室内设计专业毕业生求职信
2014/05/02 职场文书
幼儿园教师演讲稿
2014/05/06 职场文书
节约粮食标语
2014/06/18 职场文书
单位作风建设剖析材料
2014/10/11 职场文书