PHP: File Listing Class
This PHP class builds on our earlier directory listing functions and examples. The class is small and simple allowing us to extract specific file information from a single directory, or by recursing through a directory tree.
Basic usage
Our FileList PHP class comes with the following public methods:
- void set_keys( ...$keys );
- specify which values to return for each file
- void add_filter( $key, Array );
- return only files where $key matches one of the Array values
- void recurse(); or recurse( $depth );
- whether to recurse, and how far
- array scan( $directory );
- scan $directory returning an array of file details
For example, if we want to find all the XML and PDF files in the files/ directory, we use:
<?PHP
$filelist = new \Chirp\FileList();
$filelist->add_filter('mime_type', ['application/xml', 'application/pdf']);
$files = $filelist->scan("files/");
?>
This populates the $files array with details of any matching files:
Array
(
[0] => Array
(
[mime_type] => application/xml
[type] => file
[pathname] => files/ajax1.xml
[size] => 155
)
[1] => Array
(
[mime_type] => application/xml
[type] => file
[pathname] => files/ajax2.xml
[size] => 190
)
)
The list of support $key values is given below.
The FileList class
<?PHP
namespace Chirp;
// Original PHP code by Chirp Internet: www.chirpinternet.eu
// Please acknowledge use of this code by including this header.
class FileList
{
private $keys = ['type', 'pathname', 'mime_type', 'size']; // default values to extract
private $filters = [];
private $recurse = FALSE;
private $depth = 0;
public function set_keys(...$keys)
{
$this->keys = $keys;
}
public function add_filter($key, Array $values)
{
$this->filters[$key] = $values;
}
public function recurse($depth = 0)
{
$this->recurse = TRUE;
$this->depth = $depth;
}
private function extract(\SplFileInfo $finfo, $key)
{
switch($key)
{
case 'basename':
return $finfo->getBasename("." . $finfo->getExtension());
case 'ext':
return $finfo->getExtension();
case 'filename':
return $finfo->getFilename();
case 'imagesize':
return getimagesize($finfo->getRealpath());
case 'mime_type':
return mime_content_type($finfo->getRealpath());
case 'mtime':
return $finfo->getMTime();
case 'path':
return $finfo->getPath();
case 'pathname':
return $finfo->getPathname();
case 'realpath':
return $finfo->getRealPath();
case 'size':
return $finfo->getSize();
case 'type':
return $finfo->getType();
default:
error_log(__METHOD__ . ": unsupported key: '{$key}'");
$this->keys = array_diff($this->keys, [$key]);
unset($this->filters[$key]);
return FALSE;
}
}
public function scan($dir, $depth = 0)
{
$retval = [];
if($this->recurse && $this->depth && ($depth > $this->depth)) {
return $retval;
}
if(substr($dir, -1) != "/") { // add trailing slash if missing
$dir .= "/";
}
try { // open directory for reading
$d = new \DirectoryIterator($dir);
} catch(\UnexpectedValueException $e) {
error_log(__METHOD__ . ": " . $e->getMessage());
return $retval;
}
foreach($d as $fileinfo) {
if($fileinfo->isDot()) { // skip hidden files
continue;
}
if($this->recurse && ('dir' === $this->extract($fileinfo, 'type'))) {
$dir_contents = $this->scan($this->extract($fileinfo, 'pathname'), $depth + 1);
if($dir_contents) {
$retval = array_merge($retval, $dir_contents);
continue;
}
}
$file_struct = [];
foreach(array_unique(array_merge(array_keys($this->filters), $this->keys)) as $key) {
$file_struct[$key] = $this->extract($fileinfo, $key);
if(isset($this->filters[$key]) && !in_array($file_struct[$key], $this->filters[$key])) {
continue 2;
}
}
$retval[] = $file_struct;
}
return $retval;
}
}
?>
As you can see most of the work is done by the DirectoryIterator SPL class which returns a list (iterator) of SplFileInfo objects which we can query as we want.
For each file encountered we first check the filters values, and then the keys values. The array returned for each file will include all values specified in either list.
Coding-wise, the most interesting feature is the new ... token which indicates a variable-length argument list. This is only available in PHP5.6+ and in earlier versions would need to be replaced by either an Array or a call to func_get_args.
Enabling Recursion
By default the FileList class will scan only a single directory, but enabling recursion is as simple as:
$filelist = new \Chirp\FileList();
$filelist->recurse();
Or, if you only want to go down one directory level:
$filelist = new \Chirp\FileList();
$filelist->recurse(1);
For example, to recursively search for PDF files by mime type:
<?PHP
$filelist = new \Chirp\FileList();
$filelist->recurse();
$filelist->add_filter('mime_type', ['application/pdf']);
$files = $filelist->scan(".");
?>
Or by filename extension:
<?PHP
$filelist = new \Chirp\FileList();
$filelist->recurse();
$filelist->add_filter('ext', ['pdf']);
$files = $filelist->scan(".");
?>
Where . represents the current working directory.
Supported key values
The version of the class presented above supports the following keys:
- 'basename'
- file name without suffix
- 'ext'
- file name suffix
- 'filename'
- file name with suffix
- 'imagesize'
- array returned by getimagesize
- 'mime_type'
- file mime type as returned by mime_content_type
- 'mtime'
- last modified time
- 'path'
- relative directory path
- 'pathname'
- relative file path
- 'realpath'
- full path to the file
- 'size'
- file size in bytes
- 'type'
- dir, file or link
You can add support for more values by extending the extract method. There are a few generic SplFileInfo methods we've left out for brevity, and you may have other requirements.
Related Articles - Directory Listing
- PHP Directory Listing: Images
- PHP Directory Listing using SPL
- PHP File Listing Class
- PHP Directory Listing
Richard Kolseth 28 August, 2024
Me again. Having trouble upgrading from 8.1.29 to 8.3
PHP Fatal error: Uncaught Error: Call to undefined function Chirp\\mime_content_type()
You can find a patch here which should still work:
www.the-art-of-web.com/php/directory-list/
-> Fileinfo replaces mime_content_type
Richard Kolseth 20 May, 2022
One thing I found, in php 7.4: your
case 'mime_type':
return mime_content_type($finfo->getRealpath());
Was giving me an error...
may need to be:
case 'mime_type':
return $finfo->getRealpath();
Ric Kolseth 19 May, 2022
I am upgrading my server to use PHP 7.4
It seems to be having an issue with the backslash in the \ Chirp \ FileList(
Any suggestions? It's odd, because I use 7.4 on my local machine, and it gives me no issues. I did try to upgrade to PHP 8 and got similar error messages
Boris N. 23 March, 2022
Good and useful class - much appreciated!
There seems to be just one small problem... very large files may return incorrect (negative) file size values. Any idea?
Unfortunately this is a known bug with PHP not properly supporting files larger than 2Gb