Как найти каталог php

(PHP 5, PHP 7, PHP 8)

scandirПолучает список файлов и каталогов, расположенных по указанному пути

Описание

scandir(string $directory, int $sorting_order = SCANDIR_SORT_ASCENDING, ?resource $context = null): array|false

Список параметров

directory

Сканируемый каталог.

sorting_order

По умолчанию сортировка производится в алфавитном порядке
по возрастанию. Если необязательный параметр
sorting_order установлен в значение
SCANDIR_SORT_DESCENDING, сортировка производится
в алфавитном порядке по убыванию. Если же он установлен в значение
SCANDIR_SORT_NONE, то сортировка не производится.

context

За описанием параметра context
обратитесь к разделу Потоки
данного руководства.

Возвращаемые значения

Возвращает массив (array) имён файлов в случае успешного выполнения или
false в случае возникновения ошибки. Если directory не
является каталогом, возвращается false и генерируется сообщение
об ошибке уровня E_WARNING.

Список изменений

Версия Описание
8.0.0 context теперь допускает значение null.

Примеры

Пример #1 Простой пример использования функции scandir()


<?php
$dir
= '/tmp';
$files1 = scandir($dir);
$files2 = scandir($dir, SCANDIR_SORT_DESCENDING);print_r($files1);
print_r($files2);
?>

Результатом выполнения данного примера
будет что-то подобное:

Array
(
    [0] => .
    [1] => ..
    [2] => bar.php
    [3] => foo.txt
    [4] => somedir
)
Array
(
    [0] => somedir
    [1] => foo.txt
    [2] => bar.php
    [3] => ..
    [4] => .
)

Примечания

Подсказка

Для этой функции вы можете использовать URL в качестве имени файла, если была включена опция fopen wrappers. Смотрите более подробную информацию об определении имени файла в описании функции fopen(). Смотрите также список поддерживаемых обёрток URL, их возможности, замечания по использованию и список предопределённых констант в разделе Поддерживаемые протоколы и обёртки.

Смотрите также

  • opendir() – Открывает дескриптор каталога
  • readdir() – Получает элемент каталога по его дескриптору
  • glob() – Находит файловые пути, совпадающие с шаблоном
  • is_dir() – Определяет, является ли имя файла директорией
  • sort() – Сортирует массив по возрастанию

dwieeb at gmail dot com

11 years ago


Easy way to get rid of the dots that scandir() picks up in Linux environments:

<?php
$directory
= '/path/to/my/directory';
$scanned_directory = array_diff(scandir($directory), array('..', '.'));
?>


mmda dot nl at gmail dot com

10 years ago


Here is my 2 cents. I wanted to create an array of my directory structure recursively. I wanted to easely access data in a certain directory using foreach. I came up with the following:

<?php

function dirToArray($dir) {
$result = array();
$cdir = scandir($dir);

   foreach (
$cdir as $key => $value)

   {

      if (!
in_array($value,array(".","..")))

      {

         if (
is_dir($dir . DIRECTORY_SEPARATOR . $value))

         {

           
$result[$value] = dirToArray($dir . DIRECTORY_SEPARATOR . $value);

         }

         else

         {

           
$result[] = $value;

         }

      }

   }

  
   return

$result;

}

?>



Output

Array

(

   [subdir1] => Array

   (

      [0] => file1.txt

      [subsubdir] => Array

      (

         [0] => file2.txt

         [1] => file3.txt

      )

   )

   [subdir2] => Array

   (

    [0] => file4.txt

   }

)


info at remark dot no

5 years ago


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.

coolbikram0 at gmail dot com

1 year ago


A simple recursive function to list all files and subdirectories in a directory:
<?php
function listAllFiles($dir) {
 
$array = array_diff(scandir($dir), array('.', '..'));

    foreach (

$array as &$item) {
   
$item = $dir . $item;
  }
  unset(
$item);
  foreach (
$array as $item) {
    if (
is_dir($item)) {
    
$array = array_merge($array, listAllFiles($item . DIRECTORY_SEPARATOR));
    }
  }
  return
$array;
}
?>

Pawel Dlugosz

18 years ago


For directory containing files like (for example) -.jpg the results of scandir are a little "weird" ;)

<?php

      $dir

= '/somedir';
  
$files = scandir($dir);
  
print_r($files);
?>

Array
(
    [0] => -.jpg
    [1] => .
    [2] => ..
    [3] => foo.txt
    [4] => somedir
)

Beware - sorting is in ASCII order :)


kodlee at kodleeshare dot net

11 years ago


I needed to find a way to get the full path of all files in the directory and all subdirectories of a directory.

Here's my solution: Recursive functions!

<?php

function find_all_files($dir)

{

   
$root = scandir($dir);

    foreach(
$root as $value)

    {

        if(
$value === '.' || $value === '..') {continue;}

        if(
is_file("$dir/$value")) {$result[]="$dir/$value";continue;}

        foreach(
find_all_files("$dir/$value") as $value)

        {

           
$result[]=$value;

        }

    }

    return
$result;

}

?>


eep2004 at ukr dot net

8 years ago


Fastest way to get a list of files without dots.
<?php
$files
= array_slice(scandir('/path/to/directory/'), 2);

gambit_642 AT hotmailDOTcom

9 years ago


Needed something that could return the contents of single or multiple directories, recursively or non-recursively,
for all files or specified file extensions that would be
accessible easily from any scope or script.

And I wanted to allow overloading cause sometimes I'm too lazy to pass all params.
<?php
class scanDir {
    static private
$directories, $files, $ext_filter, $recursive;// ----------------------------------------------------------------------------------------------
    // scan(dirpath::string|array, extensions::string|array, recursive::true|false)
   
static public function scan(){
       
// Initialize defaults
       
self::$recursive = false;
       
self::$directories = array();
       
self::$files = array();
       
self::$ext_filter = false;// Check we have minimum parameters
       
if(!$args = func_get_args()){
            die(
"Must provide a path string or array of path strings");
        }
        if(
gettype($args[0]) != "string" && gettype($args[0]) != "array"){
            die(
"Must provide a path string or array of path strings");
        }
// Check if recursive scan | default action: no sub-directories
       
if(isset($args[2]) && $args[2] == true){self::$recursive = true;}// Was a filter on file extensions included? | default action: return all file types
       
if(isset($args[1])){
            if(
gettype($args[1]) == "array"){self::$ext_filter = array_map('strtolower', $args[1]);}
            else
            if(
gettype($args[1]) == "string"){self::$ext_filter[] = strtolower($args[1]);}
        }
// Grab path(s)
       
self::verifyPaths($args[0]);
        return
self::$files;
    }

    static private function

verifyPaths($paths){
       
$path_errors = array();
        if(
gettype($paths) == "string"){$paths = array($paths);}

        foreach(

$paths as $path){
            if(
is_dir($path)){
               
self::$directories[] = $path;
               
$dirContents = self::find_contents($path);
            } else {
               
$path_errors[] = $path;
            }
        }

        if(

$path_errors){echo "The following directories do not exists<br />";die(var_dump($path_errors));}
    }
// This is how we scan directories
   
static private function find_contents($dir){
       
$result = array();
       
$root = scandir($dir);
        foreach(
$root as $value){
            if(
$value === '.' || $value === '..') {continue;}
            if(
is_file($dir.DIRECTORY_SEPARATOR.$value)){
                if(!
self::$ext_filter || in_array(strtolower(pathinfo($dir.DIRECTORY_SEPARATOR.$value, PATHINFO_EXTENSION)), self::$ext_filter)){
                   
self::$files[] = $result[] = $dir.DIRECTORY_SEPARATOR.$value;
                }
                continue;
            }
            if(
self::$recursive){
                foreach(
self::find_contents($dir.DIRECTORY_SEPARATOR.$value) as $value) {
                   
self::$files[] = $result[] = $value;
                }
            }
        }
       
// Return required for recursive search
       
return $result;
    }
}
?>

Usage:
scanDir::scan(path(s):string|array, [file_extensions:string|array], [subfolders?:true|false]);
<?php
//Scan a single directory for all files, no sub-directories
$files = scanDir::scan('D:Websitestemp');//Scan multiple directories for all files, no sub-dirs
$dirs = array(
   
'D:folder';
   
'D:folder2';
   
'C:Other';
);
$files = scanDir::scan($dirs);// Scan multiple directories for files with provided file extension,
// no sub-dirs
$files = scanDir::scan($dirs, "jpg");
//or with an array of extensions
$file_ext = array(
   
"jpg",
   
"bmp",
   
"png"
);
$files = scanDir::scan($dirs, $file_ext);// Scan multiple directories for files with any extension,
// include files in recursive sub-folders
$files = scanDir::scan($dirs, false, true);// Multiple dirs, with specified extensions, include sub-dir files
$files = scanDir::scan($dirs, $file_ext, true);
?>


csaba at alum dot mit dot edu

18 years ago


Scandir on steroids:

For when you want to filter your file list, or only want to list so many levels of subdirectories...

<?php

function dirList($path="", $types=2, $levels=1, $aFilter=array()) {

//  returns an array of the specified files/directories

//  start search in $path (defaults to current working directory)

//  return $types:  2 => files; 1 => directories; 3 => both;

//  $levels: 1 => look in the $path only; 2 => $path and all children;

//          3 => $path, children, grandchildren; 0 => $path and all subdirectories;

//          less than 0 => complement of -$levels, OR everything starting -$levels down

//                e.g. -1 => everthing except $path; -2 => all descendants except $path + children

//  Remaining argument(s) is(are) a filter array(list) of regular expressions which operate on the full path.

//    First character (before the '/' of the regExp) '-' => NOT.

//    First character (after a possible '-') 'd' => apply to directory name

//    The filters may be passed in as an array of strings or as a list of strings

//  Note that output directories are prefixed with a '*' (done in the line above the return)

  
$dS = DIRECTORY_SEPARATOR;

   if (!(
$path = realpath($path?$path:getcwd()))) return array();    // bad path

   // next line rids terminating on drives (works since c: == c: on PHP).  OK in *nix?

  
if (substr($path,-1)==$dS) $path = substr($path,0,-1);

   if (
is_null($types)) $types = 2;

   if (
is_null($levels)) $levels = 1;

   if (
is_null($aFilter)) $aFilter=array();
// last argument may be passed as a list or as an array

  
$aFilter = array_slice(func_get_args(),3);

   if (
$aFilter && gettype($aFilter[0])=="array") $aFilter=$aFilter[0];

  
$adFilter = array();

  
// now move directory filters to separate array:

  
foreach ($aFilter as $i=>$filter)                  // for each directory filter...

    
if (($pos=stripos(" $filter","d")) && $pos<3) {  // next line eliminates the 'd'

        
$adFilter[] = substr($filter,0,$pos-1) . substr($filter,$pos);

         unset(
$aFilter[$i]); }

  
$aFilter = array_merge($aFilter);    // reset indeces
$aRes = array();                    // results, $aAcc is an Accumulator

  
$aDir = array($path);    // dirs to check

  
for ($i=$levels>0?$levels++:-1;($aAcc=array())||$i--&&$aDir;$aDir=$aAcc)

     while (
$dir = array_shift($aDir))

         foreach (
scandir ($dir) as $fileOrDir)

           if (
$fileOrDir!="." && $fileOrDir!="..") {

               if (
$dirP = is_dir ($rp="$dir$dS$fileOrDir"))

                 if (
pathFilter("$rp$dS", $adFilter))

                    
$aAcc[] = $rp;

               if (
$i<$levels-1 && ($types & (2-$dirP)))

                 if (
pathFilter($rp, $aFilter))

                    
$aRes[] = ($dirP?"*":"") . $rp; }

   return
$aRes;

}

?>



example usage:

<?php

define
("_", NULL);

// this will find all non .jpg, non .Thumbs.db files under c:Photo

$aFiles = dirList('c:Photo', _, 0, '-/.jpg$/i', '-/\\Thumbs.db$/');

$aFiles = dirList();    // find the files in the current directory

// next lines will find .jpg files in non Photo(s) subdirectories, excluding Temporary Internet Files

set_time_limit(60);        // iterating from the top level can take a while

$aFiles = dirList("c:\", _, 0, '/.jpg$/i', '-d/\\Photos?$/i', '-d/Temporary Internet/i');

?>



Note that this function will consume a lot of time if scanning large

directory structures (which is the reason for the '[-]d/.../' filters).

Csaba Gabor from Vienna


artmanniako at gmail dot com

4 years ago


How i solved problem with '.' and '..'

$x = scandir__DIR__; //any directory
foreach ($x as $key => $value) {
        if ('.' !== $value && '..' !== $value){
               echo $value;
   }
}
Simple and working


Stan P. van de Burgt

18 years ago


scandir() with regexp matching on file name and sorting options based on stat().

<?php

function myscandir($dir, $exp, $how='name', $desc=0)

{

   
$r = array();

   
$dh = @opendir($dir);

    if (
$dh) {

        while ((
$fname = readdir($dh)) !== false) {

            if (
preg_match($exp, $fname)) {

               
$stat = stat("$dir/$fname");

               
$r[$fname] = ($how == 'name')? $fname: $stat[$how];

            }

        }

       
closedir($dh);

        if (
$desc) {

           
arsort($r);

        }

        else {

           
asort($r);

        }

    }

    return(
array_keys($r));

}
$r = myscandir('./book/', '/^article[0-9]{4}.txt$/i', 'ctime', 1);

print_r($r);

?>



files can be sorted on name and stat() attributes, ascending and descending:

name    file name

dev     device number

ino     inode number

mode    inode protection mode

nlink   number of links

uid     userid of owner

gid     groupid of owner

rdev    device type, if inode device *

size    size in bytes

atime   time of last access (Unix timestamp)

mtime   time of last modification (Unix timestamp)

ctime   time of last inode change (Unix timestamp)

blksize blocksize of filesystem IO *

blocks  number of blocks allocated


fazle dot elahee at gmail dot com

11 years ago


/**
* This function will scan all files recursively in the sub-folder and folder.
*
* @author Fazle Elahee
*
*/

function scanFileNameRecursivly($path = '', &$name = array() )
{
  $path = $path == ''? dirname(__FILE__) : $path;
  $lists = @scandir($path);

    if(!empty($lists))
  {
      foreach($lists as $f)
      {

          if(is_dir($path.DIRECTORY_SEPARATOR.$f) && $f != ".." && $f != ".")
      {
          scanFileNameRecursivly($path.DIRECTORY_SEPARATOR.$f, &$name);
      }
      else
      {
          $name[] = $path.DIRECTORY_SEPARATOR.$f;
      }
      }
  }
  return $name;
}

$path = "/var/www/SimplejQueryDropdowns";
$file_names = scanFileNameRecursivly($path);

echo "<pre>";
var_dump($file_names);
echo "</pre>";


phpdotnet at lavavortex dot com

14 years ago


How a ninja may retrieve a list of files, files filtered by extension, or directories:

<?php

//NNNIIIinnnjaaa::

//array of files without directories... optionally filtered by extension

function file_list($d,$x){

       foreach(
array_diff(scandir($d),array('.','..')) as $f)if(is_file($d.'/'.$f)&&(($x)?ereg($x.'$',$f):1))$l[]=$f;

       return
$l;

}
//NNNIIIinnnjaaa::

//array of directories

function dir_list($d){

       foreach(
array_diff(scandir($d),array('.','..')) as $f)if(is_dir($d.'/'.$f))$l[]=$f;

       return
$l;

}
/********************************************************************

                    PRETTY PRETTY LIGHTS (DOCUMENTATION)

********************************************************************/

/********************************************************************

Overloaded PHP file listing function:

array file_list ( string $directory [, string $file_extension] )

$directory

  path without backslash, e.g. "/home/public_html/blarg"

$file_extention

  optionally filter specific filename extentions, e.g. ".jpg"

>>>>>>>>>>>>>>>>>>>>>>>>>>>>> TRANSLATION <<<<<<<<<<<<<<<<<<<<<<<<<<<<

file_list("/home");          //return list of files (no directories)

file_list("/home", ".jpg");  //return only .jpg file list

********************************************************************/

//(note: one slash (/) below and you enable all your test functions, guess where ;-))

/********************************************************************

// TEST FUNCTIONS... IF THESE WORK, THIS FUNCTION WORKS ON THIS PLATFORM

echo "<hr><b>File List:</b><br>";

$n = file_list(getcwd());

if($n) foreach($n as $f) echo "$f<br>";             //current files

echo "<hr><b>Files with extension .php:</b><br>";

$n = file_list(getcwd(),".php");

if($n) foreach($n as $f) echo "$f<br>";             //files with .php extensions

echo "<hr><b>Directories:</b><br>";

$d = dir_list(getcwd());

if($d) foreach($d as $f) echo "$f<br>";             //directories

/********************************************************************/

/************

RUNTIME NOTES:

file_list($arg1); // php issues a warning that there is no second parameter, but we know that, izz ok

************/

/*******************************

TESTED AND WORKING ON 2009.04.30:

OS:     Linux 2.6.9-78.0.17.ELsmp

APACHE: 2.2.9

PHP:    5.2.5

*******************************/

?>


arthur dot am dot app at gmail dot com

2 months ago


$path_abs = __DIR__ .'/test/';

if( DIRECTORY_SEPARATOR != substr($path, -1) || (DIRECTORY_SEPARATOR.DIRECTORY_SEPARATOR) == substr($path, -2))
{
    throw new Error("A non-standard path is specified. `{$path}`");
}

$scandir_result = scandir( $path, SCANDIR_SORT_DESCENDING );

for( $n = 0, $e = count($scandir_result) - 2; $n < $e; $n++ )
{
    echo ($path  .  $scandir_result[$n])  .'<br/>';
}


fatpratmatt dot at dot gmail dot com

15 years ago


This function generates a list of all files in the chosen directory and all subdirectories, throws them into a NON-multidimentional array and returns them.

Most of the recursive functions on this page only return a multi-dimensional array.

This is actually a modification of some one else's function (thanks mail at bartrail dot de ;])

<?php
function scanDirectories($rootDir, $allData=array()) {
   
// set filenames invisible if you want
   
$invisibleFileNames = array(".", "..", ".htaccess", ".htpasswd");
   
// run through content of root directory
   
$dirContent = scandir($rootDir);
    foreach(
$dirContent as $key => $content) {
       
// filter all files not accessible
       
$path = $rootDir.'/'.$content;
        if(!
in_array($content, $invisibleFileNames)) {
           
// if content is file & readable, add to array
           
if(is_file($path) && is_readable($path)) {
               
// save file name with path
               
$allData[] = $path;
           
// if content is a directory and readable, add path and name
           
}elseif(is_dir($path) && is_readable($path)) {
               
// recursive callback to open new directory
               
$allData = scanDirectories($path, $allData);
            }
        }
    }
    return
$allData;
}
?>

Example output:

print_r(scanDirectories("www"));
---
Array
(
    [0] => www/index.php
    [1] => www/admin.php
    [3] => www/css/css.css
    [4] => www/articles/2007/article1.txt
    [4] => www/articles/2006/article1.txt
    [8] => www/img/img1.png
)


Nerbert

3 years ago


If you use array_diff() to eliminate "." and ".." you must use array_values() on the result because array_diff() will return an associative array, which may cause problems for a for loop beginning at 0.

$files = array_values(array_diff(scandir($directory), array('..', '.')));


SPekary

5 years ago


Unless you specify no sorting, file names are sorted in ASCII alphabetic order, meaning numbers first, then uppercase, then lowercase letters, even on operating systems whose file system ignores the case of file names when it does its own sorting.

For example, on Mac OS, the following files will appear in this order in the Finder, when your disk is formated using the standard file system:
1file.php
a.inc
B.txt
c.txt

However, scandir will produce an array in the following order:
1file.php
B.txt
a.inc
c.txt


carneiro at isharelife dot com dot br

10 years ago


<?php
/**
     * Get an array that represents directory tree
     * @param string $directory     Directory path
     * @param bool $recursive         Include sub directories
     * @param bool $listDirs         Include directories on listing
     * @param bool $listFiles         Include files on listing
     * @param regex $exclude         Exclude paths that matches this regex
     */
   
function directoryToArray($directory, $recursive = true, $listDirs = false, $listFiles = true, $exclude = '') {
       
$arrayItems = array();
       
$skipByExclude = false;
       
$handle = opendir($directory);
        if (
$handle) {
            while (
false !== ($file = readdir($handle))) {
           
preg_match("/(^(([.]){1,2})$|(.(svn|git|md))|(Thumbs.db|.DS_STORE))$/iu", $file, $skip);
            if(
$exclude){
               
preg_match($exclude, $file, $skipByExclude);
            }
            if (!
$skip && !$skipByExclude) {
                if (
is_dir($directory. DIRECTORY_SEPARATOR . $file)) {
                    if(
$recursive) {
                       
$arrayItems = array_merge($arrayItems, directoryToArray($directory. DIRECTORY_SEPARATOR . $file, $recursive, $listDirs, $listFiles, $exclude));
                    }
                    if(
$listDirs){
                       
$file = $directory . DIRECTORY_SEPARATOR . $file;
                       
$arrayItems[] = $file;
                    }
                } else {
                    if(
$listFiles){
                       
$file = $directory . DIRECTORY_SEPARATOR . $file;
                       
$arrayItems[] = $file;
                    }
                }
            }
        }
       
closedir($handle);
        }
        return
$arrayItems;
    }
?>

simon dot riget at gmail dot com

6 years ago


This is a simple and versatile function that returns an array tree of files, matching wildcards:

<?php
// List files in tree, matching wildcards * and ?
function tree($path){
  static
$match;// Find the real directory part of the path, and set the match parameter
 
$last=strrpos($path,"/");
  if(!
is_dir($path)){
   
$match=substr($path,$last);
    while(!
is_dir($path=substr($path,0,$last)) && $last!==false)
     
$last=strrpos($path,"/",-1);
  }
  if(empty(
$match)) $match="/*";
  if(!
$path=realpath($path)) return;// List files
 
foreach(glob($path.$match) as $file){
   
$list[]=substr($file,strrpos($file,"/")+1);
  } 
// Process sub directories
 
foreach(glob("$path/*", GLOB_ONLYDIR) as $dir){
   
$list[substr($dir,strrpos($dir,"/",-1)+1)]=tree($dir);
  }

    return @

$list;
}
?>


telefoontoestel59 at hotmail dot com

7 years ago


I was looking for an easy way to get only files from a certain directory. I came up with the following line of code that will result in an array listing only files. the samen can be done for directories ofcourse.

<?php
$files
= array_filter(scandir($directory), function($file) { return is_file($file); })
?>


Tom

8 years ago


Just was curious to count files and lines in a project

<?phpfunction DirLineCounter( $dir , $result = array('lines_html' => false, 'files_count' => false, 'lines_count' => false ), $complete_table = true )
  {
$file_read = array( 'php', 'html', 'js', 'css' );
     
$dir_ignore = array();$scan_result = scandir( $dir );

            foreach (

$scan_result as $key => $value ) {

                  if ( !

in_array( $value, array( '.', '..' ) ) ) {

                          if (

is_dir( $dir . DIRECTORY_SEPARATOR . $value ) ) {

                                  if (

in_array( $value, $dir_ignore ) ) {
                    continue;
                  }
$result = DirLineCounter( $dir . DIRECTORY_SEPARATOR . $value, $result, false );

                                }
              else {

$type = explode( '.', $value );
             
$type = array_reverse( $type );
              if( !
in_array( $type[0], $file_read ) ) {
                continue;
              }
$lines = 0;
             
$handle = fopen( $dir . DIRECTORY_SEPARATOR . $value, 'r' );

              while ( !

feof( $handle ) ) {

                  if (

is_bool( $handle ) ) {
                      break;
                  }
$line = fgets( $handle );
                 
$lines++;
              }
fclose( $handle );$result['lines_html'][] = '<tr><td>' . $dir . '</td><td>' . $value . '</td><td>' . $lines . '</td></tr>';
             
$result['lines_count'] = $result['lines_count'] + $lines;
             
$result['files_count'] = $result['files_count'] + 1;

                                           }
          }
      }

            if (

$complete_table ) {$lines_html = implode('', $result['lines_html']) . '<tr><td></td><td style="border: 1px solid #222">Files Total: ' . $result['files_count'] . '</td><td style="border: 1px solid #222">Lines Total: ' . $result['lines_count'] . '</td></tr>';
        return
'<table><tr><td style="width: 60%; background-color:#ddd;">Dir</td><td style="width: 30%; background-color:#ddd;">File</td><td style="width: 10%; background-color:#ddd;">Lines</td></tr>' . $lines_html . '</table>';

              }
      else {
        return

$result;
      }

        }

  echo

DirLineCounter( '.' );?>


Patrick

9 years ago


Here is my recursive function, placing directories into new array keys and its contents within.

/* find_files( string, &array )
** Recursive function to return a multidimensional array of folders and files
** that are contained within the directory given
*/
function find_files($dir, &$dir_array)
{
    // Create array of current directory
    $files = scandir($dir);

        if(is_array($files))
    {
        foreach($files as $val)
        {
            // Skip home and previous listings
            if($val == '.' || $val == '..')
                continue;

                        // If directory then dive deeper, else add file to directory key
            if(is_dir($dir.'/'.$val))
            {
                // Add value to current array, dir or file
                $dir_array[$dir][] = $val;

                                find_files($dir.'/'.$val, $dir_array);
            }
            else
            {
                $dir_array[$dir][] = $val;
            }
        }
    }
    ksort($dir_array);
}

// Example
$folder_list = array();
find_files('/path', $folder_list)

var_dump($folder_list);

array(3) {
  ["directory_01"]=>
  array(4) {
    [0]=>
    string(12) "directory_02"
    [1]=>
    string(12) "directory_03"
    [2]=>
    string(11) "file_01.txt"
    [3]=>
    string(11) "file_02.txt"
  }
  ["directory_01/directory_02"]=>
  array(2) {
    [0]=>
    string(11) "file_03.txt"
    [1]=>
    string(11) "file_04.txt"
  }
  ["directory_01/directory_03"]=>
  array(2) {
    [0]=>
    string(11) "file_05.txt"
    [1]=>
    string(11) "file_06.txt"
  }
}


2bbasic at gmail dot com

10 years ago


<?php
//-- Directory Navigation with SCANDIR
//--
//-- optional placemenet
$exclude_list = array(".", "..", "example.txt");
if (isset(
$_GET["dir"])) {
 
$dir_path = $_SERVER["DOCUMENT_ROOT"]."/".$_GET["dir"];
}
else {
 
$dir_path = $_SERVER["DOCUMENT_ROOT"]."/";
}
//-- until here
function dir_nav() {
  global
$exclude_list, $dir_path;
 
$directories = array_diff(scandir($dir_path), $exclude_list);
  echo
"<ul style='list-style:none;padding:0'>";
  foreach(
$directories as $entry) {
    if(
is_dir($dir_path.$entry)) {
      echo
"<li style='margin-left:1em;'>[`] <a href='?dir=".$_GET["dir"].$entry."/"."'>".$entry."</a></li>";
    }
  }
  echo
"</ul>";
 
//-- separator
 
echo "<ul style='list-style:none;padding:0'>";
  foreach(
$directories as $entry) {
    if(
is_file($dir_path.$entry)) {
      echo
"<li style='margin-left:1em;'>[ ] <a href='?file=".$_GET["dir"].$entry."'>".$entry."</a></li>";
    }
  }
  echo
"</ul>";
}
dir_nav();
//-- optional placement
if (isset($_GET["file"])) {
  echo
"<div style='margin:1em;border:1px solid Silver;'>";
 
highlight_file($dir_path.$_GET['file']);
  echo
"</div>";
}
//-- until here
//--
//-- Because I love php.net
?>

moik78 at gmail dot com

13 years ago


This is a function to count the number of files of a directory

<?php

function countFiles($dir){

   
$files = array();

   
$directory = opendir($dir);

    while(
$item = readdir($directory)){

   
// We filter the elements that we don't want to appear ".", ".." and ".svn"

        
if(($item != ".") && ($item != "..") && ($item != ".svn") ){

             
$files[] = $item;

         }

    }

   
$numFiles = count($files);

    return
$numFiles;

}

?>


magicmind at netcabo dot pt

13 years ago


Hello all,

I just added a extension filter to the getDirectoryTree function, so it can filter an extension for files in the folders/subfolders:

<?php

function getDirectoryTree( $outerDir , $x){

   
$dirs = array_diff( scandir( $outerDir ), Array( ".", ".." ) );

   
$dir_array = Array();

    foreach(
$dirs as $d ){

        if(
is_dir($outerDir."/".$d)  ){

           
$dir_array[ $d ] = getDirectoryTree( $outerDir."/".$d , $x);

        }else{

         if ((
$x)?ereg($x.'$',$d):1)

           
$dir_array[ $d ] = $d;

            }

    }

    return
$dir_array;

}
$dirlist = getDirectoryTree('filmes','flv');

?>


progrium+php at gmail dot com

17 years ago


I made this to represent a directory tree in an array that uses the file or directory names as keys and full paths as the value for files. Directories are nested arrays.

<?php
function generatePathTree($rootPath) {
   
$pathStack = array($rootPath);
   
$contentsRoot = array();
   
$contents = &$contentsRoot;
    while (
$path = array_pop($pathStack)) {
      
$contents[basename($path)] = array();
      
$contents = &$contents[basename($path)];
       foreach (
scandir($path) as $filename) {
           if (
'.' != substr($filename, 0, 1)) {
              
$newPath = $path.'/'.$filename;
               if (
is_dir($newPath)) {
                  
array_push($pathStack, $newPath);
                  
$contents[basename($newPath)] = array();
               } else {
                  
$contents[basename($filename)] = $newPath;
               }
           }
       }
    }
    return
$contentsRoot[basename($rootPath)];
}
?>

The function will return something like this:

Array
(
    [index.php] => /usr/local/www/index.php
    [js] => Array
        (
            [async.js] => /usr/local/www/js/async.js
            [dom.js] => /usr/local/www/js/dom.js
            [effects.js] => /usr/local/www/js/effects.js
            [prototype.js] => /usr/local/www/js/prototype.js
        )

    [logo.png] => /usr/local/www/logo.png
    [server.php] => /usr/local/www/server.php
    [test.js] => /usr/local/www/test.js
)


dsiembab at fullchannel dot net

14 years ago


Back in the saddle of scandir I wrote this function for a function that I needed to seperate directories from files. Since I am still learning from my last example way below I would figure I would add it so it can be criticized.
<?php
function dirlist($dir, $bool = "dirs"){
  
$truedir = $dir;
  
$dir = scandir($dir);
   if(
$bool == "files"){ // dynamic function based on second pram
     
$direct = 'is_dir';
   }elseif(
$bool == "dirs"){
     
$direct = 'is_file';
   }
   foreach(
$dir as $k => $v){
      if((
$direct($truedir.$dir[$k])) || $dir[$k] == '.' || $dir[$k] == '..' ){
         unset(
$dir[$k]);
      }
   }
  
$dir = array_values($dir);
   return
$dir;
}
?>
<?php
print_r
(dirlist("../"));  //confirm array of subdirectoriesprint_r(dirlist("../", "files") // confirm list on files in the directory
?>

beingmrkenny at gmail dot com

16 years ago


I wrote this function to read a folder and place all the folders and sub folders it contains into an array.

<?php// Initialise empty array, otherwise an error occurs
$folders = array();

function

recursive_subfolders($folders) {// Set path here
   
$path = '/path/to/folder';// Create initial "Folders" array
   
if ($dir = opendir($path)) {
       
$j = 0;
        while ((
$file = readdir($dir)) !== false) {
            if (
$file != '.' && $file != '..' && is_dir($path.$file)) {
               
$j++;
               
$folders[$j] = $path . $file;
            }
        }
    }
closedir($dir);// Then check each folder in that array for subfolders and add the subfolders to the "Folders" array.
   
$j = count($folders);
    foreach (
$folders as $folder) {
        if (
$dir = opendir($folder)) {
            while ((
$file = readdir($dir)) !== false) {
               
$pathto = $folder. '/' . $file;
                if (
$file != '.' && $file != '..' && is_dir($pathto) && !in_array($pathto, $folders)) {
                   
$j++;
                   
$folders[$j] = $pathto;
                   
$folders = recursive_subfolders($folders);
                }
            }
        }
       
closedir($dir);
    }
sort($folders);
    return
$folders;
}
$folders = recursive_subfolders($folders);?>

$folders now contains an array with the full paths to each subfolder. E.g.:

Array
(
    [0] => /path/to/folder/dir1
    [1] => /path/to/folder/dir1/subdir
    [2] => /path/to/folder/dir1/subdir/subsubdir
    [3] => /path/to/dolfer/dir2
)

This function has only been tested on Linux.


webmaster at gmail dot com

3 years ago


easy way to list every item in a directory

<?php
$itemnum
= 1;
$dir = 'C:/Path/To/Directory';
foreach(
scandir($dir) as $item){
    if (!(
$item == '.')) {
        if (!(
$item == '..')) {
              echo(
$itemnum." = ".$item."<br>");
            
$itemnum = ($itemnum + 1);
}}}
?>


phpnet at novaclic dot com

13 years ago


Was looking for a simple way to search for a file/directory using a mask. Here is such a function.

By default, this function will keep in memory the scandir() result, to avoid scaning multiple time for the same directory.

Requires at least PHP5.

<?php

function sdir( $path='.', $mask='*', $nocache=0 ){

    static
$dir = array(); // cache result in memory

   
if ( !isset($dir[$path]) || $nocache) {

       
$dir[$path] = scandir($path);

    }

    foreach (
$dir[$path] as $i=>$entry) {

        if (
$entry!='.' && $entry!='..' && fnmatch($mask, $entry) ) {

           
$sdir[] = $entry;

        }

    }

    return (
$sdir);

}

?>


boen dot robot at gmail dot com

13 years ago


If you have a folder with many files and/or subfolders, doing a recursive scandir will likely either slow down your application, or cause a high rise in RAM consumption due to the large size of the generated array.

To help with this, as well as to make processing of files in a folder easier, I wrote a function that reads a folder and its subfolders recursively, and calls a function upon each match.

<?php
/**
* Calls a function for every file in a folder.
*
* @author Vasil Rangelov a.k.a. boen_robot
*
* @param string $callback The function to call. It must accept one argument that is a relative filepath of the file.
* @param string $dir The directory to traverse.
* @param array $types The file types to call the function for. Leave as NULL to match all types.
* @param bool $recursive Whether to list subfolders as well.
* @param string $baseDir String to append at the beginning of every filepath that the callback will receive.
*/
function dir_walk($callback, $dir, $types = null, $recursive = false, $baseDir = '') {
    if (
$dh = opendir($dir)) {
        while ((
$file = readdir($dh)) !== false) {
            if (
$file === '.' || $file === '..') {
                continue;
            }
            if (
is_file($dir . $file)) {
                if (
is_array($types)) {
                    if (!
in_array(strtolower(pathinfo($dir . $file, PATHINFO_EXTENSION)), $types, true)) {
                        continue;
                    }
                }
               
$callback($baseDir . $file);
            }elseif(
$recursive && is_dir($dir . $file)) {
               
dir_walk($callback, $dir . $file . DIRECTORY_SEPARATOR, $types, $recursive, $baseDir . $file . DIRECTORY_SEPARATOR);
            }
        }
       
closedir($dh);
    }
}
?>

Of course, because it is recursive, a folder with many levels of folders could potentially consume lots of memory, but then again, so can every other recursive scandir implementation here.

BTW, there's also the RecursiveDirectoryIterator SPL class:
http://bg.php.net/manual/en/class.recursivedirectoryiterator.php
which, even if using the same approach, will most likely be faster and hold down deeper levels (because it works on the C level), but this one will always work, regardless of settings... even on PHP4.

Point is, avoid recursive scandir implementations.


cHH

15 years ago


Since scandir() returns and array, here is a more concise method of dealing with the '.' and '..' problem when listing directories:

<?php

$target
= '/';

$weeds = array('.', '..');

$directories = array_diff(scandir($target), $weeds);

   
foreach(

$directories as $value)

{

   if(
is_dir($target.$value))

   {

      echo
$value.'<br />';

   }

}

?>


asamir at asamir dot net

15 years ago


This is a modification of scanDirectories function that generates a list of all files in the chosen directory and all subdirectories of specific extentions $allowext

<?php
function scanDirectories($rootDir, $allowext, $allData=array()) {
   
$dirContent = scandir($rootDir);
    foreach(
$dirContent as $key => $content) {
       
$path = $rootDir.'/'.$content;
       
$ext = substr($content, strrpos($content, '.') + 1);

                if(

in_array($ext, $allowext)) {
            if(
is_file($path) && is_readable($path)) {
               
$allData[] = $path;
            }elseif(
is_dir($path) && is_readable($path)) {
               
// recursive callback to open new directory
               
$allData = scanDirectories($path, $allData);
            }
        }
    }
    return
$allData;
}
$rootDir = "www";
$allowext = array("zip","rar","html");
$files_array = scanDirectories($rootDir,$allowext);
print_r($files_array);
?>


csaba at alum dot mit dot edu

18 years ago


A nice way to filter the files/directories you get back from scandir:

<?php
function pathFilter ($path, $aFilter) {
  
// returns true iff $path survives the tests from $aFilter
   // $aFilter is an array of regular expressions: [-]/regExp/modifiers
   // if there is a leading '-' it means exclude $path upon a match (a NOT test)
   // If the first expression has a leading '-', $path is in, unless it gets excluded.
   // Otherwise, $path is out, unless it gets included via the filter tests.
   // The tests are applied in succession.
   // A NOT test is applied only if $path is currently (within the tests) included
   // Other tests are applied only if $path is currently excluded.  Thus,
   // array("/a/", "-/b/", "/c/") => passes if $path has a c or if $path has an a but no b
   // array("/a/", "/c/", "-/b/") => passes if $path has an a or c, but no b
   // array("-/b/", "/a/", "/c/") => passes if $path has no b, or if $path has an a or c
  
if (!$aFilter) return true;           // automatic inclusion (pass) if no filters
  
foreach ($aFilter as $filter) break;  // we don't know how it's indexed
  
$in = $filter[0]=="-";                // initial in/exclusion based on first filter
  
foreach ($aFilter as $filter)         // walk the filters
     
if ($in==$not=($filter[0]=="-"))   //     testing only when necessary
        
$in ^= preg_match(substr($filter,$not),$path);    // flip in/exclusion upon a match
  
return $in;
}
?>

Csaba Gabor from Vienna


admin at kmoz000 dot free

4 years ago


function ScaniDir($path){
  $ptd= opendir($path);
  while (( $ptdf=readdir($ptd))){
      $srca = $path.'/'.$ptdf;
     if (is_file($srca) and pathinfo($srca, PATHINFO_EXTENSION)=='pdf') {$files['src'][]=$srca;$files['name'][]=explode('.',$ptdf)[0];}

         }
return $files ;

}


hex at mail dot nnov dot ru

9 years ago


// Extreme minimal version recursive scan:
function rscandir($dir){
  $dirs = array_fill_keys( array_diff( scandir( $dir ), array( '.', '..' ) ), array());
  foreach( $dirs as $d => $v )
    if( is_dir($dir."/".$d) )
      $dirs[$d] = rscandir($dir."/".$d);
  return $dirs;
}

djacobson at usa dot rugby

3 years ago


<?php
/**
  * Get rid of the dots, the occasional `.DS_Store` file and reindex the
  * resulting array all at once.
  *
  * @param string $path_to_directory The relative path to your target directory.
  *
  * @return array The reindexed array of files.
  */
function get_the_files( $path_to_directory ) {
    return
array_slice( array_diff( scandir( $path_to_directory ), array( '..', '.', '.DS_Store' ) ), 0 );
}
?>

wwwebdesigner at web doott de

18 years ago


using sort of scandir() that returns the content sorted by Filemodificationtime.

<?php
function scandir_by_mtime($folder) {
 
$dircontent = scandir($folder);
 
$arr = array();
  foreach(
$dircontent as $filename) {
    if (
$filename != '.' && $filename != '..') {
      if (
filemtime($folder.$filename) === false) return false;
     
$dat = date("YmdHis", filemtime($folder.$filename));
     
$arr[$dat] = $filename;
    }
  }
  if (!
ksort($arr)) return false;
  return
$arr;
}
?>

returns false if an error occured
otherwise it returns an array like this.
Array
(
    [20040813231320] => DSC00023.JPG
    [20040813231456] => DSC00024.JPG
    [20040814003728] => MOV00055.MPG
    [20040814003804] => DSC00056.JPG
    [20040814003946] => DSC00057.JPG
    [20040814004030] => DSC00058.JPG
    [20040814014516] => DSC00083.JPG
    [20050401161718] => album.txt
)


В данном уроке мы разберемся с типовой задачей, которая возникает во время работы над проектом PHP: получение списка файлов и директорий. Обсудим несколько базовых и более изощренных подходов, с перечисление плюсов и минусов каждого. Первые три решения будут использовать типовые функции PHP, а затем представим  более надежное с применением итераторов SPL.

Для предметного обсуждения решения и демонстраций предположим, что структура директорий имеет следующий вид:

---manager
|
---user
|   ---document.txt
|   ---data.dat
|   ---style.css
|---article.txt
|---master.dat
|---script.php
|---test.dat
|---text.txt

Базовые решения

Первый набор решений основан на использовании функции glob(), комбинации функций opendir(), readdir() и closedir(), и также функции scandir().

Использование glob()

Первое решение базируется на использовании функции glob(), которая позволяет выполнять поиск пути с помощью шаблонов. Функция имеет два параметра:

  • $pattern (обязательный): шаблон поиска
  • $flags (опциональный): один или несколько флагов, описание которых можно найти в документации

Рассмотрим примеры. Для поиска в директории всех файлов и директорий, имена которых заканчиваются на .txt, следует использовать код:

<?php
$filelist = glob("*.txt");

Если вывести переменную $filelist, то получим:

array (
  0 => 'article.txt',
  1 => 'text.txt'
)

Если нужен список файлов и директорий, имена которых начинаются на “te”, то код будет выглядеть так:

<?php
$filelist = glob("te*");

А вывод имеет вид:

array (
  0 => 'test.dat',
  1 => 'text.txt'
)

А для получения списка только директорий с именами, содержащих “ma”, используем код:

<?php
$filelist = glob("*ma*", GLOB_ONLYDIR);

Последний пример выведет:

Обратите внимание, что в последнем примере использован флаг GLOB_ONLYDIR в качестве второго параметра функции. Поэтому файл master.dat исключен из списка. Хотя функция glob() очень проста в использовании, иногда она недостаточно гибкая. Например, нет флага для получения только файлов (без директорий), которые соответствуют шаблону.

Используем opendir(), readdir(), и  closedir().

Второй подход к получению списка файлов и директорий, который мы обсудим, заключается в использовании функций  opendir(), readdir(), и closedir().

Функция  opendir() открывает директорию и возвращает дескриптор соединения. Как только дескриптор получен, можно использовать функцию readdir(). С каждым обращением данная функция выдает имя следующего файла или директории внутри открытого каталога. Если все имена уже были перечислены, функция возвращает false. Для закрытия дескриптора используется функция closedir().

В отличие от использования функции glob(), данный подход сложнее, так как у вас нет параметров, которые помогают фильтровать список возвращаемых имен файлов и директорий. Вы должны выполнить фильтрацию самостоятельно, чтобы получить нужный результат.

Ниже приведенный пример возвращает список имен файлов и директорий начинающихся на “te”:

<?php
$filelist = array();
if ($handle = opendir(".")) {
    while ($entry = readdir($handle)) {
        if (strpos($entry, "te") === 0) {
            $filelist[] = $entry;
        }
    }
    closedir($handle);
}

При выполнении выше приведенного кода, переменная $entry будет содержать такие включения, как  “.” и “..”. Это две виртуальные директории, которые имеются в каждом каталоге файловой системы. Они представляют текущий каталог и родительский каталог соответственно.

Второй пример выводит только файлы, содержащиеся в заданном каталоге.

<?php
$filelist = array();
if ($handle = opendir(".")) {
    while ($entry = readdir($handle)) {
        if (is_file($entry)) {
            $filelist[] = $entry;
        }
    }
    closedir($handle);
}

Пример выдаст следующее:

array (
  0 => 'article.txt',
  1 => 'master.dat',
  2 => 'script.php',
  3 => 'test.dat',
  4 => 'text.txt'
)

Использование scandir()

В завершение представим функцию scandir(). Она имеет только один обязательный параметр: путь для чтения.  Функция возвращает массив файлов и директорий, расположенных по указанному пути. Для получения списка файлов и директорий по определенному критерию нужно выполнить дополнительную фильтрацию. С другой стороны, решение получается более кратким и не требует управления дескрипторами.

Данный пример показывает, как получить список файлов и каталогов, имена которых начинаются на “te”:

<?php
$entries = scandir(".");
$filelist = array();
foreach($entries as $entry) {
    if (strpos($entry, "te") === 0) {
        $filelist[] = $entry;
    }
}

Воспользуемся итераторами SPL

Теперь рассмотрим использование итераторов SPL. Но прежде, чем приступить к решению нашей задачи, проведем введение в библиотеку SPL и итераторы. Библиотека SPL предоставляет серию классов для объектно ориентированных структур данных, итераторов, дескрипторов файлов и прочее.

Одно из преимуществ итераторов заключается в том, что они являются классами и их можно расширить для удовлетворения собственных нужд. Другой плюс состоит в том, что итераторы имеют собственные методы, которые являются полезными при решении множества типовых задач и располагаются в одном месте. Посмотрите на пример использования FilesystemIterator в сравнении с readdir(). Оба метода применяют цикл, но в случае readdir() вы обрабатываете только строку, а  FilesystemIterator работает с объектом, который может содержать дополнительную информацию о файле или директории (размер, владелец, права доступа и так далее).

Конечно, PHP представляет возможность для получения такой информации с помощью функций,например filesize() и fileowner(). Но PHP5 основан на использовании концепции ООП. Поэтому лучше использовать современные методы работы с языком программирования. На нашем сайте есть уроки, посвященные работе с итераторами.

Как уже сообщалось во водной части урока, мы рассмотрим использование  FilesystemIterator, RecursiveDirectoryIterator и GlobIterator. Первый наследуется от DirectoryIterator, а остальные от FilesystemIterator. Они все имеют один и тот же конструктор, который принимает два параметра:

  • $path (обязательный): путь к пункту файловой системы, над которым совершаются операции
  • $flags (опциональный): один или несколько флагов, перечисленных в документации

Реальное различие в данных итераторах заключается в их использовании для навигации по заданному пути.

FilesystemIterator

Использовать FilesystemIterator очень просто. Рассмотрим в действии. Представляем два примера. Первый показывает поиск всех файлов и каталогов, имена которых начинаются на “te”. Второй пример использует другой итератор RegexIterator для поиска всех файлов и каталогов, имена которых заканчиваются на “t.dat” или “t.php”. Итератор RegexIterator используется для фильтрации результата на основе регулярных выражений.

<?php
$iterator = new FilesystemIterator(".");
$filelist = array();
foreach($iterator as $entry) {
    if (strpos($entry->getFilename(), "te") === 0) {
        $filelist[] = $entry->getFilename();
    }
}

Выше приведенный код выдаст результат, аналогичный предыдущим примерам.

Второй пример с применением RegexIterator:

<?php
$iterator = new FilesystemIterator(".");
$filter = new RegexIterator($iterator, '/t.(php|dat)$/');
$filelist = array();
foreach($filter as $entry) {
    $filelist[] = $entry->getFilename();
}

Он будет выводить:

array (
  0 => 'script.php',
  1 => 'test.dat'
)

RecursiveDirectoryIterator

Итератор RecursiveDirectoryIterator обеспечивает интерфейс для рекурсивного прохода по директориям файловой системы. Он имеет несколько полезных методов, таких как getChildren() и hasChildren(), которые возвращают итератор для текущего места, если это директория, и проверяют, является ли текущая точка входа директорией. Следующий пример демонстрирует использование RecursiveDirectoryIterator и getChildren(). Результат будет такой же, как и в предыдущих примерах.

<?php
$iterator = new RecursiveDirectoryIterator('.');
$filter = new RegexIterator($iterator->getChildren(), '/t.(php|dat)$/');
$filelist = array();
foreach($filter as $entry) {
    $filelist[] = $entry->getFilename();
}

GlobIterator

Итератор GlobIterator выполняет проход по файловой системе также, как и функция glob(). Первый параметр может включать шаблон для имени. Пример демонстрирует использование GlobIterator с тем же результатом, что и ранее.

<?php
$iterator = new GlobIterator("te*");
$filelist = array();
foreach($iterator as $entry) {
    $filelist[] = $entry->getFilename();
}

Заключение

В данном уроке демонстрируется использование различных подходов для достижение одинаковой цели: получение списка файлов и директорий. Следует запомнить следующие ключевые моменты:

  • Функция glob() является встроенным решением, но она недостаточно гибкая.
  • Решение на основе opendir(), readdir(), и closedir() более сложное и требует дополнительной фильтрации, но оно более гибкое.
  • Функция scandir() требует дополнительной фильтрации, но работает без обработки дескриптора.
  • Если вы используете подход ООП, то следует применять библиотеку SPL. Дополнительно вы можете расширить классы своим функционалом.
  • Итератор GlobIterator имеет функцию предварительной фильтрации, а другие используют RegexIterator.

At this moment, PHP itself does not provide a way to get the project’s root directory for sure.

But you can implement a very simple method yourself that will do exactly what you’re looking for.

Solution

Create a new file in your project, let say D:/workspace/MySystem/Code/FilesManager.php (use whatever name and path suit you the best). Then, use the following code:

<?php

class FilesManager
{
  public static function rootDirectory()
  {
    // Change the second parameter to suit your needs
    return dirname(__FILE__, 2);
  }
}

Now you can do this in, let’s say D:/workspace/MySystem/Code/a/b/c/Feature.php:

echo FilesManager::rootDirectory();

And the expected result should be:

"D:/workspace/MySystem"

The output will be the same no matter where your “feature” file is located in the project.

Explanation

dirname is used to return the parent directory of the first parameter. We use the magic constant __FILE__ to give it FilesManager.php‘s path. The second parameter tells how many times to go up in the hierarchy. In this case, we need to do it twice, but it really depends where you put your file in the hierarchy. You can omit the second parameter if you only need to got up once, meaning the file is located in the root. But then, you can return __DIR__ directly instead.

This solution is guaranteed to work, no matter where the root is located on your server. Unless you end up moving the utility class somewhere else in the hierarchy.

Additional note

I’d avoid using DOCUMENT_ROOT for the following reasons (according to this answer):

  • It makes your application dependent on the server.
  • The Apache setup may give an incorrect path to the root directory.

Пути к файлам – тема, которая обычно взрывает мозг новичкам. Но не волнуйся, сейчас мы всё расставим по полочкам.

Чем отличаются пути в PHP и URL

Когда мы смотрим любимый фильм или сериал, мы видим только готовый продукт.

А за кадром существует совсем другой, невидимый для нас мир: стилисты и гримёры, искусственные декорации, наложение спецэффектов и многое другое.

Распыление красного дыма во время съёмок

Процесс съёмок музыкального клипа

В PHP существует такое же разделение. С одной стороны – реальная файловая система с реальными папками и файлами, с другой – URL адреса, которые могут не иметь ничего общего с реальной файловой структурой.

Если ты видишь на каком-нибудь сайте страницу с URL /category/monitors – это совсем не значит, что на сайте есть скрипт /category/monitors/index.php.

Вполне вероятно, что и такой папки там тоже нет, а все URL адреса обрабатываются одним единственным PHP файлом.

И даже если в URL присутствует расширение файла, например /about.html – это тоже не говорит о существовании файла about.html. Может он есть, а может и нет.

Новички часто путают пути к реальным файлам с URL адресами. Сейчас я разберу пару таких ошибок, на примере которых можно будет прочувствовать разницу.

Ошибка №1: Подстановка физического пути в URL

Очень частая ошибка новичков – пытаться подставить в URL адрес ссылку на физический файл, вроде такого:

<a href="D:OpenServerdomainstest.ruarticle.php">Статья</a>

Это неправильно. Браузер не может видеть реальную файловую структуру сервера. Он не видит никаких дисков D, он видит только URL адреса.

Правильная ссылка выглядит так (разницу объясню чуть позже):

<a href="http://test.ru/article.php">Статья</a>
<a href="/article.php">Статья</a>
<a href="article.php">Статья</a>

Ошибка №2: Подключение скриптов по URL

Иногда новички пытаются подключить физический файл по его URL:

require 'http://test.ru/article.php';

Это неправильно. Во-первых, подключится не сам скрипт, а результат его работы. Во-вторых, подключать какие-то файлы через URL вообще идея очень опасная.

Как правильно:

require 'config.php';
require 'D:OpenServerdomainstest.ruconfig.php';
require __DIR__ . '/config.php';

Абсолютный путь в PHP

Абсолютный путь – это полный путь к папке или файлу. Вот пара примеров для разных операционных систем:

  • D:OpenServerdomainstest.ruindex.php – для OpenServer на Windows
  • /var/www/html/test.ru/index.php – для Ubuntu

Как видите, это полный путь от корня диска до конкретного файла или папки. Начинается со слеша или буквы диска (Windows).

Получить абсолютный путь скрипта можно с помощью магической константы __FILE__:

<?php
    echo __FILE__;

Для получения абсолютного пути к папке, в которой находится скрипт, есть магическая константа __DIR__:

<?php
    echo __DIR__;

Как этим пользоваться. Допустим, у нас в корне сайта лежат файлы index.php и config.php и мы хотим подключить второй в первый.

Если мы хотим подключить config.php по его абсолютному пути, есть два способа сделать это:

<?php
    # Указать путь вручную
    require 'D:OpenServerdomainstest.rucpnfig.php';

    # Подставить путь к текущей папке и добавить имя файла вручную
    require __DIR__ . '/config.php';

Поскольку константа __DIR__ не добавляет слеш после последней папки, мы указываем его вручную.

Относительный путь в PHP

У относительных путей в PHP есть один подвох – они могут строиться относительно не той папки, от которой мы ожидаем.

Дело в том, что когда мы подключаем скрипт по относительному пути require(‘config.php’);, PHP будет искать его по следующему алгоритму:

  • Сначала PHP попытается найти этот файл в папках, указанных в директиве include_path. Посмотреть, что указано в этой директиве конкретно у вас можно с помощью var_dump(get_include_path());, папки разделяются символом ;

    Если мы укажем путь к скрипту в таком виде: require(‘./config.php’);, то этот шаг будет пропущен.

  • Затем PHP попытается найти файл в папке, в которой находится подключающий скрипт (в нашем случае index.php).
  • Далее PHP попытается найти файл в папке текущего рабочего каталога.

    Например, если мы в index.php подключили файл scripts/script.php, а в этом самом script.php уже пытаемся подключить файл по относительному пути, тогда поиск файла произойдёт и в папке scripts тоже.

Два последних пункта – главная проблема относительных путей. Мы не можем быть уверены, что будет подключен именно тот файл, который нам нужен.

Именно по этой причине я призываю тебя отказаться от использования относительных путей в PHP.

Кому-то из практикующих разработчиков эта фраза может не понравиться, но я считаю это единственным разумным решением.

Тем более нет ничего сложного в добавлении константы __DIR__ перед именем скрипта, что автоматически сделает путь абсолютным.

<?php
    # Отказываемся
    require 'config.php';

    # Используем
    require __DIR__ . '/config.php';

Абсолютный путь в URL

Абсолютный путь в URL означает путь от корня сайта. Корень сайта – это папка, которая содержит публичную часть сайта, т.е. доступную извне.

По-умолчанию корень сайта – это сама папка с сайтом, например site.ru, но зачастую её меняют на site.ru/public, чтобы часть скриптов нельзя было запустить по прямой ссылке в браузере.

Есть несколько способов указать абсолютный путь в URL:

  • http://test.ru/article.php – полный путь с протоколом и доменом
  • //test.ru/article.php – полный путь без протокола (берётся из текущей страницы). Содержит два слеша в начале.
  • /article.php – полный путь без протокола и домена (берутся из текущей страницы). Содержит слеш в начале.

Второй и третий варианты удобны тем, что при миграции с http на https и обратно все ссылки автоматически сменят протокол, не нужно будет бегать по всему сайту и менять вручную.

Лично я практически всегда использую третий вариант, кроме случаев, когда нужно указать ссылку на другой поддомен (blog.site.ru, shop.site.ru и т.д.).

Относительный путь в URL

Относительные пути в URL указываются без слеша в начале ссылки, например:

<a href="articles">Ссылка</a>

Относительные пути в URL более предсказуемы, чем в PHP. Но я рекомендую использовать их только там, где это действительно необходимо.

Чаще всего их использование приводит к путанице. И вот пара типичных проблем, с которыми часто сталкиваются новички.

Ошибка №1: относительные пути к стилям, скриптам и другим файлам

Представим, что мы решили подключить стили к нашему сайту:

<link rel="stylesheet" type="text/css" href="style.css">

Разработчик указывает относительный URL style.css и видит, что всё работает. По крайней мере, на главной странице.

Но при переходе по любой ссылке, например /products/15, стили перестают работать.

А причина в том, что относительный путь строится от текущего URL-адреса, а значит в нашем примере он из style.css превратился в /products/15/style.css.

Ошибка №2: Рекурсия в ссылках

При использовании относительных путей есть риск случайно создать на сайте бесконечные ссылки. Вот один из таких способов:

<a href="articles/about">О нас</a>

Для работы данного кода должна быть настроена единая точка входа.

Проблема в том, что при каждом клике по этой ссылке текущий URL будет не перезаписываться, а дополняться, в итоге через несколько кликов мы получим что-то вроде http://test.ru/articles/articles/articles/articles/about.

Текущий и родительский каталоги

Помимо указания конкретных папок, мы также можем добавить в путь указание “перейти на папку выше”, например:

<?php
    include '../config.php';

В коде выше мы подключим файл config.php, который находится не в текущей папке, а в родительской. С абсолютными путями это тоже работает:

<?php
    include __DIR__ . '/../config.php';

И с URL-адресами тоже:

<a href="../article.php">Статья</a>

Также мы можем указать ссылку на текущий каталог, что бывает актуально в некоторых операционных системах:

<?php
    include './config.php';

Для поиска файлов на сервере хорошо подходит функция glob(), которая возвращает список файлов по заданной маске, например:

$files = glob('/tmp/*.jpg');

PHP

В маске можно использовать следующие специальные символы:

* Соответствует нулю или большему количеству любых символов.
? Один любой символ.
[...] Один символ входящий в группу.
[!...] Один символ не входящий в группу.
{...,...} Вхождение подстрок, работает с флагом GLOB_BRACE.
Экранирует следующий символ, кроме случаев, когда используется флаг GLOB_NOESCAPE.

Доступные флаги:

GLOB_MARK Добавляет слеш к каждой возвращаемой директории.
GLOB_NOSORT Возвращает файлы в том виде, в котором они содержатся в директории (без сортировки). Если этот флаг не указан, то имена сортируются по алфавиту.
GLOB_NOCHECK Возвращает шаблон поиска, если с его помощью не был найден ни один файл.
GLOB_NOESCAPE Обратные слеши не экранируют метасимволы.
GLOB_BRACE Раскрывает {a,b,c} для совпадения с «a», «b» или «c».
GLOB_ONLYDIR Возвращает только директории, совпадающие с шаблоном.
GLOB_ERR Останавливается при ошибках чтения (например, директории без права чтения), по умолчанию ошибки игнорируются.

Возможно использовать несколько флагов:

$files = glob('/tmp/*.jpg', GLOB_NOSORT|GLOB_ERR);

PHP

Далее во всех примерах используется папка tmp со следующим содержимым:

Содержимое папки tmp

1

Поиск в директории

Список всех файлов и директорий

$dir = __DIR__ . '/tmp';
 
$files = array();
foreach(glob($dir . '/*') as $file) {
	$files[] = basename($file);	
} 
 
print_r($files);

PHP

Результат:

Array
(
    [0] => 1.svg
    [1] => 2.jpg
    [2] => 22-f.gif
    [3] => 22.svg
    [4] => img.png
    [5] => path
    [6] => prod.png
    [7] => style-1.txt
    [8] => style-2.css
)

Только файлы

$dir = __DIR__ . '/tmp';

$files = array();
foreach(glob($dir . '/*') as $file) {
	if (is_file($file)) {
		$files[] = basename($file);	
	}
} 

print_r($files);

PHP

Результат:

Array
(
    [0] => 1.svg
    [1] => 2.jpg
    [2] => 22-f.gif
    [3] => 22.svg
    [4] => img.png
    [5] => prod.png
    [6] => style-1.txt
    [7] => style-2.css
)

Только директории

$dir = __DIR__ . '/tmp';

$files = array();
foreach(glob($dir . '/*') as $file) {
	if (is_dir($file)) {
		$files[] = basename($file);	
	}
} 

print_r($files);

PHP

Результат:

Array
(
    [0] => path
)

Поиск по расширению

$dir = __DIR__ . '/tmp';

$files = array();
foreach(glob($dir . '/*.svg') as $file) {
	$files[] = basename($file);	
} 

print_r($files);

PHP

Результат:

Array
(
    [0] => 1.svg
    [1] => 22.svg
)

Поиск по нескольким расширениям

$dir = __DIR__ . '/tmp';

$files = array();
foreach(glob($dir . '/*.{jpg,png}', GLOB_BRACE) as $file) {
	$files[] = basename($file);	
} 

print_r($files);

PHP

Результат:

Array
(
    [0] => 2.jpg
    [1] => img.png
    [2] => prod.png
)

Поиск по имени файла

Например, в названия файлов начинаются со слова «style»:

$dir = __DIR__ . '/tmp';

$files = array();
foreach(glob($dir . '/style*.*') as $file) {
	$files[] = basename($file);	
} 

print_r($files);

PHP

Результат:

Array
(
    [0] => style-1.txt
    [1] => style-2.css
)

Или начинаются с цифр:

$dir = __DIR__ . '/tmp';

$files = array();
foreach(glob($dir . '/[0-9]*.*', GLOB_BRACE) as $obj) {
	$files[] = basename($obj);	
} 

print_r($files);

PHP

Результат:

Array
(
    [0] => 1.svg
    [1] => 2.jpg
    [2] => 22-f.gif
    [3] => 22.svg
)

2

Поиск в дереве

Поиск по всем подкатегориям более сложный т.к. требует применение рекурсии.

Список всех файлов

function glob_tree_files($path, $_base_path = null)
{
	if (is_null($_base_path)) {
		$_base_path = '';
	} else {
		$_base_path .= basename($path) . '/';
	}

	$out = array();
	foreach(glob($path . '/*') as $file) {
		if (is_dir($file)) {
			$out = array_merge($out, glob_tree_files($file, $_base_path));
		} else {
			$out[] = $_base_path . basename($file);
		}
	}
 
	return $out;
}

$dir = __DIR__ . '/tmp';
$files = glob_tree_files($dir);
print_r($files);

PHP

Результат:

Array
(
    [0] => 1.svg
    [1] => 2.jpg
    [2] => 22-f.gif
    [3] => 22.svg
    [4] => img.png
    [5] => path/icon-rew.png
    [6] => path/marker.png
    [7] => path/psd/1.psd
    [8] => path/psd/2.psd
    [9] => path/psd/index.psd
    [10] => path/sh-1.png
    [11] => path/title-1.png
    [12] => prod.png
    [13] => style-1.txt
    [14] => style-2.css
)

Список всех директорий

function glob_tree_dirs($path, $_base_path = null)
{
	if (is_null($_base_path)) {
		$_base_path = '';
	} else {
		$_base_path .= basename($path) . '/';
	}

	$out = array();
	foreach(glob($path . '/*', GLOB_ONLYDIR) as $file) {
		if (is_dir($file)) {
			$out[] = $_base_path . basename($file);
			$out = array_merge($out, glob_tree_dirs($file, $_base_path));
		}
	}
 
	return $out;
}

$dir = __DIR__ . '/tmp';
$files = glob_tree_dirs($dir);
print_r($files);

PHP

Результат:

Array
(
    [0] => path
    [1] => path/psd
)

Поиск по имени/расширению

function glob_tree_search($path, $pattern, $_base_path = null)
{
	if (is_null($_base_path)) {
		$_base_path = '';
	} else {
		$_base_path .= basename($path) . '/';
	}

	$out = array();
	foreach(glob($path . '/' . $pattern, GLOB_BRACE) as $file) {
		$out[] = $_base_path . basename($file);
	}
	
	foreach(glob($path . '/*', GLOB_ONLYDIR) as $file) {
		$out = array_merge($out, glob_tree_search($file, $pattern, $_base_path));
	}

	return $out;
}

$path = __DIR__ . '/tmp';
$files = glob_tree_search($path, '*.{jpg,png}');
print_r($files);

PHP

Результат:

Array
(
    [0] => 2.jpg
    [1] => img.png
    [2] => prod.png
    [3] => path/icon-rew.png
    [4] => path/marker.png
    [5] => path/sh-1.png
    [6] => path/title-1.png
)

Чтобы в результирующих списках выводились полные пути к файлам, достаточно удалить функцию basename().

Другие публикации

Использование API Яндекс Диска на PHP

Можно найти множество применений Яндекс Диска на своем сайте, например, хранение бекапов и отчетов, обновление прайсов,…

Работа с FTP в PHP

Протокол FTP – предназначен для передачи файлов на удаленный хост. В PHP функции для работы с FTP как правило всегда доступны и не требуется установка дополнительного расширения.

Загрузка файлов на сервер PHP

В статье приведен пример формы и php-скрипта для безопасной загрузки файлов на сервер, возможные ошибки и рекомендации при работе с данной темой.

Пример парсинга html-страницы на phpQuery

phpQuery – это удобный HTML парсер взявший за основу селекторы, фильтры и методы jQuery, которые позволяют…

Поиск похожих текстов в базе данных MySQL + PHP

Один из вариантов поиска похожих статей в базе данных основан на схождении слов в двух текстах.

Список MIME типов

Ниже приведён список MIME-заголовков и расширений файлов.

Добавить комментарий