PHP: Comment envoyer du code de réponse HTTP?

254

J'ai un script PHP qui doit faire des réponses avec des codes de réponse HTTP (codes d'état), comme HTTP 200 OK, ou du code 4XX ou 5XX.

Comment puis-je faire cela en PHP?

Paulo Coghi - Réintégrer Monica
la source

Réponses:

461

Je viens de trouver cette question et j'ai pensé qu'elle avait besoin d'une réponse plus complète:

Depuis PHP 5.4, il existe trois méthodes pour y parvenir:

Assemblage du code de réponse par vous-même (PHP> = 4.0)

La header()fonction a un cas d'utilisation spécial qui détecte une ligne de réponse HTTP et vous permet de la remplacer par une ligne personnalisée

header("HTTP/1.1 200 OK");

Cependant, cela nécessite un traitement spécial pour (Fast) 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");

Remarque: Selon la RFC HTTP , la raison peut être n'importe quelle chaîne personnalisée (conforme à la norme), mais pour des raisons de compatibilité client, je ne recommande pas d'y mettre une chaîne aléatoire.

Remarque: php_sapi_name() nécessite PHP 4.0.1

3e argument de la fonction d'en-tête (PHP> = 4.3)

Il y a évidemment quelques problèmes lors de l'utilisation de cette première variante. Le plus important dont je pense est qu'il est en partie analysé par PHP ou le serveur web et mal documenté.

Depuis 4.3, la headerfonction a un troisième argument qui vous permet de définir le code de réponse de manière assez confortable, mais son utilisation nécessite que le premier argument soit une chaîne non vide. Voici deux options:

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

Je recommande le 2ème . Le premier fait le travail sur tous les navigateurs que j'ai testé, mais certains navigateurs mineurs ou robots d' exploration Web peut avoir un problème avec une ligne d' en- tête qui ne contient que deux points. Le nom du champ d'en-tête au 2e. La variante n'est bien sûr pas normalisée de quelque façon que ce soit et pourrait être modifiée, je viens de choisir un nom, espérons-le, descriptif.

Fonction http_response_code (PHP> = 5.4)

La http_response_code()fonction a été introduite en PHP 5.4, et cela a rendu les choses beaucoup plus faciles.

http_response_code(404);

C'est tout.

Compatibilité

Voici une fonction que j'ai concoctée lorsque j'avais besoin d'une compatibilité inférieure à 5.4 mais que je voulais la fonctionnalité de la "nouvelle" http_response_codefonction. Je crois que PHP 4.3 est plus que suffisant pour la rétrocompatibilité, mais on ne sait jamais ...

// 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;
    }
}
dualed
la source
10
Je peux confirmer que header('X-PHP-Response-Code: 404', true, 404);cela fonctionne correctement sous PHP-FPM (FastCGI)
Josh
@dualed (1) ne serait pas headers_sent()toujours vrai juste après avoir appelé header()? (2) avez-vous déjà trouvé quelque chose comme http_response_text () dans le monde 5.4? Au moins l'ancien en-tête () peut affecter le texte après le code d'état.
Bob Stein
@ BobStein-VisiBone (1) headers_sent() est vrai si vous ne pouvez plus ajouter d'en-têtes parce que le contenu a déjà été envoyé, pas si vous avez ajouté un en-tête. (2) Désolé, non. Les autres langues ont un meilleur support
dualed
1
@Perry la raison pour laquelle je ne suggère pas de faire cela est la même raison pour laquelle je ne suggère pas le colon seulement. PHP peut gérer cela différemment selon les versions, car il n'est pas défini ce qui se passe avec un tel "en-tête", il peut échouer complètement - ne définissant ni en-tête ni état, ou il peut ajouter un en-tête non valide (la norme de protocole http 1.1 nécessite deux points) )
Dualed
8
J'ai passé des heures à réaliser que http_response_code(et peut-être plus généralement à modifier l'en-tête) ne fonctionne plus après echoquelque chose. J'espère que ça aide.
Neptilo
40

Malheureusement, j'ai trouvé que les solutions présentées par @dualed présentaient divers défauts.

  1. L'utilisation substr($sapi_type, 0, 3) == 'cgi'n'est pas enogh pour détecter un CGI rapide. Lors de l'utilisation de PHP-FPM FastCGI Process Manager, php_sapi_name()renvoie fpm pas cgi

  2. Fasctcgi et php-fpm exposent un autre bogue mentionné par @Josh - l'utilisation header('X-PHP-Response-Code: 404', true, 404);fonctionne correctement sous PHP-FPM (FastCGI)

  3. header("HTTP/1.1 404 Not Found");peut échouer lorsque le protocole n'est pas HTTP / 1.1 (c'est-à-dire 'HTTP / 1.0'). Le protocole actuel doit être détecté à l'aide de $_SERVER['SERVER_PROTOCOL'](disponible depuis PHP 4.1.0

  4. Il existe au moins 2 cas où l'appel http_response_code()entraîne un comportement inattendu:

    • Lorsque PHP rencontre un code de réponse HTTP qu'il ne comprend pas, PHP remplace le code par un code qu'il connaît du même groupe. Par exemple, «521 Web Server is down» est remplacé par «500 Internal Server Error». De nombreux autres codes de réponse inhabituels d'autres groupes 2xx, 3xx, 4xx sont traités de cette façon.
    • Sur un serveur avec php-fpm et nginx, la fonction http_response_code () PEUT changer le code comme prévu mais pas le message. Cela peut entraîner un étrange en-tête "404 OK" par exemple. Ce problème est également mentionné sur le site Web de PHP par un commentaire utilisateur http://www.php.net/manual/en/function.http-response-code.php#112423

Pour votre référence ici, il y a la liste complète des codes d'état de réponse HTTP (cette liste comprend les codes des normes Internet IETF ainsi que d'autres RFC IETF. Beaucoup d'entre eux ne sont PAS actuellement pris en charge par la fonction PHP http_response_code de PHP): http: //en.wikipedia .org / wiki / List_of_HTTP_status_codes

Vous pouvez facilement tester ce bug en appelant:

http_response_code(521);

Le serveur enverra le code de réponse HTTP «500 Internal Server Error» entraînant des erreurs inattendues si vous avez par exemple une application cliente personnalisée qui appelle votre serveur et attend des codes HTTP supplémentaires.


Ma solution (pour toutes les versions PHP depuis 4.1.0):

$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);
}

Conclusion

L'implémentation de http_response_code () ne prend pas en charge tous les codes de réponse HTTP et peut remplacer le code de réponse HTTP spécifié par un autre du même groupe.

La nouvelle fonction http_response_code () ne résout pas tous les problèmes impliqués mais aggrave les choses en introduisant de nouveaux bugs.

La solution de "compatibilité" proposée par @dualed ne fonctionne pas comme prévu, du moins sous PHP-FPM.

Les autres solutions proposées par @dualed comportent également divers bugs. La détection CGI rapide ne gère pas PHP-FPM. Le protocole actuel doit être détecté.

Tous les tests et commentaires sont appréciés.

Grigore Madalin
la source
21

depuis PHP 5.4, vous pouvez utiliser http_response_code()pour obtenir et définir le code d'état de l'en-tête.

voici un exemple:

<?php

// Get the current response code and set a new one
var_dump(http_response_code(404));

// Get the new response code
var_dump(http_response_code());
?>

voici le document de cette fonction sur php.net:

http_response_code

Seyed Ali Roshan
la source
D'après mon expérience, c'est la meilleure réponse.
Scruffy
Pourquoi utiliser var_dump ()?
Tomas Gonzalez
Mais pourquoi var_dump () au lieu de l'écho? Le résultat peut-il ne pas convenir à un simple écho? Ou même print_r (). var_dump () semble donc inadéquat pour le code de production ...
Tomas Gonzalez
@TomasGonzalez ce n'est pas un gros problème, je voulais juste vous montrer ce qu'il contient en imprimant tout avec var_dump () et à droite, ils ne sont pas importants
Seyed Ali Roshan
OK je vois. Ce qui a attiré mon attention, c'est que dans les documents officiels, l'exemple utilise également var_dump (). J'étais donc curieux de savoir pourquoi. Il aurait pu y avoir quelque chose qui me manquait. php.net/manual/en/function.http-response-code.php
Tomas Gonzalez
10

Ajoutez cette ligne avant toute sortie du corps, dans le cas où vous n'utilisez pas la mise en mémoire tampon de sortie.

header("HTTP/1.1 200 OK");

Remplacez la partie du message («OK») par le message approprié et le code d'état par votre code, le cas échéant (404, 501, etc.)

sparkey0
la source
2
Le message que nous mettons (pour remplacer OK) peut-il être quelque chose?
FMaz008
Cela a fonctionné pour moi. Je travaillais sur un formulaire de contact sur un site web avec PHP 5.3. Et cette solution a fonctionné pour moi. Il donnera un texte de réponse et ce code HTTP pour une fonction d'échec de la requête AJAX. C'est tout ce que je voulais.
Surjith SM
7

Si vous êtes ici à cause de Wordpress donnant 404 lors du chargement de l'environnement, cela devrait résoudre le problème:

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

Le problème est dû à l'envoi d'un en-tête Status: 404 Not Found. Vous devez outrepasser cela. Cela fonctionnera également:

define('WP_USE_THEMES', false);
require('../wp-blog-header.php');
header("HTTP/1.1 200 OK");
header("Status: 200 All rosy");
jaggedsoft
la source
en-tête ("HTTP / 1.1 200 OK"); http_response_code (201); en-tête ("Statut: 200 Tout rose"); // travail
alpc
6

Avec la fonction d'en- tête . Il y a un exemple dans la section sur le premier paramètre qu'il prend.

Quentin
la source
2

Si votre version de PHP n'inclut pas cette fonction:

<?php

function http_response_code($code = NULL) {
        if ($code !== NULL) {
            switch ($code) {
                case 100: $text = 'Continue';
                    break;
                case 101: $text = 'Switching Protocols';
                    break;
                case 200: $text = 'OK';
                    break;
                case 201: $text = 'Created';
                    break;
                case 202: $text = 'Accepted';
                    break;
                case 203: $text = 'Non-Authoritative Information';
                    break;
                case 204: $text = 'No Content';
                    break;
                case 205: $text = 'Reset Content';
                    break;
                case 206: $text = 'Partial Content';
                    break;
                case 300: $text = 'Multiple Choices';
                    break;
                case 301: $text = 'Moved Permanently';
                    break;
                case 302: $text = 'Moved Temporarily';
                    break;
                case 303: $text = 'See Other';
                    break;
                case 304: $text = 'Not Modified';
                    break;
                case 305: $text = 'Use Proxy';
                    break;
                case 400: $text = 'Bad Request';
                    break;
                case 401: $text = 'Unauthorized';
                    break;
                case 402: $text = 'Payment Required';
                    break;
                case 403: $text = 'Forbidden';
                    break;
                case 404: $text = 'Not Found';
                    break;
                case 405: $text = 'Method Not Allowed';
                    break;
                case 406: $text = 'Not Acceptable';
                    break;
                case 407: $text = 'Proxy Authentication Required';
                    break;
                case 408: $text = 'Request Time-out';
                    break;
                case 409: $text = 'Conflict';
                    break;
                case 410: $text = 'Gone';
                    break;
                case 411: $text = 'Length Required';
                    break;
                case 412: $text = 'Precondition Failed';
                    break;
                case 413: $text = 'Request Entity Too Large';
                    break;
                case 414: $text = 'Request-URI Too Large';
                    break;
                case 415: $text = 'Unsupported Media Type';
                    break;
                case 500: $text = 'Internal Server Error';
                    break;
                case 501: $text = 'Not Implemented';
                    break;
                case 502: $text = 'Bad Gateway';
                    break;
                case 503: $text = 'Service Unavailable';
                    break;
                case 504: $text = 'Gateway Time-out';
                    break;
                case 505: $text = 'HTTP Version not supported';
                    break;
                default:
                    exit('Unknown http status code "' . htmlentities($code) . '"');
                    break;
            }
            $protocol = (isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0');
            header($protocol . ' ' . $code . ' ' . $text);
            $GLOBALS['http_response_code'] = $code;
        } else {
            $code = (isset($GLOBALS['http_response_code']) ? $GLOBALS['http_response_code'] : 200);
        }
        return $code;
    }
Abdo-Host
la source
1

Nous pouvons obtenir une valeur de retour différente de http_response_code via les deux environnements différents:

  1. Environnement de serveur Web
  2. Environnement CLI

Dans l'environnement du serveur Web, renvoyez le code de réponse précédent si vous avez fourni un code de réponse ou si vous ne fournissez aucun code de réponse, il imprimera la valeur actuelle. La valeur par défaut est 200 (OK).

Dans CLI Environment, true sera renvoyé si vous avez fourni un code de réponse et false si vous ne fournissez aucun code de réponse.

Exemple d'environnement de serveur Web de la valeur de retour de Response_code:

var_dump(http_respone_code(500)); // int(200)
var_dump(http_response_code()); // int(500)

Exemple d'environnement CLI de la valeur de retour de Response_code:

var_dump(http_response_code()); // bool(false)
var_dump(http_response_code(501)); // bool(true)
var_dump(http_response_code()); // int(501)
GaziAnis
la source