Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Получаем доступ к приватным свойствам объектов в PHP без рефлексии

Anna | 31.05.2014 | нет комментариев

Несколько недель назад я работал над загвоздкой в ProxyManager. Задача была примитивна: ReflectionClass иReflectionProperty дюже, дюже, и ооочень неторопливые!
Поводом этого изыскания является моя попытка оптимизировать “hydrator” для работы с крупными объемами данных без убыточных затрат на инициализацию.

PHP 5.4 спасай!

PHP 5.4 дал нам новое API для замыканий и способ Closure#bind().
Closure#bind() в тезисе разрешает получить экземпляр замыкания с областью видимости данного класса, либо объекта. Изысканно! Вот так дозволено добавить API к присутствующим объектам!
Давайте же нарушим инкапсуляцию объектов в соответствии с нашими надобностями.
Способы доступа к приватным свойствам теснее объяснялись в документации, но я все равно приведу пример.
Украдем приватное качество Kitchen#yummy:

<?php

class Kitchen
{
    private $yummy = 'cake';
}

Определим замыкание для приобретения этого поля:

<?php

$sweetsThief = function (Kitchen $kitchen) {
    return $kitchen->yummy;
}

А сейчас украдем yummy из экземпляра Kitchen:

<?php

$kitchen = new Kitchen();

var_dump($sweetsThief($kitchen));

К сожалению, мы получим неизбежную ошибку в $sweetsThief:

Fatal error: Cannot access private property Kitchen::$yummy in [...] on line [...]

Сделаем нашего похитителя разумнее Closure#bind():

<?php

$kitchen = new Kitchen();

// Closure::bind() на самом деле создает новое замыкание
$sweetsThief = Closure::bind($sweetsThief, null, $kitchen);

var_dump($sweetsThief($kitchen));

Успех!

Closure::bind vs Reflection: быстродействие

Я сделал легкой бенчмарк для 100000 итераций инициализации:

<?php

for ($i = 0; $i < 100000; $i  = 1) {
    $sweetsThief = Closure::bind(function (Kitchen $kitchen) {
        return $kitchen->yummy;
    }, null, 'Kitchen');
}

<?php

for ($i = 0; $i < 100000; $i  = 1) {
    $sweetsThief = new ReflectionProperty('Kitchen', 'yummy');
    $sweetsThief->setAccessible(true);
}

На только что скомпилированном PHP 5.5 (Ubuntu 13.04 amd64 box), 1-й тест занял 0.325 секунд, а 2-й 0.658.

Рефлексия тут значительно неторопливей.(На 49%)

Но это вовсе не увлекательно, так как никому не понадобится инициализировать 100000 раз одно и то же, по крайней мере мне верно. Что на самом деле увлекательно — так это доступ к приватным свойствам. Я протестировал и это тоже:

<?php

$kitchen = new Kitchen();

$sweetsThief = Closure::bind(function (Kitchen $kitchen) {
    return $kitchen->yummy;
}, null, 'Kitchen');

for ($i = 0; $i < 100000; $i  = 1) {
    $sweetsThief($kitchen);
}

<?php

$kitchen = new Kitchen();

$sweetsThief = new ReflectionProperty('Kitchen', 'yummy');
$sweetsThief->setAccessible(true);

for ($i = 0; $i < 100000; $i  = 1) {
    $sweetsThief->getValue($kitchen);
}

1-й тест занял ~ 0.110 секунд, а 2-й ~ 0.199!
Это значительно стремительней рефлексии, впечатляет!(На 55%)

Доступ к приватным свойствам по ссылкам

Есть еще одно превосходство, применяя замыкания взамен рефлексии мы можем трудиться с свойствами объекта по ссылкам!

<?php

$sweetsThief = Closure::bind(function & (Kitchen $kitchen) {
    return $kitchen->yummy;
}, null, $kitchen);

$cake = & $sweetsThief($kitchen);
$cake = 'lie';

var_dump('the cake is a ' . $sweetsThief($kitchen));

Многофункциональный способ доступа

Рассматривая выше сказанное, мы можем написать примитивную обертку для приобретения всякого свойства всякого объекта:

<?php

$reader = function & ($object, $property) {
    $value = & Closure::bind(function & () use ($property) {
        return $this->$property;
    }, $object, $object)->__invoke();

    return $value;
};

$kitchen = new Kitchen();
$cake    = & $reader($kitchen, 'cake');
$cake    = 'sorry, I ate it!';

var_dump($kitchen);

Рабочий пример.
У нас есть доступ к любому свойству, в любом месте, и даже по ссылке. Ура! Мы нарушили правила еще раз!

Завершение

В следующий раз PHP показал себя с отличной и дрянный стороны единовременно. Это жуткий язык, с жутким синтаксисом, но он разрешает нам обходить всякие языковые ограничения радуя нас новой функциональностью с всяким релизом.
Я не буду применять эту технику сам, но если мне понадобится получить приватное качество объекта, да еще и по ссылке, я сделаю это именно так.

Дисклеймер: используйте данную вероятность с осторожностью!

Источник: programmingmaster.ru

Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB