Object oriented generators
The following code shows an object oriented way of implementing a well known generator function: to read lines from a large file.
class FileReader implements \Iterator { private $handle; private $current; public static function read(string $fileName) : FileReader { return new self($fileName); } public function __construct(string $fileName) { $this->handle = fopen($fileName, 'r'); $this->next(); } public function __destruct() { fclose($this->handle); } public function current() { return $this->current; } public function next() { $this->current = fgets($this->handle); } public function key() { return ftell($this->handle); } public function valid() { return !feof($this->handle); } public function rewind() { rewind($this->handle); } }
Using the file reader.
$lines = FileReader::read('path_to_large_file.txt'); foreach ($lines as $line) { echo $line; }
A comparison to using generators and the yield
keyword, based on the tests I ran:
- This approach takes the same amount of time to execute.
- It has the same memory footprint as a generator function.
- It has the benefit of easier re-usability (in my opinion).
In comparison to file_get_contents
: reading the same file required of 15MB of memory, whilst
this solution required only 2MB, because it only reads one line in memory at a time.
To round up, this is the generator solution using yield
.
function read($fileName) { $handle = fopen($fileName, 'r'); while (!feof($handle)) { yield fgets($handle); } fclose($handle); } $lines = read('path_to_large_file'); foreach ($lines as $line) { echo $line; }
👍