From 1e8784f1c7353cb635a659882aa1afbdedd437a0 Mon Sep 17 00:00:00 2001 From: Alessio Giacobbe Date: Mon, 31 Aug 2026 12:44:29 +0200 Subject: [PATCH] fix(translator): reject static/instance property redeclaration mismatch checkPropertyOverride() compared type, visibility, set-visibility, readonly and final between a child property and the parent's, but never Modifiers::STATIC. Redeclaring `public static int $x` as `public int $x` (or the reverse) was accepted, while Zend fatals with "Cannot redeclare static A::$x as non static B::$x" (and "Cannot redeclare non static ... as static ..." in the other direction): static and instance properties are different kinds of storage and can never override one another. --- phpunit/code/property_nonstatic_mismatch.php | 12 ++++++++ phpunit/code/property_static_match.php | 14 +++++++++ phpunit/code/property_static_mismatch.php | 12 ++++++++ phpunit/src/StaticPropertyOverrideTest.php | 32 ++++++++++++++++++++ src/Translator.php | 8 +++++ 5 files changed, 78 insertions(+) create mode 100644 phpunit/code/property_nonstatic_mismatch.php create mode 100644 phpunit/code/property_static_match.php create mode 100644 phpunit/code/property_static_mismatch.php create mode 100644 phpunit/src/StaticPropertyOverrideTest.php diff --git a/phpunit/code/property_nonstatic_mismatch.php b/phpunit/code/property_nonstatic_mismatch.php new file mode 100644 index 00000000..7b038f24 --- /dev/null +++ b/phpunit/code/property_nonstatic_mismatch.php @@ -0,0 +1,12 @@ +compile('property_static_match.php'); + } + + public function testStaticCannotBecomeInstance(): void + { + $this->exec( + 'Cannot redeclare static `A::$x` as non static `B::$x`', + 'property_static_mismatch.php', + ); + } + + public function testInstanceCannotBecomeStatic(): void + { + $this->exec( + 'Cannot redeclare non static `A::$x` as static `B::$x`', + 'property_nonstatic_mismatch.php', + ); + } +} diff --git a/src/Translator.php b/src/Translator.php index be6aa21d..1776ed49 100644 --- a/src/Translator.php +++ b/src/Translator.php @@ -5375,6 +5375,14 @@ private function checkPropertyOverride(Node\Stmt\Class_|Node\Stmt\Trait_|Node\St "`{$parentClass}::\${$name}`; property shadowing across inheritance is not allowed"); } $matchedOverrides[$name] = true; + // A static property and an instance property are different + // kinds of storage; Zend forbids redeclaring one as the + // other in either direction. + if (($childProp->flags & Modifiers::STATIC) !== ($parentProp->flags & Modifiers::STATIC)) { + $this->fatalError($classStmt, ($parentProp->flags & Modifiers::STATIC) + ? "Cannot redeclare static `{$parentClass}::\${$name}` as non static `{$className}::\${$name}`" + : "Cannot redeclare non static `{$parentClass}::\${$name}` as static `{$className}::\${$name}`"); + } // PHP inherits get and set independently. A child may // override only one hook, or redeclare the property // without hooks while retaining both parent hooks.