Situação atual: PHP2Shell. Duas vulnerabilidades críticas do PHP (CVSS 9,2) permitem executar código malicioso sem autenticação. Patches disponíveis desde 21/10/2026.O que fazer agora →

Erros e exceções em PHP

O PHP conhece mensagens de erro clássicas e exceções. Esta página explica a hierarquia Throwable, o tratamento limpo de erros, definições seguras para produção e a medida do PHP2Shell sobre fatal_error_backtraces.

Atualizado em 6 min de leituraConhecimento PHP

Em resumo: O PHP comunica problemas de duas maneiras: como erros clássicos, como avisos e notificações, controlados através de error_reporting, e como exceções, que se apanham com try e catch. Desde o PHP 7, também muitos erros internos lançam um objeto da classe Error; em conjunto com Exception, implementa a interface Throwable. Em produção, vale isto: registar os erros e nunca os mostrar no navegador. No PHP 8.5, por causa do PHP2Shell, fatal_error_backtraces=0 faz também parte das medidas provisórias.

Dois mundos: erros clássicos e exceções

O PHP vem de uma época em que os erros eram sobretudo emitidos como mensagem: notificações (E_NOTICE), avisos (E_WARNING), funções obsoletas (E_DEPRECATED) e erros fatais (E_ERROR). Muitas destas mensagens deixam o script continuar, muitas vezes com dados errados ou vazios.

As exceções funcionam de outra forma. Interrompem o fluxo normal e saltam para o bloco catch adequado seguinte. Se nenhum for encontrado, o script termina. Para quem decide, a diferença é importante: um aviso silencioso pode fazer com que uma encomenda seja guardada de forma incompleta, sem que ninguém repare. Uma exceção torna o problema visível e tratável.

Um benefício subestimado das mensagens clássicas são as notificações do tipo E_DEPRECATED. Anunciam que um recurso da linguagem deixará de existir numa versão futura do PHP. Quem as recolhe no registo e as avalia com regularidade sabe, antes de uma atualização, que pontos têm de ser ajustados, e não precisa de testar durante muito tempo perante uma vulnerabilidade de segurança urgente.

A hierarquia Throwable

Tudo o que se pode lançar com throw e apanhar com catch implementa Throwable. Por baixo há dois ramos:

Classe Finalidade Exemplos de subclasses
Exception erros previsíveis da aplicação e das extensões ErrorException, PDOException, classes próprias
Error erros internos do PHP TypeError, ValueError, ArgumentCountError, ArithmeticError, DivisionByZeroError, UnhandledMatchError

As classes de erro próprias derivam de Exception, não de Error. Desde o PHP 8.0, a maioria das funções internas lança, perante parâmetros inválidos, um TypeError ou ValueError, em vez de apenas emitir um aviso. strlen([]) é um desses casos.

try, catch e finally na prática

<?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 o PHP 8.0, a variável no catch é opcional
        $log->error('Zahlungsdienst nicht erreichbar', ['bestellung' => $b->id()]);
        return false;
    } finally {
        $dienst->verbindungSchliessen();
    }
}

Várias classes podem ser reunidas num só catch com |. O bloco finally corre sempre, tenha ou não ocorrido um erro. Desde o PHP 8.0, o throw é além disso uma expressão e pode, por exemplo, surgir a seguir a ??: $id = $_GET['id'] ?? throw new InvalidArgumentException('id fehlt');.

Handlers globais: set_error_handler e set_exception_handler

Duas funções apanham o que de outro modo escaparia. O set_exception_handler() regista uma função para os Throwables não apanhados. Segundo o manual, o script termina depois de ela ser chamada; serve, portanto, para o registo e para uma página de erro neutra, não para continuar a trabalhar.

Revelou-se útil um handler que escreve no registo o erro completo, com a hora e um identificador aleatório, e mostra ao visitante apenas uma página neutra com esse identificador. O suporte consegue então encontrar o caso no registo, sem que detalhes internos cheguem ao exterior.

O set_error_handler() apanha erros clássicos. O manual mostra como convertê-los numa ErrorException, para que os avisos sejam tratados como exceções:

<?php
set_error_handler(function (int $nr, string $text, string $datei, int $zeile): bool {
    if (!(error_reporting() & $nr)) {
        return false; // excluído por error_reporting
    }
    if ($nr === E_DEPRECATED || $nr === E_USER_DEPRECATED) {
        return false; // não escalar avisos sobre recursos obsoletos
    }
    throw new ErrorException($text, 0, $nr, $datei, $zeile);
});

Os erros fatais, como um excesso de memória, não se podem apanhar por este caminho. O que acontece por dentro num excesso explica-o a página memory_limit e Out-of-Memory.

Configurar a apresentação de erros e o registo

As definições mais importantes, com os valores predefinidos segundo o manual:

Definição Predefinição Recomendação para produção
error_reporting E_ALL desde o PHP 8.0.0 E_ALL
display_errors 1 Off
display_startup_errors 1 desde o PHP 8.0.0 Off
log_errors 0 On
error_log vazio (registo do servidor web ou da SAPI) ficheiro próprio fora da raiz web
fatal_error_backtraces 1, novo no PHP 8.5.0 temporariamente 0 (PHP2Shell)

Desde o PHP 8.2.0, o error_log_mode define as permissões do ficheiro de registo, por predefinição 0644. Se os registos contiverem dados pessoais ou caminhos, é sensato usar um valor mais restritivo, para que outros utilizadores do servidor não os possam ler. Com ignore_repeated_errors é possível agrupar mensagens idênticas do mesmo ficheiro e da mesma linha, para que um ciclo de erros não inunde o registo.

O php.ini-production fornecido já define o display_errors como Off. Segundo o manual, o ini_set('display_errors', ...) em tempo de execução não tem efeito sobre erros fatais, e os erros de sintaxe ocorrem antes de qualquer ini_set() chegar a correr. Estes valores pertencem, por isso, à configuração do servidor. Os fundamentos constam do artigo Desativar a apresentação de erros em produção; como manter registos sem fuga de dados, no artigo Registar em segurança.

Tratamento de erros e PHP2Shell

Para o PHP 8.5, o php.net indica como uma das medidas provisórias contra o PHP2Shell definir fatal_error_backtraces=0 e reiniciar depois o PHP. As restantes medidas dizem respeito a memory_limit, post_max_size e session.upload_progress.enabled. Nenhuma delas substitui a atualização para o PHP 8.5.12, 8.4.27, 8.3.36 ou 8.2.35.

; apenas PHP 8.5, no php.ini ou no pool FPM, depois reiniciar todos os workers
fatal_error_backtraces = 0

Depois do PHP2Shell, os registos de erros são também um importante meio de prova. Se, a partir de 20/10/2026, se acumularem erros de memória ou workers interrompidos, isso pode indicar tentativas de ataque, mas pode também ter causas inofensivas. Conserve os registos e avalie-os em conjunto com os registos de acesso, como descrito no guia Avaliar os registos do servidor web após um ataque.

Como ajuda concretamente a PHP SOS?

  • Verificamos as definições efetivas de apresentação de erros e de registo em cada site e ajustamo-las para produção.
  • Aplicamos as medidas provisórias do PHP2Shell de acordo com a versão do PHP e reiniciamos todos os workers.
  • Preservamos os registos de erros e de acesso a partir de 20/10/2026 e avaliamo-los quanto a anomalias.
  • No âmbito da monitorização de segurança, comunicamos acumulações invulgares de erros, em vez de as deixar perder-se no registo.

Perguntas frequentes

Qual é a diferença entre Error e Exception?

Ambas implementam a interface Throwable. A Exception destina-se a erros que uma aplicação espera e trata, por exemplo uma ligação à base de dados falhada. O Error representa erros internos do PHP, como um TypeError ou uma divisão por zero.

O display_errors deve estar ativo em produção?

Não. As mensagens de erro no navegador revelam caminhos, consultas e, por vezes, dados de acesso. Em produção, o display_errors deve estar em Off e o log_errors em On, para que os erros fiquem no registo e não cheguem ao visitante.

É possível apanhar um excesso de memória com try/catch?

Não. Se um script excede o memory_limit, o PHP termina-o com um erro fatal que não pode ser apanhado como exceção. Aparece apenas no registo de erros, desde que o log_errors esteja ativo.

O que é o fatal_error_backtraces?

É uma definição que existe desde o PHP 8.5.0 e que acrescenta um backtrace aos erros fatais. Está ativa por predefinição. Como medida provisória contra o PHP2Shell, o php.net indica, para o PHP 8.5, defini-la como 0 e reiniciar depois o PHP.

Fontes

Contacto imediato

Cada hora conta. Escreva-nos agora.

Descreva brevemente o sistema, a versão do PHP e o que observou. Entramos em contacto consigo, esclarecemos a situação e trabalhamos remotamente até o incidente estar concluído e documentado.

Ajuda imediata