Wyck
Wyck

Reputation: 2023

How to exclude file types from Directory Iterator loop

Simple directory iterator that is recursive and shows all files and directories/sub-directories.

I don't see any built in function to exclude certain file types, for instance in the following example I do not want to output any image related files such as .jpg, .png, etc. I know there are several methods of doing this , looking for advice on which would be best.

$scan_it = new RecursiveDirectoryIterator("/example_dir");

 foreach(new RecursiveIteratorIterator($scan_it) as $file) {

  echo $file;
  }

Upvotes: 4

Views: 16102

Answers (7)

Lucas Bustamante
Lucas Bustamante

Reputation: 17198

This is how I would do it:

// Get all files ending with ".php" in folder
$folder = __DIR__ . '/example_dir';

$iterator = new RegexIterator(
    new DirectoryIterator($folder), // You can change this to RecursiveDirectoryIterator it you want
    '/\.php$/i',
    RegexIterator::MATCH
);

/** @var SplFileInfo $field_file */
foreach ($r_iterator as $field_file) {
    echo $field_file->getPathname();
}

Upvotes: 0

Yash
Yash

Reputation: 7064

I love examples and here is one

I was looking a way to filter extension.You can include/ exclude extensions.

<?php

$dir_iterator = new RecursiveDirectoryIterator("/path");
$iterator = new RecursiveIteratorIterator($dir_iterator, RecursiveIteratorIterator::SELF_FIRST);
// could use CHILD_FIRST if you so wish

foreach ($iterator as $file) {
    echo $file, "\n";
}

?>

$file will be an SplFileInfo class, so you can do powerful stuff really easily:

<?php


foreach ($iterator as $file) {
    if ($file->isFile()) {
        echo $file->getExtension(); //By this you are king and you can access more functions of SplFileInfo
    }
}



?>

Upvotes: 1

OzzyCzech
OzzyCzech

Reputation: 10342

From PHP 5.4 can use \RecursiveCallbackFilterIterator

$iterator = new \RecursiveDirectoryIterator(getcwd(), \RecursiveDirectoryIterator::SKIP_DOTS);

$iterator = new \RecursiveCallbackFilterIterator(
  $iterator,
  function ($item) {
    return $item->getExtension() === 'php' ? true : false;
  }
);

Iterator now will contains only PHP files.

 foreach($iterator as $file) {
   echo $file;
 }

Upvotes: 6

drzaus
drzaus

Reputation: 24994

Cross-posting original answer (also in this question) --

Turning glob into an iterator seems to prefilter more easily than writing a custom FilterIterator. Also note that FilterIterator still steps through each item during iteration, just ignores it, whereas glob doesn't seem to. However, glob seems to include rather than exclude, so may not fit your scenario.

No prefilter:

$dir_iterator = new DirectoryIterator($dir);
$paginated = new LimitIterator($dir_iterator, $page * $perpage, $perpage);

Glob prefilter:

$dir_glob = $dir . '/*.{jpg,gif,png}';

$dir_iterator = new ArrayObject(glob($dir_glob, GLOB_BRACE)); // need to get iterator
$dir_iterator = $dir_iterator->getIterator();
$paginated = new LimitIterator($dir_iterator, $page * $perpage, $perpage);

Then, do your thing:

foreach ($paginated as $file) { ... }

Note that in the case of the DirectoryIterator example, $file will be an instance of SplFileInfo, whereas glob example is just the disk path.


Example FilterIterator extension

class ExtensionFilterIterator extends FilterIterator {
    private $filter;

    public function __construct(Iterator $iterator , $filter) {
        parent::__construct($iterator);
        $this->filter = $filter;
    }

    // the meat and potatoes
    public function accept() {
        $current = $this->getInnerIterator()->current();

        ### global $counter;
        ### print_r(array($counter++, $current)); // this proves it goes through the whole thing, even with limit

    // do your comparison


        // assume path
        if( is_string($current) ) {
            $extension = end( explode('.', $current) );
        }
        // assume DirectoryIterator
        else {
            // $ext = $fileinfo->getExtension(); // http://www.php.net/manual/en/class.splfileinfo.php
            $extension = pathinfo($current->getFilename(), PATHINFO_EXTENSION); // < PHP 5.3.6 -- http://www.php.net/manual/en/splfileinfo.getextension.php
        }

        return !    in_array($extension,$this->filter);
    }
}

Usage:

$dir_iterator = new ExtensionFilterIterator(new DirectoryIterator($dir), array('gif', 'jpg', 'png'));
$paginated = new LimitIterator($dir_iterator, $page * $perpage, $perpage);

Upvotes: 2

John Carter
John Carter

Reputation: 55271

Expanding on the other answers, you could do the filtering in a cleaner way by specialising RecursiveFilterIterator. Eg, the finfo-based approach:

class MyRecursiveFilterIterator extends RecursiveFilterIterator
{
    private $finfo;

    function __construct(RecursiveIterator $i)
    {
        $this->finfo = new finfo(FILEINFO_MIME_TYPE);
        parent::__construct($i);
    }

    /**
     * Filter out files with a MIME type of image/*
     */
    public function accept()
    {
        $file = $this->current();
        $filetype = $this->finfo->file($file);

        $type_parts = explode("/", $filetype, 2);
        $type = $type_parts[0];

        return ("image" !== $type);
    }

}

$scan_it = new RecursiveDirectoryIterator(".");

foreach (new RecursiveIteratorIterator(
            new MyRecursiveFilterIterator($scan_it)) as $file)
{
    print ("$file\n");
}

Similarly you could use RecursiveRegexIterator if you want to use the filename-based approach.

Upvotes: 1

Master_ex
Master_ex

Reputation: 779

You can use the finfo_file PHP function that returns information about a file.

The functions in this module try to guess the content type and encoding of a file by looking for certain magic byte sequences at specific positions within the file. While this is not a bullet proof approach the heuristics used do a very good job.

Code

<?php
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$scan_it = new RecursiveDirectoryIterator("/example_dir");

 foreach(new RecursiveIteratorIterator($scan_it) as $file) {
  echo finfo_file($finfo, $file) . "<br/>";
  }
finfo_close($finfo);
?>

The output should be something like:

image/png
directory
image/png
image/svg+xml

Edit

So you can take the non-image files with something like that:

if(!preg_match("/image.*/",finfo_file($finfo, $file)))

Upvotes: 0

anon
anon

Reputation:

Update: Ok, so I'm an idiot. PHP has a builtin for this: pathinfo()

Try this:

$filetypes = array("jpg", "png");
$filetype = pathinfo($file, PATHINFO_EXTENSION);
if (!in_array(strtolower($filetype), $filetypes)) {
  echo $file;
}

Original Answer:

Why not just run substr() on the filename and see if it matches the extension of the file type you want to exclude:

$scan_it = new RecursiveDirectoryIterator("/example_dir");

foreach(new RecursiveIteratorIterator($scan_it) as $file) {
  if (strtolower(substr($file, -4)) != ".jpg" && 
      strtolower(substr($file, -4)) != ".jpg") {
    echo $file;
  }
}

You could make it easier by using regular expressions:

if (!preg_match("/\.(jpg|png)*$/i", $file, $matches)) {
   echo $file;
}

You could even use an array to keep track of your file types:

$filetypes = array("jpg", "png");
if (!preg_match("/\.(" . implode("|", $filetypes) . ")*$/i", $file, $matches)) {
   echo $file;
}

Upvotes: 12

Related Questions