W3docs

PHP Exception

Aprenda exceções PHP: como lançar e capturar erros com try/catch/finally, métodos da classe Exception, multi-catch e classes de exceção personalizadas.

O que é uma Exceção?

Uma exceção é um objeto que representa um erro ou uma condição inesperada que interrompe o fluxo normal de um programa. Em vez de deixar um problema travar o script silenciosamente, você lança uma exceção no ponto onde algo deu errado e, em seguida, a captura em algum lugar que saiba como recuperar, registrar ou reportar o problema.

Esta página cobre como lançar e capturar exceções com try/catch/finally, os métodos que todo objeto de exceção fornece, como capturar vários tipos de exceção e como escrever suas próprias classes de exceção.

As exceções são úteis sempre que uma função não pode continuar com significado: entrada de usuário inválida, uma conexão de banco de dados com falha, um arquivo ausente ou um valor fora do intervalo permitido. Elas mantêm a lógica de tratamento de erros separada da lógica normal, de modo que o "caminho feliz" permanece legível.

throw new Exception('Something went wrong');

No PHP 7 e versões posteriores, tanto Exception quanto Error implementam a interface Throwable, portanto qualquer coisa que possa ser lançada é um Throwable.

Lançando e Capturando com try / catch / finally

Você envolve o código arriscado em um bloco try. Se uma instrução dentro lançar uma exceção, o PHP para de executar o restante do bloco try e salta para o primeiro catch correspondente. O bloco finally opcional sempre é executado depois — independentemente de uma exceção ter sido lançada ou não — o que o torna o lugar certo para liberar recursos (fechar um arquivo, um identificador de banco de dados, um bloqueio).

<?php
function divide($a, $b) {
    if ($b === 0) {
        throw new InvalidArgumentException('Division by zero is not allowed.');
    }
    return $a / $b;
}

try {
    echo divide(10, 2), "\n";   // 5
    echo divide(10, 0), "\n";   // throws — the next line never runs
} catch (InvalidArgumentException $e) {
    echo 'Caught: ' . $e->getMessage() . "\n";
} finally {
    echo "Done.\n";
}

Saída:

5
Caught: Division by zero is not allowed.
Done.

Observe que o segundo echo divide(...) nunca é impresso, porque o lançamento aborta o bloco try imediatamente. O bloco finally ainda é executado.

Lendo Informações de uma Exceção

Todo objeto de exceção carrega detalhes úteis. Os métodos mais comuns, todos herdados da classe base Exception, são:

MétodoRetorna
getMessage()A mensagem de erro legível por humanos
getCode()O código de erro inteiro que você passou ao construtor
getFile()O arquivo onde a exceção foi criada
getLine()O número de linha onde foi criada
getTraceAsString()A pilha de chamadas como string, útil para registro
getPrevious()A exceção anterior, quando uma envolve outra
<?php
class InsufficientFundsException extends Exception {}

class Account {
    private float $balance;
    public function __construct(float $balance) { $this->balance = $balance; }

    public function withdraw(float $amount): void {
        if ($amount > $this->balance) {
            throw new InsufficientFundsException(
                "Cannot withdraw $amount; balance is {$this->balance}.",
                100 // a custom error code
            );
        }
        $this->balance -= $amount;
    }
}

$account = new Account(50);
try {
    $account->withdraw(80);
} catch (InsufficientFundsException $e) {
    echo $e->getMessage() . "\n";  // Cannot withdraw 80; balance is 50.
    echo 'Code: ' . $e->getCode() . "\n"; // Code: 100
}

Saída:

Cannot withdraw 80; balance is 50.
Code: 100

Capturando Múltiplos Tipos de Exceção

Um único try pode ter vários blocos catch, verificados de cima para baixo — o primeiro cujo tipo corresponde à exceção lançada vence. Se dois tipos de exceção não relacionados devem ser tratados da mesma forma, combine-os em um bloco com o operador | (pipe) em vez de duplicar o código.

<?php
function parseAge(string $input): int {
    if (!is_numeric($input)) {
        throw new TypeError("'$input' is not a number.");
    }
    $age = (int) $input;
    if ($age < 0) {
        throw new RangeException("Age cannot be negative.");
    }
    return $age;
}

foreach (['42', 'abc', '-5'] as $value) {
    try {
        echo parseAge($value) . "\n";
    } catch (TypeError | RangeException $e) {
        echo get_class($e) . ': ' . $e->getMessage() . "\n";
    }
}

Saída:

42
TypeError: 'abc' is not a number.
RangeException: Age cannot be negative.

A ordem importa: como as exceções PHP formam uma hierarquia, coloque os tipos mais específicos primeiro e os mais amplos (como Exception ou Throwable) por último; caso contrário, o bloco amplo capturará tudo antes que o específico tenha chance.

Criando Exceções Personalizadas

Além das classes internas, você pode definir seus próprios tipos de exceção estendendo Exception (ou uma classe interna mais específica como RuntimeException). Uma classe dedicada torna seus blocos catch expressivos — você pode reagir ao seu erro especificamente — e permite anexar dados extras.

No exemplo Account acima, InsufficientFundsException é uma exceção personalizada. Muitas vezes uma subclasse vazia é suficiente; adicione métodos apenas quando precisar de comportamento extra:

<?php
class ValidationException extends Exception {
    private array $errors;

    public function __construct(string $message, array $errors = []) {
        parent::__construct($message);
        $this->errors = $errors;
    }

    public function getErrors(): array {
        return $this->errors;
    }
}

Se você sobrescrever o construtor, sempre chame parent::__construct() para que a mensagem, o código e a exceção anterior sejam configurados corretamente.

Capturando Throwable. Para tratar tanto exceções comuns quanto erros no nível do motor (como um TypeError de um tipo de argumento errado), capture Throwable. É o "captura-tudo" mais seguro, mas use-o como último recurso para não engolir acidentalmente bugs que você deveria corrigir:

try {
    // risky code
} catch (Throwable $e) {
    error_log($e->getMessage());
}

Boas Práticas

  • Capture apenas o que você pode tratar. Deixe as exceções das quais você não pode se recuperar propagar até um handler central.
  • Lance cedo, capture tarde. Lance no exato ponto em que um valor se torna inválido; capture onde você pode realmente responder.
  • Use tipos específicos. Subclasses personalizadas ou internas são melhores do que uma Exception simples para o fluxo de controle.
  • Escreva mensagens informativas e use getCode() para categorização legível por máquina.
  • Registre, não silencie. Grave exceções com error_log() em vez de engoli-las. Para exceções não capturadas, registre um handler global com set_exception_handler().
  • Não use exceções para o fluxo normal. Reserve-as para condições genuinamente excepcionais.

Como o Controle Flui

O diagrama abaixo mostra o caminho que a execução percorre através de uma estrutura try/catch/finally:

graph TD
  Try[Try Block] -->|No Exception| Finally[Finally Block]
  Try -->|Exception Thrown| Catch[Catch Block]
  Catch --> Finally

Prática

Prática
O que é verdade sobre o tratamento de exceções PHP?
O que é verdade sobre o tratamento de exceções PHP?
Was this page helpful?