Cómo enviar el error 500 Internal Server Error desde un script PHP

80

Necesito enviar "500 Internal Server Error" desde un script PHP bajo ciertas condiciones. Se supone que el script debe ser llamado por una aplicación de terceros. El script contiene un par de die("this happend")declaraciones para las que necesito enviar el 500 Internal Server Errorcódigo de respuesta en lugar del habitual 200 OK. El script de terceros reenviará la solicitud bajo ciertas condiciones que incluyen no recibir el 200 OKcódigo de respuesta.

Segunda parte de la pregunta: necesito configurar mi script de esta manera:

<?php
    custom_header( "500 Internal Server Error" );

    if ( that_happened ) {
        die( "that happened" )
    }

    if ( something_else_happened ) {
        die( "something else happened" )
    }

    update_database( );

    // the script can also fail on the above line
    // e.g. a mysql error occurred

    remove_header( "500" );
?>

Necesito enviar el 200encabezado solo después de que se haya ejecutado la última línea.

Editar

Una pregunta secundaria: ¿puedo enviar 500 encabezados extraños como estos?

HTTP/1.1 500 No Record Found
HTTP/1.1 500 Script Generated Error (E_RECORD_NOT_FOUND)
HTTP/1.1 500 Conditions Failed on Line 23

¿El servidor web registrará dichos errores?

Salman A
fuente
no es factible cuando enviaste el encabezado y lo eliminaste más tarde
ajreal
1
Pregunta adicional: Eso es completamente legítimo. Las frases de motivo no están destinadas al consumo de máquinas y pueden ser cualquier cosa. Lo que importa es solo el código de estado de tres dígitos. (RFC2616 6.1.1: "Las frases de motivo enumeradas aquí son solo recomendaciones; PUEDEN ser reemplazadas por equivalentes locales sin afectar el protocolo").
Piskvor salió del edificio el

Respuestas:

169
header($_SERVER['SERVER_PROTOCOL'] . ' 500 Internal Server Error', true, 500);
Núcleo Xii
fuente
Para su información, esta solución envía el X-Pad: evitar el encabezado de error del navegador en algunas versiones de Apache. stackoverflow.com/questions/8711584/… . http_response_code()omite este encabezado.
Anthony Rutledge
3
Eso incluso funciona si el encabezado no está al principio (como una prueba de funcionamiento), buen trabajo.
yo soy yo
Genial, ¿qué pasa si desea generar un json en lugar de un error interno del servidor 500?
tblancog hace
44

PHP 5.4 tiene una función llamada http_response_code , por lo que si está usando PHP 5.4 puede hacer lo siguiente:

http_response_code(500);

Escribí un polyfill para esta función (Gist) si está ejecutando una versión de PHP en 5.4.


Para responder a su pregunta de seguimiento, la RFC HTTP 1.1 dice:

Las frases de motivo enumeradas aquí son solo recomendaciones: PUEDEN ser reemplazadas por equivalentes locales sin afectar el protocolo.

Eso significa que puede usar el texto que desee (excluyendo los retornos de carro o los avances de línea) después del código en sí, y funcionará. Sin embargo, generalmente hay un mejor código de respuesta para usar. Por ejemplo, en lugar de utilizar un 500 para no encontrar ningún registro, puede enviar un 404 (no encontrado), y para algo como "fallaron las condiciones" (supongo que es un error de validación), podría enviar algo como un 422 (no procesable entidad).

inxilpro
fuente
Es importante tener en cuenta que esta función tampoco funciona más una vez que ha comenzado la salida.
rob74
@ rob74 True: una vez que PHP ha comenzado a enviar resultados, todas las funciones relacionadas con el encabezado dejarán de funcionar. Si no está seguro de si va a tener que cambiar el código de respuesta antes de comenzar a enviar la salida, el almacenamiento en búfer de salida es una buena solución.
inxilpro
33

Puede utilizar la siguiente función para enviar un cambio de estado:

function header_status($statusCode) {
    static $status_codes = null;

    if ($status_codes === null) {
        $status_codes = array (
            100 => 'Continue',
            101 => 'Switching Protocols',
            102 => 'Processing',
            200 => 'OK',
            201 => 'Created',
            202 => 'Accepted',
            203 => 'Non-Authoritative Information',
            204 => 'No Content',
            205 => 'Reset Content',
            206 => 'Partial Content',
            207 => 'Multi-Status',
            300 => 'Multiple Choices',
            301 => 'Moved Permanently',
            302 => 'Found',
            303 => 'See Other',
            304 => 'Not Modified',
            305 => 'Use Proxy',
            307 => 'Temporary Redirect',
            400 => 'Bad Request',
            401 => 'Unauthorized',
            402 => 'Payment Required',
            403 => 'Forbidden',
            404 => 'Not Found',
            405 => 'Method Not Allowed',
            406 => 'Not Acceptable',
            407 => 'Proxy Authentication Required',
            408 => 'Request Timeout',
            409 => 'Conflict',
            410 => 'Gone',
            411 => 'Length Required',
            412 => 'Precondition Failed',
            413 => 'Request Entity Too Large',
            414 => 'Request-URI Too Long',
            415 => 'Unsupported Media Type',
            416 => 'Requested Range Not Satisfiable',
            417 => 'Expectation Failed',
            422 => 'Unprocessable Entity',
            423 => 'Locked',
            424 => 'Failed Dependency',
            426 => 'Upgrade Required',
            500 => 'Internal Server Error',
            501 => 'Not Implemented',
            502 => 'Bad Gateway',
            503 => 'Service Unavailable',
            504 => 'Gateway Timeout',
            505 => 'HTTP Version Not Supported',
            506 => 'Variant Also Negotiates',
            507 => 'Insufficient Storage',
            509 => 'Bandwidth Limit Exceeded',
            510 => 'Not Extended'
        );
    }

    if ($status_codes[$statusCode] !== null) {
        $status_string = $statusCode . ' ' . $status_codes[$statusCode];
        header($_SERVER['SERVER_PROTOCOL'] . ' ' . $status_string, true, $statusCode);
    }
}

Puede usarlo como tal:

<?php
header_status(500);

if (that_happened) {
    die("that happened")
}

if (something_else_happened) {
    die("something else happened")
}

update_database();

header_status(200);
Andrew Moore
fuente
21
Ahorrar un par de bytes de memoria rara vez es una buena razón para escribir código de aplicación web de una forma u otra.
Dan Grossman
1
¿No te estás perdiendo un ;después die()?
henrywright
2
Falta su lista 418 => "soy una tetera" ver: en.wikipedia.org/wiki/Hyper_Text_Coffee_Pot_Control_Protocol
Louis Loudog Trottier
16

Puedes simplemente poner:

header("HTTP/1.0 500 Internal Server Error");

dentro de sus condiciones como:

if (that happened) {
    header("HTTP/1.0 500 Internal Server Error");
}

En cuanto a la consulta de la base de datos, puede hacerlo así:

$result = mysql_query("..query string..") or header("HTTP/1.0 500 Internal Server Error");

Debe recordar que debe poner este código antes de cualquier etiqueta html (o salida).

Ruel
fuente
11
Asegúrese de salir / morir / regresar / algo después de llamar a header (). PHP seguirá ejecutando código, lo que probablemente no sea deseable.
David Goodwin
8

Puedes simplificarlo así:

if ( that_happened || something_else_happened )
{
    header('X-Error-Message: Incorrect username or password', true, 500);
    die;
}

Devolverá el siguiente encabezado:

HTTP/1.1 500 Internal Server Error
...
X-Error-Message: Incorrect username or password
...

Agregado: si necesita saber exactamente qué salió mal, haga algo como esto:

if ( that_happened )
{
    header('X-Error-Message: Incorrect username', true, 500);
    die('Incorrect username');
}

if ( something_else_happened )
{
    header('X-Error-Message: Incorrect password', true, 500);
    die('Incorrect password');
}
David Kuridža
fuente
5
Ahora, ¿qué se 'x'supone que sea?
Core Xii
1
+1 Tenga en cuenta que el primer parámetro en headerdebe ser una cadena no vacía. Eso 'x'realmente no importa.
theazureshadow
1
@Core Xii, el primer parámetro no debe ser nulo como señaló @theazureshadow. En resumen, al llamar a header ('algo', verdadero, 500), se devolverá el encabezado correcto "HTTP / 1.0 500 Internal Server Error". Puede llamarme perezoso, pero es más fácil simplemente pasar el código de error que manejar la cadena de encabezado real :) Eche un vistazo a php.net/manual/en/function.header.php para obtener más detalles.
David Kuridža
El manual no es muy claro al respecto. Entonces, ¿está diciendo que si fuerza el código de estado, PHP sobrescribe el argumento de cadena a su valor correcto automáticamente? Entonces, ¿por qué diría el manual que solo tiene efecto si la cadena no está vacía?
Core Xii
1
Tenga en cuenta que en algunas configuraciones PHP (la que usa mi proveedor de alojamiento para el ejemplo, pero no mi configuración local) ¡este truco no funcionará! Apache no reconoce "x" como una cadena de encabezado correcta y fallará con un error "Encabezado mal formado".
mjsarfatti
2

Tu código debería verse así:

<?php
if ( that_happened ) {
    header("HTTP/1.0 500 Internal Server Error");
    die();
}

if ( something_else_happened ) {
    header("HTTP/1.0 500 Internal Server Error");
    die();
}

// Your function should return FALSE if something goes wrong
if ( !update_database() ) {
    header("HTTP/1.0 500 Internal Server Error");
    die();
}

// the script can also fail on the above line
// e.g. a mysql error occurred


header('HTTP/1.1 200 OK');
?>

Supongo que detiene la ejecución si algo sale mal.

Sébastien VINCENT
fuente