Skip to content

Commit b46ef8c

Browse files
ondrejmirtesclaude
andcommitted
Ask DependencyResolver about exported nodes only for the node classes that can have one
FileAnalyserCallback called resolveExportedNode() for every node the walk reported. Now it asks once per node class whether a node of the class can be exported or change the PHPDoc name scope, and skips the call for the rest. resolveFileDependencies() now expands each class found in a file to its ancestors once, and reads the referenced classes of each type object once. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GdwtgiEHwkF5BTz73jgHQQ
1 parent 4cf9d34 commit b46ef8c

2 files changed

Lines changed: 52 additions & 8 deletions

File tree

‎src/Analyser/FileAnalyserCallback.php‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,14 @@ final class FileAnalyserCallback
5959
/** @var array<string, array<Collector<Node, mixed>>> */
6060
private array $collectorsByNodeType = [];
6161

62+
/**
63+
* Whether a node of the class can be exported or change the PHPDoc name scope - most cannot, and
64+
* the walk does not ask DependencyResolver about them at all.
65+
*
66+
* @var array<string, bool>
67+
*/
68+
private array $exportableByNodeType = [];
69+
6270
/**
6371
* @param callable(Node $node, Scope $scope): void|null $outerNodeCallback
6472
* @param Node\Stmt[] $parserNodes
@@ -234,6 +242,10 @@ public function __invoke(Node $node, Scope $scope): void
234242
$this->fileCollectedData[$scope->getFile()][get_class($collector)][] = $collectedData;
235243
}
236244

245+
if (!($this->exportableByNodeType[$nodeType] ??= $this->dependencyResolver->canExportNode($node))) {
246+
return;
247+
}
248+
237249
try {
238250
$exportedNode = $this->dependencyResolver->resolveExportedNode($node, $scope);
239251
if ($exportedNode !== null) {

‎src/Dependency/DependencyResolver.php‎

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use function array_key_exists;
1616
use function array_values;
1717
use function get_class;
18+
use function is_string;
1819
use function spl_object_id;
1920
use function str_starts_with;
2021

@@ -67,6 +68,14 @@ public function __construct(
6768
$this->nameScopeTracker = new NamespaceUsesTracker();
6869
}
6970

71+
/**
72+
* Whether resolveExportedNode() can do anything with a node of this class.
73+
*/
74+
public function canExportNode(Node $node): bool
75+
{
76+
return ($this->nodeProfiles[get_class($node)] ??= $this->resolveNodeProfile($node)) !== 0;
77+
}
78+
7079
/**
7180
* What the node declares, so that the result cache notices when what a file declares changes.
7281
*/
@@ -109,26 +118,34 @@ public function resolveFileDependencies(?Dependencies $dependencies, array $anal
109118
return new ResolvedDependencies([], [], [], []);
110119
}
111120

112-
/** @var array<string, array<int, ClassReflection|FunctionReflection|ConstantReflection>> $reflectionsByFile */
113-
$reflectionsByFile = [];
121+
// what each file depends on, in the order it was found: a class by its name, expanded to it and
122+
// its ancestors once below, however many times it was found, and a function or a constant
123+
/** @var array<string, array<string, string|FunctionReflection|ConstantReflection>> $foundByFile */
124+
$foundByFile = [];
125+
/** @var array<string, array<int, true>> $typesByFile */
126+
$typesByFile = [];
114127
/** @var array<string, array<int, ClassReflection>> $usedTraitsByFile */
115128
$usedTraitsByFile = [];
116129
/** @var array<string, string> $fileDependencies */
117130
$fileDependencies = [];
118-
$dependencies->walk(function (string $file, array $types, array $classNames, array $reflections, array $filePaths, array $usedTraits) use (&$reflectionsByFile, &$usedTraitsByFile, &$fileDependencies): void {
119-
$fileReflections = $reflectionsByFile[$file] ?? [];
131+
$dependencies->walk(static function (string $file, array $types, array $classNames, array $reflections, array $filePaths, array $usedTraits) use (&$foundByFile, &$typesByFile, &$usedTraitsByFile, &$fileDependencies): void {
120132
foreach ($types as $type) {
133+
$typeId = spl_object_id($type);
134+
if (isset($typesByFile[$file][$typeId])) {
135+
continue;
136+
}
137+
138+
$typesByFile[$file][$typeId] = true;
121139
foreach ($type->getReferencedClasses() as $className) {
122-
$fileReflections += $this->getClassDependencies($className);
140+
$foundByFile[$file]['c' . $className] ??= $className;
123141
}
124142
}
125143
foreach ($classNames as $className) {
126-
$fileReflections += $this->getClassDependencies($className);
144+
$foundByFile[$file]['c' . $className] ??= $className;
127145
}
128146
foreach ($reflections as $reflection) {
129-
$fileReflections[spl_object_id($reflection)] = $reflection;
147+
$foundByFile[$file]['r' . spl_object_id($reflection)] = $reflection;
130148
}
131-
$reflectionsByFile[$file] = $fileReflections;
132149
foreach ($filePaths as $filePath) {
133150
$fileDependencies[$filePath] = $filePath;
134151
}
@@ -137,6 +154,21 @@ public function resolveFileDependencies(?Dependencies $dependencies, array $anal
137154
}
138155
});
139156

157+
/** @var array<string, array<int, ClassReflection|FunctionReflection|ConstantReflection>> $reflectionsByFile */
158+
$reflectionsByFile = [];
159+
foreach ($foundByFile as $file => $found) {
160+
$fileReflections = [];
161+
foreach ($found as $item) {
162+
if (is_string($item)) {
163+
$fileReflections += $this->getClassDependencies($item);
164+
continue;
165+
}
166+
167+
$fileReflections[spl_object_id($item)] = $item;
168+
}
169+
$reflectionsByFile[$file] = $fileReflections;
170+
}
171+
140172
$packages = [];
141173
$classReflections = [];
142174
foreach ($reflectionsByFile as $file => $reflections) {

0 commit comments

Comments
 (0)