1

I have some file list arrays with elements exceeding 100s in number that I need to convert to a tree array and insert them into the database and return the tree on call for a json api directory system I have spent a week trying different algorithms, all of them fail at some point. This is the array:

$files[] = array('dir1/dir2/dir3/file1.mkv', 44444);
$files[] = array('dir1/dir2/dir3/file2.mkv', 44444);
$files[] = array('dir1/dir2/file1.mkv', 44444);
$files[] = array('dir1/dir2/file2.txt', 11111);
$files[] = array('dir1/file1.exe', 22222);
$files[] = array('dir1/file2.exe', 22222);
$files[] = array('file1.rar', 3333);

the first element is the file path, second element is filesize. These files dont exist on this server, they are on a different server, this is the api server. I need to convert it into a nested tree array like this:

Array
(
    [dir1] => Array
        (
            [dir2] => Array
                (
                    [dir3] => Array
                    (
                        [0] => Array
                            (
                                [name] => file1.mkv
                                [size] => 44444
                            )

                        [1] => Array
                            (
                                [name] => file2.mkv
                                [size] => 44444
                            )

                    )

                    [0] => Array
                        (
                            [name] => file1.mkv
                            [size] => 44444
                        )

                    [1] => Array
                        (
                            [name] => file2.txt
                            [size] => 11111
                        )

                )

            [0] => Array
                (
                    [name] => file1.exe
                    [size] => 22222
                )

            [1] => Array
                (
                    [name] => file2.exe
                    [size] => 22222
                )

        )

    [0] => Array
        (
            [name] => file1.rar
            [size] => 3333
        )

)

I wrote some code but it only goes up to 2nd level (dir2), then on the third level (dir3), it fails to perform as expected.

the code:

foreach ($files as $file)
{
    $info = pathinfo($file[0]);
    if ($info['dirname'] != '.')
    {
        if (strpos($info['dirname'], '/') !== false)
        {
            $dirs[pathinfo($info['dirname'])['dirname']][pathinfo($info['dirname'])['basename']][] = array('name' => $info['basename'], 'size' => $file[1]);
        }
        else
        {
            $dirs[$info['dirname']][] = array('name' => $info['basename'], 'size' => $file[1]);
        }
    }
    else
    {
        $dirs[] = array('name' => $info['basename'], 'size' => $file[1]);
    }
}

This code returns the array fine if I remove the dir3 level from the file list array, but with the dir3 present, it gives:

Array
(
    [dir1/dir2] => Array
        (
            [dir3] => Array
                (
                    [0] => Array
                        (
                            [name] => file1.mkv
                            [size] => 44444
                        )

                    [1] => Array
                        (
                            [name] => file2.mkv
                            [size] => 44444
                        )

                )

        )

    [dir1] => Array
        (
            [dir2] => Array
                (
                    [0] => Array
                        (
                            [name] => file1.mkv
                            [size] => 44444
                        )

                    [1] => Array
                        (
                            [name] => file2.txt
                            [size] => 11111
                        )

                )

            [0] => Array
                (
                    [name] => file1.exe
                    [size] => 22222
                )

            [1] => Array
                (
                    [name] => file2.exe
                    [size] => 22222
                )

        )

    [0] => Array
        (
            [name] => file1.rar
            [size] => 3333
        )

)

This [dir1/dir2] is the problem, I need it to be a perfect tree so I can recurs through it and insert the records into the database.

2 Answers 2

2

Since you don't know tree depth beforehand, you can't manage this with if/else loops. It is a use case that needs recursion.

<?php
function scanpath($path) {
    $myscan = scandir($path);
    $tree=[];
    foreach($myscan as $entry) {
        //echo '<br>'.$entry;
        if($entry==='.' || $entry ==='..') {
            // do nothing
        } else  if(is_dir($path.'/'.$entry)) {
            // this is a folder, I will recurse
            $tree[$entry] = scanpath($path.'/'.$entry);
        } else {
            // this is a file or link. Value is file size
            $tree[$entry] = filesize($path.'/'.$entry);
        }
    }
    return $tree;
}
$scanresult=scanpath(__DIR__);
echo '<pre>';
print_r($scanresult);
echo '</pre>';

You see, I call a function on a given path. For each entry on that path I

  • Discard . and ..
  • If it's a folder, the node will contain the result of applying that function to the subpath
  • In any other case, the node will contain the file size

You'll need to modify my code to suit your needs. I can tell your "is file or link" case has a different format.

EDIT: since there isn't a real file structure to scan, but instead you have an array of paths, this is the solution I came with

<?php
$thefiles=[];

$thefiles[] = array('dir1/dir2/dir3/file1.mkv', 44444);
$thefiles[] = array('dir1/dir2/dir3/file2.mkv', 44444);
$thefiles[] = array('dir1/dir2/file1.mkv', 44444);
$thefiles[] = array('dir1/dir2/file2.txt', 11111);
$thefiles[] = array('dir1/dir4/file5.mkv', 22444);
$thefiles[] = array('dir1/dir4/file6.txt', 15111);
$thefiles[] = array('dir1/file1.exe', 22222);
$thefiles[] = array('dir1/file2.exe', 22222);
$thefiles[] = array('file1.rar', 3333);

$filearray=[];

function scanpath($patharray,$filesize) {
    $tree=[];
    if(count($patharray)===1) {
        $filename=array_pop($patharray);
        $tree[] = ['name'=>$filename, 'size'=>$filesize];
    } else {
        $pathpart = array_pop($patharray);
        $tree[$pathpart] = scanpath($patharray,$filesize);
    }
    return $tree;
}

foreach($thefiles as $fileentry) {
    $patharray=array_reverse(explode('/',$fileentry[0]));
    $thisarray = scanpath($patharray,$fileentry[1]);
    $filearray= array_merge_recursive($filearray,$thisarray);
}

echo '<pre>';
print_r($filearray);
echo '</pre>';

I exploded each path using slash as separator, then recursed on the path, folder by folder, until I get to the final part and the recursion ends on a file name and size.

5
  • I said the files dont exist. its just an array, so you cannot do scandir() or is_dir() Commented Nov 22, 2014 at 23:33
  • I see. Sorry about that. I'll give it a shot in a few hours. Commented Nov 22, 2014 at 23:51
  • that would be appreciated :) Commented Nov 22, 2014 at 23:54
  • It seems I got it working, please see my edited answer. Commented Nov 23, 2014 at 14:21
  • Hey, sorry for the late response. I already figured it out. But thanks for the help :) Commented Jul 15, 2015 at 23:18
0

Here is my way

$path='E:\conf\transfer';
$objects = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path), RecursiveIteratorIterator::SELF_FIRST);
$tree_array=array();
foreach($objects as $name => $object){
    if(in_array($object->getFilename(),array('..','.')) || is_dir($object->getPathname()))
    {
        continue;
    }
    $path_info=explode(DIRECTORY_SEPARATOR ,$object->getPathname());
    $file_name=array_pop($path_info);
    $path_info="['".implode("']['",$path_info)."']";
    $code='$tree_array'.$path_info.'[] = '.'\''.$file_name.'\';';
    eval($code);
}

var_dump($tree_array);

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.