counter in a php recursive function - php

I've looked thru the questions already with the system, but couldn't find the answer to my problem. I want to try a counter in a php recursive function, which looks for and deletes empty folders. I paste below a way to echo non-empties as "–" and empties as "|". It looks ok after all, but in case there's a lot to purge, it all grows into gibberish on the screen. Instead I'd like to see the numbers of folders checked vs deleted. Here's the code compiled so far using StackOverflow too. Any help pls?
function RemoveEmptySubFolders($path) {
echo "–";
$empty = true;
foreach ( glob ( $path . DIRECTORY_SEPARATOR . "*" ) as $file ) {
if (is_dir ( $file )) {
if (! RemoveEmptySubFolders ( $file ))
$empty = false;
} else {
$empty = false;
}
}
if ($empty) {
if (is_dir ( $path )) {
// echo "Removing $path...<br>";
rmdir ( $path );
echo "|";
}
}
return $empty;
}

Just pass the variables as reference:
function recursive($path, &$directories, &$removed) {
echo "-";
$directories ++;
$empty = true;
foreach ( glob ( $path . DIRECTORY_SEPARATOR . "*" ) as $file ) {
if (is_dir ( $file )) {
if (! recursive ( $file, $directories, $removed ))
$empty = false;
} else {
$empty = false;
}
}
if ($empty) {
if (is_dir ( $path )) {
$removed++;
echo "|";
}
}
return $empty;
}
$path = "c:\exampledir";
$directories = 0;
$removed = 0;
recursive($path, $directories, $removed);
echo("<br>$directories, $removed");
You could also use global variables, but that's very ugly, and every time you use a global variable othen than the standard ones, a kitty dies.

Object-Oriented variant:
Put everything in a class and add the instance attributes $checked and $deleted, then increment them with a selfmade procedure or (nasty code) just with += 1 or $var ++.

Related

Randomly select a file from directory in PHP

So I have seen solutions of this, however my question is slightly different.
I want the file to have a character at the end.
So, for example, there is a directory called imgs:
imgs
contents: div.png, div2.png, divb.png, divab.png
I need to randomly select a file from this folder, but I need it to have a b at the end. So I could only get on either divb.png or divab.png.
If I get one that doesn't end in b, I need to reselect.
I currently have some code that gives me a timeout and doesn't reselect.
function random_pic($dir = 'imgs'){
$files = glob($dir . '/*.png');
$file = array_rand($files);
if(substr($files[$file], -5)==$shortparam.".png"){
return $files[$file];
} else {
return null;
}
}
EDIT -----------------
<?php
function random_pic() {
$files = glob('imgs/*.png' );
do {
if ( isset( $file ) ) {
unset( $files[$file] );
}
$file = array_rand( $files );
} while ( ( substr( $files[ $file ], -5 != ( $shortparam . ".png" ) ) ) AND ( count( $files) > 0 ) );
if ( count( $files ) > 0 ) {
return $files[ $file ];
} else {
echo $file;
return false;
}
}
for ($i = 0 ; $i < 20; $k++){
$image = random_pic();
if($image == false){
} else {
// display image
This times out for some reason.
(Fatal error: Maximum execution time of 10 seconds exceeded in file.php on line 84)
Thanks for any help you can give!
You could achieve that with a blend of glob, array_walk(), array_rand() and preg_match().
<?php
function random_pic($dir='imgs', $extension=".png", $endChar="b"){
$files = glob($dir . "/*{$extension}");
$matches = array();
array_walk($files, function($imgFile, $index) use ($extension, $endChar, &$matches) {
$pixName = preg_replace("#" . preg_quote($extension) . "#", "", basename($imgFile));
if( preg_match("#" . preg_quote($endChar) . "$#", $pixName)){
$matches[] = $imgFile;
}
});
return (count($matches))? $matches[array_rand($matches)] : null;
}
$randomPic = random_pic(__DIR__. "/imgs", ".png", "b");
// OR JUST USE THE DEFAULTS SINCE THEY ARE JUST THE SAME IN YOUR CASE:
// $randomPic = random_pic();
var_dump($randomPic);
I obviously don't have your files and your directory structure to try this code against, but I'm pretty confident it will solve your problem.
function random_pic( $dir = 'imgs' ) {
if ( $files = glob( $dir . '/*.png' ) ) {
do {
if ( isset( $file ) ) {
unset( $files[$file] );
}
if ( count( $files ) > 0 ) {
$file = array_rand( $files );
}
} while ( ( substr( $files[ $file ], -5 != ( $shortparam . ".png" ) ) ) AND ( count( $files ) > 0 ) );
if ( count( $files ) > 0 ) {
return $files[ $file ];
} else {
return NULL;
}
} else {
return NULL;
}
}
You may want to consider returning FALSE instead of NULL if nothing is found since it's more versatile on the parent end.

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;
}
}

Delete the unwanted files and folders from destination folder as compared to source folder

I am using PHP and I need to script something like below:
I have to compare two folder structure
and with reference of source folder I
want to delete all the files/folders
present in other destination folder
which do not exist in reference source
folder, how could i do this?
EDITED:
$original = scan_dir_recursive('/var/www/html/copy2');
$mirror = scan_dir_recursive('/var/www/html/copy1');
function scan_dir_recursive($dir) {
$all_paths = array();
$new_paths = scandir($dir);
foreach ($new_paths as $path) {
if ($path == '.' || $path == '..') {
continue;
}
$path = $dir . DIRECTORY_SEPARATOR . $path;
if (is_dir($path)) {
$all_paths = array_merge($all_paths, scan_dir_recursive($path));
} else {
$all_paths[] = $path;
}
}
return $all_paths;
}
foreach($mirror as $mirr)
{
if($mirr != '.' && $mirr != '..')
{
if(!in_array($mirr, $original))
{
unlink($mirr);
// delete the file
}
}
}
The above code shows what i did..
Here My copy1 folder contains extra files than copy2 folders hence i need these extra files to be deleted.
EDITED:
Below given output is are arrays of original Mirror and of difference of both..
Original Array
(
[0] => /var/www/html/copy2/Copy (5) of New Text Document.txt
[1] => /var/www/html/copy2/Copy of New Text Document.txt
)
Mirror Array
(
[0] => /var/www/html/copy1/Copy (2) of New Text Document.txt
[1] => /var/www/html/copy1/Copy (3) of New Text Document.txt
[2] => /var/www/html/copy1/Copy (5) of New Text Document.txt
)
Difference Array
(
[0] => /var/www/html/copy1/Copy (2) of New Text Document.txt
[1] => /var/www/html/copy1/Copy (3) of New Text Document.txt
[2] => /var/www/html/copy1/Copy (5) of New Text Document.txt
)
when i iterate a loop to delete on difference array all files has to be deleted as per displayed output.. how can i rectify this.. the loop for deletion is given below.
$dirs_to_delete = array();
foreach ($diff_path as $path) {
if (is_dir($path)) {
$dirs_to_delete[] = $path;
} else {
unlink($path);
}
}
while ($dir = array_pop($dirs_to_delete)) {
rmdir($dir);
}
First you need a recursive listing of both directories. A simple function like this will work:
function scan_dir_recursive($dir, $rel = null) {
$all_paths = array();
$new_paths = scandir($dir);
foreach ($new_paths as $path) {
if ($path == '.' || $path == '..') {
continue;
}
if ($rel === null) {
$path_with_rel = $path;
} else {
$path_with_rel = $rel . DIRECTORY_SEPARATOR . $path;
}
$full_path = $dir . DIRECTORY_SEPARATOR . $path;
$all_paths[] = $path_with_rel;
if (is_dir($full_path)) {
$all_paths = array_merge(
$all_paths, scan_dir_recursive($full_path, $path_with_rel)
);
}
}
return $all_paths;
}
Then you can compute their difference with array_diff.
$diff_paths = array_diff(
scan_dir_recursive('/foo/bar/mirror'),
scan_dir_recursive('/qux/baz/source')
);
Iterating over this array, you will be able to start deleting files. Directories are a bit trickier because they must be empty first.
// warning: test this code yourself before using on real data!
$dirs_to_delete = array();
foreach ($diff_paths as $path) {
if (is_dir($path)) {
$dirs_to_delete[] = $path;
} else {
unlink($path);
}
}
while ($dir = array_pop($dirs_to_delete)) {
rmdir($dir);
}
I've tested things and it should be working well now. Of course, don't take my word for it. Make sure to setup your own safe test before deleting real data.
For recursive directories please use:
$modified_directory = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator('path/to/modified'), true
);
$modified_files = array();
foreach ($modified_directory as $file)
{
$modified_files []= $file->getPathname();
}
You can do other things like $file->isDot(), or $file->isFile(). For more file commands with SPLFileInfo visit http://www.php.net/manual/en/class.splfileinfo.php
Thanks all for the precious time given to my work, Special Thanks to erisco for his dedication for my problem, Below Code is the perfect code to acomplish the task I was supposed to do, with a little change in the erisco's last edited reply...
$source = '/var/www/html/copy1';
$mirror = '/var/www/html/copy2';
function scan_dir_recursive($dir, $rel = null) {
$all_paths = array();
$new_paths = scandir($dir);
foreach ($new_paths as $path) {
if ($path == '.' || $path == '..') {
continue;
}
if ($rel === null) {
$path_with_rel = $path;
} else {
$path_with_rel = $rel . DIRECTORY_SEPARATOR . $path;
}
$full_path = $dir . DIRECTORY_SEPARATOR . $path;
$all_paths[] = $path_with_rel;
if (is_dir($full_path)) {
$all_paths = array_merge(
$all_paths, scan_dir_recursive($full_path, $path_with_rel)
);
}
}
return $all_paths;
}
$diff_paths = array_diff(
scan_dir_recursive($mirror),
scan_dir_recursive($source)
);
echo "<pre>Difference ";print_r($diff_paths);
$dirs_to_delete = array();
foreach ($diff_paths as $path) {
$path = $mirror."/".$path;//added code to unlink.
if (is_dir($path)) {
$dirs_to_delete[] = $path;
} else {
if(unlink($path))
{
echo "File ".$path. "Deleted.";
}
}
}
while ($dir = array_pop($dirs_to_delete)) {
rmdir($dir);
}
First do a scandir() of the original folder, then do a scandir on mirror folder. start traversing the mirror folder array and check if that file is present in the scandir() of original folder. something like this
$original = scandir('path/to/original/folder');
$mirror = scandir('path/to/mirror/folder');
foreach($mirror as $mirr)
{
if($mirr != '.' && $mirr != '..')
{
if(in_array($mirr, $original))
{
// do not delete the file
}
else
{
// delete the file
unlink($mirr);
}
}
}
this should solve your problem. you will need to modify the above code accordingly (include some recursion in the above code to check if the folder that you are trying to delete is empty or not, if it is not empty then you will first need to delete all the file/folders in it and then delete the parent folder).

Recursion through a directory tree in 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...

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