Performance Optimization
Learn practical techniques for making PHP applications faster, from enabling OPcache to minimizing database queries and profiling before optimizing.
Introduction
A PHP application that is correct and secure still needs to be fast enough for real users. As traffic grows, small inefficiencies — an uncached expensive calculation, a query running in a loop, a missing index — can turn a snappy page into a slow one.
This lesson covers the most impactful, practical performance techniques for PHP applications: caching compiled code with OPcache, reducing database load, caching results, choosing efficient built-in functions, and — just as importantly — measuring before you optimize.
- How OPcache speeds up PHP by caching compiled bytecode.
- How to avoid the N+1 query problem and use database indexes effectively.
- How caching expensive computed results avoids redundant work.
- How to choose efficient array and string functions.
- Why profiling with tools like Xdebug or Blackfire should come before optimizing.
- Why upgrading your PHP version alone can meaningfully improve performance.
Enabling OPcache
Normally, PHP re-parses and compiles a script into bytecode on every single request, then throws that compiled version away once the request finishes. OPcache is a built-in extension that stores compiled bytecode in memory, so subsequent requests can skip the parsing and compiling step entirely.
; Enable OPcache and reserve memory for cached bytecode
opcache.enable=1
opcache.memory_consumption=128
opcache.max_accelerated_files=10000
opcache.validate_timestamps=1
opcache.revalidate_freq=2OPcache is one of the single biggest performance wins available for any PHP application, often improving response times dramatically, and it usually just needs to be turned on — most modern hosting and PHP installations ship with the extension already available.
Minimizing Database Queries
Database access is typically the slowest part of a web request, far slower than PHP code execution itself. A common and costly mistake is the N+1 query problem: running one query to get a list, then one additional query per item in that list.
$posts = $pdo->query("SELECT id, title, author_id FROM posts")->fetchAll();
foreach ($posts as $post) {
// One extra query per post — very slow with many posts
$stmt = $pdo->prepare("SELECT name FROM users WHERE id = ?");
$stmt->execute([$post['author_id']]);
$author = $stmt->fetchColumn();
echo $post['title'] . ' by ' . $author . "\n";
}$posts = $pdo->query(
"SELECT posts.title, users.name AS author
FROM posts
JOIN users ON users.id = posts.author_id"
)->fetchAll();
foreach ($posts as $post) {
echo $post['title'] . ' by ' . $post['author'] . "\n";
}How to Learn PHP by Priya Sharma
Understanding MVC by Diego TorresThe joined version runs one query no matter how many posts exist, instead of one query per post. Alongside writing efficient queries, adding a database index on frequently searched or joined columns (like author_id) dramatically speeds up lookups on large tables.
Caching Expensive Results
If a calculation, database query, or external API call is expensive and its result does not change on every single request, computing it once and reusing that result can save significant time.
function getExpensiveReport(): array
{
$cacheFile = __DIR__ . '/cache/report.json';
if (file_exists($cacheFile) && (time() - filemtime($cacheFile) < 3600)) {
return json_decode(file_get_contents($cacheFile), true);
}
// Imagine this involves several slow queries or heavy computation
$report = ['total_sales' => 48250, 'top_product' => 'Wireless Mouse'];
file_put_contents($cacheFile, json_encode($report));
return $report;
}
print_r(getExpensiveReport());Array
(
[total_sales] => 48250
[top_product] => Wireless Mouse
)This example writes a cache to a file for simplicity. Real applications often use a dedicated cache store like Redis or Memcached, but the underlying idea — do the expensive work once, then reuse it — stays the same.
Choosing Efficient Functions
PHP's built-in array and string functions are implemented in optimized C code, and are almost always faster than an equivalent hand-written loop in PHP.
$numbers = range(1, 5);
// Prefer built-in functions over manual loops where possible
$doubled = array_map(fn($n) => $n * 2, $numbers);
$evens = array_filter($numbers, fn($n) => $n % 2 === 0);
$sum = array_sum($numbers);
print_r($doubled);
print_r($evens);
echo "Sum: $sum\n";Array
(
[0] => 2
[1] => 4
[2] => 6
[3] => 8
[4] => 10
)
Array
(
[1] => 2
[3] => 4
)
Sum: 15Beyond speed, using array_map(), array_filter(), and similar functions also makes intent clearer than an equivalent manual foreach loop.
Profile Before You Optimize
It is tempting to guess where an application is slow, but guesses are frequently wrong. Profiling tools measure exactly where time is actually being spent, so you optimize the part that matters instead of the part that merely looks suspicious.
Xdebug
A popular PHP extension that, among its debugging features, can profile a request and show exactly which functions consumed the most time.
Blackfire
A dedicated profiling tool designed for production and development use, offering detailed timelines and comparisons between code versions.
Measure first, optimize second. Optimizing code that was never actually slow wastes effort and can make the code harder to read for no real benefit.
Free Performance from Upgrading PHP
Each major PHP version has historically brought substantial performance improvements over the last, often with zero code changes required. Moving from an old, unsupported PHP version to a current one can be one of the easiest performance wins available — alongside the security benefits covered in the previous lesson.
Upgrading PHP is sometimes the single highest-value performance change you can make, since it improves every request across the entire application without touching a line of your own code.
Common Mistakes
- Optimizing code based on guesses instead of actual profiling data.
- Running a database query inside a loop instead of fetching everything in one query.
- Forgetting to enable OPcache in production, leaving significant performance on the table.
- Caching data that changes on every request, or forgetting to invalidate a cache that is now stale.
- Rewriting simple, readable code into a "clever" but barely faster version for no measurable benefit.
Best Practices
- Enable OPcache in every production environment.
- Watch for the N+1 query pattern and prefer joins or batched queries.
- Add database indexes on columns you frequently filter or join on.
- Cache expensive, repeatable work instead of recomputing it every request.
- Profile with Xdebug or Blackfire before spending time on optimization.
- Keep your PHP version current to benefit from ongoing performance improvements.
Frequently Asked Questions
Is OPcache safe to enable on every project?
Yes, OPcache is a standard, widely used extension and is safe for virtually every PHP application; many hosting providers and PHP builds enable it by default.
What exactly is the N+1 query problem?
It is running one query to fetch a list of items, then running one additional query per item to fetch related data, resulting in N+1 total queries instead of one or two efficient ones.
Do I need Redis or Memcached to start caching?
No. You can start with a simple file-based cache like the example in this lesson, and move to a dedicated cache store like Redis later if your needs grow.
How do I know if I should profile with Xdebug or Blackfire?
Xdebug is free, widely used, and a great starting point for local development. Blackfire is a more specialized tool often used for deeper production profiling.
Will upgrading PHP versions break my code?
It can, if your code relies on deprecated or removed features, so always review the migration guide and test thoroughly. But for most well-written modern PHP code, upgrades are safe and bring real performance gains.
Key Takeaways
- OPcache caches compiled PHP bytecode, avoiding expensive recompilation on every request.
- Avoiding the N+1 query problem and using database indexes greatly reduces database load.
- Caching expensive results avoids redoing the same costly work repeatedly.
- Built-in PHP array and string functions are usually faster and clearer than manual loops.
- Profile with tools like Xdebug or Blackfire before optimizing, and remember that upgrading PHP itself often brings free performance gains.
Summary
Performance optimization in PHP is mostly about a handful of high-impact habits: caching compiled code with OPcache, minimizing and indexing database queries, caching expensive results, using efficient built-in functions, and measuring with a profiler before making changes. Combined with staying on a current PHP version, these techniques cover the vast majority of real-world performance problems.
In this lesson, you learned the core techniques for keeping a PHP application fast as it grows. Next, in the final lesson of this course, you will bring everything together into a set of overall PHP best practices.
- You understand how OPcache improves performance by caching compiled bytecode.
- You can identify and avoid the N+1 query problem.
- You know how to cache expensive computed results.
- You can choose efficient built-in functions over manual loops.
- You know to profile with Xdebug or Blackfire before optimizing, and why upgrading PHP helps.