I'm trying to get a directory structure in a multidimensional array.
I got this far:
function dirtree($dir, $regex = '', $ignoreEmpty = false)
{
if (!$dir instanceof DirectoryIterator) {
$dir = new DirectoryIterator((string) $dir);
}
$dirs = array();
$files = array();
foreach ($dir as $node) {
if ($node->isDir() && !$node->isDot()) {
$tree = dirtree($node->getPathname(), $regex, $ignoreEmpty);
if (!$ignoreEmpty || count($tree)) {
$dirs[$node->getFilename()] = $tree;
}
} elseif ($node->isFile()) {
$name = $node->getFilename();
if ('' == $regex || preg_match($regex, $name)) {
$files[] = $name;
}
}
}
asort($dirs);
sort($files);
return array_merge($dirs, $files);
}
But I am having issues getting the folder name instead of the index 0,1 .etc. This seems to be due to the fact that my directories have numeric names?
Array
(
[0] => Array // 0 should be the folder name
(
[0] => m_109225488_1.jpg
[1] => t_109225488_1.jpg
)
[1] => Array
(
[0] => m_252543961_1.jpg
[1] => t_252543961_1.jpg
)
The solution was rather simple thanks to: Merge array without loss key index
Instead of array_merge simply do $dirs + $files
Potential solution (potential issue point out by Roger Gee):
function dirtree($dir, $regex = '', $ignoreEmpty = false)
{
if (!$dir instanceof DirectoryIterator) {
$dir = new DirectoryIterator((string) $dir);
}
$dirs = array();
$files = array();
foreach ($dir as $node) {
if ($node->isDir() && !$node->isDot()) {
$tree = dirtree($node->getPathname(), $regex, $ignoreEmpty);
if (!$ignoreEmpty || count($tree)) {
$dirs[$node->getFilename()] = $tree;
}
} elseif ($node->isFile()) {
$name = $node->getFilename();
if ('' == $regex || preg_match($regex, $name)) {
$files[] = $name;
}
}
}
return $dirs + $files;
}
Better solution?
function dirtree($dir, $regex = '', $ignoreEmpty = false)
{
if (!$dir instanceof DirectoryIterator) {
$dir = new DirectoryIterator((string) $dir);
}
$filedata = array();
foreach ($dir as $node) {
if ($node->isDir() && !$node->isDot()) {
$tree = dirtree($node->getPathname(), $regex, $ignoreEmpty);
if (!$ignoreEmpty || count($tree)) {
$filedata[$node->getFilename()] = $tree;
}
} elseif ($node->isFile()) {
$name = $node->getFilename();
if ('' == $regex || preg_match($regex, $name)) {
$filedata[] = $name;
}
}
}
return $filedata;
}
Using the array union operation is dangerous since you can potentially overwrite existing files. Consider the following directory structure:
a <-- directory
├── 0 <-- directory (empty)
├── b <-- regular file
└── c <-- directory
└── d <-- regular file
Now consider running the operation using the array union. I get the following result:
array(2) {
[0]=>
array(0) {
}
["c"]=>
array(1) {
[0]=>
string(1) "d"
}
}
Notice how regular file b is not present? This is because the array union operation prefers the existing 0 index over the 0 index from the right operand (which contains the regular files).
I would stick with the original implementation present in the question or use a special bucket for files that doesn't contain a valid filesystem name (e.g. :files:). Note that this may be platform-specific as to what you choose.
In the case of the original implementation, you can decide whether the index is a directory vs regular file by calling is_array or is_scalar on the value. Note that since the directories array is the first parameter to array_merge, you are guaranteed that no directory indexes get incremented and will always refer to the correct directory names.
Here's how you could determine just the directory names:
function getDirectoryNames($result) {
$ds = [];
foreach ($result as $key => $value) {
if (is_array($value)) {
$ds[] = $key;
}
}
return $ds;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With