This article introduces to you a detailed introduction to the PHP file system. It has certain reference value. Friends in need can refer to it.
Today we will start a new journey of exploration, go deep into the PHP file system, and systematically learn and master the basic use of the PHP file system.
I believe that in the daily research and development process, everyone will inevitably need to get entangled with various documents. For example, open the.envfile and read the configuration information from it, write the error information in the project to the log file, or get the creation time of the image, etc. When dealing with these functions, we all need to use the PHP file system interface.
The following is an outline of the topics covered in this article:
1 What is a file system
2 In-depth PHP file system
Three object-oriented directory traversal
Four PHP file system mind map
This article is relatively It is long and takes about 20 minutes, so be prepared for battle!
Before we begin, we first need to clarify the problem area we are studying, understand what a file system is, and what we are studying.
In computers, the file system (file system or filesystem) is used to manage how data is stored and retrieved. - Wikipedia
Simply put, it is how we should manage our directories (folders) and files. Usually, we store files with similar attributes in the same directory for subsequent search. This common operation involves directories and files.
For software engineers, a very typical usage scenario is to store the files of modules such as controllers, views, and models in different directory structures when developingMVCprojects. Convenient management.
In any case, we divide files and directories according to different characteristics to solve the problem of file storage and search.
After having these understandings, we should naturally think of the object of study of the PHP file system (or file system) we are currently studying. A simple summary is:
Directory (folder)
File
In other words, the PHP file system function processing we explain in this article is basically It revolves around directories and files.
The PHP file system provides more than 80 available file system functions built-in. Due to the large number of powerful functions, naturally this article cannot explain all the system functions one by one. Firstly, time is too short; secondly, we don’t have that much energy to master them all in a short period of time.
Despite this, you don’t have to be discouraged. This article will use limited time and energy to study the following common topics in file processing:
How to obtain the metadata
How to obtain the MIME type of the file
Operation processing of files and directories
Permission management of files and directories
In addition, as a supplementary note, the PHP standard function library not only provides us with process-oriented file system processing functions. At the same time, it also encapsulates the object-oriented interface and iterator interface for common directory and file operations for everyone to use:
SplFileInfo
finfo
DirectoryIterator
RecursiveDirectoryIterator
creation time,file name,file sizeorfile permissions, etc. This type of data that can indicate the basic characteristics of the file is "meta data."
2.1.2 Commonly used metadata acquisition In this section, we will learn some file metadata functions that are often needed to obtain, including:// 文件路径请求改成你自己的文件路径 $filename = "f://filesystem/test.txt"; // 面向过程: 获取文件时间 $modifyTimestamp = filemtime($filename); // 面向对象 $file = new SplFileInfo($filename); $modifyTimestamp = $file->getMTime();Copy after login
可以使用函数 fileatile($filename) 或 SplFileInfo::getATime() 方法,来获取文件的最后被访问时间戳。
// 文件路径请求改成你自己的文件路径 $filename = "f://filesystem/test.txt"; // 面向过程: 获取文件时间 $accessTimestamp = fileatime($filename); // 面向对象 $file = new SplFileInfo($filename); $accessTimestamp = $file->getATime();
除了filemtile和fileatime之外,还有filectime来获取文件的 inode 修改时间(可认为是创建时间)。
有关时间的函数常用的就这些,为了方便记住,我们来看看它们是如何命名的:
2.1 面向过程 file 前缀,面向对象 get 前缀
2.2 a: access(访问);m:modify(修改);c:create(创建)
2.3 time 后缀
2.4 fileatime,SplFileInfo::getATime;filemtime,SplFileInfo::getMTime;filectime,SplFileInfo::getCTime。
是不是很简单呢!
注意,使用 filectime 时,对于 Windows 系统会获取创建时间,但对于类 Unix 系统是修改时间,因为在类 Unix 系统中多数文件系统并没有创建时间的概念。具体说明可以看 PHP: how can I get file creation date?。
3 获取文件的路径信息
除了时间这些元数据,另一个经常遇到的情况是获取文件的路径信息,包括:
3.1 目录信息
获取目录信息我们可以使用pathinfo($filename, PATHINFO_DIRNAME)](http://php.net/manual/zh/function.pathinfo.php)**、**[dirname($filename)和SplFileInfo::getPath()
比如下面给出的文件:
$filename = 'F:\Program Files\SSH Communications Security\SSH Secure Shell\Output.txt';
将会获取到F:\Program Files\SSH Communications Security\SSH Secure Shell这部分目录信息。
3.2 文件名信息
这里我们所有的文件名指的是不带扩展名后缀的文件名称,比如需要获取your_path/filename.txt中的filename部分。
需要取得文件名信息,我们可以使用pathinfo($filename, PATHINFO_FILENAME)](http://php.net/manual/zh/function.pathinfo.php)**、**[basename($filename, $suffix)](http://php.net/manual/zh/function.basename.php)** 和 **[SplFileInfo::getBasename($suffix)获取。
这里给出的$suffix** 指不获取 **$suffix扩展名部分(比如不获取$suffix = '.txt')。
请看下面的示例:
$filename = 'F:\Program Files\SSH Communications Security\SSH Secure Shell\Output.txt';
将会获取到Output这部分文件名信息。
3.3 扩展名信息
扩展名我们可以使用pathinfo($filename, PATHINFO_EXTENSION)和SplFileInfo::getExtension()方法拿到。
基于前面的了解,我们可以获取到txt这部分扩展信息,这里不再赘述。
3.4 basename(文件名 + 扩展名)信息
basename指的是文件名 + 扩展名内容信息,可以使用pathinfo($filename, PATHINFO_BASENAME)](http://php.net/manual/zh/function.pathinfo.php)**、 **[basename($filename)、SplFileInfo::getBasename()和SplFileInfo::getFilename()方法拿到。
虽然这里我们列出了很多的函数,但是基本上还是比较容易理解的,需要注意的是:
pathinfo 可以获取所有文件相关的路径信息,如果指定第二个参数选项将仅获取该部分的信息
文件名和 basename 不是特别容易理解,你可以使用完全相同的方法或函数basename和SplFileInfo::getBasename()获取他们,区别在于是否摘除指定的$suffix后缀。
3.5 示例
getPath(); echo '--- directory begin: ---' . PHP_EOL; echo $directory1 . PHP_EOL, $directory2 . PHP_EOL, $directory3 . PHP_EOL; // 文件名 $suffix = '.txt'; $filename1 = pathinfo($filename, PATHINFO_FILENAME); $filename2 = basename($filename, $suffix); $filename3 = $file->getBasename($suffix); echo '--- filename begin: ---' . PHP_EOL; echo $filename1 . PHP_EOL, $filename2 . PHP_EOL, $filename3 . PHP_EOL; // 扩展名 $extension1 = pathinfo($filename, PATHINFO_EXTENSION); $extension2 = $file->getExtension(); echo '--- extension begin: ---' . PHP_EOL; echo $extension1 . PHP_EOL, $extension2 . PHP_EOL; // basename = 文件名 + 扩展名 $basename1 = pathinfo($filename, PATHINFO_BASENAME); $basename2 = basename($filename); $basename3 = $file->getBasename(); $basename4 = $file->getFilename(); echo '--- basename begin: ---' . PHP_EOL; echo $basename1 . PHP_EOL, $basename2 . PHP_EOL, $basename3 . PHP_EOL, $basename4 . PHP_EOL;
它们的运行结果如下:
--- directory begin: --- F:\Program Files\SSH Communications Security\SSH Secure Shell F:\Program Files\SSH Communications Security\SSH Secure Shell F:\Program Files\SSH Communications Security\SSH Secure Shell --- filename begin: --- Output Output Output --- extension begin: --- txt txt --- basename begin: --- Output.txt Output.txt Output.txt Output.txt
3.6 Detailed introduction to PHP file system图
另外需要注意的一点是在使用 SplFileInfo 获取 basename 时,getBasename() 和 getFilename() 返回基本一致,但是在处理根目录下的文件名获取时表现稍有不同。
这里可以到官方文档中用户 提交的反馈 去详细了解一下。
4 获取文件的绝对路径
绝对路径由realpath($path)和SplFileInfo::getRealpath()获取。
5 获取文件类型
可以使用filetype($filename)和SplFileInfo::getType()来获取文件的类型。
返回值范围:
dir
file
char
fifo
block
link
unknown
可以查看 Linux 文件类型与扩展名 相关文件类型,这里我们重点关注下dir目录和file普通文件类型即可。
6 获取文件大小
可以使用filesize($filename)和SplFileInfo::getSize()来获取文件的大小,不再赘述。
7 获取文件权限
可以使用fileperms($filename)和SplFileInfo::getPerms()来获取到文件的所属权限。
值得注意的是它们的返回值是十进制表示的权限,如果需要获取类似0655八进制权限表示法,我们需要对返回值进行处处理才行:
// @see http://php.net/manual/zh/function.fileperms.php#refsect1-function.fileperms-examples $permissions = substr(sprintf("%o", fileperms($filename)), -4);
你可以通过 PHP: fileperms() values and convert these 了解更多关于 PHP 获取文件权限转换的更多细节。
基本上学习完这些文件元数据信息获取方法,差不多可以应对日常开发过程中的多数应用场景,尽管如此,还是建议仔细去阅读官方 文件系统函数,那里才是知识的源泉。
掌握文件的元数据,对我们了解文件的特性大有裨益,就好比两个人谈恋爱,懂得彼此才是最好的状态。
可以说我们日常在处理文件的过程中,更多的是在操作文件或者目录(文件夹),本节我们将学习文件系统操作相关知识。
依据文件类型的不同我们可以简单的将操作分为:
对目录(dir)的操作
和对普通文件(file)的操作
在处理目录时我们一般涉及如下处理:
创建目录
删除目录
打开目录
读取目录
关闭目录句柄
场景一
我们有一套 CMS 管理系统支持文件上传处理,当目录不存在时依据文件上传时间,动态的创建文件存储目录,比如,我们依据年/月/日(2018/01/01)格式创建目录。这里就涉及到目录创建的处理。
场景二
当然,文件上传完成了还不够,我们还需要读取各个目录下的所有文件。这里涉及打开目录、读取目录以及读取完成后关闭目录句柄。
有了相关概念和思路后,我们具体看看究竟 PHP 文件系统给我们提供了哪些方便处理目录的函数呢?
在 PHP 文件系统扩展中同样给我们提供了处理 目录结构的系统函数。
其中创建一个新目录需要使用[mkdir($pathname [, $mode = 0777, $recursive = false])](http://php.net/manual/zh/func...函数。
$pathname 参数为待创建目录的路径
$mode 为创建目录时的访问权限,0777 意味着获取最大访问权限
$recursive 用于标识是否递归创建目录,默认 false 不会递归创建
请看一个示例:
$pathname = "/path/to/your/upload/file/2018/01/01"; $created = mkdir($pathname);
创建目录是不是特别的简单呢?
但是等等,我们在类 Unix 系统中满心欢喜的使用mkdir并采用$mode=0777权限来创建一个全新的目录,但为什么当我们进入到目录中看到的目录的权限却是0755呢?
这里涉及到umask掩码的问题!
重点:原来我们在类 Unix 系统中创建新目录是给出的权限会默认减去当前系统的umask值,才是实际创建目录时的所属权限。
什么意思呢?
比如:
// 我们期望创建的文件权限 $mode = 0777; // 当前系统中 umask 值 $umask = 0022;// 可以由 umask 命令查看当前系统 umask 值,默认是 0022 // 实际创建的文件权限 0777 - 0022 ------ = 0755
现在我们来对之前的实例稍作修改,看看 PHP 如何创建目录时得到希望的系统权限吧:
$pathname = "/path/to/your/upload/file/2018/01/01"; // 将系统 umask 设置为 0,并取得当前 umask 值(比如默认 0022) $umask = umask(0); $created = mkdir($pathname, $mode = 0777); // 将系统 umask 设置回原值 umask($umask);
有关 umask 函数说明可以查看官方手册。另外可以查看 Why can't PHP create a directory with 777 permissions? 这个问答了解更多细节。
面向过程的目录遍历提供两种解决方案:
通过 opendir、readdir 和 closedir 来遍历目录;
另一种是直接使用scandir遍历指定路径中的文件和目录。
目录遍历示例一,出自 官方文档:
目录遍历示例二,出自 官方文档:
. // [1] => .. // [2] => bar.php // [3] => foo.txt // [4] => somedir // )
目录的操作处理大致就是在处理这两类问题,相比于普通文件的处理来讲简单很多,下一节我们会学习有关普通文件的处理,请大家做好战斗准备。
可以说我们在处理文件系统时,绝大多数都是在处理一个普通文件,那么我们在操作文件时,我们究竟在做什么呢?
你可能已经想到了,没错我们多数时候就是在处理如下文件问题:
创建一个新的空文件
打开一个文件句柄,以供后续读取或写入
将文件中的内容覆盖掉(覆盖写入),或者在文件末尾写入新的内容(追加写入)
读取文件的内容
删除文件
复制文件
关闭文件句柄
文件的读取和写入相对会复杂一些,所以这两部分的内容会在稍后详细讲解。先让我们看看其它几个常见文件处理。
创建空文件有两种方式:
一是:以写入(w)模式使用 fopen($filename, $mode = 'wb') 打开一个文件,当文件不存在时则会创建一个新文件;
二是:使用 touch 函数创建一个新文件。
这两个函数同其它文件系统函数使用大致相同,感兴趣的朋友可以阅读手册,这里不作展开。
删除文件由 unlink($filename) 函数完成。
复制文件由 copy($source, $dest) 函数完成,会将 $source 文件拷贝到 $dest 文件中。
如果需要移动文件(重命名)可以使用rename($oldname, $newname)完成这个处理。
以上都是相对简单的文件处理函数就不一一举例说明了。
接下来学习如何读取文件中的内容。依据二八原则,可以说我们百分之八十的时间都在处理文件写入和读取的处理,所以我们有必要理清如何对文件进行读取和写入。
读取文件的标准流程是:
打开一个文件句柄;
使用文件读取函数读取文件;
判断是否到文件结尾,到结尾则结束读取,否则回到操作 2;
读取完成关闭句柄;
开始之前我们需要准备一个有数据的文件,比如F:\php_workspace\php-code-kata\read.txt,在看一个简单的文件读取示例:
Copy after login
现在,我们来详细讲解一下上述代码做了什么处理吧:
使用 fopen($filename, $mode) 打开一个文件或 URL 句柄,供后续文件系统函数使用;
使用 fgetc($handle) 函数从文件句柄中读取一个字符;
使用 feof($handle) 判断文件句柄是否到文件的结尾处,否则继续读取文件;
当读取完成后使用 fclose($handle) 关闭打开的文件句柄,完成文件读取的所有操作。
总体来说,在读取文件时按照以上处理流程,基本上太容易出错的。不过即便如此,还是有些重点需要我们小心处理:
我们以什么模式打开一个文件句柄,示例中使用$mode='rb'r(read) 只读模式开个一个文件句柄(只读模式下不能对文件尽心写入)。另外还有几个常用模式可供使用:
r+ 读写模式
w(write) 覆盖写入
w+ 覆盖读写
a(append) 追加写入
a+ 追加读写
b重点关注此模式,为增强项目可移植和健壮性,推荐所有模式添加「b」模式强制使用二进制模式
有关所有可用模式的说明可以从 模式 手册中查找。
在执行文件内容读取时除了逐字符读取(fgetc),要支持一下集中读取形式:
fgets($handle) 每次读取一行数据
fgetss($handle) 每次读取一行数据,并过来 HTML 标记
fgetcsv($handle) 读取 CSV 文件,每次读取一样并解析字段
fread($handle, $length) 每次从句柄中最多读取$length个字节。
处理可以从句柄中读取文件数据,PHP 还提供将整个文件读取的方法:
file($filename) 把整个文件读入一个数组中
file_get_contents($filename) 将整个文件读入一个字符串
注意:读取文件操作时我们推荐使用 file_get_contents。
到这里我们基本上就涵盖了文件读取的所有知识点,相信大家对文件读取已经有了一个比较系统的认知。
下面我们进入到文件写入处理中,看看文件写入的正确姿势。
典型的文件写入流程基本上和文件读取流程一致:
打开一个文件句柄;
使用文件读取函数向文件中写入内容;
写入完成关闭句柄。
依据惯例我们来看一个简单的示例:
Copy after login
注意:这里我们以追加写入的模式 $mode = 'ab'写入文件内容。
文件写入就如同文件读取一样的简单,相信大家能够轻松掌握这方面的知识。然而,我们显示世界可能充满了荆棘,稍不留神可能就会深陷泥沼。比如:
我在写入文件时,同时其他人也在对同一个文件进行写入,怎么办?我们可以使用 flock($handle, LOCK_EX) 加锁函数进行独占写入。
每次都需要打开文件、写入、再关闭是在麻烦!有没有更简单的方式写文件呢?PHP 同样为你考虑到了这点,所以提供了 [file_put_contents($filename, $data [, LOCK_EX])](http://php.net/manual/zh/func... 将一个字符串写入文件,同样的它也支持独占写入。
到这里,我们基本上就学习完 PHP 文件系统中大多数常用的函数了。然而就如我所说的那样,现实世界总是残酷的。尤其是在读写文件时,经常会遇到各种各样的错误,我们应该如何才能避免呢?
嗯,PHP 一样为我们内置了检测文件有效性的函数,规避各种错误。
文件有效性检测
检测文件的有效性能够让我们规避常见的开发错误,比如:
当相文件中写入数据时,是不是需要检测它有可写的权限,并且它是不是一个文件而非文件夹?
读取文件内容时,是不是需要查看下我们能不能对其进行读取?
在安装项目时,我们是不是需要检测已经依据实例配置文件创建了实际的配置文件呢?
这些内容都需要使用到文件有效性检测相关知识。
判断文件是否可写我们有:is_writable($filename) 和SplFileInfo::isWritable()。
路径目录判断:is_dir($filename)](http://php.net/manual/zh/function.is-dir.php) 和 **[SplFileInfo::isDir()](http://php.net/manual/zh/splfileinfo.isdir.php)**;文件判断:[is_file($filename) 和 **SplFileInfo::isFile()。
检测文件或目录是否已经创建过,我们使用 file_exists($filename) 函数完成。
如何修改文件权限
当我们能够正确的检测文件是否存在时,我们还需要面对的问题时,如果我们的文件当前用户不可写入,我们应该如何修改权限使其可写呢?
这里就涉及修改文件权限操作,之前我们在创建目录是已经接触过umask掩码相关知识。这里我们将讲解那些已经创建的文件权限变更的方法。
通常,我们会使用chmod($filename, $mode)去修改一个文件的权限。
另外,还可以关注以下几个权限相关的处理函数:
chgrp($filename, $group) 改变文件所属的组
chown($filename, $user) 改变文件的所有者
以及,之前提到过的 umask 修改掩码函数。
文章进行到这里,其实基本上 PHP 文件系统的所有知识都已经涉及到了。那么,下回见吧?不不不...
为了应对实战(面试需要),我们可能需要进一步对目录遍历做更进一步的研究。还记得我们之前使用过scandir来遍历指定路径中的文件和目录夹么?
现在我们将使用面向对象的接口来重新实现一个权限的目录遍历处理。
使用面向对象的接口来遍历目录,是一个非常有意义的教程,这里我们所涉及使用的接口包括:
DirectoryIterator 创建非递归的目录迭代器
RecursiveDirectoryIterator 创建递归的目录迭代器
RecursiveIteratorIterator 创建一个递归迭代器的迭代器(用于迭代获取 RecursiveIteratorIterator 示例)
话不多说,我们看下如何创建一个功能强大的支持递归迭代的目录迭代程序:
/** * 目录扫描 * * @method listContents($path, $recursive = false) 获取目录中所有文件及文件夹 */ class DirectoryScanner { /** * 获取目录中所有文件及文件夹 * * @param $path 目录 * @param $recursive 递归获取 * * @return array */ public static function listContents($path, $recursive = false) { $iter = $recursive ? static::getRecursiveDirectoryIterator($path) : static::getDirectoryIterator($path); $result = []; foreach ($iter as $file) { if (in_array($file->getFilename(), ['.', '..'])) { continue; } $result[] = clone $file; } return $result; } /** * 获取目录迭代器 * * @param $path 目录 * * @return DirectoryIterator::class */ public static function getDirectoryIterator($path) { return new DirectoryIterator($path); } /** * 获取递归目录迭代器 * * @param $path 目录 * @param $mode 遍历模式: RecursiveIteratorIterator::SELF_FIRST 从当前目录开始遍历;RecursiveIteratorIterator::CHILD_FIRST 从子目录开始遍历 * * @return RecursiveIteratorIterator::class */ public static function getRecursiveDirectoryIterator($path, $mode = RecursiveIteratorIterator::SELF_FIRST ) { return new RecursiveIteratorIterator( new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS), $mode ); } } $path = 'F:\php_workspace\php-code-kata\direcotry-iterator\dir'; var_dump(DirectoryScanner::listContents($path)); var_dump(DirectoryScanner::listContents($path, true));
文件系统思维导图
相关推荐:
The above is the detailed content of Detailed introduction to PHP file system. For more information, please follow other related articles on the PHP Chinese website!