Skip to content

Extract stubs from PHP 8.6 - #163

Open
SanderMuller wants to merge 3 commits into
phpstan:mainfrom
SanderMuller:add-php-86
Open

Extract stubs from PHP 8.6#163
SanderMuller wants to merge 3 commits into
phpstan:mainfrom
SanderMuller:add-php-86

Conversation

@SanderMuller

Copy link
Copy Markdown
Contributor

Adds a PHP 8.6 extraction step, and fixes what that step exposed in the extractor.

8.6 has no branch of its own in php-src yet, so the step checks out master. It reports
PHP_VERSION "8.6.0-dev" today.

The extractor drops properties and enum cases

The class merge in compareStatements() collects ClassMethod and ClassConst from the old statements
and puts them back. Property and EnumCase are collected by neither, so they are lost whenever a class
carries them.

Untagged members go into $oldStmts, which only methods and constants are read back out of. Members that
already carry #[\Since] land in $untouchedStmts and survive, which is why this has stayed invisible.

Running 8.5 to 8.6 without the fix loses real members, all of which are unchanged in php-src master:

stub lost
Uri/WhatWg/UrlValidationErrorType all 29 enum cases
Uri/WhatWg/UrlValidationError 3 properties
Uri/UriComparisonMode 2 enum cases
Uri/WhatWg/InvalidUrlException public readonly array $errors
Zend/NoDiscard public readonly ?string $message
ext/curl/CurlSharePersistentHandle public readonly array $options

The fix mirrors the constants path. A member missing from the old version gets #[\Since], and members
keep their place above the methods.

The diff after the fix

I ran the extraction locally against php-src master to see it before CI does:

  • 71 new stub files
  • 167 modified, of which 155 are additions only and 11 are pure reordering
  • 1 file with a net deletion, ext/spl/DirectoryIterator, and that one is a PHPDoc reformat:
    /** @return int|false */ becomes a block with @tentative-return-type and @return (int | false).

Before the extractor fix the same run produced 7 files with net deletions. After it, none that lose a
member.

Checked afterwards: UrlValidationErrorType has the same 29 cases as master, and NoDiscard keeps its
property.

No stubs are committed here, since the workflow commits those itself on main.

Comment thread .github/workflows/update.yml Fixed
@ondrejmirtes

Copy link
Copy Markdown
Member

Why do we need properties and enum cases? PHPStan will not make use of this information in my opinion. It's just more things to parse.

Also I don't understand why the diff moves stuff around. For example for DateTime.php, there's:


 class DateTime implements \DateTimeInterface
 {
+    /**
+     * @tentative-return-type
+     * @return void
+     */
+    #[\Until('8.5')]
+    public function __wakeup()
+    {
+    }
     public function __construct(string $datetime = "now", ?DateTimeZone $timezone = null)
     {
     }
@@ -13,14 +21,6 @@ class DateTime implements \DateTimeInterface
     public function __unserialize(array $data): void
     {
     }
-    /**
-     * @tentative-return-type
-     * @return void
-     */
-    #[\Until('8.5')]
-    public function __wakeup()
-    {
-    }

We should work on so that the order is stable.

If you want to order things first as a preparatory PR (while still going up to only 8.5), that'd be okay if it'd help make the diff cleaner and help us understand what really changes for PHP 8.6.

@SanderMuller

Copy link
Copy Markdown
Contributor Author

You are right about properties and enum cases, and I checked rather than argue. Php8SignatureMapProvider
walks $class->getNode()->stmts and reads only ClassMethod and ClassConst, plus Function_ for the
function map. Nothing else in phpstan-src reads these files. So the members I preserved are parsed and never
used, and I will drop that commit.

One consequence to confirm: without it, the 8.5 to 8.6 run deletes members that are in the stubs today, in
7 files. Uri\WhatWg\UrlValidationErrorType loses all 29 enum cases, UrlValidationError 3 properties,
Zend\NoDiscard its $message, and so on. Fine by your reasoning, and I would rather you say so than
assume it.

The ordering

Found it, and it explains your DateTime example exactly.

filterStatementsByVersion() splits the old class body in two and returns [$oldStmts, $newStmts]. The
caller takes that as [$untouchedStmts, $oldStmts] and then builds the new body as:

$newStmtsToSet = $untouchedStmts;   // everything outside the current version window
foreach ($newMethods as $stmt) { ... }
foreach ($newConstants as $stmt) { ... }

So the body is rebuilt by category, not in source order, and whatever is outside the window is written
first.

DateTime::__wakeup carries #[\Until('8.5')]. Going from 8.4 its untilId is 80499, which is not below
80400, so it stays with the ordinary statements. Going from 8.5 it is below 80500, so it moves into
$untouchedStmts and is emitted before every method. That is the jump to the top of the class in your
diff.

It follows that the churn grows at each version step, because more members age out of the window each time.

I will send the preparatory PR you offered, still going up to 8.5 only. It keeps the body in source order
and splices the aged-out members back where they were. Then this PR rebases on it, and the 8.6 diff should
be new symbols and real signature changes.

SanderMuller and others added 3 commits September 10, 2026 10:56
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@SanderMuller

Copy link
Copy Markdown
Contributor Author

Rebased on the reorder, so the 8.6 diff has no reordering left in it: 82 modified files and 71 new stubs.

I dropped the extractor change that kept properties and enum cases, as you asked. Seven files lose members
because of it. Happy to put it back if you'd rather.

The only extractor change left is the parser one. php-src master uses asymmetric visibility in the dom
stubs, and the PHP 8.3 the workflow installs cannot parse it.

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.

3 participants