Skip to content

Share the resolved class constant type between the native and PHPDoc passes when the constant has no PHPDoc type - #6968

Merged
ondrejmirtes merged 1 commit into
2.3.xfrom
share-class-const-native-cache
Nov 6, 2026
Merged

ondrejmirtes merged 1 commit into
2.3.xfrom
share-class-const-native-cache

Conversation

@ondrejmirtes

Copy link
Copy Markdown
Member

Since 9b24ee5 ("Do not use PHPDoc type of overridable class constants as their native type", first in 2.3.3), getClassConstFetchTypeByReflection() caches the native-types pass under a separate <Class>::<CONST>#native key. As a result, a fetch like self::X or Foo::X resolves and transforms the constant's initializer twice, once for each pass. For huge constant arrays this costs a lot. Bisecting with instruction counts showed that bug-12671.php, bug-12159.php and bug-10538.php in tests/bench/data got slower at this commit.

In this branch (a fetch on a class name, not on an object), the PHPDoc type is the only input that differs between the two passes. ConstantResolver::resolveClassConstantType() only uses it for a dynamicConstantNames list entry without a native type.

Change

  • On a cache miss, the resolver now reads getConstantPhpDocType() once.
  • When the constant has no PHPDoc type, the resolved type is stored under both keys, the plain one and #native, so the second pass hits the cache.
  • Constants with a PHPDoc type keep separate entries as before, so the behaviour from 9b24ee5 is unchanged.
  • A cache hit is still a single array lookup.
  • The native mirror in turbo-ext/src/InitializerExprTypeResolver.cpp gets the identical change.

Instruction counts

Net instructions = cachegrind count of the file minus the count of an empty file at the same commit, so PHPStan's boot is excluded. Measured with PHP 8.4.27 at level 8 with bleedingEdge, without the turbo extension.

file 2.3.x (bcca2b0) this PR (e178292) change
bug-12671.php 14.97 G 10.24 G −31.6 %
bug-12159.php 3.63 G 3.09 G −14.9 %
bug-10538.php 20.43 G 17.19 G −15.9 %

For reference, on 2f22669, the parent of 9b24ee5, the counts are 10.25 G, 2.83 G and 17.09 G. bug-12671 and bug-10538 are back to that level. The remaining ~0.26 G on bug-12159 comes from later commits, not from 9b24ee5.

Wall-clock times

These are medians of 9 runs after 2 warm-up runs (hyperfine) of bin/phpstan analyse -c <config> --debug --no-progress <file> on 2.3.x (bcca2b0) and on this branch. The repository has no tests/bench/phpstan.neon, so the config is the equivalent of tests/bench/BenchCase.php: it includes conf/bleedingEdge.neon and sets level: 8 and inferPrivatePropertyTypeFromConstructor: true.

Machine: Amp orb (E2B sandbox), Intel Xeon @ 2.60GHz, 16 vCPUs. PHP 8.4.27 NTS without the turbo extension.

file 2.3.x this PR change
bug-12671.php 2.457 s 1.988 s −19.1 %
bug-12159.php 1.311 s 1.246 s −4.9 %
bug-10538.php 3.294 s 2.929 s −11.1 %

Tests

  • make tests passes without the extension. The only failures were two DirectoryWalkerTest cases that failed only under parallel load; that test passes when run alone and is unrelated to this change.
  • make tests passes with phpstan_turbo.so loaded.
  • turbo-ext/tests/smoke.php prints ALL OK, and turbo-ext/tests/signature-parity.php passes.
  • turbo-ext/bin/side-by-side.php pairs InitializerExprTypeResolver. Some generated headers are reported stale; that comes from the PHP 8.4 environment and also happens without this change.
  • phpcs (build-cs) on src/Reflection/InitializerExprTypeResolver.php is clean, and self-analysis of that file with build/phpstan.neon finds no errors.
  • For the three benchmark files, the errors are identical before and after the change, and identical with and without the extension.
  • tests/PHPStan/Analyser/nsrt/class-constant-native-type.php from 9b24ee5 still passes.

@ondrejmirtes
ondrejmirtes merged commit 1ba97a6 into 2.3.x Nov 6, 2026
1039 of 1043 checks passed
@ondrejmirtes
ondrejmirtes deleted the share-class-const-native-cache branch November 6, 2026 16:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant