# Erros e exceções em PHP

> Erros e exceções em PHP: Throwable, Error e Exception, try/catch/finally, handlers globais e definições seguras para a apresentação de erros e os registos.

Fonte: https://phpsos.de/pt/php/fehler-und-exceptions/  
Atualizado em: 25/10/2026  
Prestador: PHP SOS, Assistência de emergência PHP e segurança de servidores. Ajuda imediata por WhatsApp: https://wa.me/491736494914?text=Ol%C3%A1%2C%20PHP%20SOS%2C%20precisamos%20de%20ajuda%20com%20o%20nosso%20servidor%20PHP.%20Empresa%3A%20%E2%80%A6%20Site%3A%20%E2%80%A6

**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
<?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
<?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](https://phpsos.de/pt/php/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](https://phpsos.de/pt/blog/display-errors-produktion/); como manter registos sem fuga de dados, no artigo [Registar em segurança](https://phpsos.de/pt/blog/sicher-loggen-php/).

## 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.

```ini
; 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](https://phpsos.de/pt/ratgeber/logfiles-auswerten/).

## 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](https://phpsos.de/pt/leistungen/security-monitoring/), 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

- [Manual do PHP: configuração do tratamento de erros](https://www.php.net/manual/de/errorfunc.configuration.php)
- [Manual do PHP: classe Error](https://www.php.net/manual/de/class.error.php)
- [Manual do PHP: ErrorException](https://www.php.net/manual/en/class.errorexception.php)
- [Manual do PHP: set_exception_handler](https://www.php.net/manual/en/function.set-exception-handler.php)
- [php.net: avisos de segurança](https://www.php.net/)
