Ogy
Ogy

Reputation: 107

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?

Upvotes: 8

Views: 10385

Answers (8)

VIPLIKE
VIPLIKE

Reputation: 196

public function sortedScanDir($dir) {
  // scan the current folder
  $content = scandir($dir);

  // create arrays
  $folders = [];
  $files = [];

  // loop through
  foreach ($content as $file) {
    $fileName = $dir . '/' . $file;
    if (is_dir($fileName)) {
      $folders[] = $file;
    } else {
      $files[] = $file;
    }
  }

  // combine
  return array_merge($folders, $files);
}

Upvotes: 0

Varga Tamas
Varga Tamas

Reputation: 53

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

Upvotes: 2

crazeyez
crazeyez

Reputation: 499

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.

Upvotes: 1

MatRt
MatRt

Reputation: 3534

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.

Upvotes: 4

Dipesh Parmar
Dipesh Parmar

Reputation: 27354

Try

<?php
    $path = $_SERVER['DOCUMENT_ROOT'];
    chdir ($path);

    $dir = array_diff (scandir ('.'), array ('.', '..', '.DS_Store', 'Thumbs.db'));

    usort ($dir, create_function ('$a,$b', '
        return  is_dir ($a)
        ? (is_dir ($b) ? strnatcasecmp ($a, $b) : -1)
        : (is_dir ($b) ? 1 : (
            strcasecmp (pathinfo ($a, PATHINFO_EXTENSION), pathinfo ($b, PATHINFO_EXTENSION)) == 0
            ? strnatcasecmp ($a, $b)
            : strcasecmp (pathinfo ($a, PATHINFO_EXTENSION), pathinfo ($b, PATHINFO_EXTENSION))
        ))
    ;
'));

    header ('content-type: text/plain');
    print_r ($dir);
?>

Upvotes: 1

sjdaws
sjdaws

Reputation: 3536

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

}

Upvotes: 1

David Kiger
David Kiger

Reputation: 1996

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.

Upvotes: 3

Prasanth Bendra
Prasanth Bendra

Reputation: 32710

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

Upvotes: 9

Related Questions