Skip to content

Load genuinely-new native classes on every PHP version, tagged with @since - #12008

Merged
danog merged 3 commits into
vimeo:6.xfrom
danog:versioned-native-class-stubs
Sep 28, 2026
Merged

danog merged 3 commits into
vimeo:6.xfrom
danog:versioned-native-class-stubs

Conversation

@danog

@danog danog commented Sep 28, 2026 •

Copy link
Copy Markdown
Collaborator

Part 2 of the split of #11974; #12006 and #12007 are merged.

With the always-loaded @since 8.5 stub, #[\NoDiscard] is reported as undefined at 8.0 on every runtime PHP version, including PHP 8.5, where the class comes from reflection. The runtime-dependent NoDiscardTest case was dropped from 6.x, so that coverage returns here as attributeBelowItsVersion.

Summary

Classes introduced by PHP 8.0–8.5 used to live in the version-gated Php80/Php81/Php82/Php85 stubs. Below their version, analysis knew nothing about them. They now live in the always-loaded stubs/PhpVersionedClasses.phpstub, each with an @since tag. Analysis knows their shape on every version, and with #12006 a use below that version is reported as undefined unless polyfilled.

The moved classes:

  • Stringable
  • ReflectionAttribute, ReflectionUnionType, ReflectionIntersectionType
  • ReflectionEnum, ReflectionEnumUnitCase, ReflectionEnumBackedCase
  • UnhandledMatchError
  • CurlHandle, CurlMultiHandle, CurlShareHandle, CurlSharePersistentHandle
  • UnitEnum, BackedEnum
  • the resource→object classes: FTP\Connection, IMAP\Connection, LDAP\*, PgSql\*, PSpell\*
  • SensitiveParameterValue
  • NoDiscard

What stays version-gated

  • Reopens of pre-existing classes stay in Php8X.phpstub.
  • Refined signatures of pre-existing functions (get_headers and pack in Php80Functions, str_split in Php82Functions) and the #[Attribute] meta-class (Php80Attribute) are split out and stay gated, so their stricter typing does not apply on older versions.
  • Php85.phpstub is removed.
  • When the running PHP is older than 8.5, PhpVersionedClasses.phpstub is also preloaded, replacing the old Php85.phpstub preload.

Parsing note

The always-loaded stub is parsed with the grammar of the analysed version, and readonly is only a keyword from 8.1 on. The stub therefore uses @readonly (and @psalm-immutable classes) instead of the keyword. Otherwise older parsers drop e.g. UnitEnum::$name, and the property comes out mixed.

Tests

NativeSymbolAvailabilityTest gains cases for:

  • an interface used below its version
  • a project polyfill of Stringable
  • property availability inherited from the class
  • the stub shape (UnitEnum::$name) being kept below 8.1

A scratch project with symfony/polyfill-php80 installed, analysed at 7.4, reports nothing for Stringable or UnhandledMatchError.

🤖 Generated with Claude Code

@danog danog added the release:feature The PR will be included in 'Features' section of the release notes label Sep 28, 2026
@danog
danog force-pushed the versioned-native-class-stubs branch from 4c697d0 to 41cc692 Compare September 28, 2026 12:12
…SInCE

Classes introduced by PHP 8.0-8.5 (Stringable, ReflectionAttribute,
ReflectionUnionType, UnhandledMatchError, the Curl handles, the enum interfaces
and reflection classes, ReflectionIntersectionType, the resource->object classes,
SensitiveParameterValue, NoDiscard, CurlSharePersistentHandle) move to the
always-loaded stubs/PhpVersionedClasses.phpstub with an `@since` tag. Analysis
therefore knows their shape on every version, and a use below that version is
reported as undefined unless polyfilled.

Reopens of pre-existing classes stay in the version-gated Php8X.phpstub files, as
do the refined signatures of pre-existing functions (Php80Functions: get_headers,
pack; Php82Functions: str_split) and the #[Attribute] meta-class (Php80Attribute),
so their stricter typing is not applied on older versions. Php85.phpstub is gone.

PhpVersionedClasses.phpstub is parsed with the grammar of the analysed version,
so its readonly properties are declared with `@readonly` instead of the 8.1
keyword, which would otherwise make older parsers drop them.

Co-Authored-By: Claude Opus 5.5 
danog and others added 2 commits September 28, 2026 14:42
On a PHP 8.5 runtime this only holds with the always-loaded @SInCE stub.

Co-Authored-By: Claude Opus 5.5 
@danog
danog merged commit cd08783 into vimeo:6.x Sep 28, 2026
61 checks passed
@danog danog mentioned this pull request Sep 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release:feature The PR will be included in 'Features' section of the release notes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant