PHP: Деревья

Теория: Манипуляции с виртуальной файловой системой

Библиотека, которая используется для построения деревьев, рассчитана только на неизменяемые файловые структуры. То есть уже после создания ее поменять нельзя. Но можно на основе старой структуры сделать новую, в которой какие-то части будут изменены.

Неизменяемая структура выбрана для этого курса не случайно. Такую структуру легче отлаживать и меньше шансов допустить ошибки. И она позволяет максимально погрузиться в использование функций высшего порядка.

Базовые операции с узлами

Пакет php-immutable-fs-trees позволяет не только создавать, но и извлекать данные из уже созданных файлов и директорий. Функции этой библиотеки позволяют не лезть во внутреннюю структуру самого дерева:

<?php

use function Php\Immutable\Fs\Trees\trees\mkdir;
use function Php\Immutable\Fs\Trees\trees\mkfile;
use function Php\Immutable\Fs\Trees\trees\getChildren;
use function Php\Immutable\Fs\Trees\trees\getName;
use function Php\Immutable\Fs\Trees\trees\getMeta;

$tree = mkdir('/', [mkfile('hexlet.log')], ['hidden' => true]);
getName($tree); // '/'
getMeta($tree)['hidden']; // true

[$file] = getChildren($tree);
getName($file); // 'hexlet.log'
isset(getMeta($file)['unknown']); // false

// А вот так делать не надо
// У файлов нет детей
getChildren($file);

Дополнительно в пакете есть две функции для проверки типа. С их помощью можно выборочно работать с файлами и директориями:

<?php

use function Php\Immutable\Fs\Trees\trees\mkdir;
use function Php\Immutable\Fs\Trees\trees\mkfile;
use function Php\Immutable\Fs\Trees\trees\getChildren;
use function Php\Immutable\Fs\Trees\trees\getName;
use function Php\Immutable\Fs\Trees\trees\getMeta;
use function Php\Immutable\Fs\Trees\trees\isDirectory;
use function Php\Immutable\Fs\Trees\trees\isFile;

$tree = mkdir('/', [mkfile('hexlet.log')], ['hidden' => true]);
isDirectory($tree); // true
isFile($tree); // false

[$file] = getChildren($tree);
isFile($file); // true
isDirectory($file); // false

Рассмотренных операций хватит для выполнения любых преобразований над файлами и директориями. Начнем с самых простых, которые не требуют рекурсивного обхода.

Обработка

Любая обработка в неизменяемом стиле сводится к формированию новых данных на основе старых. Ниже мы реализуем некоторые варианты преобразования, раскрывающие эту идею.

Изменение имени файла

<?php

$file = mkfile('one', ['size' => 35]);
$newFile = mkfile('new name', getMeta($file));

Фактически здесь создается новый файл с метаданными старого.

Сортировка содержимого директории

<?php

// Сортировка в обратном порядке
$tree = mkdir('/', [
    mkfile('one'),
    mkfile('two'),
    mkdir('three'),
]);

$children = getChildren($tree);
$newChildren = array_reverse($children);
$tree2 = mkdir(getName($tree), $newChildren, getMeta($tree));
// [
//     'name' => '/',
//     'type' => 'directory',
//     'meta' => [],
//     'children' => [
//         ['name' => 'three', 'type' => 'directory', 'meta' => [], 'children' => []],
//         ['name' => 'two', 'type' => 'file', 'meta' => []],
//         ['name' => 'one', 'type' => 'file', 'meta' => []]
//     ]
// ]

Обновление содержимого директории

<?php

// Приведение к нижнему регистру имен директорий и файлов
// внутри конкретной директории

$tree = mkdir('/', [
    mkfile('oNe'),
    mkfile('Two'),
    mkdir('THREE'),
]);

$children = getChildren($tree);
$newChildren = array_map(function ($child) {
    $name = getName($child);
    if (isDirectory($child)) {
        return mkdir(strtolower($name), getChildren($child), getMeta($child));
    }
    return mkfile(strtolower($name), getMeta($child));
}, $children);

$tree2 = mkdir(getName($tree), $newChildren, getMeta($tree));
// [
//     'name' => '/',
//     'type' => 'directory',
//     'meta' => [],
//     'children' => [
//         ['name' => 'one', 'type' => 'file', 'meta' => []],
//         ['name' => 'two', 'type' => 'file', 'meta' => []],
//         ['name' => 'three', 'type' => 'directory', 'meta' => [], 'children' => []]
//     ]
// ]

Удаление файлов внутри директории

<?php

$tree = mkdir('/', [
    mkfile('one'),
    mkfile('two'),
    mkdir('three'),
]);

$children = getChildren($tree);
$newChildren = array_filter($children, fn($child) => isDirectory($child));
$tree2 = mkdir(getName($tree), $newChildren, getMeta($tree));
// [
//     'name' => '/',
//     'type' => 'directory',
//     'meta' => [],
//     'children' => [
//         ['name' => 'three', 'type' => 'directory', 'meta' => [], 'children' => []]
//     ]
// ]

Рекомендуемые программы

Завершено

0 / 9