}> Tracking data */ protected array $tracking_data = []; protected static string $tracker_file = BUILD_ROOT_PATH . '/.build.json'; protected ?DirDiff $current_diff = null; protected ?string $current_package = null; protected ?string $current_type = null; public function __construct() { $this->loadTrackingData(); } /** * Start tracking for a package. * * @param Package $package The package to track * @param string $type The operation type: 'build' or 'install' */ public function startTracking(Package $package, string $type = 'build'): void { $this->current_package = $package->getName(); $this->current_type = $type; $this->current_diff = new DirDiff(BUILD_ROOT_PATH, false); } /** * Stop tracking and record the changes. */ public function stopTracking(): void { if ($this->current_diff === null || $this->current_package === null) { return; } $increment_files = $this->current_diff->getIncrementFiles(true); if ($increment_files !== []) { // Remove buildroot prefix if exists and normalize paths $normalized_files = array_map(function ($file) { // Remove leading slashes return ltrim($file, '/\\'); }, $increment_files); $this->tracking_data[$this->current_package] = [ 'package' => $this->current_package, 'type' => $this->current_type, 'files' => array_values($normalized_files), 'time' => date('Y-m-d H:i:s'), ]; $this->saveTrackingData(); } $this->current_diff = null; $this->current_package = null; $this->current_type = null; } /** * Get tracking data for a specific package. * * @param string $package_name Package name * @return null|array Tracking data or null if not found */ public function getPackageTracking(string $package_name): ?array { return $this->tracking_data[$package_name] ?? null; } /** * Get all tracking data. * * @return array All tracking data */ public function getAllTracking(): array { return $this->tracking_data; } /** * Find which package introduced a specific file. * * @param string $file File path (relative to buildroot) * @return null|string Package name or null if not found */ public function findFileSource(string $file): ?string { $file = ltrim($file, '/\\'); foreach ($this->tracking_data as $package_name => $data) { if (in_array($file, $data['files'], true)) { return $package_name; } } return null; } /** * Clear tracking data for a specific package. * * @param string $package_name Package name */ public function clearPackageTracking(string $package_name): void { unset($this->tracking_data[$package_name]); $this->saveTrackingData(); } /** * Clear all tracking data. */ public function clearAllTracking(): void { $this->tracking_data = []; $this->saveTrackingData(); } /** * Get tracking statistics. * * @return array{total_packages: int, total_files: int, by_type: array} */ public function getStatistics(): array { $total_files = 0; $by_type = []; foreach ($this->tracking_data as $data) { $total_files += count($data['files']); $type = $data['type']; $by_type[$type] = ($by_type[$type] ?? 0) + 1; } return [ 'total_packages' => count($this->tracking_data), 'total_files' => $total_files, 'by_type' => $by_type, ]; } /** * Get the tracker file path. */ public static function getTrackerFilePath(): string { return self::$tracker_file; } /** * Load tracking data from file. */ protected function loadTrackingData(): void { if (is_file(self::$tracker_file)) { $content = file_get_contents(self::$tracker_file); $data = json_decode($content, true); if (is_array($data)) { $this->tracking_data = $data; } } } /** * Save tracking data to file. */ protected function saveTrackingData(): void { FileSystem::createDir(dirname(self::$tracker_file)); $content = json_encode($this->tracking_data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); FileSystem::writeFile(self::$tracker_file, $content); } }