0

0

类型提示的实现

php中文网

php中文网

发布时间:2016-06-21 08:47:06

|

1258人浏览过

|

来源于php中文网

原创

PHP是弱类型语言,向方法传递参数时候也并不严格检查数据类型。 不过有时需要判断传递到方法中的参数,为此PHP中提供了一些函数,来判断数据的类型。 比如is_numeric(),判断是否是一个数值或者可转换为数值的字符串,比如用于判断对象的类型运算符:instanceof。 instanceof 用来测定一个给定的对象是否来自指定的对象类。instanceof 运算符是 PHP 5 引进的。 在此之前是使用的is_a(),不过现在已经不推荐使用。

为了避免对象类型不规范引起的问题,php5中引入了类型提示这个概念。在定义方法参数时,同时定义参数的对象类型。 如果在调用的时候,传入参数的类型与定义的参数类型不符,则会报错。这样就可以过滤对象的类型,或者说保证了数据的安全性。

PHP中的类型提示功能只能用于参数为对象的提示,而无法用于为整数,字串,浮点等类型提示。在PHP5.1之后,PHP支持对数组的类型提示。

要使用类型提示,只要在方法(或函数)的对象型参数前加一个已存在的类的名称,当使用类型提示时, 你不仅可以指定对象类型,还可以指定抽象类和接口。

一个数组的类型提示示例:

 

function array_print(Array $arr) {

print_r($arr);}

array_print(1);

以上的这段代码有一点问题,它触发了我们这次所介绍的类型提示,这段代码在PHP5.1之后的版本执行,会报错如下:

 

Catchable fatal error: Argument 1 passed to array_print() must be an array, 

integer given, called in  ...

当我们把函数参数中的整形变量变为数组时,程序会正常运行,调用print_r函数输出数组。 那么这个类型提示是如何实现的呢? 不管是在类中的方法,还是我们调用的函数,都是使用function关键字作为其声明的标记, 而类型提示的实现是与函数的声明相关的,在声明时就已经确定了参数的类型是哪些,但是需要在调用时才会显示出来。 这里,我们从两个方面说明类型提示的实现:

 

参数声明时的类型提示

函数或方法调用时的类型提示

将刚才的那个例子修改一下:

 

    function array_print(Array $arr = 1) {

    print_r($arr);}

    array_print(array(1));

这段代码与前面的那个示例相比,函数的参数设置了一个默认值,但是这个默认值是一个整形变量, 它与参数给定的类型提示Array不一样,因此,当我们运行这段代码时会很快看到程序会报错如下:

 

Fatal error: Default value for parameters with array type hint 

can only be an array or NULL

为什么为很快看到报错呢? 因为默认值的检测过程发生在中间代码生成阶段,与运行时的报错不同,它还没有生成中间代码,也没有执行中间代码的过程。 在Zend/zend_language_parser.y文件中,我们找到函数的参数列表在编译时都会调用zend_do_receive_arg函数。 而在这个函数的参数列表中,第5个参数( znode *class_type)与我们这节所要表述的类型提示密切相关。 这个参数的作用是声明类型提示中的类型,这里的类型有三种:

 

空,即没有类型提示

类名,用户定义或PHP自定义的类、接口等

数组,编译期间对应的token是T_ARRAY,即Array字符串

在zend_do_receive_arg函数中,针对class_type参数做了一系列的操作,基本上是针对上面列出的三种类型, 其中对于类名,程序并没有判断这个类是否存在,即使你使用了一个不存在的类名, 程序在报错时,显示的也会是实参所给的对象并不是给定类的实例。

以上是声明类型提示的过程以及在声明过程中对参数默认值的判断过程,下面我们看下在函数或方法调用时类型提示的实现。

从上面的声明过程我们知道PHP在编译类型提示的相关代码时调用的是Zend/zend_complie.c文件中的zend_do_receive_arg函数, 在这个函数中将类型提示的判断的opcode被赋值为ZEND_RECV。根据opcode的映射计算规则得出其在执行时调用的是ZEND_RECV_SPEC_HANDLER。 其代码如下:

 

    static int ZEND_FASTCALL  ZEND_RECV_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS){

        ...//省略

        if (param == NULL) {

                char *space;

                char *class_name = get_active_class_name(&space TSRMLS_CC);

                zend_execute_data *ptr = EX(prev_execute_data);

 

            if (zend_verify_arg_type((zend_function *) EG(active_op_array), arg_num, NULL, opline->extended_value TSRMLS_CC)) {

                   ...//省略

            }

               ...//省略

            } else {

              ...//省略

                zend_verify_arg_type((zend_function *) EG(active_op_array), arg_num, *param, opline->extended_value TSRMLS_CC);

              ...//省略

        }

  ...//省略}

如上所示:在ZEND_RECV_SPEC_HANDLER中最后调用的是zend_verify_arg_type。其代码如下:

 

    static inline int zend_verify_arg_type(zend_function *zf, zend_uint arg_num, zval *arg, ulong fetch_type TSRMLS_DC){

       ...//省略

 

if (cur_arg_info->class_name) {

    const char *class_name;

 

    if (!arg) {

        need_msg = zend_verify_arg_class_kind(cur_arg_info, fetch_type, &class_name, &ce TSRMLS_CC);

        return zend_verify_arg_error(zf, arg_num, cur_arg_info, need_msg, class_name, "none", "" TSRMLS_CC);

Sora
Sora

Sora是OpenAI发布的一种文生视频AI大模型,可以根据文本指令创建现实和富有想象力的场景。

下载

    }

    if (Z_TYPE_P(arg) == IS_OBJECT) { // 既然是类对象参数, 传递的参数需要是对象类型

        // 下面检查这个对象是否是参数提示类的实例对象, 这里是允许传递子类实例对象

        need_msg = zend_verify_arg_class_kind(cur_arg_info, fetch_type, &class_name, &ce TSRMLS_CC);

        if (!ce !instanceof_function(Z_OBJCE_P(arg), ce TSRMLS_CC)) {

            return zend_verify_arg_error(zf, arg_num, cur_arg_info, need_msg, class_name, "instance of ", Z_OBJCE_P(arg)->name TSRMLS_CC);

        }

    } else if (Z_TYPE_P(arg) != IS_NULL !cur_arg_info->allow_null) { // 参数为NULL, 也是可以通过检查的,

                                                                        // 如果函数定义了参数默认值, 不传递参数调用也是可以通过检查的

        need_msg = zend_verify_arg_class_kind(cur_arg_info, fetch_type, &class_name, &ce TSRMLS_CC);

        return zend_verify_arg_error(zf, arg_num, cur_arg_info, need_msg, class_name, zend_zval_type_name(arg), "" TSRMLS_CC);

    }

    } else if (cur_arg_info->array_type_hint) { //  数组

        if (!arg) {

            return zend_verify_arg_error(zf, arg_num, cur_arg_info, "be an array", "", "none", "" TSRMLS_CC);

        }

        if (Z_TYPE_P(arg) != IS_ARRAY && (Z_TYPE_P(arg) != IS_NULL !cur_arg_info->allow_null)) {

            return zend_verify_arg_error(zf, arg_num, cur_arg_info, "be an array", "", zend_zval_type_name(arg), "" TSRMLS_CC);

        }

    }

    return 1;}

zend_verify_arg_type的整个流程如图3.1所示:

 

图3.1 类型提示判断流程图

 

 

如果类型提示报错,zend_verify_arg_type函数最后都会调用 zend_verify_arg_class_kind 生成报错信息, 并且调用 zend_verify_arg_error 报错。如下所示代码:

 

static inline char * zend_verify_arg_class_kind(const zend_arg_info *cur_arg_info, ulong fetch_type, const char **class_name, zend_class_entry **pce TSRMLS_DC){

    *pce = zend_fetch_class(cur_arg_info->class_name, cur_arg_info->class_name_len, (fetch_type ZEND_FETCH_CLASS_AUTO ZEND_FETCH_CLASS_NO_AUTOLOAD) TSRMLS_CC);

 

*class_name = (*pce) ? (*pce)->name: cur_arg_info->class_name;

if (*pce && (*pce)->ce_flags & ZEND_ACC_INTERFACE) {

    return "implement interface ";

} else {

    return "be an instance of ";

}}

 

 

static inline int zend_verify_arg_error(const zend_function *zf, zend_uint arg_num, const zend_arg_info *cur_arg_info, const char *need_msg, const char *need_kind, const char *given_msg, char *given_kind TSRMLS_DC){

    zend_execute_data *ptr = EG(current_execute_data)->prev_execute_data;

    char *fname = zf->common.function_name;

    char *fsep;

    char *fclass;

 

if (zf->common.scope) {

    fsep =  "::";

    fclass = zf->common.scope->name;

} else {

    fsep =  "";

    fclass = "";

}

 

if (ptr && ptr->op_array) {

    zend_error(E_RECOVERABLE_ERROR, "Argument %d passed to %s%s%s() must %s%s, %s%s given, called in %s on line %d and defined", arg_num, fclass, fsep, fname, need_msg, need_kind, given_msg, given_kind, ptr->op_array->filename, ptr->opline->lineno);

} else {

    zend_error(E_RECOVERABLE_ERROR, "Argument %d passed to %s%s%s() must %s%s, %s%s given", arg_num, fclass, fsep, fname, need_msg, need_kind, given_msg, given_kind);

}

return 0;}

在上面的代码中,我们可以找到前面的报错信息中的一些关键字Argument、 passed to、called in等。 这就是我们在调用函数或方法时类型提示显示错误信息的最终执行位置。



本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
2026春节习俗大全
2026春节习俗大全

本专题整合了2026春节习俗大全,阅读专题下面的文章了解更多详细内容。

68

2026.02.11

Yandex网页版官方入口使用指南_国际版与俄罗斯版访问方法解析
Yandex网页版官方入口使用指南_国际版与俄罗斯版访问方法解析

本专题全面整理了Yandex搜索引擎的官方入口信息,涵盖国际版与俄罗斯版官网访问方式、网页版直达入口及免登录使用说明,帮助用户快速、安全地进入Yandex官网,高效使用其搜索与相关服务。

200

2026.02.11

虫虫漫画网页版入口与免费阅读指南_正版漫画全集在线查看方法
虫虫漫画网页版入口与免费阅读指南_正版漫画全集在线查看方法

本专题系统整理了虫虫漫画官网及网页版最新入口,涵盖免登录观看、正版漫画全集在线阅读方式,并汇总稳定可用的访问渠道,帮助用户快速找到虫虫漫画官方页面,轻松在线阅读各类热门漫画内容。

40

2026.02.11

Docker容器化部署与DevOps实践
Docker容器化部署与DevOps实践

本专题面向后端与运维开发者,系统讲解 Docker 容器化技术在实际项目中的应用。内容涵盖 Docker 镜像构建、容器运行机制、Docker Compose 多服务编排,以及在 DevOps 流程中的持续集成与持续部署实践。通过真实场景演示,帮助开发者实现应用的快速部署、环境一致性与运维自动化。

4

2026.02.11

Rust异步编程与Tokio运行时实战
Rust异步编程与Tokio运行时实战

本专题聚焦 Rust 语言的异步编程模型,深入讲解 async/await 机制与 Tokio 运行时的核心原理。内容包括异步任务调度、Future 执行模型、并发安全、网络 IO 编程以及高并发场景下的性能优化。通过实战示例,帮助开发者使用 Rust 构建高性能、低延迟的后端服务与网络应用。

1

2026.02.11

Spring Boot企业级开发与MyBatis Plus实战
Spring Boot企业级开发与MyBatis Plus实战

本专题面向 Java 后端开发者,系统讲解如何基于 Spring Boot 与 MyBatis Plus 构建高效、规范的企业级应用。内容涵盖项目架构设计、数据访问层封装、通用 CRUD 实现、分页与条件查询、代码生成器以及常见性能优化方案。通过完整实战案例,帮助开发者提升后端开发效率,减少重复代码,快速交付稳定可维护的业务系统。

6

2026.02.11

包子漫画网页版入口与全集阅读指南_正版免费漫画快速访问方法
包子漫画网页版入口与全集阅读指南_正版免费漫画快速访问方法

本专题汇总了包子漫画官网和网页版入口,提供最新章节抢先看方法、正版免费阅读指南,以及稳定访问方式,帮助用户快速直达包子漫画页面,无广告畅享全集漫画内容。

159

2026.02.10

MC.JS网页版快速畅玩指南_MC.JS官网在线入口及免安装体验方法
MC.JS网页版快速畅玩指南_MC.JS官网在线入口及免安装体验方法

本专题汇总了MC.JS官网入口和网页版快速畅玩方法,提供免安装访问、不同版本(1.8.8、1.12.8)在线体验指南,以及正版网页端操作说明,帮助玩家轻松进入MC.JS世界,实现即时畅玩与高效体验。

89

2026.02.10

谷歌邮箱网页版登录与注册全指南_Gmail账号快速访问与安全操作教程
谷歌邮箱网页版登录与注册全指南_Gmail账号快速访问与安全操作教程

本专题汇总了谷歌邮箱网页版的最新登录入口和注册方法,详细提供官方账号快速访问方式、网页版操作教程及安全登录技巧,帮助用户轻松管理Gmail邮箱账户,实现高效、安全的邮箱使用体验。

78

2026.02.10

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
zend框架2视频教程
zend框架2视频教程

共79课时 | 14.1万人学习

Excel 教程
Excel 教程

共162课时 | 16.8万人学习

Pandas 教程
Pandas 教程

共15课时 | 1.1万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号