PHP 8 Performance Gains

PHP 8 introduced the JIT compiler and significant engine improvements. Always run PHP 8.2+ in production for best performance.

OPcache

OPcache stores compiled bytecode in memory, avoiding recompilation on every request. Enable in php.ini:

  opcache.enable=1
opcache.memory_consumption=256
opcache.max_accelerated_files=20000
opcache.validate_timestamps=0   ; production: disable file checks
  

Verify with php -i | grep opcache or phpinfo().

Profiling

Identify bottlenecks before optimizing:

  • Xdebug — function-level profiling (development only)
  • Blackfire.io — production-safe profiling
  • SPX — lightweight open-source profiler
  composer require --dev spx/spx
  

Caching Strategies

Layer Tool Use Case
Opcode OPcache Compiled PHP bytecode
Object Redis, Memcached Database query results, computed data
HTTP Varnish, CloudFlare Full page / API response caching
Framework Laravel cache, Symfony cache Application-level caching
  // Simple file/APCu cache pattern
$cacheKey = 'users_active';
if (!$users = apcu_fetch($cacheKey)) {
    $users = $db->query('SELECT * FROM users WHERE active = 1');
    apcu_store($cacheKey, $users, 300); // 5 minutes
}
  

Database Optimization

  • Add indexes on frequently queried columns
  • Avoid SELECT * — fetch only needed columns
  • Use eager loading to prevent N+1 queries (Eloquent: User::with('posts')->get())
  • Paginate large result sets

Autoloading

Use Composer’s optimized autoloader in production:

  composer install --optimize-autoloader --no-dev
composer dump-autoload -o
  

Lazy Loading & Deferred Work

  • Queue heavy tasks (email, image processing) with Redis/RabbitMQ
  • Use generators for large datasets instead of loading everything into memory

Benchmark Before and After

  $start = hrtime(true);
// code to benchmark
$elapsed = (hrtime(true) - $start) / 1e6;
echo "Took {$elapsed} ms\n";
  

Measure, optimize the hot path, measure again. Premature optimization wastes time; profiling tells you where to focus.

Real-World OPcache Tuning

In containerized deployments, mount application code as read-only and set opcache.validate_timestamps=0. After deployments, reload PHP-FPM to pick up changes:

  sudo systemctl reload php8.2-fpm
  

Monitor hit rate via opcache_get_status() — aim for above 99% in steady state.

Redis Object Cache Example

  $redis = new Redis();
$redis->connect('127.0.0.1', 6379);

$key = 'product:' . $id;
if ($cached = $redis->get($key)) {
    return json_decode($cached, true);
}

$product = $repository->find($id);
$redis->setex($key, 3600, json_encode($product));
return $product;
  

N+1 Query Fix

  // Bad — N+1 queries
foreach (User::all() as $user) {
    echo $user->posts->count();
}

// Good — eager load
foreach (User::with('posts')->get() as $user) {
    echo $user->posts->count();
}
  

Memory and Generators

  function readLargeCsv(string $path): Generator {
    $handle = fopen($path, 'r');
    while (($row = fgetcsv($handle)) !== false) {
        yield $row;
    }
    fclose($handle);
}

foreach (readLargeCsv('million-rows.csv') as $row) {
    processRow($row); // constant memory
}
  

Production Checklist

  • PHP 8.2+ with OPcache enabled
  • composer install --optimize-autoloader --no-dev
  • Database indexes on WHERE/JOIN columns
  • Query logging in staging to catch N+1 patterns
  • Queue workers for email, PDF, and image jobs
  • Horizontal scaling behind a load balancer when CPU-bound

Common Pitfalls

  • Caching without invalidation strategy — stale data frustrates users.
  • Optimizing cold code paths identified by guesswork instead of profiling.
  • Running Xdebug in production — it adds 2–10× overhead.