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
124: The Iterator and Generator Interfaces in PHP
A lot of developers I've mentored usually hit a wall when they first encounter the Iterator interface. They tell me, "Why would I bother implementing five different methods just to loop through some data? I can just return an array and use a foreach loop."
The "Just Return an Array" Fallacy
The problem with returning an array is that you're forcing PHP to load every single element into memory before the loop even starts. If you're dealing with a few dozen database rows, sure, it doesn't matter. But what happens when you're parsing a 500MB log file or streaming a million records from a legacy API?
// The "wrong" way for large datasets
function getLargeLogFile($filename) {
$lines = file($filename); // This loads the WHOLE file into RAM.
return $lines;
}
// If the file is 1GB and your memory limit is 128MB,
// this crashes before the first iteration of the loop.
foreach (getLargeLogFile('access.log') as $line) {
echo $line;
}
This is where the Iterator interface comes in. It doesn't give you the data all at once; it gives you a mechanism to fetch the next piece of data only when the loop actually asks for it.
Custom Traversal via the Iterator Interface
To make an object "iterable," you implement the Iterator interface. This requires five methods: current(), key(), next(), rewind(), and valid(). Itβs a bit of a chore to write, but it gives you total control over the pointer.
class LogFileIterator implements Iterator {
private $handle;
private $currentLine;
private $position = 0;
public function __construct($filename) {
$this->handle = fopen($filename, 'r');
}
public function rewind(): void {
rewind($this->handle);
$this->position = 0;
}
public function current(): mixed {
return $this->currentLine;
}
public function key(): mixed {
return $this->position;
}
public function next(): void {
$this->currentLine = fgets($this->handle);
$this->position++;
}
public function valid(): bool {
return !feof($this->handle);
}
}
Now, when you use this in a foreach, PHP calls these methods internally. The file is read line-by-line. Your memory usage stays flat regardless of whether the log file is 10KB or 10GB. I've seen this single change save production servers from constant Out-of-Memory (OOM) crashes.
Cutting the Boilerplate with Generators
Implementing Iterator is powerful, but let's be honest: writing those five methods every time is tedious. That's why PHP introduced Generators. A Generator is essentially a simplified way to create an Iterator without the class boilerplate, using the yield keyword.
When a function contains yield, it no longer returns a value immediately. Instead, it returns a Generator object. When the loop asks for the next value, the function execution resumes exactly where it left off.
function getLogLinesGenerator($filename) {
$handle = fopen($filename, 'r');
try {
while (($line = fgets($handle)) !== false) {
yield $line; // The function "pauses" here and returns the value
}
} finally {
fclose($handle);
}
}
// This looks like a simple array loop, but it's using a Generator under the hood.
foreach (getLogLinesGenerator('access.log') as $line) {
if (str_contains($line, 'ERROR')) {
echo $line;
}
}
The yield keyword handles the current(), next(), and valid() logic for you automatically. It's cleaner, faster to write, and just as memory-efficient. If you find yourself building a custom class just to iterate over a resource, ask yourself if a Generator could do the job in five lines instead of thirty.
π Practical Task
Exercise: Memory-Efficient User Activity Filter
You are tasked with processing a massive CSV file containing user activity logs. The file is too large to be loaded into an array. You need to create a generator that reads the file and only yields rows where the "action" column (the second column) is equal to 'purchase'.
Requirements:
- Create a function called
filterPurchases($filename). - The function must use
yieldto return rows one by one. - It must open the file using
fopen()and read it usingfgetcsv()to ensure memory efficiency. - The generator should only yield the row if the second element of the CSV array is
'purchase'. - Ensure the file handle is closed after the loop finishes (hint: use a
try...finallyblock).
Test your code with this logic:
$purchases = filterPurchases('activity_log.csv');
foreach ($purchases as $row) {
echo "User {$row[0]} made a purchase!\n";
}
There are no comments for now.