When trying to change it,throw an exception.
I made another version that uses @readonly in the docblock instead of private $r_propname. This still doesn't stop the declaring class from setting the property, but will work for public readonly access.
Sample Class:
class Person {
    use Readonly;
    /**
     * @readonly
     */
    protected $name;
    protected $phoneNumber;
    public function __construct($name){
        $this->name = $name;
        $this->phoneNumber = '123-555-1234';
    }
}
The ReadOnly trait
trait Readonly {
    public function readonly_getProperty($prop){
        if (!property_exists($this,$prop)){
            //pretty close to the standard error if a protected property is accessed from a public scope
            trigger_error('Undefined property: '.get_class($this).'::\$'.$prop,E_USER_NOTICE);
        }
        
        $refProp = new \ReflectionProperty($this, $prop);
        $docblock = $refProp->getDocComment();
        // a * followed by any number of spaces, followed by @readonly
        $allow_read = preg_match('/\*\s*\@readonly/', $docblock);
        if ($allow_read){
            $actual = $this->$prop;
            return $actual;
        }
        
        throw new \Error("Cannot access non-public property '{$prop}' of class '".get_class($this)."'");
    }
    public function __get($prop){
        return $this->readonly_getProperty($prop);
    }
    
}
See the source code & test on my gitlab
I suppose a solution, for class properties, would be to :
__get method to access that property, using the "fake" name__set method so it throws an exception when trying to set that property.For variables, I don't think it's possible to have a read-only variable for which PHP will throw an exception when you're trying to write to it.
For instance, consider this little class :
class MyClass {
    protected $_data = array(
        'myVar' => 'test'
    );
    public function __get($name) {
        if (isset($this->_data[$name])) {
            return $this->_data[$name];
        } else {
            // non-existant property
            // => up to you to decide what to do
        }
    }
    public function __set($name, $value) {
        if ($name === 'myVar') {
            throw new Exception("not allowed : $name");
        } else {
            // => up to you to decide what to do
        }
    }
}
Instanciating the class and trying to read the property :
$a = new MyClass();
echo $a->myVar . '<br />';
Will get you the expected output :
test
While trying to write to the property :
$a->myVar = 10;
Will get you an Exception :
Exception: not allowed : myVar in /.../temp.php on line 19
                        class test {
   const CANT_CHANGE_ME = 1;
}
and you refer it as test::CANT_CHANGE_ME
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With