campusboy87 / dir_to_array.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
/** |
* Function |
* |
* @param [string|null] $dir Directory |
* @return mixed |
*/ |
function dir_to_array ( $ dir = null ) |
$ result = array (); |
$ cdir = scandir( $ dir ); |
foreach ( $ cdir as $ key => $ value ) |
if ( ! in_array( $ value , array ( ‘.’ , ‘..’ ), true ) ) |
if ( is_dir( $ dir . DIRECTORY_SEPARATOR . $ value ) ) |
$ result [ $ value ] = dir_to_array( $ dir . DIRECTORY_SEPARATOR . $ value ); |
> else |
$ result [] = » < $ dir >/ $ value «; |
> |
> |
> |
if ( ! is_array( $ result ) ) |
return array (); |
> |
ksort( $ result ); |
return $ result ; |
> |
/** |
* Array flatten |
* |
* @param array $array Array |
* |
* @return array |
*/ |
function array_flatten ( $ array ) |
$ return = array (); |
foreach ( $ array as $ key => $ value ) |
if ( is_array( $ value ) ) |
$ return = array_merge( $ return , array_flatten( $ value ) ); |
> else |
$ return [ $ key ] = $ value ; |
> |
> |
return $ return ; |
> |
var_dump( array_flatten( dir_to_array( ‘includes’ ) ) ); |
Deep recursive array of directory structure in PHP
I want to have something like that, as an array.
Meaning I have 1 big array and in that array are more arrays. Each set and subset gets its own array.
I’m trying to make it look something like this:
Array ( [Set 1] => Array([0] => Item 1 of Set 1, [1] => Item 1 of Set 1. ) [Set 2] => Array([Subnet 1] => Array([0] => Item 1 of Subset 1 of Set 2,[1] => . ), [Subnet 2] => Array([0] => . . . ), . [0] => Random File) [set 3] => Array(. ) . )
But that’s not what I’m looking for. I’ve been meddling with it but it’s giving me nothing but trouble.
Here’s an example, view source for larger resolution(no clicking apparently. ).
6 Answers 6
I recommend using DirectoryIterator to build your array
Here’s a snippet I threw together real quick, but I don’t have an environment to test it in currently so be prepared to debug it.
$fileData = fillArrayWithFileNodes( new DirectoryIterator( '/path/to/root/' ) ); function fillArrayWithFileNodes( DirectoryIterator $dir ) < $data = array(); foreach ( $dir as $node ) < if ( $node->isDir() && !$node->isDot() ) < $data[$node->getFilename()] = fillArrayWithFileNodes( new DirectoryIterator( $node->getPathname() ) ); > else if ( $node->isFile() ) < $data[] = $node->getFilename(); > > return $data; >
I’m getting this error: Fatal error: Maximum function nesting level of ‘100’ reached, aborting! And I don’t see why I should get this. highest nesting I have is 5 levels.
Your original questions asks how to recursively walk over a directory tree and load that data into an array — my answer does that. If you’re expecting something more you’re going to have to provide more detail.
A simple implementation without any error handling:
function dirToArray($dir) < $contents = array(); # Foreach node in $dir foreach (scandir($dir) as $node) < # Skip link to current and parent folder if ($node == '.') continue; if ($node == '..') continue; # Check if it's a node or a folder if (is_dir($dir . DIRECTORY_SEPARATOR . $node)) < # Add directory recursively, be sure to pass a valid path # to the function, not just the folder's name $contents[$node] = dirToArray($dir . DIRECTORY_SEPARATOR . $node); >else < # Add node, the keys will be updated automatically $contents[] = $node; >> # done return $contents; >
What happens here is that the directory listing is flattened and all files are listed right after one another.
positively nice, yes. I made some small adjustments. using DIRECTORY_ITERATOR isn’t really necessary most of the time. oh boy, the comment editor wont let me format. ugh. I’ll add an answer below with my snippet.
@tim Just a heads-up that there is no DIRECTORY_ITERATOR in @soulmerge’s code. (DIRECTORY_SEPARATOR ≠ DIRECTORY_ITERATOR)
Based on the code of @soulmerge’s answer. I just removed some nits and the comments and use $startpath as my starting directory. (THANK YOU @soulmerge!)
function dirToArray($dir) < $contents = array(); foreach (scandir($dir) as $node) < if ($node == '.' || $node == '..') continue; if (is_dir($dir . '/' . $node)) < $contents[$node] = dirToArray($dir . '/' . $node); >else < $contents[] = $node; >> return $contents; > $r = dirToArray($startpath); print_r($r);
I’ve had success with the PEAR File_Find package, specifically the mapTreeMultiple() method. Your code would become something like:
require_once 'File/Find.php'; $fileList = File_Find::mapTreeMultiple($dirPath);
This should return an array similar to what you’re asking for.
What’s this ‘File/Find.php’ file? I’ve scanned my harddrive and I have no such file on it. I do have PEAR installed. Might be that I’m using Windows.
I would like to point out a fact that may surprise people for which indexes in the resulting tables are important. Solutions presented here using sequences:
will generate unexpected results when directory names contain only numbers. Example:
/111000/file1 /file2 /700030/file1 /file2 /456098 /file1 /file2 /999900/file1 /file2 /file1 /file2
Array ( [111000] => Array ( [0] => file1 [1] => file2 ) [700030] => Array ( [456098] => Array ( [0] => file1 [1] => file2 ) [456099] => file1 file2 Array ( [0] => file1 [1] => file2 ) [999901] => file1 file2
As you can see 4 elements has index as incrementation of last name of directory.
I needed a solution like the answers by @tim & @soulmerge. I was trying to do bulk php UnitTest templates for all of the php files in the default codeigniter folders.
This was step one in my process, to get the full recursive contents of a directory / folder as an array. Then recreate the file structure, and inside the directories have files with the proper name, class structure, brackets, methods and comment sections ready to fill in for the php UnitTest.
To summarize: give a directory name, get a single layered array of all directories within it as keys and all files within as values.
I expanded a their answer a bit further and you get the following:
function getPathContents($path) < // was a file path provided? if ($path === null)< // default file paths in codeigniter 3.0 $dirs = array( './models', './controllers' ); >else < // file path was provided // it can be a comma separated list or singular filepath or file $dirs = explode(',', $path); >// final array $contents = array(); // for each directory / file given foreach ($dirs as $dir) < // is it a directory? if (is_dir($dir)) < // scan the directory and for each file inside foreach (scandir($dir) as $node) < // skip current and parent directory if ($node === '.' || $node === '..') < continue; >else < // check for sub directories if (is_dir($dir . '/' . $node)) < // recursive check for sub directories $recurseArray = getPathContents($dir . '/' . $node); // merge current and recursive results $contents = array_merge($contents, $recurseArray); >else < // it a file, put it in the array if it's extension is '.php' if (substr($node, -4) === '.php') < // don'r remove periods if current or parent directory was input if ($dir === '.' || $dir === '..') < $contents[$dir][] = $node; >else < // remove period from directory name $contents[str_replace('.', '', $dir)][] = $node; >> > > > > else < // file name was given $contents[] = $dir; >> return $contents; >
How can I save a directory tree to an array in PHP?
This way, I can easily resuse the tree throughout my site. I've been playing around with this code but it's still not doing what I want:
private function get_tree() < $uploads = __RELPATH__ . DS . 'public' . DS . 'uploads'; $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($uploads), RecursiveIteratorIterator::SELF_FIRST); $output = array(); foreach($iterator as $file) < $relativePath = str_replace($uploads . DS, '', $file); if ($file->isDir()) < if (!in_array($relativePath, $output)) $output[$relativePath] = array(); >> return $output; >
possible duplicate of Want to show Directory Tree of server (PHP) at client side using FLEX ??. Accepted solutions prints out directory tree as array. Just remove the part that would evaluate it back to a real array.
2 Answers 2
Im horrible with recursive functions. But i managed to come up with this..not sure it's what you're looking for
function tree($path, $top) < $ignore = array('cgi-bin', '.', '..' ); $handle = opendir($path); while( ($file = readdir($handle)) !== false ) < if( !in_array( $file, $ignore ) )< if( is_dir("$path/$file") ) < $top[$file] = @ tree("$path/$file", $top[$file]); >else < $top[] = $file; >> > closedir( $handle ); return $top; >
Assuming your directory tree is
Array ( [a] => Array ( [aa] => Array ( [0] => aa.txt ) ) [b] => Array ( [0] => bb.txt ) [c] => Array ( [0] => cc.txt ) )
But, take a break and go watch the eclipse that happens every 300 and something years. You dont want to say 'no' when your kids, grandkids, great-grandkids ask you 'did you know you were alive when the winter solstice eclipse occurred?? did you see it. "
It appears you didn't address the case in which $relativePath is not a directory. You did not describe your output, but i suppose it consists entirely of empty arrays without the desired files ever being inside. Is this correct?
You need to handle the end-case where a file is reached instead of a directory. Take a stab yourself, I can try to post for you later if you are still struggling.
Also, I'm afraid this won't deal with greater depths. Although I am not sure if you need that based on your example.
This won't build a full tree, as i explained the comment below, but depending on what you're looking for that might be ok. A full tree would take some more significant changes. However, what I post below should at least put the files into the folders.
private function get_tree() < $uploads = __RELPATH__ . DS . 'public' . DS . 'uploads'; $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($uploads), RecursiveIteratorIterator::SELF_FIRST); $output = array(); foreach($iterator as $file) < $relativePath = str_replace($uploads . DS, '', $file); if ($file->isDir()) < if (!in_array($relativePath, $output)) < $output[$relativePath] = array(); $lastDir = $relativePath; >> else < $p = path_info($file); $d = $p['dirname']; $f = $p['basename']; $output[$d][] = basename($f); >> return $output; >
How to convert directory structure to array of urls
I want to convert my directory structure to array format with file urls. Here is my directory structure.
public |-product_001 |-documents | |- doc_001.txt | |- doc_002.txt | |- doc_003.txt | |-gallery |- img_001.png |- img_002.png |- img_003.png
array( 'product_001' =>array( 'documents' =>array( 0 => "public/product_001/documents/doc_001.txt", 1 => "public/product_001/documents/doc_002.txt", 2 => "public/product_001/documents/doc_003.txt" ) 'gallery' =>array( 0 => "public/product_001/gallery/img_001.png", 1 => "public/product_001/gallery/img_002.png", 2 => "public/product_001/gallery/img_003.png" ) ) )
function dirToArray($dir,$url) < $result = array(); $cdir = scandir($dir); foreach ($cdir as $key =>$value) < if (!in_array($value, array(".", ".."))) < if (is_dir($dir . DIRECTORY_SEPARATOR . $value)) < $url.=DIRECTORY_SEPARATOR.$value; $result[$value] = dirToArray($dir . DIRECTORY_SEPARATOR . $value,$url); >else < $result[] = $url.DIRECTORY_SEPARATOR.$value; >> > return $result; >
Array ( [product_001] => Array ( [documents] => Array ( [0] => public/product_001/documents/doc_001.txt [1] => public/product_001/documents/doc_002.txt [2] => public/product_001/documents/doc_003.txt ) => Array ( [0] => public/product_001/documents/gallery/img_001.png [1] => public/product_001/documents/gallery/img_002.png [2] => public/product_001/documents/gallery/img_003.png ) ) )