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 相关文章推荐
dedecms系统常用术语汇总
Apr 03 PHP
PHP 配置open_basedir 让各虚拟站点独立运行
Nov 12 PHP
PHP fopen()和 file_get_contents()应用与差异介绍
Mar 19 PHP
php操作MongoDB基础教程(连接、新增、修改、删除、查询)
Mar 25 PHP
PHP OPP机制和模式简介(抽象类、接口和契约式编程)
Jun 09 PHP
php中字符串和正则表达式详解
Oct 23 PHP
Windows下安装PHP单元测试环境PHPUnit图文教程
Oct 24 PHP
php使用mysqli向数据库添加数据的方法
Mar 20 PHP
PHP统一页面编码避免乱码问题
Apr 09 PHP
PHP 常用的header头部定义汇总
Jun 19 PHP
CI框架网页缓存简单用法分析
Dec 26 PHP
PHP的PDO预处理语句与存储过程
Jan 27 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
老机欣赏|中国60年代精品收音机
2021/03/02 无线电
mysql 中InnoDB和MyISAM的区别分析小结
2008/04/15 PHP
PHP连接SQLServer2005方法及代码
2013/12/26 PHP
让CodeIgniter数据库缓存自动过期的处理的方法
2014/06/12 PHP
PHP中使用数组指针函数操作数组示例
2014/11/19 PHP
PHP 实现从数据库导出到.csv文件方法
2017/07/06 PHP
ubutu 16.04环境下,PHP与mysql数据库,网页登录验证实例讲解
2017/07/20 PHP
IE的fireEvent方法概述及应用
2013/02/22 Javascript
使用Node.js配合Nginx实现高负载网络
2015/06/28 Javascript
jquery实现可自动判断位置的弹出层效果代码
2015/10/12 Javascript
JS实现浏览器状态栏显示时间的方法
2015/10/27 Javascript
Bootstrap每天必学之面板
2015/11/30 Javascript
javascript实现label标签跳出循环操作
2016/03/06 Javascript
Javascript的表单验证长度
2016/03/16 Javascript
javascript中使用未定义变量或值的情况分析
2016/07/19 Javascript
解析javascript图片懒加载与预加载的分析总结
2016/10/27 Javascript
利用Js+Css实现折纸动态导航效果实例源码
2017/01/25 Javascript
详解vue2.0 transition 多个元素嵌套使用过渡
2017/06/19 Javascript
详解Vue学习笔记入门篇之组件的内容分发(slot)
2017/07/17 Javascript
node thread.sleep实现示例
2018/06/20 Javascript
微信小程序中换行空格(多个空格)写法详解
2018/07/10 Javascript
vue 如何使用递归组件
2020/10/23 Javascript
[01:11:10]2014 DOTA2华西杯精英邀请赛 5 24 iG VS VG加赛
2014/05/26 DOTA
Python正则表达式介绍
2012/08/06 Python
Python常用内置函数总结
2015/02/08 Python
用Python编写web API的教程
2015/04/30 Python
python+selenium实现登录账户后自动点击的示例
2017/12/22 Python
将Python字符串生成PDF的实例代码详解
2019/05/17 Python
python输入多行字符串的方法总结
2019/07/02 Python
10张动图学会python循环与递归问题
2021/02/06 Python
逃课检讨书
2015/01/26 职场文书
以权谋私检举信范文
2015/03/02 职场文书
党员证明信
2015/06/19 职场文书
新闻稿件写作技巧
2015/07/18 职场文书
详解Apache SkyWalking 告警配置指南
2021/04/22 Servers
Windows server 2012搭建FTP服务器
2022/04/29 Servers