Web

Как получить защищенное свойство объекта в 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, поэтому я не могу использовать эту функцию там. Есть ли решение, как получить такое свойство?

 

Ответ 1

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

function accessProtected($obj, $prop) {

  $reflection = new ReflectionClass($obj);

  $property = $reflection->getProperty($prop);

  $property->setAccessible(true);

  return $property->getValue($obj);

}

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

 

 Ответ 2

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

function getProtectedValue($obj, $name) {

  $array = (array)$obj;

  $prefix = chr(0).'*'.chr(0);

  return $array[$prefix.$name];

}

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

 

Ответ 3

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

//тестовый класс с ограниченными свойствами

 class test{

    protected $bar="protected bar";

    private $foo="private foo";

    public function printProperties(){

        echo $this->bar."::".$this->foo;   

     }

 }

$testInstance=new test();

//мы можем изменить или прочитать ограниченные свойства, сделав это...

$change=function(){

    $this->bar="I changed bar";

    $this->foo="I changed foo";

};

$change->call($testInstance);

$testInstance->printProperties();

//вывод здесь bar::I changed foo in php 7.0 

 

 Ответ 4

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

PHP 7.4+

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

class Test {

  protected $data = 'Protected variable!';

}

// Будет выведено "Защищенная переменная!".

echo (fn() => $this->data)->call(new Test);

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

class Test {

  protected $data = 'Testing';

}

$test = new Test;

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

// Будет выведено "Новые данные!".

echo (fn() => $this->data)->call($test);

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

class Test {

  protected $data = 'Data!';

}

$test = new Test;

(function() {

  $this->new_data = "New {$this->data}";

})->call($test);

// Будет выведено "Новые данные!".

echo (fn() => $this->new_data)->call($test);

 

 Ответ 5

Я предпочитаю объявлять все свойства, которые могут быть доступны для записи извне, как public. Свойства, которые вы хотите видеть для доступа из любого места, но не записывать, следует объявить как protected и создать «магический» метод __get(), чтобы вы могли их прочитать. Пример:

/**

 * Class Test

 *

 * @property int $protected

 *

 */

class Test {

    private const READABLE = ['protected'];

    protected $protected = 1;

    public $public = 2;

    public function __get($property) {

        //если вы хотите прочитать любой защищенный или приватный член

        return $this->$property ?? null;

        //если вы хотите, чтобы только некоторые защищенные и частные значения были доступны для чтения

        if (in_array($property, self::READABLE)) {

            return $this->$property;

        }

    }

}

$test = new Test();

echo $test->protected; //1вывод

echo $test->public; // 2 вывод

$test->protected = 3; //выдает ошибку - защищенное свойство

 Лучше всего было бы иметь декларацию о приватности, например:

public readonly $protected = 1; // только для чтения снаружи

public  $public = 2; //читаемый и записываемый извне

 Но такого синтаксиса пока не существует (или... по крайней мере, я о нем не знаю).

Вы должны объявить свойства protected/private, которые будут доступны для чтения в Class DocBlock, чтобы вы могли авто заполнить их, иначе вы сможете получить к ним доступ, но ваша IDE не распознает их при автозаполнении во время написания кода.

 

Ответ 6

Если вы не можете изменить исходный класс, а расширять его тоже не вариант, вы можете использовать интерфейс ReflectionProperty. В библиотеке phptoolcase есть удобный метод для этого:

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

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

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

 Вы можете найти инструмент здесь: 

http://phptoolcase.com/guides/ptc-hm-guide.html

 

Схожие статьи

Web

Как подключиться к нескольким базам данных MySQL на одной веб-странице

Web

Как преобразовать серию «родитель-потомок» в иерархическое дерево

Web

Как я могу измерить скорость кода, написанного на PHP

Web

Как я могу добавить динамически созданный URL-адрес в переменную PHP?