Компонент Filesystem

Дата обновления перевода 2023-01-20

Компонент Filesystem

Компонент Filesystem предоставляет независимые от платформ инструменты для операций файловых систем и для манипуляций с путями файлов/каталогов.

Установка

1
$ composer require symfony/filesystem

Note

Если вы устанавливаете этот компонент вне приложения Symfony, вам нужно подключить файл vendor/autoload.php в вашем коде для включения механизма автозагрузки классов, предоставляемых Composer. Детальнее читайте в этой статье.

Использование

Компонент содержит два основных класса, под названиями Filesystem и Path:

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();
}

Утилиты файловой системы

mkdir

mkdir() создаёт каталог рекурсивно. В файловых системах POSIX, каталоги создаются со значением режима по умолчанию 0777. Вы можете использовать второй аргумент, чтобы установить ваш собственный режим:

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

Note

Вы можете передать массив или любой traversable объект в качестве первого аргумента.

Note

Эта функция игнорирует уже существующие каталоги.

Note

Текущий umask влияет на разрешения каталога. Установите umask для вашего веб-сервера, используйте функцию PHP umask или используйте функцию chmod после создания каталога.

exists

exists() проверяет наличие одного или болеефайлоа или каталогов и возвращает false, если какие-либо из них отсутствуют:

1
2
3
4
5
6
// если этот каталог с этим абсолютным путём существует, вернуть true
$fileSystem->exists('/tmp/photos');

// если rabbit.jpg существует, bottle.png не существует, вернуть false
// не абсолютные пути являются относительными к каталогу, где находится запущенный скрипт PHP
$fileSystem->exists(array('rabbit.jpg', 'bottle.png'));

Note

Вы можете передать массив или любой traversable объект в качестве первого аргумента.

copy

copy() делает копию одного файла (используйте mirror(), чтобы копировать каталоги). Если цель уже существует, то файл копируется только, если дата изменения источника позже, чем у цели. Это поведение можно переопределить третьим булевым аргументом:

1
2
3
4
5
// работает только, если image-ICC было изменено после image.jpg
$fileSystem->copy('image-ICC.jpg', 'image.jpg');

// image.jpg будет переопределено
$fileSystem->copy('image-ICC.jpg', 'image.jpg', true);

touch

touch() устанавливает доступ и время изменения файла. Текущее время используется по умолчанию. Вы можете установить ваше собственное с помощью второго аргумента. Третий аргумент - это время доступа:

1
2
3
4
5
6
// устанавливает время изменения в текущей отметке времени
$fileSystem->touch('file.txt');
// устанавливает время изменения на 10 секунд в будущем
$fileSystem->touch('file.txt', time() + 10);
// устанавливает время изменения на 10 секунд в прошлом
$fileSystem->touch('file.txt', time(), time() - 10);

Note

Вы можете передать массив или любой traversable объект в качестве первого аргумента.

chown

chown() изменяет владельца файла. Третий аргумент - рекурсивная булева опция:

1
2
3
4
// устанавливает владельца видео lolcat как www-data
$fileSystem->chown('lolcat.mp4', 'www-data');
// рекурсивно изменяет владельца каталога видео
$fileSystem->chown('/video', 'www-data', true);

Note

Вы можете передать массив или любой traversable объект в качестве первого аргумента.

chgrp

chgrp() изменяет группу файла. Третий аргумент - рекурсивная булева опция:

1
2
3
4
// устанавливает группу видео lolcat как nginx
$fileSystem->chgrp('lolcat.mp4', 'nginx');
// рекурсивно изменяет группу каталога видео
$fileSystem->chgrp('/video', 'nginx', true);

Note

Вы можете передать массив или любой traversable объект в качестве первого аргумента.

chmod

chmod() изменяет режим или разрешения файла. Четвертый аргумент - рекурсивная булева опция:

1
2
3
4
// устанавливает режим видео 0600
$fileSystem->chmod('video.ogg', 0600);
// рекурсивно изменяет режим каталога src
$fileSystem->chmod('src', 0700, 0000, true);

Note

Вы можете передать массив или любой traversable объект в качестве первого аргумента.

remove

remove() удаляет файлы, каталогы и сивольные ссылки:

1
$fileSystem->remove(array('symlink', '/path/to/directory', 'activity.log'));

Note

Вы можете передать массив или любой traversable объект в качестве первого аргумента.

rename

rename() изменяет имя одного файла или каталога:

1
2
3
4
5
6
// переименовывает файл
$fileSystem->rename('/tmp/processed_video.ogg', '/path/to/store/video_647.ogg');
// переименовывает каталог
$fileSystem->rename('/tmp/files', '/path/to/store/files');
// если цель уже существует, для перезаписи доступен третий булев аргумент.
$filesystem->rename('/tmp/processed_video2.ogg', '/path/to/store/video_647.ogg', true);

symlink() создаёт символьную ссылку от цели к направлению. Если файловая система не поддерживает символьные ссылки, доступен третий булев аргумент:

1
2
3
4
5
// создаёт символьную ссылку
$fileSystem->symlink('/path/to/source', '/path/to/destination');
// дублирует каталог источника, если файловая система
// не поддерживает символьные ссылки
$fileSystem->symlink('/path/to/source', '/path/to/destination', true);

readlink() читает ссылки целей.

Метод readlink(), предоставленные компонентом Filesystem всегда ведёт себя одинаково на всех операционных системах (в отличие от функции PHP readlink):

1
2
3
4
5
6
// возвращает следующую прямую цель ссылки, не рассматривая существование цели
$fileSystem->readlink('/path/to/link');

// возвращает абсолютнуюп полностью разрешённую финальную версию цели
// (если были вложенные ссылки, они разрешены)
$fileSystem->readlink('/path/to/link', true);

Его поведение следующее:

1
public function readlink($path, $canonicalize = false)
  • Когда $canonicalize - false:
    • если $path не существует, или не является ссылкой, то возвращается null.
    • если $path - ссылка, возвращается следующая прямая цель ссылки, не рассматривая существование цели.
  • Когда $canonicalize - true:
    • если $path не существует, то возвращается null.
    • если $path существует, то возвращается его абсолютная полностью разрешённая финальная версия.

Note

Если вы хотите каноникализировать путь, не проверяя его существование, вы можете использовать вместо этого метод canonicalize().

makePathRelative

makePathRelative() берёт два абсолютных пути и возвращает относительный путь от второго пути к первому:

1
2
3
4
5
6
7
// возвращает '../'
$fileSystem->makePathRelative(
    '/var/lib/symfony/src/Symfony/',
    '/var/lib/symfony/src/Symfony/Component'
);
// возвращает '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
// возвращает true
$fileSystem->isAbsolutePath('/tmp');
// возвращает true
$fileSystem->isAbsolutePath('c:\\Windows');
// возвращает false
$fileSystem->isAbsolutePath('tmp');
// возвращает false
$fileSystem->isAbsolutePath('../dir');

tempnam

tempnam() создает временный файл с уникальным именем, и возвращает его путь, или вызывает исключение при неудаче:

1
2
3
4
// возвращает путь вроде : /tmp/prefix_wyjgtF
$filesystem->tempnam('/tmp', 'prefix_');
// возвращает путь вроде : /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 user@example.com');
// третий аргумент сообщает, должен ли файл снова быть заблокирован при внесении записей в него
$filesystem->appendToFile('logs.txt', 'Email sent to user@example.com', true);

Если либо файл, либо каталог, содержащий его, не существует, то этот метод создаёт их перед добавлением содержания.

Утилиты для манипуляций с путями

Работа с путями файлов обычно вызывает некоторые сложности:

  • Разница платформ: пути файлов выглядят по-разному на разных платформах. Пути файлов UNIX начинаются со слеша ("/"), в то время, как пути файлов Windows начинаются с систмного диска ("C:"). UNIX использует слеш вправо, в то время, как Windows по умолчанию использует слеш влево.
  • Абсолютные/относительные пути: веб-приложениям часто необходимо разбираться с абсолютными и относительными путями. Преобразование одних в другие правильным образом является сложным и утомительным.

Path предоставляет служебные методы для работы с такими проблемами.

Каноникализация

Возвращает самое короткое имя пути, эквивалентное заданному пути. Итеративно применяет следующие правила до тех пор, пока дальнейшая обработка не станет невозможной:

  • удаляются сегменты ".";
  • разрешаются сегменты "..";
  • слеши влево ("") преобразуются в слеши вправо ("/");
  • корневые пути ("/" и "C:/") всегда заканчиваются слешем;
  • некорневые пути никогда не заканчиваются слешем;
  • схемы (такие, как "phar://") остаются;
  • " " заменяются домашним каталогом пользователя.

Вы можете каноникализировать путь с помощью 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

Используйте makeAbsolute() и makeRelative(), чтобы проверить, является путь относительным или абсолютным:

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
9
10
$paths = [
    '/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',
];

Path::getLongestCommonBasePath($paths);
// => /var/www/vhosts/project/httpdocs

Используйте этот путь вместе с makeRelative(), чтобы сократить сохраненные пути:

1
2
3
4
5
6
7
8
9
$bp = '/var/www/vhosts/project/httpdocs';

return [
    $bp.'/config/config.yaml',
    $bp.'/config/routing.yaml',
    $bp.'/config/services.yaml',
    $bp.'/images/banana.gif',
    $bp.'/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:/

Обработка ошибок

Когда что-то идёт не так, вызывается исключение, реализующее ExceptionInterface или IOExceptionInterface.

Note

Если создание каталога проходит неуспешно, вызывается IOException.