Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHP: sort folder first and then files

$dir = '/master/files';
$files = scandir($dir);
foreach($files as $file){
   if(($file != '.') && ($file != '..')){
      if(is_dir($dir.'/'.$file)){
         echo '<li class="folder">'.$file.'</li>';
      }else{
         echo '<li class="file">'.$file.'</li>';
      }
   }
}

From the script above, I get result:

images (folder)
index.html    
javascript (folder)
style.css

How to sort the folder first and then files?

like image 628
Ogy Avatar asked Feb 25 '13 05:02

Ogy


6 Answers

Try this :

$dir = '/master/files';
$directories = array();
$files_list  = array();
$files = scandir($dir);
foreach($files as $file){
   if(($file != '.') && ($file != '..')){
      if(is_dir($dir.'/'.$file)){
         $directories[]  = $file;

      }else{
         $files_list[]    = $file;

      }
   }
}

foreach($directories as $directory){
   echo '<li class="folder">'.$directory.'</li>';
}
foreach($files_list as $file_list){
   echo '<li class="file">'.$file_list.'</li>';
}
like image 69
Prasanth Bendra Avatar answered Oct 06 '22 01:10

Prasanth Bendra


You don't need to make 2 loops, you can do the job with this piece of code:

<?php

function scandirSorted($path) {

    $sortedData = array();
    foreach(scandir($path) as $file) {

        // Skip the . and .. folder
        if($file == '.' || $file == '..')
            continue;            

        if(is_file($path . $file)) {
            // Add entry at the end of the array
            array_push($sortedData, '<li class="folder">' . $file . '</li>');
        } else {
            // Add entry at the begin of the array
            array_unshift($sortedData, '<li class="file">' . $file . '</li>');
        }
    }
    return $sortedData;
}

?>

This function will return the list of entries of your path, folders first, then files.

like image 29
MatRt Avatar answered Oct 06 '22 00:10

MatRt


Modifying your code as little as possible:

$folder_list = "";
$file_list = "";
$dir = '/master/files';
$files = scandir($dir);
foreach($files as $file){
   if(($file != '.') && ($file != '..')){
      if(is_dir($dir.'/'.$file)){
         $folder_list .= '<li class="folder">'.$file.'</li>';
      }else{
         $file_list .= '<li class="file">'.$file.'</li>';
      }
   }
}

print $folder_list;
print $file_list;

This only loops through everything once, rather than requiring multiple passes.

like image 28
David Kiger Avatar answered Oct 06 '22 00:10

David Kiger


I have a solution for N deep recursive directory scan:

function scanRecursively($dir = "/") {
    $scan = array_diff(scandir($dir), array('.', '..'));
    $tree = array();
    $queue = array();
    foreach ( $scan as $item ) 
        if ( is_file($item) ) $queue[] = $item;
        else $tree[] = scanRecursively($dir . '/' . $item);
    return array_merge($tree, $queue);
}
like image 37
Varga Tamas Avatar answered Oct 06 '22 00:10

Varga Tamas


Store the output in 2 arrays, then iterate through the arrays to output them in the right order.

$dir = '/master/files';

$contents = scandir($dir);

// create blank arrays to store folders and files
$folders = $files = array();

foreach ($contents as $file) {

    if (($file != '.') && ($file != '..')) {

        if (is_dir($dir.'/'.$file)) {

            // add to folders array
            $folders[] = $file;

        } else {

            // add to files array
            $files[] = $file;

        }
    }
}

// output folders
foreach ($folders as $folder) {

    echo '<li class="folder">' . $folder . '</li>';

}

// output files
foreach ($files as $file) {

    echo '<li class="file">' . $file . '</li>';

}
like image 26
sjdaws Avatar answered Oct 06 '22 00:10

sjdaws


Being a CodeIgniter lover, I have in fact modified the core directory_helper from that to include the ability to have certain files exempt from the scanning in addition to setting the depth and choosing if hidden files should be included.

All credit goes to the original authors of CI. I simply added to it with the exempt array and building in the sorting.

It uses ksort to order the folders, as the folder name is set as the key and natsort to order the files inside each folder.

The only thing you may need to do is define what the DIRECTORY_SEPARATOR is for your environment but I don't think you will need to modify much else.

function directory_map($source_dir, $directory_depth = 0, $hidden = FALSE, $exempt = array())
{
    if ($fp = @opendir($source_dir))
    {
        $folddata   = array();
        $filedata   = array();
        $new_depth  = $directory_depth - 1;
        $source_dir = rtrim($source_dir, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR;

        while (FALSE !== ($file = readdir($fp)))
        {
            // Remove '.', '..', and hidden files [optional]
            if ($file === '.' OR $file === '..' OR ($hidden === FALSE && $file[0] === '.'))
            {
                continue;
            }

            is_dir($source_dir.$file) && $file .= DIRECTORY_SEPARATOR;

            if (($directory_depth < 1 OR $new_depth > 0) && is_dir($source_dir.$file))
            {
                $folddata[$file] = directory_map($source_dir.$file, $new_depth, $hidden, $exempt);

            }
            elseif(empty($exempt) || !empty($exempt) && !in_array($file, $exempt))
            {
                $filedata[] = $file;
            }
        }

        !empty($folddata) ? ksort($folddata) : false;
        !empty($filedata) ? natsort($filedata) : false;

        closedir($fp);
        return array_merge($folddata, $filedata);
    }

    return FALSE;
}

Usage example would be:

$filelist = directory_map('full_server_path');

As mentioned above, it will set the folder name as the key for the child array, so you can expect something along the lines of the following:

Array(
[documents/] => Array(
    [0] => 'document_a.pdf',
    [1] => 'document_b.pdf'    
    ),
[images/] => Array(
    [tn/] = Array(
        [0] => 'picture_a.jpg',
        [1] => 'picture_b.jpg'
        ),
    [0] => 'picture_a.jpg',
    [1] => 'picture_b.jpg'        
    ),
[0] => 'file_a.jpg',
[1] => 'file_b.jpg'
);

Just keep in mind that the exempt will be applied to all folders. This is handy if you want to skip out a index.html file or other file that is used in the directories you don't want included.

like image 41
crazeyez Avatar answered Oct 06 '22 00:10

crazeyez