Readonly: Subtyping

From a typing perspective, one can think of a readonly object as a supertype of its mutable counterpart. For example, readonly values cannot be passed to a function that takes mutable values.

class Foo {
  public int $prop = 0;
}

function takes_mutable(Foo $x): void {
  $x->prop = 4;
}

function test(): void {
    $z = readonly new Foo();
    takes_mutable($z); // error, takes_mutable's first parameter
                       // is mutable, but $z is readonly
}

Similarly, functions cannot return readonly values unless they are annotated to return readonly.

class Foo {}
function returns_mutable(readonly Foo $x): Foo {
  return $x; // error, $x is readonly
}
function returns_readonly(readonly Foo $x): readonly Foo {
  return $x; // correct
}

Note that non-readonly (i.e. mutable) values can be passed to a function that takes a readonly parameter:

class Foo {}
// promises not to modify $x
function takes_readonly(readonly Foo $x): void {
}

function test(): void {
    $z = new Foo();
    takes_readonly($z); // ok
}

Similarly, class properties cannot be set to readonly values unless they are declared as readonly properties.

class Bar {}
class Foo {
  public function __construct(
    public readonly Bar $ro_prop,
    public Bar $mut_prop
  ){}
}

function test(
  Foo $x,
  readonly Bar $bar,
) : void {
  $x->mut_prop = $bar; // error, $bar is readonly but the prop is mutable
  $x->ro_prop = $bar; // ok
}
Was This Page Useful?
Thank You!
Thank You! If you'd like to share more feedback, please file an issue.