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
203: Caching with Memcached
Imagine you're a chef in a busy kitchen. In the back of the building, there's a massive walk-in freezer—that's your database. It has everything you need, but it's a long walk, the door is heavy, and it takes time to find exactly what you're looking for. If you have to walk to that freezer every single time a customer orders a salad just to get one leaf of lettuce, your service is going to be painfully slow.
To fix this, you use a "prep station" right next to your cutting board. You grab a big bowl of pre-washed lettuce from the freezer once, and you keep it right there in front of you. Now, when the next ten orders come in, you aren't walking; you're just reaching. That prep station is exactly what Memcached is for your PHP application.
In technical terms, the freezer is your disk-based database (like MySQL), and the prep station is Memcached—a high-performance, distributed memory object caching system. It stores data in RAM, which is orders of magnitude faster than reading from a disk.
Moving from the Pantry to the Prep Station
When we use Memcached, we don't just dump everything into it. We use a pattern I call "Check-Fetch-Store." You don't just ask the database for data; you ask the cache first. If it's there (a "cache hit"), you're done. If it's not (a "cache miss"), you go to the database, get the data, and then save a copy in the cache for the next person.
Let's look at a real example. Suppose you have a function that fetches a complex list of "Featured Products" from your database. This query involves three joins and takes about 500ms to run. That's a lifetime in web performance.
<?php
$m = new Memcached();
// We connect to the local Memcached server
$m->addServer('localhost', 11211);
$cacheKey = 'featured_products_list';
// 1. Check: Is the data already in our "prep station"?
$products = $m->get($cacheKey);
if ($products === false) {
// 2. Fetch: Cache miss! We have to walk to the "freezer" (Database)
echo "Fetching from database... ";
// Simulating a heavy database query
$products = $db->query("SELECT * FROM products WHERE featured = 1 LIMIT 10")->fetchAll();
// 3. Store: Put it in the cache so we don't have to do this again
// We'll set it to expire in 3600 seconds (1 hour)
$m->set($cacheKey, $products, 3600);
} else {
echo "Fetching from cache! ";
}
foreach ($products as $product) {
echo $product['name'] . "
";
}
?>
The Danger of Stale Data
Here is where most developers trip up: the "stale data" problem. If you cache your featured products for an hour, but you change a product's price in the database five minutes later, your customers will still see the old price for another 55 minutes. It's frustrating and can lead to support tickets.
You have two main ways to handle this. First, you can set a shorter TTL (Time To Live)—the third argument in the set() method. If the data changes frequently, maybe cache it for 300 seconds instead of 3600.
The second, more professional approach is "Cache Invalidation." Whenever you update a product in your admin panel, you should explicitly tell Memcached to delete that specific key. I usually do this by calling $m->delete($cacheKey) inside the update logic. This forces the next visitor to trigger a "cache miss," fetching the fresh data from the database and re-caching it. It's the equivalent of the chef throwing away the wilted lettuce and grabbing a fresh bowl from the freezer.
When NOT to use Memcached
I've seen people try to cache everything, and it usually ends in a mess. Don't use Memcached for data that is unique to every single user (like a personal shopping cart) unless you have a very specific reason and a lot of RAM. The real power of Memcached comes from caching "expensive" data that is shared across many users—like site settings, top-level categories, or global API responses.
📋 Practical Task
Exercise: Optimizing a Heavy Category Page
You are working on an e-commerce site where the "Category Tree" (a nested list of all product categories) is generated by a recursive database query. This query is incredibly slow and runs on every single page load because the menu appears on every page.
Your Task:
- Create a PHP script that implements the "Check-Fetch-Store" pattern using the
Memcachedclass. - Use the cache key
'site_category_tree'. - Simulate the "heavy database query" by using
usleep(500000);(this pauses the script for half a second) and returning a hard-coded array of categories. - Set the cache expiration to 15 minutes.
- Add a simple toggle in your code (e.g., a
GETparameter like?refresh=1) that, when present, callsdelete()on the cache key to simulate an admin updating the categories.
Goal: When you refresh the page the first time, you should see a delay. Every subsequent refresh should be nearly instant. When you add ?refresh=1 to the URL, the next load should be slow again as the cache is rebuilt.
There are no comments for now.