PHP7扩展开发教程[12] – 如何抛出错误和异常?

本篇博客属于php7扩展系列教程,上一篇地址《PHP7扩展开发教程[11] – 如何引入php文件?》。

本章将演示如何在扩展中抛出错误信息error,或者异常exception。

正式开始

本章代码:https://github.com/owenliang/php7-extension-explore/tree/master/course12-how-to-throw-error-and-exception

我新增了一个全局函数用于测试error:zif_myext_test_error,它的实现如下:

调用test_error()方法,可以看到PHP抛出的错误信息:

错误输出用到了系列的php_error_docref函数,它们在php源码的main/php.h中声明,在main/main.c中定义:

docref参数用于指向文档(比如php.net上某个函数的说明),一般我们不会用。

type是错误级别,这个大家都很熟悉,其枚举定义在Zend/zend_errors.h:

其中ERROR相关级别是FATAL错误,会导致PHP脚本停止向下执行。所以,一般我们抛WANRING或者NOTICE就可以了。

如果你仔细观察输出的错误信息,应该可以发现param1和param2其实最终会用来格式化错误输出信息,作为函数的参数展现。

那么,php_error_docref系列函数的实现大概是什么样子呢?

可见,它间接通过php_verror实现,这个函数比较长,其主要功能是获取当前执行上下文的函数名、类名、代码行、文件名等信息,经过一系列处理最终格式化为一个错误字符串message,然后调用了php_error进一步处理:

那么php_error其实是zend_error的一个宏名字,zend_error函数除了帮我们输出message外,还会检查PHP里是否设置过错误处理函数(set_error_handler),如果设置了就会回调,这里就不贴代码了。

可见,php_error_docref系列函数能够生成更多上下文信息,而直接调用zend_error并不会帮我们生成这些信息,因此作为一个扩展来说,一般都是使用php_error_docref而不是直接使用php_error/zend_error。


接下来是异常,在函数zif_myext_test_exception实现:

在PHP代码里,我这样进行了测试:

当我传参数0时进入第2个分支,zend_throw_exception函数定义如下:

第1个参数是异常类的class句柄,如果传NULL则表示使用默认的Exception类作为异常类型,message和code和PHP里创建Exception类一样,分别是异常提示信息和异常错误码。

这个函数会根据exception_ce创建一个新的异常对象,设置其message和code,最后帮我们抛出去:

可见,如果没有传递exception_ce就会使用默认的一个zend_ce_exception作为异常类句柄,如果传递了则会检查我们的类是否继承自异常类接口。接着,它就会创建一个object类对象,设置对象的属性,最后抛出它。

我们不需要释放这个函数的返回值,因为最终这个object是会返回到PHP层,交由用户层捕获。

在这里,我们会看到PHP里捕获的异常对象如下:

接下来test_exception传1进入第1个分支,我们自定义一个新的异常类叫做MyException,令它继承自Exception类,全局变量zend_ce_exception已经被Zend初始化为Exception类的句柄,可以直接使用:

我不打算覆写Exception类的方法(例如:getMessage),所以方法列表为空。最后,调用zend_throw_exception只是将第1个参数改为了我的MyException类句柄,其输出如下:

结语

通过本章,你应该掌握:

  • 抛出error。
  • 抛出exception。

 

如果文章帮助您解决了工作难题,您可以帮我点击屏幕上的任意广告,或者赞助少量费用来支持我的持续创作,谢谢~