Resolve a fully-qualified PHP class name to a file path using PSR-4 mappings. The `class_name` should be the namespace-qualified name (e.g. `"Klarna\\Customer"` or `"Klarna\\Rest\\Order"`). A leading `\` is stripped if present (PHP fully-qualified syntax). Returns the first path that exists on disk, or `None` if no mapping matches or the resolved file doesn't exist.
(
mappings: &[Psr4Mapping],
workspace_root: &Path,
class_name: &str,
)
| 390 | /// Returns the first path that exists on disk, or `None` if no mapping |
| 391 | /// matches or the resolved file doesn't exist. |
| 392 | pub fn resolve_class_path( |
| 393 | mappings: &[Psr4Mapping], |
| 394 | workspace_root: &Path, |
| 395 | class_name: &str, |
| 396 | ) -> Option<PathBuf> { |
| 397 | let name = class_name; |
| 398 | |
| 399 | // Skip built-in type keywords that are never real classes |
| 400 | if crate::php_type::is_keyword_type(name) { |
| 401 | return None; |
| 402 | } |
| 403 | |
| 404 | // Try each mapping (already sorted longest-prefix-first) |
| 405 | for mapping in mappings { |
| 406 | let relative = if mapping.prefix.is_empty() { |
| 407 | // Empty prefix matches everything (root namespace fallback) |
| 408 | Some(name) |
| 409 | } else { |
| 410 | name.strip_prefix(&mapping.prefix) |
| 411 | }; |
| 412 | |
| 413 | if let Some(relative_class) = relative { |
| 414 | // Convert namespace separators to directory separators |
| 415 | let relative_path = relative_class.replace('\\', "/"); |
| 416 | let file_path = workspace_root |
| 417 | .join(&mapping.base_path) |
| 418 | .join(format!("{}.php", relative_path)); |
| 419 | |
| 420 | if file_path.is_file() { |
| 421 | return Some(file_path); |
| 422 | } |
| 423 | } |
| 424 | } |
| 425 | |
| 426 | None |
| 427 | } |
| 428 | |
| 429 | /// Extract file paths from `require_once` statements in PHP source content. |
| 430 | /// |