我知道这是一个旧线程,但是当我在 Google 上搜索时,它会作为第一个答案跳出来。这些天我试图弄清楚如何做到这一点,所以我认为我应该从 2018 年开始发布我的解决方法。
众所周知,不,不能简单地从 PHP 触发 Apache 的错误页面。没有办法做到这一点。因此,经过一番研究,我能找到的最佳解决方法是使用 PHP 函数来显示自定义错误页面,它是从您在 Apache 中指定的内容和您想要的页面中调用的触发 500 内部服务器错误。
这是我的 Apache conf:
ErrorDocument 400 /error.php
ErrorDocument 401 /error.php
ErrorDocument 403 /error.php
ErrorDocument 404 /error.php
ErrorDocument 500 /error.php
如您所见,所有 4xx 和 500 错误都被重定向到同一个 php 文件。为什么我不为每个代码使用单独的文件是另一个问题,这里与您的问题无关,所以让我们忘记它,现在专注于error.php。
这里有几行error.php:
<?php
require_once("error_page.php");
$relative_path = "";
$internal_server_error = FALSE;
if (isset($_SERVER['REDIRECT_URL']) && $_SERVER['REDIRECT_URL'] != "")
{
$relative_path = $_SERVER['REDIRECT_URL'];
$internal_server_error = (http_response_code() >= 500);
}
else if (isset($_SERVER['REQUEST_URI']))
{
$relative_path = $_SERVER['REQUEST_URI'];
}
ErrorPage::GenerateErrorPage($internal_server_error, $relative_path);
?>
简而言之,它通过http_response_code()从Apache接收HTTP状态码,并将状态码简单地分为两类:Internal Server Error和Non-Internal Server Error。然后它将类别传递给ErrorPage::GenerateErrorPage(),它实际上会生成错误页面的内容。
在ErrorPage::GenerateErrorPage(),我做了这样的事情:
<?php
http_response_code($internal_server_error ? 500 : 404);
?>
在函数的开头,这样就不会有任何烦人的“headers already sent”。此函数将生成一个功能齐全的错误页面,其中包含正确的(或至少是我们预期的)HTTP 状态代码。设置好HTTP状态码后,你可能猜到了,下面的php代码会生成两个准备好的内容之一,一个是500,一个是404。
现在让我们回到你的问题。
您想触发与 Apache 完全相同的错误页面,现在您可以简单地从任何地方调用 ErrorPage::GenerateErrorPage(),只要您给它正确的参数(TRUE 表示 500,FALSE 表示404 在我的情况下)。
显然,由于 Apache 错误页面是由同一函数 ErrorPage::GenerateErrorPage() 生成的,我们可以保证您在任何地方触发的内容正是您对 Apache 错误页面的期望。
这是我的触发页面示例:
<?php
ob_start();
require_once("error_page.php");
try
{
// Actual contents go here
phpInfo();
throw new Exception('test exception');
}
catch (Exception $e)
{
ob_clean();
ErrorPage::GenerateErrorPage(TRUE);
ob_end_flush();
exit();
}
ob_end_flush();
?>
我使用ob_start()和ob_clean()的原因是,通过这种方式,我可以在异常发生之前清除任何输出,并且发送一个看起来与Apache完全一样的纯500 Internal Server Error页面给出,触发页面上没有任何内容(在这种情况下为phpInfo())。 ob_start() 和 ob_clean() 也确保不会有“标头已发送”,因为除非您致电 ob_end_flush(),否则不会发送任何内容。
上面的解决方案是根据我这几天的研究得出的。到目前为止,这对我来说很好,但我不确定这是否是正确的方法。欢迎任何 cmets、问题、想法、建议和改进。