# Errores y excepciones en PHP

> Errores y excepciones en PHP: Throwable, Error y Exception, try/catch/finally, handlers globales y ajustes seguros para la salida de errores y los logs.

Fuente: https://phpsos.de/es/php/fehler-und-exceptions/  
Actualizado: 25/10/2026  
Proveedor: PHP SOS, Ayuda de emergencia para PHP y seguridad de servidores. Ayuda inmediata por WhatsApp: https://wa.me/491736494914?text=Hola%20PHP%20SOS%2C%20necesitamos%20ayuda%20con%20nuestro%20servidor%20PHP.%20Empresa%3A%20%E2%80%A6%20Sitio%20web%3A%20%E2%80%A6

**En resumen:** PHP comunica los problemas por dos vías: como errores clásicos, como advertencias y avisos, que se controlan con `error_reporting`, y como excepciones, que se capturan con `try` y `catch`. Desde PHP 7, muchos errores internos también lanzan un objeto de la clase `Error`; junto con `Exception`, implementa la interfaz `Throwable`. En producción rige lo siguiente: registrar los errores en el log y no mostrarlos nunca en el navegador. En PHP 8.5, a causa de PHP2Shell, `fatal_error_backtraces=0` forma además parte de las medidas provisionales.

## Dos mundos: errores clásicos y excepciones

PHP viene de una época en la que los errores se emitían sobre todo como mensajes: avisos (`E_NOTICE`), advertencias (`E_WARNING`), funciones obsoletas (`E_DEPRECATED`) y errores fatales (`E_ERROR`). Muchos de estos mensajes dejan que el script siga ejecutándose, a menudo con datos incorrectos o vacíos.

Las excepciones funcionan de otro modo. Interrumpen el flujo normal y saltan al siguiente bloque `catch` que encaje. Si no se encuentra ninguno, el script termina. Para quienes toman decisiones, la diferencia es importante: una advertencia silenciosa puede hacer que un pedido se guarde de forma incompleta sin que nadie lo note. Una excepción hace visible el problema y permite tratarlo.

Una utilidad infravalorada de los mensajes clásicos son los avisos de tipo `E_DEPRECATED`. Anuncian que un recurso del lenguaje desaparecerá en una versión futura de PHP. Quien los recoge en el log y los evalúa con regularidad sabe, antes de una actualización, qué puntos hay que adaptar, y no tiene que probar durante mucho tiempo cuando surge una vulnerabilidad urgente.

## La jerarquía Throwable

Todo lo que se puede lanzar con `throw` y capturar con `catch` implementa `Throwable`. De ahí cuelgan dos ramas:

| Clase | Finalidad | Ejemplos de subclases |
|---|---|---|
| `Exception` | errores previsibles de la aplicación y de las extensiones | `ErrorException`, `PDOException`, clases propias |
| `Error` | errores internos de PHP | `TypeError`, `ValueError`, `ArgumentCountError`, `ArithmeticError`, `DivisionByZeroError`, `UnhandledMatchError` |

Las clases de error propias derivan de `Exception`, no de `Error`. Desde PHP 8.0, la mayoría de las funciones internas lanzan un `TypeError` o un `ValueError` ante parámetros no válidos, en lugar de emitir solo una advertencia. `strlen([])` es uno de esos casos.

## try, catch y finally en la práctica

```php
<?php
declare(strict_types=1);

final class ZahlungFehlgeschlagen extends RuntimeException {}

function bezahlen(Bestellung $b, Zahlungsdienst $dienst, LoggerInterface $log): bool
{
    try {
        $dienst->belasten($b->betrag(), $b->referenz());
        return true;
    } catch (ZahlungFehlgeschlagen $e) {
        $log->warning('Zahlung abgelehnt', ['bestellung' => $b->id(), 'exception' => $e]);
        return false;
    } catch (TimeoutException | VerbindungsFehler) {
        // desde PHP 8.0 la variable en el catch es opcional
        $log->error('Zahlungsdienst nicht erreichbar', ['bestellung' => $b->id()]);
        return false;
    } finally {
        $dienst->verbindungSchliessen();
    }
}
```

Varias clases se pueden agrupar en un `catch` con `|`. El bloque `finally` se ejecuta siempre, haya ocurrido un error o no. Desde PHP 8.0, además, `throw` es una expresión y puede aparecer, por ejemplo, tras `??`: `$id = $_GET['id'] ?? throw new InvalidArgumentException('id fehlt');`.

## Handlers globales: set_error_handler y set_exception_handler

Dos funciones interceptan lo que de otro modo se escaparía. `set_exception_handler()` registra una función para los Throwable no capturados. Según el manual, el script termina después de llamarla; sirve, por tanto, para registrar en el log y mostrar una página de error neutra, no para seguir trabajando.

Ha dado buen resultado un handler que escribe en el log el error completo con la hora y un identificador aleatorio, y que al visitante solo le muestra una página neutra con ese identificador. El soporte puede entonces localizar el caso en el log sin que salgan al exterior detalles internos.

`set_error_handler()` captura los errores clásicos. El manual muestra cómo convertirlos en una `ErrorException`, de modo que las advertencias se traten como excepciones:

```php
<?php
set_error_handler(function (int $nr, string $text, string $datei, int $zeile): bool {
    if (!(error_reporting() & $nr)) {
        return false; // excluido por error_reporting
    }
    if ($nr === E_DEPRECATED || $nr === E_USER_DEPRECATED) {
        return false; // no escalar los avisos de funciones obsoletas
    }
    throw new ErrorException($text, 0, $nr, $datei, $zeile);
});
```

Los errores fatales, como un desbordamiento de memoria, no se pueden capturar por esta vía. Qué ocurre en el interior durante un desbordamiento lo explica la página [memory_limit y Out-of-Memory](https://phpsos.de/es/php/out-of-memory/).

## Configurar la salida de errores y el registro

Los ajustes más importantes con sus valores por defecto según el manual:

| Ajuste | Valor por defecto | Recomendación en producción |
|---|---|---|
| `error_reporting` | `E_ALL` desde PHP 8.0.0 | `E_ALL` |
| `display_errors` | `1` | `Off` |
| `display_startup_errors` | `1` desde PHP 8.0.0 | `Off` |
| `log_errors` | `0` | `On` |
| `error_log` | vacío (log del servidor web o de la SAPI) | archivo propio fuera del webroot |
| `fatal_error_backtraces` | `1`, nuevo en PHP 8.5.0 | provisionalmente `0` (PHP2Shell) |

Desde PHP 8.2.0, `error_log_mode` fija los permisos del archivo de log, por defecto `0644`. Si los logs contienen datos personales o rutas, conviene un valor más estricto para que otros usuarios del servidor no puedan leerlos. Con `ignore_repeated_errors` se pueden agrupar mensajes idénticos del mismo archivo y línea, para que un bucle de errores no inunde el log.

El `php.ini-production` incluido ya pone `display_errors` en `Off`. Según el manual, `ini_set('display_errors', ...)` no actúa en tiempo de ejecución sobre los errores fatales, y los errores de sintaxis ocurren antes de que se ejecute cualquier `ini_set()`. Por eso estos valores pertenecen a la configuración del servidor. Los antecedentes figuran en el artículo [Desactivar la salida de errores en producción](https://phpsos.de/es/blog/display-errors-produktion/); cómo llevar los logs sin filtrar datos, en el artículo [Registrar logs con seguridad](https://phpsos.de/es/blog/sicher-loggen-php/).

## Tratamiento de errores y PHP2Shell

Para PHP 8.5, php.net indica como una de las medidas provisionales frente a PHP2Shell poner `fatal_error_backtraces=0` y reiniciar PHP después. Las demás medidas afectan a `memory_limit`, `post_max_size` y `session.upload_progress.enabled`. Ninguna de ellas sustituye a la actualización a PHP 8.5.12, 8.4.27, 8.3.36 o 8.2.35.

```ini
; solo PHP 8.5, en php.ini o en el pool de FPM; después, reiniciar todos los workers
fatal_error_backtraces = 0
```

Además, tras PHP2Shell los logs de errores son una prueba importante. Si a partir del 20/10/2026 se acumulan errores de memoria o workers interrumpidos, puede indicar intentos de ataque, aunque también puede tener causas inofensivas. Conserve los logs y evalúelos junto con los logs de acceso, como se describe en la guía [Analizar los logs del servidor web tras un ataque](https://phpsos.de/es/ratgeber/logfiles-auswerten/).

## ¿Cómo ayuda PHP SOS en concreto?

- Revisamos los ajustes efectivos de salida de errores y de registro de cada sitio web y los dejamos aptos para producción.
- Aplicamos las medidas provisionales de PHP2Shell adecuadas a la versión de PHP y reiniciamos todos los workers.
- Aseguramos los logs de errores y de acceso desde el 20/10/2026 y los analizamos en busca de anomalías.
- Como parte de la [monitorización de seguridad](https://phpsos.de/es/leistungen/security-monitoring/) avisamos de acumulaciones inusuales de errores, en lugar de dejar que se pierdan en el log.


## Preguntas frecuentes

**¿Qué diferencia hay entre Error y Exception?**  
Ambas implementan la interfaz Throwable. Exception está pensada para errores que una aplicación espera y gestiona, como una conexión fallida con la base de datos. Error representa los errores internos de PHP, como un TypeError o una división por cero.

**¿Debe estar activado display_errors en producción?**  
No. Los mensajes de error en el navegador revelan rutas, consultas y, a veces, credenciales. En producción, display_errors debe estar en Off y log_errors en On, para que los errores acaben en el log y no ante el visitante.

**¿Se puede capturar un desbordamiento de memoria con try/catch?**  
No. Si un script supera memory_limit, PHP lo termina con un error fatal que no se puede capturar como excepción. Solo aparece en el log de errores, siempre que log_errors esté activo.

**¿Qué es fatal_error_backtraces?**  
Un ajuste que existe desde PHP 8.5.0 y que añade un backtrace a los errores fatales. Está activo por defecto. Como medida provisional frente a PHP2Shell, php.net indica para PHP 8.5 ponerlo a 0 y reiniciar PHP después.

## Fuentes

- [Manual de PHP: configuración del tratamiento de errores](https://www.php.net/manual/de/errorfunc.configuration.php)
- [Manual de PHP: clase Error](https://www.php.net/manual/de/class.error.php)
- [Manual de PHP: ErrorException](https://www.php.net/manual/en/class.errorexception.php)
- [Manual de PHP: set_exception_handler](https://www.php.net/manual/en/function.set-exception-handler.php)
- [php.net: avisos de seguridad](https://www.php.net/)
