Свойства PHP только для чтения?


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

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

6 54

6 ответов:

вы можете сделать это так:

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 для изменения свойства извне.

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

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

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

Я вижу, что вы уже получили свой ответ, но для тех, кто все еще ищет:

просто объявите все переменные" только для чтения " как частные или защищенные и используйте магический метод _ _ 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 - >instance, поскольку этот метод позволит пользователям читать все объявленные переменные. Для блокировки нескольких переменных используйте массив с in_array ().

вот способ визуализации всех свойств вашего класса 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

для тех, кто ищет способ предоставления ваших частных / защищенных свойств для сериализации, если вы решите использовать метод getter, чтобы сделать их только для чтения, вот способ сделать это (@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();
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() только для чтения ... тогда вы просто просто должны сделать следующее вместо этого:

    return $this->m_value;

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