Примените косую черту к папкам с помощью FilesystemIterator
Наличие следующего фрагмента для рекурсивного сопоставления содержимого текущего каталога:
$files = new RecursiveIteratorIterator
(
new RecursiveDirectoryIterator('./',
FilesystemIterator::SKIP_DOTS | FilesystemIterator::UNIX_PATHS),
RecursiveIteratorIterator::SELF_FIRST
);
$files = array_values(array_map('strval', iterator_to_array($files)));
Возвращает что-то вроде этого:
Array
(
[0] => ./1.png
[1] => ./a.php
[2] => ./adminer
[3] => ./adminer/adminer.css
[4] => ./adminer/adminer.php
)
Есть ли способ, которым я могу получить RecursiveDirectoryIterator
/ FilesystemIterator
подражать GLOB_MARK
поведение, которое существует в glob()
функционировать? Из руководства:
GLOB_MARK - добавляет косую черту в каждый возвращенный каталог.
Я знаю, что мог бы подражать этому, просто делая:
foreach ($files as $key => $value)
{
$files[$key] .= (is_dir($value) ? '/' : '');
}
Но для этого потребуется (излишне?) Много раз ударить по диску. Я ищу способ быстро определить, является ли путь каталогом или обычным файлом, и завершающий слеш кажется идеальным решением.
Я планирую обойти десятки (если не сотни) тысяч файлов с этим, поэтому производительность имеет решающее значение.
Бонусный вопрос: есть ли способ получить только каталоги (рекурсивно)?
2 ответа
Вы можете создать свой собственный GlobMarkIterator
преимущества:
- Возвращает конечные слеши в каталог так же, как
GLOB_MARK
- Нет необходимости использовать
array_map
сstrval
преобразовать его в строку - Без дополнительных
foreach
цикл сis_dir
- Все так же быстро, как оригинал
- Да, я знаю, что я обманул
пример
$ri = new RecursiveIteratorIterator(new GlobMarkIterator('./', FilesystemIterator::SKIP_DOTS | FilesystemIterator::UNIX_PATHS), RecursiveIteratorIterator::SELF_FIRST);
$files = array_values(iterator_to_array($ri));
echo "<pre>";
print_r($files);
Выход
Array
(
[0] => ./test/backups/ <----------- Note ending slash
[1] => ./test/CSV/
[2] => ./test/CSV/abc.csv
[3] => ./test/final/
[4] => ./test/thumb/
[5] => ./test/thumb/a.png
[6] => ./test/thumb/s.svg
[7] => ./test/thumb/sample.svg
)
Бонусный вопрос: есть ли способ получить только каталоги (рекурсивно)?
Это должен был быть другой вопрос, но все же.... Я надеюсь, что вы не будете удовлетворены и не вознаградите за это
Решение:
$ri = new RecursiveIteratorIterator( new GlobMarkDirectory('./test'), RecursiveIteratorIterator::SELF_FIRST);
$dir = array_values(iterator_to_array($ri));
echo "<pre>";
print_r($dir);
Выход
Array
(
[0] => ./test/backups/
[1] => ./test/CSV/
[2] => ./test/final/
[3] => ./test/thumb/
)
Используемый класс
GlobMarkIterator
class GlobMarkIterator extends RecursiveDirectoryIterator {
function current() {
return $this->isDir() ? $this->getPathname() . "/" : $this->getPathname();
}
}
GlobMarkDirectory
Учебный класс
class GlobMarkDirectory extends RecursiveFilterIterator {
public function __construct($path) {
parent::__construct(new GlobMarkIterator($path, FilesystemIterator::SKIP_DOTS | FilesystemIterator::UNIX_PATHS));
}
public function accept() {
return $this->getInnerIterator()->isDir();
}
public function getChildren() {
return new GlobMarkDirectory($this->getInnerIterator()->getPathname());
}
}
РЕДАКТИРОВАТЬ.. если вас не волнует empty dir
и вы не хотите использовать isDir
из-за скорости и накладных расходов, вот еще одно решение
Решение
$ri = new RecursiveIteratorIterator(new GlobMarkFastDirectory (__DIR__), RecursiveIteratorIterator::SELF_FIRST);
$dir = array_values(array_unique(iterator_to_array($ri)));
GlobMarkFastDirectory
class GlobMarkFastDirectory extends RecursiveDirectoryIterator {
function current() {
return dirname($this->getPathname()) ."/";
}
}
Это мое лучшее усилие на данный момент:
$files = new RecursiveIteratorIterator
(
new RecursiveDirectoryIterator
(
str_replace('\\', '/', realpath('./')),
FilesystemIterator::SKIP_DOTS | FilesystemIterator::UNIX_PATHS
),
RecursiveIteratorIterator::LEAVES_ONLY
);
$files = array_keys(iterator_to_array($files));
$folders = array();
/*
foreach ($files as $key => $value) // not needed anymore
{
$files[$key] .= (is_dir($value) === true) ? '/' : '';
}
*/
$files = array_flip($files);
foreach ($files as $key => $value)
{
$folder = dirname($key) . '/'; // doesn't issue a stat call
if (array_key_exists($folder, $folders) !== true)
{
$folders[$folder] = 0;
}
$folders[$folder] += $files[$key] = sprintf('%u', filesize($key));
}
Я мог бы объединить $folders
массив с $files
массив, чтобы ответить на вопрос точно, но выделение одного из других было именно моей главной целью, так что нет смысла делать это.