Considering that the inner workings of this class are extremely easy to follow, move on and pay attention to the following one, which is a simple file helper that saves to - and fetches data from - a specified text file:
/** * Constructor */ public function __construct($file = '') { if ($file !== '') { $this->setFile($file); } }
/** * Set the target file */ public function setFile($file) { if (!file_exists($file)) { throw new \InvalidArgumentException('The target file ' . $file . ' does not exist.'); } $this->_file = $file; }
/** * Get the target file */ public function getFile() { return $this->_file; }
/** * Write the specified data to the target file */ public function write($data) { if (!$fp = fopen($this->_file, 'a+')) { throw new \UnexpectedValueException('Error opening the target file.'); } if (!fwrite($fp, $data . "\n")) { throw new \UnexpectedValueException('Error writing data to the target file.'); } fclose($fp); return $this; }
/** * Get the external iterator */ public function getIterator() { if ($this->_iterator === null) { if (($data = file_get_contents($this->_file)) !== false) { $data = explode("\n", $data); $this->_iterator = new ArrayCollection($data); } else { throw new \UnexpectedValueException('Error reading data from the target file.'); } } return $this->_iterator; } }
Regardless of the rather naive implementation of the previous file helper, it’s worth it to take a close look at - and analyze in detail - the implementation of its “getIterator()” method. Effectively, the method invokes internally the earlier array collection in order to retrieve the contents of its target file. But if this method is so simple, what’s so important about it? Well, since the helper implements the “IteratorAggregate” SPL interface, it permits us to transparently lazy-load file data!
If you still aren’t convinced, look at the following snippet, which will should make your doubts vanish in a snap:
<?php
use Utility\FileProcessor as FileProcessor;
// include the autoloader require_once 'Autoloader.php'; Autoloader::getInstance();
// create an instance of the FileProcessor class $fileProc = new FileProcessor;
// write some data to the target file $fileProc->write('This is the first line of the file') ->write('This is the second line of the file') ->write('This is the third line of the file');
// lazy-load file data foreach ($fileProc as $key => $value) { echo $value . '<br />'; }
/* displays the following
This is the first line of the file This is the second line of the file This is the third line of the file
*/
As the above code sample shows, using the file helper for reading/writing data to the specified target text file is actually a breeze. But by far the most engaging aspect of this class is its ability to fetch that data whenever an instance of it is placed inside a “foreach” construct.
Even when quite simplistic, this example shows in a nutshell how the use of an external iterator along with an implementer of the “IteratorAggregate” native interface can compose a hard-to-beat duo, especially when it comes to loading data on request, either from a file, a relational database or a web service.
But before you launch your code editor and start testing the file helper, let me tell you a some more good news. Since the data that it fetches from the target file is converted to an array before being retuned to the client code, it’s possible to improve its implementation by using the built-in ArrayIterator class, instead of appealing to a custom array collection.
Well, that improvement process deserves a close look, right? Therefore, in the following segment I’ll be refactoring the helper so it can use the aforementioned native class.