Error handling: set_error_handler e tipos de erro
1. Introdução ao tratamento de erros no PHP
No PHP, erros e exceções são conceitos distintos, embora ambos representem situações anormais durante a execução. Erros são problemas tradicionais do PHP, enquanto exceções são objetos que podem ser "lançados" e "capturados" com try/catch. Historicamente, o PHP tratava erros de forma procedural, mas versões modernas permitem maior integração entre esses dois mundos.
Os tipos de erro mais comuns incluem:
- E_NOTICE: Avisos sobre código que pode causar problemas (ex: variável não definida)
- E_WARNING: Avisos mais sérios que não interrompem a execução (ex: include de arquivo inexistente)
- E_ERROR: Erros fatais que interrompem o script (ex: memória insuficiente)
A função error_reporting() controla quais níveis de erro serão reportados:
// Reportar todos os erros exceto E_NOTICE
error_reporting(E_ALL & ~E_NOTICE);
// Reportar apenas erros fatais e warnings
error_reporting(E_ERROR | E_WARNING);
Por padrão, o PHP exibe erros na saída (em desenvolvimento) ou os registra em log (em produção), dependendo das configurações de display_errors e log_errors no php.ini.
2. A função set_error_handler()
A função set_error_handler() permite substituir o manipulador padrão de erros do PHP por uma função personalizada. Sua sintaxe é:
set_error_handler(
callable $callback,
int $error_levels = E_ALL
): ?callable
O callback deve ter a seguinte assinatura:
function handler(
int $errno,
string $errstr,
string $errfile = '',
int $errline = 0,
array $errcontext = []
): bool
$errno: Nível do erro (constante como E_WARNING)$errstr: Mensagem descritiva do erro$errfile: Arquivo onde o erro ocorreu$errline: Linha do erro$errcontext: Array com todas as variáveis no escopo do erro (obsoleto no PHP 8.0+)
Se o callback retornar false, o manipulador padrão do PHP será executado após o callback.
Exemplo básico:
function meuHandler(int $errno, string $errstr, string $errfile, int $errline): bool {
echo "Erro [$errno]: $errstr em $errfile na linha $errline\n";
return true; // Suprime o manipulador padrão
}
set_error_handler('meuHandler');
// Teste
echo $variavelInexistente; // Gera E_NOTICE, mas nosso callback trata
3. Hierarquia e tipos de erro tratáveis
Nem todos os erros podem ser capturados pelo set_error_handler(). Erros capturáveis incluem:
- E_WARNING, E_NOTICE, E_USER_ERROR, E_USER_WARNING, E_USER_NOTICE
- E_DEPRECATED, E_USER_DEPRECATED
- E_STRICT (obsoleto no PHP 8.0)
Erros não capturáveis (fatais):
- E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING
- E_COMPILE_ERROR, E_COMPILE_WARNING
As constantes de erro possuem valores numéricos:
| Constante | Valor | Descrição |
|---|---|---|
| E_ERROR | 1 | Erro fatal |
| E_WARNING | 2 | Aviso |
| E_NOTICE | 8 | Notificação |
| E_USER_ERROR | 256 | Erro gerado pelo usuário |
| E_ALL | 32767 | Todos os erros |
Dentro do callback, você pode verificar o nível do erro:
function handler(int $errno, string $errstr): bool {
switch ($errno) {
case E_WARNING:
echo "Aviso: $errstr";
break;
case E_NOTICE:
echo "Nota: $errstr";
break;
case E_USER_ERROR:
echo "Erro do usuário: $errstr";
exit(1);
}
return true;
}
4. Criando manipuladores personalizados
Uma técnica poderosa é converter erros em exceções usando a classe ErrorException:
function errorToException(int $errno, string $errstr, string $errfile, int $errline): bool {
throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
}
set_error_handler('errorToException');
try {
// Código que pode gerar warning
$resultado = 10 / 0; // Division by zero
} catch (ErrorException $e) {
echo "Exceção capturada: " . $e->getMessage();
}
Para um handler robusto que diferencia ambientes:
function customErrorHandler(int $errno, string $errstr, string $errfile, int $errline): bool {
$isDev = $_ENV['APP_ENV'] === 'development';
// Log sempre
error_log("[$errno] $errstr em $errfile:$errline");
if ($isDev) {
// Exibe detalhes no desenvolvimento
echo "<pre>Erro: $errstr\nArquivo: $errfile\nLinha: $errline</pre>";
} else {
// Em produção, mostra mensagem genérica
echo "Ocorreu um erro interno. Tente novamente mais tarde.";
}
return true;
}
set_error_handler('customErrorHandler');
5. Restaurando o manipulador padrão
A função restore_error_handler() restaura o manipulador anterior da pilha:
function meuHandler($errno, $errstr) {
echo "Handler personalizado\n";
return true;
}
set_error_handler('meuHandler');
// ... código que usa o handler personalizado ...
restore_error_handler(); // Volta ao handler padrão
O PHP mantém uma pilha de manipuladores. Cada chamada a set_error_handler() empilha um novo handler, e restore_error_handler() desempilha. Isso permite múltiplos manipuladores em diferentes partes do código:
function handler1($errno, $errstr) { echo "Handler 1\n"; return true; }
function handler2($errno, $errstr) { echo "Handler 2\n"; return true; }
set_error_handler('handler1');
set_error_handler('handler2');
trigger_error("Teste"); // Chama handler2
restore_error_handler(); // Remove handler2
trigger_error("Teste"); // Chama handler1
6. Erros fatais com register_shutdown_function
O set_error_handler() não captura erros fatais como E_ERROR. Para esses casos, use register_shutdown_function() combinado com error_get_last():
function shutdownHandler() {
$error = error_get_last();
if ($error !== null && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR])) {
echo "Erro fatal capturado: {$error['message']}\n";
echo "Em {$error['file']}:{$error['line']}\n";
}
}
register_shutdown_function('shutdownHandler');
// Exemplo que gera erro fatal
// $obj = new NonExistentClass(); // Erro fatal: Class not found
Combinando ambas as funções para cobertura completa:
function errorHandler($errno, $errstr, $errfile, $errline) {
throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
}
function shutdownHandler() {
$error = error_get_last();
if ($error && in_array($error['type'], [E_ERROR, E_PARSE])) {
echo "Erro fatal: {$error['message']}";
}
}
set_error_handler('errorHandler');
register_shutdown_function('shutdownHandler');
7. Boas práticas e armadilhas comuns
Evite suprimir erros com @: O operador @ suprime erros, mas também impede o callback de ser executado. Prefira verificar condições antes de executar código propenso a erros.
// Ruim
$conteudo = @file_get_contents('arquivo.txt');
// Bom
if (file_exists('arquivo.txt')) {
$conteudo = file_get_contents('arquivo.txt');
}
Cuidado com loops infinitos: Se seu callback gerar um erro, ele chamará a si mesmo recursivamente. Sempre verifique se o código do callback é seguro:
function safeHandler($errno, $errstr) {
// Não use funções que possam gerar erros aqui
static $recursionGuard = false;
if ($recursionGuard) return false;
$recursionGuard = true;
error_log("Erro: $errstr");
$recursionGuard = false;
return true;
}
Performance: Manipuladores personalizados têm custo. Use-os apenas quando necessário e evite operações pesadas dentro do callback.
Compatibilidade: No PHP 8.x, o parâmetro $errcontext foi removido do callback. Certifique-se de que sua assinatura seja compatível.
8. Exemplo prático completo
Abaixo, um sistema completo de tratamento de erros:
<?php
class ErrorHandler {
private static bool $initialized = false;
public static function init(): void {
if (self::$initialized) return;
self::$initialized = true;
set_error_handler([self::class, 'handleError']);
register_shutdown_function([self::class, 'handleShutdown']);
}
public static function handleError(
int $errno,
string $errstr,
string $errfile = '',
int $errline = 0
): bool {
$isDev = ($_ENV['APP_ENV'] ?? 'production') === 'development';
// Log sempre
$logMessage = sprintf(
"[%s] Erro %d: %s em %s:%d",
date('Y-m-d H:i:s'),
$errno,
$errstr,
$errfile,
$errline
);
error_log($logMessage);
if ($isDev) {
// Em desenvolvimento, exibe detalhes e converte para exceção
throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
}
// Em produção, apenas loga e retorna false para o handler padrão
return false;
}
public static function handleShutdown(): void {
$error = error_get_last();
if ($error === null) return;
$fatalTypes = [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR];
if (!in_array($error['type'], $fatalTypes, true)) return;
$logMessage = sprintf(
"[FATAL] %s: %s em %s:%d",
date('Y-m-d H:i:s'),
$error['message'],
$error['file'],
$error['line']
);
error_log($logMessage);
if (($_ENV['APP_ENV'] ?? 'production') === 'development') {
echo "<h1>Erro Fatal</h1>";
echo "<p>{$error['message']}</p>";
echo "<p>Em {$error['file']}:{$error['line']}</p>";
} else {
echo "Ocorreu um erro interno. Já fomos notificados.";
}
}
}
// Inicialização
ErrorHandler::init();
// Testes
echo $variavelInexistente; // E_NOTICE tratado
$resultado = 10 / 0; // Division by zero
Este exemplo demonstra:
- Inicialização segura com guarda contra múltiplas chamadas
- Tratamento diferenciado para desenvolvimento e produção
- Log centralizado de todos os erros
- Captura de erros fatais via shutdown function
- Conversão de erros em exceções no ambiente de desenvolvimento
Lembre-se: um bom tratamento de erros não apenas evita que o usuário veja mensagens técnicas, mas também fornece informações valiosas para depuração e monitoramento do sistema.
Referências
- PHP: set_error_handler - Manual oficial — Documentação completa da função com exemplos e notas sobre compatibilidade
- PHP: ErrorException - Manual oficial — Classe para converter erros em exceções
- PHP: error_reporting - Manual oficial — Controle de níveis de erro reportados
- PHP: register_shutdown_function - Manual oficial — Função para capturar erros fatais no encerramento do script
- PHP The Right Way: Error Handling — Guia de boas práticas para tratamento de erros em PHP
- PHP: Predefined Error Constants — Lista completa de constantes de erro com seus valores e descrições
- PHP 8.0: Removido o parâmetro errcontext — Notas sobre mudanças de compatibilidade no PHP 8.0