PHP Reflection на замыканиях

в 10:32, , рубрики: closure, php, reflection

Привет, Habr! Сегодня хочу рассказать про свой костыль, который помог мне не погружаться в дебри PHP Reflection. Ведь все пишут костыли, просто кто-то пишет большие, а кто-то поменьше.

image

Я активно использую Laravel в своих проектах. Для тех, кто не знаком с этим framework'ом — не отчаивайтесь, потому что я объясню непонятные моменты.

В этот раз я писал некоторое расширение правил валидации:

Validator::extend('someRule', function ($attribute, $value, $parameters, $validator) {
        // some code...
        return $result; // boolean
}, ':attribute is invalid');

И мне потребовалось получить список всех правил, передаваемых валидатору. Как оказалось, эта простая на первый взгляд задача заняла у меня немного больше времени, чем я планировал. Свойство было приватным. И никаких getter'ов для него в реализации класса не было. Изменять класс я конечно же не стал, ибо после composer update эта правка тут же слетит.

Следует сказать, что раньше я никогда не использовал Reflection, но слышал, для чего оно используется. Так вот, я начал читать документацию. Естественно, код из примера с первого раза не завелся и нужно было искать еще. И тут я подумал, что решение должно быть проще.

И я нашел таки простой и элегантный костыль. Все-таки так лучше не делать. Приватные свойства на то и приватные, чтобы туда не лазили. Но обстоятельства требуют, так что...

Validator::extend('someRule', function ($attribute, $value, $parameters, $validator) {

        // Это функция-ниндзя. Она врывается в валидатор и крадет приватное свойство.
        // Это очень коварно и подло но у меня нет другого выбора (есть, но там писать больше)
        $ninja = function() { 
                // именно в этом свойстве хранится массив с нужными мне данными
                return $this->initialRules;
        };
        $initialRules = $ninja->call($validator); // параметр $newThis

        // some code
        return $result;

}, ':attribute is invalid');

Если кто-то еще не понял, объясню: я создал анонимную функцию, которая возвращает некоторое свойство. А потом просто подменил контекст на контекст валидатора (laravel передает экземпляр этого класса). Тоесть, замыкание теперь имеет доступ к этому объекту изнутри и может получить доступ к любому приватному свойству и методу.

Работает вся это красота, начиная с PHP 5.4

Это собственно все, что я хотел рассказать. Может до меня это уже изобрели, но в мою голову это идея пришла сама, поэтому я решил ею поделиться. Вдруг, это решение упростит коме-то жизнь.

Спасибо за внимание.

Автор: HackerZhenya

Источник

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


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