jitsu/error

合理的PHP错误处理和静默处理

0.2.0 2016-05-07 07:40 UTC

This package is auto-updated.

Last update: 2024-09-11 18:04:03 UTC


README

此包用于使用更规范的行为覆盖PHP的默认错误和异常处理程序,这是一项绝对必要的措施,在开发期间极大地帮助调试,并在生产中创建更安全的程序。

此包是 Jitsu 的一部分。

安装

使用 Composer 安装此包

composer require jitsu/error

关于

PHP的默认错误处理行为很糟糕,以及它对“错误”和异常的任意区分几乎没有意义,这并不是什么秘密。此包定义了一个函数 Jitsu\bootstrap(),它以全局级别覆盖PHP的默认错误处理程序,以更合理的行为。具体来说,它注册了一个简单的错误处理程序,该程序将所有错误转换为可以被捕获和处理的 ErrorException,并注册了一个全局异常处理程序,该程序在退出前可选地打印完整的堆栈跟踪。

请注意,这些覆盖不会以任何方式限制你在全局处理程序到达之前拦截和处理异常/错误;这仅仅改变了未处理的异常/错误报告(或不报告)的方式。全局覆盖主要用于调试,因为默认情况下错误不会停止脚本,并且很容易被忽略。然而,当应用程序在实时环境中部署时,它们同样重要,因为它们确保堆栈跟踪和其他敏感信息不会被显示给最终用户。在调用 Jitsu\bootstrap() 之后,你仍然可以并且应该用应用程序特定的错误处理逻辑包装你的代码。这允许你,例如,显示自定义的500错误页面。

你的PHP脚本中的第一个命令应该是

<?php
require_once __DIR__ . '/vendor/jitsu/error/error.php';
\Jitsu\bootstrap(true); // Turns on all error reporting for debugging

尽早调用 bootstrap() 非常重要,以确保之前没有错误被遗漏。按照设计,这个包不是自动加载的,因为它理想上应该在自动加载注册之前被包含。

请注意,bootstrap() 有两种模式:调试和生产。在生产环境中,你会使用参数 false 调用 bootstrap,以表示应该抑制错误报告。

<?php
require_once __DIR__ . '/vendor/jitsu/error/error.php';
\Jitsu\bootstrap(false); // Turns off all error reporting for production

高级用法

此时,你可能想知道,“如果我在脚本开始时必须调用 bootstrap($run_in_debug_mode),我该如何配置变量 $run_in_debug_mode?”

一个解决方案是在之前从配置文件、数据库等中读取它,希望一切顺利,这很可能是大多数情况下会发生的。然而,你仍然有错过可能发生在读取应用程序配置设置时的关键错误的风险。

一个更安全的解决方案是根本不使用变量,而是通过一个巧妙的构建过程,在预处理步骤中将应用程序的入口点(index.php)生成带有适当的 truefalse 常量的文件。

让我们看看我们如何实现这一点。

假设我们有以下项目结构

deploy.sh
prepare.sh
build/
  dev/
  prod/
app/
  main.php
  composer.json
  vendor/
bootstrap/
  index-dev.php
  index-prod.php

deploy.sh

#/bin/sh
scp -r build/prod/. user@example.com:/var/www

prepare.sh

#/bin/sh
mkdir -p build/$1 && \
cd build/$1 && \
ln -sf ../bootstrap/index-$1.php index.php && \
ln -sf ../app .

bootstrap/index-dev.php

<?php
require_once 'app/vendor/jitsu/error/error.php';
\Jitsu\bootstrap(true);
require 'app/main.php';

bootstrap/index-prod.php

<?php
require_once 'app/vendor/jitsu/error/error.php';
\Jitsu\bootstrap(false);
require 'app/main.php';

app/main.php

<?php
require __DIR__ . '/vendor/autoload.php';
$config = MyApp::readConfig();
try {
	// Do application-specific stuff
	MyApp::routeRequest($config);
} catch(\Exception $e) {
	echo "Oops! Something went wrong.\n\n";
	if($config->show_stack_traces) {
		Jitsu\printException($e);
	}
}

在这里,我们有一个构建部署脚本、一个构建准备脚本、一个未提交到版本控制的build/目录、一个包含应用程序代码的app/目录,以及一个包含多个版本的index.phpbootstrap/目录。实际上,我们有一个管理系统,该系统管理两个名为devprod的"构建"。

我们可以在build/下通过将index.php链接到适当的版本,并创建指向app/目录的符号链接来生成构建。这样,我们为两个构建提供了完全独立的入口点,其中包含了不同的index.php文件,同时最大限度地减少了每个构建中代码的重复。在dev构建中,所有调试功能都被打开;在prod构建中,所有错误、堆栈跟踪等都被静音。

在共享的main.php文件中,我们在设置错误处理程序之后安全地读取配置设置。我们还显示处理HTTP请求时抛出的任何异常,但仅在我们读取的配置设置表明我们应该显示堆栈跟踪时。

我们可以通过运行./prepare.sh dev./prepare.sh prod来生成构建。然后,我们可以通过运行./deploy.sh将生产构建部署到某个远程服务器。

当然,这只是一个简单的例子,可能会经历许多变化。

命名空间

所有函数都在Jitsu命名空间下定义。

API

包含文件error.php

Jitsu\bootstrap($debug = true)

设置PHP脚本是否应在调试模式下运行或在生产模式下运行,并用更合理的行为覆盖PHP的全局错误和异常处理程序。

该函数应在PHP应用程序入口点的非常开始处调用,以便尽可能早地引导错误和异常处理;否则,之前发生的错误可能会未被发现地滑过。

如果$debug为true,则脚本的全局错误和异常处理程序将被覆盖,以便显示所有错误,这对于开发环境中的调试是合适的。如果$debug为false,则所有错误都将被抑制,并且脚本将静默退出,这对于生产环境是合适的。

在两种情况下,错误都将转换为可以在全局异常处理程序之前捕获和处理的ErrorException

此外,如果$debug为false,则默认的X-Powered-By头将被删除。

Jitsu\overrideErrorHandlers($debug = true)

覆盖PHP的默认错误和异常处理程序,以便显示或隐藏所有错误。

如果$debug为true,则在遇到它们时将显示所有未处理的异常、启动错误、致命错误等。如果$debug为false,则将抑制所有这些错误。

在两种情况下,错误都将转换为可以在全局异常处理程序之前捕获和处理的ErrorException

Jitsu\initErrorVisibility($debug = true)

配置是否显示PHP错误或抑制它们。

如果覆盖了错误处理程序,则这里影响的一些设置是多余的,但其中一些与错误处理程序不会接收到的错误有关,即启动错误和内存泄漏。

Jitsu\overrideErrorHandler()

覆盖全局错误处理程序,以便将所有PHP错误转换为异常。

处理程序在遇到错误时简单地将它们转换为ErrorException(除非使用了@错误抑制运算符)。

Jitsu\overrideFatalErrorHandler($debug = true)

用更有用的行为覆盖全局致命错误处理程序。

按照设计,PHP不允许处理致命错误。然而,我们可以注册一个关闭函数,在程序最后的喘息机会中打印(或不清除)有关错误的(或不打印)信息,这比默认行为要好。

请注意,在两种情况下,为了抑制通常的错误输出,禁用了所有PHP错误的默认输出。

Jitsu\overrideExceptionHandler($debug = true)

用更有用的行为覆盖全局异常处理程序。

如果 $debug 为 true,则未处理的异常将导致脚本打印堆栈跟踪并退出。如果 $debug 为 false,则脚本将静默退出。

Jitsu\printException($e)

格式化输出异常及其堆栈跟踪。

Jitsu\errorName($type)

获取 PHP 错误常量的描述性字符串。

Jitsu\removePoweredByHeader()

配置脚本不在 HTTP 响应中发送默认的 X-Powered-By 头部。