44

I know that glob can look for all files or only all directories inside a folder :

echo "All files:\n";
$all = glob("/*");
var_dump($all);

echo "Only directories\n";
$dirs = glob("/*", GLOB_ONLYDIR);
var_dump($dirs);

But I didn't found something to find only files in a single line efficiently.

$files = array_diff(glob("/*"), glob("/*", GLOB_ONLYDIR));

Works well but reads directory twice (even if there are some optimizations that make the second browsing quicker).

Deepak Rai
  • 2,163
  • 3
  • 21
  • 36
Alain Tiemblo
  • 36,099
  • 17
  • 121
  • 153

8 Answers8

62

I finally found a solution :

echo "Only files\n";
$files = array_filter(glob("/*"), 'is_file');
var_dump($files);

But take care, array_filter will preserve numeric keys : use array_values if you need to reindex the array.

Steve Tauber
  • 9,551
  • 5
  • 42
  • 46
Alain Tiemblo
  • 36,099
  • 17
  • 121
  • 153
  • 1
    Nice! Using foreach loop will resolve the issue of preserved numeric keys from array_filter. – Imdad Nov 27 '15 at 11:30
7

You can use GLOB_BRACE to match documents against a list of known file extensions:

$files = glob("/path/to/directory/*.{jpg,gif,png,html,htm,php,ini}", GLOB_BRACE);

see: http://www.electrictoolbox.com/php-glob-find-files/

RafaSashi
  • 16,483
  • 8
  • 84
  • 94
  • 10
    Will work in most cases, but take care as directories can also be named `toto.jpg` if you wish. – Alain Tiemblo Jun 10 '14 at 20:06
  • I learned that having a space between the file types in the brace does in fact matter. Don't do it like I did and save yourself some time! – Joe McLean Mar 17 '23 at 01:59
7

There is an easier way, just one line:

$files = glob("/path/to/directory/*.{*}", GLOB_BRACE);

the {*} means all file endings, so every file, but no folder!

Dominik Nöth
  • 185
  • 2
  • 8
0

10% faster compared to the solution of @AlainTiemblo, because it does not use an additional is_file check:

$files = array_filter(glob("/*", GLOB_MARK), function($path){ return $path[ strlen($path) - 1 ] != '/'; });

Instead it uses the inbuild GLOB_MARK flag to add a slash to each directory and by that we are able to remove those entries through array_filter() and an anonymous function.

Since PHP 7.1.0 supports Negative numeric indices you can use this instead, too:

$files = array_filter(glob("/*", GLOB_MARK), function($path){return $path[-1] != '/';});

No relevant speed gain, but it helps avoiding the vertical scrollbar in this post ^^

As array_filter() preserve the keys you should consider re-indexing the array with array_values() afterwards:

$files = array_values($files);
mgutt
  • 5,867
  • 2
  • 50
  • 77
  • Nope! I thought it worked at first but it lists folders as well. – Jens Törnell Sep 08 '19 at 13:07
  • @JensTörnell php.net says: `GLOB_MARK` "Adds a slash to each directory returned" So if you get folders this would be a bug in `glob`. Did you test `glob("/*", GLOB_MARK)` alone to verify your discovery? Or did you use `$path[-1]` with an too old PHP version? – mgutt Sep 09 '19 at 08:39
  • Not compatible with Windows because "`GLOB_MARK` - Adds a slash (a backslash on Windows) to each directory returned" whereas the answer uses only '/' to check. – DrLightman Jan 24 '21 at 10:54
  • Jens and DrL: Change '/' to DIRECTORY_SEPARATOR in both proposed answers in both proposed answers (PHP 7.0 and later vs PHP before 7.0) then this answer will work on all operating systems where directory separator is one character. I have always used the array_filter with 'is _file' as the second parameter and found it plenty fast for typical directories but if you have a large enough directory to glob, this solution does work and should be marginally faster. – Ted Cohen Feb 18 '22 at 08:34
0

Invert regexp do the job.

preg_grep(
    ';^.*(\\\\|/)$;',
    glob("/*", GLOB_MARK),
    PREG_GREP_INVERT
);

\\\\ is for Windows backslash

Rob G
  • 1
0
function glob_file_only($path){
    return array_filter(glob($path,GLOB_MARK),function($file){
        return substr($file,-1)!=DIRECTORY_SEPARATOR;
    });
}

This builds on the work of others that answered. It only touches the directory once instead of twice and it works for windows as well as linux.

Suraj Rao
  • 29,388
  • 11
  • 94
  • 103
Ted Cohen
  • 1,017
  • 10
  • 17
-1

This worked for me. if this helps anyone.

for file_name in glob.glob('**/*', recursive=True):
    # we get all files and dirs
    if os.path.isfile(file_name):
        # we have only the files
code-freeze
  • 465
  • 8
  • 8
-10
$all = glob("/*.*");

this will list everything with a "." after the file name. so basically, all files.