PHP / Как получить доступ ко всем свойствам объекта, не используюя отражение

в 12:26, , рубрики: php, php 5.3, reflection, метки: , ,

Для чего получать доступ ко всем свойствам объекта и при этом не менять его интерфейс? Например, для того, чтобы написать свою сериализацию. Или чтобы передать объект в приемлемом виде используя http. Или для чего-нибудь ещё.
Возьмём для эксперимента простой класс.
class aClass
{
protected $protected_property = 'protected_value';
private $private_property = 'private_value';
public $public_property = 'public_value';
}

$an_object = new aClass;

var_dump($an_object);
// object(aClass)#1 (3) {
// ["protected_property":protected]=>
// string(15) "protected_value"
// ["private_property":"aClass":private]=>
// string(13) "private_value"
// ["public_property"]=>
// string(12) "public_value"
// }

Если использовать отражение, то получить все свойства объекта можно получить, например, вот таким способом.
$an_array = array();
$reflection = new ReflectionClass($an_object);
$properties = $reflection->getProperties();
foreach ($properties as $property)
{
$property->setAccessible(true);
$an_array[$property->getName()] = $property->getValue($an_object);
if (!$property->isPublic())
$property->setAccessible(false);
}

var_dump($an_array);
// array(3) {
// ["protected_property"]=>
// string(15) "protected_value"
// ["private_property"]=>
// string(13) "private_value"
// ["public_property"]=>
// string(12) "public_value"
// }

Есть более простой способ получить все свойства в виде массива.
$an_array = (array) $an_object;

var_dump($an_array);
// array(3) {
// ["�*�protected_property"]=>
// string(15) "protected_value"
// ["�aClass�private_property"]=>
// string(13) "private_value"
// ["public_property"]=>
// string(12) "public_value"
// }

Получилось немного «грязновато», если надо, то можно очистить ключи от лишних данных. Например, вот так:
$key = ($key{0} === "") ? substr($key, strpos($key, "", 1) + 1) : $key;

Кстати, обратный трюк с преобразованием массива в объект не сработает. Таким образом можно получить только объект stdClass.
$an_another_object = (object) $an_array;

var_dump($an_another_object);
// object(stdClass)#6 (3) {
// ["protected_property":protected]=>
// string(15) "protected_value"
// ["private_property":"aClass":private]=>
// string(13) "private_value"
// ["public_property"]=>
// string(12) "public_value"
// }

Есть ещё недокументированный способ получения свойств. Этот способ даже похоже на баг, но таким не является, поэтому его можно смело использовать.
$an_array = array();
reset($an_object);
while (list($key, $val) = each($an_object))
$an_array[$key] = $val;

var_dump($an_array);
// array(3) {
// ["�*�protected_property"]=>
// string(15) "protected_value"
// ["�aClass�private_property"]=>
// string(13) "private_value"
// ["public_property"]=>
// string(12) "public_value"
// }

В свою очередь, чтобы установить новое значение для произвольного свойства (в том числе и приватного), можно использовать уже другой недокументированный приём.
$an_array["aClassprivate_property"] = 'new_private_value';
array_walk($an_object, function(&$val, $key, $array){$val = $array[$key];}, $an_array);

var_dump($an_object);
// object(aClass)#1 (3) {
// ["protected_property":protected]=>
// string(15) "protected_value"
// ["private_property":"aClass":private]=>
// string(17) "new_private_value"
// ["public_property"]=>
// string(12) "public_value"
// }

Заключение
Используя тот или иной случай помните, что таким способом вы вероломно вторгаетесь в защищённую область объекта, тем самым обойдёте все выставленные защиты в виде protected или private, а объект об этом даже ничего и не узнает. Поэтому, если есть возможность, то лучше сделать специальные методы доступа.


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js