Directories

Directories and Recursive Traversal

mkdir($path, 0775, true) creates missing parents, rmdir() removes only an empty directory, scandir() returns sorted names with . and .., and glob() matches shell patterns. For trees, RecursiveDirectoryIterator inside RecursiveIteratorIterator (SPL Iterators) yields an SplFileInfo per entry.

scandir(), glob(), totals from SplFileInfo, and deleting a tree child-firstPHP
<?php
declare(strict_types=1);
mkdir(($root = __DIR__ . '/media') . '/covers/2026', 0775, true);
foreach (['covers/dune.jpg', 'covers/2026/emma.png', 'odes.pdf'] as $i => $f) {
  file_put_contents("$root/$f", str_repeat('x', 100 * ($i + 1)));
}
echo implode(' ', scandir($root)), ' | ', count(glob("$root/covers/*.jpg")), "\n";
$tree = fn(int $mode) => new RecursiveIteratorIterator(
  new RecursiveDirectoryIterator($root, FilesystemIterator::SKIP_DOTS), $mode);
$bytes = [];
foreach ($tree(RecursiveIteratorIterator::LEAVES_ONLY) as $info) {
  $bytes[$info->getExtension()] = ($bytes[$info->getExtension()] ?? 0) + $info->getSize();
}
foreach ($tree(RecursiveIteratorIterator::CHILD_FIRST) as $info) {
  $info->isDir() ? rmdir($info->getPathname()) : unlink($info->getPathname());
}
echo json_encode($bytes), ' ', var_export(rmdir($root), true), "\n";
Output
. .. covers odes.pdf | 1
{"pdf":300,"jpg":100,"png":200} true

CHILD_FIRST visits a directory after its contents, the only order in which rmdir() succeeds. The iterator yields on-disk order (note the keys), so sort when order matters. PHP 8.5 deprecates readdir() and closedir() without a handle; pass the one opendir() returned.