Otimização de performance no PHP

Performance em PHP não é um luxo — é um requisito para qualquer aplicação que pretenda escalar. Neste artigo, você aprenderá técnicas práticas para diagnosticar gargalos, otimizar código, gerenciar memória, configurar cache e preparar seu ambiente de produção para o melhor desempenho possível.

1. Diagnóstico e Identificação de Gargalos

Antes de otimizar, é preciso medir. Ferramentas de profiling como Xdebug (com xdebug.mode=profile), Blackfire.io e Tideways permitem visualizar o tempo gasto em cada função e identificar os pontos mais lentos.

// Exemplo de profiling manual com microtime
$start = microtime(true);
// ... código a ser analisado ...
$end = microtime(true);
echo "Tempo de execução: " . ($end - $start) . " segundos";

Além do profiling, analise logs do servidor e métricas de banco de dados. Consulte lentas no MySQL com EXPLAIN e utilize ferramentas como o Slow Query Log para identificar consultas problemáticas.

2. Otimização do Código PHP

Uso eficiente de estruturas de dados

Arrays associativos são flexíveis, mas objetos imutáveis (como stdClass ou DTOs tipados) podem ser mais rápidos em operações repetitivas.

// Ruim: array associativo com muitas iterações
$data = ['nome' => 'João', 'idade' => 30];
foreach ($data as $key => $value) { /* ... */ }

// Melhor: objeto imutável para acesso direto
$data = new stdClass();
$data->nome = 'João';
$data->idade = 30;
echo $data->nome;

Redução de chamadas de funções dentro de loops

Chamar funções dentro de loops (especialmente funções de string, como strlen ou strpos) pode ser custoso. Extraia a chamada para fora do loop sempre que possível.

// Ruim: strlen() chamado a cada iteração
for ($i = 0; $i < count($array); $i++) {
    if (strlen($array[$i]) > 10) { /* ... */ }
}

// Melhor: armazena o tamanho máximo fora do loop
$count = count($array);
for ($i = 0; $i < $count; $i++) {
    // ...
}

Emprego de funções nativas do PHP

Funções nativas são escritas em C e executam muito mais rápido que implementações customizadas em PHP. Prefira array_map(), array_filter(), array_reduce() e in_array() a loops manuais.

// Ruim: loop manual para filtrar
$filtered = [];
foreach ($users as $user) {
    if ($user->active) {
        $filtered[] = $user;
    }
}

// Melhor: função nativa
$filtered = array_filter($users, fn($user) => $user->active);

3. Gerenciamento de Memória e Coleta de Lixo

O garbage collector do PHP entra em ação quando o contador de referências de uma variável chega a zero. Em scripts de longa duração (filas, workers), vazamentos de memória podem ocorrer se referências cíclicas não forem quebradas.

// Exemplo de liberação explícita
function processLargeData() {
    $data = fetchHugeDataSet(); // milhões de registros
    // processa...
    unset($data); // libera memória imediatamente
    gc_collect_cycles(); // força coleta de ciclos
}

Evite manter referências desnecessárias e use unset() em variáveis grandes após o uso. Em scripts que rodam por horas, chame gc_collect_cycles() periodicamente.

4. Cache de Opcode e Compilação

Configuração e tuning do OPcache

O OPcache armazena o bytecode compilado dos scripts PHP, eliminando a necessidade de recompilar a cada requisição. Configure no php.ini:

opcache.enable=1
opcache.memory_consumption=256
opcache.max_accelerated_files=20000
opcache.revalidate_freq=2
opcache.validate_timestamps=0  ; Em produção: desligue validação para maior performance

Cache de arquivos incluídos

Com opcache.file_cache, você pode salvar o cache em disco, útil para servidores sem memória compartilhada (ex: ambientes com Docker).

opcache.file_cache=/tmp/opcache
opcache.file_cache_only=1

Uso de JIT no PHP 8.x

O JIT (Just-In-Time Compilation) pode acelerar drasticamente operações matemáticas e loops intensivos. Ative no php.ini:

opcache.jit=1255
opcache.jit_buffer_size=100M

Teste com benchmarks específicos — o JIT é mais eficaz em código numérico do que em aplicações web típicas.

5. Otimização de I/O e Conexões

Conexões persistentes com banco de dados

Use PDO::ATTR_PERSISTENT para reutilizar conexões, evitando o overhead de handshake a cada requisição.

$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass', [
    PDO::ATTR_PERSISTENT => true
]);

Cache de resultados com Redis ou Memcached

Evite consultas repetitivas ao banco armazenando resultados em cache na memória.

$cacheKey = 'user_profile:' . $userId;
$profile = $redis->get($cacheKey);
if (!$profile) {
    $profile = $db->query("SELECT * FROM users WHERE id = ?", [$userId])->fetch();
    $redis->setex($cacheKey, 3600, serialize($profile));
}

Redução de operações de arquivo e uso de streams

Operações de I/O em disco são lentas. Prefira streams para processar arquivos grandes sem carregá-los inteiramente na memória.

$handle = fopen('largefile.csv', 'r');
while (($line = fgets($handle)) !== false) {
    // processa linha por linha
}
fclose($handle);

6. Estratégias de Cache no Aplicativo

Cache de saída com ob_start()

Armazene o HTML gerado em cache para evitar reprocessamento.

function cacheOutput($key, $ttl = 60) {
    $cached = apcu_fetch($key);
    if ($cached !== false) {
        echo $cached;
        return;
    }
    ob_start();
    register_shutdown_function(function() use ($key, $ttl) {
        $content = ob_get_flush();
        apcu_store($key, $content, $ttl);
    });
}

Cache de funções e resultados complexos

Use memoização para funções que processam dados pesados.

function expensiveComputation($input) {
    static $cache = [];
    if (isset($cache[$input])) {
        return $cache[$input];
    }
    $result = /* cálculo pesado */;
    $cache[$input] = $result;
    return $result;
}

Cache de bytecode para templates

Motores de template como Twig e Blade permitem cache de templates compilados. Ative essa opção em produção.

7. Otimização para Ambientes de Produção

Configuração do PHP-FPM

Ajuste o número de processos filhos (pm.max_children), requisições por processo (pm.max_requests) e timeouts.

pm = dynamic
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 35
pm.max_requests = 500

Balanceamento de carga e servidores web

O Nginx com PHP-FPM geralmente supera o Apache em concorrência. Use fastcgi_cache no Nginx para cache de respostas inteiras.

location ~ \.php$ {
    fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
    fastcgi_cache mycache;
    fastcgi_cache_valid 200 60m;
}

Monitoramento contínuo

Ferramentas como New Relic e Datadog fornecem dashboards de performance, rastreamento de transações e alertas de lentidão. Integre-as ao seu deploy para detectar regressões.

Conclusão

Otimizar performance no PHP é um processo contínuo que envolve diagnóstico preciso, código enxuto, gerenciamento inteligente de memória, cache em múltiplas camadas e um ambiente de produção bem configurado. Comece medindo, aplique as técnicas apresentadas e monitore os resultados. Com essas práticas, sua aplicação PHP estará pronta para escalar com eficiência.

Referências