我有一个PHP脚本,需要使用HTTP响应代码(状态代码)做出响应,如HTTP 200 OK,或一些4XX或5XX代码。

如何在PHP中做到这一点?


当前回答

不幸的是,我发现@dualed提出的解决方案有各种缺陷。

Using substr($sapi_type, 0, 3) == 'cgi' is not enogh to detect fast CGI. When using PHP-FPM FastCGI Process Manager, php_sapi_name() returns fpm not cgi Fasctcgi and php-fpm expose another bug mentioned by @Josh - using header('X-PHP-Response-Code: 404', true, 404); does work properly under PHP-FPM (FastCGI) header("HTTP/1.1 404 Not Found"); may fail when the protocol is not HTTP/1.1 (i.e. 'HTTP/1.0'). Current protocol must be detected using $_SERVER['SERVER_PROTOCOL'] (available since PHP 4.1.0) There are at least 2 cases when calling http_response_code() result in unexpected behaviour:

When PHP encounter an HTTP response code it does not understand, PHP will replace the code with one it knows from the same group. For example "521 Web server is down" is replaced by "500 Internal Server Error". Many other uncommon response codes from other groups 2xx, 3xx, 4xx are handled this way. On a server with php-fpm and nginx http_response_code() function MAY change the code as expected but not the message. This may result in a strange "404 OK" header for example. This problem is also mentioned on PHP website by a user comment http://www.php.net/manual/en/function.http-response-code.php#112423

这里有HTTP响应状态代码的完整列表供您参考(该列表包括来自IETF互联网标准以及其他IETF rfc的代码。其中许多目前不支持PHP http_response_code函数):http://en.wikipedia.org/wiki/List_of_HTTP_status_codes

你可以通过调用:

http_response_code(521);

如果您有一个自定义客户端应用程序需要额外的HTTP代码,服务器将发送“500 Internal server Error”HTTP响应代码,从而导致错误。


我的解决方案(适用于4.1.0以后的所有PHP版本):

$httpStatusCode = 521;
$httpStatusMsg  = 'Web server is down';
$phpSapiName    = substr(php_sapi_name(), 0, 3);
if ($phpSapiName == 'cgi' || $phpSapiName == 'fpm') {
    header('Status: '.$httpStatusCode.' '.$httpStatusMsg);
} else {
    $protocol = isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0';
    header($protocol.' '.$httpStatusCode.' '.$httpStatusMsg);
}

结论

http_response_code()实现不支持所有的HTTP响应代码,可能会用同一组中的另一个HTTP响应代码覆盖指定的HTTP响应代码。

新的http_response_code()函数并不能解决所有涉及的问题,反而会引入新的错误。

@dualed提供的“兼容性”解决方案并没有像预期的那样工作,至少在PHP-FPM下是这样。

@dualed提供的其他解决方案也有各种bug。快速CGI检测不处理PHP-FPM。必须检测当前协议。

感谢任何测试和评论。

其他回答

使用header函数。在第一个参数小节中有一个例子。

在不使用输出缓冲的情况下,在body的任何输出之前添加这一行。

header("HTTP/1.1 200 OK");

将消息部分('OK')替换为适当的消息,并将状态代码替换为适当的代码(404、501等)

我刚发现这个问题,觉得需要一个更全面的答案:

从PHP 5.4开始,有三种方法来完成这个任务:

自行组装响应代码(PHP >= 4.0)

header()函数有一个特殊的用例,它检测HTTP响应行,并允许您用自定义的响应行替换它

header("HTTP/1.1 200 OK");

但是,这需要对(快速)CGI PHP进行特殊处理:

$sapi_type = php_sapi_name();
if (substr($sapi_type, 0, 3) == 'cgi')
    header("Status: 404 Not Found");
else
    header("HTTP/1.1 404 Not Found");

注意:根据HTTP RFC,原因短语可以是任何自定义字符串(符合标准),但为了客户端兼容性,我不建议在那里放置随机字符串。

注意:php_sapi_name()需要PHP 4.0.1

头函数的第三个参数(PHP >= 4.3)

在使用第一个变体时,显然存在一些问题。我认为最大的问题是,它部分是由PHP或web服务器解析的,并且缺乏文档。

从4.3开始,header函数有了第三个参数,可以让您轻松地设置响应代码,但使用它需要第一个参数是非空字符串。这里有两种选择:

header(':', true, 404);
header('X-PHP-Response-Code: 404', true, 404);

我推荐第二部。第一种方法在我测试过的所有浏览器上都可以运行,但一些小型浏览器或网络爬虫可能会在标题行中只包含冒号时出现问题。头字段名在第二个。变体当然不是标准化的,可以修改,我只是选择了一个描述性的名字。

http_response_code函数(PHP >= 5.4)

http_response_code()函数是在PHP 5.4中引入的,它使事情变得简单得多。

http_response_code(404);

这是所有。

兼容性

这里是一个函数,当我需要兼容性低于5.4,但想要“新”http_response_code函数的功能时,我已经煮熟了。我相信PHP 4.3的向后兼容性已经足够了,但是你永远不知道…

// For 4.3.0 <= PHP <= 5.4.0
if (!function_exists('http_response_code'))
{
    function http_response_code($newcode = NULL)
    {
        static $code = 200;
        if($newcode !== NULL)
        {
            header('X-PHP-Response-Code: '.$newcode, true, $newcode);
            if(!headers_sent())
                $code = $newcode;
        }       
        return $code;
    }
}

如果你在这里是因为Wordpress在加载环境时给出404错误,这应该可以解决这个问题:

define('WP_USE_THEMES', false);
require('../wp-blog-header.php');
status_header( 200 );
//$wp_query->is_404=false; // if necessary

该问题是由于它发送一个状态:404 Not Found头。你必须重写它。 这也可以:

define('WP_USE_THEMES', false);
require('../wp-blog-header.php');
header("HTTP/1.1 200 OK");
header("Status: 200 All rosy");

不幸的是,我发现@dualed提出的解决方案有各种缺陷。

Using substr($sapi_type, 0, 3) == 'cgi' is not enogh to detect fast CGI. When using PHP-FPM FastCGI Process Manager, php_sapi_name() returns fpm not cgi Fasctcgi and php-fpm expose another bug mentioned by @Josh - using header('X-PHP-Response-Code: 404', true, 404); does work properly under PHP-FPM (FastCGI) header("HTTP/1.1 404 Not Found"); may fail when the protocol is not HTTP/1.1 (i.e. 'HTTP/1.0'). Current protocol must be detected using $_SERVER['SERVER_PROTOCOL'] (available since PHP 4.1.0) There are at least 2 cases when calling http_response_code() result in unexpected behaviour:

When PHP encounter an HTTP response code it does not understand, PHP will replace the code with one it knows from the same group. For example "521 Web server is down" is replaced by "500 Internal Server Error". Many other uncommon response codes from other groups 2xx, 3xx, 4xx are handled this way. On a server with php-fpm and nginx http_response_code() function MAY change the code as expected but not the message. This may result in a strange "404 OK" header for example. This problem is also mentioned on PHP website by a user comment http://www.php.net/manual/en/function.http-response-code.php#112423

这里有HTTP响应状态代码的完整列表供您参考(该列表包括来自IETF互联网标准以及其他IETF rfc的代码。其中许多目前不支持PHP http_response_code函数):http://en.wikipedia.org/wiki/List_of_HTTP_status_codes

你可以通过调用:

http_response_code(521);

如果您有一个自定义客户端应用程序需要额外的HTTP代码,服务器将发送“500 Internal server Error”HTTP响应代码,从而导致错误。


我的解决方案(适用于4.1.0以后的所有PHP版本):

$httpStatusCode = 521;
$httpStatusMsg  = 'Web server is down';
$phpSapiName    = substr(php_sapi_name(), 0, 3);
if ($phpSapiName == 'cgi' || $phpSapiName == 'fpm') {
    header('Status: '.$httpStatusCode.' '.$httpStatusMsg);
} else {
    $protocol = isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0';
    header($protocol.' '.$httpStatusCode.' '.$httpStatusMsg);
}

结论

http_response_code()实现不支持所有的HTTP响应代码,可能会用同一组中的另一个HTTP响应代码覆盖指定的HTTP响应代码。

新的http_response_code()函数并不能解决所有涉及的问题,反而会引入新的错误。

@dualed提供的“兼容性”解决方案并没有像预期的那样工作,至少在PHP-FPM下是这样。

@dualed提供的其他解决方案也有各种bug。快速CGI检测不处理PHP-FPM。必须检测当前协议。

感谢任何测试和评论。