dir()
(PHP 4, PHP 5, PHP 7)
返回一个 Directory 类实例
说明
dir(string $directory[,resource $context]):Directory
以面向对象的方式访问目录。打开$directory参数指定的目录。
参数
- $directory
被打开的目录
- $context
Note:在 PHP 5.0.0中增加了对上下文(Context)的支持。有关上下文(Context)的说明参见Streams。
返回值
成功的话,返回一个Directory类实例,参数错误的情况下返回NULL
,其它错误情况返回FALSE
。
范例
Example #1dir()示例
请特别注意下面示例中Directory::read()函数返回值的判断方式。我们严格测试(值相等,并且类型相同,请参考比较运算符)返回值等于FALSE
,因为有些情况下,目录名可能"等于"FALSE
,导致跳出循环。
<?php $d = dir("/etc/php5"); echo "Handle: " . $d->handle . "\n"; echo "Path: " . $d->path . "\n"; while (false !== ($entry = $d->read())) { echo $entry."\n"; } $d->close(); ?>
以上例程的输出类似于:
Handle: Resource id #2 Path: /etc/php5 . .. apache cgi cli
注释
Note:
目录条目返回的顺序依赖于系统。
Regarding samuel's comment about the dir() function not supporting Unicode properly, it's all in the encoding. The function does NOT internally change Unicode characters into question marks (?), as I was first led to believe. If you simply try to output them in UTF-8, they'll show up just right.
This one's pretty nice. After getting frustrated for hunting down .jpg files in my massive music collection (PHP would run out of memory), I thought there should be a preg_ls function. function preg_ls ($path=".", $rec=false, $pat="/.*/") { // it's going to be used repeatedly, ensure we compile it for speed. $pat=preg_replace("|(/.*/[^S]*)|s", "\\1S", $pat); //Remove trailing slashes from path while (substr($path,-1,1)=="/") $path=substr($path,0,-1); //also, make sure that $path is a directory and repair any screwups if (!is_dir($path)) $path=dirname($path); //assert either truth or falsehoold of $rec, allow no scalars to mean truth if ($rec!==true) $rec=false; //get a directory handle $d=dir($path); //initialise the output array $ret=Array(); //loop, reading until there's no more to read while (false!==($e=$d->read())) { //Ignore parent- and self-links if (($e==".")||($e=="..")) continue; //If we're working recursively and it's a directory, grab and merge if ($rec && is_dir($path."/".$e)) { $ret=array_merge($ret,preg_ls($path."/".$e,$rec,$pat)); continue; } //If it don't match, exclude it if (!preg_match($pat,$e)) continue; //In all other cases, add it to the output array $ret[]=$path."/".$e; } //finally, return the array return $ret; } Not bad for a mere 18 lines, don't you think? Example use: foreach (preg_ls("/etc/X11", true, "/.*\.conf/i") as $file) echo $file."\n"; Output: /etc/X11/xkb/README.config /etc/X11/xorg.conf-vesa /etc/X11/xorg.conf~ /etc/X11/gui.conf /etc/X11/xorg.conf /etc/X11/xorg.conf-fbdev
Here my solution how to do effective recursiv directory listing. Have fun. <?php /** * example of use: */ $d = new RecDir("/etc/",false); echo "Path: " . $d->getRootPath() . "\n"; while (false !== ($entry = $d->read())) { echo $entry."\n"; } $d->close(); class RecDir { protected $currentPath; protected $slash; protected $rootPath; protected $recursiveTree; function __construct($rootPath,$win=false) { switch($win) { case true: $this->slash = '\\'; break; default: $this->slash = '/'; } $this->rootPath = $rootPath; $this->currentPath = $rootPath; $this->recursiveTree = array(dir($this->rootPath)); $this->rewind(); } function __destruct() { $this->close(); } public function close() { while(true === ($d = array_pop($this->recursiveTree))) { $d->close(); } } public function closeChildren() { while(count($this->recursiveTree)>1 && false !== ($d = array_pop($this->recursiveTree))) { $d->close(); return true; } return false; } public function getRootPath() { if(isset($this->rootPath)) { return $this->rootPath; } return false; } public function getCurrentPath() { if(isset($this->currentPath)) { return $this->currentPath; } return false; } public function read() { while(count($this->recursiveTree)>0) { $d = end($this->recursiveTree); if((false !== ($entry = $d->read()))) { if($entry!='.' && $entry!='..') { $path = $d->path.$entry; if(is_file($path)) { return $path; } elseif(is_dir($path.$this->slash)) { $this->currentPath = $path.$this->slash; if($child = @dir($path.$this->slash)) { $this->recursiveTree[] = $child; } } } } else { array_pop($this->recursiveTree)->close(); } } return false; } public function rewind() { $this->closeChildren(); $this->rewindCurrent(); } public function rewindCurrent() { return end($this->recursiveTree)->rewind(); } } ?>
Note that the dir object will use the default encoding for non-unicode programs on Windows with PHP 5.x. So, if you have a file named with characters unsupported by the current default encoding, the dir->read() method will return a wrong entry. <?php /* ** This script is on the same directory than a file named with ** unsupported characters for the current default encoding. */ $d = dir("./"); while(false !== ($e = $d->read())) echo $e . '<br/>'; ?> This will print a "?" for every unsupported characters, and not the right file name. So take care if you check with is_file/is_dir right after enumerating.
The dir Class, from what I can tell, on a Windows box is not a live image of the directory. When the class is instantiated it takes a snapshot of the directory and then the iterator works off that. I may be wrong, but when I run two processes that look to see if a directory exists, and then deletes the dir when some processing takes place. Deletes from one process do not effect the iteration of the second. To get around this I check that the file exists before doing my processing: $d = dir($dataDir); while (false !== ($entry = $d->read())) if ($entry != '..' && $entry != '.' && file_exists("$dataDir\\$entry")) { // do stuff } $d->close(); I run this as a batch process and can activate it multiple times to process the directory listing in parallel. -CF
to get a dir of http://www.example.com/directory <?php function remotedir($dir) { $dir = str_replace(" ", "%20", html_entity_decode($dir)); if (($rh = fopen($dir, 'rb')) === FALSE) { return false; } $i = 0; while (!feof($rh)) { $archivos = fgetss($rh); $directorio[$i++] = trim( substr($archivos,1,strpos($archivos," ",1)) ); } fclose($rh); return $directorio; } ?>
<?php // use $entry[0] != '.' to detect if it's '..' or '.' $d = dir('.'); echo "Pointeur : " . $d->handle . "<br/>\n"; echo "Chemin : " . $d->path . "<br/>\n"; while (false !== ($entry = $d->read())) { if( $entry[0] != '.' ) { echo . "<br/>\n"; } } $d->close() ?>
<?PHP /*Simple, good looking recursive function for printing directories. Just copy/paste and it is ready to go!*/ function printCurrentDirRecursively($originDirectory, $printDistance=0){ // just a little html-styling if($printDistance==0)echo '<div style="color:#35a; font-family:Verdana; font-size:11px;">'; $leftWhiteSpace = ""; for ($i=0; $i < $printDistance; $i++) $leftWhiteSpace = $leftWhiteSpace." "; $CurrentWorkingDirectory = dir($originDirectory); while($entry=$CurrentWorkingDirectory->read()){ if($entry != "." && $entry != ".."){ if(is_dir($originDirectory."\\".$entry)){ echo $leftWhiteSpace."<b>".$entry."</b><br>\n"; printCurrentDirRecursively($originDirectory."\\".$entry, $printDistance+2); } else{ echo $leftWhiteSpace.$entry."<br>\n"; } } } $CurrentWorkingDirectory->close(); if($printDistance==0)echo "</div>"; } //TEST IT! printCurrentDirRecursively(getcwd()); ?>
<?php // best small recurcive dir() // $entry[0]!='.' <=== specifically to protect FTP files with a dot '.' as first carractère // return : // download\file\text\test.txt; // download\video\anime\test.mp4; // download\video\film\test2.mkv; echo rdir('download'); // start dir in \\download function rdir($path='',$k='') { $d = dir($path); while (false !== ($entry = $d->read())) { if($entry[0]!='.') { if(is_dir($path . '\\' . $entry)) { $k = rdir($k,$path . '\\' . $entry); } else { $k .= $path . '\\' . $entry . "; \n" ; } } } $d->close(); return $k; } ?>
With SPL, you could recursively list all of the folders inside the current directory like this: <?php $it = new RecursiveDirectoryIterator('./'); // RecursiveIteratorIterator accepts the following modes: // LEAVES_ONLY = 0 (default) // SELF_FIRST = 1 // CHILD_FIRST = 2 foreach (new RecursiveIteratorIterator($it, 2) as $path) { if ($path->isDir()) { echo "$path\n"; } } ?>
function directoryList($start,$win32=false){ if($win32){ $slash="\\"; }else{ $slash="/"; } $basename = pathinfo($start); $basename = $basename['basename']; $ls=array(); $dir = dir($start); while($item = $dir->read()){ if(is_dir($start.$slash.$item)&& $item!="." && $item!=".."){ $ls[$basename][]=directoryList($start.$slash.$item,$win32); }else{ if($item!="."&&$item!=".."){ $ls[$basename][]=$item; } } } return $ls; } $path = pathinfo(__FILE__); $ls = directoryList($path['dirname'], true);
i've modified the script below to get the leaf folders of any directory (folders with no subfolders). note: this does not return the folder passed in as a parameter, even if it has no subfolders. <?php function get_leaf_dirs($dir) { $array = array(); $d = dir($dir); while (false !== ($entry = $d->read())) { if($entry!='.' && $entry!='..') { $entry = $dir.'/'.$entry; if(is_dir($entry)) { $subdirs = get_leaf_dirs($entry); if ($subdirs) $array = array_merge($array, $subdirs); else $array[] = $entry; } } } $d->close(); return $array; } ?>
IMHO, thats take most effect with smaller number of errors;) function get_leaf_dirs($dir) { $array = array(); $d = @dir($dir); if($d) { while (false !== ($entry = $d->read())) { if($entry!='.' && $entry!='..') { $entry = $dir.'/'.$entry; if(is_dir($entry)) { $subdirs = get_leaf_dirs($entry); if ($subdirs) $array = array_merge($array, $subdirs); else $array[] = $entry; } } } $d->close(); } return $array; }
<?php $i = new RecursiveIteratorIterator(new RecursiveDirectoryIterator('.')); ?> works for me..
Regarding jaqb's post about a correction to the read_dir function, I have one small fix too if people wish to also list the directories inside this directory and read them into the same array. <? function read_dir($dir) { $array = array(); $d = dir($dir); while (false !== ($entry = $d->read())) { if($entry!='.' && $entry!='..') { $entry = $dir.'/'.$entry; if(is_dir($entry)) { $array[] = $entry; $array = array_merge($array, read_dir($entry)); } else { $array[] = $entry; } } } $d->close(); return $array; } ?>
That's the way, I'm storing recursive dirs to an array. <?php public static function getTreeFolders($sRootPath = UPLOAD_PATH_PROJECT, $iDepth = 0) { $iDepth++; $aDirs = array(); $oDir = dir($sRootPath); while(($sDir = $oDir->read()) !== false) { if($sDir != '.' && $sDir != '..' && is_dir($sRootPath.$sDir)) { $aDirs[$iDepth]['sName'][] = $sDir; $aDirs[$iDepth]['aSub'][] = self::getTreeFolders($sRootPath.$sDir.'/',$iDepth); } } $oDir->close(); return empty($aDirs) ? false : $aDirs; } ?>
When creating custom solutions, use predefined PHP constants to shorten your code and improve performances: http://www.php.net/manual/en/reserved.constants.php For example, DIRECTORY_SEPARATOR may replace a function in which you check PHP_OS to set if the directory separator is "/" or "\\". Cheers.
Saw the leaf dirs bit... quick mod: function preg_ls ($path=".", $rec=false, $pat="/.*/") { $pat=preg_replace ("|(/.*/[^S]*)|s", "\\1S", $pat); while (substr ($path,-1,1) =="/") $path=substr ($path,0,-1); if (!is_dir ($path) ) $path=dirname ($path); if ($rec!==true) $rec=false; $d=dir ($path); $ret=Array (); while (false!== ($e=$d->read () ) ) { if ( ($e==".") || ($e=="..") ) continue; if ($rec && is_dir ($path."/".$e) ) { $ret=array_merge ($ret,preg_ls($path."/".$e,$rec,$pat)); continue; } if (!preg_match ($pat,$e) ) continue; $ret[]=$path."/".$e; } return (empty ($ret) && preg_match ($pat,basename($path))) ? Array ($path."/") : $ret; } example: foreach (preg_ls ("/usr/share/fluxbox", true, "/[LT]e[sa]/i") as $file) echo $file."\n"; output: /usr/share/fluxbox/styles/Leaf/ /usr/share/fluxbox/styles/Clean /usr/share/fluxbox/styles/Testing/