John Smith
John Smith

Reputation: 6207

Php, can "readonly" replace protected get-ters?

before Php 8.1 we could wrote that:

abstract class Base
{
    private $thing;

    public function __construct($thing)
    {
        $this->thing = $thing;
    }

    protected function getThing()
    {
        return $this->thing;
    }
}

class Test1 extends Base
{
    private function needThing()
    {
        ... $this->getThing();
    }
}

class Test2 extends Base
{
    private function needThing()
    {
        ... $this->getThing();
    }
}

so getThing() was to get the inner thing object. It could have been written like that:

abstract class Base
{
    protected $thing;

    public function __construct($thing)
    {
        $this->thing = $thing;
    }
}

class Test1 extends Base
{
    private function needThing()
    {
        ... $this->thing;
    }
}

class Test2 extends Base
{
    private function needThing()
    {
        ... $this->thing;
    }
}

it's shorter, does the same, but not $thing isn't really protected, it can be read and modified (the first example at least prevented from modifing). But here comes the readonly modifier:

abstract class Base
{
    readonly private $thing;

    public function __construct($thing)
    {
        $this->thing = $thing;
    }

    protected function getThing()
    {
        return $this->thing;
    }
}

class Test1 extends Base
{
    private function needThing()
    {
        ... $this->thing;
    }
}

class Test2 extends Base
{
    private function needThing()
    {
        ... $this->thing;
    }
}

I think this is a serious game changer, makes a lot of getters futile. Of course it looks you're reaching a tag variable, but still acts like a method, once it was written (in constructor) and now no longer possible to overwrite.

So, can "readonly" replace protected get-ters?

Upvotes: 0

Views: 578

Answers (1)

IMSoP
IMSoP

Reputation: 97938

You're almost there. The readonly modifier doesn't add any visibility, so you still need to declare the property protected:

abstract class Base
{
    readonly protected $thing;

    public function __construct($thing)
    {
        $this->thing = $thing;
    }
}

class Test1 extends Base
{
    private function needThing()
    {
        ... $this->thing;
    }
}

class Test2 extends Base
{
    private function needThing()
    {
        ... $this->thing;
    }
}

This will then work for your specific use case. The RFC where the feature was proposed used an example of a public getter being replaced by a public readonly property, which is exactly the same principle: the property can now have the visibility that the getter used to have.

It's important to note that this is not a universal replacement for getters though - in the original code, the base class could write to the property as many times as it liked, but the readonly keyword means "write only once". For instance, you might have a counter that is incremented by a particular method in the base class, and want child classes to read it but not write it:

abstract class Base
{
    private int $counter = 0;

    protected function getCounter(): int {
       return $this->counter;
    }

    public function doSomething(): void {
        // ...
        $this->counter++;
        // ...
    }
}

class Test1 extends Base
{
    private function needCounter()
    {
        // ... 
        $counter = $this->getCounter();
        // ...
    }
}

Here, readonly is not useful, because it would forbid the $this->counter++ statement in the base class. There is currently a proposal for "asymmetric visibility", which would cater for this use case - you would be able to say that the variable was "private for writing, but protected for reading". If this passes, the earliest it will be introduced is in PHP 8.3, at the end of 2023.

Upvotes: 1

Related Questions