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
153: SplObjectStorage and WeakMap in PHP
Imagine you're running a high-end coat check at a gala. When a guest hands you their coat, you don't sew a name tag directly into the fabric of the coat—that would be intrusive and probably ruin the garment. Instead, you give the coat a ticket and record some information about it (like which wardrobe rack it's on) in your ledger. The coat remains unchanged, but you have a way to associate extra data with that specific physical object.
In PHP, this is exactly the problem SplObjectStorage and WeakMap solve. Usually, if you want to associate data with an object, you'd either add a property to the class or use an array. But arrays only take strings or integers as keys. You can't use an object as an array key. These two tools allow you to use the object itself as the key.
Giving Objects Metadata Without Touching the Class
Let's say you're building a game engine. You have GameCharacter objects, but you have a separate CombatManager that needs to track which characters are currently "stunned" and for how many turns. You don't want to add a $stunTurns property to the GameCharacter class because that's a combat-specific detail, not a core character attribute.
This is where SplObjectStorage shines. It's essentially a set that can also store data associated with each element.
class GameCharacter {
public function __construct(public string $name) {}
}
$combatManager = new SplObjectStorage();
$hero = new GameCharacter("Alistair");
$goblin = new GameCharacter("Grog");
// Attach data (stun turns) to the object
$combatManager[$hero] = 2;
$combatManager[$goblin] = 1;
if (isset($combatManager[$hero])) {
echo "{$hero->name} is stunned for {$combatManager[$hero]} turns!";
}
I love this approach because it keeps your domain models clean. The GameCharacter doesn't need to know it's being tracked by a combat system; it just exists.
The Memory Leak Trap and the WeakMap Solution
Here is the catch with SplObjectStorage: it creates a "strong reference" to the object. As long as that object is inside your storage, PHP's garbage collector cannot delete it from memory, even if you unset() the original variable. In a long-running script (like a ReactPHP server or a complex worker), this is a recipe for a memory leak.
Enter WeakMap. A WeakMap works almost identically to SplObjectStorage, but it holds a "weak reference." If the object is no longer referenced anywhere else in your code, PHP will reclaim that memory and automatically remove the entry from the WeakMap.
I typically use WeakMap for caching calculated values. For instance, if you have a heavy computation based on an object, you can cache the result without preventing the object from being destroyed when it's no longer needed.
class HeavyDocument {
public function __construct(public string $content) {}
}
$cache = new WeakMap();
$doc = new HeavyDocument("A very long legal text...");
// Calculate a hash and store it in the WeakMap
$cache[$doc] = md5($doc->content);
unset($doc);
// Now, the HeavyDocument object is gone from memory,
// and the entry in $cache is automatically deleted.
Which one should you actually use?
If you need a collection that behaves like a set (checking if an object exists in a group) and you intend for that collection to "own" the objects, go with SplObjectStorage. It also provides a useful internal pointer for iterating through objects.
If you are attaching metadata, caches, or "sidecar" data to objects that are managed elsewhere in your application, always use WeakMap. It's the safer choice for memory management and avoids the headache of manually cleaning up your maps when objects are destroyed.
📋 Practical Task
Exercise: Implementing a Non-Intrusive Entity Permissions Cache
You are building a system where User objects are passed around, and you need to track their temporary "session permissions" without modifying the User class itself. Because users log in and out frequently, you must ensure that when a User object is destroyed, their permissions cache is automatically cleared to prevent memory leaks.
Your Task:
- Create a
Userclass with a$usernameproperty. - Create a
PermissionManagerclass. - Inside
PermissionManager, implement a privateWeakMapto store permissions. - Implement a method
assignPermission(User $user, string $permission)that stores the permission string associated with that user. - Implement a method
hasPermission(User $user): boolthat checks if the user has a specific permission. - In your test code, create a user, assign a permission, verify it exists, then
unset()the user and explain (via a comment) why aWeakMapwas necessary here instead of an array orSplObjectStorage.
There are no comments for now.