strpbrk($n, '*?') !== false)); return new Excludes(true, array_values(array_diff($names, $patterns)), [], 'root/', $patterns); } public static function is_wordpress(string $dir): bool { return is_file($dir . '/wp-config.php') || is_file($dir . '/wp-load.php') || is_dir($dir . '/wp-includes'); } public static function is_server_setting(string $name): bool { return in_array(strtolower($name), self::SERVER_SETTINGS, true); } /** * The root's own top-level items, sorted by name, with their sizes. Sizes are summed until $seconds run out; * an item not fully measured by then says "more". Another WordPress site is flagged and not measured. * * @return array */ public static function items(array $roots, float $seconds = 2.0): array { $root = self::dir(); $excludes = self::excludes($roots); $deadline = microtime(true) + $seconds; $items = []; foreach (Inventory::top_level($root, $excludes) as $name) { $path = $root . '/' . $name; $dir = is_dir($path); $wordpress = $dir && self::is_wordpress($path); [$bytes, $more] = $wordpress ? [0, false] : self::size($path, $name, $excludes, $deadline); $items[] = ['name' => $name, 'dir' => $dir, 'bytes' => $bytes, 'more' => $more, 'wordpress' => $wordpress]; } return $items; } /** @return string[] the items left out unless chosen: other WordPress sites */ public static function default_skip(array $items): array { return array_column(array_filter($items, static fn ($i) => !empty($i['wordpress'])), 'name'); } /** @return array{0: int, 1: bool} bytes, and whether time ran out before everything was counted */ private static function size(string $path, string $name, Excludes $excludes, float $deadline): array { if (microtime(true) >= $deadline) { return [0, true]; } if (!is_dir($path)) { return [is_file($path) ? (int) filesize($path) : 0, false]; } $bytes = 0; $it = new \RecursiveIteratorIterator(new \RecursiveCallbackFilterIterator( new \RecursiveDirectoryIterator($path, \FilesystemIterator::SKIP_DOTS), static fn (\SplFileInfo $f) => !$f->isLink() && !$excludes->excluded($name . '/' . substr(wp_normalize_path($f->getPathname()), strlen($path) + 1)) )); try { foreach ($it as $file) { if (microtime(true) >= $deadline) { return [$bytes, true]; } $bytes += $file->isFile() ? (int) $file->getSize() : 0; } } catch (\UnexpectedValueException $e) { return [$bytes, true]; // an unreadable folder: what was counted so far } return [$bytes, false]; } }