Search code examples
phpdesign-patternsiteratordecorator

Decorate an Iterator in PHP - enable skipping


I have a class that is basically a decorator for PHP's DirectoryIterator. Each file's contents are processed by the class and then returned by the current() method. Currently, when the file is a dot file or the file cannot be processed, I return false from the current() method. But I would rather like to skip the dot- and unprocessable files, and only return processed data.

P.S. The code below is a simplified example. I don't want to pre-process all files in the constructor.

class Pages implements \Iterator
{
    public function __construct(string $path)
    {
        $this->di = new \DirectoryIterator($path);
    }

    public function rewind() {
        return $this->di->rewind();
    }

    public function current() {
        $file = $this->di->current();
        if($file->isDot()) {
            return false;
        }
        $content = file_get_contents($file->getPathName());
        if($content === 'Cannot be processed!') {
            return false;
        }
        return $content;
    }

    public function key() {
        return $this->di->key();
    }

    public function next() {
        return $this->di->next();
    }

    public function valid() {
        return $this->di->valid();
    }
}

Solution

  • You can greatly simplify the code of your own answer by extending the already existing abstract PHP class FilterIterator :

    class DirectoryFilterIterator
      extends FilterIterator
    {
      public function __construct(string $path) {
        parent::__construct(new DirectoryIterator($path));
      }
    
      public function accept() : bool {
        $current = parent::current();
        // let's not only ensure it's not a dot but that it's an actual readable file
        if(!$current->isFile() || !$current->isReadable()) {
          return false;
        }
        else {
          $contents = file_get_contents($current->getPathname());
          if($contents === 'Some value') {
            return false;
          }
        }
        
        return true;    
      }
    }
    
    foreach(new DirectoryFilterIterator('.') as $key => $value) {
      echo "$key -> $value\n";
    }
    
    

    As you can also see, instead of merely ensuring that the item is not a dot, I ensure the item is an actual readable file as I think that is a little more robust for your intentions.


    PS: As your original intention was to create a decorator/wrapper you can of course alter the constructor of DirectoryFilterIterator to accept a DirectoryIterator instead:

    public function __construct(DirectoryIterator $it) {
      parent::__construct($it);
    }
    
    

    ...and then change the instantiation to:

    foreach(new DirectoryFilterIterator(new DirectoryIterator('.')) as $key => $value) {
      echo "$key -> $value\n";
    }