Как получить защищенное свойство объекта в PHP

У меня есть объект с защищенным свойством, которое я хочу получить и установить. Объект выглядит как

Fields_Form_Element_Location Object
(
[helper] => formText
[_allowEmpty:protected] => 1
[_autoInsertNotEmptyValidator:protected] => 1
[_belongsTo:protected] => 


[_description:protected] => 
[_disableLoadDefaultDecorators:protected] => 
[_errorMessages:protected] => Array
    (
    )

[_errors:protected] => Array
    (
    )
[_isErrorForced:protected] => 
[_label:protected] => Current City


[_value:protected] => 93399
[class] => field_container field_19 option_1 parent_1
)

Я хочу получить свойство value объекта. Когда я пытаюсь $obj->_value или $obj->value, возникает ошибка. Я поискал и нашел решение использовать PHP Reflection Class. Он работал на моем локальном компьютере, но на сервере версия PHP - 5.2.17, поэтому я не могу использовать эту функцию там. Итак, какое решение, как получить такую ​​собственность?


person Awais Qarni    schedule 02.12.2013    source источник
comment
Используйте геттер и сеттер   -  person luxcem    schedule 02.12.2013
comment
Вы упустили какой-то контекст? Вам просто нужно написать соответствующую пару методов установки / получения. А если вы не можете изменить класс, вы можете просто расширить его.   -  person Álvaro González    schedule 02.12.2013
comment
@Arnaud Я думаю, он понял основы ООП. Я думаю, что настоящая проблема здесь в том, что он не может изменять класс Fields_Form_Element_Location.   -  person idmean    schedule 02.12.2013
comment
@ ÁlvaroG.Vicario Да, я показал только некоторые свойства объекта. Объект был слишком большим, чтобы показывать его здесь.   -  person Awais Qarni    schedule 02.12.2013
comment
Посмотрите код класса или документацию, предлагает ли он вам какие-либо геттеры для доступа к этим данным. В противном случае вы не должны получать к нему доступ. Узнай почему. Если вам все равно нужно получить к нему доступ, вам нужно изменить класс и / или поговорить с его автором.   -  person deceze♦    schedule 02.12.2013
comment
@deceze Я не могу получить доступ к классу, так как он зашифрован :-(   -  person Awais Qarni    schedule 02.12.2013
comment
Я был недостаточно ясен. С отсутствующим контекстом я не имел в виду большую свалку. Поскольку вы задаете очевидный вопрос, вы упоминаете отражение и публикуете print_r(), а не PHP-код. Я предполагал, что вы делаете что-то очень конкретное или используете конкретный инструмент.   -  person Álvaro González    schedule 02.12.2013
comment
И у вас тоже нет документации ...?   -  person deceze♦    schedule 02.12.2013
comment
@ ÁlvaroG.Vicario На самом деле я переопределяю основное поведение системы   -  person Awais Qarni    schedule 02.12.2013
comment
@deceze Нет документации :-( Это платный инструмент   -  person Awais Qarni    schedule 02.12.2013
comment
Платный инструмент без документации? Тогда за что вы платите? D-;   -  person deceze♦    schedule 03.12.2013


Ответы (8)


Это то, что означает "защищенный", как объясняется в главе Видимость:

Доступ к членам, объявленным защищенными, можно получить только внутри самого класса, а также унаследованных и родительских классов.

Если вам нужно получить доступ к собственности извне, выберите один:

  • Не объявляйте его защищенным, вместо этого сделайте его общедоступным
  • Напишите пару функций для получения и установки значения (геттеры и сеттеры)

Если вы не хотите изменять исходный класс (потому что это сторонняя библиотека, которую вы не хотите путать), создайте собственный класс, расширяющий исходный:

class MyFields_Form_Element_Location extends Fields_Form_Element_Location{
}

... и добавьте туда свой геттер / сеттер.

person Álvaro González    schedule 02.12.2013
comment
но что, если вы используете какую-то внешнюю библиотеку, и вам нужно ее отладить и вы хотите распечатать значения некоторых защищенных полей? - person Kamil Kiełczewski; 10.01.2017
comment
@ KamilKiełczewski Извините, я не понимаю, о чем вы говорите. Вы можете выполнить отладку в любое время. Видимость - это концепция дизайна приложений. - person Álvaro González; 10.01.2017
comment
Дело в том, что у вас очень большой объект (в моем случае это очередь laravel sqs), и вы должны удаленно отлаживать через ssh, и вы хотите видеть только выбранные (защищенные / частные) поля (не все поля объекта). - person Kamil Kiełczewski; 10.01.2017
comment
@ KamilKiełczewski Но ... Вы пытаетесь что-то прокомментировать в моем ответе или задаете новый вопрос? - person Álvaro González; 10.01.2017
comment
это всего лишь комментарий к тому, что вы не включили в свой ответ, но не волнуйтесь: P - person Kamil Kiełczewski; 10.01.2017
comment
@ KamilKiełczewski Извините, но я просто не могу этого понять. Если вы имеете в виду, что стандартные принципы ООП каким-то образом мешают отладке, я категорически не согласен, но, конечно, вы всегда можете найти сценарий, в котором со спагетти легче справиться ;-) - person Álvaro González; 10.01.2017
comment
проблема в том, что если вы установите его как общедоступное, значение может быть установлено отовсюду, чего вы в действительности никогда не хотите. Лучший способ - сделать его защищенным или даже частным и сделать __set и __get, чтобы вы могли контролировать то, что только читается, но не доступно для записи. Добавьте свойство в DocBlock, и у вас будет автозаполнение. Мне это больше всего нравится. - person Lachezar Raychev; 30.05.2021

Вот действительно простой пример (без проверки ошибок) того, как использовать ReflectionClass:

function accessProtected($obj, $prop) {
  $reflection = new ReflectionClass($obj);
  $property = $reflection->getProperty($prop);
  $property->setAccessible(true);
  return $property->getValue($obj);
}

Я знаю, что вы сказали, что ограничены версией 5.2, но это было 2 года назад, 5.5 - самая старая поддерживаемая версия, а я ' м в надежде помочь людям с современными версиями.

person drewish    schedule 05.02.2015
comment
Это определенный взлом, но в моем случае фреймворк, с которым я работал, ограничивал мои движения. Этот помог, и его можно использовать, если вы знаете, что делаете и почему - спасибо! :) - person Stan Smulders; 10.11.2015
comment
Да, я нашел это действительно полезным в модульных тестах, где вы хотите проверить присвоение частной собственности, но не обязательно делать ее общедоступной. - person drewish; 05.10.2016
comment
ReflectionClass доступен во всех версиях 5.x и не имеет библиотек, требований или необходимых настроек (доступно во всех сборках PHP). - person Phil M; 14.01.2017
comment
@PhilM, но ReflectionProperty::setAccessible - это PHP 5 ›= 5.3.0 - person drewish; 13.02.2018
comment
Отражение - одна из тех вещей, где, если вы обнаружите, что используете ее, вам следует хорошенько присмотреться, чтобы убедиться, что вам это нужно. Тем не менее, всему есть время и место. Я разделяю мнение, что это полезно в модульных тестах, чтобы гарантировать, что объект имеет доступ к данным, которые он должен. В моем случае я использую имитацию очереди Laravel, чтобы проверить не только то, что данное задание было запущено, но и то, что ему были предоставлены правильные данные, хранящиеся в защищенном свойстве. Асинхронность делает это утверждение немного сложным без этого. Спасибо @drewish. - person kmuenkel; 19.04.2018
comment
Сработало безупречно. Преобразование obj в массив не сработало для меня, как этот: stackoverflow.com/a/27754169/9933916 - person Eje; 11.04.2020
comment
@Jee Оба работают на меня. Однако я думаю, что использование отражения, вероятно, было бы проще, если бы вы хотели установить свойство, а не просто получить его. - person mbomb007; 28.10.2020
comment
Я тебя люблю !! сэкономил мне много времени, чертовски охраняемая собственность ... - person Alin Razvan; 25.01.2021

Тип объекта может быть преобразован в (ассоциативный) массив, а защищенные члены имеют ключи с префиксом chr(0).'*'.chr(0) (см. Комментарий @ fardelian здесь). Используя эту недокументированную функцию, вы можете написать экспозера:

function getProtectedValue($obj, $name) {
  $array = (array)$obj;
  $prefix = chr(0).'*'.chr(0);
  return $array[$prefix.$name];
}

Кроме того, вы можете проанализировать значение из сериализованной строки, где (кажется ) защищенные члены имеют одинаковый префикс.

Это работает в PHP 5.2 без накладных расходов на ReflectionClass. Однако есть причины, по которым некоторые свойства защищены и скрыты от клиентского кода. Чтение или запись могут сделать данные несогласованными, или автор предлагает другой способ раскрыть их, чтобы сделать интерфейс как можно более компактным. Когда есть причины для непосредственного чтения защищенного свойства, правильным подходом является реализация __ get (), поэтому всегда проверяйте его наличие.

person Jan Turoň    schedule 03.01.2015
comment
Ах, в этом есть смысл. Когда я попытался просмотреть объект, он просто выглядел как *propertyName, поэтому я был сбит с толку, когда не смог получить к нему доступ с этим. - person mbomb007; 28.10.2020

Если вы хотите повозиться с классом без добавления геттеров и сеттеров ....

PHP 7 добавляет метод call ($ obj) (быстрее, чем старый bindTo) при закрытии, позволяя вам вызывать функцию, поэтому переменная $this будет действовать так же, как в классе - с полными разрешениями.

 //test class with restricted properties
 class test{
    protected $bar="protected bar";
    private $foo="private foo";
    public function printProperties(){
        echo $this->bar."::".$this->foo;   
     }
 }

$testInstance=new test();
//we can change or read the restricted properties by doing this...
$change=function(){
    $this->bar="I changed bar";
    $this->foo="I changed foo";
};
$change->call($testInstance);
$testInstance->printProperties();
//outputs I changed bar::I changed foo in php 7.0 
person user2782001    schedule 05.06.2017
comment
Как раз то, что мне нужно, но поскольку мне просто нужно было защищенное значение var _eventPrefix, его просто нужно изменить: $prefix = function() {return $this->_eventPrefix;}; $result = $prefix->call($obj);. - person kiatng; 11.03.2020

Если вы не можете изменить исходный класс и его расширение тоже не вариант, вы можете использовать интерфейс ReflectionProperty.

В библиотеке phptoolcase есть удобный способ для этого:

$value = PtcHandyMan::getProperty($your_object , 'propertyName');

Статическое свойство из одноэлементного класса:

$value = PtcHandyMan::getProperty('myCLassName', 'propertyName');

Вы можете найти этот инструмент здесь: http://phptoolcase.com/guides/ptc-hm-guide.html

person Charlie    schedule 09.11.2014

Для PHP 7.4+ мы можем использовать стрелочную функцию и Closure::call для доступа к закрытым и защищенным участникам с помощью всего одного маленькая строка:

PHP 7.4+

Получение защищенных / закрытых членов:

class Test {
  protected $data = 'Protected variable!';
}

// Will output "Protected variable!"
echo (fn() => $this->data)->call(new Test);

Изменение защищенных / закрытых членов:

class Test {
  protected $data = 'Testing';
}

$test = new Test;

(fn() => $this->data = "New Data!")->call($test);

// Will output "New Data!"
echo (fn() => $this->data)->call($test);

Конечно, мы можем использовать обычную функцию Closure, если хотим изменить / использовать несколько членов:

class Test {
  protected $data = 'Data!';
}

$test = new Test;

(function() {
  $this->new_data = "New {$this->data}";
})->call($test);

// Will output "New Data!"
echo (fn() => $this->new_data)->call($test);
person Christos Lytras    schedule 19.02.2021

Мне нравится объявлять каждое свойство, доступное для записи извне, как public. Свойства, которые должны быть видны для внешнего мира, но не доступны для записи, следует объявить как protected и написать __get () < / em> магический метод, чтобы вы могли их прочитать. Пример:

/**
 * Class Test
 *
 * @property int $protected
 *
 */
class Test
{
    
    private const READABLE = ['protected'];
    
    protected $protected = 1;
    
    public $public = 2;
    
    public function __get($property)
    {
        //if you want to read every protected or private
        return $this->$property ?? null;
    
        //if you want only some protected and private values to be readable
        if (in_array($property, self::READABLE)) {
            return $this->$property;
        }
    }
}

$test = new Test();
echo $test->protected; //outputs 1
echo $test->public; //outputs 2

$test->protected = 3; //outputs error - protected property

Лучше всего иметь объявление свойства, например:

public readonly $protected = 1; //only readable from the outside
public  $public = 2; //readable and writable from the outside

но такого синтаксиса пока нет (или ... по крайней мере, я об этом не знаю). PS вам следует объявить свойства protected / private, которые будут доступны для чтения в Class DockBlock, как показано, чтобы вы могли их автозаполнить, в противном случае вы иметь к ним доступ, но ваша IDE не распознает их при автозаполнении, когда вы пишете код.

person Lachezar Raychev    schedule 30.05.2021

person    schedule
comment
Любое объяснение, пожалуйста? - person Stephan Vierkant; 08.03.2017
comment
Ответы только на коде мало что делают для тех, кто пытается учиться. Teach a man to fish - хорошая аналогия здесь. - person Regular Jo; 09.03.2017
comment
Ответ следует удалить. Он специфичен для Drupal и использует некоторые настраиваемые поля (например, field_captioned_carousel без контекста для того, для чего они нужны. - person mbomb007; 28.10.2020