Skip to content

Do not inline overridable methods of abstract and @api classes in the phar build - #6412

Merged
ondrejmirtes merged 1 commit into
2.2.xfrom
inliner-extensible-classes
Sep 10, 2026
Merged

Do not inline overridable methods of abstract and @api classes in the phar build#6412
ondrejmirtes merged 1 commit into
2.2.xfrom
inliner-extensible-classes

Conversation

@ondrejmirtes

Copy link
Copy Markdown
Member

Fixes the shipmonk/dead-code-detector integration job and the phpstan-strict-rules / phpstan-symfony / phpstan-doctrine extension-tests jobs on phpstan/phpstan 2.2.x, failing since the phar built from 9f7d822 (last green: d8ed7dd).

Root cause: cd5c22a (getter inlining in the phar build) treated src/ + bundled vendor as a closed world and inlined any non-final method nothing there overrides. Third-party subclasses were invisible to that scan, and the affected classes are the ones whose hooks exist to be overridden:

  • RuleTestCase::getCollectors()new CollectorRegistry([]) in the phar, so dead-code-detector's collectors never ran and its CollectedDataNode rule was never dispatched (375 failing tests).
  • RuleTestCase::shouldPolluteScopeWithLoopInitialAssignments()true (phpstan-strict-rules).
  • ObjectType::describeAdditionalCacheKey()'' inside ObjectType's cache-key builder, while phpstan-symfony overrides it in ParentObjectType/TreeBuilderType.

Fix: InlineCallCollector::isExtensible() keeps abstract classes and non-final @api classes out of the closed world, for callees and for properties an inlined body would make public. Final classes, final/private methods and non-@api concrete classes (MutatingScope, php-parser and BetterReflection value classes) are inlined as before. On this tree 7,177 of 8,578 call sites remain; the 1,401 dropped sit in 33 classes, mostly ConstantStringType/ConstantArrayType/ConstantIntegerType getters.

Verification with a phar built from this branch:

Suite Current 2.2.x phar This branch
shipmonk/dead-code-detector @ CI ref 375 failures / 809 OK (809)
phpstan-strict-rules 1 failure OK (67)
phpstan-symfony OK (447)

New unit test tests/PHPStan/Build/InlineCallCollectorTest.php pins the rule against a fixture world; without the guard it reports AbstractHooks::getHooks => [] and ApiGetter::describeAdditionalCacheKey => ''.

🤖 Generated with Claude Code

https://claude.ai/code/session_01UMpKB12KsL5HB8iUMCgrUM

… phar build

The getter inliner treated src/ and the bundled vendor packages as a closed
world: a non-final method nothing there overrides was inlined at PHPStan's
own call sites. Third-party subclasses were the blind spot, and the classes
they subclass are exactly the ones whose hooks exist to be overridden:
RuleTestCase::getCollectors() became `new CollectorRegistry([])` in the
phar (shipmonk/dead-code-detector reported nothing),
shouldPolluteScopeWithLoopInitialAssignments() became `true`
(phpstan-strict-rules), ObjectType::describeAdditionalCacheKey() became ''
(phpstan-symfony overrides it in ParentObjectType and TreeBuilderType).

The closed world now stops at the extension surface: an abstract class or a
non-final class tagged @api may be subclassed by extensions under the
backward compatibility promise, so its overridable methods stay calls and
its properties are not made public. Final classes, final and private
methods and non-@api concrete classes are inlined as before. 7,177 of
8,578 call sites remain; the 1,401 dropped are in 33 classes, mostly the
constant type getters.

The scanned directories are an optional constructor argument so the
collector can be tested against a fixture world.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UMpKB12KsL5HB8iUMCgrUM
@ondrejmirtes
ondrejmirtes merged commit 8225ce9 into 2.2.x Sep 10, 2026
799 of 805 checks passed
@ondrejmirtes
ondrejmirtes deleted the inliner-extensible-classes branch September 10, 2026 15:59
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