['php', 'js', 'css', 'webmanifest', 'svg', 'png', 'ico'], ]; $excludePrefixes = ['car/']; function collect_asset_files(string $root, array $scanRoots, array $excludePrefixes): array { $files = []; $rootPath = realpath($root); if (!$rootPath) { return $files; } foreach ($scanRoots as $relativeRoot => $extensions) { $base = realpath($rootPath . DIRECTORY_SEPARATOR . $relativeRoot); if (!$base || strpos($base, $rootPath) !== 0) { continue; } $allowed = array_fill_keys(array_map('strtolower', $extensions), true); $iterator = new RecursiveIteratorIterator( new RecursiveDirectoryIterator($base, FilesystemIterator::SKIP_DOTS) ); foreach ($iterator as $file) { if (!$file instanceof SplFileInfo || !$file->isFile()) { continue; } $path = $file->getRealPath(); if (!$path || strpos($path, $rootPath . DIRECTORY_SEPARATOR) !== 0) { continue; } $relativePath = str_replace(DIRECTORY_SEPARATOR, '/', ltrim(str_replace($rootPath, '', $path), DIRECTORY_SEPARATOR)); foreach ($excludePrefixes as $prefix) { if (strpos($relativePath, $prefix) === 0) { continue 2; } } $extension = strtolower($file->getExtension()); if (isset($allowed[$extension])) { $files[] = $path; } } } sort($files, SORT_STRING); return array_values(array_unique($files)); } clearstatcache(); $rootPath = realpath($root); $fingerprints = []; foreach (collect_asset_files($root, $scanRoots, $excludePrefixes) as $fullPath) { $relativePath = ltrim(str_replace($rootPath ?: $root, '', $fullPath), DIRECTORY_SEPARATOR); $fileHash = is_readable($fullPath) ? (hash_file('sha256', $fullPath) ?: 'hash-failed') : 'unreadable'; $fingerprints[] = implode('|', [ str_replace(DIRECTORY_SEPARATOR, '/', $relativePath), (string)filesize($fullPath), (string)filemtime($fullPath), $fileHash, ]); } echo json_encode([ 'result' => 'ok', 'service' => $service, 'version' => hash('sha256', implode("\n", $fingerprints)), 'count' => count($fingerprints), 'generatedAt' => time(), ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);