skip to Main Content
<?php

declare(strict_types=1);

class Example {
    public readonly array $setOne;
    public readonly array $setTwo;

    public function __construct()
    {
        // Populate $setOne and $setTwo with a lot of data
    }

    public function doThing()
    {
        $map = [
            'one' => &$this->setOne,
            'two' => &$this->setTwo,
        ];

        // Loop over the Map and evaluate the data
        // Take action on the data, but don't try to change it
    }
}

When I try to create a reference to a readonly property in PHP >=8.1, I get the error Fatal error: Uncaught Error: Cannot modify readonly property. This would make sense if I try to do something like unset($map['one'][0]); However, I get this error just by creating the reference.

Why is the creation of a reference considered a modification by PHP?

2

Answers


  1. If you need to work with the values of read-only properties without risking modification, you can use values directly or create copies of the data.
    Try this:

    public function doThing(){
      $values = [
        'one' => $this->setOne, // Use value directly
        'two' => $this->setTwo,
      ];
    
    // Now loop over $values and work with the data
    }
    
    Login or Signup to reply.
  2. I would highly recommend reading the PHP manual section on references and getting a clear understanding of what references are, and what they are not.

    Assigning a reference in PHP effectively means "give this one value in memory two different names, usable interchangeably".

    When you write $foo =& $obj->bar; you get something like this:

    current scope --> local var $foo --+
                                        
                                         +--> [anonymous value in memory]
                                        /
    object $obj --> property $bar -----+
    

    If you then write $foo = 42;, PHP will follow the reference and write to the value in memory, without ever looking at the object. Any constraints that object $obj has about that property can’t be checked, because there’s no way to follow the arrows backwards.

    In order to reject the assignment, PHP would need to track the details of the property on the value itself. This is actually how typed properties are implemented – when you access one by reference, you get a special "typed reference" which makes sure you can’t assign a value that’s not allowed on the property. Conceptually, something like this:

    current scope --> local var $foo --+
                                        
                                         +--> [type constraint] --> [anonymous value in memory]
                                        /
    object $obj --> property $bar -----+
    

    It would be possible to do this for readonly as well, but it wouldn’t make much sense, because there wouldn’t be any point in a reference you couldn’t write to.

    It’s a common mistake to think that a "read-only reference" would be useful for performance, because it avoids copying the value; but the truth is that PHP already optimises assignments using something called "Copy On Write". When you write $a = [1,2,3]; $b = $a;, PHP doesn’t immediately copy the data of the array, it just points both variables to the same memory; only when you modify one or the other (e.g. $a[] = 42; or $b[0] = 101;) is the data actually copied.

    A read-only reference won’t make that any more efficient, and in some cases will actually make it worse, because it makes it harder for PHP to optimise your code.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search