alias as written => FQCN, in declaration order */ public array $aliases = []; public ClassLike $classLike; /** @var list */ public array $lines; public function __construct( public readonly string $path, public readonly string $expectedFqcn, Parser $parser, ) { $code = file_get_contents($path); if ($code === false) { throw new GenerationError(["cannot read $path"]); } $this->lines = explode("\n", $code); $ast = $parser->parse($code); if ($ast === null) { throw new GenerationError(["cannot parse $path"]); } $traverser = new NodeTraverser(); $traverser->addVisitor(new NameResolver(null, ['replaceNodes' => false])); $ast = $traverser->traverse($ast); $classLike = null; foreach ($ast as $stmt) { if (!$stmt instanceof Namespace_) { continue; } $this->namespace = $stmt->name?->toString(); foreach ($stmt->stmts as $inner) { if ($inner instanceof Use_) { if ($inner->type !== Use_::TYPE_NORMAL) { throw new GenerationError(["$path: function/const use statements are not supported"]); } foreach ($inner->uses as $use) { $this->aliases[$use->getAlias()->toString()] = $use->name->toString(); } } elseif ($inner instanceof GroupUse) { throw new GenerationError(["$path: group use statements are not supported"]); } elseif ($inner instanceof ClassLike) { $fqcn = ($this->namespace === null ? '' : $this->namespace . '\\') . $inner->name?->toString(); if ($fqcn === $expectedFqcn) { $classLike = $inner; } } } } if ($classLike === null) { throw new GenerationError(["$path does not declare $expectedFqcn"]); } $this->classLike = $classLike; } /** The declaration's verbatim source text, without any leading doc comment. */ public function verbatim(int $startLine, int $endLine): string { return implode("\n", array_slice($this->lines, $startLine - 1, $endLine - $startLine + 1)); } }