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 相关文章推荐
一个ORACLE分页程序,挺实用的.
Oct 09 PHP
一个高ai的分页函数和一个url函数
Oct 09 PHP
用PHP实现ODBC数据分页显示一例
Oct 09 PHP
PHP读写文件的方法(生成HTML)
Nov 27 PHP
php实现的MySQL通用查询程序
Mar 11 PHP
php session 错误
May 21 PHP
PHP CURL模拟GET及POST函数代码
Apr 25 PHP
学习php设计模式 php实现访问者模式(Visitor)
Dec 07 PHP
php实现URL加密解密的方法
Nov 17 PHP
laravel 中某一字段自增、自减的例子
Oct 11 PHP
TP5框架简单登录功能实现方法示例
Oct 31 PHP
laravel框架分组控制器和分组路由实现方法示例
Jan 25 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
织梦模板标记简介
2007/03/11 PHP
浅谈使用 PHP 进行手机 APP 开发(API 接口开发)
2014/08/11 PHP
yii2使用gridView实现下拉列表筛选数据
2017/04/10 PHP
PHP+MySQL高并发加锁事务处理问题解决方法
2018/04/30 PHP
在Laravel 的 Blade 模版中实现定义变量
2019/10/14 PHP
javascript实现仿银行密码输入框效果的代码
2007/12/13 Javascript
JS限制Textarea文本域字符个数的具体实现
2013/08/02 Javascript
javascript操作table(insertRow,deleteRow,insertCell,deleteCell方法详解)
2013/12/16 Javascript
jQuery中parents()方法用法实例
2015/01/07 Javascript
JavaScript实现自动变换表格边框颜色
2015/05/08 Javascript
nodejs实现获取某宝商品分类
2015/05/28 NodeJs
jquery判断当前浏览器的实现代码
2015/11/07 Javascript
js表单元素checked、radio被选中的几种方法(详解)
2016/08/22 Javascript
JS获取html元素的标记名实现方法
2016/10/08 Javascript
Vue单页式应用(Hash模式下)实现微信分享的实例
2017/07/21 Javascript
详解element-ui日期时间选择器的日期格式化问题
2019/04/08 Javascript
JavaScript实现下拉列表
2021/01/20 Javascript
python进阶教程之函数对象(函数也是对象)
2014/08/30 Python
利用ctypes提高Python的执行速度
2016/09/09 Python
Python+tkinter使用80行代码实现一个计算器实例
2018/01/16 Python
python开启摄像头以及深度学习实现目标检测方法
2018/08/03 Python
pandas重新生成索引的方法
2018/11/06 Python
Python3 SSH远程连接服务器的方法示例
2018/12/29 Python
python实现广度优先搜索过程解析
2019/10/19 Python
解决pycharm中opencv-python导入cv2后无法自动补全的问题(不用作任何文件上的修改)
2020/03/05 Python
Python3+SQLAlchemy+Sqlite3实现ORM教程
2021/02/16 Python
python和opencv构建运动检测器的实现
2021/03/03 Python
康帕斯酒店预订:Compass Hospitality(支持中文)
2018/08/23 全球购物
给水排水工程专业毕业生推荐信
2013/10/28 职场文书
运动会邀请函范文
2014/01/31 职场文书
共产党员公开承诺书
2014/03/25 职场文书
新学期国旗下演讲稿
2014/05/08 职场文书
“四风”问题整改措施和努力方向
2014/09/20 职场文书
2014年远程教育工作总结
2014/12/09 职场文书
材料员岗位职责
2015/02/10 职场文书
JavaScript前端面试扁平数据转tree与tree数据扁平化
2022/06/14 Javascript