diff --git a/phpunit/code/unset-readonly-class-property.php b/phpunit/code/unset-readonly-class-property.php new file mode 100644 index 00000000..8ddd9b79 --- /dev/null +++ b/phpunit/code/unset-readonly-class-property.php @@ -0,0 +1,16 @@ +value = 1; + } + + public function clear(): void + { + unset($this->value); + } +} diff --git a/phpunit/code/unset-readonly-property.php b/phpunit/code/unset-readonly-property.php new file mode 100644 index 00000000..9ca29ef0 --- /dev/null +++ b/phpunit/code/unset-readonly-property.php @@ -0,0 +1,17 @@ +value = 1; + } +} + +function main(): void +{ + $object = new ReadonlyPropertyUnset(); + unset($object->value); +} diff --git a/phpunit/src/UndefineTest.php b/phpunit/src/UndefineTest.php index 0d53fa76..23e47887 100644 --- a/phpunit/src/UndefineTest.php +++ b/phpunit/src/UndefineTest.php @@ -18,6 +18,19 @@ class UndefineTest extends \BaseTest $this->exec('Attempt to unset static property', 'unset-static-prop.php'); } + public function testUnsetReadonlyPropertyIsRejected(): void + { + $this->exec('Cannot unset readonly property `ReadonlyPropertyUnset::$value`', 'unset-readonly-property.php'); + } + + public function testUnsetReadonlyClassPropertyIsRejected(): void + { + $this->exec( + 'Cannot unset readonly property `ReadonlyClassPropertyUnset::$value`', + 'unset-readonly-class-property.php' + ); + } + public function testPropertyAccessOnUndefinedVar(): void { $this->compile('undefined-prop-access.php'); diff --git a/src/Parser/PropertyAccessTrait.php b/src/Parser/PropertyAccessTrait.php index 4f5aac76..68a0f025 100644 --- a/src/Parser/PropertyAccessTrait.php +++ b/src/Parser/PropertyAccessTrait.php @@ -737,6 +737,14 @@ trait PropertyAccessTrait $propertyId = $this->getPropertyIdentifier($var, $var->var, $var->name); $def = $this->getNativePropertyDef($var); if ($def) { + if ($def->isReadonly()) { + $this->fatalError( + $var, + 'Cannot unset readonly property `' + . $this->getObjectPropertyTypeCheckDisplayName($var) + . '`' + ); + } // Object typed properties are backed by Zend object // properties, so PHP can represent their uninitialized // state after unset(). Keep that behavior instead of