Skip to content

PHP 8.3: Readonly amendments #1989

@afilina

Description

@afilina

Related to #1589

Analysis

The RFC had 2 proposals:

  • Non-readonly classes can extend readonly classes in PHP 8.3: ❌ did not pass
  • Readonly properties can be reinitialized during cloning in PHP 8.3: ✅ passed

Summary of existing PHP 8.2 behavior based on the RFC:

class Foo {
    public function __construct(
        public readonly string $bar,
        public readonly string $baz
    ) {}
    
    public function __clone()
    {
        unset($this->baz); // Fatal error
        $this->bar = 'new value'; // Fatal error
    }
}
$foo = new Foo('bar', 'baz');
clone $foo;

Updated behavior in PHP 8.3:

class Foo {
    public function __construct(
        public readonly string $bar,
        public readonly string $baz
    ) {}
    
    public function __clone()
    {
        unset($this->baz); // Uninitializing is allowed
        unset($this->baz); // Uninitializing a second time is allowed
        $this->bar = 'new value'; // Reinitializing to a new value is allowed
        $this->bar = 'new value'; // Fatal error: cannot reinitialize a second time, even if it's the same value
    }
}
$foo = new Foo('bar', 'baz');
clone $foo;

Top 2000 Packages

Detection in PHP 8.2

Assuming the property is readonly:

function __clone() { unset($this->bar); } Unset in __clone: error
function __clone() { $this->bar = 'new value'; } Assign in __clone: error

Detection in PHP 8.3

Assuming the property is readonly:

function __clone() { unset($this->bar); } Unset in __clone: valid
function __clone() { unset($this->bar); unset($this->bar); } Repeated unset in __clone: valid
function __clone() { $this->bar = 'new value'; } Assign in __clone: valid
function __clone() { $this->bar = 'new value'; $this->bar = 'new value'; } Repeated assign in __clone: error

Syntax Variations

Detectability

  • Find readonly properties ✅ Already done in the original sniff
  • Find assignment in __clone ✅❌ Partially doable, as __clone can call other methods in other files
  • Find unsetting in __clone ✅❌ Partially doable, as __clone can call other methods in other files

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions