Что такое рефлексия php

Введение в PHP Reflection API

Привет, Хабр! Представляю вашему вниманию перевод статьи «Introduction to PHP Reflection API» автора Mustafa Magdi.

Как в PHP анализировать структуру данных

image loader

Вступление

Когда я начал программировать на PHP, то не знал о возможностях Reflection API. Главная причина в том, что мне не нужно было проектировать свои простые классы, модули или даже пакеты. Затем я обнаружил, что это играет главную роль во многих областях. В статье мы рассмотрим Reflection API по следующим пунктам:

1. Что такое Reflection API

В информатике отражение или рефлексия (холоним интроспекции, англ. reflection) означает процесс, во время которого программа может отслеживать и модифицировать собственную структуру и поведение во время выполнения. — Wikipedia.

Что означает возможность остановить и взглянуть внутрь своего кода (reverse-engineering )? Давайте посмотрим на следующий фрагмент кода:

Класс Profile — чёрный ящик. Используя Reflection API вы сможете прочитать, что там находится внутри:

Таким образом ReflectionClass выступает аналитиком для нашего класса Profile, и в этом состоит главная идея Reflection API.

PHP даёт вам ключ к любому запертому ящику, таким образом мы имеем ключи
для следующего:

ReflectionClass: сообщает информацию о классе.
ReflectionFunction: сообщает информацию о функции.
ReflectionParameter: извлекает информацию о параметрах функции или метода.
ReflectionClassConstant: сообщает информацию о константе класса.

Полный список вы можете изучить на php.net

2. Установка и конфигурирование

Для использования классов Reflection API нет необходимости что-либо устанавливать или настраивать, так как они входят в состав ядра PHP.

3. Примеры использования

Далее представлено несколько примеров того, как мы можем использовать Reflection API:

Пример 1:
Получить родительский класс для определённого класса:

Пример 2:
Получить документацию метода getUserName() :

Пример 3:
Может использоваться как instanceOf и is_a() для валидации объектов:

Пример 4:
В некоторых ситуациях вы можете застрять с unit-тестированием и задаться вопросом: «Как я могу протестировать закрытую функцию?!»

Не беспокойтесь, вот хитрость:

Предыдущие примеры довольно просты, но есть другие примеры, в которых вы можете увидеть, как Reflection API используется более обширно:

4. Заключение

PHP предоставляет полноценный Reflection API, который помогает легко достичь различные области ООП-структур.

5. Ссылки

Также можно посмотреть пример использования Reflection API в пакете Codeception в классе Stub.
Этот класс через рефлексию помогает мо́кать методы и свойства в unit-тестах.

Следует добавить, что Reflection API работает довольно медленно, по этому не стоит сильно увлекаться. Использовать рекомендуется в тестах или во время отладки, но если можно обойтись без него, то лучше так и сделать. И категорически не рекомендуется использовать в рабочем коде проекта, т.к. это ещё и не безопасно.

Источник

PHP Reflection API

Попробуем теперь перенести это понятие из жизни человека на время выполнения программы. Получим что-то типа того, что программа во время своего выполнения может в реальном времени «узнавать» о своём состоянии и изменять своё поведение. Википедия же предлагает следующее определение: «Рефлексия означает процесс, во время которого программа может отслеживать и модифицировать собственную структуру и поведение во время выполнения.»

В PHP имеется очень мощный набор инструментов, позволяющий реализовать рефлексию. Рассмотрим некоторые инструменты для рефлексии, о которых мы уже знаем. Языковые конструкции self и static, магические константы __DIR__ и __CLASS__, функции get_defined_vars(), func_get_args() или eval(). В конце концов возможность создавать объект класса, имя которого хранится в переменной:

а затем и вызов метода, название которого так же хранится в переменной:

Всё это рефлексия, всё это используется для того, чтобы влиять на поведение программы непосредственно во время её выполнения.

Однако есть в PHP кое-что ещё более мощное — это PHP Reflection API.
PHP Reflection API – это набор специальных классов-рефлекторов, позволяющих вывести рефлексию на новый уровень. С помощью этих классов мы можем создавать объекты-рефлекторы для разных типов данных в PHP, которые позволят творить с ними всё что только вздумается.

Перейдём к практике и рассмотрим класс-рефлектор для функций — ReflectionFunction.

Создадим новую функцию:

Теперь создадим объект-рефлектор для неё:

После чего нам становится доступен ряд методов у этого объекта, благодаря которым мы можем делать с этой функцией очень интересные вещи. Например, узнать в каком файле объявлена функция:

или узнать строки её начала и конца:

Ещё мы можем получить комментарий к функции в формате PHPDoc (почитайте о PHPDoc, если до сих пор этого не сделали):

Все методы мы рассматривать не будем, если стало интересно — почитайте документацию.

Рефлексия объектов

Помимо этого можно создавать рефлекторы объектов. Давайте попробуем создать объект-рефлектор для нашей сущности Article.

Сделаем это в контроллере для статей. И давайте сразу используем этот рефлектор для вывода свойств объекта Article.

Посмотрим на результат:
f7f659eccf0a64a8740c9a544455f67f622ed25881cf77c71ea286d905bac486

А теперь давайте просто создадим массив, содержащий только имена свойств, в виде строк.

3c56ab59de284be1676c15cef59007087fcbd151a668635ea3445157c37835ee

Эти знания понадобятся нам в следующем уроке. А пока ещё несколько интересных методов для рефлектора объектов.

Получить все методы:

Получить все константы:

Создание нового объекта (даже с непубличным конструктором)

Создание нового объекта без вызова конструктора (o_O)

Этих знаний достаточно, чтобы начать использовать Reflection API. Главное помните — использования этого инструмента следует по возможности избегать, так как это работает довольно медленно. Большинство задач можно решить без использования рефлексии, но знать о ней настоящий профи обязан. Иногда её использование позволяет создать довольно изящные решения, одно из которых мы рассмотрим в следующем уроке. А со всеми возможные рефлекторами можно ознакомиться в официальной документации. До встречи 😉

Источник

Про Reflect API доступным языком

0764232f2001455d83c3ac48a68525e8

Всем привет! Недавно услышал, как одни молодые фронтендеры пытались объяснить другим молодым фронтендерам, что такое Reflect в JavaScript. В итоге кто-то сказал, что это такая же штука, как прокси. Ситуация напомнила мне анекдот:

Встречаются два майнера:
— Ты что-нибудь понимаешь в этом?
— Ну объяснить смогу.
— Это понятно, но ты что-нибудь понимаешь в этом?

Вот и с Reflect в JS для кого-то получилась такая же ситуация. Вроде бы что-то говорят, а для чего — непонятно. В итоге я подумал, что стоит об этом рассказать еще раз простым языком с примерами.

Сначала дадим определение, что такое рефлексия в программировании:
Reflection/Reflect API — это API, который предоставляет возможность проводить реверс-инжиниринг классов, интерфейсов, функций, методов и модулей.

Отсюда становится немного понятнее, для чего это API должно использоваться. Reflection API существует в разных языках программирования и, порой, используется для обхода ограничений, накладываемых ЯП. Также он используется для разработки различных вспомогательных утилит и для реализации различных паттернов (таких как Injection) и много чего еще.

Например, Reflection API есть в Java. Он используется для просмотра информации о классах, интерфейсах, методах, полях, конструкторах и аннотациях во время выполнения java программ. К примеру, с помощью Reflection в Java можно использовать ООП паттерн — Public Morozov.

В PHP тоже существует Reflection API, который позволяет не только делать реверс-инжиниринг, но даже позволяет получать doc-блоки комментариев, что используется в различных системах автодокументирования.

В JavaScript Reflect — это встроенный объект, который предоставляет методы для перехватывания JavaScript операций. По сути, это неймспейс (как и Math). Reflect содержит в себе набор функций, которые называются точно так же, как и методы для Proxy.

Некоторые из этих методов — те же, что и соответствующие им методы класса Object или Function. JavaScript растет и превращается в большой и сложный ЯП. В язык приходят различные вещи из других языков. На сегодня Reflect API умеет не так много, как в других ЯП. Тем не менее, есть предложения по расширению, которые еще не вошли в стандарт, но уже используются. Например, Reflection Metadata.

Можно сказать, что неймспейс Reflect в JS — это результат рефакторинга кода. Мы уже пользовались ранее возможностями Reflect API, просто все эти возможности были вшиты в базовый класс Object.

Reflect Metadata / Metadata Reflection

Это API создано для получения информации об объектах в рантайме. Это proposal, который пока не является стандартом. Сейчас активно используется полифил. На сегодняшний день активно применяется в Angular. С помощью этого API реализованы Inject и декораторы (анотаторы).

Собственно ради Angular в TypeScript был добавлен расширенный синтаксис декораторов. Одной из интересных особенностей декораторов является возможность получать информацию о типе декорируемого свойства или параметра. Чтобы это заработало, нужно подключить библиотеку reflect-metadata, которая расширяет стандартный объект Reflect и включить опцию emitDecoratorMetadata к конфиге TS. После этого для свойств, которые имеют хотя бы один декоратор, можно вызвать Reflect.getMetadata с ключом «design:type».

В чем различие Reflect от Proxy?

Reflect — это набор полезных методов для работы с объектами, половина которых — это переписанные уже существующие из Object. Сделано это с целью улучшения семантики и наведения порядка, так как Object — это базовый класс, но при этом он содержит очень много методов, которые не должны в нем находиться. Также если вы создаете объект с пустым прототипом, то у вас исчезают методы рефлексии (ниже покажу на примере, что это значит).

Proxy — это класс, который всегда создает новый объект с установленными обработчиками для перехвата доступа. Он позволяет отлавливать любые действия с объектом и модифицировать их. Для реализации различной логики часто применяется Reflect. Ниже на примерах это будет хорошо видно.

Use Cases

Ну и рассмотрим способы применения Reflect API. Некоторые примеры уже давно известны, просто для этих целей мы привыкли использовать методы из класса Object. Но было бы правильнее, по логике, использовать их из пакета Reflect (пакеты — терминология из Java).

Автогенерируемые поля объекта

Мы можем создать объект, в котором поля объекта будут создаваться автоматически во время доступа к ним

Все круто, но такой объект нельзя сериализовать в JSON, получим ошибку. Добавим магический метод сериализации — toJSON

Динамический вызов конструктора

Но хотим уметь динамически вызывать конструктор и создавать объект. Для этого есть Reflect.construct:

Может понадобиться для использования в фабриках (ООП гайз поймут). Пример:

Как такое пишется в 2017 году:

Повторяем поведение jQuery

Следующая строка показывает как можно сделать jQuery в 2 строки:

Удобно, если нужно что-то быстро наваять без зависимостей, а писать длинные нативные конструкции лень. Но в этой реализации есть минус — выбрасывает исключение при работе с null:

Используя Proxy и Reflect можем переписать этот пример:

Теперь при попытке обращения к null свойствам просто будем получать undefined:

Так почему же надо использовать Reflect?

Reflect API более удобен при обработке ошибок. К примеру, всем знакома инструкция:
Object.defineProperty(obj, name, desc)

В случае неудачи будет выброшено исключение. А вот Reflect не генерит исключений на все подряд, а умеет возвращать булев результат:

Это позволяет обрабатывать ошибки через условия, а не try-catch. Пример применения Reflect API с обработкой ошибки:

А теперь можно писать так:

Но надо сказать, что есть случаи, когда Reflect также выбрасывает исключения.

Некоторые записи выходят короче

Разница в поведении

Вроде бы все понятно. Делаем выводы, что лучше. Reflect API более логичный.

Работа с объектами с пустым прототипом

Как видите, мы уже не имеем методов рефлексии, например, hasOwnProperty. Поэтомы мы либо пользуемся старым способом, обращаясь к прототипу базового класса, либо обращаемся к Reflect API:

Источник

Класс ReflectionMethod

Введение

Класс ReflectionMethod сообщает информацию о методах.

Обзор классов

Свойства

Предопределённые константы

Модификаторы ReflectionMethod

ReflectionMethod::IS_PUBLIC

ReflectionMethod::IS_PROTECTED

ReflectionMethod::IS_PRIVATE

ReflectionMethod::IS_ABSTRACT

ReflectionMethod::IS_FINAL

Значения этих констант могут изменяться от версии к версии PHP. Рекомендуется всегда использовать константы и не полагаться напрямую на значения.

Содержание

User Contributed Notes 3 notes

We can make a «Automatic dependenci injector» in classes when her constructors depends other classes (with type hint).

class Dependence1 <
function foo () <
echo «foo» ;
>
>

class Dependence2 <
function foo2 () <
echo «foo2» ;
>
>

// Automatic dependence injection (CLASSES)

object(myClass)#6 (2) <
[«dep1″:»myClass»:private]=>
object(Dependence1)#4 (0) <
>
[«dep2″:»myClass»:private]=>
object(Dependence2)#5 (0) <
>
>

I have written a function which returns the value of a given DocComment tag.

class Example
<
/**
* This is my DocComment!
*
* @DocTag: prints Hello World!
*/
public function myMethod ()
<
echo ‘Hello World!’ ;
>
>

?>

Maybe you can add this functionality to the getDocComment methods of the reflection classes.

Источник

Рефлексия

Программный интерфейс Reflection API для PHP версии 5 — то же самое, что и API-интерфейс System.Reflection сборки mscorlib.dll для C#. Он состоит из встроенных классов для анализа свойств, методов и классов. В некоторых отношениях он напоминает рассмотренные в предыдущей статье функции для работы с объектами, такие как get_class_vars(), но является более гибким и позволяет учитывать больше нюансов. Он также предназначен для более эффективной работы с объектно-ориентированными средствами PHP, такими как управление доступом, интерфейсы и абстрактные классы. Старые, более ограниченные, функции классов так работать не могут.

Основные сведения

Интерфейс Reflection API можно использовать для исследования не только классов. Например, класс ReflectionFunction предоставляет информацию о заданной функции, a ReflectionExtension — информацию о скомпилированных расширениях языка PHP. В таблице ниже перечислены некоторые классы интерфейса Reflection API:

Некоторые классы интерфейса Reflection API

Класс Описание
Reflection Содержит статический метод export(), предоставляющий итоговую информацию о классе
ReflectionClass Позволяет получить информацию о классе и содержит средства для работы с ним
ReflectionMethod Позволяет получить информацию о методах класса и содержит средства для работы с ними
ReflectionParameter Позволяет получить информацию об аргументах метода
ReflectionProperty Позволяет получить информацию о свойствах класса
ReflectionFunction Позволяет получить информацию о функциях и содержит средства для работы с ними
ReflectionExtension Позволяет получить информацию о расширениях PHP
ReflectionException Предназначен для обработки ошибок

Некоторые классы из Reflection API позволяют во время выполнения программы получить просто беспрецедентную информацию об объектах, функциях и расширениях языка, содержащихся в сценарии, которую раньше невозможно было добыть. Из-за более широких возможностей и большей эффективности, во многих случаях следует использовать интерфейс Reflection API, а не функции для работы с классами и объектами. И вскоре вы поймете, что это незаменимый инструмент для исследования классов. Например, с его помощью можно создавать диаграммы классов или документацию, сохранять информацию об объекте в базе данных, исследовать методы доступа объекта (установщики или получатели), которые используются для извлечения значений полей. Создание инфраструктуры, которая вызывает методы в классах модуля в соответствии с принятой схемой наименования — это еще один пример применения интерфейса Reflection.

Мы уже встречались с некоторыми функциями для изучения атрибутов классов. Они полезны, но обычно довольно ограниченны. А теперь давайте рассмотрим инструмент, который готов к выполнению данной работы. Класс ReflectionClass предоставляет методы, которые собирают информацию обо всех аспектах заданного класса, независимо от того, внутренний это класс или определенный пользователем. Конструктору класса ReflectionClass в качестве единственного аргумента передается имя класса, как показано ниже:

Создав объект типа ReflectionClass, вы можете использовать вспомогательный класс Reflection для создания итоговой информации о классе CDProduct. У класса Reflection есть статический метод export(), который форматирует и создает дамп данных, содержащихся в объекте типа Reflection (точнее, в любом экземпляре класса, который реализует интерфейс Reflector). Вот фрагмент вывода, полученного в результате вызова метода Reflection::export():

img124Исчерпывающие сведения о классе, полученные с помощью рефлексии

Как видите, метод Reflection::export() предоставляет отличный доступ к информации о классе. Этот метод дает обобщенную информацию почти о всех аспектах класса CDProduct, включая состояние доступа к свойствам и методам, аргументы, необходимые каждому методу, реализованные интерфейсы и расположение каждого метода внутри файла сценария.

Исследование класса

Метод Reflection::export() предоставляет много полезной информации для отладки, но интерфейс Reflection API можно использовать особым образом. Давайте будем работать непосредственно с классами Reflection. Вы уже знаете, как создать экземпляр объекта ReflectionClass. А теперь давайте используем объект ReflectionClass, чтобы исследовать класс CDProduct в процессе выполнения сценария. Мы хотим узнать, к какому типу класса он относится и можно ли создать его экземпляр? Вот функция, которая поможет ответить на эти вопросы:

Метод ReflectionClass::getName() возвращает имя исследуемого класса. Метод ReflectionClass::isUserDefined() возвращает истинное значение, если класс был объявлен в PHP-коде. Метод ReflectionClass::isInternal() возвращает истинное значение, если класс является встроенным. С помощью метода ReflectionClass::isAbstract() можно проверить, является ли класс абстрактным. Метод ReflectionClass::isInterface() позволяет узнать, является ли класс интерфейсом.

img125Определение параметров класса с помощью рефлексии

Если вы хотите создать экземпляр класса, то с помощью метода ReflectionClass::isInstantiable() можно проверить, осуществимо ли это. Можно исследовать даже исходный код класса, определенного пользователем. Объект ReflectionClass позволяет получить имя файла класса и номера первой и последней строк в этом файле, в которых этот класс определяется. Вот простой метод, в котором объект типа ReflectionClass используется для доступа к исходному коду класса:

Как видите, класс ReflectionUtil очень прост и содержит единственный статический метод ReflectionUtil::getClassSource(). Этому методу в качестве единственного аргумента передается объект типа ReflectionClass, а он возвращает исходный код класса. Метод ReflectionClass::getFileName() возвращает абсолютное имя файла класса, поэтому код должен работать без проблем и открыть данный исходный файл. Функция file() возвращает массив всех строк в файле. Метод ReflectionClass::getStartLine() возвращает номер начальной строки в исходном файле, а метод ReflectionClass::getEndLine() — номер последней строки, в которых содержится определение класса. И теперь для получения нужных строк достаточно воспользоваться функцией array_slice().

Для компактности в этом коде опущена обработка ошибок. Но в реальном приложении нужно будет проверять аргументы и коды возврата.

img126Определение исходного кода класса с помощью рефлексии

Исследование методов

Так же как ReflectionClass используется для исследования класса, объект типа ReflectionMethod применяется для исследования метода. Ссылку на объект ReflectionMethod можно получить двумя способами. Во-первых, можно получить массив объектов типа ReflectionMethod, вызвав метод ReflectionClass::getMethods(). А во-вторых, если вас интересует конкретный метод, то передайте его имя методу ReflectionClass::getMethod(), который и вернет соответствующий объект типа ReflectionMethod.

Ниже мы воспользуемся методом ReflectionClass::getMethods(), чтобы проверить возможности класса ReflectionMethod:

В этом коде для получения массива объектов типа ReflectionMethod используется вызов метода ReflectionClass::getMethods(). Затем в цикле выполняется обращение к каждому элементу массива и вызывается функция methodData(), которой передается полученный объект типа ReflectionMethod.

img127Исследование методов класса с помощью рефлексии

Имена методов, вызываемых в функции methodData(), отражают их назначение: код проверяет, является ли метод определенным пользователем, встроенным, абстрактным, общедоступным, защищенным, статическим или завершенным. Можно также проверить, является ли метод конструктором для своего класса и возвращает он ссылку или значение. Однако здесь нужно сделать одно замечание: метод ReflectionMethod::returnsReference() не возвращает истинное значение, даже если проверяемый метод возвращает объект целиком (а не ссылку на него), хотя в PHP 5 объекты передаются и присваиваются по ссылке. Этот метод возвращает истинное значение, только если исследуемый метод был явно объявлен таким, который возвращает ссылку (путем помещения символа амперсанда перед именем метода).

Исследование аргументов методов

Теперь, когда стало возможным ограничивать типы аргументов с помощью сигнатур методов, чрезвычайно полезной кажется возможность исследования аргументов, объявленных в сигнатуре метода. В интерфейсе Reflection API именно для этой цели предусмотрен класс ReflectionParameter. Чтобы получить объект типа ReflectionParameter, нам понадобится помощь объекта ReflectionMethod. Метод ReflectionMethod::getParameters() возвращает массив объектов типа ReflectionParameter.

Имея объект ReflectionParameter, можно узнать следующее: имя аргумента, была ли переменная передана по ссылке, информацию о классе, который используется для уточнения типа аргумента и будет ли метод по умолчанию назначать значение Null для аргумента.

Ниже приведен пример использования некоторых методов класса ReflectionParameter:

Код PHP img128Получение информации о аргументах метода-конструктора

Метод ReflectionClass::getMethod() возвращает объект типа ReflectionMethod для интересующего нас метода (в рассмотренном примере это конструктор). Затем вызывается метод ReflectionClass::getParameters() для получения массива объектов типа ReflectionParameter, соответствующих данному методу. В цикле функции argData() передается объект типа ReflectionParameter, а она возвращает информацию об аргументе.

Сначала мы узнаем имя переменной-аргумента с помощью метода ReflectionParameter::getName(). Метод ReflectionParameter::getClass() возвращает объект типа ReflectionClass, если в сигнатуре метода было использовано уточнение. Затем с помощью метода isPassedByReference() проверяется, является ли аргумент ссылкой. И, наконец, с помощью метода isDefaultValueAvailable() проверяется, было ли аргументу присвоено значение по умолчанию.

Источник

Справочник по обустройству дома и дачи
Adblock
detector