dynamic formation of array with incremental depth in PHP - php

I want to use a function to recursively scan a folder, and assign the contents of each scan to an array.
It's simple enough to recurse through each successive index in the array using either next() or foreach - but how to dynamically add a layer of depth to the array (without hard coding it into the function) is giving me problems. Here's some pseudo:
function myScanner($start){
static $files = array();
$files = scandir($start);
//do some filtering here to omit unwanted types
$next = next($files);
//recurse scan
//PROBLEM: how to increment position in array to store results
//$next_position = $files[][][].... ad infinitum
//myScanner($start.DIRECTORY_SEPARATOR.$next);
}
any ideas?

Try something like this:
// $array is a pointer to your array
// $start is a directory to start the scan
function myScanner($start, &$array){
// opening $start directory handle
$handle = opendir($start);
// now we try to read the directory contents
while (false !== ($file = readdir($handle))) {
// filtering . and .. "folders"
if ($file != "." && $file != "..") {
// a variable to test if this file is a directory
$dirtest = $start . DIRECTORY_SEPARATOR . $file;
// check it
if (is_dir($dirtest)) {
// if it is the directory then run the function again
// DIRECTORY_SEPARATOR here to not mix files and directories with the same name
myScanner($dirtest, $array[$file . DIRECTORY_SEPARATOR]);
} else {
// else we just add this file to an array
$array[$file] = '';
}
}
}
// closing directory handle
closedir($handle);
}
// test it
$mytree = array();
myScanner('/var/www', $mytree);
print "<pre>";
print_r($mytree);
print "</pre>";

Try to use this function (and edit it for your demands):
function getDirTree($dir,$p=true) {
$d = dir($dir);$x=array();
while (false !== ($r = $d->read())) {
if($r!="."&&$r!=".."&&(($p==false&&is_dir($dir.$r))||$p==true)) {
$x[$r] = (is_dir($dir.$r)?array():(is_file($dir.$r)?true:false));
}
}
foreach ($x as $key => $value) {
if (is_dir($dir.$key."/")) {
$x[$key] = getDirTree($dir.$key."/",$p);
}
}
ksort($x);
return $x;
}
It returns sorted array of directories.

Related

Recursive function scanning a folder and storing it in an array

I am trying to make a recursive function to go through all of the folder path that I have given it in the parameters.
What I am trying to do is to store the folder tree into an array for example I have Folder1 and this folder contains 4 text files and another folder and I want the structure to be a multidimensional array like the following
Array 1 = Folder one
Array 1 = text.text.....So on so forth
I have the following function that I build but its not working as I want it too. I know that I need to check whether it is in the root directory or not but when it becomes recursive it becoems harder
function displayAllFolders($root)
{
$foldersArray = array();
$listFolderFile = scandir($root);
foreach($listFolderFile as $row)
{
if($row == "." || $row == "..")
{
continue;
}
elseif(is_dir("$root/$row") == true)
{
$foldersArray["$root/$row"] = "$row";
$folder = "$root/$row";
#$foldersArray[] = displayAllFolders("$root/$row");
}
else
{
$foldersArray[]= array("$root/$row") ;
}
}
var_dump($foldersArray);
}
Using RecursiveDirectoryIterator with RecursiveIteratorIterator this becomes rather easy, e.g.:
$it = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator(
// root dir
'.',
// ignore dots
RecursiveDirectoryIterator::SKIP_DOTS
),
// include directories
RecursiveIteratorIterator::SELF_FIRST
// default is:
// RecursiveIteratorIterator::LEAVES_ONLY
//
// which would only list files
);
foreach ($it as $entry) {
/* #var $entry \SplFileInfo */
echo $entry->getPathname(), "\n";
}
Your approach isn't recursive at all.
It would be recursive if you called the same function again in case of a directory. You only make one sweep.
Have a look here:
http://php.net/manual/en/function.scandir.php
A few solutions are posted. I would advise you to start with the usercomment by mmda dot nl.
(function is named dirToArray, exactly what you are tryting to do.)
In case it will be removed, I pasted it here:
function dirToArray($dir) {
$result = array();
$cdir = scandir($dir);
foreach ($cdir as $key => $value) {
if (!in_array($value,array(".",".."))) {
if (is_dir($dir . DIRECTORY_SEPARATOR . $value)) {
$result[$value] = dirToArray($dir . DIRECTORY_SEPARATOR . $value);
}
else {
$result[] = $value;
}
}
}
return $result;
}
Why not using PHP itself? Just have a look at the RecursiveDirectoryIterator of the standard php library (SPL).
$folders = [];
$iterator = new RecursiveDirectoryIterator(new RecursiveDirectoryIterator($directory));
iterator_apply($iterator, 'scanFolders', array($iterator, $folders));
function scanFolders($iterator, $folders) {
while ($iterator->valid()) {
if ($iterator->hasChildren()) {
scanFolders($iterator->getChildren(), $folders);
} else {
$folders[] = $iterator->current();
}
$iterator->next();
}
}

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.

How to get list of files as array, where key is the same as value?

I could use some help with this. I have to get list of files from one directory, and return them as array, but key needs to be the same as value, so output would be looking like this:
array(
'file1.png' => 'file1.png',
'file2.png' => 'file2.png',
'file3.png' => 'file3.png'
)
I found this code:
function images($directory) {
// create an array to hold directory list
$results = array();
// create a handler for the directory
$handler = opendir($directory);
// open directory and walk through the filenames
while ($file = readdir($handler)) {
// if file isn't this directory or its parent, add it to the results
if ($file != "." && $file != "..")
{
$results[] = $file;
}
}
// tidy up: close the handler
closedir($handler);
// done!
return $results;
}
It's working fine, but it returns regular array.
Can someone help me with this?
Also small note at the end, I need to list only image files (png,gif,jpeg).
Change your following line
$results[] = $file;
To
$results[$file] = $file;
To limit file extension do as below
$ext = pathinfo($file, PATHINFO_EXTENSION);
$allowed_files = array('png','gif');
if(in_array($ext,$allowed_files)){
$results[$file] = $file;
}
Something like this should to the work
$image_array = [];
foreach ($images as $image_key => $image_name) {
if ($image_key == $image_name) {
$image_array[] = $image_name;
}
return $image_array;
}
Why not using glob and array_combine ?
function images($directory) {
$files = glob("{$directory}/*.png");
return array_combine($files, $files);
}
glob() get files on your directory according to a standard pattern ( such as *.png )
array_combine() creates an associative array using an array of keys and an array of values
now do this on my script
$scan=scandir("your image directory");
$c=count($scan);
echo "<h3>found $c image.</h3>";
for($i=0; $i<=$c; $i++):
if(substr($scan[$i],-3)!=='png') continue;
echo "<img onClick=\"javascript:select('$scan[$i]');\" src='yourdirectory/$scan[$i]' />";
endfor;
this code only list png images from your directory.

how can recursion function return accumulative array(array_merge) (php)

function file_list($path){
$final_result=array();
if ($handle = opendir($path)) {
while (false !== ($file = readdir($handle))) {
if ($file != "." && $file != "..") {
if (is_dir($path."/".$file)) {
//echo $path."/".$file."\n";//directory
return file_list($path."/".$file);
} else {
//echo $path."/".$file."\n"; //all file
if(stripos($path."/".$file,"playlist.data"))
{
//echo $path."/".$file."\n"; //file contains "list.data"
$content = file_get_contents($path."/".$file);
preg_match_all("/([0-9]*).txt,0/",$content, $matches);
$result=array_unique($matches[1]);
$final_result=array_merge($final_result,$result);
$final_result=array_unique($final_result);
sort($final_result);
return($final_result);
}
}
}
}
}
}
print_r(file_list($argv[1]));
list.data file like this:
1.txt
3.txt
another list.data file like this:
2.txt
4.txt
so the result should be array like this:
Array(
[0]=>1
[1]=>2
[2]=>3
[3]=>4
)
I was aim to search the specified directory of the file system recursively,when I found file name called "list.data", I read this file and using regex to filter and array to $result. since I have more than one "list.data", I wannted to merge every $result to $final_result. but this script output nothing.
can anybody tell me does something wrong with file_list function.
i am execute this php script in CLI mode like this:
php.exe script.php "d:/test/listdir"
This is a paraphrased pseudo-code version showing the recursive merging logic:
function file_list($path) {
$result = array();
foreach ($path as $file) {
if (is_dir($file)) {
$result = array_merge($result, file_list($file));
} else {
$result[] = ...
}
}
return $result;
}
The function always returns an array, even if it's empty. Going through a directory, you add your results into the $result array. If you encounter a subdirectory, you let file_list go through it, merging the results into your current $result array (because file_list returns an array of results). Finally, after all entries in the directory have been handled, you return the list of results to the caller (which may be file_list itself).
If you feel like experimenting, look into the RecursiveDirectoryIterator, which would allow you to flatten the logic in your function, making it non-recursive.
Best way to do what you're trying to do: let PHP recurse for you.
$iter = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator(
$path, FilesystemIterator::CURRENT_AS_FILEINFO ) );
$out = array();
foreach( $iter as $file )
{
if( $file->getBasename() == 'list.data' )
{
$listdata = file_get_contents( $file->getPathname() );
// do something with list.data
$out = array_merge( $listDataOutput, $out );
}
}

PHP Array Blocklist

I've created this code to cycle through the folders in the current directory and echo out a link to the folder, it all works fine. How would I go about using the $blacklist array as an array to hold the directory names of directories I dont want to show?
$blacklist = array('dropdown');
$results = array();
$dir = opendir("./");
while($file = readdir($dir)) {
if($file != "." && $file != "..") {
$results[] = $file;
}
}
closedir($dir);
foreach($results as $file) {
if($blocked != true) {
$fileUrl = $file;
$fileExplodedName = explode("_", $file);
$fileName = "";
$fileNameCount = count($fileExplodedName);
echo "<a href='".$fileUrl."'>";
$i = 1;
foreach($fileExplodedName as $name) {
$fileName .= $name." ";
}
echo trim($fileName);
echo "</a><br/>";
}
}
array_diff is the best tool for this job -- it's the shortest to write, very clear to read, and I would expect also the fastest.
$filesToShow = array_diff($results, $blacklist);
foreach($filesToShow as $file) {
// display the file
}
Use in_array for this.
$blocked = in_array($file, $blacklist);
Note that this is rather expensive. The runtime complexity of in_array is O(n) so don't make a large blacklist. This is actually faster, but with more "clumsy" code:
$blacklist = array('dropdown' => true);
/* ... */
$blocked = isset($blacklist[$file]);
The runtime complexity of the block check is then reduced to O(1) since the array (hashmap) is constant time on key lookup.

Categories