PHP 文件系统完全指南

今天我们将开启一个新的探索之旅,深入了解 php 文件系统,系统地学习和掌握 php 文件系统的基本使用方法。

在日常的开发过程中,我们常常需要处理各种文件。例如,读取 .env 文件中的配置信息、将项目中的错误信息写入日志文件或者获取图片的创建时间等。在实现这些功能时,我们需要使用 PHP 文件系统接口。

以下是本文所涵盖主题的提纲:

一 什么是文件系统二 深入 PHP 文件系统三 面向对象的目录遍历四 PHP 文件系统思维导图

本文篇幅较长,阅读时间约为 20 分钟,请做好准备!

一 什么是文件系统

立即学习“PHP免费学习笔记(深入)”;

在开始之前,我们首先需要明确我们所研究的问题领域,理解什么是文件系统,以及我们所研究的对象。

简单来说,文件系统就是管理我们的目录(文件夹)和文件的方式。通常,我们会将具有相似属性的文件存储在同一个目录中,以便后续查找。这种常见的操作就会涉及到目录和文件的管理。

对于软件工程师来说,一个典型的使用场景是在开发 MVC 项目时,将控制器、视图和模型等模块的文件存储在不同的目录结构中以便管理。

无论如何,我们根据不同特性划分文件和目录都是为了解决文件存储和查找的问题。

有了这些认知后,我们自然会想到我们当前研究的 PHP 文件系统(或文件系统)的研究对象,简单来说就是:

目录(文件夹)文件

也就是说,本文所讲解的 PHP 文件系统函数处理,基本都是围绕目录和文件展开的。

二 深入 PHP 文件系统

在 PHP 文件系统中,内置提供了超过 80 个可用的文件系统函数。由于数量繁多且功能强大,本文无法逐一讲解所有系统函数。一方面,时间有限;另一方面,我们也没有足够的精力在短时间内掌握所有函数。

尽管如此,大家不必气馁,本文将利用有限的时间和精力,研究以下几个在文件处理时的常见话题:

文件的元数据如何获取文件的 MIME 类型如何获取文件和目录的操作文件和目录的权限管理

另外,补充说明一点,PHP 标准函数库不仅为我们提供了面向过程的文件系统处理函数,同时还封装了常用目录及文件操作的面向对象接口和迭代器接口,方便大家使用:

SplFileInfofinfoDirectoryIteratorRecursiveDirectoryIterator

2.1 文件系统的元数据

2.1.1 什么是元数据

元数据(meta data):通俗地说就是“数据的数据”。以一个 php 文件为例,它的元数据可以是创建时间、文件名、文件大小或文件所有权限等,这类能够表明该文件基本特征的数据就是“元数据(meta data)”。

2.1.2 常用元数据获取

在这一节,我们将学习一些经常需要获取的文件元数据函数,包括:

获取文件的最后修改时间获取文件的上次访问时间获取文件的路径信息获取文件的绝对路径获取文件类型获取文件大小获取文件权限获取文件所属用户及用户组

让我们开始吧!

获取文件的最后修改时间

要获取文件的上次被修改时间戳,我们可以使用函数 filemtime($filename) 或 SplFileInfo::getMTime() 方法。

// 文件路径请求改成你自己的文件路径$filename = "f://filesystem/test.txt";// 面向过程: 获取文件时间$modifyTimestamp = filemtime($filename);// 面向对象$file = new SplFileInfo($filename);$modifyTimestamp = $file->getMTime();

获取文件的上次访问时间

可以使用函数 fileatime($filename) 或 SplFileInfo::getATime() 方法,来获取文件的最后被访问时间戳。

// 文件路径请求改成你自己的文件路径$filename = "f://filesystem/test.txt";// 面向过程: 获取文件时间$accessTimestamp = fileatime($filename);// 面向对象$file = new SplFileInfo($filename);$accessTimestamp = $file->getATime();

除了 filemtime 和 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。

是不是很简单呢!

3 获取文件的路径信息

除了时间这些元数据,另一个经常遇到的情况是获取文件的路径信息,包括:

3.1 目录信息

获取目录信息我们可以使用 pathinfo($filename, PATHINFO_DIRNAME)、dirname($filename) 和 SplFileInfo::getPath()。

比如下面给出的文件:

$filename = 'F:Program FilesSSH Communications SecuritySSH Secure ShellOutput.txt';

将会获取到 F:Program FilesSSH Communications SecuritySSH Secure Shell 这部分目录信息。

3.2 文件名信息

这里我们所有的文件名指的是不带扩展名后缀的文件名称,比如需要获取 your_path/filename.txt 中的 filename 部分。

需要取得文件名信息,我们可以使用 pathinfo($filename, PATHINFO_FILENAME)、basename($filename, $suffix) 和 SplFileInfo::getBasename($suffix) 获取。

这里给出的 $suffix 指不获取 $suffix 扩展名部分(比如不获取 $suffix = ‘.txt’)。

请看下面的示例:

$filename = 'F:Program FilesSSH Communications SecuritySSH Secure ShellOutput.txt';

将会获取到 Output 这部分文件名信息。

3.3 扩展名信息

扩展名我们可以使用 pathinfo($filename, PATHINFO_EXTENSION) 和 SplFileInfo::getExtension() 方法拿到。

基于前面的了解,我们可以获取到 txt 这部分扩展信息,这里不再赘述。

3.4 basename(文件名 + 扩展名)信息

basename 指的是 文件名 + 扩展名 内容信息,可以使用 pathinfo($filename, PATHINFO_BASENAME)、 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 FilesSSH Communications SecuritySSH Secure ShellF:Program FilesSSH Communications SecuritySSH Secure ShellF:Program FilesSSH Communications SecuritySSH Secure Shell--- filename begin: ---OutputOutputOutput--- extension begin: ---txttxt--- basename begin: ---Output.txtOutput.txtOutput.txtOutput.txt

3.6 文件路径信息关系图

PHP 文件系统完全指南

4 获取文件的绝对路径

绝对路径由 realpath($path) 和 SplFileInfo::getRealpath() 获取。

5 获取文件类型

可以使用 filetype($filename) 和 SplFileInfo::getType() 来获取文件的类型。

小文AI论文 小文AI论文

轻松解决论文写作难题,AI论文助您一键完成,仅需一杯咖啡时间,即可轻松问鼎学术高峰!

小文AI论文 69 查看详情 小文AI论文

返回值范围:

dirfilecharfifoblocklinkunknown

可以查看 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 获取文件权限转换的更多细节。

基本上学习完这些文件元数据信息获取方法,差不多可以应对日常开发过程中的多数应用场景,尽管如此,还是建议仔细去阅读官方 文件系统函数,那里才是知识的源泉。

掌握文件的元数据,对我们了解文件的特性大有裨益,就好比两个人谈恋爱,懂得彼此才是最好的状态。

2.2 文件系统操作

可以说我们日常在处理文件的过程中,更多的是在操作文件或者目录(文件夹),本节我们将学习文件系统操作相关知识。

依据文件类型的不同我们可以简单的将操作分为:

对目录(dir)的操作对普通文件(file)的操作

2.2.1 目录操作

使用场景

在处理目录时我们一般涉及如下处理:

创建目录删除目录打开目录读取目录关闭目录句柄

场景一

我们有一套 CMS 管理系统支持文件上传处理,当目录不存在时依据文件上传时间,动态的创建文件存储目录,比如,我们依据 年/月/日(2018/01/01) 格式创建目录。这里就涉及到 目录创建 的处理。

场景二

当然,文件上传完成了还不够,我们还需要读取各个目录下的所有文件。这里涉及 打开目录、读取目录 以及读取完成后 关闭目录句柄。

有了相关概念和思路后,我们具体看看究竟 PHP 文件系统给我们提供了哪些方便处理目录的函数呢?

2.2.1.1 创建目录

在 PHP 文件系统扩展中同样给我们提供了处理 目录结构的系统函数。

其中创建一个新目录需要使用 mkdir($pathname [, $mode = 0777, $recursive = false]) 函数。

$pathname 参数为待创建目录的路径$mode 为创建目录时的访问权限,0777 意味着获取最大访问权限$recursive 用于标识是否递归创建目录,默认 false 不会递归创建

请看一个示例:

$pathname = "/path/to/your/upload/file/2018/01/01";$created = mkdir($pathname);

创建目录是不是特别的简单呢?

但是等等,我们在类 Unix 系统中满心欢喜的使用 mkdir 并采用 $mode=0777 权限来创建一个全新的目录,但为什么当我们进入到目录中看到的目录的权限却是 0755 呢?

umask 掩码

这里涉及到 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? 这个问答了解更多细节。

2.2.1.2 目录遍历

面向过程的目录遍历提供两种解决方案:

通过 opendir、readdir 和 closedir 来遍历目录;另一种是直接使用 scandir 遍历指定路径中的文件和目录。

目录遍历示例一,出自 官方文档:


目录遍历示例二,出自 官方文档:

 .//     [1] => ..//     [2] => bar.php//     [3] => foo.txt//     [4] => somedir// )

目录的操作处理大致就是在处理这两类问题,相比于普通文件的处理来讲简单很多,下一节我们会学习有关普通文件的处理,请大家做好战斗准备。

2.2.2 文件操作

使用场景

可以说我们在处理文件系统时,绝大多数都是在处理一个普通文件,那么我们在操作文件时,我们究竟在做什么呢?

你可能已经想到了,没错我们多数时候就是在处理如下文件问题:

创建一个新的空文件打开一个文件句柄,以供后续读取或写入将文件中的内容覆盖掉(覆盖写入),或者在文件末尾写入新的内容(追加写入)读取文件的内容删除文件复制文件关闭文件句柄

文件的读取和写入相对会复杂一些,所以这两部分的内容会在稍后详细讲解。先让我们看看其它几个常见文件处理。

2.2.2.1 创建空文件

创建空文件有两种方式: 一是:以写入(w)模式使用 fopen($filename, $mode = ‘wb’) 打开一个文件,当文件不存在时则会创建一个新文件; 二是:使用 touch 函数创建一个新文件。

这两个函数同其它文件系统函数使用大致相同,感兴趣的朋友可以阅读手册,这里不作展开。

2.2.2.2 删除文件

删除文件由 unlink($filename) 函数完成。

2.2.2.3 复制文件

复制文件由 copy($source, $dest) 函数完成,会将 $source 文件拷贝到 $dest 文件中。

如果需要移动文件(重命名)可以使用 rename($oldname, $newname) 完成这个处理。

以上就是PHP 文件系统完全指南的详细内容,更多请关注php中文网其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/514202.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年11月9日 02:21:38
下一篇 2025年11月9日 02:22:17

相关推荐

  • 使用 Go 语言进行原始套接字编程

    本文旨在指导开发者如何使用 Go 语言进行原始套接字编程,以实现自定义网络数据包的发送和接收。重点介绍使用 go.net/ipv4 库创建和操作原始套接字,以及如何构造自定义 IP 头部来实现源 IP 地址欺骗等高级网络功能。同时,也强调了使用原始套接字的安全风险和权限要求。 在某些网络编程场景下,…

    2025年12月16日
    000
  • Go语言在Fish Shell中的GOPATH正确配置指南

    本文旨在解决go语言开发者在使用fish shell时,因gopath环境变量配置不当导致的”cannot find package”错误。核心解决方案在于理解fish shell的变量导出机制,即在`~/.config/fish/config.fish`文件中使用`set …

    2025年12月16日
    000
  • 深入理解常量时间单字节比较:为什么需要它?

    本文深入探讨了go语言`crypto/subtle`包中`constanttimebyteeq`函数的设计哲学与必要性。尽管单字节比较在cpu层面通常被认为是常量时间操作,但传统条件分支可能引入分支预测失败的性能开销,并在安全敏感场景下构成侧信道攻击风险。`constanttimebyteeq`通过…

    2025年12月16日
    000
  • 深入理解Go并发:Goroutines、Channels与调度器行为

    本文旨在深入探讨Go语言的并发模型,重点解析Goroutines、Channels的工作原理及其与Go调度器之间的关系。通过分析一个具体的并发示例,我们将揭示Go程序执行顺序的非确定性,并提供如何使用Channels进行有效同步和通信的策略,以确保程序行为符合预期。 Go语言以其内置的并发原语而闻名…

    2025年12月16日
    000
  • 深入理解Go程序与Ptrace的交互:挑战与替代方案

    本文深入探讨了使用`ptrace`对go程序进行系统调用拦截的固有挑战。由于go运行时将goroutine多路复用到os线程的复杂机制,`ptrace`的线程绑定特性导致跟踪行为不稳定,表现为程序挂起和系统调用序列不一致。文章解释了go调度器的工作原理如何与`ptrace`的预期行为冲突,并提供了针…

    2025年12月16日
    000
  • 使用Go语言进行原始套接字编程

    本文介绍了如何使用Go语言进行原始套接字编程,以实现自定义IP数据包的发送和接收。由于安全限制,需要root权限或CAP_NET_RAW能力才能运行此类程序。文章将重点介绍使用 `go.net/ipv4` 包创建和操作原始套接字,以及如何构建和发送带有自定义IP头的UDP数据包,以满足特定网络需求,…

    2025年12月16日
    000
  • 深入理解Go程序与ptrace系统调用的不兼容性

    本文深入探讨了在Go程序中使用`ptrace`进行系统调用拦截时遇到的挂起和数据不一致问题。核心原因在于Go运行时(runtime)的goroutine与OS线程的调度机制与`ptrace`单线程追踪模式的根本冲突。文章将解释这一冲突的原理,并提供针对不同需求场景的替代解决方案,避免不当使用`ptr…

    2025年12月16日
    000
  • Go语言中正确使用导入包结构体作为类型的方法

    本文详细阐述了在go语言中如何正确地引用和使用从外部包导入的结构体作为类型。当尝试将导入包中的结构体(如`database/sql`包的`db`)用作函数参数时,必须使用完整的包名进行限定,以避免“未定义”错误,确保代码的编译与运行。 Go语言包引用机制概述 在Go语言中,代码被组织成包(packa…

    2025年12月16日
    000
  • 使用 Go (Golang) 枚举 Windows 注册表值

    本文档详细介绍了如何使用 Go 语言枚举 Windows 注册表中的值。通过 `golang.org/x/sys/windows/registry` 包,我们可以安全有效地访问和读取注册表信息。本文将提供代码示例,展示如何打开注册表键、读取键值名称,并将不同类型的注册表值转换为字符串。此外,还将讨论…

    2025年12月16日
    000
  • Go语言Levigo库的安装与常见问题解决

    本文旨在提供go语言levigo库的安装指南,并解决在安装过程中常见的“undefined referenc++e”链接错误。核心内容包括理解levigo对底层leveldb c++库的依赖,以及通过安装leveldb开发包(如`libleveldb-dev`)来正确满足这些依赖,从而确保levig…

    2025年12月16日
    000
  • 深入理解Go运行时:为何ptrace难以有效跟踪Go程序

    本文深入探讨了在go程序中使用`ptrace`进行系统调用拦截时遇到的挑战,核心原因在于go运行时对goroutine的调度和多路复用机制。由于go的goroutine可以在不同的操作系统线程之间切换,`ptrace`这种基于单线程的跟踪方式无法稳定捕捉go程序的系统调用行为,导致进程挂起和跟踪结果…

    2025年12月16日
    000
  • Golang项目依赖管理:理解go get与Go Modules的精髓

    本文旨在阐明go语言中依赖管理的机制,特别针对python/django开发者对`requirements.txt`类文件的期望。我们将深入探讨`go get`命令如何智能地处理依赖,包括其传递性依赖解析能力,并介绍现代go项目依赖管理的核心——go modules,以及`go.mod`和`go.s…

    2025年12月16日
    000
  • Golang如何在Linux服务器配置开发环境

    安装Go后配置环境变量并验证运行,1. 下载解压Go至/usr/local,2. 添加bin目录到PATH并设置GOPATH,3. 执行go version和go env验证,4. 编写hello.go测试程序确认环境正常。 要在Linux服务器上配置Golang开发环境,关键步骤是安装Go、设置工…

    2025年12月16日
    000
  • Go语言defer机制解析与非常规访问探讨

    go语言中的`defer`语句用于调度函数在当前函数返回前执行,常用于资源清理。然而,`defer`调用的列表是go运行时内部实现细节,通常无法从外部直接获取其引用或多次调用。尽管通过`cgo`和`unsafe`包理论上可以尝试访问这些内部结构,但这种做法极不推荐,因为它不可靠、不安全且缺乏可移植性…

    2025年12月16日
    000
  • 获取 PayPal OAuth 访问令牌时遇到 400 错误:解决方案及最佳实践

    本文旨在帮助开发者解决在使用 PayPal OAuth 获取访问令牌时遇到的 400 错误。通过分析常见错误原因,提供详细的排查步骤和解决方案,并分享最佳实践,确保顺利集成 PayPal OAuth 认证流程。重点关注 `grant_type` 参数的正确传递,并提供 Go 语言示例代码进行演示。 …

    2025年12月16日
    000
  • Golang中HTTP重定向与Cookie自动管理实践

    本文详细介绍了在golang中如何实现http请求重定向时自动携带并管理cookie。通过利用go标准库中的`net/http/cookiejar`包,开发者可以轻松地配置http客户端,使其在遇到302等重定向响应时,自动保存收到的cookie,并将其发送至新的跳转地址,确保会话状态的连续性,简化…

    2025年12月16日
    000
  • 深入理解Go语言与Ptrace:系统调用拦截的挑战与策略

    本文深入探讨了在go语言中尝试使用`ptrace`进行系统调用拦截时面临的固有挑战。由于go运行时将goroutine多路复用至os线程,并可能在系统调用期间切换线程,导致`ptrace`这种线程绑定的调试机制难以可靠地跟踪go程序的系统调用。文章解释了这一机制冲突的原理,并提供了针对不同场景的替代…

    2025年12月16日
    000
  • Go 模板解析问题:空白页面的排查与解决

    本文旨在解决 Go 语言模板解析时遇到的空白页面问题。我们将深入探讨 `template.ParseFiles` 和 `template.New` 的区别,分析导致空白页面的原因,并提供两种有效的解决方案,帮助开发者避免此类错误,提升模板使用的效率和准确性。 在使用 Go 语言进行 Web 开发时,…

    2025年12月16日
    000
  • 构建稳定的PHP与Go Unix域套接字通信:连接管理与最佳实践

    本文探讨了php客户端在使用unix域套接字与go服务器通信时遇到的连接挂起问题。核心原因在于go服务器在发送响应后未关闭连接,导致php客户端持续等待。解决方案是在go服务器的连接处理函数中添加`defer c.close()`以确保连接正确终止,从而使php客户端能正常完成读取并释放资源。 Un…

    2025年12月16日
    000
  • Golang指针与interface结合有什么注意事项

    指针赋值给interface时,interface保存的是指针的类型和值,因此nil指针不等于nil interface;只有当interface的类型和值均为nil时才为nil。方法接收者为指针时,只有该指针类型实现interface,值类型无法直接赋值;函数传参中使用指针+interface可修…

    2025年12月16日 好文分享
    000

发表回复

登录后才能评论
关注微信