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

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

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

Кажется, что «только для чтения» - это особое ключевое слово, которое можно использовать только с классами, скомпилированными в PHP. К сожалению, "общедоступное только для чтения" было бы отличным способом избежать использования __get () и __set ().

shadowhand 15.07.2009 06:37

Это было рассмотрено как RFC в 2014 году (wiki.php.net/rfc/readonly_properties), но было отозвано после значительного количества разногласий (markmail.org/message/7l3ci3sboma2nlzq). Мне бы хотелось, чтобы readonly использовался как ключевое слово для свойств, которое значительно упростило бы жизнь, вместо того, чтобы постоянно определять геттеры или использовать шаблон прокси.

e_i_pi 25.05.2017 08:11

В настоящее время существует проект RFC (27 июня 2020 г.), предлагающий добавить функции только для чтения в PHP 8.0: «Это ранний черновик, в настоящее время мы ждем отзывов.» Адрес электронной почты автора указан, и я считаю, что вы можете отправить им предложения по электронной почте.

Reed 27.06.2020 22:15
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
59
4
46 799
6
Перейти к ответу Данный вопрос помечен как решенный

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

Действительно! Метод __get работает очень медленно. У меня был класс конфигурации, который использовал что-то вроде этого, чтобы можно было получить доступ к каждой частной переменной, но не изменить. Когда я пропустил свой код через профилировщик, я был шокирован временем, которое он использовал :( Очень грустно. Мне хотелось, чтобы у php был атрибут readonly.

AntonioCS 21.12.2009 20:39

Без __get это можно реализовать только внутри (в расширении).

Artefacto 21.06.2010 18:19

Странно то, что они документируют их как readonly public, а не как частные.

Rafael Barros 01.03.2014 16:49

Прошло несколько лет. Выпущены новые версии PHP. Метод __get все еще слишком медленный в PHP? Я использую PHP 7.0

rineez 13.09.2018 14:40

Если все свойства являются частными, то метод __set() не нужен, так как PHP сам поймает частное свойство. Альтернативный подход - сделать свойства public, а затем использовать Только метод __set(), чтобы отловить любую попытку установить свойства. В этом случае метод __get() не нужен, поскольку свойства можно читать напрямую.

Jason 18.01.2021 17:12
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 () прямо сейчас будет либо получать, либо устанавливать.

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

Просто объявите все переменные, доступные только для чтения, как закрытые или защищенные и используйте волшебный метод __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 ().

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

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

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

Если переменная не предназначена для непосредственного редактирования, несмотря на то, что она является public, PHP-программисты часто используют соглашение $pleaseTouch по сравнению с $_doNotTouch, чтобы указать, следует ли полагаться на данное свойство извне или нет.

Seldom 'Where's Monica' Needy 27.10.2015 01:55

Класс, реализующий интерфейс JsonSerializable, может иметь настраиваемое свойство, которое будет закодировано путем определения его с помощью метода jsonSerialize. Вы можете показать там частные свойства, которые хотите закодировать.

Taufik Nurrohman 28.02.2019 09:48

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

Стоит отметить, что PHP теперь включает интерфейс JsonSerializable начиная с версии 5.4.

smdvlpr 21.08.2012 17:45

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

Чтобы это работало, ваши свойства должны быть недоступны, поэтому вам нужно изменить public на private или protected.

story 01.02.2017 02:18

@story ты тестировал? поскольку у меня есть, и он работает, $ foo и $ bar доступны для чтения извне, но не для записи (кроме класса и унаследованного класса). Не знаю, задокументировано ли это, но извне __get и __set trap имеют приоритет. полезность public здесь предназначена для самоанализа IDE.

Le Petit Monde de Purexo 15.02.2017 17:13

Да, я это тестировал. Вы можете получить и установить просто отлично, но это не пройдет через ваши магические методы. stackoverflow.com/questions/4713680/…

story 16.02.2017 20:48

странно, сработало, как я ожидал для меня (я использовал public для самоанализа IDE). Я изменяю общедоступный на защищенный, как вы сказали

Le Petit Monde de Purexo 21.02.2017 13:24

разве это не должно быть похоже на return $this->$name ?: null;?

mrReiha 23.04.2019 17:17

@mrReiha, я считаю, что ответ на ваш вопрос - «нет», потому что тогда вы не могли бы вернуть значение переменных, которые оцениваются как false в логическом контексте, например 0 или '' ... вместо этого вы получите нулевое значение.

scott8035 23.08.2019 12:54

@ scott8035, тогда я не уверен, что я вообще знаю результат ??. Я думал, что это переведено как «проверить значение $ name и, если оно истинно, вернуть его, в противном случае вернуть ноль». поправьте меня, если я не на той странице.

mrReiha 23.08.2019 20:18

@mrReiha, а что, если переменная содержит строку нулевой длины или нулевое значение? В этом случае он оценит значение false и вернет ноль вместо фактического значения, содержащегося в переменной.

scott8035 24.08.2019 23:06

@ scott8035, значит, вы говорите, что ?? не будет действовать как оператор if (). Он проверит, имеет ли эта переменная значение (что угодно, а не только правдиво) или нет. Это верно?

mrReiha 01.09.2019 23:05

@mrReiha, это ?? возвращает левое выражение, если аргумент существуют равен ненулевой. В противном случае возвращается правильное выражение. Он действует как оператор if (), состоящий из двух частей: if (isset (expr1) &&! Is_null (expr1)) {return expr1; } else {return expr2; }.

scott8035 04.09.2019 17:06

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