Recursion through a directory tree in PHP - php

I have a set of folders that has a depth of at least 4 or 5 levels. I'm looking to recurse through the directory tree as deep as it goes, and iterate over every file. I've gotten the code to go down into the first sets of subdirectories, but no deeper, and I'm not sure why. Any ideas?
$count = 0;
$dir = "/Applications/MAMP/htdocs/site.com";
function recurseDirs($main, $count){
$dir = "/Applications/MAMP/htdocs/site.com";
$dirHandle = opendir($main);
echo "here";
while($file = readdir($dirHandle)){
if(is_dir($file) && $file != '.' && $file != '..'){
echo "isdir";
recurseDirs($file);
}
else{
$count++;
echo "$count: filename: $file in $dir/$main \n<br />";
}
}
}
recurseDirs($dir, $count);

Check out the new RecursiveDirectoryIterator.
It's still far from perfect as you can't order the search results and other things, but to simply get a list of files, it's fine.
There are simple examples to get you started in the manual like this one:
<?php
$path = realpath('/etc');
$objects = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path),
RecursiveIteratorIterator::SELF_FIRST);
foreach($objects as $name => $object){
echo "$name\n";
}
?>

There is an error in the call
recurseDirs($file);
and in
is_dir($file)
you have to give the full path:
recurseDirs($main . '/' .$file, $count);
and
is_dir($main . '/' .$file)
However, like other anwerers, I suggest to use RecursiveDirectoryIteretor.

The call to is_dir and recurseDirs is not fully correct. Also your counting didn't work correctly. This works for me:
$dir = "/usr/";
function recurseDirs($main, $count=0){
$dirHandle = opendir($main);
while($file = readdir($dirHandle)){
if(is_dir($main.$file."/") && $file != '.' && $file != '..'){
echo "Directory {$file}: <br />";
$count = recurseDirs($main.$file."/",$count); // Correct call and fixed counting
}
else{
$count++;
echo "$count: filename: $file in $main \n<br />";
}
}
return $count;
}
$number_of_files = recurseDirs($dir);
Notice the changed calls to the function above and the new return value of the function.

So yeah: Today I was being lazy and Googled for a cookie cutter solution to a recursive directory listing and came across this. As I ended up writing my own function (as to why I even spent the time to Google for this is beyond me - I always seem to feel the need to re-invent the wheel for no suitable reason) I felt inclined to share my take on this.
While there are opinions for and against the use of RecursiveDirectoryIterator, I'll simply post my take on a simple recursive directory function and avoid the politics of chiming in on RecursiveDirectoryIterator.
Here it is:
function recursiveDirectoryList( $root )
{
/*
* this next conditional isn't required for the code to function, but I
* did not want double directory separators in the resulting array values
* if a trailing directory separator was provided in the root path;
* this seemed an efficient manner to remedy said problem easily...
*/
if( substr( $root, -1 ) === DIRECTORY_SEPARATOR )
{
$root = substr( $root, 0, strlen( $root ) - 1 );
}
if( ! is_dir( $root ) ) return array();
$files = array();
$dir_handle = opendir( $root );
while( ( $entry = readdir( $dir_handle ) ) !== false )
{
if( $entry === '.' || $entry === '..' ) continue;
if( is_dir( $root . DIRECTORY_SEPARATOR . $entry ) )
{
$sub_files = recursiveDirectoryList(
$root .
DIRECTORY_SEPARATOR .
$entry .
DIRECTORY_SEPARATOR
);
$files = array_merge( $files, $sub_files );
}
else
{
$files[] = $root . DIRECTORY_SEPARATOR . $entry;
}
}
return (array) $files;
}
With this function, the answer as to obtaining a file count is simple:
$dirpath = '/your/directory/path/goes/here/';
$files = recursiveDirectoryList( $dirpath );
$number_of_files = sizeof( $files );
But, if you don't want the overhead of an array of the respective file paths - or simply don't need it - there is no need to pass a count to the recursive function as was recommended.
One could simple amend my original function to perform the counting as such:
function recursiveDirectoryListC( $root )
{
$count = 0;
if( ! is_dir( $root ) ) return (int) $count;
$dir_handle = opendir( $root );
while( ( $entry = readdir( $dir_handle ) ) !== false )
{
if( $entry === '.' || $entry === '..' ) continue;
if( is_dir( $root . DIRECTORY_SEPARATOR . $entry ) )
{
$count += recursiveDirectoryListC(
$root .
DIRECTORY_SEPARATOR .
$entry .
DIRECTORY_SEPARATOR
);
}
else
{
$count++;
}
}
return (int) $count;
}
In both of these functions the opendir() function should really be wrapped in a conditional in the event that the directory is not readable or another error occurs. Make sure to do so correctly:
if( ( $dir_handle = opendir( $dir ) ) !== false )
{
/* perform directory read logic */
}
else
{
/* do something on failure */
}
Hope this helps someone...

Related

How to count files inside folders and subfolders with PHP

Is there a effective way to count files inside a single folder which inside it have more files and subfolders?
I tried something like this, still didn't give me the right number. Windows recognise there are 159 files inside Images folder. When I only get 144
<?php
$img = count(glob("./assets/images/*.*"));
$about = count(glob("./assets/images/aboutus/*.*"));
$blog1 = count(glob("./assets/images/blog/*.*"));
$mason = count(glob("./assets/images/blog/masonary/*.*"));
$tl = count(glob("./assets/images/blog/timeline/*.*"));
$blog2 = count(glob("./assets/images/blogdetails/*.*"));
$gallery = count(glob("./assets/images/gallery/*.*"));
$home = count(glob("./assets/images/home/*.*"));
$keg = count(glob("./assets/images/kegiatan/*.*"));
$port1 = count(glob("./assets/images/portfolio/*.*"));
$port2 = count(glob("./assets/images/portfolio-details/*.*"));
$srv = count(glob("./assets/images/services/*.*"));
$usr = count(glob("./assets/images/users/*.*"));
$count = $img+$about+$blog1+$mason+$tl+$blog2+$gallery+$home+$keg+$port1+$port2+$srv+$usr;
?>
Help??
EDITED:
Done. My mistakes. I didn't count the subfolders in certain folder.
But still, is there a way to make it short?
Here is a start of what you could try. You still need to modify it to count the files or count the array that is given.
function getFromDir( $dir ) {
$cdir = scandir( $dir );
$result = array();
foreach( $cdir as $key => $value ) {
if( !in_array( $value, array('.', '..') ) ) {
if( is_dir( $dir . DIRECTORY_SEPARATOR . $value ) ) {
$result[$value] = getFromDir($dir . DIRECTORY_SEPARATOR . $value);
} else {
$result[] = $value;
}
}
}
return $result;
}
If you would pass "./assets/images/" as a paramater it should get all sub folders aswell.
So you end up with something like:
$count = count(getFromDir("./assets/images/"));

Auto include/require all files under all directories

I would like to automatically include/require all .php files under all directories. For example:
(Directory structure)
-[ classes
--[ loader (directory)
---[ plugin.class.php
--[ main.class.php
--[ database.class.php
I need a function that automatically loads all files that end in .php
I have tried all-sorts:
$scan = scandir('classes/');
foreach ($scan as $class) {
if (strpos($class, '.class.php') !== false) {
include($scan . $class);
}
}
Well, if you want to include all php files with a certain ending even in subdirectories, you have to make a recursive function. Something like this:
function load_classphp($directory) {
if(is_dir($directory)) {
$scan = scandir($directory);
unset($scan[0], $scan[1]); //unset . and ..
foreach($scan as $file) {
if(is_dir($directory."/".$file)) {
load_classphp($directory."/".$file);
} else {
if(strpos($file, '.class.php') !== false) {
include_once($directory."/".$file);
}
}
}
}
}
load_classphp('./classes');
This is probably the simplest way to recursively find patterned files:
$dir = new RecursiveDirectoryIterator('classes/');
$iter = new RecursiveIteratorIterator($dir);
$files = new RegexIterator($iter, '/^.+\.class\.php$/', RecursiveRegexIterator::GET_MATCH); // an Iterator, not an array
foreach ( $files as $file ) {
include $file; // $file includes `classes/`
}
RecursiveDirectoryIterator is a tricky beast, so learning all about it is probably not doable. The interwebs has many specific examples though. The trick is to search for it.
If the php files you want to include are PHP classes, then you should use PHP Autoloader
It's not a safe practice to include all php files in all directories automatically. Performance might be degraded if you're including unnecessary files.
Here's the code that should work (I have not tested it):
$scan = scandir('classes/');
foreach ($scan as $class) {
if (strpos($class, '.class.php') !== false) {
include('classes/' . $class);
}
}
If you want recursive include RecursiveIteratorIterator will help you.
$dir = new RecursiveDirectoryIterator('change this to your custom root directory');
foreach (new RecursiveIteratorIterator($dir) as $file) {
if (!is_dir($file)) {
if( fnmatch('*.php', $file) ) // you can change the file extension to any that you require.
/* do anything here */
}
}
function autoload( $path ) {
$items = glob( $path . DIRECTORY_SEPARATOR . "*" );
foreach( $items as $item ) {
$isPhp = pathinfo( $item )["extension"] === "php";
if ( is_file( $item ) && $isPhp ) {
require_once $item;
} elseif ( is_dir( $item ) ) {
autoload( $item );
}
}
}
autoload( dirname( __FILE__ ) . DIRECTORY_SEPARATOR . "classes" );
An easy way : a simple function using RecursiveDirectoryIterator instead of glob().
function include_dir_r( $dir_path ) {
$path = realpath( $dir_path );
$objects = new \RecursiveIteratorIterator( new \RecursiveDirectoryIterator( $path ), \RecursiveIteratorIterator::SELF_FIRST );
foreach( $objects as $name => $object ) {
if( $object->getFilename() !== "." && $object->getFilename() !== ".." ) {
if( !is_dir( $name ) ){
include_once $name;
}
}
}
}
One that I use is
function fileLoader($dir) {
$files = scandir($dir);
foreach ($files as $file) {
if ($file == '.' || $file == '..') {
continue;
}
$path = $dir . '/' . $file;
if (is_dir($path)) {
__DIR__.$path;
} else {
require_once $path;
}
}
}
# calling the function
fileLoader('mydirectory')

php - list folders that contain a specific file

A php script should list all available "modules". A module is a subdirectory that contains at least an info.php file.
Now I want a list of all subdirectories that contain the "info.php" file (i.e. a list of all modules) and came up with this code:
$modules = array();
if ( $handle = opendir( MODULE_DIR ) ) {
while ( false !== ( $entry = readdir( $handle ) ) ) {
if ( $entry === '.' || $entry === '..' ) { continue; }
$info_file = MODULE_DIR . $entry . '/info.php';
if ( ! is_file( $info_file ) ) { continue; }
$modules[] = $entry;
}
closedir( $handle );
}
Question: Is there a shorter/nicer way to get the list, preferably without the loop?
A nice and clean solution can be achieved using the function glob():
foreach(glob('src/*/info.php') as $path) {
echo basename(dirname($path)) . PHP_EOL;
}
Take a look at the RecursiveDirectoryIterator
http://php.net/manual/en/class.recursivedirectoryiterator.php
In your case the code would look like this:
$it = new RecursiveIteratorIterator(new RecursiveDirectoryIterator('.'));
foreach($it as $key => $item) {
if(basename($key) === 'info.php') {
echo dirname($key) . PHP_EOL;
}
}

PHP recursive directory path

i have this function to return the full directory tree:
function getDirectory( $path = '.', $level = 0 ){
$ignore = array( 'cgi-bin', '.', '..' );
// Directories to ignore when listing output. Many hosts
// will deny PHP access to the cgi-bin.
$dh = #opendir( $path );
// Open the directory to the handle $dh
while( false !== ( $file = readdir( $dh ) ) ){
// Loop through the directory
if( !in_array( $file, $ignore ) ){
// Check that this file is not to be ignored
$spaces = str_repeat( ' ', ( $level * 4 ) );
// Just to add spacing to the list, to better
// show the directory tree.
if( is_dir( "$path/$file" ) ){
// Its a directory, so we need to keep reading down...
echo "<strong>$spaces $file</strong><br />";
getDirectory( "$path/$file", ($level+1) );
// Re-call this same function but on a new directory.
// this is what makes function recursive.
} else {
echo "$spaces $file<br />";
// Just print out the filename
}
}
}
closedir( $dh );
// Close the directory handle
}
but what i want to do is to search for a file/folder and return it's path, how can i do that? do you have such a function or can you give me some tips on how to do this?
Try to use RecursiveIteratorIterator in combination with RecursiveDirectoryIterator
$path = realpath('/path/you/want/to/search/in');
$objects = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($path),
RecursiveIteratorIterator::SELF_FIRST);
foreach($objects as $name => $object){
if($object->getFilename() === 'work.txt') {
echo $object->getPathname();
}
}
Additional reading:
http://www.phpro.org/tutorials/Introduction-to-SPL.html
do you have such a function or can you give me some tips on how to do
this?
Yes I do.
I actually asked a similar question earlier this morning, but I figure it out. The problem I was having is that the file names . and .. are returned by readdir() and they cause problems when attempting to opendir() with them. When I filtered these out, my recursion worked perfectly. You might want to modify the format in which it outputs the directories that fit the search. Or modify it to output all files and directories. Find an image for "go.jpg" and try it out.
I can't find my post to notify that I found the solution.
define ('HOME', $_SERVER['DOCUMENT_ROOT']);
function searchalldirectories($directory, $seachterm, $maxrecursions, $maxopendir){
$dircontent= '';
$dirs= array();
if ($maxopendir > 0){
$maxopendir--;
$handle= opendir( HOME.'/'.$directory);
while (( $dirlisting= readdir($handle)) !== false){
$dn= ''; $fn= ' File';
if ( is_dir( HOME.'/'.$directory.'/'.$dirlisting) && $maxrecursions>0 && strpos( $dirlisting, '.')!==0){
$dirs[ count($dirs)]= $directory.'/'.$dirlisting;
$dn= '/'; $fn= 'Dir';
}
if ( stripos($dirlisting, $seachterm) !== false){
$dircontent.= '<input type="image" src="go.jpg" name="cmd" value="home:/'.$directory.'/'.$dirlisting.'"> '.$fn.':// <b>'.$directory.'/'.$dirlisting.$dn.'/</b><br>';
}
}
closedir( $handle);
for ( $i=0; $i<count( $dirs); $i++){
$dircontent.= searchalldirectories( $dirs[$i], $seachterm, ($maxrecursions-1), $maxopendir);
}
}
return $dircontent;
}

Get the hierarchy of a directory with PHP

I'm trying to find all the files and folders under a specified directory
For example I have /home/user/stuff
I want to return
/home/user/stuff/folder1/image1.jpg
/home/user/stuff/folder1/image2.jpg
/home/user/stuff/folder2/subfolder1/image1.jpg
/home/user/stuff/image1.jpg
Hopefully that makes sense!
function dir_contents_recursive($dir) {
// open handler for the directory
$iter = new DirectoryIterator($dir);
foreach( $iter as $item ) {
// make sure you don't try to access the current dir or the parent
if ($item != '.' && $item != '..') {
if( $item->isDir() ) {
// call the function on the folder
dir_contents_recursive("$dir/$item");
} else {
// print files
echo $dir . "/" .$item->getFilename() . "<br>";
}
}
}
}
foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir)) as $f) {
echo "$f \r\n";
}
The working solution (change with your folder name)
<?php
$path = realpath('yourfolder/subfolder');
## or use like this
## $path = '/home/user/stuff/folder1';
foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path)) as $filename)
{
echo "$filename\n";
}
?>
$dir = "/home/user/stuff/";
$scan = scandir($dir);
foreach ($scan as $output) {
echo "$output" . "<br />";
}
Find all the files and folders under a specified directory.
function getDirRecursive($dir, &$output = []) {
$scandir = scandir($dir);
foreach ($scandir as $a => $name) {
$path = realpath($dir . DIRECTORY_SEPARATOR . $name);
if (!is_dir($path)) {
$output[] = $path;
} else if ($name != "." && $name != "..") {
getDirRecursive($path, $output);
$output[] = $path;
}
}
return $output;
}
var_dump(getDirRecursive('/home/user/stuff'));
Output (example) :
array (size=4)
0 => string '/home/user/stuff/folder1/image1.jpg' (length=35)
1 => string '/home/user/stuff/folder1/image2.jpg' (length=35)
2 => string '/home/user/stuff/folder2/subfolder1/image1.jpg' (length=46)
3 => string '/home/user/stuff/image1.jpg' (length=27)
listAllFiles( '../cooktail/' ); //send directory path to get the all files and floder of root dir
function listAllFiles( $strDir ) {
$dir = new DirectoryIterator( $strDir );
foreach( $dir as $fileinfo ) {
if( $fileinfo == '.' || $fileinfo == '..' ) continue;
if( $fileinfo->isDir() ) {
listAllFiles( "$strDir/$fileinfo" );
}
echo $fileinfo->getFilename() . "<br/>";
}
}
Beside RecursiveDirectoryIterator solution there is also glob() solution:
// do some extra filtering here, if necessary
function recurse( $item ) {
return is_dir( $item ) ? array_map( 'recurse', glob( "$item/*" ) ) : $item;
};
// array_walk_recursive: any key that holds an array will not be passed to the function.
array_walk_recursive( ( recurse( 'home/user/stuff' ) ), function( $item ) { print_r( $item ); } );
You can use the RecursiveDirectoryIterator or even the glob function.
Alternatively, the scandir function will do the job.

Categories