Skip to content

PHP Performance — Complete Guide to Optimizing PHP Applications

DodaTech Updated 2026-06-28 7 min read

In this tutorial, you will learn about PHP Performance. We cover key concepts, practical examples, and best practices to help you master this topic.

PHP performance optimization is the practice of measuring and improving application speed through opcode caching, JIT compilation, database query optimization, lazy loading, and profiling to reduce response times and server resource usage. This tutorial covers configuring OPcache for script caching, enabling and tuning PHP 8's JIT compiler, profiling with Xdebug and Blackfire, optimizing database queries with indexing and eager loading, implementing caching strategies with Redis and APCu, and using Opcache Preloading for framework optimization.

What You'll Learn

  • Configuring OPcache for maximum cache hit rates
  • Enabling PHP 8 JIT compiler and tuning it for different workloads
  • Profiling PHP applications with Xdebug and analyzing cachegrind files
  • Optimizing database queries with indexes, eager loading, and query batching
  • Implementing application caching with APCu and Redis
  • Using Opcache Preloading for bootstrap optimization
  • Reducing memory usage with generators and lazy loading

Why It Matters

A slow application loses users. Amazon found that every 100ms of latency costs 1% in revenue. Google reported that a 0.5-second delay reduced traffic by 20%. PHP's interpreted nature makes optimization more critical than for compiled languages. With proper caching and profiling, a PHP application can serve thousands of requests per second on modest hardware.

Real-World Use

Doda Browser's sync API handles 10,000 requests per second during peak hours. The team achieved this through PHP 8 JIT compilation, Redis caching for session data, OPcache with preloading for the framework bootstrap, and database query optimization that reduced average page load time from 250ms to 45ms.

Learning Path

flowchart LR
  A[CLI Scripts] --> B[Performance\nYou are here]
  B --> C[PHP 8 Features]
  style B fill:#f90,color:#fff

Configuring OPcache

OPcache stores compiled PHP scripts in shared memory, eliminating the need to recompile on every request. Enable it in php.ini:

opcache.enable=1
opcache.memory_consumption=256
opcache.max_accelerated_files=20000
opcache.revalidate_freq=2
opcache.validate_timestamps=0
opcache.enable_cli=1

For production, disable timestamp validation:

opcache.validate_timestamps=0

Reset the cache after deployment:

php -r 'opcache_reset();'

Verify OPcache status with a script:

<?php
$status = opcache_get_status();
if ($status !== false) {
  echo "Cache hits: " . ($status['opcache_statistics']['hits'] ?? 0) . "\n";
  echo "Cache misses: " . ($status['opcache_statistics']['misses'] ?? 0) . "\n";
  echo "Memory used: " . round($status['memory_usage']['used_memory'] / 1024 / 1024, 2) . " MB\n";
}

Output:

Cache hits: 15234
Cache misses: 87
Memory used: 42.15 MB

PHP 8 JIT Compilation

PHP 8.0 introduced a JIT (Just-In-Time) compiler that translates PHP bytecode to native machine code. Enable it in php.ini:

opcache.jit=1255
opcache.jit_buffer_size=100M

The JIT value 1255 encodes:

  • 1: CRFG (Control Flow Graph) tracing mode
  • 2: No Register Allocation
  • 5: Use AVX instruction set (if available)
  • 5: Optimization level (max)

For CPU-heavy workloads, enable JIT and verify:

<?php
function fibonacci(int $n): int
{
  if ($n <= 1) return $n;
  return fibonacci($n - 1) + fibonacci($n - 2);
}

$start = microtime(true);
$result = fibonacci(40);
$elapsed = (microtime(true) - $start) * 1000;

echo "Result: $result\n";
echo "Time: " . round($elapsed, 2) . " ms\n";

With JIT enabled, this benchmark runs 3-8x faster than without JIT.

Profiling with Xdebug

Xdebug profiler generates cachegrind files that show which functions consume the most time:

xdebug.mode=profile
xdebug.output_dir=/tmp/profiling

Profile a specific request:

php -d xdebug.mode=profile script.php

Analyze the output with KCachegrind (Linux) or Webgrind:

# Install webgrind for browser-based analysis
git clone https://github.com/jokkedk/webgrind.git
cd webgrind
php -S 127.0.0.1:8080

Open http://127.0.0.1:8080 and load the cachegrind file. Look for slow functions, excessive memory allocation, and unexpected call counts.

Database Query Optimization

Inefficient database queries are the most common performance bottleneck.

Before optimization, a naive query loads related data in N+1 queries:

<?php
$pdo = new PDO('mysql:host=localhost;dbname=blog', 'root', '');

$users = $pdo->query('SELECT * FROM users')->fetchAll();

foreach ($users as $user) {
  // N+1 problem: one query per user
  $posts = $pdo->query("SELECT COUNT(*) FROM posts WHERE user_id = {$user['id']}")->fetchColumn();
  echo "{$user['name']}: $posts posts\n";
}

Optimized with a JOIN and single query:

<?php
$pdo = new PDO('mysql:host=localhost;dbname=blog', 'root', '');

$stmt = $pdo->query('
  SELECT u.name, COUNT(p.id) as post_count
  FROM users u
  LEFT JOIN posts p ON p.user_id = u.id
  GROUP BY u.id
');

while ($row = $stmt->fetch()) {
  echo "{$row['name']}: {$row['post_count']} posts\n";
}

Add indexes to frequently queried columns:

CREATE INDEX idx_posts_user_id ON posts(user_id);
CREATE INDEX idx_posts_created_at ON posts(created_at);

Application Caching with APCu

APCu provides in-memory caching for PHP applications:

pecl install apcu
<?php
function getExpensiveData(): array
{
  $cacheKey = 'app.expensive_data';
  $data = apcu_fetch($cacheKey, $success);

  if ($success) {
    echo "Cache hit\n";
    return $data;
  }

  echo "Cache miss\n";
  $data = ['result' => 'computed_value', 'time' => time()];
  apcu_store($cacheKey, $data, 3600); // Cache for 1 hour
  return $data;
}

$result = getExpensiveData();
echo "Result: {$result['result']}\n";

Output:

Cache miss
Result: computed_value

Opcache Preloading

Preloading loads PHP files into shared memory before any request arrives, eliminating lazy loading overhead for framework bootstrap files.

Enable preloading in php.ini:

opcache.preload=/var/www/html/preload.php

Create preload.php:

<?php
// Preload framework classes into OPcache shared memory
$files = [
  __DIR__ . '/vendor/autoload.php',
  __DIR__ . '/vendor/symfony/http-foundation/Request.php',
  __DIR__ . '/vendor/symfony/http-foundation/Response.php',
  __DIR__ . '/vendor/doctrine/orm/lib/Doctrine/ORM/EntityManager.php',
];

foreach ($files as $file) {
  if (file_exists($file)) {
    opcache_compile_file($file);
  }
}

Common Mistakes

  1. Guessing instead of profiling: Engineers often optimize the wrong code. Always profile first to identify the actual bottleneck, then optimize based on data.
  2. Not using OPcache in production: Without OPcache, PHP recompiles every script on every request, wasting CPU and increasing latency by 50-200%.
  3. Creating unnecessary objects in hot paths: Instantiating objects inside loops causes GC pressure. Reuse objects or use arrays for high-throughput code.
  4. Missing database indexes: A missing index turns a 1ms query into a 500ms full table scan. Use EXPLAIN to verify query execution plans.
  5. Over-caching: Caching too aggressively can return stale data and mask slow queries. Set appropriate TTLs and invalidate cache on data changes.

Practice Questions

  1. What is OPcache and how does it improve performance?

    • OPcache stores compiled PHP bytecode in shared memory, skipping the compilation step on every request. It typically reduces response time by 50%.
  2. How does JIT compilation differ from OPcache?

    • OPcache caches bytecode. JIT compiles bytecode to native machine code at runtime, speeding up CPU-heavy operations like loops, math, and string processing.
  3. What is the N+1 query problem and how do you fix it?

    • N+1 occurs when you execute one query for the parent and N additional queries for each child. Fix it with JOIN or eager loading.
  4. What is the purpose of apcu_fetch and apcu_store?

    • apcu_fetch retrieves cached data from shared memory. apcu_store stores data with a TTL. Use them to cache expensive computations or database results.
  5. Challenge: Profile a real Laravel or Symfony application with Xdebug, identify the three slowest functions, optimize them, and measure the improvement with a before-and-after benchmark.

Mini Project

Build a PHP performance benchmarking tool:

  • Create a CLI script that benchmarks common operations: string concatenation, array iteration, database queries, and JSON encoding.
  • Run each operation 10,000 times and measure total time with microtime.
  • Test with OPcache on and off, JIT on and off.
  • Generate a report showing execution times and memory usage for each configuration.
  • Add profiling with Xdebug for the database query test.

FAQ

What is the single most important PHP performance optimization?

Enabling OPcache. Without it, PHP recompiles every script on every request, wasting 50-200% of CPU time. It is a one-line configuration change with enormous impact.

Should I enable JIT for my WordPress site?

JIT primarily helps CPU-bound code. WordPress is typically I/O-bound (database queries, file reads). JIT may not show significant gains for typical WordPress workloads.

How much memory should I allocate for OPcache?

Start with 256 MB for the buffer and monitor opcache_get_status. If the memory_used approaches the buffer size, increase it. For large frameworks like Symfony, 512 MB may be needed.

What is Opcache Preloading?

Preloading loads specified PHP files into OPcache shared memory before any request arrives, eliminating lazy loading overhead for framework classes. It provides a 5-15% improvement for frameworks.

How can I measure PHP performance?

Use Xdebug for profiling, microtime for micro-benchmarks, Blackfire for production profiling, and tools like Apache Bench (ab) or wrk for load testing.

What's Next

Now that you can optimize performance, explore the latest language capabilities in PHP 8 Features.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro