Caching
By the end of this lesson you'll be able to cache expensive work with the cache-aside pattern, pick the right store (file, APCu, or Redis), set TTLs and invalidate safely, and avoid the cache stampede that brings sites down — turning a 200ms page into a sub-20ms one.
Learn Python, JavaScript, Java and more with free interactive lessons, real projects and AI-powered help. Beginner-friendly.
Part of the free Php course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
What You'll Learn in This Lesson
1️⃣ Why Cache, and the Cache-Aside Pattern
A cache is a fast, temporary store for the result of slow work — a database query, an API call, a heavy calculation — so you only pay for that work once. Reading from a database might take milliseconds; reading from a cache takes microseconds, often 100x faster . Two words you'll use constantly: a hit means the value was in the cache (fast), and a miss means it wasn't, so you had to do the slow work.
The standard recipe is cache-aside (also called lazy loading): check the cache first; on a miss, run the query, store the result, then return it. It's popular because it only caches what's actually asked for, and if the cache is empty or down a miss simply falls through to the database. Watch the loop — four requests, but only two database calls.
That's the entire idea. Every cache below — file, APCu, Redis — is the same three steps ( check → miss → store ); only the place the data is kept changes.
2️⃣ A File-Based Cache (and TTL)
The simplest real cache writes each value to a file on disk — no extra software needed. The new idea here is the TTL ( time-to-live ): how many seconds a value is allowed to live before it's treated as expired and recomputed. Without a TTL a value can become stale — users keep seeing yesterday's data forever. Here the file's last-modified time tells us how old the entry is.
File caches are great for rendered HTML fragments or config that rarely changes. They're a little slower than memory (disk access) and don't share well across many servers — which is exactly what the next two stores fix.
3️⃣ APCu — In-Memory Caching
APCu is a cache built into PHP that keeps data in the server's RAM , so reads are measured in microseconds. You enable the apcu extension, then use apcu_store($key, $value, $ttl) and apcu_fetch($key, $found) . The catch: it's per-server (each web server has its own copy) and is wiped on restart . Perfect for a single machine; not for a cluster.
4️⃣ Redis & Memcached — A Shared Cache
Once you have more than one web server, a per-server cache isn't enough — they'd each cache different things. Redis and Memcached run as their own separate service that every server connects to over the network, so the cache is shared . Memcached is a lean key/value store; Redis adds richer types (lists, sets, hashes), persistence to disk, and more — it's the usual default today. Note invalidation at the end: when the underlying data changes, you del() the key so the next read recomputes.
5️⃣ OPcache and the Cache Stampede
OPcache is a different kind of cache: instead of your data , it caches your compiled code . PHP normally reparses and recompiles every .php file on every request; OPcache keeps the compiled bytecode in memory so that work happens once — typically a 2–3x speed-up for free . You write no code; it's switched on in php.ini .
Finally, the failure mode to know about: a cache stampede (or "dog-piling"). A hot key expires, and in the same instant dozens of requests all miss — so they all run the same expensive query at once and crush the database. The fixes: a short lock so only the first request recomputes while the rest wait or serve the old value; early expiration , where one request refreshes just before the TTL; and a little random jitter on TTLs so many keys don't expire on the very same second.
6️⃣ Your Turn
Time to wire up the pattern yourself. The script below is almost complete — fill each ___ using the 👉 hint, then run it and check it against the Output panel.
One more — this time about freshness . Compare an entry's age against the TTL so stale data is detected.
📋 Quick Reference — Caching in PHP
No code is filled in this time — just a brief and an outline. Write it yourself, run it on onecompiler.com/php or your own machine, then check your result against the expected output in the comments. This is the same write-run-check loop you'll use on every real cache.
Practice quiz
In the cache-aside pattern, what happens on a cache MISS?
- Return null and stop
- Delete the cache key
- Run the slow query, store the result in the cache, then return it
- Throw an exception
Answer: Run the slow query, store the result in the cache, then return it. Cache-aside: check the cache first; on a miss, run the slow work, store the result, then return it. Later requests for that key are fast hits.
What is a cache 'hit'?
- The value was already in the cache, so no slow work was needed
- The cache was cleared
- The cache server crashed
- A new value was written to the cache
Answer: The value was already in the cache, so no slow work was needed. A hit means the value was in the cache (fast); a miss means it wasn't, so you had to do the slow work.
What does TTL stand for, and what does it control?
- Total transfer load — bytes per request
- Time to login — session length
- Tries till lockout — failed attempts
- Time-to-live — how many seconds before an entry is treated as expired
Answer: Time-to-live — how many seconds before an entry is treated as expired. TTL is the number of seconds an entry may stay before it is recomputed — the simplest form of cache invalidation, stopping stale data living forever.
What is the key limitation of APCu compared to Redis?
- It cannot store integers
- It is per-server (each web server has its own copy) and wiped on restart
- It requires a separate network service
- It never expires entries
Answer: It is per-server (each web server has its own copy) and wiped on restart. APCu lives in one server's RAM, so it's per-server and cleared on restart — great for a single machine, not for a cluster.
Why use Redis or Memcached instead of APCu once you have multiple web servers?
- They run as a separate service shared by every web server
- They are written in PHP
- They never need a TTL
- They cache compiled bytecode
Answer: They run as a separate service shared by every web server. Redis/Memcached run as their own service every server connects to, so the cache is shared — essential past one machine.
What does OPcache cache?
- Database query results
- User session data
- The compiled bytecode of your PHP scripts
- Rendered HTML fragments
Answer: The compiled bytecode of your PHP scripts. OPcache caches compiled bytecode so PHP doesn't reparse and recompile every .php file per request — a 2-3x speed-up, configured in php.ini.
How do you enable OPcache?
- Call opcache_start() in every script
- Set opcache.enable=1 in php.ini (no application code)
- composer require opcache
- Wrap each function in a cache() call
Answer: Set opcache.enable=1 in php.ini (no application code). OPcache is switched on in php.ini with opcache.enable=1; you write no application code for it.
What is a cache stampede (dog-piling)?
- The cache server runs out of disk
- Two users edit the same record
- A TTL is set too long
- A hot key expires and many requests miss at once, all running the same expensive query
Answer: A hot key expires and many requests miss at once, all running the same expensive query. When a popular key expires, dozens of requests miss simultaneously and all recompute, hammering the database. Locks, early expiration, and TTL jitter prevent it.
What is cache invalidation, and why isn't a TTL alone enough?
- Restarting the cache server nightly
- Deleting/overwriting a key when its source changes; a TTL only bounds how long it's wrong
- Encrypting cache keys
- Setting per_page limits
Answer: Deleting/overwriting a key when its source changes; a TTL only bounds how long it's wrong. On a write that affects a cached value you del() the key for immediate correctness; a TTL only limits the staleness window.
Which Redis command stores a value with an expiry (TTL)?
- get
- del
- setex (SET with EXpiry)
- ping
Answer: setex (SET with EXpiry). setex stores a value as a string with a TTL in seconds; del removes a key to invalidate it; get reads it (null on a miss).