如何在PHP中捕捉require()或include()的错误?

29

我正在使用PHP5编写一个脚本,需要引用某些文件的代码。当某个文件无法被引用时,会先发出一个警告,然后再抛出一个致命错误。当无法包含代码时,我想要打印自己的错误消息。如果require未能正常工作,是否可以执行最后一个命令?下面的代码没有起作用:

require('fileERROR.php5') or die("Unable to load configuration file.");

使用 error_reporting(0) 抑制所有错误消息只会得到一个白屏,而不使用 error_reporting 则会显示 PHP 错误,而我不想显示它们。

7个回答

32
你可以通过使用 set_error_handlerErrorException 来实现这一点。 ErrorException 页面中的示例是:
<?php
function exception_error_handler($errno, $errstr, $errfile, $errline ) {
    throw new ErrorException($errstr, $errno, 0, $errfile, $errline);
}
set_error_handler("exception_error_handler");

/* Trigger exception */
strpos();
?>

一旦将错误处理为异常,您可以执行以下操作:

<?php
try {
    include 'fileERROR.php5';
} catch (ErrorException $ex) {
    echo "Unable to load configuration file.";
    // you can exit or die here if you prefer - also you can log your error,
    // or any other steps you wish to take
}
?>

@SajnEvardsson php手册中ErrorException的第三个参数是$errno,而不是第二个。 - Basit
2
上面的示例直接从ErrorException文档(上面的链接)中复制而来 - 在“官方”示例中,他们使用$errno作为$code,而其他人(包括该页面上的评论)建议将其用作$severity - 请注意,如果您愿意,可以同时使用它们,也可以都不使用。 - Sjan Evardsson
似乎这是一个过度工程化的方式去完成很简单的事情。 - Jacek Dziurdzikowski
这实际上是在我的情况下一个有用的解决方案。我有遗留(框架之前!)代码,文件名有后缀... ClassA.class.phpInterfaceA.interface.php。长期目标:修复所有问题。短期目标:通过安全地尝试两者来减少冗余的文件存在检查。 - cautionbug

26

我只是使用'file_exists()':

if (file_exists("must_have.php")) {
    require "must_have.php";
}
else {
    echo "Please try back in five minutes...\n";
}

2
最好使用 is_readable();它检查文件是否存在,以及文件的可读性(简单来说,文件必须存在才能被读取)。此外,为了防止包含目录,您应该使用 is_dir()(即 !is_dir($filename))。 - MAChitgarha
file_exists 不会检查包含路径,因此可能无法正常工作。 - Dan Stevens
在你的echo语句结尾处加上die()。如果文件不存在,require会停止脚本运行并显示echo,但是在结尾处添加die()将不会在页面中显示任何内容,除了消息。 - Dexter
它还可以处理目录,不仅仅是文件,这样更好。 - Mbotet

4
更好的方法是先在路径上使用realpath。如果文件不存在,realpath 将返回false
$filename = realpath(getcwd() . "/fileERROR.php5");
$filename && return require($filename);
trigger_error("Could not find file {$filename}", E_USER_ERROR);

你甚至可以在应用程序的命名空间中创建自己的 require 函数,它包装了 PHP 的 require 函数。
namespace app;

function require_safe($filename) {
  $path = realpath(getcwd() . $filename);
  $path && return require($path);
  trigger_error("Could not find file {$path}", E_USER_ERROR);
}

现在你可以在任何文件中使用它。
namespace app;

require_safe("fileERROR.php5");

我完全不知道为什么,但是当在PHP CLI /控制台中运行时,你的代码不能正确工作(产生假阳性)。也就是说,它停止脚本的执行,并声称文件XYZ不存在,而实际上它确实存在。注释掉你的代码并直接使用include()可以正常工作,文件也被成功包含而无错误。 - trejder

3

从PHP 7.0开始,您可以捕获由require()抛出的Throwable异常。

try {
    require '/path/not-found/or-not-available.php';
}
catch (Error $e) {
    // debugging example:
    die('Caught error => ' . $e->getMessage());
}

将输出如下内容:

捕获到错误 => 打开 /path/not-found/or-not-available.php 失败 [...]


2

你需要使用include()函数。如果使用require()函数时引用了不存在的文件,会产生致命错误并退出脚本,因此die()函数不会被执行。而include()函数只会产生警告,然后脚本继续运行。


1
你的答案可能是特定于某个PHP版本或特定于某些php.ini配置。在我的环境中(PHP 5.5.4),无论我将require更改为include,都没有任何区别。两者都会抛出丑陋的致命错误,我无法捕获。 - trejder
@trejder - 这不正确。include 会发出一个警告,但程序会继续执行。这是非致命的。而 require 会产生一个错误并停止程序。虽然两者都会在 error.log 中生成条目。 - Rohit Gupta
@RohitGupta,您在提到我七年前的评论,那个评论谈论了100%配置特定的事情。您确定无法按照我描述的方式配置PHP吗?我不知道七年前我的PHP配置是什么样子的,但我几乎看不出我会在这里写一些不真实的东西的原因。我认为我当时写下了我观察到的情况以及我的环境实际上的行为。 - trejder

1
一个我正在使用的简单方法是:
<?php
    ...
    if(!include 'config.php'){
        die("File not found handler. >_<");
    }
   ...
?>

有任何特定的情况,这不会起作用吗? - ShifraSec
我已经给你点了踩,因为这仍然会生成一个警告。虽然执行仍在继续,但它会在错误日志中留下两行。这仍然是一个错误条件,你没有捕捉到它。 - Rohit Gupta

1

我建议您查看set_error_handler()函数文档中最近期评论

它提供了以下方法(并附有示例)来捕获致命错误:

<?php
function shutdown()
{
    $a=error_get_last();
    if($a==null)  
        echo "No errors";
    else
         print_r($a);

}
register_shutdown_function('shutdown');
ini_set('max_execution_time',1 );
sleep(3);
?> 

我还没有尝试过这个建议,但在其他致命错误的情况下可能也可以使用。


虽然这可能有效,但是它会产生一个好问题,即是否适用于OP所询问的条件?我相信大多数来到这里的人都在寻找一种解决方案,可以在捕获不存在的文件上使用require并继续脚本执行。能够优雅地死亡(即显示我想要的东西,但最终仍然结束脚本——这似乎不适合许多情况。我假设,如果脚本将要死亡,它可以显示任何它想要的东西。我想防止在提供给require的不存在文件上死亡/终止。 - trejder

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接