跳到内容

Filesystem 组件

编辑此页

Filesystem 组件提供了平台无关的实用工具,用于文件系统操作和文件/目录路径操作。

安装

1
$ composer require symfony/filesystem

注意

如果你在 Symfony 应用程序之外安装此组件,你必须在你的代码中 require vendor/autoload.php 文件,以启用 Composer 提供的类自动加载机制。阅读 这篇文章 以获取更多详细信息。

用法

该组件包含两个主要的类,分别名为 FilesystemPath

1
2
3
4
5
6
7
8
9
10
11
12
13
use Symfony\Component\Filesystem\Exception\IOExceptionInterface;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Filesystem\Path;

$filesystem = new Filesystem();

try {
    $filesystem->mkdir(
        Path::normalize(sys_get_temp_dir().'/'.random_int(0, 1000)),
    );
} catch (IOExceptionInterface $exception) {
    echo "An error occurred while creating your directory at ".$exception->getPath();
}

Filesystem 实用工具

mkdir

mkdir() 递归地创建目录。在 POSIX 文件系统上,目录默认以模式值 0777 创建。你可以使用第二个参数来设置你自己的模式

1
$filesystem->mkdir('/tmp/photos', 0700);

注意

你可以传递一个数组或任何 Traversable 对象作为第一个参数。

注意

此函数会忽略已存在的目录。

注意

目录权限受当前的 umask 影响。为你网络服务器设置 umask,使用 PHP 的 umask 函数,或者在使用目录创建后使用 chmod 函数。

exists

exists() 检查一个或多个文件或目录是否存在,如果任何一个文件或目录缺失,则返回 false

1
2
3
4
5
6
// if this absolute directory exists, returns true
$filesystem->exists('/tmp/photos');

// if rabbit.jpg exists and bottle.png does not exist, returns false
// non-absolute paths are relative to the directory where the running PHP script is stored
$filesystem->exists(['rabbit.jpg', 'bottle.png']);

注意

你可以传递一个数组或任何 Traversable 对象作为第一个参数。

copy

copy() 创建单个文件的副本(使用 mirror() 复制目录)。如果目标已存在,则仅当源修改日期晚于目标日期时才复制文件。此行为可以通过第三个布尔参数覆盖

1
2
3
4
5
// works only if image-ICC has been modified after image.jpg
$filesystem->copy('image-ICC.jpg', 'image.jpg');

// image.jpg will be overridden
$filesystem->copy('image-ICC.jpg', 'image.jpg', true);

touch

touch() 设置文件的访问和修改时间。默认使用当前时间。你可以使用第二个参数设置你自己的时间。第三个参数是访问时间

1
2
3
4
5
6
// sets modification time to the current timestamp
$filesystem->touch('file.txt');
// sets modification time 10 seconds in the future
$filesystem->touch('file.txt', time() + 10);
// sets access time 10 seconds in the past
$filesystem->touch('file.txt', time(), time() - 10);

注意

你可以传递一个数组或任何 Traversable 对象作为第一个参数。

chown

chown() 更改文件的所有者。第三个参数是布尔递归选项

1
2
3
4
// sets the owner of the lolcat video to www-data
$filesystem->chown('lolcat.mp4', 'www-data');
// changes the owner of the video directory recursively
$filesystem->chown('/video', 'www-data', true);

注意

你可以传递一个数组或任何 Traversable 对象作为第一个参数。

chgrp

chgrp() 更改文件的组。第三个参数是布尔递归选项

1
2
3
4
// sets the group of the lolcat video to nginx
$filesystem->chgrp('lolcat.mp4', 'nginx');
// changes the group of the video directory recursively
$filesystem->chgrp('/video', 'nginx', true);

注意

你可以传递一个数组或任何 Traversable 对象作为第一个参数。

chmod

chmod() 更改文件的模式或权限。第四个参数是布尔递归选项

1
2
3
4
// sets the mode of the video to 0600
$filesystem->chmod('video.ogg', 0600);
// changes the mode of the src directory recursively
$filesystem->chmod('src', 0700, 0000, true);

注意

你可以传递一个数组或任何 Traversable 对象作为第一个参数。

remove

remove() 删除文件、目录和符号链接

1
$filesystem->remove(['symlink', '/path/to/directory', 'activity.log']);

注意

你可以传递一个数组或任何 Traversable 对象作为第一个参数。

rename

rename() 更改单个文件或目录的名称

1
2
3
4
5
6
// renames a file
$filesystem->rename('/tmp/processed_video.ogg', '/path/to/store/video_647.ogg');
// renames a directory
$filesystem->rename('/tmp/files', '/path/to/store/files');
// if the target already exists, a third boolean argument is available to overwrite.
$filesystem->rename('/tmp/processed_video2.ogg', '/path/to/store/video_647.ogg', true);

symlink() 从目标创建到目的地的符号链接。如果文件系统不支持符号链接,则可以使用第三个布尔参数

1
2
3
4
5
// creates a symbolic link
$filesystem->symlink('/path/to/source', '/path/to/destination');
// duplicates the source directory if the filesystem
// does not support symbolic links
$filesystem->symlink('/path/to/source', '/path/to/destination', true);

readlink() 读取链接目标。

Filesystem 组件提供的 readlink() 方法在所有操作系统上的行为方式都相同(与 PHP 的 readlink 函数不同)

1
2
3
4
5
// returns the next direct target of the link without considering the existence of the target
$filesystem->readlink('/path/to/link');

// returns its absolute fully resolved final version of the target (if there are nested links, they are resolved)
$filesystem->readlink('/path/to/link', true);

其行为如下:

  • $canonicalizefalse 时:

    • 如果 $path 不存在或不是链接,则返回 null
    • 如果 $path 是链接,它将返回链接的下一个直接目标,而不考虑目标是否存在。
  • $canonicalizetrue 时:

    • 如果 $path 不存在,则返回 null。
    • 如果 $path 存在,则返回其绝对完全解析的最终版本。

注意

如果你希望在不检查路径是否存在的情况下规范化路径,则可以使用 canonicalize() 方法。

makePathRelative

makePathRelative() 接受两个绝对路径,并返回从第二个路径到第一个路径的相对路径

1
2
3
4
5
6
7
// returns '../'
$filesystem->makePathRelative(
    '/var/lib/symfony/src/Symfony/',
    '/var/lib/symfony/src/Symfony/Component'
);
// returns 'videos/'
$filesystem->makePathRelative('/tmp/videos', '/tmp');

mirror

mirror() 将源目录的所有内容复制到目标目录中(使用 copy() 方法复制单个文件)

1
$filesystem->mirror('/path/to/source', '/path/to/target');

isAbsolutePath

isAbsolutePath() 如果给定的路径是绝对路径,则返回 true,否则返回 false

1
2
3
4
5
6
7
8
// returns true
$filesystem->isAbsolutePath('/tmp');
// returns true
$filesystem->isAbsolutePath('c:\\Windows');
// returns false
$filesystem->isAbsolutePath('tmp');
// returns false
$filesystem->isAbsolutePath('../dir');

tempnam

tempnam() 创建一个带有唯一文件名的临时文件,并返回其路径,或者在失败时抛出异常

1
2
3
4
// returns a path like : /tmp/prefix_wyjgtF
$filesystem->tempnam('/tmp', 'prefix_');
// returns a path like : /tmp/prefix_wyjgtF.png
$filesystem->tempnam('/tmp', 'prefix_', '.png');

dumpFile

dumpFile() 将给定的内容保存到一个文件中(如果文件及其目录不存在,则创建它们)。它以原子方式执行此操作:它首先写入一个临时文件,然后在完成后将其移动到新的文件位置。这意味着用户将始终看到完整的旧文件或完整的新文件(但永远不会看到部分写入的文件)

1
$filesystem->dumpFile('file.txt', 'Hello World');

现在 file.txt 文件包含 Hello World

appendToFile

appendToFile() 在某些文件的末尾添加新内容

1
2
3
$filesystem->appendToFile('logs.txt', 'Email sent to [email protected]');
// the third argument tells whether the file should be locked when writing to it
$filesystem->appendToFile('logs.txt', 'Email sent to [email protected]', true);

如果文件或其包含目录不存在,此方法会在追加内容之前创建它们。

readFile

7.1

readFile() 方法在 Symfony 7.1 中引入。

readFile() 以字符串形式返回文件的所有内容。与 PHP 的 file_get_contents 函数不同,当给定的文件路径不可读以及传递目录路径而不是文件时,它会抛出异常

1
$contents = $filesystem->readFile('/some/path/to/file.txt');

现在 $contents 变量存储了 file.txt 文件的所有内容。

路径操作实用工具

处理文件路径通常会遇到一些困难:

  • 平台差异:文件路径在不同的平台上看起来不同。UNIX 文件路径以斜杠 (“/”) 开头,而 Windows 文件路径以系统驱动器 (“C:”) 开头。UNIX 使用正斜杠,而 Windows 默认使用反斜杠。
  • 绝对/相对路径:Web 应用程序经常需要处理绝对路径和相对路径。正确地将一种路径转换为另一种路径是棘手且重复的。

Path 提供了实用方法来解决这些问题。

规范化

返回与给定路径等效的最短路径名。它迭代地应用以下规则,直到无法进行进一步处理:

  • “.” 段被移除;
  • “..” 段被解析;
  • 反斜杠 (“\”) 被转换为正斜杠 (“/”);
  • 根路径 (“/” 和 “C:/”) 始终以斜杠结尾;
  • 非根路径永远不会以斜杠结尾;
  • 方案(例如 “phar://”)被保留;
  • ~ 替换为用户的 home 目录。

你可以使用 canonicalize() 规范化路径

1
2
echo Path::canonicalize('/var/www/vhost/webmozart/../config.ini');
// => /var/www/vhost/config.ini

你可以将绝对路径和相对路径传递给 canonicalize() 方法。当传递相对路径时,路径开头的 “..” 段会被保留

1
2
echo Path::canonicalize('../uploads/../config/config.yaml');
// => ../config/config.yaml

格式错误的路径将原样返回

1
2
echo Path::canonicalize('C:Programs/PHP/php.ini');
// => C:Programs/PHP/php.ini

转换绝对/相对路径

绝对/相对路径可以使用方法 makeAbsolute()makeRelative() 进行转换。

makeAbsolute() 方法需要一个相对路径和一个基础路径,以该基础路径为基础来解析相对路径

1
2
echo Path::makeAbsolute('config/config.yaml', '/var/www/project');
// => /var/www/project/config/config.yaml

如果在第一个参数中传递了绝对路径,则绝对路径将原样返回

1
2
echo Path::makeAbsolute('/usr/share/lib/config.ini', '/var/www/project');
// => /usr/share/lib/config.ini

该方法会解析 “..” 段(如果存在任何段)

1
2
echo Path::makeAbsolute('../config/config.yaml', '/var/www/project/uploads');
// => /var/www/project/config/config.yaml

如果你希望能够同时接受相对路径(例如,相对于项目根目录的相对路径)和绝对路径,则此方法非常有用。

makeRelative()makeAbsolute() 的逆操作

1
2
echo Path::makeRelative('/var/www/project/config/config.yaml', '/var/www/project');
// => config/config.yaml

如果路径不在基础路径内,则该方法将根据需要前置 “..” 段

1
2
echo Path::makeRelative('/var/www/project/config/config.yaml', '/var/www/project/uploads');
// => ../config/config.yaml

使用 isAbsolute()isRelative() 检查路径是绝对路径还是相对路径

1
2
Path::isAbsolute('C:\Programs\PHP\php.ini')
// => true

所有这四个方法都在内部规范化传递的路径。

查找最长公共基础路径

当你在文件系统中存储绝对文件路径时,这会导致大量重复的信息

1
2
3
4
5
6
7
return [
    '/var/www/vhosts/project/httpdocs/config/config.yaml',
    '/var/www/vhosts/project/httpdocs/config/routing.yaml',
    '/var/www/vhosts/project/httpdocs/config/services.yaml',
    '/var/www/vhosts/project/httpdocs/images/banana.gif',
    '/var/www/vhosts/project/httpdocs/uploads/images/nicer-banana.gif',
];

特别是在存储大量路径时,重复信息的数量非常明显。您可以使用 getLongestCommonBasePath() 来检查路径列表中是否存在公共基础路径

1
2
3
4
5
6
7
8
$basePath = Path::getLongestCommonBasePath(
    '/var/www/vhosts/project/httpdocs/config/config.yaml',
    '/var/www/vhosts/project/httpdocs/config/routing.yaml',
    '/var/www/vhosts/project/httpdocs/config/services.yaml',
    '/var/www/vhosts/project/httpdocs/images/banana.gif',
    '/var/www/vhosts/project/httpdocs/uploads/images/nicer-banana.gif'
);
// => /var/www/vhosts/project/httpdocs

使用此公共基础路径来缩短存储的路径

1
2
3
4
5
6
7
return [
    $basePath.'/config/config.yaml',
    $basePath.'/config/routing.yaml',
    $basePath.'/config/services.yaml',
    $basePath.'/images/banana.gif',
    $basePath.'/uploads/images/nicer-banana.gif',
];

getLongestCommonBasePath() 始终返回规范路径。

使用 isBasePath() 来测试一个路径是否是另一个路径的基础路径

1
2
3
4
5
6
7
8
Path::isBasePath("/var/www", "/var/www/project");
// => true

Path::isBasePath("/var/www", "/var/www/project/..");
// => true

Path::isBasePath("/var/www", "/var/www/project/../..");
// => false

查找目录/根目录

PHP 提供了函数 dirname 来获取文件路径的目录路径。此方法有一些怪癖

  • dirname() 在 UNIX 上不接受反斜杠
  • dirname("C:/Programs") 返回 "C:",而不是 "C:/"
  • dirname("C:/") 返回 ".",而不是 "C:/"
  • dirname("C:") 返回 ".",而不是 "C:/"
  • dirname("Programs") 返回 ".",而不是 ""
  • dirname() 不会对结果进行规范化

getDirectory() 修复了这些缺点

1
2
echo Path::getDirectory("C:\Programs");
// => C:/

此外,您可以使用 getRoot() 来获取路径的根目录

1
2
3
4
5
echo Path::getRoot("/etc/apache2/sites-available");
// => /

echo Path::getRoot("C:\Programs\Apache\Config");
// => C:/

错误处理

每当发生错误时,都会抛出一个实现了 ExceptionInterfaceIOExceptionInterface 的异常。

注意

如果目录创建失败,则会抛出 IOException 异常。

本作品,包括代码示例,根据 Creative Commons BY-SA 3.0 许可协议获得许可。
目录
    版本