Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement a read-only member variable in PHP?

When trying to change it,throw an exception.

like image 609
user198729 Avatar asked Feb 26 '10 18:02

user198729


3 Answers

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

like image 69
Reed Avatar answered Sep 22 '22 03:09

Reed


I suppose a solution, for class properties, would be to :

  • not define a property with the name that interests you
  • use the magic __get method to access that property, using the "fake" name
  • define the __set method so it throws an exception when trying to set that property.
  • See Overloading, for more informations on magic methods.

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
like image 24
Pascal MARTIN Avatar answered Nov 15 '22 09:11

Pascal MARTIN


class test {
   const CANT_CHANGE_ME = 1;
}

and you refer it as test::CANT_CHANGE_ME

like image 15
chelmertz Avatar answered Nov 15 '22 08:11

chelmertz