Tag: BackendDevelopment

  • Guia Definitivo: Como Debugar PHP 5, 7 e 8 em Produção Como um Sênior

    O texto ficou um pouco grande porque inclui alguns exemplos de código, caso alguém precise. Espero que te ajude a sobreviver e mizar algumas horas ou ter mais horas para descançar.

    Trabalhar com múltiplas versões de PHP simultaneamente é como ser um arqueólogo digital – você precisa conhecer as ferramentas de cada era. Este guia cobre desde o veterano PHP 5 até o moderno PHP 8, com técnicas testadas em produção real.

    Sumário

    O Desafio das Múltiplas Versões

    Vamos ser honestos: você provavelmente mantém sistemas em PHP 5.6 (aquele legado que “funciona, não mexe”), migrou alguns para PHP 7.4, e está experimentando o PHP 8. Cada versão tem suas peculiaridades, e debugar requer estratégias diferentes.

    1. Diagnóstico Inicial Por Versão

    Comandos Essenciais Para Cada PHP

    # Descobrindo com qual versão você está lidando
    php -v
    
    # PHP 5.x - O veterano
    php5 -m | grep -E 'xdebug|opcache|apc'
    php5 -d display_errors=1 script.php
    
    # PHP 7.x - O equilibrado
    php7.4 -i | grep -E 'error|memory|time'
    php7.4 -d opcache.enable=0 script.php
    
    # PHP 8.x - O moderno
    php8.2 -i | grep -i jit
    php8.2 -d zend.assertions=1 script.php

    Tabela de Recursos de Debug por Versão

    FeaturePHP 5.6PHP 7.0-7.4PHP 8.0+
    error_get_last()
    Throwable
    get_debug_type()
    str_contains()
    Typed Properties⚠️ (7.4+)
    Match Expression
    JIT Debugging

    2. Estratégias de Debug Por Versão

    PHP 5 – Modo Sobrevivência

    // PHP 5.6 - Debug old school
    class LegacyDebugger {
        public static function trace($var, $label = '') {
            $backtrace = debug_backtrace();
            $file = basename($backtrace[0]['file']);
            $line = $backtrace[0]['line'];
    
            error_log(sprintf(
                "[%s] %s:%d - %s: %s",
                date('Y-m-d H:i:s'),
                $file,
                $line,
                $label,
                print_r($var, true)
            ));
        }
    
        // Porque PHP 5 não tem finally em algumas versões
        public static function safeExecute($callback) {
            $error = null;
            try {
                return call_user_func($callback);
            } catch (Exception $e) {
                $error = $e;
            }
    
            // Cleanup code aqui
            if ($error) {
                throw $error;
            }
        }
    }
    
    // Tratamento de erro fatal no PHP 5
    register_shutdown_function(function() {
        $error = error_get_last();
        if ($error && ($error['type'] === E_ERROR || $error['type'] === E_PARSE)) {
            error_log("FATAL: " . json_encode($error));
            // Enviar alerta
        }
    });

    PHP 7 – Poder e Flexibilidade

    // PHP 7.x - Recursos modernos
    class ModernDebugger {
        // Uso de Throwable (PHP 7+)
        public static function safeCatch(callable $callback) {
            try {
                return $callback();
            } catch (\Throwable $e) {
                self::logException($e);
    
                // PHP 7.4+ tem getTrace() melhorado
                if (PHP_VERSION_ID >= 70400) {
                    $trace = $e->getTraceAsString();
                } else {
                    $trace = self::formatTrace($e->getTrace());
                }
    
                error_log("Stack: " . $trace);
                throw $e;
            }
        }
    
        // Type hints disponíveis
        public static function validateData(?array $data): bool {
            // Null coalescing operator (PHP 7+)
            $required = $data['required'] ?? false;
    
            // Spaceship operator para comparação
            $priority = $data['priority'] ?? 0;
            return $priority <=> 100;
        }
    
        // Group use declarations (PHP 7+)
        public static function checkMemory(): void {
            $usage = memory_get_usage(true);
            $peak = memory_get_peak_usage(true);
    
            // PHP 7+ tem opcache melhorado
            if (function_exists('opcache_get_status')) {
                $opcache = opcache_get_status();
                error_log("OPcache hits: " . ($opcache['opcache_statistics']['hits'] ?? 0));
            }
        }
    }

    PHP 8 – Arsenal Completo

    // PHP 8.x - Recursos avançados
    class AdvancedDebugger {
        // Union types (PHP 8+)
        public static function debug(mixed $data, string|int $level = 'info'): void {
            // Match expression (PHP 8+)
            $prefix = match($level) {
                'error', 1 => '🔴 ERROR',
                'warning', 2 => '🟠 WARNING',
                'info', 3 => '🔵 INFO',
                default => '⚪ DEBUG'
            };
    
            // get_debug_type() para tipo preciso (PHP 8+)
            $type = get_debug_type($data);
    
            // Named arguments (PHP 8+)
            self::writeLog(
                message: "$prefix [$type]: " . print_r($data, true),
                timestamp: time(),
                backtrace: debug_backtrace(limit: 3)
            );
        }
    
        // Attributes para metadados (PHP 8+)
        #[Deprecated("Use debug() instead")]
        public static function oldDebug($data): void {
            self::debug($data);
        }
    
        // Constructor property promotion (PHP 8+)
        public function __construct(
            private string $logPath = '/tmp/debug.log',
            private bool $verbose = false
        ) {}
    
        // Nullsafe operator (PHP 8+)
        public static function safeAccess(?object $obj): void {
            // Não precisa mais de isset()
            $value = $obj?->property?->subProperty;
    
            // String functions novas
            if (str_contains($value ?? '', 'error')) {
                if (str_starts_with($value, 'ERROR:')) {
                    // JIT compile info
                    if (opcache_get_status()['jit']['enabled'] ?? false) {
                        error_log("JIT enabled - checking optimization");
                    }
                }
            }
        }
    }

    3. Problemas Específicos Por Versão

    PHP 5 – Os Clássicos

    // Magic quotes (removido no PHP 5.4+)
    if (get_magic_quotes_gpc()) {
        $_POST = array_map('stripslashes', $_POST);
    }
    
    // mysql_* functions (deprecated)
    if (!function_exists('mysqli_connect')) {
        // Fallback para mysql antigo
        $conn = mysql_connect($host, $user, $pass);
        mysql_select_db($db);
    } else {
        $conn = mysqli_connect($host, $user, $pass, $db);
    }
    
    // Erros de referência
    $arr = array();
    // PHP 5: Notice apenas
    // PHP 7+: Warning
    $value = &$arr['nonexistent']['key'];

    PHP 7 – Mudanças Breaking

    // Division by zero behavior mudou
    try {
        // PHP 5: Warning e retorna false
        // PHP 7: DivisionByZeroError
        $result = 10 / 0;
    } catch (\DivisionByZeroError $e) {
        error_log("Division by zero caught");
    }
    
    // Type declarations mais rígidas
    declare(strict_types=1); // PHP 7+
    
    function calculate(int $a, int $b): int {
        return $a + $b;
    }
    
    // PHP 7.0: TypeError se passar string
    // calculate("10", "20"); // Fatal error
    
    // Uniform variable syntax
    // PHP 5 vs PHP 7 interpretação diferente
    $foo = 'bar';
    $bar = ['baz' => 'value'];
    // PHP 5: ${$foo['baz']}
    // PHP 7: ($$foo)['baz']

    PHP 8 – Novos Comportamentos

    // Mudanças em comparação
    // PHP 7: 0 == "string" é true
    // PHP 8: 0 == "string" é false
    
    // Consistent type errors
    try {
        // PHP 8 lança TypeError consistentemente
        strlen([]); // TypeError: strlen(): Argument #1 must be of type string
    } catch (\TypeError $e) {
        error_log("Type error: " . $e->getMessage());
    }
    
    // Named parameters mudaram dynamic calls
    $params = ['param2' => 'value2', 'param1' => 'value1'];
    // PHP 8: ordem não importa com named parameters
    call_user_func_array('myFunction', $params);
    
    // Warnings promovidos a Errors
    try {
        // PHP 7: Warning
        // PHP 8: Error
        $undefined['key'];
    } catch (\Error $e) {
        error_log("Undefined variable access");
    }

    4. Ferramentas de Debug Por Versão

    Configuração XDebug Otimizada

    ; PHP 5 - XDebug 2.x
    xdebug.remote_enable=1
    xdebug.remote_autostart=1
    xdebug.remote_host=localhost
    xdebug.profiler_enable_trigger=1
    xdebug.trace_enable_trigger=1
    
    ; PHP 7 - XDebug 2.x/3.x
    xdebug.mode=debug,profile,trace ; XDebug 3
    xdebug.client_host=localhost     ; XDebug 3
    xdebug.start_with_request=trigger ; XDebug 3
    xdebug.output_dir=/tmp/xdebug
    
    ; PHP 8 - XDebug 3.x
    xdebug.mode=develop,debug,profile
    xdebug.start_with_request=yes
    xdebug.discover_client_host=true
    xdebug.log_level=0

    Profiling Por Versão

    class VersionAwareProfiler {
        public static function profile(callable $callback, string $label = 'Operation') {
            $phpVersion = PHP_MAJOR_VERSION;
    
            // Início
            $startTime = microtime(true);
            $startMemory = memory_get_usage(true);
    
            // Execução com profiling específico
            if ($phpVersion >= 8) {
                // PHP 8: JIT stats
                $jitBefore = opcache_get_status()['jit'] ?? [];
            }
    
            $result = $callback();
    
            // Fim
            $endTime = microtime(true);
            $endMemory = memory_get_usage(true);
    
            // Relatório
            $report = [
                'php_version' => PHP_VERSION,
                'label' => $label,
                'time_ms' => round(($endTime - $startTime) * 1000, 2),
                'memory_mb' => round(($endMemory - $startMemory) / 1024 / 1024, 2),
                'peak_memory_mb' => round(memory_get_peak_usage(true) / 1024 / 1024, 2)
            ];
    
            if ($phpVersion >= 8) {
                $jitAfter = opcache_get_status()['jit'] ?? [];
                $report['jit_buffer_free'] = $jitAfter['buffer_free'] ?? 'N/A';
            }
    
            error_log("PROFILE: " . json_encode($report));
            return $result;
        }
    }

    5. Migração Segura Entre Versões

    Checklist de Compatibilidade

    class CompatibilityChecker {
        public static function checkEnvironment(): array {
            $report = [
                'current_version' => PHP_VERSION,
                'version_id' => PHP_VERSION_ID,
                'checks' => []
            ];
    
            // PHP 5 para 7
            if (PHP_MAJOR_VERSION == 5) {
                $report['checks']['mysql_extension'] = extension_loaded('mysql') ? '⚠️ Deprecated' : '✅';
                $report['checks']['ereg_functions'] = function_exists('ereg') ? '⚠️ Removed in PHP 7' : '✅';
                $report['checks']['asp_tags'] = ini_get('asp_tags') ? '⚠️ Removed in PHP 7' : '✅';
            }
    
            // PHP 7 para 8
            if (PHP_MAJOR_VERSION == 7) {
                $report['checks']['libxml_disable_entity_loader'] = 
                    function_exists('libxml_disable_entity_loader') ? '⚠️ Deprecated in PHP 8' : '✅';
                $report['checks']['each_function'] = 
                    function_exists('each') ? '⚠️ Removed in PHP 8' : '✅';
                $report['checks']['create_function'] = 
                    function_exists('create_function') ? '⚠️ Removed in PHP 8' : '✅';
            }
    
            // Recursos disponíveis
            $report['features'] = [
                'opcache' => extension_loaded('opcache'),
                'apcu' => extension_loaded('apcu'),
                'xdebug' => extension_loaded('xdebug'),
                'jit' => PHP_MAJOR_VERSION >= 8 && opcache_get_status()['jit']['enabled'] ?? false
            ];
    
            return $report;
        }
    }

    6.Scripts de Emergência Multi-Versão

    🚨 Kit de Sobrevivência Universal

    /**
     * emergency-debug.php
     * Funciona em PHP 5.6+
     */
    
    // Detecção de versão e configuração
    $phpVersion = PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION;
    $isLegacy = PHP_MAJOR_VERSION < 7;
    $isModern = PHP_MAJOR_VERSION >= 8;
    
    // Configuração segura para qualquer versão
    error_reporting(E_ALL);
    ini_set('display_errors', 1);
    ini_set('log_errors', 1);
    ini_set('error_log', '/tmp/emergency-' . date('Y-m-d') . '.log');
    
    // Handler universal de erros
    set_error_handler(function($errno, $errstr, $errfile, $errline) use ($phpVersion) {
        $levels = [
            E_ERROR => 'ERROR',
            E_WARNING => 'WARNING',
            E_NOTICE => 'NOTICE',
            E_DEPRECATED => 'DEPRECATED'
        ];
    
        $level = $levels[$errno] ?? 'UNKNOWN';
        $message = sprintf(
            "[PHP %s] %s: %s in %s:%d",
            $phpVersion,
            $level,
            $errstr,
            basename($errfile),
            $errline
        );
    
        error_log($message);
    
        // Em desenvolvimento, mostra na tela
        if (ini_get('display_errors')) {
            echo "<pre>$message</pre>\n";
        }
    
        return true; // Previne handler padrão
    });
    
    // Exception handler compatível
    set_exception_handler(function($e) use ($isLegacy, $isModern) {
        $class = get_class($e);
        $message = $e->getMessage();
        $file = $e->getFile();
        $line = $e->getLine();
    
        $output = "Uncaught $class: $message\n";
        $output .= "File: $file:$line\n";
    
        if ($isModern) {
            // PHP 8 tem mais informações
            $output .= "Type: " . get_debug_type($e) . "\n";
        }
    
        $output .= "Stack trace:\n" . $e->getTraceAsString();
    
        error_log($output);
    
        // Resposta HTTP apropriada
        if (!headers_sent()) {
            header('HTTP/1.1 500 Internal Server Error');
        }
    
        echo "<h1>500 - Internal Server Error</h1>";
        if (ini_get('display_errors')) {
            echo "<pre>$output</pre>";
        }
    });
    
    // Shutdown function para pegar erros fatais
    register_shutdown_function(function() use ($phpVersion) {
        $error = error_get_last();
        if ($error && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR])) {
            $message = sprintf(
                "[PHP %s] FATAL: %s in %s:%d",
                $phpVersion,
                $error['message'],
                $error['file'],
                $error['line']
            );
    
            error_log($message);
    
            // Tentar enviar alerta (adapte para seu sistema)
            if (function_exists('mail')) {
                @mail(
                    'dev-team@company.com',
                    'FATAL ERROR - PHP ' . $phpVersion,
                    $message
                );
            }
        }
    });
    
    // Informações do ambiente
    echo "<h2>Debug Information - PHP $phpVersion</h2>";
    echo "<pre>";
    echo "Memory Usage: " . round(memory_get_usage(true) / 1024 / 1024, 2) . " MB\n";
    echo "Peak Memory: " . round(memory_get_peak_usage(true) / 1024 / 1024, 2) . " MB\n";
    echo "Included Files: " . count(get_included_files()) . "\n";
    
    if (PHP_MAJOR_VERSION >= 7) {
        echo "OPcache: " . (function_exists('opcache_get_status') ? 'Enabled' : 'Disabled') . "\n";
    }
    
    if (PHP_MAJOR_VERSION >= 8) {
        $jit = opcache_get_status()['jit'] ?? [];
        echo "JIT: " . ($jit['enabled'] ?? false ? 'Enabled' : 'Disabled') . "\n";
    }
    
    echo "</pre>";

    Tabela de Decisão Rápida

    SintomaPHP 5PHP 7PHP 8
    White screendisplay_errors=1Check ThrowableCheck TypeError
    Memory exhaustedIncrease limitCheck generatorsCheck JIT buffer
    Slow performanceAPC cacheOPcacheJIT + Preloading
    Syntax errorCheck [] arraysCheck ?? operatorCheck match expression
    Type errorsAdd checksUse declare(strict)Use union types
    DeprecationSuppress noticesPlan migrationUpdate urgently

    Checklist Universal de Debug

    □ Identificar versão exata do PHP
    □ Verificar logs específicos da versão
    □ Testar com error reporting máximo
    □ Verificar extensões carregadas
    □ Confirmar configurações do php.ini
    □ Validar compatibilidade de sintaxe
    □ Checar diferenças de comportamento
    □ Revisar funções deprecated/removed
    □ Testar handlers de erro/exception
    □ Documentar solução por versão

    Conclusão: Dominando Todas as Eras do PHP

    Trabalhar com múltiplas versões de PHP é como ser fluente em diferentes dialetos do mesmo idioma. Cada versão tem suas forças:

    • PHP 5: Estável, previsível, amplamente suportado
    • PHP 7: Rápido, moderno, melhor handling de erros
    • PHP 8: Poderoso, JIT, recursos de linguagem avançados

    O segredo é conhecer as peculiaridades de cada versão e ter um toolkit que funcione em todas elas. Com este guia, você está preparado para debugar qualquer versão que aparecer no seu caminho.

    Lembre-se: O melhor código é aquele que funciona em produção, independente da versão do PHP. 🚀


    Sobre o autor: Mauro Rocha Tavares – Debugando PHP desde a versão 4, sou um sobrevivente das magic quotes, também já usei muito var_dump() e print_r(), veterano das migrações impossíveis.

    Compartilhe suas experiências: Qual foi o bug mais desafiador que você encontrou em diferentes versões do PHP?


    Tags: #PHP5 #PHP7 #PHP8 #Debugging #LegacyCode #ModernPHP #Troubleshooting #WebDevelopment #DevOps #BackendDevelopment #Backend