173

I'm looking for a PHP script that loops through all of the files in a directory so I can do things with the filename, such as format, print or add it to a link. I'd like to be able to sort the files by name, type or by date created/added/modified. (Think fancy directory "index".) I'd also like to be able to add exclusions to the list of files, such as the script itself or other "system" files. (Like the . and .. "directories".)

Being that I'd like to be able to modify the script, I'm more interested in looking at the PHP docs and learning how to write one myself. That said, if there are any existing scripts, tutorials and whatnot, please let me know.

1

10 Answers 10

302

You can use the DirectoryIterator. Example from php Manual:

<?php
$dir = new DirectoryIterator(dirname(__FILE__));
foreach ($dir as $fileinfo) {
    if (!$fileinfo->isDot()) {
        var_dump($fileinfo->getFilename());
    }
}
?>
Sign up to request clarification or add additional context in comments.

6 Comments

Note: many servers don't have SPL installed, so you won't be able to use the DirectoryIterator class (see my alternative post below). Use this if you can though!
Note[2]: Make sure you understand that the dirname() function above will get the parent folder of whatever path you put there. In my case, I assumed dirname was a wrapper for the directory name/path, so it was not needed.
Also, if dirname it's a large filesystem, problems with memory are evident. On my case with 1 millons of files, app needs ~512M ram on memory_limit.
If you need the complete path like /home/examples/banana.jpg use $fileinfo->getPathname()
You can use !$fileinfo->isDir() to avoid action on directories
|
51

If you don't have access to DirectoryIterator class try this:

<?php
$path = "/path/to/files";

if ($handle = opendir($path)) {
    while (false !== ($file = readdir($handle))) {
        if ('.' === $file) continue;
        if ('..' === $file) continue;

        // do something with the file
    }
    closedir($handle);
}
?>

4 Comments

Can you name a situation where you wouldn't have access to it?
A lot of legacy applications use PHP 4, which has no access to the DirectoryIterator.
Why '.' === $file? This isn't Java.
Dave... Nope it is matching the dots and does not continue if it doesn't match in PHP. Search for the difference between == and ===.
40

Use the scandir() function:

<?php
    $directory = '/path/to/files';

    if (!is_dir($directory)) {
        exit('Invalid diretory path');
    }

    $files = array();
    foreach (scandir($directory) as $file) {
        if ($file !== '.' && $file !== '..') {
            $files[] = $file;
        }
    }

    var_dump($files);
?>

Comments

24

You can also make use of FilesystemIterator. It requires even less code then DirectoryIterator, and automatically removes . and ...

// Let's traverse the images directory
$fileSystemIterator = new FilesystemIterator('images');

$entries = array();
foreach ($fileSystemIterator as $fileInfo){
    $entries[] = $fileInfo->getFilename();
}

var_dump($entries);

//OUTPUT
object(FilesystemIterator)[1]

array (size=14)
  0 => string 'aa[1].jpg' (length=9)
  1 => string 'Chrysanthemum.jpg' (length=17)
  2 => string 'Desert.jpg' (length=10)
  3 => string 'giphy_billclinton_sad.gif' (length=25)
  4 => string 'giphy_shut_your.gif' (length=19)
  5 => string 'Hydrangeas.jpg' (length=14)
  6 => string 'Jellyfish.jpg' (length=13)
  7 => string 'Koala.jpg' (length=9)
  8 => string 'Lighthouse.jpg' (length=14)
  9 => string 'Penguins.jpg' (length=12)
  10 => string 'pnggrad16rgb.png' (length=16)
  11 => string 'pnggrad16rgba.png' (length=17)
  12 => string 'pnggradHDrgba.png' (length=17)
  13 => string 'Tulips.jpg' (length=10)

Link: http://php.net/manual/en/class.filesystemiterator.php

Comments

6

You can use this code to loop through a directory recursively:

$path = "/home/myhome";
$rdi = new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::KEY_AS_PATHNAME);
foreach (new RecursiveIteratorIterator($rdi, RecursiveIteratorIterator::SELF_FIRST) as $file => $info) {
    echo $file."\n";
}

Comments

5

Most of the time I imagine you want to skip . and ... Here is that with recursion:

<?php

$rdi = new RecursiveDirectoryIterator('.', FilesystemIterator::SKIP_DOTS);
$rii = new RecursiveIteratorIterator($rdi);

foreach ($rii as $di) {
   echo $di->getFilename(), "\n";
}

https://php.net/class.recursivedirectoryiterator

Comments

4

glob() has provisions for sorting and pattern matching. Since the return value is an array, you can do most of everything else you need.

5 Comments

This is good unless you are dealing with a lot of files... > 10,000. You'll run out of memory.
@NexusRex: You shouldn't be reading 10,000 records from a database either, but that's out of scope as far as the question is concerned
Agreed! If reading from a database you can paginate with "limit" though—no such luck when you have a directory with 5 million XML files to iterate through.
There is SPL GlobIterator.
This is awesome for the correct use. In my case I want to purge the downloads folder each night for a small company website. I want to be able to have subdirectories in the downloads folder and this is the solution I was looking for. Thanks for posting!
3

For completeness (since this seems to be a high-traffic page), let's not forget the good old dir() function:

$entries = [];
$d = dir("/"); // dir to scan
while (false !== ($entry = $d->read())) { // mind the strict bool check!
    if ($entry[0] == '.') continue; // ignore anything starting with a dot
    $entries[] = $entry;
}
$d->close();
sort($entries); // or whatever desired

print_r($entries);

Comments

0

you can do this as well

$path = "/public";

$objects = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path), RecursiveIteratorIterator::SELF_FIRST);

foreach ($objects as $name => $object) {
  if ('.' === $object) continue;
  if ('..' === $object) continue;

str_replace('/public/', '/', $object->getPathname());

// for example : /public/admin/image.png => /admin/image.png

Comments

-1

There is a problem with using the accepted answer's suggestion of DirectoryIterator. After all, just load up the documentation page for the iterator and see if you can find any problems -- in fact, look at the available methods. Do you see any method for directoryiterator to determine if an entry in a folder is a folder or not? Why is there no isFolder() or isDirectory() or anything that can be called on files? There's getFilename(), but why can't I determine if I have another folder that needs another directoryiterator?

In that event, you need to use is_dir() in combination with DirectoryIterator. Here is a recursive example that separates out files from folders and then recursively iterates over newly-discovered folders:

$dir = '/some_directory/';

function parseDir($dir) {
    foreach (new DirectoryIterator($dir) as $file) {
        if($file->isDot()) continue;
        $new_item = $dir . $file->getFilename() . '/';
        if(is_dir($new_item)) {
            print('Directory found!' . $new_item . PHP_EOL);
            parseDir($new_item);    # Recursion
        } else {
            print('File found!' . $new_item . PHP_EOL);
        }
    }
}

2 Comments

This is incorrect. Check the documentation for SpfFileInfo. The methods isDir and isFile are publicly available
"why can't I determine if I have another folder that needs another directoryiterator" - Have a look at the RecursiveDirectoryIterator

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.