Apuntes php

PHP: Porque a veces necesitas un poco de caos en tu vida.

Manejo de Errores y Depuración en PHP

El manejo de errores y su depuración son habilidades esenciales para cualquier programador. Un buen manejo de errores facilita el mantenimiento del código y ayuda a identificar problemas rápidamente.

Tipos de Errores en PHP

PHP tiene varios tipos (niveles) de errores, según su gravedad y el momento en que ocurren. Conocer estas categorías te ayudará a interpretar mejor los mensajes de error que aparecen durante el desarrollo y a solucionar problemas con mayor rapidez:

  • Errores de Sintaxis (Parse errors): Ocurren cuando el código no cumple con las reglas gramaticales de PHP.
  • Errores Fatales (Fatal errors): Detienen la ejecución del script inmediatamente.
  • Advertencias (Warnings): No detienen la ejecución pero indican un problema potencial.
  • Avisos (Notices): Indican posibles problemas en el código que no afectan la ejecución.
  • Errores Estrictos (Strict standards): Sugerencias sobre mejores prácticas de código.
  • Excepciones (Exceptions): Errores que pueden ser capturados y manejados durante la ejecución.

Configuración de Error Reporting

El comportamiento de los errores en PHP puede configurarse desde varios lugares, dependiendo del entorno o de si estás en fase de desarrollo o la página está online:

  • php.ini: Archivo de configuración global de PHP. Aquí se definen directivas como error_reporting o display_errors.
  • .htaccess o apache2.conf: Si usas PHP como módulo de Apache, puedes modificar ciertas directivas directamente desde la configuración del servidor.
  • Desde el propio script: Ideal para controlar errores de forma puntual o durante el desarrollo. Usa error_reporting() y ini_set().
<?php
    // Mostrar todos los errores
error_reporting(E_ALL);
ini_set('display_errors', 1);

// Solo mostrar errores y advertencias
error_reporting(E_ERROR | E_WARNING);

// Ocultar errores (no recomendado para desarrollo)
error_reporting(0);
ini_set('display_errors', 0);
?>

Configuración en php.ini

Para una configuración permanente, puedes modificar el archivo php.ini:

; Mostrar todos los errores
error_reporting = E_ALL
display_errors = On

; Entorno de producción (no mostrar errores al usuario)
error_reporting = E_ALL
display_errors = Off
log_errors = On
error_log = /ruta/al/archivo/php_errors.log

La función ini_set() solo puede usarse dentro de un script PHP. Sirve para modificar una directiva de configuración en tiempo de ejecución, pero solo afecta al propio script (y a los que incluya). En php.ini, .htaccess o apache2.conf debes usar las formas correspondientes:

  • En php.ini: display_errors = On
  • En .htaccess (si usas Apache con mod_php): php_flag display_errors On
  • En el script PHP: ini_set('display_errors', 1);

Nota: En desarrollo, muestra todos los errores.
En producción, ocúltalos al usuario final pero guárdalos en un archivo de log.

Manejo de Excepciones con Try-Catch

En PHP, una excepción es una forma especial de manejar errores. A diferencia de los errores tradicionales (como Warning o Notice), una excepción interrumpe el flujo normal del programa y puede ser capturada mediante un bloque try / catch.

Cuando ocurre una excepción, PHP detiene la ejecución normal y busca si hay un bloque catch para manejarla. Si no se captura, se muestra un mensaje de error fatal y el script termina.

Las excepciones permiten manejar errores de forma más controlada y clara, especialmente en operaciones propensas a fallos como acceder a archivos, trabajar con bases de datos o hacer conexiones externas.
Cuando ocurre una excepción dentro del bloque try, la ejecución de ese bloque se interrumpe inmediatamente y pasa al bloque catch, donde puedes gestionar el error sin que el script se detenga por completo.

No todas las funciones de PHP lanzan excepciones. De hecho, la mayoría de las funciones nativas simplemente emiten warnings o notices cuando ocurre un error, y no pueden ser capturadas con try/catch directamente.

Para poder interceptar esos errores comunes como si fueran excepciones, es necesario definir un manejador de errores personalizado mediante la función set_error_handler(). Este manejador puede transformar errores normales en objetos Exception, lo que permite tratarlos con try/catch.

<?php


        set_error_handler(function ($errno, $errstr, $errfile, $errline) {
            throw new Exception($errstr);
        });

        try {
            echo "Inicio del bloque try<br>";

            // Esta línea lanza una excepción (archivo no existe)
            $contenido = file_get_contents("no_existe.txt");

            // Esta línea nunca se ejecutará
            echo "Esto nunca se verá<br>";
        } catch (Exception $e) {
            echo "Excepción capturada: " . $e‐>getMessage() . "<br>";
        }

        echo "El script continúa después del try‐catch<br>";

En este ejemplo, si file_get_contents() falla, se lanza una excepción que es capturada por el bloque catch.

Lanzar Excepciones Personalizadas

Puedes crear y lanzar tus propias excepciones:

<?php

    function verificarEdad($edad) {
    if ($edad < 0) {
        throw new Exception("La edad no puede ser negativa");
    }
    if ($edad < 18) {
        throw new Exception("Debes ser mayor de edad");
    }
    return true;
}

try {
    verificarEdad(16);
    echo "Acceso permitido";
} catch (Exception $e) {
    echo "Error: " . $e->getMessage();
}
?>

mas ejemplos aún:

<?php

        set_error_handler(function ($errno, $errstr, $errfile, $errline) {
            throw new Exception($errstr);
        });

        try {
            echo "Voy a intentar leer un archivo importante...
"; $datos = file_get_contents("config.txt"); echo "Archivo cargado con éxito:
"; echo nl2br($datos); } catch (Exception $e) { echo "No se pudo leer el archivo.
"; echo "Voy a usar configuraciones por defecto.
"; // Aquí usamos valores por defecto, o seguimos otra alternativa $datos = "modo=seguro\nidioma=español"; echo nl2br($datos); }

Supongamos que alguien ha leido hasta aquí y se pregunta ... y esto no es lo que toda la vida hemos hecho con if y else? ¿Por qué complicarnos con excepciones?
Buena pregunta !. La respuesta "ortodoxa" es que if/else se usa para el control del flujo del programa basado en condiciones conocidas, mientras que try/catch se utiliza para manejar situaciones excepcionales o errores inesperados que pueden ocurrir durante la ejecución

  • if/else: Se usa para tomar decisiones basadas en condiciones que puedes evaluar y anticipar. Por ejemplo, verificar si un archivo existe antes de intentar abrirlo, o si una variable tiene un valor específico antes de realizar una operación.
  • try/catch: Se usa para capturar excepciones, que son errores o situaciones inesperadas que ocurren durante la ejecución del código.
Situación ¿`if/else`? ¿`try/catch`?
Leer un archivo local que tú controlas Sí (usa file_exists()) No necesario
Leer un archivo que puede no existir o depende del usuario A veces sí Puede ser útil
Conexión a base de datos (por ejemplo con PDO) No Sí (puede lanzar excepciones)
Comprobar valores antes de usarlos (existencia de claves, tipos, etc.) Sí (usa isset(), is_array()...) No
Uso de funciones antiguas que generan warnings No fiable Si defines un set_error_handler()
Control general de errores en entornos poco fiables Puede complicarse Recomendado para centralizar el manejo

Nota: En scripts donde toda la funcionalidad depende de la conexión a la base de datos, o de leer un archivo, no siempre es necesario usar try/catch. Si el fallo de conexión impide continuar, un mensaje claro y un die() pueden ser suficientes.
En cambio, en aplicaciones más complejas, el uso de try/catch permite manejar el error de forma más flexible: mostrar un mensaje personalizado, registrar el fallo en un log, redirigir al usuario, etc.

Herramientas de Depuración

PHP ofrece varias herramientas para depurar tu código:

var_dump() Muestra información detallada sobre una variable:

<?php
    $usuario = [
    'nombre' => 'María',
    'edad' => 25,
    'roles' => ['editor', 'admin']
];

var_dump($usuario);
// Salida: array(3) { ["nombre"]=> string(6) "María" ["edad"]=> int(25) ["roles"]=> array(2) { [0]=> string(6) "editor" [1]=> string(5) "admin" } }
?><

print_r() es similar a var_dump() pero con un formato más legible:

<?php
            print_r($usuario);
/* Salida:
Array
(
    [nombre] => María
    [edad] => 25
    [roles] => Array
        (
            [0] => editor
            [1] => admin
        )
)
*/
?>

Formateo de salida para depuración: Para una salida más legible en el navegador:

<?php
echo "<pre>";
print_r($usuario);
echo "</pre>";

// Alternativa con var_dump
echo "<pre>";
var_dump($usuario);
echo "</pre>";

La mayor legibilidad de print_r se refiere a la salida del resultado en una terminal. Si como es normal vas a usarlo en una web, tienes que encerrarlo entre etiquetas <pre> para conservar el formato, porque el navegador no lo hace automáticamente.

También puedes usar var_export() para obtener una representación legible de una variable:

<?php
$datos = [
    'usuario' => 'admin',
    'roles' => ['editor', 'admin'],
];
echo "<pre>";
var_export($datos);
echo "</pre>";
// Salida:
/*
array (
    'usuario' => 'admin',
    'roles' =>  array (


        0 => 'editor',
        1 => 'admin',
    ),
)*/
?>

Nota: La principal diferencia/ventaja es que var_export() devuelve una representación de la variable que es válida como código PHP, lo que permite copiarla y usarla directamente en otro script.

debug_backtrace()

Permite ver la pila de llamadas a funciones (entender qué funciones se han llamado y desde dónde):


<?php
// Esta función simula un sistema de log simple
function registrarLog($mensaje)
    {
    // debug_backtrace() devuelve un array con información sobre cómo se llegó hasta aquí
    $pila = debug_backtrace();
    // $pila[0] es la función actual (registrarLog)
    // $pila[1] es quien llamó a registrarLog (por ejemplo, ejecutarProceso)
    $llamador = $pila[1]; // Comprobamos quién invocó esta función

    // Mostramos información útil de depuración
    echo "Llamada desde archivo: " . $llamador['file'] . "\n";
    echo "En la línea: " . $llamador['line'] . "\n";
    echo "Desde la función: " . $llamador['function'] . "()\n";
    echo "Mensaje: $mensaje\n";
    }

// Una función cualquiera que llama a registrarLog
function ejecutarProceso()
    {
    registrarLog("El proceso ha comenzado.");
    }
// Iniciamos el proceso
ejecutarProceso();
?>

debug_print_backtrace()

Mientras que debug_backtrace() devuelve un array con la información, debug_print_backtrace() la imprime directamente, en un formato ya listo para ver por pantalla.

Uso de die() o exit()

una es alias de la otra. Detiene la ejecución y muestra un mensaje:

<?php
$conexion = mysqli_connect('localhost', 'usuario', 'contraseña', 'basedatos');
if (!$conexion) {
    die('Error de conexión: ' . mysqli_connect_error());
}
?>

Nota: códigos de salida: puedes llamar a exit() o die() con un número para especificar manualmente el código de salida del script. Si no indicas ninguno, PHP usa 0 (terminación correcta) por defecto:

  • exit(0) → Terminación correcta
  • exit(1) → Terminación con error
  • 2-255: Errores específicos (si lo necesitas)

Este código de salida solo tiene efecto en línea de comandos (CLI), como en scripts ejecutados desde la terminal o tareas programadas (cron). En entornos web, el navegador no ve ese valor: solo recibe lo que imprima el script antes de finalizar.

Usando error_log()

El registro de errores es crucial, especialmente en entornos de producción:

<?php
// Registra en el archivo de log predeterminado
error_log("Se produjo un error en el proceso de pago");

// Registra en un archivo específico
error_log("Error crítico en el sistema", 3, "/ruta/a/mi/archivo.log");

// Envía por email (requiere configuración de email en PHP)
error_log("Error urgente", 1, "admin@example.com");
?>

Nota: la función error_log() permite escribir mensajes personalizados en el registro de errores, incluso si PHP no ha generado un error real; esto no reemplaza a log_errors sino que lo complementa

  • log_errors = On: PHP registra automáticamente sus propios errores.
  • error_log(): El desarrollador escribe manualmente mensajes en el log.

Ambos métodos pueden usarse juntos. Si log_errors está en Off, los errores de PHP no se registran, pero error_log() seguirá funcionando.

Buenas Prácticas

  1. Nunca mostrar errores al usuario final (configura display_errors = Off).
  2. Siempre registrar errores en archivos de log (configura log_errors = On).
  3. Usar try-catch para manejar errores cuando sea aconsejable.
  4. Validar datos de entrada para prevenir errores.
  5. Crear jerarquías de excepciones para distintos tipos de errores.
  6. Usar constantes descriptivas para códigos de error.
  7. Implementar un sistema de logging estructurado.
  8. Verificar regularmente los archivos de log (y no olvides controlar periódicamente su tamaño).

Recursos Adicionales


Recuerda que un buen manejo de errores y una estrategia de depuración efectiva son componentes clave para desarrollar aplicaciones PHP robustas y mantenibles.

TOP