mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-10 03:27:59 +00:00
feat(php): add PHP/PSR-4 import resolution with composer.json support
- Add .php, .phtml to EXTENSIONS array - Add ComposerConfig interface and loadComposerConfig() (mirrors TsconfigPaths pattern) - Add resolvePhpImport() with PSR-4 longest-match resolution + suffix fallback - Wire PHP handling into processImports() and processImportsFromExtracted() Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
2164dc22f1
commit
804d975cd0
1 changed files with 96 additions and 0 deletions
|
|
@ -101,6 +101,37 @@ async function loadGoModulePath(repoRoot: string): Promise<GoModuleConfig | null
|
|||
return null;
|
||||
}
|
||||
|
||||
/** PHP Composer PSR-4 autoload config */
|
||||
interface ComposerConfig {
|
||||
/** Map of namespace prefix -> directory (e.g., "App\\" -> "app/") */
|
||||
psr4: Map<string, string>;
|
||||
}
|
||||
|
||||
async function loadComposerConfig(repoRoot: string): Promise<ComposerConfig | null> {
|
||||
try {
|
||||
const composerPath = path.join(repoRoot, 'composer.json');
|
||||
const raw = await fs.readFile(composerPath, 'utf-8');
|
||||
const composer = JSON.parse(raw);
|
||||
const psr4Raw = composer.autoload?.['psr-4'] ?? {};
|
||||
const psr4Dev = composer['autoload-dev']?.['psr-4'] ?? {};
|
||||
const merged = { ...psr4Raw, ...psr4Dev };
|
||||
|
||||
const psr4 = new Map<string, string>();
|
||||
for (const [ns, dir] of Object.entries(merged)) {
|
||||
const nsNorm = (ns as string).replace(/\\+$/, '');
|
||||
const dirNorm = (dir as string).replace(/\\/g, '/').replace(/\/+$/, '');
|
||||
psr4.set(nsNorm, dirNorm);
|
||||
}
|
||||
|
||||
if (isDev) {
|
||||
console.log(`📦 Loaded ${psr4.size} PSR-4 mappings from composer.json`);
|
||||
}
|
||||
return { psr4 };
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// IMPORT PATH RESOLUTION
|
||||
// ============================================================================
|
||||
|
|
@ -122,6 +153,8 @@ const EXTENSIONS = [
|
|||
'.go',
|
||||
// Rust
|
||||
'.rs', '/mod.rs',
|
||||
// PHP
|
||||
'.php', '.phtml',
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
@ -551,6 +584,48 @@ function resolveGoPackage(
|
|||
return matches;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// PHP PSR-4 IMPORT RESOLUTION
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Resolve a PHP use-statement import path using PSR-4 mappings.
|
||||
* e.g. "App\Http\Controllers\UserController" -> "app/Http/Controllers/UserController.php"
|
||||
*/
|
||||
function resolvePhpImport(
|
||||
importPath: string,
|
||||
composerConfig: ComposerConfig | null,
|
||||
allFiles: Set<string>,
|
||||
normalizedFileList: string[],
|
||||
allFileList: string[],
|
||||
index?: SuffixIndex,
|
||||
): string | null {
|
||||
// Normalize: replace backslashes with forward slashes
|
||||
const normalized = importPath.replace(/\\/g, '/');
|
||||
|
||||
// Try PSR-4 resolution if composer.json was found
|
||||
if (composerConfig) {
|
||||
// Sort namespaces by length descending (longest match wins)
|
||||
const sorted = [...composerConfig.psr4.entries()].sort((a, b) => b[0].length - a[0].length);
|
||||
for (const [nsPrefix, dirPrefix] of sorted) {
|
||||
const nsPrefixSlash = nsPrefix.replace(/\\/g, '/');
|
||||
if (normalized.startsWith(nsPrefixSlash + '/') || normalized === nsPrefixSlash) {
|
||||
const remainder = normalized.slice(nsPrefixSlash.length).replace(/^\//, '');
|
||||
const filePath = dirPrefix + (remainder ? '/' + remainder : '') + '.php';
|
||||
if (allFiles.has(filePath)) return filePath;
|
||||
if (index) {
|
||||
const result = index.getInsensitive(filePath);
|
||||
if (result) return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: suffix matching (works without composer.json)
|
||||
const pathParts = normalized.split('/').filter(Boolean);
|
||||
return suffixResolve(pathParts, normalizedFileList, allFileList, index);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// MAIN IMPORT PROCESSOR
|
||||
// ============================================================================
|
||||
|
|
@ -581,6 +656,7 @@ export const processImports = async (
|
|||
const effectiveRoot = repoRoot || '';
|
||||
const tsconfigPaths = await loadTsconfigPaths(effectiveRoot);
|
||||
const goModule = await loadGoModulePath(effectiveRoot);
|
||||
const composerConfig = await loadComposerConfig(effectiveRoot);
|
||||
|
||||
// Helper: add an IMPORTS edge + update import map
|
||||
const addImportEdge = (filePath: string, resolvedPath: string) => {
|
||||
|
|
@ -705,6 +781,15 @@ export const processImports = async (
|
|||
// Fall through if no files found (package might be external)
|
||||
}
|
||||
|
||||
// ---- PHP: handle namespace-based imports (use statements) ----
|
||||
if (language === SupportedLanguages.PHP) {
|
||||
const resolved = resolvePhpImport(rawImportPath, composerConfig, allFilePaths, normalizedFileList, allFileList, index);
|
||||
if (resolved) {
|
||||
addImportEdge(file.path, resolved);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// ---- Standard single-file resolution ----
|
||||
const resolvedPath = resolveImportPath(
|
||||
file.path,
|
||||
|
|
@ -757,6 +842,7 @@ export const processImportsFromExtracted = async (
|
|||
const effectiveRoot = repoRoot || '';
|
||||
const tsconfigPaths = await loadTsconfigPaths(effectiveRoot);
|
||||
const goModule = await loadGoModulePath(effectiveRoot);
|
||||
const composerConfig = await loadComposerConfig(effectiveRoot);
|
||||
|
||||
const addImportEdge = (filePath: string, resolvedPath: string) => {
|
||||
const sourceId = generateId('File', filePath);
|
||||
|
|
@ -856,6 +942,16 @@ export const processImportsFromExtracted = async (
|
|||
}
|
||||
}
|
||||
|
||||
// PHP: handle namespace-based imports (use statements)
|
||||
if (language === SupportedLanguages.PHP) {
|
||||
const resolved = resolvePhpImport(rawImportPath, composerConfig, allFilePaths, normalizedFileList, allFileList, index);
|
||||
if (resolved) {
|
||||
resolveCache.set(cacheKey, resolved);
|
||||
addImportEdge(filePath, resolved);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Standard resolution (has its own internal cache)
|
||||
const resolvedPath = resolveImportPath(
|
||||
filePath,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue