PHP
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Functions
-
Section 4: Object-Oriented PHP
-
Section 5: Working with Data
-
Section 6: Modern PHP (PHP 8)
-
Section 7: Working with Files and Networking
-
Section 8: Common PHP Frameworks Overview
-
Section 9: Tooling and Ecosystem
-
Section 10: Practical Projects
-
Section 11: Interview Practice
-
Section 12: More Standard Library
-
Section 13: Data Structures and Algorithms in PHP
-
Section 14: More Practice Exercises
-
Section 15: More Security and Best Practices
-
Section 16: More Testing and Tooling
-
Section 17: WordPress-Style CMS Concepts
-
Section 18: Advanced OOP Practice
-
Section 19: More Web Fundamentals
-
Section 20: Database Practice
-
Section 21: PHP Manual: Array Functions
-
Section 22: PHP Manual: Date and Calendar Functions
-
Section 23: PHP Manual: Filesystem and Directory Functions
-
Section 24: PHP Manual: Filter and Var Handling
-
Section 25: PHP Manual: Math Functions
-
Section 26: PHP Manual: JSON and XML
-
Section 27: PHP Manual: Network and Stream Functions
-
Section 28: PHP Manual: Error and Exception Handling
-
Section 29: PHP Manual: Output Control and Misc
-
Section 30: PHP Manual: FTP, Zip, and Mail
-
Section 31: Modern PHP Frameworks Deep Dive
-
Section 32: PHP Design Patterns
-
Section 33: More Practice Exercises
-
Section 34: PHP Performance and Deployment
-
Section 35: More Interview Practice
-
Section 36: More PHP Standard Library
-
Section 37: PHP Concurrency and Async
-
Section 38: More Web Development Practice
-
Section 39: PHP Testing Deep Dive
-
Section 40: Composer and Package Development
-
Section 41: PHP Security Deep Dive
-
Section 42: More Practical Projects
-
Section 43: Legacy PHP Maintenance
-
Section 44: More Algorithm Practice
-
Section 45: Final Practice and Review
-
Section 46: PHP for E-Commerce Patterns
-
Section 47: PHP API Design Deep Dive
-
Section 48: PHP Caching Strategies
-
Section 49: PHP Queue and Background Jobs
-
Section 50: PHP Multi-Tenancy Patterns
-
Section 51: PHP Real-Time Features
-
Section 52: PHP CMS and Content Modeling
-
Section 53: PHP Internationalization
-
Section 54: More Framework-Specific Practice
-
Section 55: PHP Legacy Code Refactoring
-
Section 56: More Practice Projects Round 2
-
Section 57: PHP Command-Line Applications
-
Section 58: PHP and Microservices
-
Section 59: More Interview and Review Round 2
202: Caching with Redis in PHP
A few years ago, I was helping a colleague who had built a beautiful e-commerce storefront. Everything was great until they hit a flash sale. Suddenly, the site ground to a halt. When we looked at the logs, the database was screaming—specifically, one query that generated the main navigation category tree. It was a complex recursive query that ran on every single page load. Even though the categories only changed once a month, the server was recalculating that same tree ten thousand times a minute. He tried indexing the tables, but you can only optimize a query so much before you're fighting the laws of physics.
That's where Redis comes in. Instead of asking the database for the same answer over and over, we just store the final result in memory. Redis is an in-memory data structure store, which means it's orders of magnitude faster than a traditional disk-based database because it doesn't have to deal with the overhead of a relational engine or disk I/O.
Implementing the Cache-Aside Pattern
The most common way you'll use Redis in PHP is through the "Cache-Aside" pattern. The logic is simple: your application first checks the cache. If the data is there (a "cache hit"), you return it immediately. If it's not (a "cache miss"), you fetch it from the database, store it in Redis for next time, and then return it. I've seen developers make the mistake of trying to keep the cache and DB perfectly synced in real-time, but for most use cases, a simple "check, fetch, store" flow is all you need.
<?php
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$cacheKey = 'product_categories_tree';
$categories = $redis->get($cacheKey);
if ($categories === false) {
// Cache miss: This is the expensive part we want to avoid
$categories = $db->query("SELECT * FROM categories WHERE active = 1")->fetchAll();
// We store the result as a JSON string because Redis stores strings/blobs
$redis->set($cacheKey, json_encode($categories));
echo "Fetched from Database";
} else {
$categories = json_decode($categories, true);
echo "Fetched from Redis";
}
?>
Managing Data Freshness and TTLs
The biggest danger with caching is "stale data." If you cache your category tree forever, and then you add a new category in the admin panel, your customers won't see it until you manually clear the cache. I usually advise against "infinite" caches. Instead, use a Time-To-Live (TTL). By adding an expiration time, you ensure that the data eventually refreshes itself even if you forget to trigger a manual update.
In the example above, I used set(), but you should almost always use setex() (Set with Expiration). It allows you to define the lifetime of the key in seconds in a single atomic operation.
// Cache the data for 3600 seconds (1 hour)
$redis->setex($cacheKey, 3600, json_encode($categories));
If you have a specific event—like a product price update—you can also explicitly delete the cache key using $redis->del($cacheKey). This forces the next visitor to trigger a cache miss and fetch the fresh data from the database.
Handling Complex Data with Redis Hashes
Storing everything as a JSON string is fine for simple lists, but if you're caching individual user profiles or product details, JSON becomes inefficient. If you only need to update a user's "last_login" timestamp, you shouldn't have to decode a giant JSON blob, change one value, and re-encode the whole thing. This is where Redis Hashes come in.
Think of a Hash as a mini-dictionary inside a Redis key. You can set and get individual fields without touching the rest of the object. It keeps your memory usage tighter and your code cleaner when dealing with objects.
// Storing a user session as a hash
$redis->hMSet('user:123', [
'username' => 'dev_pro',
'email' => 'dev@example.com',
'role' => 'admin'
]);
// Fetching only the email
$email = $redis->hGet('user:123', 'email');📋 Practical Task
Exercise: Implementing a Cached Weather API Wrapper
You are building a weather dashboard that fetches data from a third-party API. The API has a strict rate limit, and the weather doesn't change every second, so fetching it on every page load is both wasteful and risky.
Your Task: Create a PHP script that implements the following logic:
- Define a cache key based on a city name (e.g.,
weather:london). - Check Redis to see if the weather data for that city already exists.
- If it exists, return the cached data.
- If it does not exist, simulate an API call (you can use a
sleep(2)to simulate network latency and a hardcoded array of weather data). - Store the result in Redis with a TTL of 15 minutes (900 seconds).
- Print a message to the screen indicating whether the data came from the "API" or the "Cache".
There are no comments for now.