What you are looking for is also called recursive directory migration. This means that you are viewing all directories and a list of subdirectories and files there. If there is a subdirectory, it also goes through, etc. Etc., therefore it is recursive.
As you can imagine, this is a pretty common thing that you need when you write software, and PHP supports you with that. It offers one RecursiveDirectoryIterator so that directories can be recursively repeated, and the standard RecursiveIteratorIterator for traversal. Then you can easily access all files and directories using a simple iteration, for example, through foreach :
$rootpath = '.'; $fileinfos = new RecursiveIteratorIterator( new RecursiveDirectoryIterator($rootpath) ); foreach($fileinfos as $pathname => $fileinfo) { if (!$fileinfo->isFile()) continue; var_dump($pathname); }
This example primarily indicates the directory that you want to move. I accept the current:
$rootpath = '.';
The following line of code is a little long, it creates an instance of the directory iterator , and then an iterator-iterator so that the tree structure can be moved in a single / flat outline:
$fileinfos = new RecursiveIteratorIterator( new RecursiveDirectoryIterator($rootpath) );
These $fileinfos then repeated using a simple foreach :
foreach($fileinfos as $pathname => $fileinfo) {
There is a test inside it to skip all directories from the output. This is done using the SplFileInfo object, which is renamed. It is provided by a recursive directory iterator and contains many useful properties and methods when working with files. You can also return the file extension, database information about size and time, etc. Etc.
if (!$fileinfo->isFile()) continue;
Finally, I just give the path, which is the full path to the file:
var_dump($pathname);
Sample output will look like this (here, on the Windows operating system):
string(12) ".\.buildpath" string(11) ".\.htaccess" string(33) ".\dom\xml-attacks\attacks-xml.php" string(38) ".\dom\xml-attacks\billion-laughs-2.xml" string(36) ".\dom\xml-attacks\billion-laughs.xml" string(40) ".\dom\xml-attacks\quadratic-blowup-2.xml" string(40) ".\dom\xml-attacks\quadratic-blowup-3.xml" string(38) ".\dom\xml-attacks\quadratic-blowup.xml" string(22) ".\dom\xmltree-dump.php" string(25) ".\dom\xpath-list-tags.php" string(22) ".\dom\xpath-search.php" string(27) ".\dom\xpath-text-search.php" string(29) ".\encrypt-decrypt\decrypt.php" string(29) ".\encrypt-decrypt\encrypt.php" string(26) ".\encrypt-decrypt\test.php" string(13) ".\favicon.ico"
If there is a subdirectory that is not available, the following throws an exception. This behavior can be controlled with some flags when creating a RecursiveIteratorIterator :
$fileinfos = new RecursiveIteratorIterator( new RecursiveDirectoryIterator('.'), RecursiveIteratorIterator::LEAVES_ONLY, RecursiveIteratorIterator::CATCH_GET_CHILD );
Hope this was informative. You can also wrap this in your own class, and you can also provide a FilterIterator to move the decision about whether the file should be specified or not from the foreach .
The strength of the combination of RecursiveDirectoryIterator and RecursiveIteratorIterator goes beyond its flexibility. What has not been described above is called the so-called FilterIterator s. I thought I would add another example that uses two self-written ones placed in each other to combine them.
- One of them is to filter out all files and directories starting with a period (this is considered hidden files on UNIX systems, so you should not transfer this information outside) and
- Another that filters the list in files only. This is a check that was previously inside the foreach.
Another change in this use case is to use the getSubPathname() function, which returns a subpath starting from the root iteration path, so what you are looking for.
I also explicitly add the SKIP_DOTS flag, which prevents movement . and .. (technically not very necessary, because filters will filter them the same way they are directories, but I think this is more correct) and returned as UNIX_PATHS , so path strings are always unix-like paths, regardless on whether system is usually a good idea if these values ββare requested via HTTP later, as in your case:
$rootpath = '.'; $fileinfos = new RecursiveIteratorIterator( new FilesOnlyFilter( new VisibleOnlyFilter( new RecursiveDirectoryIterator( $rootpath, FilesystemIterator::SKIP_DOTS | FilesystemIterator::UNIX_PATHS ) ) ), RecursiveIteratorIterator::LEAVES_ONLY, RecursiveIteratorIterator::CATCH_GET_CHILD ); foreach ($fileinfos as $pathname => $fileinfo) { echo $fileinfos->getSubPathname(), "\n"; }
This example is similar to the previous one, although the build method for $fileinfos slightly different. Particularly important is the part about filters:
new FilesOnlyFilter( new VisibleOnlyFilter( new RecursiveDirectoryIterator($rootpath, ...) ) ),
So, the directory iterator is placed in the filter, and the filter itself is placed in another filter. The rest has not changed.
The code for these filters is pretty simple, they work with the accept function, which is either true or false , which should accept or filter:
class VisibleOnlyFilter extends RecursiveFilterIterator { public function accept() { $fileName = $this->getInnerIterator()->current()->getFileName(); $firstChar = $fileName[0]; return $firstChar !== '.'; } } class FilesOnlyFilter extends RecursiveFilterIterator { public function accept() { $iterator = $this->getInnerIterator();
And that again. Naturally, you can use these filters for other cases. For example. if you have another directory listing.
And another example output with $rootpath disabled:
test.html test.rss tests/test-pad-2.php tests/test-pad-3.php tests/test-pad-4.php tests/test-pad-5.php tests/test-pad-6.php tests/test-pad.php TLD/PSL/C/dkim-regdom.c TLD/PSL/C/dkim-regdom.h TLD/PSL/C/Makefile TLD/PSL/C/punycode.pl TLD/PSL/C/test-dkim-regdom.c TLD/PSL/C/test-dkim-regdom.sh TLD/PSL/C/tld-canon.h TLD/PSL/generateEffectiveTLDs.php
More .git or .svn directory traversal or list of files like .builtpath or .project .
Note for FilesOnlyFilter and LEAVES_ONLY : <Sub> The filter explicitly prohibits the use of directories and links based on the SplFileInfo object ( only ordinary files that exist ). So this is real file system filtering.
Another way to get only entries other than a directory with RecursiveIteratorIterator due to the standard LEAVES_ONLY flag (also used in the examples here). This flag does not work as a filter and does not depend on the main iterator. It simply indicates that the iteration should not return branches (here: directories in the case of a directory iterator).