Suas ideias em realidade digital!
>
Melhorar a performance em PHP é essencial para qualquer site, sistema ou API que precisa responder rápido, consumir menos recursos e oferecer uma boa experiência para o usuário. Quando uma aplicação PHP começa a ficar lenta, o problema pode estar no banco de dados, em consultas pesadas, ausência de cache, arquivos grandes, respostas JSON extensas, falta de paginação ou configuração incorreta do servidor.
A boa notícia é que você não precisa usar um framework para deixar sua aplicação mais rápida. Com PHP puro, boas práticas de banco de dados, OPcache, cache simples, compressão e organização do código, já é possível melhorar bastante o tempo de resposta do sistema.
Neste artigo, você vai aprender como melhorar a performance de uma API ou site em PHP sem framework, com exemplos práticos para aplicar em projetos reais.
Uma aplicação PHP pode ficar lenta por vários motivos. Nem sempre o problema está no PHP em si. Muitas vezes, a lentidão vem de consultas SQL mal otimizadas, retorno excessivo de dados, falta de índices no banco, arquivos pesados ou lógica mal estruturada.
Algumas causas comuns de lentidão são:
SELECT * sem necessidade;Antes de otimizar, você precisa medir. Sem medição, qualquer mudança vira tentativa no escuro.
Uma forma simples de medir o tempo de execução de um script PHP é usando microtime.
<?php
$inicio = microtime(true);
// Código da aplicação aqui
$fim = microtime(true);
$tempo = $fim - $inicio;
echo 'Tempo de execução: ' . round($tempo, 4) . ' segundos';
Em APIs, você pode registrar esse tempo no log em vez de exibir na tela.
<?php
$inicio = microtime(true);
// Processamento da API
$tempo = microtime(true) - $inicio;
error_log('Tempo de resposta da API: ' . round($tempo, 4) . ' segundos');
Essa medição ajuda a identificar endpoints lentos, relatórios pesados e consultas demoradas.
Um dos maiores problemas de performance em APIs PHP é retornar listas enormes sem paginação. Mesmo que funcione no começo, quando a tabela cresce, o sistema começa a ficar lento.
Evite consultas assim:
SELECT * FROM pedidos ORDER BY id DESC
Em uma tabela com milhares de registros, isso pode consumir memória, tempo de banco e tráfego desnecessário.
Prefira usar paginação:
SELECT id, cliente, status, created_at
FROM pedidos
ORDER BY id DESC
LIMIT 20 OFFSET 0
Assim, a API retorna apenas os dados necessários para a página atual.
Um exemplo simples de paginação em PHP:
<?php
$page = isset($_GET['page']) ? (int) $_GET['page'] : 1;
$limit = isset($_GET['limit']) ? (int) $_GET['limit'] : 20;
if ($page < 1) {
$page = 1;
}
if ($limit < 1) {
$limit = 20;
}
if ($limit > 100) {
$limit = 100;
}
$offset = ($page - 1) * $limit;
$sql = "
SELECT id, cliente, status, created_at
FROM pedidos
ORDER BY id DESC
LIMIT :limit OFFSET :offset
";
$stmt = $pdo->prepare($sql);
$stmt->bindValue(':limit', $limit, PDO::PARAM_INT);
$stmt->bindValue(':offset', $offset, PDO::PARAM_INT);
$stmt->execute();
$pedidos = $stmt->fetchAll();
Além de melhorar performance, limitar o máximo de registros por página evita abuso da API.
O uso de SELECT * pode trazer colunas desnecessárias, aumentando o volume de dados processado pelo banco,
transferido para o PHP e enviado para o frontend.
Evite:
SELECT * FROM usuarios
Prefira:
SELECT id, nome, email, status FROM usuarios
Retorne apenas os campos que realmente serão usados na tela ou na API.
Índices ajudam o banco de dados a encontrar registros mais rapidamente.
Se sua aplicação filtra muito por email, status, created_at ou usuario_id,
esses campos podem precisar de índice.
CREATE INDEX idx_usuarios_email ON usuarios(email);
CREATE INDEX idx_usuarios_status ON usuarios(status);
CREATE INDEX idx_pedidos_created_at ON pedidos(created_at);
CREATE INDEX idx_pedidos_usuario_id ON pedidos(usuario_id);
Índices melhoram buscas e filtros, mas devem ser usados com cuidado. Índices demais podem impactar gravações, então priorize campos realmente usados em consultas frequentes.
O comando EXPLAIN ajuda a entender como o MySQL executa uma consulta.
EXPLAIN SELECT id, nome, email
FROM usuarios
WHERE email = 'teste@exemplo.com';
Com ele, você consegue identificar se a consulta está usando índice ou fazendo varredura completa na tabela.
Se uma consulta importante está lenta, usar EXPLAIN é um bom caminho para descobrir o gargalo.
Em APIs, o tamanho da resposta influencia diretamente a performance. Quanto mais dados você envia, mais tempo a API leva para montar, transferir e o frontend interpretar.
Evite respostas com campos desnecessários:
{
"id": 1,
"nome": "João",
"email": "joao@exemplo.com",
"senha": "$2y$10$...",
"token_interno": "abc",
"observacoes_internas": "..."
}
Prefira retornar apenas o necessário:
{
"id": 1,
"nome": "João",
"email": "joao@exemplo.com"
}
Cache é uma técnica para guardar temporariamente o resultado de uma operação pesada. Assim, em vez de consultar o banco toda vez, a aplicação pode reaproveitar um resultado salvo por alguns minutos.
Exemplos de dados que podem usar cache:
Um cache simples em arquivo pode resolver muitos casos em projetos pequenos e médios.
<?php
function cacheGet(string $key, int $ttlSeconds): ?array
{
$cacheDir = __DIR__ . '/cache';
if (!is_dir($cacheDir)) {
mkdir($cacheDir, 0755, true);
}
$file = $cacheDir . '/' . md5($key) . '.json';
if (!file_exists($file)) {
return null;
}
if ((time() - filemtime($file)) > $ttlSeconds) {
unlink($file);
return null;
}
$content = file_get_contents($file);
return json_decode($content, true);
}
function cacheSet(string $key, array $data): void
{
$cacheDir = __DIR__ . '/cache';
if (!is_dir($cacheDir)) {
mkdir($cacheDir, 0755, true);
}
$file = $cacheDir . '/' . md5($key) . '.json';
file_put_contents($file, json_encode($data, JSON_UNESCAPED_UNICODE));
}
Exemplo de uso:
<?php
$cacheKey = 'categorias_ativas';
$categorias = cacheGet($cacheKey, 300); // 5 minutos
if ($categorias === null) {
$stmt = $pdo->query("SELECT id, nome FROM categorias WHERE status = 'ativo'");
$categorias = $stmt->fetchAll();
cacheSet($cacheKey, $categorias);
}
echo json_encode([
'success' => true,
'data' => $categorias
]);
Esse tipo de cache reduz consultas repetidas ao banco.
O OPcache melhora a performance armazenando scripts PHP pré-compilados em memória. Sem ele, o PHP precisa carregar e compilar os arquivos a cada requisição.
Em servidores de produção, o OPcache deve estar ativado sempre que possível.
Exemplo de configuração básica no php.ini:
opcache.enable=1
opcache.memory_consumption=128
opcache.interned_strings_buffer=16
opcache.max_accelerated_files=10000
opcache.validate_timestamps=1
opcache.revalidate_freq=2
Em produção, algumas configurações podem variar conforme o servidor e o fluxo de deploy. O importante é verificar se o OPcache está ativo.
Você pode criar temporariamente um arquivo com phpinfo.
<?php
phpinfo();
Depois procure por OPcache na página.
phpinfo depois do teste.
Ele expõe informações sensíveis do servidor.
Loops mal planejados podem afetar muito a performance, principalmente quando fazem consultas ao banco dentro de cada repetição.
Evite:
<?php
foreach ($usuarios as $usuario) {
$stmt = $pdo->prepare("SELECT COUNT(*) FROM pedidos WHERE usuario_id = :id");
$stmt->execute([
':id' => $usuario['id']
]);
$totalPedidos = $stmt->fetchColumn();
}
Esse padrão é conhecido como problema N+1: para cada usuário, uma nova consulta é feita.
Prefira buscar tudo em uma consulta com JOIN ou agrupamento:
SELECT u.id, u.nome, COUNT(p.id) AS total_pedidos
FROM usuarios u
LEFT JOIN pedidos p ON p.usuario_id = u.id
GROUP BY u.id, u.nome
JOIN pode melhorar performance ao evitar várias consultas, mas também pode ficar pesado se usado sem índices ou sem necessidade.
Sempre que usar JOIN, confira:
EXPLAIN mostra uso eficiente dos índices.A compressão reduz o tamanho transferido entre servidor e navegador. Isso ajuda especialmente em respostas HTML, CSS, JavaScript e JSON.
Em muitos servidores, a compressão é configurada no Apache, Nginx ou painel da hospedagem.
No Apache, pode-se usar mod_deflate. No Nginx, a compressão geralmente é ativada com diretivas de gzip.
Para APIs que retornam JSON grande, compressão pode reduzir bastante o tamanho da resposta.
Sites PHP muitas vezes ficam lentos não por causa do PHP, mas por causa de imagens pesadas. Use imagens redimensionadas, formatos modernos como WebP e carregamento sob demanda quando possível.
Boas práticas:
Arquivos como CSS, JavaScript, imagens e fontes podem ser armazenados no cache do navegador. Isso reduz novas requisições e melhora o carregamento em visitas futuras.
Exemplo para Apache com .htaccess:
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType image/webp "access plus 1 month"
ExpiresByType image/png "access plus 1 month"
ExpiresByType image/jpeg "access plus 1 month"
ExpiresByType text/css "access plus 7 days"
ExpiresByType application/javascript "access plus 7 days"
</IfModule>
Isso ajuda muito em sites com muitos recursos estáticos.
Se sua aplicação usa muitos require ou include, verifique se está carregando apenas o necessário.
Arquivos grandes e não utilizados aumentam o tempo de inicialização. Organize helpers, controllers e serviços para carregar somente o que a rota precisa.
Mesmo em PHP puro, você pode usar o autoload do Composer para organizar classes.
Isso evita uma sequência grande de require_once.
{
"autoload": {
"psr-4": {
"App\\": "app/"
}
}
}
Depois:
composer dump-autoload
E no PHP:
<?php
require __DIR__ . '/vendor/autoload.php';
Isso deixa o projeto mais organizado e facilita manutenção.
Algumas tarefas não deveriam acontecer diretamente durante uma requisição normal do usuário.
Exemplos:
Sempre que possível, divida o processamento em partes, use filas, tarefas agendadas ou rotinas separadas.
Relatórios são uma das maiores causas de lentidão em sistemas PHP. Em vez de gerar tudo de uma vez, aplique filtros e paginação.
SELECT id, cliente, valor, created_at
FROM vendas
WHERE created_at BETWEEN :inicio AND :fim
ORDER BY created_at DESC
LIMIT :limit OFFSET :offset
Para exportações muito grandes, considere processar em lotes.
Uma boa prática é registrar no log quando uma requisição ultrapassa determinado tempo.
<?php
$inicio = microtime(true);
// código da API
$duracao = microtime(true) - $inicio;
if ($duracao > 1.5) {
error_log('Endpoint lento: ' . ($_SERVER['REQUEST_URI'] ?? '') . ' - ' . round($duracao, 4) . 's');
}
Assim, você consegue descobrir quais rotas precisam de otimização.
Aumentar memory_limit pode resolver temporariamente alguns erros, mas nem sempre é a solução correta.
Se uma requisição consome memória demais, pode haver consulta sem paginação, loop pesado ou processamento exagerado.
Exemplo de configuração:
memory_limit = 256M
max_execution_time = 60
Use esses ajustes com cuidado e investigue a causa da lentidão.
<?php
header('Content-Type: application/json; charset=utf-8');
$inicio = microtime(true);
function jsonResponse(int $statusCode, array $data): void
{
global $inicio;
$duracao = microtime(true) - $inicio;
if ($duracao > 1.5) {
error_log('Resposta lenta: ' . ($_SERVER['REQUEST_URI'] ?? '') . ' - ' . round($duracao, 4) . 's');
}
http_response_code($statusCode);
echo json_encode($data, JSON_UNESCAPED_UNICODE);
exit;
}
try {
$pdo = new PDO(
'mysql:host=localhost;dbname=sua_base;charset=utf8mb4',
'usuario',
'senha',
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false
]
);
$page = isset($_GET['page']) ? (int) $_GET['page'] : 1;
$limit = isset($_GET['limit']) ? (int) $_GET['limit'] : 20;
if ($page < 1) {
$page = 1;
}
if ($limit < 1) {
$limit = 20;
}
if ($limit > 100) {
$limit = 100;
}
$offset = ($page - 1) * $limit;
$sql = "
SELECT id, nome, email, status
FROM usuarios
ORDER BY id DESC
LIMIT :limit OFFSET :offset
";
$stmt = $pdo->prepare($sql);
$stmt->bindValue(':limit', $limit, PDO::PARAM_INT);
$stmt->bindValue(':offset', $offset, PDO::PARAM_INT);
$stmt->execute();
$usuarios = $stmt->fetchAll();
jsonResponse(200, [
'success' => true,
'data' => $usuarios,
'pagination' => [
'page' => $page,
'limit' => $limit
]
]);
} catch (PDOException $e) {
error_log('Erro PDO: ' . $e->getMessage());
jsonResponse(500, [
'success' => false,
'message' => 'Erro interno ao consultar os dados.'
]);
} catch (Throwable $e) {
error_log('Erro inesperado: ' . $e->getMessage());
jsonResponse(500, [
'success' => false,
'message' => 'Erro interno no servidor.'
]);
}
SELECT * sem necessidade;EXPLAIN em consultas lentas;Antes de mudar o código, meça o tempo de resposta e identifique o gargalo real.
Muitas vezes, a lentidão está no banco, em imagens, no servidor ou em consultas sem índice.
Quanto maior a resposta, maior o custo para servidor, rede e frontend.
OPcache é uma das otimizações mais importantes em servidores PHP de produção.
Cache é útil, mas precisa ser aplicado com cuidado para não mostrar informações desatualizadas.
Melhorar a performance em PHP não depende obrigatoriamente de framework. Com PHP puro, você já pode aplicar boas práticas importantes como paginação, consultas otimizadas, índices no banco, cache simples, OPcache, compressão e logs de endpoints lentos.
O mais importante é medir antes de otimizar. Descubra se o gargalo está no banco, no PHP, no tamanho da resposta, em imagens pesadas ou em processamento excessivo. Depois disso, aplique a correção certa.
Com uma estrutura bem organizada, sua API ou site PHP pode ficar mais rápido, mais econômico e mais preparado para crescer.
Meça o tempo de resposta, otimize consultas SQL, use paginação, ative OPcache, reduza arquivos pesados, use cache e comprima recursos estáticos.
Sim. O OPcache evita que o PHP precise recompilar scripts a cada requisição, melhorando a performance em ambientes de produção.
As causas mais comuns são consultas SQL pesadas, ausência de paginação, retorno excessivo de dados, falta de índices, loops desnecessários ou servidor com poucos recursos.
Sim. Para projetos pequenos e médios, cache em arquivo pode reduzir consultas repetidas ao banco e melhorar o tempo de resposta.
Pode prejudicar, principalmente em tabelas grandes ou com muitas colunas. O ideal é retornar apenas os campos necessários.
Sim. Paginação reduz a quantidade de dados processados, transferidos e renderizados pelo frontend.
Meça o tempo de resposta, otimize consultas SQL, use paginação, ative OPcache, reduza arquivos pesados, use cache e comprima recursos estáticos.
Sim. O OPcache evita que o PHP precise recompilar scripts a cada requisição, melhorando a performance em ambientes de produção.
As causas mais comuns são consultas SQL pesadas, ausência de paginação, retorno excessivo de dados, falta de índices, loops desnecessários ou servidor com poucos recursos.
Sim. Para projetos pequenos e médios, cache em arquivo pode reduzir consultas repetidas ao banco e melhorar o tempo de resposta.
Pode prejudicar, principalmente em tabelas grandes ou com muitas colunas. O ideal é retornar apenas os campos necessários.
Sim. Paginação reduz a quantidade de dados processados, transferidos e renderizados pelo frontend.