Статьи

Самоанализ и отражение в PHP

Самоанализ — это общая особенность любого языка программирования, которая позволяет программисту манипулировать классами объектов. Вы найдете интроспекцию особенно полезной, если не знаете, какой класс или метод вам нужно выполнить во время разработки.

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

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

PHP интроспекционные функции

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

  • class_exists() — проверяет, был ли определен класс
  • get_class() — возвращает имя класса объекта
  • get_parent_class() — возвращает имя класса родительского класса объекта
  • is_subclass_of() — проверяет, есть ли у объекта данный родительский класс

Вот пример PHP-кода, который содержит определение классов Introspection и Child и выводит информацию, извлеченную перечисленными выше функциями:

 <?php class Introspection { public function description() { echo "I am a super class for the Child class.n"; } } class Child extends Introspection { public function description() { echo "I'm " . get_class($this) , " class.n"; echo "I'm " . get_parent_class($this) , "'s child.n"; } } if (class_exists("Introspection")) { $introspection = new Introspection(); echo "The class name is: " . get_class($introspection) . "n"; $introspection->description(); } if (class_exists("Child")) { $child = new Child(); $child->description(); if (is_subclass_of($child, "Introspection")) { echo "Yes, " . get_class($child) . " is a subclass of Introspection.n"; } else { echo "No, " . get_class($child) . " is not a subclass of Introspection.n"; } } 

Вывод вышеуказанного кода должен быть следующим:

  Название класса: Самоанализ
 Я супер класс для детского класса.
 Я детский класс.
 Я ребенок Самоанализ.
 Да, Child - это подкласс самоанализа. 

Вы можете определить, был ли данный класс определен с помощью class_exists() , который принимает строковый аргумент, представляющий имя нужного класса для проверки, и необязательное логическое значение, вызывать или не вызывать автозагрузчик в процессе.

get_class() и get_parent_class() возвращают имя класса объекта или имя класса его родителя соответственно. Оба принимают в качестве аргументов экземпляр объекта.

is_subclass_of() принимают экземпляр объекта в качестве первого аргумента и строку, представляющую имя родительского класса, и возвращают, принадлежит ли объект классу, который является подклассом класса, заданного в качестве аргумента.

Вот второй пример, содержащий определение для интерфейса GBPCurrencyConverter класса GBPCurrencyConverter и выводит информацию, извлеченную перечисленными выше функциями. Как и в первом примере, я сначала перечислю функции, а затем покажу код.

  • get_declared_classes() — возвращает список всех объявленных классов
  • get_class_methods() — возвращает имена методов класса
  • get_class_vars() — возвращает свойства класса по умолчанию
  • interface_exists() — проверяет, определен ли интерфейс
  • method_exists() — проверяет, определяет ли объект метод
 <?php interface ICurrencyConverter { public function convert($currency, $amount); } class GBPCurrencyConverter implements ICurrencyConverter { public $name = "GBPCurrencyConverter"; public $rates = array("USD" => 0.622846, "AUD" => 0.643478); protected $var1; private $var2; function __construct() {} function convert($currency, $amount) { return $rates[$currency] * $amount; } } if (interface_exists("ICurrencyConverter")) { echo "ICurrencyConverter interface exists.n"; } $classes = get_declared_classes(); echo "The following classes are available:n"; print_r($classes); if (in_array("GBPCurrencyConverter", $classes)) { print "GBPCurrencyConverter is declared.n"; $gbpConverter = new GBPCurrencyConverter(); $methods = get_class_methods($gbpConverter); echo "The following methods are available:n"; print_r($methods); $vars = get_class_vars("GBPCurrencyConverter"); echo "The following properties are available:n"; print_r($vars); echo "The method convert() exists for GBPCurrencyConverter: "; var_dump(method_exists($gbpConverter, "convert")); } 

Вывод вышеуказанного кода должен быть следующим:

  Интерфейс ICurrencyConverter существует.
 Доступны следующие классы:
 массив
 (
     [0] => stdClass
     [1] => Исключение
     [2] => ErrorException
     [3] => Закрытие
     [4] => DateTime
     [5] => DateTimeZone
     [6] => DateInterval
     [7] => DatePeriod
     ...
     [154] => GBPCurrencyConverter
 )
 GBPCurrencyConverter объявлен.
 Доступны следующие методы:
 массив
 (
     [0] => __construct
     [1] => конвертировать
 )
 Доступны следующие свойства:
 массив
 (
     [name] => GBPCurrencyConverter
     [ставок] => Массив
         (
             [USD] => 0,622846
             [AUD] => 0,643478
         )
 )
 Метод convert () существует для GBPCurrencyConverter: bool (true) 

Как вы уже догадались, метод interface_exists() очень похож на class_exists() описанный в первом примере. Он определяет, был ли задан данный интерфейс, и принимает строковый аргумент для имени интерфейса и необязательного автозагрузчика Boolean.

Метод get_declared_classes() возвращает массив с именами всех определенных классов и не принимает аргументов. В зависимости от того, какие библиотеки вы загрузили (собранные в PHP или загруженные с помощью require / include), могут присутствовать дополнительные классы.

get_class_method() принимает либо экземпляр объекта, либо строку в качестве аргумента, представляющего нужный класс, и возвращает массив имен методов, определенных классом.

Обратите внимание, что из всех свойств, определенных в классе ICurrencyConverter и возвращенных методом get_class_vars() , в выводе появились только $name и $rates . Частные и охраняемые объекты были пропущены.

PHP Reflection API

PHP поддерживает рефлексию через свой Reflection API. Как вы можете видеть из руководства по PHP, API Reflection гораздо более щедрый, чем самоанализ, и предлагает большое количество классов и методов, которые вы можете использовать для выполнения задач отражения. Класс ReflectionClass является основным классом API и используется для применения отражения к классам, интерфейсам и методам, а также для извлечения информации обо всех компонентах класса. Отражение легко внедрить в код приложения и, как и самоанализ, также очень интуитивно понятно.

Вот пример, иллюстрирующий отражение с использованием тех же определений для интерфейса GBPCurrencyConverter классов Child и GBPCurrencyConverter :

 <?php $child = new ReflectionClass("Child"); $parent = $child->getParentClass(); echo $child->getName() . " is a subclass of " . $parent->getName() . ".n"; $reflection = new ReflectionClass("GBPCurrencyConverter"); $interfaceNames = $reflection->getInterfaceNames(); if (in_array("ICurrencyConverter", $interfaceNames)) { echo "GBPCurrencyConverter implements ICurrencyConverter.n"; } $methods = $reflection->getMethods(); echo "The following methods are available:n"; print_r($methods); if ($reflection->hasMethod("convert")) { echo "The method convert() exists for GBPCurrencyConverter.n"; } 

Вывод вышеуказанного кода должен быть следующим:

  Ребенок является подклассом самоанализа.
 GBPCurrencyConverter реализует ICurrencyConverter.
 Доступны следующие методы:
 массив
 (
     [0] => ReflectionMethod Object
         (
             [name] => __construct
             [class] => GBPCurrencyConverter
         )

     [1] => ReflectionMethod Object
         (
             [name] => конвертировать
             [class] => GBPCurrencyConverter
         )

 )
 Метод convert () существует для GBPCurrencyConverter. 

Метод getInterfaceNames() возвращает массив с именами интерфейсов, которые реализует класс. Метод getParentClass() может возвращать объектное представление ReflectionClass родительского класса или false, если родительского элемента нет. Чтобы getName() имя объекта ReflectionClass , вы используете метод getName() , как вы видели в приведенном выше коде.

Метод getMethods() извлекает массив методов и может принимать в качестве необязательного аргумента комбинацию ReflectionMethod::IS_STATIC IS_PUBLIC ReflectionMethod::IS_STATIC , IS_PUBLIC , IS_PROTECTED , IS_PRIVATE , IS_ABSTRACT и IS_FINAL для фильтрации списка на основе видимости.

Reflection API обеспечивает хорошую реализацию рефлексии, давая вам возможность создавать более сложные приложения, такие как ApiGen , хотя дальнейшее обсуждение выходит за рамки этой статьи.

Резюме

В этой статье вы узнали, как использовать функции самоанализа PHP и API Refection для получения информации о классах, интерфейсах, свойствах и методах. Цель получения этой информации — получить более полное представление о вашем коде во время выполнения и создать сложные приложения.

Изображение через Fotolia