关闭 x
IT技术网
    技 采 号
    ITJS.cn - 技术改变世界
    • 实用工具
    • 菜鸟教程
    IT采购网 中国存储网 科技号 CIO智库

    IT技术网

    IT采购网
    • 首页
    • 行业资讯
    • 系统运维
      • 操作系统
        • Windows
        • Linux
        • Mac OS
      • 数据库
        • MySQL
        • Oracle
        • SQL Server
      • 网站建设
    • 人工智能
    • 半导体芯片
    • 笔记本电脑
    • 智能手机
    • 智能汽车
    • 编程语言
    IT技术网 - ITJS.CN
    首页 » PHP »PHP实现强类型函数返回值

    PHP实现强类型函数返回值

    2014-10-24 00:00:00 出处:Benjamin
    分享

    在开发过程中,函数的返回值类型应该是确定不变的,但PHP是弱类型的语言,

    所以PHP是没有此类语法验证的,正因为如此,造成了很多坑坑。

    比如下面的代码:

    < php
    function getArticles(…){
    $arrData = array();
    if($exp1){
    return $arrData;
    }else if($exp2){
    return 1;
    }else{
    return false;
    }

    }
    $arrData =getArticles(…);
    foreach($arrData as $record){
    //do something.
    ….
    }
    >

    函数getArticles根据不同的条件返回不同类型的值,有bool、int、还有数组,正常情况这类函数是希望返回数组,然后拿数组去做一些其他操作,

    可因为函数返回值类型不固定,调用时就很可能产生各种预想不到的坑,

    因此我就想,既然不能规范,那直接强制好了。

    函数/方法返回值可以强制类型,如 图

    支持四种强制类型限制:int、array、bool、object,当返回值与函数声明中的类型不匹配时,抛出warning,本来想抛出error,但是觉得

    太狠了,只能算是个异常,不能算错误,所以就用warning好了。

    PHP本身是不支持 int function 这样的语法的,所以要支持,就先要搞定语法解析器,关于语法解析器,可以移步这里>>>查看

    详情,这里就不讲了,

    先修改语法扫描 Zend/zend_language_scanner.l文件

    增加如下代码:

    <ST_IN_SCRIPTING>”int” {
    return T_FUNCTION_RETURN_INT;
    }
    <ST_IN_SCRIPTING>”bool” {
    return T_FUNCTION_RETURN_OBJECT;
    }
    <ST_IN_SCRIPTING>”object” {
    return T_FUNCTION_RETURN_OBJECT;
    }
    <ST_IN_SCRIPTING>”resource” {
    return T_FUNCTION_RETURN_RESOURCE;
    }

    意思很简单,扫描器扫描到到关键字 int、bool、object、resource、array时返回相应的T_FUNCTION_* ,这是一个token,

    scanner根据不同的token做不同的处理,token要先在Zend/zend_language_parser.y文件中定义

    增加如下代码

    ……….
    %token T_FUNCTION_RETURN_INT
    %token T_FUNCTION_RETURN_BOOL
    %token T_FUNCTION_RETURN_STRING
    %token T_FUNCTION_RETURN_OBJECT
    %token T_FUNCTION_RETURN_RESOURCE
    1

    然后增加token处理逻辑:

    1
    function:
    T_FUNCTION { $$.u.opline_num = CG(zend_lineno);$$.u.EA.var = 0; }
    | T_FUNCTION_RETURN_INT T_FUNCTION {
    $$.u.opline_num = CG(zend_lineno);
    $$.u.EA.var = IS_LONG;
    }
    | T_FUNCTION_RETURN_BOOL T_FUNCTION {
    $$.u.opline_num = CG(zend_lineno);
    $$.u.EA.var = IS_BOOL;
    }
    | T_FUNCTION_RETURN_STRING T_FUNCTION {
    $$.u.opline_num = CG(zend_lineno);
    $$.u.EA.var = IS_STRING;
    }
    | T_FUNCTION_RETURN_OBJECT T_FUNCTION {
    $$.u.opline_num = CG(zend_lineno);
    $$.u.EA.var = IS_OBJECT;
    }
    | T_FUNCTION_RETURN_RESOURCE T_FUNCTION {
    $$.u.opline_num = CG(zend_lineno);
    $$.u.EA.var = IS_RESOURCE;
    }
    | T_ARRAY T_FUNCTION {
    $$.u.opline_num = CG(zend_lineno);
    $$.u.EA.var = IS_ARRAY;
    }

    $$.u.EA.var 存储的是 函数返回类型,最后要拿他来跟返回值类型做匹配,

    这样语法解释器就可以处理我们新的php语法了。

    这还不够,还需要修改函数声明定义的处理逻辑

    Zend/zend_compile.c ::zend_do_begin_function_declaration

    ……
    zend_op_array op_array;
    char *name = function_name->u.constant.value.str.val;
    int name_len = function_name->u.constant.value.str.len;
    int function_type = function_token->u.EA.var; //保存函数类型,在语法解释器中增加的: $$.u.EA.var = IS_LONG;
    int function_begin_line = function_token->u.opline_num;
    ……
    op_array.function_name = name;
    op_array.fn_type = function_type; //将类型保存到op_array中,
    op_array.return_reference = return_reference;
    op_array.fn_flags |= fn_flags;
    op_array.pass_rest_by_reference = 0;
    ……….

    PHP是先解析PHP语法生成相应的opcode,将需要的环境、参数信息保存到execute_data全局变量中,最后在通过execute函数逐条执行opcode,

    所以要做处理就要把函数的类型保存到opcode中:op_array.fn_type = function_type;

    op_array是没有fn_type的,要修改op_array的结构,增加zend_uint fn_type;

    (关于opcode你可以想象一下 从c转为汇编,我博客中也有相关文章,可以参考一下)

    最后要修改opcode的毁掉函数,函数的返回 return 会生成token T_RETURN,T_RETURN会根据返回的类型调用不同的calback函数:

    ZEND_RETURN_SPEC_CONST_HANDLER
    ZEND_RETURN_SPEC_TMP_HANDLER
    ZEND_RETURN_SPEC_VAR_HANDLER

    它有三个callback,假如返回值是一个 const类型的数据,则 ZEND_RETURN_SPEC_CONST_HANDLER
    返回值是临时数据,如 : return 1,则ZEND_RETURN_SPEC_TMP_HANDLER
    返回值是一个变量,如 : return $a,则ZEND_RETURN_SPEC_VAR_HANDLER

    所以要在这三个callback函数中增加处理逻辑:

    在callback函数return之前增加如下代码

    if((EG(active_op_array)->fn_type > 0) && Z_TYPE_P(retval_ptr) != EG(active_op_array)->fn_type){
    php_error_docref0(NULL TSRMLS_DC,E_WARNING, “function name %s return a wrong type.”, EG(active_op_array)->function_name );
    }

    fn_type 去跟 返回值的类型作比较,假如没有匹配到,就会抛出这个warning。

    我已经打了补丁,目前只支持php5.3版本,有需要的可以拿去玩一玩。

    不清楚为什么官方不支持此语法,我觉得还是挺有必要的。

    下载补丁:php-syntax.patch

    上一篇返回首页 下一篇

    声明: 此文观点不代表本站立场;转载务必保留本文链接;版权疑问请联系我们。

    别人在看

    正版 Windows 11产品密钥怎么查找/查看?

    还有3个月,微软将停止 Windows 10 的更新

    Windows 10 终止支持后,企业为何要立即升级?

    Windows 10 将于 2025年10 月终止技术支持,建议迁移到 Windows 11

    Windows 12 发布推迟,微软正全力筹备Windows 11 25H2更新

    Linux 退出 mail的命令是什么

    Linux 提醒 No space left on device,但我的空间看起来还有不少空余呢

    hiberfil.sys文件可以删除吗?了解该文件并手把手教你删除C盘的hiberfil.sys文件

    Window 10和 Windows 11哪个好?答案是:看你自己的需求

    盗版软件成公司里的“隐形炸弹”?老板们的“法务噩梦” 有救了!

    IT头条

    公安部:我国在售汽车搭载的“智驾”系统都不具备“自动驾驶”功能

    02:03

    液冷服务器概念股走强,博汇、润泽等液冷概念股票大涨

    01:17

    亚太地区的 AI 驱动型医疗保健:2025 年及以后的下一步是什么?

    16:30

    智能手机市场风云:iPhone领跑销量榜,华为缺席引争议

    15:43

    大数据算法和“老师傅”经验叠加 智慧化收储粮食尽显“科技范”

    15:17

    技术热点

    商业智能成CIO优先关注点 技术落地方显成效(1)

    用linux安装MySQL时产生问题破解

    JAVA中关于Map的九大问题

    windows 7旗舰版无法使用远程登录如何开启telnet服务

    Android View 事件分发机制详解

    MySQL用户变量的用法

      友情链接:
    • IT采购网
    • 科技号
    • 中国存储网
    • 存储网
    • 半导体联盟
    • 医疗软件网
    • 软件中国
    • ITbrand
    • 采购中国
    • CIO智库
    • 考研题库
    • 法务网
    • AI工具网
    • 电子芯片网
    • 安全库
    • 隐私保护
    • 版权申明
    • 联系我们
    IT技术网 版权所有 © 2020-2025,京ICP备14047533号-20,Power by OK设计网

    在上方输入关键词后,回车键 开始搜索。Esc键 取消该搜索窗口。