Skip to content

Declare the five implicitly-nullable parameters nullable (PHP 8.4 readiness) - #24972

Merged
PurHur merged 1 commit into
masterfrom
fix/php84-implicit-nullable-params
Jul 29, 2026
Merged

PurHur merged 1 commit into
masterfrom
fix/php84-implicit-nullable-params

Conversation

@PurHur

@PurHur PurHur commented Jul 29, 2026

Copy link
Copy Markdown
Owner

PHP 8.4 deprecates Foo $x = null and writes the notice to stderr, which the compliance harness compares. On an 8.4 host, every case whose path touches one of these would differ from Zend on stderr alone.

ext/gettext/GettextFunction.php        int $max     -> ?int $max
lib/Func/Internal.php                  string $name -> ?string $name
lib/Visitor/ExitFunctionResolver.php   Block $prior -> ?Block $prior
lib/Visitor/InOperatorResolver.php     Block $prior -> ?Block $prior
lib/Visitor/VoidCastResolver.php       Block $prior -> ?Block $prior

?Type has been valid since PHP 7.1 and these declarations already accepted null, so behaviour is unchanged on every supported version.

Five, not the 37 I first reported

My first scan counted three things that are not deprecated:

  • typed properties — protected ?SplObjectStorage $seen = null;
  • unions already containing null — null|string|Variable|ClosureState $handler = null
  • mixed — mixed $x = null is not deprecated, and rewriting it to ?mixed is a fatal error

That last one would have broken the build. It was caught by dry-running the fixer and reading its output before applying anything, which is the only reason it didn't ship.

Verified

In php-compiler:8.4-dev (PHP 8.4.23):

check result
bin/vm.php hello-world rc=0, stdout hello 3
implicit-nullable deprecations from lib/ + ext/ 4 → 0
total deprecations on that path 22 → 17
php -l on all five files clean

The remaining 16 are vendor/ircmaxell and need a patches/ entry — the layer already carries ~40 php-cfg patches.

In php-compiler:22.04-dev (PHP 8.2.32): same run, rc=0, zero stderr.

Not the only 8.4 deprecation

ext/gettext/_.php:10 trips Using "_" as a class name is deprecated since 8.4 — a different class entirely. I am enumerating the rest by running the corpus on both hosts and diffing failing case names, rather than listing them from memory.

🤖 Generated with Claude Code

…diness)

PHP 8.4 deprecates `Foo $x = null` and writes the notice to stderr, which the
compliance harness compares — so on an 8.4 host every case carrying one of
these on its path would differ from Zend on stderr alone.

  ext/gettext/GettextFunction.php        int $max     -> ?int $max
  lib/Func/Internal.php                  string $name -> ?string $name
  lib/Visitor/ExitFunctionResolver.php   Block $prior -> ?Block $prior
  lib/Visitor/InOperatorResolver.php     Block $prior -> ?Block $prior
  lib/Visitor/VoidCastResolver.php       Block $prior -> ?Block $prior

`?Type` has been valid since PHP 7.1 and the declarations already accepted
null, so this changes no behaviour on any supported version.

Five, not the 37 I first reported. The first scan counted three things that
are not deprecated at all: typed properties (`protected ?SplObjectStorage
$seen = null`), unions that already include null (`null|string|Variable|
ClosureState $handler = null`), and `mixed $x = null` — mixed already
includes null, and rewriting it to `?mixed` is a FATAL ERROR. That last one
was caught by dry-running the fixer before applying it.

Verified in php-compiler:8.4-dev (PHP 8.4.23):
  - bin/vm.php hello-world: rc=0, stdout "hello 3"
  - implicit-nullable deprecations from lib/ + ext/: 4 -> 0
  - total deprecations on that path: 22 -> 17 (remainder is vendor/ircmaxell,
    which needs a patches/ entry, plus one unrelated class)
  - php -l clean on all five files
And in php-compiler:22.04-dev (PHP 8.2.32): same run, rc=0, zero stderr.

Not the only 8.4 deprecation in the tree — ext/gettext/_.php also trips
"Using \"_\" as a class name is deprecated since 8.4". Enumerating the rest by
measurement rather than from memory.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@PurHur
PurHur merged commit 9adfec7 into master Jul 29, 2026
2 checks passed
@PurHur
PurHur deleted the fix/php84-implicit-nullable-params branch July 29, 2026 19:14
PurHur pushed a commit that referenced this pull request Jul 29, 2026
Completes the implicit-nullable work started in #24972. PHP 8.4 deprecates
`Foo $x = null` and writes the notice to stderr, which the compliance harness
compares — so on an 8.4 host every case whose path touches one of these would
differ from Zend on stderr alone.

35 parameters across 23 files in php-cfg and php-optimizer, via the existing
patches/ layer (167 patches already there). `?Type` has been valid since PHP
7.1 and these declarations already accepted null, so behaviour is unchanged
on every supported version. The counterpart declarations in lib/Visitor/*
were fixed directly in #24972, so interface and implementations stay
consistent.

Measured in php-compiler:8.4-dev (PHP 8.4.23), bin/vm.php hello-world:

  implicit-nullable deprecations   16 -> 0
  total deprecations               17 -> 1

The one remaining is a different 8.4 deprecation class entirely — `Using "_"
as a class name is deprecated since 8.4` in ext/gettext/_.php — and is not
addressed here.

Verified:
  - apply-patches.sh rc=0, and rc=0 again on a second run (idempotent; the
    three "error"-matching lines are pre-existing "Skip ... (already
    applied)" messages whose PATCH NAMES contain "error", identical on master)
  - php-compiler:22.04-dev (PHP 8.2.32) with a fresh composer install:
    apply-patches rc=0, vm rc=0, stdout "hello 3", ZERO stderr
  - patch generated by diffing a pristine backup, then vendor restored and
    confirmed byte-identical before committing, so the patch is the only
    thing that applies the change

Note on process: the first attempt wired the apply_patch call into a
conditional helper whose anchor appears twice, the edit asserted and failed,
and the follow-up measurement showed 16 deprecations — which looked like the
patch not working rather than the patch never having run. Wired at top level
instead, next to the other unconditional php-llvm patches.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
PurHur pushed a commit that referenced this pull request Jul 29, 2026
Completes the implicit-nullable work started in #24972. PHP 8.4 deprecates
`Foo $x = null` and writes the notice to stderr, which the compliance harness
compares — so on an 8.4 host every case whose path touches one of these would
differ from Zend on stderr alone.

35 parameters across 23 files in php-cfg and php-optimizer, via the existing
patches/ layer (167 patches already there). `?Type` has been valid since PHP
7.1 and these declarations already accepted null, so behaviour is unchanged
on every supported version. The counterpart declarations in lib/Visitor/*
were fixed directly in #24972, so interface and implementations stay
consistent.

Measured in php-compiler:8.4-dev (PHP 8.4.23), bin/vm.php hello-world:

  implicit-nullable deprecations   16 -> 0
  total deprecations               17 -> 1

The one remaining is a different 8.4 deprecation class entirely — `Using "_"
as a class name is deprecated since 8.4` in ext/gettext/_.php — and is not
addressed here.

Verified:
  - apply-patches.sh rc=0, and rc=0 again on a second run (idempotent; the
    three "error"-matching lines are pre-existing "Skip ... (already
    applied)" messages whose PATCH NAMES contain "error", identical on master)
  - php-compiler:22.04-dev (PHP 8.2.32) with a fresh composer install:
    apply-patches rc=0, vm rc=0, stdout "hello 3", ZERO stderr
  - patch generated by diffing a pristine backup, then vendor restored and
    confirmed byte-identical before committing, so the patch is the only
    thing that applies the change

Note on process: the first attempt wired the apply_patch call into a
conditional helper whose anchor appears twice, the edit asserted and failed,
and the follow-up measurement showed 16 deprecations — which looked like the
patch not working rather than the patch never having run. Wired at top level
instead, next to the other unconditional php-llvm patches.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
PurHur added a commit that referenced this pull request Jul 29, 2026
#24988)

* Patch the 35 implicitly-nullable vendor parameters (PHP 8.4 readiness)

Completes the implicit-nullable work started in #24972. PHP 8.4 deprecates
`Foo $x = null` and writes the notice to stderr, which the compliance harness
compares — so on an 8.4 host every case whose path touches one of these would
differ from Zend on stderr alone.

35 parameters across 23 files in php-cfg and php-optimizer, via the existing
patches/ layer (167 patches already there). `?Type` has been valid since PHP
7.1 and these declarations already accepted null, so behaviour is unchanged
on every supported version. The counterpart declarations in lib/Visitor/*
were fixed directly in #24972, so interface and implementations stay
consistent.

Measured in php-compiler:8.4-dev (PHP 8.4.23), bin/vm.php hello-world:

  implicit-nullable deprecations   16 -> 0
  total deprecations               17 -> 1

The one remaining is a different 8.4 deprecation class entirely — `Using "_"
as a class name is deprecated since 8.4` in ext/gettext/_.php — and is not
addressed here.

Verified:
  - apply-patches.sh rc=0, and rc=0 again on a second run (idempotent; the
    three "error"-matching lines are pre-existing "Skip ... (already
    applied)" messages whose PATCH NAMES contain "error", identical on master)
  - php-compiler:22.04-dev (PHP 8.2.32) with a fresh composer install:
    apply-patches rc=0, vm rc=0, stdout "hello 3", ZERO stderr
  - patch generated by diffing a pristine backup, then vendor restored and
    confirmed byte-identical before committing, so the patch is the only
    thing that applies the change

Note on process: the first attempt wired the apply_patch call into a
conditional helper whose anchor appears twice, the edit asserted and failed,
and the follow-up measurement showed 16 deprecations — which looked like the
patch not working rather than the patch never having run. Wired at top level
instead, next to the other unconditional php-llvm patches.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Fix php-vendor-implicit-nullable-84.patch to apply after overlays

The hunks were diffed against post-overlay vendor (Exit_/Property/StaticVar
comments and signatures), so applying before php-cfg overlays failed on a
fresh composer install and broke the PR gate. Move the apply after the
php-cfg overlay block, regenerate the patch, and add an idempotency guard.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: PurHur <tedyyyyy@gmail.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: PurHur <PurHur@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@PurHur

PurHur commented Jul 29, 2026

Copy link
Copy Markdown
Owner Author

Follow-up vendor patch merged in #24988 (5f977ca41).

The first PR gate failed because the patch was applied before php-cfg overlays while hunks matched post-overlay vendor. Regenerated the patch, moved apply after the overlay block, added an idempotency guard. Local verify: fresh composer install + apply-patches.sh ×2 (apply then skip) + bin/vm.php hello OK.

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.

2 participants