Enumerate files in parent directory - php

I have the following function that enumerates files and directories in a given folder. It works fine for doing subfolders, but for some reason, it doesn't want to work on a parent directory. Any ideas why? I imagine it might be something with PHP's settings or something, but I don't know where to begin. If it is, I'm out of luck since this is will be running on a cheap shared hosting setup.
Here's how you use the function. The first parameter is the path to enumerate, and the second parameter is a list of filters to be ignored. I've tried passing the full path as listed below. I've tried passing just .., ./.. and realpath('..'). Nothing seems to work. I know the function isn't silently failing somehow. If I manually add a directory to the dirs array, I get a value returned.
$projFolder = '/hsphere/local/home/customerid/sitename/foldertoindex';
$items = enumerateDirs($projFolder, array(0 => "Admin", 1 => "inc"));
Here's the function itself
function enumerateDirs($directory, $filterList)
{
$handle = opendir($directory);
while (false !== ($item = readdir($handle)))
{
if ($item != "." && $item != ".." && $item != "inc" && array_search($item, $filterList) === false)
{
$path = "{$directory->path}/{$item}";
if (is_dir($item))
{
$tmp['name'] = $item;
$dirs[$item] = $tmp;
unset($tmp);
}
elseif (is_file($item))
{
$tmp['name'] = $item;
$files[] = $tmp;
unset($tmp);
}
}
}
ksort($dirs, SORT_STRING);
sort($dirs);
ksort($files, SORT_STRING);
sort($files);
return array("dirs" => $dirs, "files" => $files);
}

You are mixing up opendir and dir. You also need to pass the full path (including the directory component) to is_dir and is_file. (I assume that's what you meant to do with $path.) Otherwise, the functions will look for the corresponding file system objects in the script file's directory.
Try this for a quick fix:
<?php
function enumerateDirs($directory, $filterList)
{
$handle = dir($directory);
while (false !== ($item = $handle->read()))
{
if ($item != "." && $item != ".." && $item != "inc" && array_search($item, $filterList) === false)
{
$path = "{$handle->path}/{$item}";
$tmp['name'] = $item;
if (is_dir($path))
{
$dirs[] = $tmp;
}
elseif (is_file($path))
{
$files[] = $tmp;
}
unset($tmp);
}
}
$handle->close();
/* Anonymous functions will need PHP 5.3+. If your version is older, take a
* look at create_function
*/
$sortFunc = function ($a, $b) { return strcmp($a['name'], $b['name']); };
usort($dirs, $sortFunc);
usort($files, $sortFunc);
return array("dirs" => $dirs, "files" => $files);
}
$ret = enumerateDirs('../', array());
var_dump($ret);
Note: $files or $dirs might be not set after the while loop. (There might be no files or directories.) In that case, usort will throw an error. You should check for that in some way.

Related

PHP - Directory browsing from recursive to iterative

Hello I am trying to make the following function iterative. It browses threw all directories and gives me all files in there.
function getFilesFromDirectory($directory, &$results = array()){
$files = scandir($directory);
foreach($files as $key => $value){
$path = realpath($directory.DIRECTORY_SEPARATOR.$value);
if(!is_dir($path)) {
$results[] = $path;
} else if($value != "." && $value != "..") {
getFilesFromDirectory($path, $results);
$results[] = $path;
}
}
return $results;
}
I am sure that it is possible to make this function iterative but I really have no approach how I can do this.
Your going to want to use a few PHP base classes to implement this.
Using a RecursiveDirectoryIterator inside of a RecursiveIteratorIterator will allow you to iterate over everything within a directory regardless of how nested.
Its worth noting when looping over the $iterator below each $item is an object of type SplFileinfo. Information on this class can be found here: http://php.net/manual/en/class.splfileinfo.php
<?php
//Iterate over a directory and add the filenames of all found children
function getFilesFromDirectory($directory){
//Return an empty array if the directory could not be found
if(!is_dir($directory)){
return array();
}
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($directory)
);
$found = array();
foreach($iterator as $item){
if(method_exists($item, 'isFile') && $item->isFile()){
//Uncomment the below to exclude dot files
//if(method_exists($item, 'isDot') && $item->isDot()){
// continue;
//}
//Pathname == full file path
$found[] = $item->getPathname();
}
}
return $found;
}
An var_dump of some found files i did using this function as a test:
Hope this helps!

directoryIterator has wrong "starting point"

I have a recursive directory iterator looking like this:
function ReadFolderDirectory($dir,$listDir= array())
{
$listDir = array();
if($handler = opendir($dir))
{
while (($sub = readdir($handler)) !== FALSE)
{
if ($sub != "." && $sub != ".." && $sub != "Thumb.db")
{
if(is_file($dir."/".$sub))
{
$listDir[] = $sub;
}elseif(is_dir($dir."/".$sub))
{
$listDir[$sub] = ReadFolderDirectory($dir."/".$sub);
}
}
}
closedir($handler);
}
return $listDir;
}
And I use it like this
$path = dirname(__FILE__);
$path = $path.'/uploadedImages/';
$dir = new DirectoryIterator($path);
$filesArray = ReadFolderDirectory($dir);
echo json_encode($filesArray);
Now when I try to set the starting point of the directory iteration, as you see I have added /uploadedImages/ to the path, but it seems to disregard that.
No matter if that is there or not, it always starts from the same directory as the php file is in, thus including also the index.php, ._index,php, and listing of course the folder uploadedImages itself.
Any thoughts what I am missing?
There is an error in your code, you have just to remove this code line :
$dir = new DirectoryIterator($path);
Explanation
opendir(String $pathToDir) // expects a String, but you pass an Object as a parameter

Finding empty folders recursively and delete them recursively

I have an directory tree which has been passed to array.
I would like to there empty folders inside this array.
How can I determine empty folders like /wp-content/uploads/2014/02/ and /wp-content/uploads/2014/.
How can I delete them recursively.
Here is my array
array (
0 => './do-update.php',
5 => './wp-config.php',
6 => './wp-content/',
7 => './wp-content/uploads/',
8 => './wp-content/uploads/2013/',
9 => './wp-content/uploads/2013/05/',
10 => './wp-content/uploads/2013/05/kabeduvarkad-1024x768.jpg',
26 => './wp-content/uploads/2013/05/kabeduvarkad2.jpg',
27 => './wp-content/uploads/2013/10/',
28 => './wp-content/uploads/2014/',
29 => './wp-content/uploads/2014/02/',
30 => './wp-content/uploads/de.php',
31 => './wp-update.tar.gz',
32 => './wp-update/',
33 => './wp-update/wp-update.tar',
)
Thank you very much to Andresch Serj for him effords.
Who wants to delete empty folders recursively with performance, you can use this solution.
function list_directory($dir) {
$file_list = array();
$stack[] = $dir;
while ($stack) {
$current_dir = array_pop($stack);
if ($dh = opendir($current_dir)){
while (($file = readdir($dh)) !== false) {
if ($file !== '.' AND $file !== '..') {
$current_file = "{$current_dir}/{$file}";
$report = array();
if (is_file($current_file)) {
$file_list[] = "{$current_dir}/{$file}";
} elseif (is_dir($current_file)) {
$stack[] = $current_file;
$file_list[] = "{$current_dir}/{$file}/";
}
}
}
}
}
sort($file_list, SORT_LOCALE_STRING);
return $file_list;
}
function remove_emptyfolders($array_filelist){
$files = array();
$folders = array();
foreach($array_filelist as $path){
// better performance for is_dir function
if ($path[strlen($path)-1] == '/'){ // check for last character if it is / which is a folder.
$folders[] = $path;
}
else{
$files[] = $path;
}
}
// bos olmayan klasorleri buluyoruz.
// eger klasor ismi dosya isimlerinin icerisinde gecmiyorsa bos demektir? right?
$folders_notempty = array();
foreach($files as $file){
foreach($folders as $folder){
if(strpos($file,$folder) !== false){
// dublicate olmasin diye key isimlerinin ismine yazdırdık.
$folders_notempty[$folder] = $folder;
}
}
}
// bos olmayanla klasorleri, digerlerinden cikariyoruz.
$folders_empty = array();
foreach($folders as $folder){
// eger bos olmayanlarin icerisinde bu dosya yoksa
if(!in_array($folder, $folders_notempty)){
$folders_empty[] = $folder;
}
}
// once en uzaktan silmeye baslamaliyiz. kisaca tersten.
$folders_empty = array_reverse($folders_empty);
$folders_deleted = array();
foreach($folders_empty as $k){
try{
$folders_deleted[$k] = 'NOT Succesfull';
if(rmdir($k)){ $folders_deleted[$k] = 'Deleted'; continue; }
chmod($k, 0777);
if(rmdir($k)){ $folders_deleted[$k] = 'Deleted after chmod'; }
}catch (Exception $e) {
print_r($e);
}
}
return $folders_deleted;
}
$files = list_directory(getcwd());
//print_r($files);
$files_deleted = remove_emptyfolders($files);
print_r($files_deleted);
Simply iterate over your array using foreach.
foreach ($filesArray as $file) {
Then for each file, check if it is a folder using is_dir like this
if (is_dir ($file)) {
If it is a folder/directory, read the directory, for instanse using scandir.
$directoryContent = scandir($file);
If the result of scandir is empty, you have an empty folder that you can delete with unlink.
if (count($directoryContent) <= 2) { // checkig if there is moire than . and ..
unlink($file);
If you have trouble with unlink, you may have to set file permissions accordingly.
If instead you need a function that recursively deletes empty subfolders given some paht, you should consider reading the SO question that was linkes in the comments.
EDIT
After taking into consideration your comments, what you do want is a function that deletes parent folders as well. So for a geiven level1/level2/level3 where level3 is empty and the only folder/file in level2 you want level2 to be deleted as well.
So from your example array, you want ./wp-content/uploads/2014/ deleted and not just ./wp-content/uploads/2014/10, but only if ./wp-content/uploads/2014/10 has no content or subfolders with content.
So how to do that?
Simle: Extend your check for weather that folder is empty. If it is empty, manipoulate the given file/path string to get the parent folder. By now you should outsource this to a recursive functions indeed.
function doesDirectoryOnlyContainEmptyFolders($path) {
if(is_dir($path) {
$directoryContent = scandir($path);
if (count($directoryContent) <= 2) {
return true;
}
else {
foreach ($directoryContent as $subPath) {
if($filePath !== '.' && $filePath !== '..' && !doesDirectoryOnlyContainEmptyFolders($subPath)) {
return false;
}
}
return true;
}
}
return false;
}
So this function checks recursively if a path has only empty folders or folders containing empty folders - recursively.
Now you want to check your paths and maybe delete them, recursively downwards and upwards.
function deleteEmptyFoldersRecursivelyUpAndDown($path) {
if (is_dir($path)) {
if(doesDirectoryOnlyContainEmptyFolders($path)) {
unlink($path);
$parentFolder = substr($path, 0, strripos ($path, '/'));
deleteEmptyFoldersRecursivelyUpAndDown($parentFolder);
}
else {
$directoryContent = scandir($path);
foreach ($directoryContent as $subPath) {
deleteEmptyFoldersRecursivelyUpAndDown($subPath);
}
}
}
}
If the given path is a directory, we check if it is empty using our recursive function.
If it is, we delete it and recursively check the parent directory.
If it is not, we iterate over its content to find empty folders, again calling the function itself recursively.
With these two function you have all you need. Simply iterate over your path array and use deleteEmptyFoldersRecursivelyUpAndDownon all entries. If they are faulty, you'll manage to debug them i presume.

list files in subfolders and put in array as key = value

I am sorry if this question is obvious for the ninjas, but I am quite a novice in PHP and I am struggling with it all day ..
I am trying to get a list of all files from a folder structure.
Currently it gives me something like
Array([0]->path/filename [1]->path/filename) Array([0]->path/filename [1]->path/filename..)
(one for each folder)
function o99_list_all_files_in_dir($dir) //need to ocheck for server compatibility (unix,linux,win)
{
$root = scandir($dir);
foreach($root as $value)
{
if($value === '.' || $value === '..') {continue;}
if(is_file("$dir/$value")) {$result[]="$dir/$value";continue;}
//if(is_file("$dir/$value")) {$result["$dir"]="$value";continue;}
foreach(k99_list_all_files_in_dir("$dir/$value") as $value)
{
$result[]=$value;
//$result["$dir"]=$value;
}
}
//print_r($result);
return $result;
}
Few questions :
1 - I need both the path and the filename pair so I thought to get an array like so :
results([path] -> [filename] [anotherpath] -> [anotherfilename]).
but if I try to construct another array (switch uncomment and comment lines) the function will give me only the 1st file in each dir.
2 - Later on , I am using this function in order to have both the path and filename seperated , So I tried this :
$result = o99_list_all_files_in_dir($upload_dir);
foreach ($result as $image) {
reset($image);
while (list($key, $val) = each($image)) {
// echo "$key => $val\n";
}
$filename = pathinfo($image);// I need the path here ...
...
... but obviously it is not working (otherwise I would not be here :-)
3 - Bonus question : How can I filter files from the results (like thumbs.db for example) or decide how to ignore or not certain extensions ??
EDIT I
4 - !important (forgot before) - what do I need to be careful about when dealing with unknows server paths (Linux, Win, Unix) ...will this function work on all ?
Try this code:
function scanFileNameRecursivly($path = '', &$name = array() )
{
$path = $path == ''? dirname(__FILE__) : $path;
$lists = #scandir($path);
if(!empty($lists))
{
foreach($lists as $f)
{
if(is_dir($path.DIRECTORY_SEPARATOR.$f) && $f != ".." && $f != ".")
{
scanFileNameRecursivly($path.DIRECTORY_SEPARATOR.$f, &$name);
}
else
{
$name[] = $path.DIRECTORY_SEPARATOR.$f;
}
}
}
return $name;
}
$path = "abs path to your directory";
$file_names = scanFileNameRecursivly($path);
echo "<pre>";
var_dump($file_names);
echo "</pre>";

How can I use PHP to check if a directory is empty?

I am using the following script to read a directory. If there is no file in the directory it should say empty. The problem is, it just keeps saying the directory is empty even though there ARE files inside and vice versa.
<?php
$pid = $_GET["prodref"];
$dir = '/assets/'.$pid.'/v';
$q = (count(glob("$dir/*")) === 0) ? 'Empty' : 'Not empty';
if ($q=="Empty")
echo "the folder is empty";
else
echo "the folder is NOT empty";
?>
It seems that you need scandir instead of glob, as glob can't see unix hidden files.
<?php
$pid = basename($_GET["prodref"]); //let's sanitize it a bit
$dir = "/assets/$pid/v";
if (is_dir_empty($dir)) {
echo "the folder is empty";
}else{
echo "the folder is NOT empty";
}
function is_dir_empty($dir) {
if (!is_readable($dir)) return null;
return (count(scandir($dir)) == 2);
}
?>
Note that this code is not the summit of efficiency, as it's unnecessary to read all the files only to tell if directory is empty. So, the better version would be
function dir_is_empty($dir) {
$handle = opendir($dir);
while (false !== ($entry = readdir($handle))) {
if ($entry != "." && $entry != "..") {
closedir($handle);
return false;
}
}
closedir($handle);
return true;
}
By the way, do not use words to substitute boolean values. The very purpose of the latter is to tell you if something empty or not. An
a === b
expression already returns Empty or Non Empty in terms of programming language, false or true respectively - so, you can use the very result in control structures like IF() without any intermediate values
I think using the FilesystemIterator should be the fastest and easiest way:
// PHP 5 >= 5.3.0
$iterator = new \FilesystemIterator($dir);
$isDirEmpty = !$iterator->valid();
Or using class member access on instantiation:
// PHP 5 >= 5.4.0
$isDirEmpty = !(new \FilesystemIterator($dir))->valid();
This works because a new FilesystemIterator will initially point to the first file in the folder - if there are no files in the folder, valid() will return false. (see documentation here.)
As pointed out by abdulmanov.ilmir, optionally check if the directory exists before using the FileSystemIterator because otherwise it'll throw an UnexpectedValueException.
I found a quick solution
<?php
$dir = 'directory'; // dir path assign here
echo (count(glob("$dir/*")) === 0) ? 'Empty' : 'Not empty';
?>
use
if ($q == "Empty")
instead of
if ($q="Empty")
For a object oriented approach using the RecursiveDirectoryIterator from the Standard PHP Library (SPL).
<?php
namespace My\Folder;
use RecursiveDirectoryIterator;
class FileHelper
{
/**
* #param string $dir
* #return bool
*/
public static function isEmpty($dir)
{
$di = new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS);
return iterator_count($di) === 0;
}
}
No need to make an instance of your FileHelper whenever you need it, you can access this static method wherever you need it like this:
FileHelper::isEmpty($dir);
The FileHelper class can be extended with other useful methods for copying, deleting, renaming, etc.
There is no need to check the validity of the directory inside the method because if it is invalid the constructor of the RecursiveDirectoryIterator will throw an UnexpectedValueException which that covers that part sufficiently.
This is a very old thread, but I thought I'd give my ten cents. The other solutions didn't work for me.
Here is my solution:
function is_dir_empty($dir) {
foreach (new DirectoryIterator($dir) as $fileInfo) {
if($fileInfo->isDot()) continue;
return false;
}
return true;
}
Short and sweet. Works like a charm.
I used:
if(is_readable($dir)&&count(scandir($dir))==2) ... //then the dir is empty
Try this:
<?php
$dirPath = "Add your path here";
$destdir = $dirPath;
$handle = opendir($destdir);
$c = 0;
while ($file = readdir($handle)&& $c<3) {
$c++;
}
if ($c>2) {
print "Not empty";
} else {
print "Empty";
}
?>
Probably because of assignment operator in if statement.
Change:
if ($q="Empty")
To:
if ($q=="Empty")
# Your Common Sense
I think your performant example could be more performant using strict comparison:
function is_dir_empty($dir) {
if (!is_readable($dir)) return null;
$handle = opendir($dir);
while (false !== ($entry = readdir($handle))) {
if ($entry !== '.' && $entry !== '..') { // <-- better use strict comparison here
closedir($handle); // <-- always clean up! Close the directory stream
return false;
}
}
closedir($handle); // <-- always clean up! Close the directory stream
return true;
}
Function count usage maybe slow on big array. isset is ever faster
This will work properly on PHP >= 5.4.0 (see Changelog here)
function dir_is_empty($path){ //$path is realpath or relative path
$d = scandir($path, SCANDIR_SORT_NONE ); // get dir, without sorting improve performace (see Comment below).
if ($d){
// avoid "count($d)", much faster on big array.
// Index 2 means that there is a third element after ".." and "."
return !isset($d[2]);
}
return false; // or throw an error
}
Otherwise, using #Your Common Sense solution it's better for avoid load file list on RAM
Thanks and vote up to #soger too, to improve this answer using SCANDIR_SORT_NONE option.
Just correct your code like this:
<?php
$pid = $_GET["prodref"];
$dir = '/assets/'.$pid.'/v';
$q = count(glob("$dir/*")) == 0;
if ($q) {
echo "the folder is empty";
} else {
echo "the folder is NOT empty";
}
?>
Even an empty directory contains 2 files . and .., one is a link to the current directory and the second to the parent. Thus, you can use code like this:
$files = scandir("path to directory/");
if(count($files) == 2) {
//do something if empty
}
I use this method in my Wordpress CSV 2 POST plugin.
public function does_folder_contain_file_type( $path, $extension ){
$all_files = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $path ) );
$html_files = new RegexIterator( $all_files, '/\.'.$extension.'/' );
foreach( $html_files as $file) {
return true;// a file with $extension was found
}
return false;// no files with our extension found
}
It works by specific extension but is easily changed to suit your needs by removing "new RegexIterator(" line. Count $all_files.
public function does_folder_contain_file_type( $path, $extension ){
$all_files = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $path ) );
return count( $all_files );
}
I had a similar problem recently, although, the highest up-voted answer did not really work for me, hence, I had to come up with a similar solution. and again this may also not be the most efficient way to go about the problem,
I created a function like so
function is_empty_dir($dir)
{
if (is_dir($dir))
{
$objects = scandir($dir);
foreach ($objects as $object)
{
if ($object != "." && $object != "..")
{
if (filetype($dir."/".$object) == "dir")
{
return false;
} else {
return false;
}
}
}
reset($objects);
return true;
}
and used it to check for empty dricetory like so
if(is_empty_dir($path)){
rmdir($path);
}
You can use this:
function isEmptyDir($dir)
{
return (($files = #scandir($dir)) && count($files) <= 2);
}
The first question is when is a directory empty? In a directory there are 2 files the '.' and '..'.
Next to that on a Mac there maybe the file '.DS_Store'. This file is created when some kind of content is added to the directory. If these 3 files are in the directory you may say the directory is empty.
So to test if a directory is empty (without testing if $dir is a directory):
function isDirEmpty( $dir ) {
$count = 0;
foreach (new DirectoryIterator( $dir ) as $fileInfo) {
if ( $fileInfo->isDot() || $fileInfo->getBasename() == '.DS_Store' ) {
continue;
}
$count++;
}
return ($count === 0);
}
#Your Common Sense,#Enyby
Some improvement of your code:
function dir_is_empty($dir) {
$handle = opendir($dir);
$result = true;
while (false !== ($entry = readdir($handle))) {
if ($entry != "." && $entry != "..") {
$result = false;
break 2;
}
}
closedir($handle);
return $result;
}
I use a variable for storing the result and set it to true.
If the directory is empty the only files that are returned are . and .. (on a linux server, you could extend the condition for mac if you need to) and therefore the condition is true.
Then the value of result is set to false and break 2 exit the if and the while loop so the next statement executed is closedir.
Therefore the while loop will only have 3 circles before it will end regardless if the directory is empty or not.
$is_folder_empty = function(string $folder) : bool {
if (!is_dir($folder))
return TRUE;
// This wont work on non linux OS.
return is_null(shell_exec("ls {$folder}"));
};
$is_folder_empty2 = function(string $folder) : bool {
if (!is_dir($folder))
return TRUE;
// Empty folders have two files in it. Single dot and
// double dot.
return count(scandir($folder)) === 2;
};
var_dump($is_folder_empty('/tmp/demo'));
var_dump($is_folder_empty2('/tmp/demo'));

Categories