Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

XerK's avatar
Level 1

List all the files and folders in a Directory with PHP recursive function

I'm trying to go through all of the files in a directory, and if there is a directory, go through all of its files and so on until there are no more directories to go to. I need to get like JSON below.

$routes = [
            '/RootFolder/Folder1/File1.doc',
            '/RootFolder/Folder1/SubFolder1/File1.txt',
            '/RootFolder/Folder1/SubFolder1/File2.txt',
            '/RootFolder/Folder2/SubFolder1/File2.txt',
            '/RootFolder/Folder2/SubFolder1/SubSubFolder1/File4.doc',
        ];
data: [{
                    label: 'RootFolder',
                    children: [{
                            label: 'Folder1',
                            children: [{
                                file: 'File1.doc'
                            }],
                            children: [{
                                label: 'SubFolder1',
                                children: [{
                                    file: 'File1.txt'
                                }, {
                                    file: 'File2.txt'
                                }],
                            }]
                        },
                        {
                            label: 'Folder2',
                            children: [{
                                label: 'SubFolder1',
                                children: [{
                                    file: 'File1.txt'
                                }, {
                                    file: 'File2.txt'
                                }, {
                                    label: 'SubSubFolder1',
                                    children: [{
                                        file: 'File4.doc'
                                    }, ],
                                }]
                            }]
                        },
                    ],
                }],
0 likes
13 replies
guybrush_threepwood's avatar

Hi @xerk

Try something like this:

function getTree($path)
{
    $tree = [];
  
    $branch = [
      'label' => basename($path)
    ];
  
    foreach (File::files($path) as $file) {
      $branch['children'][] = basename($file);
    }
   
    foreach (File::directories($path) as $directory) {
      $branch['children'][] = getTree($directory);
    }
  
    return array_merge($tree, $branch);
}

$tree = getTree(storage_path());

$encodedTree = json_encode([
    'data' => [ $tree ]
], JSON_PRETTY_PRINT);

var_dump($encodedTree);

It's a prototype I just put together, I haven't tested it extensively. You'll probably have to tweak it further to attain the exact output you're looking for.

Good luck.

1 like
XerK's avatar
Level 1

THANK YOU,

But I need to know how display files for a specific user?

Sorry, but I didn't work on the authorization of files before.

I was thinking about return all files for authentication for a user and display it on a file manager.

guybrush_threepwood's avatar

Hi,

Sorry, I don't think I full understand. How do user files actually work?

Does each user have a specific folder for his files? Are the files uploaded by the user? Are they stored on disk only or are they referenced in a database table too? How do you avoid a user seeing another user's files/folders?

XerK's avatar
Level 1

they only referenced in a database table

  • How do you avoid a user seeing another user's files/folders?

I think this is my solution about it: I need to retrieve only files the user is assigned in a database from multistable tables and display it in my Vuejs file manager.

$routes = [
            '/projects/company-name-folder/project-name/baseFile.png',
            '/projects/company-name-folder/project-name/final_files/File1.png',
	     '/projects/company-name-folder/project-name/final_files/File2.png',
              '/projects/company-name-folder2/project-name/2final_files/File2.png',
              '/projects/company-name-folder2/project-name2/File.png',
        ];
function getTree($path)
        {
            $tree = [];

            $branch = [
                'label' => basename($path)
            ];

            foreach (File::files($path) as $key => $file) {
                $branch['children'][$key]['file'] = basename($file);
            }

            foreach (File::directories($path) as $directory) {
                $branch['children'][] = getTree($directory);
            }

            return array_merge($tree, $branch);
        }
        // Storage::disk('public')->listContents(''));
        $tree = getTree(storage_path('app/public'));

        $encodedTree = json_encode([
            'data' => [$tree]
        ], JSON_PRETTY_PRINT);

        return $encodedTree;

Your code is great and I thank you again for it, But how can I change storage_path to my database URLs files.

guybrush_threepwood's avatar

I understand. Then my code won't do, since it serves a completely different purpose.

Does the $routes variable contain the exact output that's expected from the database for a single user? If you confirm I can work out a different solution based on that.

1 like
XerK's avatar
Level 1

Yes, It's for a single user.

guybrush_threepwood's avatar

Hi @xerk

Try this:

$routes = [
    '/RootFolder/Folder1/File1.doc',
    '/RootFolder/Folder1/SubFolder1/File1.txt',
    '/RootFolder/Folder1/SubFolder1/File2.txt',
    '/RootFolder/Folder2/SubFolder1/File2.txt',
    '/RootFolder/Folder2/SubFolder1/SubSubFolder1/File4.doc',
];

function routesToArray($routes)
{ 
    $routeList = [];

    foreach ($routes as $route) {  
        $pathName = Str::after($route, '/');
        $pathName = Str::beforeLast($pathName, '/');
        $pathName = $pathName . '/files';
        $pathName = str_replace('/', '.', $pathName);

        $fileName = Str::afterLast($route, '/');

        $files = Arr::get($routeList, $pathName) ?? [];
        $files = array_merge($files, Arr::wrap($fileName)); 

        Arr::set($routeList, $pathName, $files);
    }
  
    return $routeList;
}

function getTreeBranches($paths)
{  
    $tree = [];
    $mainKey = key($paths);
  
    if ($mainKey == 'files') {
        return $paths[$mainKey];
    }
  
    $tree['label'] = $mainKey;
  
    $paths = $paths[$mainKey];
   
    foreach ($paths as $key => $path) {
        $tree['children'][] = getTreeBranches([$key => $path]);
    }
  
    return $tree;
}

$routeList = routesToArray($routes);
$routeTree = getTreeBranches($routeList);
Tray2's avatar

I did this on one of my little projects.

function getDirContents($dir, &$results = array()){
    $files = scandir($dir);

    foreach($files as $key => $value){
        $path = realpath($dir.DIRECTORY_SEPARATOR.$value);
        if(!is_dir($path)) {
            $results[] = $path;
        } else if($value != "." && $value != "..") {
            getDirContents($path, $results);
            $results[] = $path;
        }
    }

    return $results;
}```
XerK's avatar
Level 1

I have problem with rename key of array of files need to rename file: 'File2.txt' and put full url with file this last thing

{ file: 'File2.txt', url: '/RootFolder/Folder1/File1.doc' }

guybrush_threepwood's avatar

Hi @xerk

Try this:

$routes = [
    '/RootFolder/Folder1/File1.doc',
    '/RootFolder/Folder1/SubFolder1/File1.txt',
    '/RootFolder/Folder1/SubFolder1/File2.txt',
    '/RootFolder/Folder2/SubFolder1/File2.txt',
    '/RootFolder/Folder2/SubFolder1/SubSubFolder1/File4.doc',
];

function routesToArray($routes)
{ 
    $routeList = [];

    foreach ($routes as $route) {  
        $pathName = Str::after($route, '/');
        $pathName = Str::beforeLast($pathName, '/');
        $pathName = $pathName . '/files';
        $pathName = str_replace('/', '.', $pathName);

        $fileName = Str::afterLast($route, '/');
        
      	$existingFiles = Arr::get($routeList, $pathName) ?? [];
           
      	$newFile = [
            [
                'file' => $fileName,
                'url' => $route
            ]
        ]; 

      	$files = array_merge($existingFiles, $newFile);

        Arr::set($routeList, $pathName, $files);
    }
  
    return $routeList;
}

function getTreeBranches($paths)
{  
    $tree = [];
    
    $mainKey = key($paths);
    $tree['label'] = $mainKey;
    $paths = $paths[$mainKey];
   
    foreach ($paths as $key => $path) {
        if ($key == 'files') {
      	    $tree['children'] = array_merge($tree['children'] ?? [], $path);
      	    continue;
    	}
        $tree['children'][] = getTreeBranches([$key => $path]);
    }
  
    return $tree;
}

$routeList = routesToArray($routes);
$routeTree = [
  'data' => getTreeBranches($routeList)
];
var_dump(json_encode($routeTree, JSON_PRETTY_PRINT));

The code ended up being pretty messy with all the changes. Maybe you should consider refactoring.

XerK's avatar
Level 1

It only shows the first key in array sorry about it @guybrush_threepwood, I tried many things and can't

0: "/pictures/proof/Ek7rdD8ljxfkvnUDP4jj23kofsLKtmj9mfVVMZ42.jpg"
1: "/pictures/proof/hTIYjQyJdkTTtCKOtVfVbIUiEbZhOfQC3sH74txk.jpg"
2: "/projects/Motawer/2020/asd/Final Files/4IEibhzjUshSUN6Dkq21A7pRcAotOGsur0Touv59.png"
3: "/pictures/proof/p20xnbXq4nRAOk2bCJ2OvdBhYc08ghjvs7Vc98WQ.jpg"
4: "/projects/Motawer/2020/asd/Proofs/yr6icxnAIYz95eDXjy446yjVc8gKJ5b6mO1kvfqk.jpg"

Only return pictures folder didn't show projects folder

Its should return an array not object so can you help

guybrush_threepwood's avatar
Level 33

Hi @xerk

My bad, I assumed the first level directory wouldn't change (since you named it in your example "RootFolder"):

Try this:

    $routes = [
        '/pictures/proof/Ek7rdD8ljxfkvnUDP4jj23kofsLKtmj9mfVVMZ42.jpg',
        '/pictures/proof/hTIYjQyJdkTTtCKOtVfVbIUiEbZhOfQC3sH74txk.jpg',
        '/projects/Motawer/2020/asd/Final Files/4IEibhzjUshSUN6Dkq21A7pRcAotOGsur0Touv59.png',
        '/pictures/proof/p20xnbXq4nRAOk2bCJ2OvdBhYc08ghjvs7Vc98WQ.jpg',
        '/projects/Motawer/2020/asd/Proofs/yr6icxnAIYz95eDXjy446yjVc8gKJ5b6mO1kvfqk.jpg',
        '/projects/Motawer/2020/asd/yr6icxnAIYz95eDXjy446yjVc8gKJ5b6mO1kvfqk.jpg',
        '/projects/Motawer/2020/asd/Proofs2/yr6icxnAIYz95eDXjy446yjVc8gKJ5b6mO1kvfqk.jpg',
        '/sarasa.jpg',
    ];

    function routesToArray($routes)
    {
        $routeList = [];

        foreach ($routes as $route) {
            if (substr_count($route, '/') > 1) {
                $pathName = Str::after($route, '/');
                $pathName = Str::beforeLast($pathName, '/');
                $pathName = $pathName . '/!@_files';
            } else {
                $pathName = '!@_files';
            }

            $pathName = str_replace('/', '.', $pathName);

            $fileName = Str::afterLast($route, '/');

            $existingFiles = Arr::get($routeList, $pathName) ?? [];

            $newFile = [
                [
                    'file' => $fileName,
                    'url' => $route
                ]
            ];

            $files = array_merge($existingFiles, $newFile);

            Arr::set($routeList, $pathName, $files);
        }

        return $routeList;
    }

    function getTreeBranches($paths)
    {
        $tree = [];

        foreach ($paths as $key => $path) {
            if($key === '!@_files') {
                $tree = array_merge($tree, $path);
                continue;
            }

            $tree[] = [
                'label' => strval($key),
                'children' => getTreeBranches($path),
            ];
        }

        return $tree;
    }

    $routeList = routesToArray($routes);

    $routeTree = [
        'data' => getTreeBranches($routeList)
    ];

    var_dump(json_encode($routeTree, JSON_PRETTY_PRINT));

I updated the script to allow files on the top level folder and files in the same level as directories that contain other files.

1 like

Please or to participate in this conversation.