Reputation: 18542
I've been doing quite a bit of research and am coming to a conclusion I'd rather just implemented everything with dumb old scandir
and foreach
instead of going the iterator way.
Basically, I need a way to get contents of directories - optionally sorted, optionally recursive and optionally filtered by file name. Which IMO are the most basic functionality you would expect from any somewhat sophisticated file system traverser.
Well it turns out, neither RecursiveDirectoryIterator
nor FilesystemIterator
support sorting or filtering. One has to wrap them in a class that extends ArrayObject
to achieve sorting - or FilterIterator
for filtering. So to achieve both you have to write two classes wrapping everything in a myriad of levels and the code ends up looking outlandish and overcomplicated.
Am I missing something about the approach, or should I scratch my progress and rewrite everything in 20+ lines in simple stupid if/else/foreach
code?
Upvotes: 1
Views: 202
Reputation: 2916
Iterators found in the SPL aren't all "ready-to-go". They are basic blocks that can be used to create whatever you want. The FileSystemIterator
has some basic functionality for filtering maybe, but you want to create more yourself, and it's quite easy to do so.
The upside of using iterators like FilesystemIterator
and such, is that you detach the traversal logic from your filter logic from your sort logic from your business logic.
Say you have the following:
$dir = opendir('.');
while (($file = readdir($dir)) !== false) {
// business logic
}
and later on, you decide to filter only MP3 files:
$dir = opendir('.');
while (($file = readdir($dir)) !== false) {
if (preg_match('|\.mp3$|i', $file)) {
// business logic
}
}
But what about filtering MP3 and JPG files, or MP3 files that are less than 5MB and only a week old, and JPG files that are over 2MB, and what about multiple directories, or recursive directories etc? Your "foreach/while" loop becomes a nightmare, even though it started out quite nice.
By detaching your traversal logic from your business logic, means that it's easier to maintain, test and even reuse:
$it = new DirectoryIterator("."); // Dir iterator
$it = new RegexIterator($it, "|\.mp3$|i"); // filter MP3's
$it = new FilesizeIterator($it, "<6MB"); // Only less than 6MB
$it = new LimitIterator($it, 0, 100); // First 100 items only
foreach ($it as $file) {
// extrabonus: $file is a SplFileInfo object
}
It's easy to imagine how we could "build" the iterator logic into more complex examples, and we can reuse things like a RegexIterator
for other things besides filenames too. It's also much easier to test because we only need to check if regexFilterIterator
correctly filters regexes, that's it.
Sorting and such can be addded as well, based on whatever you like, but still: no changes in your business logic, and still detached from all other logic.
Take a look at Symfony2's Finder
component: it uses lots of iterators to filter and sort items.
Upvotes: 7