Parse a `.phar` archive and register its PHP classes in the fqn_uri_index for lazy loading. The phar's raw bytes are read from disk, parsed by [`phar::PharArchive`], and stored in [`phar_archives`](crate::Backend::phar_archives). Each `.php` file inside the archive is scanned with the lightweight [`find_classes`](classmap_scanner::find_classes) byte scanner, and discovered classes are registered
(&self, phar_path: &Path)
| 1965 | /// to extract content from the phar instead of reading from disk) |
| 1966 | /// and a `phar://` URI for completions and workspace symbols |
| 1967 | fn scan_phar_archive(&self, phar_path: &Path) { |
| 1968 | // Avoid scanning the same phar twice. |
| 1969 | if self.phar_archives.read().contains_key(phar_path) { |
| 1970 | return; |
| 1971 | } |
| 1972 | |
| 1973 | let data = match std::fs::read(phar_path) { |
| 1974 | Ok(d) => d, |
| 1975 | Err(_) => return, |
| 1976 | }; |
| 1977 | |
| 1978 | let archive = match phar::PharArchive::parse(data) { |
| 1979 | Some(a) => a, |
| 1980 | None => { |
| 1981 | tracing::warn!("failed to parse phar archive: {}", phar_path.display()); |
| 1982 | return; |
| 1983 | } |
| 1984 | }; |
| 1985 | |
| 1986 | // Collect PHP file paths first so we can iterate while |
| 1987 | // holding the archive reference. |
| 1988 | let php_files: Vec<String> = archive |
| 1989 | .file_paths() |
| 1990 | .filter(|p| p.ends_with(".php")) |
| 1991 | .map(String::from) |
| 1992 | .collect(); |
| 1993 | |
| 1994 | let mut classmap_entries: Vec<(String, PathBuf)> = Vec::new(); |
| 1995 | let mut fqn_uri_entries: Vec<(String, String)> = Vec::new(); |
| 1996 | |
| 1997 | for internal_path in &php_files { |
| 1998 | if let Some(content) = archive.read_file(internal_path) { |
| 1999 | let classes = classmap_scanner::find_classes(content); |
| 2000 | for fqn in classes { |
| 2001 | // Sentinel path: "archive.phar!internal/path.php" |
| 2002 | let sentinel = |
| 2003 | PathBuf::from(format!("{}!{}", phar_path.display(), internal_path)); |
| 2004 | let phar_uri = format!("phar://{}/{}", phar_path.display(), internal_path); |
| 2005 | classmap_entries.push((fqn.clone(), sentinel)); |
| 2006 | fqn_uri_entries.push((fqn, phar_uri)); |
| 2007 | } |
| 2008 | } |
| 2009 | } |
| 2010 | |
| 2011 | let class_count = classmap_entries.len(); |
| 2012 | |
| 2013 | // Register classes in the fqn_uri_index. |
| 2014 | { |
| 2015 | let mut idx = self.fqn_uri_index.write(); |
| 2016 | for (fqn, path) in classmap_entries { |
| 2017 | idx.entry(fqn) |
| 2018 | .or_insert_with(|| crate::util::path_to_uri(&path)); |
| 2019 | } |
| 2020 | for (fqn, uri) in fqn_uri_entries { |
| 2021 | idx.entry(fqn).or_insert(uri); |
| 2022 | } |
| 2023 | } |
| 2024 |
no test coverage detected