Свойства PHP Readonly?

При использовании классов PHP DOM (DOMNode, DOMEElement и т.д.) я заметил, что они обладают действительно свойствами readonly. Например, я могу прочитать свойство $nodeName для DOMNode, но я не могу написать ему (если я делаю PHP, генерирует фатальную ошибку).

Как я могу создавать собственные свойства readonly в PHP?

Ответ 1

Вы можете сделать это следующим образом:

class Example {
    private $__readOnly = 'hello world';
    function __get($name) {
        if($name === 'readOnly')
            return $this->__readOnly;
        user_error("Invalid property: " . __CLASS__ . "->$name");
    }
    function __set($name, $value) {
        user_error("Can't set property: " . __CLASS__ . "->$name");
    }
}

Используйте это только тогда, когда вам это действительно нужно - он медленнее обычного доступа к свойствам. Для PHP лучше всего использовать политику только с использованием методов setter для изменения свойства снаружи.

Ответ 2

Но частные свойства, открытые только с помощью __get(), не видны для функций, которые перечисляют элементы объекта - json_encode(), например.

Я регулярно передаю объекты PHP в Javascript, используя json_encode(), поскольку это, кажется, хороший способ передать сложные структуры с большим количеством данных, заполненных из базы данных. Я должен использовать общедоступные свойства в этих объектах, чтобы эти данные были заполнены в Javascript, который его использует, но это означает, что эти свойства должны быть общедоступными (и, следовательно, рискует, что другой программист не на той же длине волны (или, возможно, сам после плохой ночи) может изменить их напрямую). Если я делаю их частными и использую __get() и __set(), то json_encode() их не видит.

Было бы неплохо иметь ключевое слово доступности "только для чтения"

Ответ 3

Я вижу, что у вас уже есть ваш ответ, но для тех, кто все еще ищет:

Просто объявляйте все переменные "readonly" частным или защищенным и используйте магический метод __get() следующим образом:

/**
 * This is used to fetch readonly variables, you can not read the registry
 * instance reference through here.
 * 
 * @param string $var
 * @return bool|string|array
 */
public function __get ($var)
{
    return ($var != "instance" && isset($this->$var)) ? $this->$var : false;
}

Как вы можете видеть, я также защитил переменную экземпляра $this- > , так как этот метод позволит пользователям читать все объявленные переменные. Чтобы заблокировать несколько переменных, используйте массив с параметром in_array().

Ответ 4

Вот способ визуализации всего свойства вашего класса read_only извне, унаследованный класс имеет доступ на запись; -).

class Test {
    protected $foo;
    protected $bar;

    public function __construct($foo, $bar) {
        $this->foo = $foo;
        $this->bar = $bar;
    }

/**
 * All property accessible from outside but readonly
 * if property does not exist return null
 *
 * @param string $name
 *
 * @return mixed|null
 */
    public function __get ($name) {
        return $this->$name ?? null;
    }

/**
 * __set trap, property not writeable
 *
 * @param string $name
 * @param mixed $value
 *
 * @return mixed
 */
    function __set ($name, $value) {
        return $value;
    }
}

проверено в php7

Ответ 5

Для тех, кто ищет способ опубликования ваших приватных/защищенных свойств для сериализации, если вы решите использовать метод getter, чтобы сделать их readonly, вот как это сделать (@Matt: для json в качестве примера):

interface json_serialize {
    public function json_encode( $asJson = true );
    public function json_decode( $value );
}

class test implements json_serialize {
    public $obj = null;
    protected $num = 123;
    protected $string = 'string';
    protected $vars = array( 'array', 'array' );
    // getter
    public function __get( $name ) {
        return( $this->$name );
    }
    // json_decode
    public function json_encode( $asJson = true ) {
        $result = array();
        foreach( $this as $key => $value )
            if( is_object( $value ) ) {
                if( $value instanceof json_serialize )
                    $result[$key] = $value->json_encode( false );
                else
                    trigger_error( 'Object not encoded: ' . get_class( $this ).'::'.$key, E_USER_WARNING );
            } else
                $result[$key] = $value;
        return( $asJson ? json_encode( $result ) : $result );
    }
    // json_encode
    public function json_decode( $value ) {
        $json = json_decode( $value, true );
        foreach( $json as $key => $value ) {
            // recursively loop through each variable reset them
        }
    }
}
$test = new test();
$test->obj = new test();
echo $test->string;
echo $test->json_encode();

Ответ 6

Class PropertyExample {

        private $m_value;

        public function Value() {
            $args = func_get_args();
            return $this->getSet($this->m_value, $args);
        }

        protected function _getSet(&$property, $args){
            switch (sizeOf($args)){
                case 0:
                    return $property;
                case 1:
                    $property = $args[0];
                    break;  
                default:
                    $backtrace = debug_backtrace();
                    throw new Exception($backtrace[2]['function'] . ' accepts either 0 or 1 parameters');
            }
        }


}

Вот как я занимаюсь получением/настройкой своих свойств, если вы хотите сделать Value() readonly... тогда вы просто просто сделаете следующее:

    return $this->m_value;

Здесь, когда функция Value() теперь будет либо получать, либо устанавливать.