Categoria: Blog

Your blog category

  • 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

  • Como instalar Z-Shell (ZSH) e Oh-My-Zsh no Ubuntu e WSL no Windows

    Como instalar Z-Shell (ZSH) e Oh-My-Zsh no Ubuntu e WSL no Windows


    Se você ainda está usando o bom e velho Bash, parabéns você está vivendo no passado, mas com dignidade. Agora, se quiser entrar para o clube dos terminais estilosos e produtivos, é hora de conhecer o Z-Shell, ou simplesmente ZSH.

    ZSH é um shell poderoso, altamente configurável e cheio de recursos que vão desde autocompletar inteligente até temas e plugins que transformam seu terminal num painel de controle de nave estelar.

    E quando você combina o ZSH com o Oh My Zsh, que é um gerenciador de configurações e plugins, a experiência passa de “linha de comando” para “ambiente de trabalho com autoestima”.


    Antes de tudo: o que é, e por que usar ZSH

    ZSH é um shell Unix como o Bash, mas com superpoderes. Ele traz:

    • Temas e cores, porque produtividade também é estética;
    • Plugins, que deixam o terminal mais útil e menos entediante.
    • Autocompletar inteligente, que parece adivinhar seus comandos;
    • Correção automática, porque errar git pus ao invés de git push é humano;

    Em resumo: ZSH é o Bash depois de um curso de pós-graduação em ergonomia digital.


    Verifique seu shell atual

    Antes de mudar tudo, descubra o que está rodando aí:

    echo $SHELL

    Se aparecer algo como /bin/bash, parabéns: você está pronto para evoluir.


    1. Atualizando o sistema

    Começamos com o básico. Abra o terminal e execute:

    sudo apt update
    sudo apt upgrade

    Nada de instalar ferramentas em cima de um sistema desatualizado — isso é pedir para o caos se manifestar.


    2. Instalando dependências

    Precisamos garantir que o Git, Wget e companhia estejam prontos para a ação:

    sudo apt install wget git zip unzip

    Esses pacotes serão usados tanto para baixar o Oh My Zsh quanto para clonar temas e plugins depois.


    3. Instalando o ZSH

    Agora vem a estrela do show:

    sudo apt install -y zsh

    Simples assim. E para verificar se deu tudo certo:

    zsh --version

    Se aparecer algo como zsh 5.x.x, está tudo certo.
    Mas o ZSH ainda não é o shell padrão — então segura a empolgação, ainda falta o toque de mágica.


    4. Instalando o Oh My Zsh

    O Oh My Zsh é o gerenciador que vai cuidar das configurações, temas e plugins do seu novo shell.

    Instale com:

    sh -c "$(wget https://raw.github.com/robbyrussell/oh-my-zsh/master/tools/install.sh -O -)"

    Durante a instalação, ele perguntará se você quer definir o ZSH como shell padrão.
    Diga “sim” (y), e pronto — você agora é oficialmente um cidadão do terminal moderno.


    5. Instalando as fontes Powerline

    Os temas do ZSH adoram usar ícones e símbolos especiais. Para que tudo apareça bonito, instale as fontes Powerline:

    sudo apt-get install -y powerline fonts-powerline
    

    No Windows (via WSL), use as fontes MesloLGS NF, disponíveis no repositório do github: https://github.com/ryanoasis/nerd-fonts/releases/download/v3.3.0/Meslo.zip
    Depois de instalar, configure o Windows Terminal para usar uma delas como fonte padrão.

    Sem isso, seu terminal vai parecer uma sopa de caracteres estranhos — e ninguém merece um prompt ilegível.


    6. Personalizando o ZSH

    Agora vem a parte divertida: deixar o terminal com a sua cara.
    Abra o arquivo de configuração do ZSH:

    nano ~/.zshrc

    Ali você verá uma linha assim:

    ZSH_THEME="robbyrussell"

    Troque o valor pelo tema desejado, por exemplo:

    ZSH_THEME="agnoster"

    Salve, feche e atualize:

    source ~/.zshrc

    Pronto! Novo visual ativado.


    7. Instalando Temas Avançados

    Spaceship

    Um dos temas mais modernos e limpos:

    git clone https://github.com/denysdovhan/spaceship-prompt.git "$ZSH_CUSTOM/themes/spaceship-prompt"
    ln -s "$ZSH_CUSTOM/themes/spaceship-prompt/spaceship.zsh-theme" "$ZSH_CUSTOM/themes/spaceship.zsh-theme"

    Depois edite o arquivo .zshrc e defina:

    ZSH_THEME="spaceship"

    Powerlevel10k

    O queridinho da comunidade que é bonito, rápido e personalizável:

    git clone --depth=1 https://github.com/romkatv/powerlevel10k.git ${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}/themes/powerlevel10k

    E depois:

    ZSH_THEME="powerlevel10k/powerlevel10k"

    Recarregue o shell:

    source ~/.zshrc

    O Powerlevel10k vai abrir um assistente de configuração — siga as instruções e monte o prompt dos seus sonhos.


    8. Instalando Plugins

    Quer deixar o ZSH mais inteligente ainda? Adicione os plugins a seguir:

    Esses 3 plugins são mágicos:

    • zsh-autosuggestions te sugere comandos baseados no histórico;

    Instale com o comando:

    git clone https://github.com/zsh-users/zsh-autosuggestions ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autosuggestions
    • zsh-syntax-highlighting colore o que você digita, indicando erros e comandos válidos.

    Instale com o comando:

    git clone https://github.com/zsh-users/zsh-syntax-highlighting.git ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-syntax-highlighting
    • fzf buscador de arquivos interativo para linha de comando

    Instale com o comando:

    git clone --depth 1 https://github.com/junegunn/fzf.git ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/fzf
    ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/fzf/install

    Não esqueça de adicionar no seu .zshrc:

    plugins=(
      git
      zsh-autosuggestions
      zsh-syntax-highlighting
      fzf
    )

    Recarregue o shell e aprecie a diferença.

    Para saber mais sobre Plugins

    Plugins do Oh My Zsh

    No projeto de plugins do github você terá acesso a dezenas de plugins em: https://github.com/ohmyzsh/ohmyzsh/wiki/plugins

    fzf

    fzf é um buscador de arquivos interativo para linha de comando que pode ser usado com qualquer lista, arquivos, histórico de comandos, processos, nomes de host, favoritos, git commits, etc. Ele nos possibilita pesquisar pelo terminal de forma simples e rápida. Para saber mais e conhecer as opções acesse o repositório no github https://github.com/junegunn/fzf


    Conclusão

    Com o ZSH e o Oh My Zsh, o terminal deixa de ser um castigo e passa a ser um aliado — bonito, rápido e cheio de recursos.
    E o melhor: tudo o que funciona no Ubuntu funciona também no WSL do Windows.

    Você não só vai digitar comandos como vai curtir digitar comandos.

    E lembre-se: um terminal configurado com carinho é o primeiro passo para uma vida de programador mais produtiva (e com menos vontade de jogar o teclado pela janela).