29

I have an array I'm getting back from scandir, but it contains "." and ".." and I don't want it to.

My code:

$indir = scandir('../pages');
$fileextensions = array(".", "php", "html", "htm", "shtml");
$replaceextensions = str_replace($fileextensions, "", $indir);

I am doing a string replace on the file extensions, thus causing [0] and [1] to appear empty, but they are "." and ".."

array(4) {
[0]=>
string(0) ""
[1]=>
string(0) ""
[2]=>
string(4) "test"
[3]=>
string(4) "home"
}

How would I remove the "." and ".." from the array?

Mark Amery
  • 143,130
  • 81
  • 406
  • 459
Rbn
  • 381
  • 2
  • 4
  • 11
  • Related, and perhaps what you're really looking for: [Exclude hidden files from scandir](http://stackoverflow.com/questions/8532569/exclude-hidden-files-from-scandir) – Mark Amery Jun 09 '15 at 11:09

9 Answers9

50

You can use array_filter.

$indir = array_filter(scandir('../pages'), function($item) {
    return !is_dir('../pages/' . $item);
});

Note this filters out all directories and leaves only files and symlinks. If you really want to only exclude only files (and directories) starting with ., then you could do something like:

$indir = array_filter(scandir('../pages'), function($item) {
    return $item[0] !== '.';
});
leftclickben
  • 4,564
  • 23
  • 24
  • 2
    Awesome. Perfect explanation. Thanks so much. – Rbn Feb 04 '13 at 04:05
  • Don't use array_filter with a callback for this, just use `array_diff`. – Arnold Daniels Feb 04 '13 at 04:08
  • 1
    @ArnoldDaniels That would only work to remove exactly `.` and `..`, my solutions are more generalised and neither can be done with `array_diff()` – leftclickben Feb 04 '13 at 04:11
  • @leftclickben Removing `.` and `..` is exactly his question. – Arnold Daniels Feb 04 '13 at 04:14
  • 2
    Just because a junior programmer thinks they are only looking for `.` and `..`, in my experience that is rarely the case. The question states that he is putting names of files into a database. Therefore my first answer was to filter out the directories, which includes `.` and `..`. – leftclickben Feb 04 '13 at 04:23
  • 1
    Also, why do you say "don't use array_filter.. for this"? What is your reason for telling me not to using it? – leftclickben Feb 04 '13 at 04:23
  • The way the headline of the question is written, it's asking more: it says "Include JUST files". This means people are going to find it in searches for this question, not merely removing `.` and `..`. Also from a practical standpoint a solution that does not remove subdirectories is unstable because if later, a subdirectory is created, the code may break. This sort of problem can be devilishly hard to debug too, as a script will break without any modifications to the script itself. Better to write the more robust, extensible code the first time around. – cazort Oct 19 '22 at 13:59
29

Fastest way to remove dots as files in scandir

$files = array_slice(scandir('/path/to/directory/'), 2); 

From the PHP Manual

mintedsky
  • 1,073
  • 13
  • 18
  • 3
    This is just what I was looking for. Easy straight forward. This should be considered as best answer – Ikhlak S. Jun 20 '16 at 18:20
  • yeah seems like the best approach, seems like there should be an included filter for scan_dir built in. Seems like a no-brainer. I could be wrong. – vikingben Dec 07 '18 at 20:13
  • 1
    Taken from a [comment](https://www.php.net/manual/en/function.scandir.php#122438) on the PHP site itself: Someone wrote that array_slice could be used to quickly remove directory entries "." and "..". However, "-" is a valid entry that would come before those, so array_slice would remove the wrong entries. – Max Oct 21 '19 at 09:15
  • 2
    Never ever saw a "-" in my directory. I think in most cases this will work fine. – mintedsky Oct 22 '19 at 15:09
  • This solution does not work if you have a prefix '#' or '-' in your entries, because they go before the dot entries. – Zelkovar Feb 02 '22 at 07:36
  • This solution will remove the first two entries from the list, independently of what they are. It is not a robust or extensible solution in that it will not remove other directories if there are any subdirectories in the directory in question. It also will fail (removing two files and leaving the two unwanted entries) if you change the sort order returned by `scandir` by sending a second parameter. You might not need this functionality, but it's worth knowing the limitations of this approach. – cazort Oct 19 '22 at 13:27
9

array_diff will do what you're looking for:

$indir = scandir('../pages');
$fileextensions = array(".", "php", "html", "htm", "shtml");
$indir = array_diff($indir, array('.', '..'));
$replaceextensions = str_replace($fileextensions, "", $indir);

http://php.net/manual/en/function.array-diff.php

Jonathan Wren
  • 3,662
  • 23
  • 29
  • As with mintedsky's solution, this solution will fail if there are any subdirectories, and it will also fail if the sort order of `scandir` is changed. The original question specifies "include JUST files", with this emphasis. A solution that does not filter out subdirectories is unstable as the code may break if a subdirectory is later created. Such bugs can be hard to debug because the code breaks without any modification to the script itself. The solution by leftclickben addresses these issues and is about as concise. Best to avoid later headache! – cazort Oct 19 '22 at 14:07
3

I am aware erknrio provided an answer for this, but here is a cleaner way of getting an array of files without directories (modified to be more efficient):

$dirPath = 'dashboard';

$dir = scandir($dirPath);

foreach($dir as $index => &$item)
{
    if(is_dir($dirPath. '/' . $item))
    {
        unset($dir[$index]);
    }
}

$dir = array_values($dir);
FluorescentGreen5
  • 879
  • 11
  • 23
0

You can use this snippet. It returns just files in directory:

function only_files($dir_element) {
    if (!is_dir($dir_element)) {
        return $dir_element;
    }
}

function givemefiles($dir) {
    $scanned_dir = scandir($dir);
    return array_filter($scanned_dir, "only_files");
}

$dir_path = '../pages';

givemefiles($dir_path);
echo "<pre>";
var_dump($scanned_dir);
echo "</pre>";
mrroot5
  • 1,811
  • 3
  • 28
  • 33
0

simply use preg_replace to remove all kind of hidden's file from directory

$files = array(".", "..", "html", ".~html", "shtml");    
$newfiles = preg_grep('/^([^.])/', scandir($files));
Devdutt Sharma
  • 391
  • 1
  • 2
  • 10
0

If you need a clean function;

function getFiles($dir){
    return array_values(array_filter(scandir($dir), function($file){
        global $dir;
        return !is_dir("{$dir}/{$file}");
    }));
}

I think this function is both neat and will work well. Also, the keys of the returned array in this function are sorted correctly.

Emrah Tuncel
  • 678
  • 8
  • 13
0

All other answers are good. I have a clean workaround that worked for me.

Use glob function that takes a regex and returns only files that match. It seems to exclude the . and .. directories, so using a simple

$indir = glob('../pages/*');

should work well.

Epanemu
  • 106
  • 1
  • 11
  • This solution effectively removes the `.` an `..` entries, but it includes subdirectories, which the headline of the question explicitly states to exclude, i.e. "include JUST files", with that emphasis. The solution by leftclickben addresses this shortcoming, you could probably easily modify your approach using `glob` instead of `scandir` too. If the code would break if used on a directory, you want to add the extra check because a subdirectory might get created later down the line, causing code breakage that could be tricky to debug. – cazort Oct 19 '22 at 14:14
  • 1
    Ah, my mistake for not reading the question carefully. The application I used it for ensured no further subdirectories, so I had not encountered this issue. Nevertheless, it worked flawlessly without much code required, so I wanted to share it. Thank you for pointing this out. – Epanemu Oct 27 '22 at 17:37
0
function dscan( $path ){
     global $dfiles;
     if( !isset( $dfiles ) ){
       $dfiles = array();
     }
     $files = preg_grep('/^.*\.php$/i', scandir($path));
     foreach( $files as $file ){
       if( !in_array( "$path/$file",$dfiles ) ){
         $dfiles[] = array( 'path' => "$path/$file", 'name' => $file );
       }
     }
     return $dfiles;
   }
   dscan( $path );
   print_r( $dfiles );
  • 2
    Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Jan 07 '23 at 15:16