PHP RFC: 允许 __toString () 方法抛出异常

200 阅读3分钟

简介

当前禁止从 __toString() 中引发异常,这将导致致命错误。这使得在 __toString() 中编写任意代码都是危险的,并使其作为通用 API 的使用成为问题。这个 RFC 消除了这个限制。

当前行为的基本原理是,在整个引擎和标准库的很多地方都进行了字符串转换,并且并非所有地方都准备 “正确地” 处理异常,因为从某种意义上说,应尽早处理异常。

从技术的角度来看,这种限制最终是徒劳的,因为在字符串转换期间的异常仍然可以被可恢复的错误转换为异常的错误处理程序触发:

[set_error_handler](http://www.php.net/set_error_handler)(function() {
    throw new Exception();
});

try {
    (string) new stdClass;
} catch (Exception $e) {
    echo "(string) threw an exception...\n";
}

事实上,Symfony 使用此漏洞 可以解决当前的限制。不幸的是,这依赖于 $errcontext 参数,该参数在 PHP 8 中已经消失了。

尽管如此,之前这个话题的讨论并没有使这个限制松动,直到我们对该代码库的字符串转换进行全面的审核。这已经在附加的实现中完成。pull request


提议

允许从__toString() 抛出异常,其行为将和往常一样,不再触发致命错误。

另外,按照 PHP7 建立的错误策略,将 ”could not be converted to string” 和 “__toString () must return a string value” 的致命错误转换为正确的 Error 异常。


扩展准则

扩展作者想要确保他们优雅地处理字符串转换带来的异常,应该考虑以下的准则:

  • 如果 zval_get_string(), convert_to_string() 生成一个异常,它们仍会产生一个字符串。这个字符串保证可以被保留。这意味着不必释放它,但是可以这样做。你可以在上下文中选择更方便的选项。
  • 如果一个对象转换成字符串失败,那么字符串转换的结果为空,如果数组转换成字符串并且错误处理程序将结果提升为异常,那么字符串转换的结果将为 “Array”。(其行为同以前一样。)
  • 使用常规的 if (EG(exception)) 来检查是否抛出异常就足够了:
zend_string *str = zval_get_string(val);
if (EG(exception)) {
    // 可能在这里释放其他资源。
    return;
}

-  除此之外,模型转换作为一个容易犯错的操作,提供了许多帮助 API:

// 像 zval_get_string(),但是在转换失败的时候返回 NULL 。
zend_string *str = zval_try_get_string(val);
if (!str) {
    // 可能在这里释放其他资源。
    return;
}
// 主代码
zend_string_release(str);

// 像 zval_get_tmp_string(),但是在转换失败的时候返回 NULL 。
zend_string *tmp, *str = zval_try_get_tmp_string(val, &tmp);
if (!str) {
   // 可能在这里释放其他资源。
    return;
}
// 主代码
zend_tmp_string_release(tmp);

// 像 convert_to_string() ,但是在转换成功/失败的时候返回布尔值。
if (!try_convert_to_string(val)) {
     // 可能在这里释放其他资源。
    return;
}
// 主代码。
  • try_convert_to_string() 在转换失败的时候,不会修改初始值。因此,使用它比 convert_to_string()和异常检查更为安全。
  • 虽然检查每个字符串转换会确保你的安全,但是忽视这些检查通常只会导致一些不需要的计算和可能的冗余警告。最主要的是注意修改例如数据库这种持久性结构的操作。
八重樱:怎么从一名码农成为架构师的必看知识点:目录大全(不定期更新)​
zhuanlan.zhihu.com

以上内容希望帮助到大家,很多PHPer在进阶的时候总会遇到一些问题和瓶颈,业务代码写多了没有方向感,不知道该从那里入手去提升,对此我整理了一些资料,包括但不限于:分布式架构、高可扩展、高性能、高并发、服务器性能调优、TP6,laravel,YII2,Redis,Swoole、Swoft、Kafka、Mysql优化、shell脚本、Docker、微服务、Nginx等多个知识点高级进阶干货需要的可以免费分享给大家,需要的可以加入我的官方群点击此处