Вопрос

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

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

Это было полезно?

Решение

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

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 лучше всего принять политику использования только методов установки для изменения свойства извне.

Другие советы

Но частные свойства, доступные только с помощью __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().

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top