XXXV. Filesystem 文件系统函数

需求

无需外部库文件就可以加入本扩展模块的支持。但如要使 PHP 在 Linux 上支持 LFS(大文件)的话,必须有最新的 glibc 并在编译时加入参数:-D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64

安装

本函数库作为 PHP 内核的一部分,不用安装就能使用。

运行时配置

这些函数的行为受 php.ini 的影响。

表格 1. 文件系统和流配置选项

名称默认值可修改范围更新记录
allow_url_fopen"1"PHP_INI_SYSTEMPHP_INI_ALL in PHP <= 4.3.4. PHP 4.0.4 版以后可用。
user_agentNULLPHP_INI_ALLPHP 4.3.0 版以后可用。
default_socket_timeout"60"PHP_INI_ALLPHP 4.3.0 版以后可用。
from""PHP_INI_ALL 
auto_detect_line_endings"0"PHP_INI_ALLPHP 4.3.0 版以后可用。

以下是配置选项的简要解释。

allow_url_fopen boolean

本选项激活了 URL 形式的 fopen 封装协议使得可以访问 URL 对象例如文件。默认的封装协议提供用 ftp 和 http 协议来访问远程文件,一些扩展库例如 zlib 可能会注册更多的封装协议。

注: 出于安全性考虑,这个选项只能通过 php.ini 中设置。

注: 本选项是版本 4.0.3 发行后立即引进的。4.0.3 以及以前的版本只能在编译时通过配置项 --disable-url-fopen-wrapper 来取消此特性。

警告

Windows 版在 PHP 4.3.0 之前,下列函数不支持远程文件访问:include()include_once(), require()require_once()参考 LV, Image 图像函数 扩展库中的 imagecreatefromXXX 函数。

user_agent string

定义 PHP 发送的 User-Agent。

default_socket_timeout integer

基于 socket 的流的默认超时时间(秒)。

注: 本配置参数是 PHP 4.3.0 引进的。

from string

定义匿名 ftp 的密码(email 地址)。

auto_detect_line_endings boolean

当设为 On 时,PHP 将检查通过 fgets()file() 取得的数据中的行结束符号是符合 Unix,MS-Dos,还是 Macintosh 的习惯。

这使得 PHP 可以和 Macintosh 系统交互操作,但是默认值是 Off,因为在检测第一行的 EOL 习惯时会有很小的性能损失,而且在 Unix 系统下使用回车符号作为项目分隔符的人们会遭遇向下不兼容的行为。

注: 本配置参数是 PHP 4.3.0 引进的。

预定义常量

以下常量由本扩展模块定义,因此只有在本扩展模块被编译到 PHP 中,或者在运行时被动态加载后才有效。

GLOB_BRACE (integer)

GLOB_ONLYDIR (integer)

GLOB_MARK (integer)

GLOB_NOSORT (integer)

GLOB_NOCHECK (integer)

GLOB_NOESCAPE (integer)

PATHINFO_DIRNAME (integer)

PATHINFO_BASENAME (integer)

PATHINFO_EXTENSION (integer)

FILE_USE_INCLUDE_PATH (integer)

FILE_APPEND (integer)

FILE_IGNORE_NEW_LINES (integer)

FILE_SKIP_EMPTY_LINES (integer)

参见

相关函数参见目录函数程序执行函数

有关可以用于远程文件的各种 URL 封装协议的说明列表,参见附录 L

目录
basename -- 返回路径中的文件名部分
chgrp -- 改变文件所属的组
chmod -- 改变文件模式
chown -- 改变文件的所有者
clearstatcache -- 清除文件状态缓存
copy -- 拷贝文件
delete -- 参见 unlink()unset()
dirname -- 返回路径中的目录部分
disk_free_space -- 返回目录中的可用空间
disk_total_space -- 返回一个目录的磁盘总大小
diskfreespace -- disk_free_space()的别名
fclose -- 关闭一个已打开的文件指针
feof -- 测试文件指针是否到了文件结束的位置
fflush -- 将缓冲内容输出到文件
fgetc -- 从文件指针中读取字符
fgetcsv -- 从文件指针中读入一行并解析 CSV 字段
fgets -- 从文件指针中读取一行
fgetss -- 从文件指针中读取一行并过滤掉 HTML 标记
file_exists -- 检查文件或目录是否存在
file_get_contents -- 将整个文件读入一个字符串
file_put_contents -- 将一个字符串写入文件
file -- 把整个文件读入一个数组中
fileatime -- 取得文件的上次访问时间
filectime -- 取得文件的 inode 修改时间
filegroup -- 取得文件的组
fileinode -- 取得文件的 inode
filemtime -- 取得文件修改时间
fileowner -- 取得文件的所有者
fileperms -- 取得文件的权限
filesize -- 取得文件大小
filetype -- 取得文件类型
flock -- 轻便的咨询文件锁定
fnmatch -- 用模式匹配文件名
fopen -- 打开文件或者 URL
fpassthru -- 输出文件指针处的所有剩余数据
fputcsv -- 将行格式化为 CSV 并写入文件指针
fputs -- fwrite()的别名
fread -- 读取文件(可安全用于二进制文件)
fscanf -- 从文件中格式化输入
fseek -- 在文件指针中定位
fstat -- 通过已打开的文件指针取得文件信息
ftell -- 返回文件指针读/写的位置
ftruncate -- 将文件截断到给定的长度
fwrite -- 写入文件(可安全用于二进制文件)
glob -- 寻找与模式匹配的文件路径
is_dir -- 判断给定文件名是否是一个目录
is_executable -- 判断给定文件名是否可执行
is_file -- 判断给定文件名是否为一个正常的文件
is_link -- 判断给定文件名是否为一个符号连接
is_readable -- 判断给定文件名是否可读
is_uploaded_file -- 判断文件是否是通过 HTTP POST 上传的
is_writable -- 判断给定的文件名是否可写
is_writeable -- is_writable()的别名
link -- 建立一个硬连接
linkinfo -- 获取一个连接的信息
lstat -- 给出一个文件或符号连接的信息
mkdir -- 新建目录
move_uploaded_file -- 将上传的文件移动到新位置
parse_ini_file -- 解析一个配置文件
pathinfo -- 返回文件路径的信息
pclose -- 关闭进程文件指针
popen -- 打开进程文件指针
readfile -- 输出一个文件
readlink -- 返回符号连接指向的目标
realpath -- 返回规范化的绝对路径名
rename -- 重命名一个文件或目录
rewind -- 倒回文件指针的位置
rmdir -- 删除目录
set_file_buffer -- stream_set_write_buffer()的别名
stat -- 给出文件的信息
symlink -- 建立符号连接
tempnam -- 建立一个具有唯一文件名的文件
tmpfile -- 建立一个临时文件
touch -- 设定文件的访问和修改时间
umask -- 改变当前的 umask
unlink -- 删除文件

add a note add a note User Contributed Notes
16-Aug-2006 02:11
Note that file functions automatically handle HTTP response codes, and in case of "Location" header you will get content of new reloacated page:

<?php

$url
= "http://nu-imaging.com/catalog/"; // here we have redirect to /catalog/1/
$fp = fopen($fp);
echo
fread($fp, 1024); // we get content of /catalog/1/
fclose($fp);

?>
hello at nospam4methanks dot com
04-Aug-2006 04:31
<?php

// faster "fcountext" function

function extCount ($dir) {

  if (
is_dir ($dir)) {

  
$g = glob ($dir . '/*.*');
   foreach (
$g as $v) {
    
$this = pathinfo($v);
    
$e[] = $this['extension'];
   }

  
// return extension count
  
return count(array_unique($e));
  
// alternately, you could return an array of the extensions found -
  
return array_values(array_unique($e));

  }

 
// otherwise
 
return false;

}

?>
tim at astolat dot it
04-Jun-2006 11:16
Sorry, important typo in previous post:
$dodgychars = "[^0-9a-zA-z()_-]";
should be (notice captial Z)
$dodgychars = "[^0-9a-zA-Z()_-]";
tim at astolat dot it
04-Jun-2006 02:03
Little function to sanitize a user supplied file name, and optionally force a file extension:

function sanitize_filename($filename, $forceextension="")
{
  /*
  1. Remove leading and trailing dots
  2. Remove dodgy characters from filename, including spaces and dots except last.
  3. Force extension if specified
  */
 
  $defaultfilename = "none";
  $dodgychars = "[^0-9a-zA-z()_-]"; // allow only alphanumeric, underscore, parentheses and hyphen
 
  $filename = preg_replace("/^[.]*/","",$filename); // lose any leading dots
  $filename = preg_replace("/[.]*$/","",$filename); // lose any trailing dots
  $filename = $filename?$filename:$defaultfilename; // if filename is blank, provide default

  $lastdotpos=strrpos($filename, "."); // save last dot position
  $filename = preg_replace("/$dodgychars/","_",$filename); // replace dodgy characters
  $afterdot = "";
  if ($lastdotpos !== false) { // Split into name and extension, if any.
   $beforedot = substr($filename, 0, $lastdotpos);
   if ($lastdotpos < (strlen($filename) - 1))
     $afterdot = substr($filename, $lastdotpos + 1);
  }
  else // no extension
   $beforedot = $filename;

  if ($forceextension)
   $filename = $beforedot . "." . $forceextension;
  elseif ($afterdot)
   $filename = $beforedot . "." . $afterdot;
  else
   $filename = $beforedot;

  return $filename;
}
echo sanitize_filename("..file<>@**()name.ddd.badextension", "extension")."\n";
echo sanitize_filename("..file<>@**()name.extension.ddd")."\n";
echo sanitize_filename("...", "extension")."\n";
echo sanitize_filename("...")."\n";
echo sanitize_filename("filename")."\n";
echo sanitize_filename("filename", "extension")."\n";
echo sanitize_filename(".xyz...xxx..", "extension")."\n";
echo sanitize_filename(".xyz...xxx..", "")."\n";

produces

file_____()name_ddd.extension
file_____()name_extension.ddd
none.extension
none
filename
filename.extension
xyz__.extension
xyz__.xxx
codatrix at yahoo dot com
06-Apr-2006 12:58
The following underlisted function called "fcountext" is a function I wrote, for counting the number of file extensions within a given directory. Hope you find it satisfactory as there is no alternative solution via PHP's core filesystem's functions for counting the number of file extensions within a given directory.

<?php

  
/*
   * Author: ORUNTA C. N.
   * Email: codatrix@yahoo.com
   *
   * Name: fcountext()
   * Function: To count the number of file extensions within a given directory.
   * Argument(s): $dir, i.e. directory path - for e.g. 'htdocs/yahoo/'
   * Return-type: Integer
   */
  
  
function fcountext($dir)
   {
      
//Authenticate - if directory?
      
if(is_dir($dir)) {
          
          
//Open and establish directory handle/pointer
          
$dp = opendir($dir);
          
          
//Create extensible array
          
$afile = array();
          
          
//Set initializer array;
          
$i = 0;
          
          
//Read contents of directory into array $afile, thus:
          
while($content = readdir($dp)) {
              
              
//Concatenate filepath
              
$filepath = $dir.$content;
              
              
//Confirm any initial value within array...
              
if(!count($afile)) {
              
                  
//Authenticate - if file?
                  
if(is_file($filepath)) {
                  
                      
//Get extension using PHP's 'pathinfo'
                      
$pathinfo = pathinfo($filepath);
                      
                      
//Store into the array
                      
$afile[$i] = $pathinfo['extension'];
                   }
                  
               } else {
              
                  
//Authenticate - if file?
                  
if(is_file($filepath)) {
                  
                      
//Get extension using PHP's 'pathinfo'
                      
$pathinfo = pathinfo($filepath);
                      
                      
//Count array into $len
                      
$len = count($afile);
                      
                      
//Compare $pathinfo['extension'] with elements of array $afile
                      
for($i=0; $i<$len; $i++) {
                           if(
$afile[$i] != $pathinfo['extension']) $stat = 'T';
                           else {
                              
$stat = 'F';
                               break;
                           }
                       }
                      
                      
//If Status is true, then store into array
                      
if($stat == 'T') {
                          
$afile[$len] = $pathinfo['extension'];
                       }
                   }
               }
           }
      
          
//Count total no. of different extensions.
          
$extnum = count($afile);
          
          
//Return no. of ext.
          
return $extnum;
          
          
//Close directory
          
closedir($dp);
      
       } else {
      
          
//Return no Value
          
return 'Invalid Directory';
       }
   }

?>

The above function can be incorporated as you deem fit into a class situation to be accessed publicly.
wsuttonjr at hyponiqs dot com
15-Oct-2005 03:26
Here's a little function I wrote that I thought someone might find useful.  Ever notice those fancy breadcrumbed headings on Web sites such as Macromedia.com?  Ever say, "I want one, but I don't want to write the HTML in each time?"  Well, here you go.  This does that based on your directory structure.

Do note, however, that it only works if you use files and folders that are named like:
/my-folder/my-file.php
/my_folder/my_file.php
/My_Folder/My_File.php
/My-Folder/My-File.php
...and the variable <i>$path</i> must be similarly formatted.  A preceding forward slash (/) must be on that <i>$path</i>.  $_SERVER['PHP_SELF'], $_SERVER['DOCUMENT_ROOT'], and other $_SERVER variables always add that.

When I have some free time (sooner rather than later), I'm going to write a more in-depth script for this.  I just needed something simple for now.  You can expect a PHP 4 and PHP 5 script class dedicated to this one idea.  For now, this simple function does the trick.  Anyway, here you go:

<?php
function breadCrumbs($path)
{
  
/**
     *  What it does is split a path string into its two base objects -- a
     *  file name and a directory name.
     * 
     *  After that, it then splits the directory into an array of sub-
     *  directories and adds them to a stack of links with keys of 'name'
     *  (directory name) and 'href' (hyperlink reference path).
     *
     *  Each item in this indexed associative array is then stacked again
     *  into an array of actual hyperlinks (i.e. <a href="Blah/Blah2.php">)
     *  split by a tree notification ( > ) of sorts.
     *
     *  Finally, the entire string, which may look like:
     *  <a href="#">Services</a> > <a href="#">Web</a>
     *  has the current file being viewed added to it.  That finished string
     *  is then returned to the calling script/function for display to the
     *  browser.
     *
     *  The returned HTML may look like:
     *  <a href="#">Services</a> > <a href="#">Web</a> > Home
     */
  
   // set the index file and name for each directory
  
$dirIndex = 'Home.php';
  
$dirIndexName = 'Home';
  
  
// split $path into basename and dirname
  
$file = basename($path, '.php');
  
$dir  = dirname($path);
  
  
// change all backslashes to forward slashes
  
$dir  = str_replace('\\', '/', $dir);
  
  
// remove preceding forward slash (/)
  
$dir  = substr($dir, 1, strlen($dir));
  
  
// ** ADDED **
   // add a trailing '/ ' to add a blank folder name - will be removed
   // NOTE: This is to ensure that an array IS created
  
$dir .= '/ ';
  
  
// split dirname into an array
  
$dirs = preg_split('@\/@', $dir);
  
  
// get the last array index from $dirs
  
$lastIndex = count($dirs) - 1;
  
  
// remove last $dirs if empty
  
if ($dirs[$lastIndex] == '' || is_null($dirs[$lastIndex])) {
       unset(
$dirs[$lastIndex]);
   }
  
  
// remove empty dir
  
if ($dirs[$lastIndex] == ' ') {
       unset(
$dirs[$lastIndex]);
   }
  
  
// get an accurate directory count
  
$dirCount = count($dirs);
  
  
// create $link and $links variables
  
$link  = '/';
  
$links = array();
  
  
// stack each directory into a link and dirname
  
for ($i = 0; $i < count($dirs); $i++) {
      
// change $dirs[$i] into capitalized word(s) and add spaces (for
       // grammatical correctness)
      
$link .= ucwords(preg_replace('@(-|_)@', ' ', $dirs[$i])) . '/';
      
      
$links[$i]['href'] = $link . $dirIndex;
      
$links[$i]['name'] = ucwords($dirs[$i]);
   }
  
  
// instanciate $breadcrumbs array
  
$breadcrumbs = array();
  
  
// stack $links into HTML-equivalent links
  
for ($i = 0; $i < count($links); $i++) {
      
$breadcrumbs[$i] = '<a href="' . $links[$i]['href'] . '">' . $links[$i]['name'] . '</a>';
   }
  
  
// see if links are needed or if a parent document
  
if ($dirs[0] == '' || empty($dirs[0])) {
      
// it is not so use just the file name
      
$breadcrumb = $file;
   } else {
      
// it is so compile the links into a breadcrumb string
      
$breadcrumb  = '<a href="/' . $dirIndex . '">' . $dirIndexName . '</a> &#8212; ' . join($breadcrumbs, ' > ');
      
$breadcrumb .= ' > ' . $file;
   }
  
  
// return compiled breadcrumb string
  
return $breadcrumb;
}
?>
Ciprian Danea, cipriandanea at yahoo dot com
28-Jul-2005 10:28
Here is a little script that will enumerate a directory (given as a string) recursively.

The main working function is get_dir($path,$max_depth='')
Optional aguments:
$max_depth : obvious it'll limit the recursive depth.
Not so optional:
$l=0 : the first call must omit this, or explicitly set it to 0, since it represents the starting depth
$total='': the current total filesize; it should also be left alone, although it autosets itself to 0 on the first call;

In order to do something with the files other than just echoing them, simply replace the echo functions with your own;

Thanks to:
dave at birko dot cjb dot net (for the nice filesize function)
vbwebprofi at gmx dot de  (for the fileperms function)

function get_dir($path,$max_depth='',$l=0,$total=''){
   if(!is_dir($path))return;
   $path=substr($path,-1)!="/"?$path."/":$path;
   if(!$l){
       echo "\nEnumerating directory $path :\n\n";
       $total=0;
   }
   if($max_depth==='' || ($max_depth>$l && is_int($max_depth))) $test_depth=true;
   else $test_depth=false;
   $pre="";
   $c=$l;
   while($c--)$pre.="\t";
   $dir=opendir($path);
   while($f=readdir($dir)){
       if($f=="."||$f=="..")continue;
       $file=$path.$f;
       $size="";
       if(is_file($file)||!is_dir($file)){
           $s=filesize($file);
           $total+=$s;
           $size="[ ".fsize($s)." ]";
           }
       else $f.="/";
       while(strlen($size)<16)
           $size=" ".$size;
       echo "\n".get_permissions(fileperms($file)).$size.$pre."\t".$f;
       if(is_dir($file) && $test_depth)
           $total=get_dir($file,$max_depth,$l+1,$total);
   }
   if(!$l)
       echo "\n\nTotal size: ".fsize($total);
   return $total;
}

function get_permissions($fperms) {
   $out;
   if($fperms & 0x1000)    // FIFO pipe
     $out = 'p';
   elseif($fperms & 0x2000) // Character outecial
     $out = 'c';
   elseif($fperms & 0x3000) // Socket [ original value 0xD000, wrong for linux, but this is also registering as a directory... ant ideas?]
     $out = 's';
   elseif($fperms & 0x4000) // Directory
     $out = 'd';
   elseif($fperms & 0x6000) // Block outecial
     $out = 'b';
   elseif($fperms & 0x8000) // Regular
     $out = '-';
   elseif($fperms & 0xA000) // Symbolic Link
     $out = 'l';
   else                        // UNKNOWN
     $out = 'u';
   // owner
   $out .= (($fperms & 0x0100) ? 'r' : '-') .
         (($fperms & 0x0080) ? 'w' : '-') .
         (($fperms & 0x0040) ? (($fperms & 0x0800) ? 's' : 'x' ) :
                                   (($fperms & 0x0800) ? 'S' : '-'));
   // group
   $out .= (($fperms & 0x0020) ? 'r' : '-') .
         (($fperms & 0x0010) ? 'w' : '-') .
         (($fperms & 0x0008) ? (($fperms & 0x0400) ? 's' : 'x' ) :
                                   (($fperms & 0x0400) ? 'S' : '-'));
   // world
   $out .= (($fperms & 0x0004) ? 'r' : '-') .
           (($fperms & 0x0002) ? 'w' : '-') .
           (($fperms & 0x0001) ? (($fperms & 0x0200) ? 't' : 'x' ) :
                                 (($fperms & 0x0200) ? 'T' : '-'));
   return $out;
 }

function fsize($size) {
       $a = array("B", "KB", "MB", "GB", "TB", "PB");
       $pos = 0;
       while ($size >= 1024) {
               $size /= 1024;
               $pos++;
       }
       return round($size,2)." ".$a[$pos];
}

//usage example
get_dir("/tmp/");    // full depth
get_dir("/tmp/",4); //maximum depth set to 4
28-May-2005 03:17
Again, please note, if you ask a question, report a bug, or request a feature, your note will be deleted.) Again, please note, if you ask a question, report a bug, or request a feature, your note will be deleted.) Again, please note, if you ask a question, report a bug, or request a feature, your note will be deleted.) Again, please note, if you ask a question, report a bug, or request a feature, your note will be deleted.) Again, please note, if you ask a question, report a bug, or request a feature, your note will be deleted.) Again, please note, if you ask a question, report a bug, or request a feature, your note will be deleted.) Again, please note, if you ask a question, report a bug, or request a feature, your note will be deleted.) Again, please note, if you ask a question, report a bug, or request a feature, your note will be deleted.)
hans at lintoo dot dk
23-Mar-2005 12:41
PHP5 Object File

I couldn't find a File Object in PHP5, so I decided to create one myself.

When a function that needs a path requires the file it will output a temp file and return the path to it, so that ie. parse_ini_file will work with the file object using:

<?php
parse_ini_file
($myFile->requireFilePath());
?>

It is avaible for download at:
http://www.lintoo.dk/public/dbase_and_file_class.zip

Hans Duedal
tunnelareaten at gmail dot com
26-Feb-2005 12:27
I made this function to search and/or display files by extension or for a string occurance in the filename. Any comments or enhancements are welcome offcourse. I'll update this function soon.

usage: list_files([string], [string], [int 1 | 0], [int 1 | 0]);

search for extension: list_files([string], [string], [0], [int 1 | 0]);
returns array: $myArray = list_files([string], [string], [0], [0]);
echo result: list_files([string], [string], [0], [1]);

search for string occurance: list_files([string], [string], [1], [int 1 | 0]);
returns array: $myArray = list_files([string], [string], [1], [0]);
echo result: list_files([string], [string], [1], [1]);

<?php

function list_files($directory, $stringSearch, $searchHandler, $outputHandler) {
 
$errorHandler = false;
 
$result = array();
 if (!
$directoryHandler = @opendir ($directory)) {
  echo (
"<pre>\nerror: directory \"$directory\" doesn't exist!\n</pre>\n");
 return
$errorHandler = true;
 }
 if (
$searchHandler === 0) {
  while (
false !== ($fileName = @readdir ($directoryHandler))) {
   if(@
substr ($fileName, - @strlen ($stringSearch)) === $stringSearch) {
   @
array_push ($result, $fileName);
   }
  }
 }
 if (
$searchHandler === 1) {
  while(
false !== ($fileName = @readdir ($directoryHandler))) {
   if(@
substr_count ($fileName, $stringSearch) > 0) {
   @
array_push ($result, $fileName);
   }
  }
 }
 if ((
$errorHandler === true) &&  (@count ($result) === 0)) {
  echo (
"<pre>\nerror: no filetype \"$fileExtension\" found!\n</pre>\n");
 }
 else {
 
sort ($result);
  if (
$outputHandler === 0) {
   return
$result;
  }
  if (
$outputHandler === 1) {
   echo (
"<pre>\n");
  
print_r ($result);
   echo (
"</pre>\n");
  }
 }
}

?>
ben at nullcreations dot net
14-Nov-2004 06:29
Directed at: fankounter at libero dot it

Erm, I think you missed glob() which does what you're doing, only much more efficiently.

see: http://php.net/glob
fankounter at libero dot it
05-Nov-2004 10:31
// ls(dir,pattern) return file list in "dir" folder matching "pattern"
// ls("path","module.php?") search into "path" folder for module.php3, module.php4, ...
// ls("images/","*.jpg") search into "images" folder for JPG images

function ls($__dir="./",$__pattern="*.*")
{
 settype($__dir,"string");
 settype($__pattern,"string");

 $__ls=array();
 $__regexp=preg_quote($__pattern,"/");
 $__regexp=preg_replace("/[\\x5C][\x2A]/",".*",$__regexp