From 8feaca536906a4f68e5d68df48b1d69e998fcf41 Mon Sep 17 00:00:00 2001 From: PurHur Date: Tue, 18 Aug 2026 02:26:27 +0000 Subject: [PATCH] Add compliance guard for magic property pre/post dec (#32014) Repro scripts and VM/JIT .phpt lock in silent __get/__set behavior for --$obj->n and $obj->n-- covered by the #31992 inc/dec guard in lib/VM.php and lib/JIT.php; register inc/dec suites in phpunit.xml.dist. Co-authored-by: Cursor --- phpunit.xml.dist | 4 +++ .../MagicPropDecNoUndefWarnJITTest.php | 29 +++++++++++++++++++ .../MagicPropDecNoUndefWarnVMTest.php | 29 +++++++++++++++++++ .../magic_prop_dec_no_undef_warn.phpt | 28 ++++++++++++++++++ test/repro/maintainer_gap_magic_prop_dec.php | 10 +++++++ .../maintainer_gap_magic_prop_postdec.php | 10 +++++++ 6 files changed, 110 insertions(+) create mode 100644 test/compliance/MagicPropDecNoUndefWarnJITTest.php create mode 100644 test/compliance/MagicPropDecNoUndefWarnVMTest.php create mode 100644 test/compliance/cases/language/magic_prop_dec_no_undef_warn.phpt create mode 100644 test/repro/maintainer_gap_magic_prop_dec.php create mode 100644 test/repro/maintainer_gap_magic_prop_postdec.php diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 6154947a69b..fd3ff804565 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -533,6 +533,10 @@ ./test/compliance/UnserializeEmptySilentJITTest.php ./test/compliance/DynPropIncUndefinedWarnVMTest.php ./test/compliance/DynPropIncUndefinedWarnJITTest.php + ./test/compliance/MagicPropIncNoUndefWarnVMTest.php + ./test/compliance/MagicPropIncNoUndefWarnJITTest.php + ./test/compliance/MagicPropDecNoUndefWarnVMTest.php + ./test/compliance/MagicPropDecNoUndefWarnJITTest.php ./test/compliance/XmlGetCurrentReflectionVMTest.php ./test/compliance/XmlParserGetOptionReflectionVMTest.php ./test/compliance/XmlParserFreeReflectionVMTest.php diff --git a/test/compliance/MagicPropDecNoUndefWarnJITTest.php b/test/compliance/MagicPropDecNoUndefWarnJITTest.php new file mode 100644 index 00000000000..1b29cfaea82 --- /dev/null +++ b/test/compliance/MagicPropDecNoUndefWarnJITTest.php @@ -0,0 +1,29 @@ +n / $obj->n-- with __get/__set emits no Undefined property (#32014). + */ +final class MagicPropDecNoUndefWarnJITTest extends BaseTest +{ + protected static string $DIR = __DIR__; + + public static function providePHPTests(): \Generator + { + $file = 'magic_prop_dec_no_undef_warn.phpt'; + yield $file => self::parsePHPT( + __DIR__.'/cases/language/'.$file, + $file + ); + } + + public function setUp(): void + { + $this->BIN = realpath(__DIR__.'/../../bin/jit.php'); + } +} diff --git a/test/compliance/MagicPropDecNoUndefWarnVMTest.php b/test/compliance/MagicPropDecNoUndefWarnVMTest.php new file mode 100644 index 00000000000..451f13b0e06 --- /dev/null +++ b/test/compliance/MagicPropDecNoUndefWarnVMTest.php @@ -0,0 +1,29 @@ +n / $obj->n-- with __get/__set emits no Undefined property (#32014). + */ +final class MagicPropDecNoUndefWarnVMTest extends BaseTest +{ + protected static string $DIR = __DIR__; + + public static function providePHPTests(): \Generator + { + $file = 'magic_prop_dec_no_undef_warn.phpt'; + yield $file => self::parsePHPT( + __DIR__.'/cases/language/'.$file, + $file + ); + } + + public function setUp(): void + { + $this->BIN = realpath(__DIR__.'/../../bin/vm.php'); + } +} diff --git a/test/compliance/cases/language/magic_prop_dec_no_undef_warn.phpt b/test/compliance/cases/language/magic_prop_dec_no_undef_warn.phpt new file mode 100644 index 00000000000..2568678f09a --- /dev/null +++ b/test/compliance/cases/language/magic_prop_dec_no_undef_warn.phpt @@ -0,0 +1,28 @@ +--TEST-- +Language: --$obj->n / $obj->n-- with __get/__set emits no Undefined property (#32014, zend_object_handlers.c) +--FILE-- + 1]; + public function __get($k) { return $this->d[$k]; } + public function __set($k, $v) { $this->d[$k] = $v; } +} +$errs = []; +set_error_handler(static function (int $errno, string $errstr) use (&$errs): bool { + $errs[] = [$errno, $errstr]; + return true; +}); +$m = new M(); +--$m->n; +restore_error_handler(); +foreach ($errs as $e) { + echo "err[{$e[0]}] {$e[1]}\n"; +} +echo "dec=", $m->n, "\n"; +$m = new M(); +$m->n--; +echo "postdec=", $m->n, "\n"; +--EXPECT-- +dec=0 +postdec=0 diff --git a/test/repro/maintainer_gap_magic_prop_dec.php b/test/repro/maintainer_gap_magic_prop_dec.php new file mode 100644 index 00000000000..dc277dbb08c --- /dev/null +++ b/test/repro/maintainer_gap_magic_prop_dec.php @@ -0,0 +1,10 @@ + 1]; + public function __get($k) { return $this->d[$k]; } + public function __set($k, $v) { $this->d[$k] = $v; } +} +$m = new M(); +--$m->n; +echo 'dec=', $m->n, "\n"; diff --git a/test/repro/maintainer_gap_magic_prop_postdec.php b/test/repro/maintainer_gap_magic_prop_postdec.php new file mode 100644 index 00000000000..42710ead767 --- /dev/null +++ b/test/repro/maintainer_gap_magic_prop_postdec.php @@ -0,0 +1,10 @@ + 1]; + public function __get($k) { return $this->d[$k]; } + public function __set($k, $v) { $this->d[$k] = $v; } +} +$m = new M(); +$m->n--; +echo 'postdec=', $m->n, "\n";