Сергей Фастунов
В данном уроке описывается использование функции интроспекции в PHP и Reflection API для получения информации о классах, интерфейсах, свойствах и методах. Такие действия нужны для составления полной картины о коде в момент выполнения и создания сложных приложений.
Интроспекция является общим свойством для любого языка программирования, который позволяет программисту манипулировать объектами классов. Она очень полезна в тех случаях, когда во время разработки неизвестно, какой класс или метод нужно использовать.
Интроспекция в PHP позволяет проверить классы, интерфейсы, методы и свойства. В PHP имеется большое количество функций, которые можно использовать для решения таких задач. Мы представим краткий обзор некоторых классов, методов и функций PHP с примерами их использования. Также в уроке будет представлен API, который имеет функционал очень близкий к интроспекции - Reflection API.
Функции интроспекции PHP
В первом примере демонстрируются полезные функции интроспекции PHP. Их можно использовать для получения основной информации о классе - имя, имя родительского класса и так далее.
class_exists()
- проверяет определение класса
get_class()
- возвращает имя класса объекта
get_parent_class()
- возвращает имя родительского класса объекта
is_subclass_of()
- проверяет, имеется ли в родителях объекта заданный класс
Пример кода PHP? который содержит определение для классов Introspection
и Child
, а также выводит информацию, полученную с помощью перечисленных выше функций:
04 |
public function description() { |
05 |
echo "Я супер класс для класса Child.\n" ; |
09 |
class Child extends Introspection |
11 |
public function description() { |
12 |
echo "Я класс " . get_class( $this ) , ".\n" ; |
13 |
echo "Я потомок класса " . get_parent_class( $this ) , ".\n" ; |
17 |
if ( class_exists ( "Introspection" )) { |
18 |
$introspection = new Introspection(); |
19 |
echo "Имя класса : " . get_class( $introspection ) . "\n" ; |
20 |
$introspection ->description(); |
23 |
if ( class_exists ( "Child" )) { |
25 |
$child ->description(); |
27 |
if ( is_subclass_of ( $child , "Introspection" )) { |
28 |
echo "Да, " . get_class( $child ) . " является подклассом Introspection.\n" ; |
31 |
echo "Нет, " . get_class( $child ) . " не является подклассом Introspection.\n" ; |
Выше приведенный код выведет:
1 |
Имя класса: Introspection |
2 |
Я супер класс для класса Child. |
4 |
Я потомок класса Introspection. |
5 |
Да, Child является подклассом Introspection. |
Вы можете определить, будет или нет определяться класс с помощью метода class_exists()
, который получает в качестве аргумента строку с именем проверяемого класса и опциональное логическое значение, которое определяет автоматическую загрузку.
Методы get_class()
и get_parent_class()
возвращают имя класса объекта или его родителя соответственно. Оба метода принимают в качестве аргумента объекты.
Метод is_subclass_of()
получает объект и строку, в которой содержится имя родительского класса, а возвращает логическое значение результата проверки принадлежности объекта родительскому классу.
Во втором примере определяется интерфейс ICurrencyConverter
и классGBPCurrencyConverter
и выводится информация с помощью ниже перечисленных функций.
get_declared_classes()
- возвращает список всех объявленных классов
get_class_methods()
- возвращает имена методов класса
get_class_vars()
- возвращает свойства класса
interface_exists()
- проверяет, определен или нет интерфейс
method_exists()
- проверяет, определен или нет метод
02 |
interface ICurrencyConverter |
04 |
public function convert( $currency , $amount ); |
07 |
class GBPCurrencyConverter implements ICurrencyConverter |
09 |
public $name = "GBPCurrencyConverter" ; |
10 |
public $rates = array ( "USD" => 0.622846, |
15 |
function __construct() {} |
17 |
function convert( $currency , $amount ) { |
18 |
return $rates [ $currency ] * $amount ; |
22 |
if ( interface_exists ( "ICurrencyConverter" )) { |
23 |
echo "Интерфейс ICurrencyConverter определен.\n" ; |
26 |
$classes = get_declared_classes(); |
27 |
echo "Доступны следующие классы:\n" ; |
30 |
if (in_array( "GBPCurrencyConverter" , $classes )) { |
31 |
print "Определен класс GBPCurrencyConverter.\n" ; |
33 |
$gbpConverter = new GBPCurrencyConverter(); |
35 |
$methods = get_class_methods( $gbpConverter ); |
36 |
echo "Доступны следующие методы:\n" ; |
39 |
$vars = get_class_vars( "GBPCurrencyConverter" ); |
40 |
echo "Доступны следующие свойства:\n" ; |
43 |
echo "Метод convert() есть в классе GBPCurrencyConverter: " ; |
44 |
var_dump(method_exists( $gbpConverter , "convert" )); |
Код выдаст результат:
01 |
Интерфейс ICurrencyConverter определен. |
02 |
Доступны следующие классы: |
14 |
[154] => GBPCurrencyConverter |
16 |
Определен класс GBPCurrencyConverter. |
17 |
Доступны следующие методы: |
23 |
Доступны следующие свойства: |
26 |
[name] => GBPCurrencyConverter |
33 |
Метод convert() есть в классе GBPCurrencyConverter: bool(true) |
Метод interface_exists()
очень похож на метод class_exists()
, который обсуждался ранее. Он проверяет, определен или нет заданный интерфейс. В качестве параметров он получает имя интерфейса и логическую переменную для автозазгрузки (опционально).
Метод get_declared_classes()
возвращает массив имен всех определенных классов. В зависимости от загруженных библиотек результат может быть разным.
Метод get_class_method()
получает экземпляр объекта или строку с именем нужного класса в качестве аргумента, а возвращает массив имен методов, которые определены в классе.
Обратите внимание на различие определенных в классе ICurrencyConverter
свойств и списком, возвращаемым методом get_class_vars()
(вывелись только $name
и $rates
). Частные и защищенные свойства пропускаются.
Reflection API
PHP поддерживает отражение с помощью Reflection API. Reflection API предлагает существенно больше классов и методов для решения задач отражения. Класс ReflectionClass
является основным классом API и используется для получения информации о классах, интерфейсах, методах и всех компонентов классов. Отражение очень легко применять в своем коде.
Ниже приводится пример использования отражения с определениями интерфейсаICurrencyConverter
и классов Child
и GBPCurrencyConverter
:
02 |
$child = new ReflectionClass( "Child" ); |
03 |
$parent = $child ->getParentClass(); |
04 |
echo $child ->getName() . " является подклассом " . $parent ->getName() . ".\n" ; |
06 |
$reflection = new ReflectionClass( "GBPCurrencyConverter" ); |
07 |
$interfaceNames = $reflection ->getInterfaceNames(); |
08 |
if (in_array( "ICurrencyConverter" , $interfaceNames )) { |
09 |
echo "GBPCurrencyConverter реализует ICurrencyConverter.\n" ; |
12 |
$methods = $reflection ->getMethods(); |
13 |
echo "Доступны следующие мтоды:\n" ; |
16 |
if ( $reflection ->hasMethod( "convert" )) { |
17 |
echo "Метод convert() есть в классе GBPCurrencyConverter.\n" ; |
Код выдаст следующий результат:
01 |
Child является подклассом Introspection. |
02 |
GBPCurrencyConverter реализует ICurrencyConverter. |
03 |
Доступны следующие методы: |
06 |
[0] => ReflectionMethod Object |
09 |
[ class ] => GBPCurrencyConverter |
12 |
[1] => ReflectionMethod Object |
15 |
[ class ] => GBPCurrencyConverter |
19 |
Метод convert() есть в классе GBPCurrencyConverter. |
Метод getInterfaceNames()
возвращает массив с именами интерфейсов, которые реализует класс. Метод getParentClass()
может вернуть объект ReflectionClass
, представляющий родительский класс, или значение false, если родителя нет. Для получения имени объектаReflectionClass
используется метод getName().
Метод getMethods()
возвращает массив имен методов и может принимать опциональный аргумент - битовую маску из значений ReflectionMethod::IS_STATIC
, IS_PUBLIC
, IS_PROTECTED
,IS_PRIVATE
, IS_ABSTRACT
, и IS_FINAL
для фильтрации списка.
Reflection API предоставляет разработчику отличную реализацию отражения, с помощью которой можно создавать очень сложные приложения, такие как ApiGen.
Ссылки по теме