102

I am using the following script to read a directory. If there is no file in the directory it should say empty. The problem is, it just keeps saying the directory is empty even though there ARE files inside and vice versa.

<?php
$pid = $_GET["prodref"];
$dir = '/assets/'.$pid.'/v';
$q   = (count(glob("$dir/*")) === 0) ? 'Empty' : 'Not empty';
    
if ($q=="Empty") 
    echo "the folder is empty"; 
else
    echo "the folder is NOT empty";
?>
1
  • 8
    It's just a typo in your if statement. Use == (compare) instead of the single = (assign). Commented Sep 21, 2011 at 9:56

22 Answers 22

159

It seems that you need scandir instead of glob, as glob can't see unix hidden files.

<?php
$pid = basename($_GET["prodref"]); //let's sanitize it a bit
$dir = "/assets/$pid/v";

if (is_dir_empty($dir)) {
  echo "the folder is empty"; 
}else{
  echo "the folder is NOT empty";
}

function is_dir_empty($dir) {
  return (count(scandir($dir)) == 2);
}

Note that this code is not the summit of efficiency, as it's unnecessary to read all the files only to tell if directory is empty. So, the better version would be

function dir_is_empty($dir) {
  $handle = opendir($dir);
  while (false !== ($entry = readdir($handle))) {
    if ($entry != "." && $entry != "..") {
      closedir($handle);
      return false;
    }
  }
  closedir($handle);
  return true;
}

By the way, do not use words to substitute boolean values. The very purpose of the latter is to tell you if something empty or not. An

a === b

expression already returns Empty or Non Empty in terms of programming language, false or true respectively - so, you can use the very result in control structures like IF() without any intermediate values

Sign up to request clarification or add additional context in comments.

14 Comments

I think both our code is wrong because I removed all files from the folder and it still says the folder is not empty... is there a way to check for hidden files like thumbs.db etc in linux??
I think the FTP folder is say .. and . in the file is empty. How can I check if and remove the .. and thumbs.db etc??
glob doesn't support linux hidden files. if you want them you have to go for openir solution like in the deleted answer
it seems you need scandir instead of glob.
Do create testing environment. create empty directory in the same folder where script is. make $dir = 'testfolder'; manually. then run this code. debug is printing out as much information as possible to see what is going wrong. $dir = 'testfolder';var_dump(scan_dir($dir)); will tell you what is in this directory
|
88

I think using the FilesystemIterator should be the fastest and easiest way:

// PHP 5 >= 5.3.0
$iterator = new \FilesystemIterator($dir);
$isDirEmpty = !$iterator->valid();

Or using class member access on instantiation:

// PHP 5 >= 5.4.0
$isDirEmpty = !(new \FilesystemIterator($dir))->valid();

This works because a new FilesystemIterator will initially point to the first file in the folder - if there are no files in the folder, valid() will return false. (see documentation here.)

As pointed out by abdulmanov.ilmir, optionally check if the directory exists before using the FileSystemIterator because otherwise it'll throw an UnexpectedValueException.

3 Comments

Mmm... tingling in my loins for this one.
You should consider that if $dir is not exists then an exception will be thrown.
Short and sweet.
15

I found a quick solution

<?php
  $dir = 'directory'; // dir path assign here
  echo (count(glob("$dir/*")) === 0) ? 'Empty' : 'Not empty';
?>

2 Comments

The solution I use in php 5.2 for simple case.
Beware that glob does not see hidden files. A directory containing ie ".htaccess" will be treated as empty with this code
7

For a object oriented approach using the RecursiveDirectoryIterator from the Standard PHP Library (SPL).

<?php

namespace My\Folder;

use RecursiveDirectoryIterator;

class FileHelper
{
    /**
     * @param string $dir
     * @return bool
     */
    public static function isEmpty($dir)
    {
        $di = new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS);
        return iterator_count($di) === 0;
    }
}

No need to make an instance of your FileHelper whenever you need it, you can access this static method wherever you need it like this:

FileHelper::isEmpty($dir);

The FileHelper class can be extended with other useful methods for copying, deleting, renaming, etc.

There is no need to check the validity of the directory inside the method because if it is invalid the constructor of the RecursiveDirectoryIterator will throw an UnexpectedValueException which that covers that part sufficiently.

Comments

6

This is a very old thread, but I thought I'd give my ten cents. The other solutions didn't work for me.

Here is my solution:

function is_dir_empty($dir) {
    foreach (new DirectoryIterator($dir) as $fileInfo) {
        if($fileInfo->isDot()) continue;
        return false;
    }
    return true;
}

Short and sweet. Works like a charm.

Comments

5

use

if ($q == "Empty")

instead of

if ($q="Empty")

Comments

5

Try this:

<?php
$dirPath = "Add your path here";

$destdir = $dirPath;

$handle = opendir($destdir);
$c = 0;
while ($file = readdir($handle)&& $c<3) {
    $c++;
}

if ($c>2) {
    print "Not empty";
} else {
    print "Empty";
} 

?>

2 Comments

Thanks! I wrote it quite quickly and its my first post here @Piotr Nowicki
Sure mate, it's just my civic duty ;-) Welcome to StackOverflow!
5

I used:

if(is_readable($dir)&&count(scandir($dir))==2) ... //then the dir is empty

Comments

4

Probably because of assignment operator in if statement.

Change:

if ($q="Empty")

To:

if ($q=="Empty")

Comments

4

@ Your Common Sense

I think your performant example could be more performant using strict comparison:

function is_dir_empty($dir) {
  if (!is_readable($dir)) return null; 
  $handle = opendir($dir);
  while (false !== ($entry = readdir($handle))) {
    if ($entry !== '.' && $entry !== '..') { // <-- better use strict comparison here
      closedir($handle); // <-- always clean up! Close the directory stream
      return false;
    }
  }
  closedir($handle); // <-- always clean up! Close the directory stream
  return true;
}

4 Comments

Good point regarding clean up: The return false case is not taking care of it ;-)
@BeatChristen Thx for the hint! Fixed it.
What I don't understand is why you say that it is better to use strict comparision when comparing to "." and "..". The readdir() function will always return a string (or the false) so I don't see the point. I'd also like to add that cleaning up after yourself is indeed always a good idea, one would think that after return when the $handle variable goes out of scope, closedir() would happen automatically but I just wrote a little test program, and it doesn't. Which is strange because other stuff, like flock do happen automatically.
@AndréFiedler You are a genius, brilliant and responsible person. In all other answers, they first calculating total number of files and then checking if count > 0. But if a directory contains even one file, it is not empty.
4

Function count usage maybe slow on big array. isset is ever faster

This will work properly on PHP >= 5.4.0 (see Changelog here)

function dir_is_empty($path){ //$path is realpath or relative path

    $d = scandir($path, SCANDIR_SORT_NONE ); // get dir, without sorting improve performace (see Comment below). 

    if ($d){

        // avoid "count($d)", much faster on big array. 
        // Index 2 means that there is a third element after ".." and "."

        return !isset($d[2]); 
    }

    return false; // or throw an error
}

Otherwise, using @Your Common Sense solution it's better for avoid load file list on RAM

Thanks and vote up to @soger too, to improve this answer using SCANDIR_SORT_NONE option.

1 Comment

As long as we worry about performance, you may want to add the SCANDIR_SORT_NONE parameter too.
2

Just correct your code like this:

<?php
    $pid = $_GET["prodref"];
    $dir = '/assets/'.$pid.'/v';
    $q = count(glob("$dir/*")) == 0;

    if ($q) {
        echo "the folder is empty"; 
    } else {
        echo "the folder is NOT empty";
    }
?>

Comments

2

Well, still nobody came up with the clearest and quite short solution:

function isEmptyFolder($dir): bool
{
    return scandir($dir) === ['.', '..'];
}

For older PHP versions:

function isEmptyFolder($dir)
{
    return scandir($dir) === array('.', '..');
}

But normally we want to check before if the folder exists, because scandir will throw an exception if the folder doesn't exist. So if you want to ensure the folder exists but is empty (especially before the use of rmdir), use this:

function isEmptyFolder($dir): bool
{
    return is_dir($dir) && is_readable($dir) && scandir($dir) === ['.', '..'];
}

If you consider a non-existing folder technically as empty (well, somehow it is) then use this:

function isEmptyFolder($dir): bool
{
    return !is_dir($dir) || is_readable($dir) && scandir($dir) === ['.', '..'];
}

It's hard to consider all eventualities and how to react. What circumstances can you assume and which do you have to check? What if the name exists, but is a file, not a folder? What if the folder exists, but you don't have access to it? Do you want to create new files in it or delete it? Do you want to ignore the notorious Thumbs.db on Windows? It depends on your aim on that folder.

Regarding the performance issue the author of the accepted answer brings up: Well, I didn't do any performance checks, but I doubt that this is such a big issue. I bet scandir (now) uses the underlying OS function and this may have been optimized over various PHP versions to squeeze out more performance. So looping with PHP over files (now) is maybe slower even if you jump out very early. This might be only relevant with really big folders with many files. But then you might have a bigger problem than the performance thing to save some microseconds. Let's be honest: How often do you check for a folder being empty in a script? More than once? Maybe in an overnight maintenance cronjob but hardly in a regular webpage a user is waiting for.

Comments

1

Even an empty directory contains 2 files . and .., one is a link to the current directory and the second to the parent. Thus, you can use code like this:

$files = scandir("path to directory/");
if(count($files) == 2) {
  //do something if empty
}

1 Comment

Please add some explanation to your answer such that others can learn from it
0

I use this method in my Wordpress CSV 2 POST plugin.

    public function does_folder_contain_file_type( $path, $extension ){
        $all_files  = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $path ) );

        $html_files = new RegexIterator( $all_files, '/\.'.$extension.'/' );  

        foreach( $html_files as $file) {
            return true;// a file with $extension was found
        }   

    return false;// no files with our extension found
}

It works by specific extension but is easily changed to suit your needs by removing "new RegexIterator(" line. Count $all_files.

    public function does_folder_contain_file_type( $path, $extension ){
        $all_files  = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $path ) );

        return count( $all_files );
    }

Comments

0

I had a similar problem recently, although, the highest up-voted answer did not really work for me, hence, I had to come up with a similar solution. and again this may also not be the most efficient way to go about the problem,

I created a function like so

function is_empty_dir($dir)
   {
       if (is_dir($dir))
       {
            $objects = scandir($dir);
            foreach ($objects as $object)
            {
                if ($object != "." && $object != "..")
                {
                    if (filetype($dir."/".$object) == "dir")
                    {
                         return false;
                    } else { 
                        return false;
                    }
                }
            }
            reset($objects);
            return true;
       }

and used it to check for empty dricetory like so

if(is_empty_dir($path)){
            rmdir($path);
        }

Comments

0

You can use this:

function isEmptyDir($dir)
{
    return (($files = @scandir($dir)) && count($files) <= 2);
}

Comments

0

The first question is when is a directory empty? In a directory there are 2 files the '.' and '..'.
Next to that on a Mac there maybe the file '.DS_Store'. This file is created when some kind of content is added to the directory. If these 3 files are in the directory you may say the directory is empty. So to test if a directory is empty (without testing if $dir is a directory):

function isDirEmpty( $dir ) {
  $count = 0;
  foreach (new DirectoryIterator( $dir ) as $fileInfo) {
     if ( $fileInfo->isDot() || $fileInfo->getBasename() == '.DS_Store' ) {
        continue;
     }
     $count++;
  }
  return ($count === 0);
}

Comments

0

@Your Common Sense,@Enyby

Some improvement of your code:

function dir_is_empty($dir) {
    $handle = opendir($dir);
    $result = true;
    while (false !== ($entry = readdir($handle))) {
        if ($entry != "." && $entry != "..") {
            $result = false;
            break 2;
        }
    }
    closedir($handle);
    return $result;
}

I use a variable for storing the result and set it to true.
If the directory is empty the only files that are returned are . and .. (on a linux server, you could extend the condition for mac if you need to) and therefore the condition is true.
Then the value of result is set to false and break 2 exit the if and the while loop so the next statement executed is closedir.
Therefore the while loop will only have 3 circles before it will end regardless if the directory is empty or not.

Comments

0
$is_folder_empty = function(string $folder) : bool {
    if (!is_dir($folder))
        return TRUE;

    // This wont work on non linux OS.
    return is_null(shell_exec("ls {$folder}"));
};
$is_folder_empty2 = function(string $folder) : bool {
    if (!is_dir($folder))
        return TRUE;
    
    // Empty folders have two files in it. Single dot and
    // double dot.
    return count(scandir($folder)) === 2;
};

var_dump($is_folder_empty('/tmp/demo'));
var_dump($is_folder_empty2('/tmp/demo'));

Comments

0

You could also use this function snippet:

function isEmptyFolder(string $folder): bool
{
    $files = scandir($folder);

    foreach ($files as $fileName) {
        if (isTopReference($fileName)) {
            continue;
        }

        return false;
    }

    return true;
}

function isTopReference($fileName): bool
{
    return ($fileName === '.' || $fileName === '..');
}

Usage: if (isEmptyFolder('\folder\location'))

Comments

-1

I suggest the following code (notice the the last '|' is bitwise or not boolean)

function dir_not_empty($path){
        return opendir($path)
            && 
            ((readdir() && readdir() && readdir())|closedir());
    }

This function:

  • returns false if opendir fails

  • if opendir succeeds, it tries to read 3 times. If any attempt fails, it returns false, otherwise true. Before returning it calls closedir (beware of the bitwise or '|', not the bollean or '||').

closedir always returns null, so when or'ed with a value it does not affect the value.

If you can spot any error/bug please comment, because I am using this code in production and it would be useful to know.

2 Comments

Shorter code is not a virtue. A code, that requires a special note, is a vice. AFAIK, the order of . and .. is not guaranteed in all systems
@YourCommonSense, "Shorter code is not a virtue": true. But simpler code is. Moreover, the order has nothing to do with this. Their existence only is required (and guaranteed). If the directory has more than 2 entries it is not empty. Nobody said anything about order. This code makes sure that, no matter what happens, closedir is called. And can very easily be modified to return error information.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.