Tratamento de erros e depuração

Após 32 aulas sobre programação, você sem dúvida já se deparou com várias dificuldades — páginas em branco, mensagens de erro incompreensíveis e a dificuldade de saber como usar o try/catch. Esta aula reúne todas as habilidades de depuração, levando você da fase de “encontrar bugs por sorte” para a de “resolução sistemática de problemas”.

1. Níveis de erro

O PHP possui três tipos de erros:

TEXT
Notice      ← "You may have overlooked something" (undefined variable, offset)
Warning     ← "The code has a problem but can continue" (include file not found)
Fatal Error ← "The code has a serious error and must stop" (calling a non-existent function)
PHP
<?php
// Notice: undefined variable (PHP continues execution)
echo $undefinedVar;  // Notice: Undefined variable $undefinedVar

// Warning: file not found (PHP continues execution)
include 'nonexistent.php';  // Warning: Failed opening...

// Fatal Error: calling a non-existent function (PHP stops)
thisFunctionDoesNotExist();  // Fatal Error: Call to undefined function
?>

2. Controle dos relatórios de erros

PHP
<?php
// Development: show all errors
error_reporting(E_ALL);
ini_set('display_errors', '1');

// Production: log but don't display
error_reporting(E_ALL);
ini_set('display_errors', '0');
ini_set('log_errors', '1');
ini_set('error_log', __DIR__ . '/php-errors.log');
?>
🔥 Erro comum: Nunca configure display_errors = On em um servidor de produção — as mensagens de erro podem expor caminhos de arquivos, senhas de bancos de dados e outras informações confidenciais. Os usuários devem ver uma página genérica de erro 500; os detalhes do erro são registrados no log.

▶ Exemplo: Configuração sensível ao ambiente

PHP
<?php
$isDev = ($_SERVER['SERVER_NAME'] ?? '') === 'localhost';

if ($isDev) {
    error_reporting(E_ALL);
    ini_set('display_errors', '1');
} else {
    error_reporting(E_ALL);
    ini_set('display_errors', '0');
    ini_set('log_errors', '1');
    ini_set('error_log', '/var/log/php/errors.log');
}
?>
▶ Experimente

3. Tratamento de exceções: try/catch/finally

PHP
<?php
function divide(float $a, float $b): float {
    if ($b === 0.0) {
        throw new Exception("Division by zero is not allowed!");
    }
    return $a / $b;
}

try {
    echo divide(10, 2) . "<br>";  // 5
    echo divide(10, 0) . "<br>";  // Throws an exception
} catch (Exception $e) {
    echo "[Caught Exception] {$e->getMessage()}<br>";
    echo "File: {$e->getFile()}<br>";
    echo "Line: {$e->getLine()}<br>";
} finally {
    echo "Whether success or failure, finally always runs (e.g., close files, release resources)<br>";
}
?>
Bloco Quando é executado
try A execução começa aqui — as exceções são monitoradas
catch É executado se uma exceção for lançada no bloco try
finally Sempre é executado, independentemente das circunstâncias (geralmente usado para limpeza)

4. Exceções personalizadas

PHP
<?php
class ValidationException extends Exception {
    private array $errors;
    
    public function __construct(array $errors, string $message = "Validation failed") {
        parent::__construct($message);
        $this->errors = $errors;
    }
    
    public function getErrors(): array {
        return $this->errors;
    }
}

class UserNotFoundException extends Exception {}

// Using custom exceptions
function findUser(int $id): array {
    $users = [1 => ['name' => 'John'], 2 => ['name' => 'Jane']];
    
    if (!isset($users[$id])) {
        throw new UserNotFoundException("User ID {$id} does not exist");
    }
    return $users[$id];
}

function validateUsername(string $name): void {
    $errors = [];
    if ($name === '') $errors[] = 'Username cannot be empty';
    if (strlen($name) > 50) $errors[] = 'Username is too long';
    if (!empty($errors)) throw new ValidationException($errors);
}

// Unified exception handling
try {
    validateUsername('');
    $user = findUser(99);
} catch (ValidationException $e) {
    echo "[Validation Failed]<br>" . implode("<br>", $e->getErrors());
} catch (UserNotFoundException $e) {
    echo "[User Not Found] {$e->getMessage()}";
} catch (Exception $e) {
    echo "[Unknown Error] {$e->getMessage()}";
}
?>

5. Registro em log

PHP
<?php
class AppLogger {
    public function __construct(
        private string $logDir
    ) {
        if (!is_dir($logDir)) {
            mkdir($logDir, 0755, true);
        }
    }
    
    public function info(string $message, array $context = []): void {
        $this->log('INFO', $message, $context);
    }
    
    public function error(string $message, array $context = []): void {
        $this->log('ERROR', $message, $context);
    }
    
    private function log(string $level, string $message, array $context): void {
        $date = date('Y-m-d');
        $time = date('H:i:s');
        
        $line = "[{$time}] [{$level}] {$message}";
        if (!empty($context)) {
            $line .= ' ' . json_encode($context, JSON_UNESCAPED_UNICODE);
        }
        $line .= "\n";
        
        $file = "{$this->logDir}/app-{$date}.log";
        file_put_contents($file, $line, FILE_APPEND | LOCK_EX);
    }
}

$log = new AppLogger(__DIR__ . '/logs');
$log->info('User logged in', ['username' => 'John', 'ip' => $_SERVER['REMOTE_ADDR'] ?? '']);
$log->error('Payment failed', ['order_id' => 12345, 'reason' => 'Insufficient balance']);
?>

6. Técnicas de depuração

▶ Exemplo: Funções utilitárias de depuração

PHP
<?php
// Most commonly used debug output
$data = ['name' => 'John', 'skills' => ['PHP', 'MySQL']];

var_dump($data);  // Detailed output (type + value)
// array(2) { ["name"]=> string(4) "John" ["skills"]=> ... }

print_r($data);   // Concise output
// Array ( [name] => John [skills] => Array ( [0] => PHP [1] => MySQL ) )

// Formatted var_dump (recommended)
echo '<pre>'; print_r($data); echo '</pre>';

// Debug and terminate
var_dump($data); exit;  // or die(var_dump($data));
?>
▶ Experimente

(1) Referência rápida para depuração

PHP
<?php
// Value + type
var_dump($variable);

// Only show in development
if ($isDev) {
    echo '<pre>'; var_dump($data); echo '</pre>';
}

// Log it
error_log("Debug info: " . print_r($data, true));

// Check if execution reaches a certain line
echo "HERE"; exit;  // or error_log("Reached this line");

// View the call stack
debug_print_backtrace();

// Check if a variable is set
var_dump(isset($var), empty($var));
?>

7. Buffer de saída e manipuladores de erro personalizados

PHP
<?php
// Output buffering: capture echo/print output
ob_start();
echo "This text won't be sent directly to the browser.";
$content = ob_get_clean();  // Get and clear the buffer
// $content is now "This text won't be sent directly to the browser."

// Custom error handler
function myErrorHandler(int $errno, string $errstr, string $errfile, int $errline): bool {
    $message = "[{$errno}] {$errstr} in {$errfile}:{$errline}";
    error_log($message);
    
    if (ini_get('display_errors')) {
        echo "<div style='background:#ffebee;padding:10px;margin:10px'>
            <strong>PHP Error:</strong> {$message}
        </div>";
    }
    return true;  // Prevent PHP's default handling
}
set_error_handler('myErrorHandler');

// Test the custom error handler
echo $undefined;  // Now displays in a nice HTML box
?>

❓ Perguntas Frequentes

P: Quando devo usar as verificações try/catch em vez de if? R: Use if para erros esperados (o usuário não preencheu seu e-mail). Use try/catch para erros inesperados (queda na conexão com o banco de dados, perda de permissão de arquivo). if significa “Sei que isso pode dar errado”, enquanto catch significa “Não sei o que pode dar errado, mas se ocorrer uma falha, vou resolver o problema”.

P: Como faço para depurar uma página em branco? R: A causa mais comum é um erro fatal com display_errors = Off. Passos para solucionar o problema: (1) adicione error_reporting(E_ALL); ini_set('display_errors', '1');; (2) verifique o log de erros do PHP; (3) insira echo "1"; exit; em intervalos para descobrir em qual linha a execução é interrompida.

P: O que é o Xdebug? Eu preciso dele? R: O Xdebug é uma extensão profissional de depuração para PHP — permite definir pontos de interrupção, percorrer o código passo a passo e inspecionar variáveis. Para quem está começando, o var_dump já é suficiente. Quando seu projeto ultrapassar 1.000 linhas, vale a pena instalá-lo. A combinação do VS Code com o Xdebug é tão prática quanto as Ferramentas de Desenvolvimento do navegador.

📖 Resumo

📝 Exercícios

  1. Crie uma página de calculadora de divisão que utilize try/catch para lidar com a divisão por zero e exiba uma mensagem amigável.
  2. Crie uma classe base AppException com as subclasses ValidationException e DatabaseException. Utilize-as na lógica de registro de usuários e identifique-as separadamente.
  3. Adicione registros de log ao exercício 2: quando forem capturadas exceções, grave os detalhes do erro (carimbo de data/hora, arquivo, número da linha) em um arquivo de log.
Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%