Parse a PHP file from disk (or from a phar archive), cache the results, and return the extracted classes. Convenience wrapper around [`parse_and_cache_content`] that reads the file and derives a URI from the path. Used by [`find_or_load_class`] (Phases 1.5 and 2) and by the go-to-implementation scanner. Phar support:** when `file_path` contains a `!` separator (e.g. `/path/to/phpstan.phar!src/T
(&self, file_path: &Path)
| 257 | /// of reading from disk. The URI uses a `phar://` scheme so that |
| 258 | /// go-to-definition can distinguish phar-sourced classes. |
| 259 | pub(crate) fn parse_and_cache_file(&self, file_path: &Path) -> Option<Vec<Arc<ClassInfo>>> { |
| 260 | let path_str = file_path.to_str().unwrap_or_default(); |
| 261 | |
| 262 | // ── Phar path: "archive.phar!internal/path.php" ───────── |
| 263 | if let Some(sep) = path_str.find('!') { |
| 264 | let phar_path = Path::new(&path_str[..sep]); |
| 265 | let internal_path = &path_str[sep + 1..]; |
| 266 | |
| 267 | let uri = format!("phar://{}/{}", phar_path.display(), internal_path); |
| 268 | |
| 269 | // Deduplicate concurrent parses of the same phar entry. |
| 270 | if !self.parse_inflight.lock().insert(uri.clone()) { |
| 271 | return self.wait_for_cached_result(&uri); |
| 272 | } |
| 273 | let result = (|| { |
| 274 | let archives = self.phar_archives.read(); |
| 275 | let archive = archives.get(phar_path)?; |
| 276 | let bytes = archive.read_file(internal_path)?; |
| 277 | let content = std::str::from_utf8(bytes).ok()?; |
| 278 | self.parse_and_cache_content(content, &uri) |
| 279 | })(); |
| 280 | self.parse_inflight.lock().remove(&uri); |
| 281 | return result; |
| 282 | } |
| 283 | |
| 284 | // ── Regular file path ─────────────────────────────────── |
| 285 | let uri = crate::util::path_to_uri(file_path); |
| 286 | |
| 287 | // Deduplicate concurrent parses of the same file. |
| 288 | if !self.parse_inflight.lock().insert(uri.clone()) { |
| 289 | return self.wait_for_cached_result(&uri); |
| 290 | } |
| 291 | let content = std::fs::read_to_string(file_path).ok(); |
| 292 | let result = content.and_then(|c| self.parse_and_cache_content(&c, &uri)); |
| 293 | self.parse_inflight.lock().remove(&uri); |
| 294 | result |
| 295 | } |
| 296 | |
| 297 | /// Spin-wait for another thread to finish parsing a file and return |
| 298 | /// the cached result from `uri_classes_index`. |
no test coverage detected