KdgDev
KdgDev

Reputation: 14529

Deep recursive array of directory structure in PHP

I'm trying to put some folders on my hard-drive into an array.

For instance, vacation pictures. Let's say we have this structure:

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(...)
    ...
)

I came across this: http://www.the-art-of-web.com/php/dirlist/

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...). Example

Upvotes: 19

Views: 25820

Answers (6)

0kph
0kph

Reputation: 11

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:

    $contents[$node] = ...
                       ...
    $contents[] =      ...

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

Result:

Array
(
    [111000] => Array
        (
            [0] => file1
            [1] => file2
        )

    [700030] => Array
        (
            [456098] => Array
                (
                    [0] => file1
                    [1] => file2
                )

            [456099] => file1 <---- someone can expect 0
            [456100] => file2 <---- someone can expect 1
        )

    [999900] => Array
        (
            [0] => file1
            [1] => file2
        )

    [999901] => file1   <---- someone can expect 0
    [999902] => file2   <---- someone can expect 1
)

As you can see 4 elements has index as incrementation of last name of directory.

Upvotes: 1

modnarrandom
modnarrandom

Reputation: 161

So 6 years later....

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;
}

Upvotes: 0

tim
tim

Reputation: 3903

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);

Upvotes: 6

Peter Bailey
Peter Bailey

Reputation: 105878

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;
}

Upvotes: 47

Michael Johnson
Michael Johnson

Reputation: 2307

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.

Upvotes: 2

soulmerge
soulmerge

Reputation: 75704

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;
}

Upvotes: 14

Related Questions