Tratamento de Erros em PHP: do try/catch às Soluções Industriais
\n\nTodo desenvolvedor PHP enfrenta erros. O básico try/catch é apenas a ponta do iceberg. Neste artigo, vamos explorar técnicas avançadas de tratamento de erros que transformarão código frágil em um sistema robusto, capaz de reagir corretamente a qualquer falha.
1. Mergulho Profundo no try/catch: Bloco finally e Múltiplos catch
\n\nO try/catch padrão é a base, mas muitos esquecem o bloco finally, que é executado sempre, independentemente de ter ocorrido uma exceção. É o local ideal para fechar recursos (arquivos, conexões com banco de dados).
<?php\nfunction processFile(string $filename): void {\n $handle = fopen($filename, 'r');\n if (!$handle) {\n throw new \\RuntimeException("Não foi possível abrir o arquivo: $filename");\n }\n \n try {\n // Processamento do arquivo\n while (($line = fgets($handle)) !== false) {\n // ... lógica de processamento\n }\n } catch (\\RuntimeException $e) {\n // Tratamento específico de erros de entrada/saída\n error_log("Erro ao ler arquivo: " . $e->getMessage());\n throw $e; // Relançar para cima\n } catch (\\Exception $e) {\n // Tratamento de quaisquer outras exceções\n error_log("Erro desconhecido: " . $e->getMessage());\n throw $e;\n } finally {\n fclose($handle); // Sempre fechar o arquivo\n }\n}\n?>\n\nImportante: A ordem dos blocos catch é relevante. Exceções mais específicas (por exemplo, \\RuntimeException) devem vir antes das genéricas (\\Exception). Caso contrário, o bloco específico nunca será executado.
2. Classes de Exceção Personalizadas: Semântica e Hierarquia
\n\nAs exceções nativas (PDOException, RuntimeException) muitas vezes não são suficientemente informativas. Crie sua própria hierarquia de exceções para separar erros por tipo e simplificar seu tratamento.
\n\n<?php\n// Exceção base da aplicação\nclass AppException extends \\Exception {}\n\n// Erros de validação\nclass ValidationException extends AppException {\n private array $errors;\n \n public function __construct(array $errors, string $message = "Erro de validação") {\n parent::__construct($message, 422);\n $this->errors = $errors;\n }\n \n public function getErrors(): array {\n return $this->errors;\n }\n}\n\n// Erros de acesso\nclass AccessDeniedException extends AppException {\n public function __construct(string $message = "Acesso negado") {\n parent::__construct($message, 403);\n }\n}\n\n// Exemplo de uso\nfunction registerUser(array $data): void {\n $errors = [];\n \n if (empty($data['email'])) {\n $errors['email'] = 'Email é obrigatório';\n }\n if (empty($data['password'])) {\n $errors['password'] = 'Senha é obrigatória';\n }\n \n if (!empty($errors)) {\n throw new ValidationException($errors);\n }\n \n // Lógica de registro...\n}\n\ntry {\n registerUser(['email' => '']);\n} catch (ValidationException $e) {\n // Tratamento específico: retornar JSON com erros\n http_response_code(422);\n echo json_encode(['errors' => $e->getErrors()]);\n} catch (AccessDeniedException $e) {\n http_response_code(403);\n echo json_encode(['error' => $e->getMessage()]);\n} catch (AppException $e) {\n // Tratamento geral para todos os outros erros da aplicação\n http_response_code(500);\n echo json_encode(['error' => 'Erro interno do servidor']);\n}\n?>\n\nDica: Use códigos HTTP (400, 403, 422, 500) como códigos de exceção — isso simplifica a integração com REST APIs.
\n\n3. Manipulador Global de Erros: ErrorHandler e ShutdownHandler
\n\nNem todos os erros podem ser capturados com try/catch. Erros fatais (E_ERROR), avisos (E_WARNING) e erros de análise