Привет, Хабр! Представляю вашему вниманию перевод статьи "Introduction to PHP Reflection API" автора Mustafa Magdi.
Когда я начал программировать на PHP, то не знал о возможностях Reflection API. Главная причина в том, что мне не нужно было проектировать свои простые классы, модули или даже пакеты. Затем я обнаружил, что это играет главную роль во многих областях. В статье мы рассмотрим Reflection API по следующим пунктам:
Класс Profile — чёрный ящик. Используя Reflection API вы сможете прочитать, что там находится внутри:
Таким образом ReflectionClass выступает аналитиком для нашего класса Profile, и в этом состоит главная идея Reflection API.
PHP даёт вам ключ к любому запертому ящику, таким образом мы имеем ключи
для следующего:
Полный список вы можете изучить на php.net
Для использования классов Reflection API нет необходимости что-либо устанавливать или настраивать, так как они входят в состав ядра PHP.
Далее представлено несколько примеров того, как мы можем использовать Reflection API:
Пример 1:
Получить родительский класс для определённого класса:
Пример 2:
Получить документацию метода
Пример 3:
Может использоваться как
Пример 4:
В некоторых ситуациях вы можете застрять с unit-тестированием и задаться вопросом: «Как я могу протестировать закрытую функцию?!»
Не беспокойтесь, вот хитрость:
Предыдущие примеры довольно просты, но есть другие примеры, в которых вы можете увидеть, как Reflection API используется более обширно:
PHP предоставляет полноценный Reflection API, который помогает легко достичь различные области ООП-структур.
От переводчика:
Также можно посмотреть пример использования Reflection API в пакете Codeception в классе Stub.
Этот класс через рефлексию помогает мо?кать методы и свойства в unit-тестах.
Следует добавить, что Reflection API работает довольно медленно, по этому не стоит сильно увлекаться. Использовать рекомендуется в тестах или во время отладки, но если можно обойтись без него, то лучше так и сделать. И категорически не рекомендуется использовать в рабочем коде проекта, т.к. это ещё и не безопасно.
Как в PHP анализировать структуру данных
Вступление
Когда я начал программировать на PHP, то не знал о возможностях Reflection API. Главная причина в том, что мне не нужно было проектировать свои простые классы, модули или даже пакеты. Затем я обнаружил, что это играет главную роль во многих областях. В статье мы рассмотрим Reflection API по следующим пунктам:
1. Что такое Reflection API
В информатике отражение или рефлексия (холоним интроспекции, англ. reflection) означает процесс, во время которого программа может отслеживать и модифицировать собственную структуру и поведение во время выполнения. — Wikipedia.Что означает возможность остановить и взглянуть внутрь своего кода (reverse-engineering)? Давайте посмотрим на следующий фрагмент кода:
/**
* Class Profile
*/
class Profile
{
/**
* @return string
*/
public function getUserName(): string
{
return 'Foo';
}
}
Класс Profile — чёрный ящик. Используя Reflection API вы сможете прочитать, что там находится внутри:
// инициализация
$reflectionClass = new ReflectionClass('Profile');
// получить имя класса
var_dump($reflectionClass->getName());
=> output: string(7) "Profile"
// получить документацию класса
var_dump($reflectionClass->getDocComment());
=> output:
string(24) "/**
* Class Profile
*/"
Таким образом ReflectionClass выступает аналитиком для нашего класса Profile, и в этом состоит главная идея Reflection API.
PHP даёт вам ключ к любому запертому ящику, таким образом мы имеем ключи
для следующего:
ReflectionClass: сообщает информацию о классе.
ReflectionFunction: сообщает информацию о функции.
ReflectionParameter: извлекает информацию о параметрах функции или метода.
ReflectionClassConstant: сообщает информацию о константе класса.
Полный список вы можете изучить на php.net
2. Установка и конфигурирование
Для использования классов Reflection API нет необходимости что-либо устанавливать или настраивать, так как они входят в состав ядра PHP.
3. Примеры использования
Далее представлено несколько примеров того, как мы можем использовать Reflection API:
Пример 1:
Получить родительский класс для определённого класса:
// дочерний класс
class Child extends Profile
{
}
$class = new ReflectionClass('Child');
// получаем список всех родителей
print_r($class->getParentClass()); // ['Profile']
Пример 2:
Получить документацию метода
getUserName()
:$method = new ReflectionMethod('Profile', 'getUserName');
var_dump($method->getDocComment());
=> output:
string(33) "/**
* @return string
*/"
Пример 3:
Может использоваться как
instanceOf
и is_a()
для валидации объектов:$class = new ReflectionClass('Profile');
$obj = new Profile();
var_dump($class->isInstance($obj)); // bool(true)
// такой же как
var_dump(is_a($obj, 'Profile')); // bool(true)
// такой же как
var_dump($obj instanceof Profile); // bool(true)
Пример 4:
В некоторых ситуациях вы можете застрять с unit-тестированием и задаться вопросом: «Как я могу протестировать закрытую функцию?!»
Не беспокойтесь, вот хитрость:
// добавим закрытый метод getName()
private function getName(): string
{
return 'Foo';
}
$method = new ReflectionMethod('Profile', 'getUserName');
// проверим является ли метод закрытым и сделаем его доступным
if ($method->isPrivate()) {
$method->setAccessible(true);
}
echo $method->invoke(new Profile()); // Foo
Предыдущие примеры довольно просты, но есть другие примеры, в которых вы можете увидеть, как Reflection API используется более обширно:
- Генератор документации к API: пакет lavarel-apidoc-generator широко использует ReflectionClass и ReflrectionMethod для получения и последующей обработки информации о блоках документации классов и методов, и оформления этих блоков кода.
- Dependency Injection Container: проверить всю тему вы можете здесь
4. Заключение
PHP предоставляет полноценный Reflection API, который помогает легко достичь различные области ООП-структур.
5. Ссылки
От переводчика:
Также можно посмотреть пример использования Reflection API в пакете Codeception в классе Stub.
Этот класс через рефлексию помогает мо?кать методы и свойства в unit-тестах.
Следует добавить, что Reflection API работает довольно медленно, по этому не стоит сильно увлекаться. Использовать рекомендуется в тестах или во время отладки, но если можно обойтись без него, то лучше так и сделать. И категорически не рекомендуется использовать в рабочем коде проекта, т.к. это ещё и не безопасно.
EvgeniiR
Спасибо за статью!
Функционал Reflection, конечно, интересная вещь. Жалко что места в реальных проектах ему места практически нету(кроме довольно специфических задач, в т.ч. описанных в статье)
ErickSkrauch
Совсем нет. Ни в одном.
amakhrov
В коде приложения, скорее всего, таким заниматься не надо (ну, разве что колхозишь какой-то свой велосипед).
В коде же фреймворков и библиотек может встречаться довольно часто.
Допустим, PHP-DI активно использует рефлексию для реализации автосвязывания (когда DI контейнер по сигнатуре конструктора сам определяет, что в него надо внедрять).
Или Doctrine — на основе описания модели (обычный PHP класс со специальными phpdoc-аннотациями) генерирует sql-скрипты.
Тот же phpunit — разработчик пишет тестовый класс в соответствии с определенными соглашениями (имена методов, phpdoc-аннотации), а тестовый фреймворк через рефлексию определяет, что и как в этом тесте надо запускать.
OnYourLips
А как вы приватные методы тестируете?
EvgeniiR
Ну без Reflection для таких случаев иногда делают методы protected вместо private, но это в любом случае плохой тон, по хорошему тестировать нужно интерфейс.
SerafimArts
Приватные методы можно и без рефлексии вызывать, как и получать инфу из приватных полей.
Никто же пока не отменял контексты:
ProgrammSM Автор
Помимо рефлексии можно ещё через Closure с замыканием:
$privateProperty в данном случае будет содержать ссылку на приватное свойство, которое можем смотреть и изменять. Работает, к слову, быстрее рефлекции.
amakhrov
Принципиально не тестирую приватные методы. Тестирую публичный интерфейс.
Если что-то приватное очень хочется протестировать, скорее всего, это означает, что эта приватная логика — сама по себе отдельная ответственность. И выносится в отдельный класс (в соответствии с SRP).
OnYourLips
Иногда классы очень сильно разрастаются, до нескольких тысяч строк кода, и без тестирования приватных методов уже не обойтись.
Весь код не выйдет держать в идеальном порядке, особенно когда разработчиков не три человека, тогда и приходится тестировать приватные методы.
EvgeniiR
Но ведь по вашей же ссылке «218 code results in symfony/symfony»? Либы для работы с аннотацииями, умный DI, тестирование и подобные штуки вполне могут быть написаны через Reflection, а к приложениям они просто подключаются в виде готовых библиотек (phpunit, codeception, symfony annotations etc.)
VokaMut
Например в Laravel:
Запрос: site.ru/post/1
В маршрутах написано правило 'post/{post}'
Action в контроллере тебе уже не нужно делать запрос, и определять класс Request, оба параметры будут распознаны Reflection API и переданы: