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
- PHP Manual: OPcache — Documentação oficial sobre configuração e uso do OPcache.
- Blackfire.io: PHP Performance Profiling — Guia completo de profiling com Blackfire para identificar gargalos.
- Tideways: PHP Profiler and Monitoring — Ferramenta de profiling e monitoramento de performance para PHP.
- PHP Manual: Garbage Collection — Explicação detalhada do coletor de lixo do PHP.
- New Relic: PHP Monitoring — Documentação do agente PHP para monitoramento contínuo.
- PHP.Watch: JIT Compilation in PHP 8.x — Artigo técnico aprofundado sobre a compilação JIT no PHP 8.
- Redis: Caching with PHP — Guia oficial para uso de Redis como cache em aplicações PHP.