PHP Reflection API
Всем привет. Поговорим для начала о том, что же вообще такое рефлексия (от англ. reflection - отражение) в программировании. Этот термин пришёл к нам из психологии. Там данное слово означает способность человека к самоанализу, оценке своих поступков, мыслей и прочего вот этого всего. Кроме того, человек в процессе всего этого может ещё и изменять свою точку зрения, что приведёт к изменению его поведения. Конечно, наверняка есть более подходящее понятие, но для нас главное понять суть.
Попробуем теперь перенести это понятие из жизни человека на время выполнения программы. Получим что-то типа того, что программа во время своего выполнения может в реальном времени «узнавать» о своём состоянии и изменять своё поведение. Википедия же предлагает следующее определение: "Рефлексия означает процесс, во время которого программа может отслеживать и модифицировать собственную структуру и поведение во время выполнения."
В PHP имеется очень мощный набор инструментов, позволяющий реализовать рефлексию. Рассмотрим некоторые инструменты для рефлексии, о которых мы уже знаем. Языковые конструкции self и static, магические константы __DIR__ и __CLASS__, функции get_defined_vars(), func_get_args() или eval(). В конце концов возможность создавать объект класса, имя которого хранится в переменной:
$obj = new $className();
а затем и вызов метода, название которого так же хранится в переменной:
$obj->$methodName();
Всё это рефлексия, всё это используется для того, чтобы влиять на поведение программы непосредственно во время её выполнения.
Однако есть в PHP кое-что ещё более мощное — это PHP Reflection API.
PHP Reflection API – это набор специальных классов-рефлекторов, позволяющих вывести рефлексию на новый уровень. С помощью этих классов мы можем создавать объекты-рефлекторы для разных типов данных в PHP, которые позволят творить с ними всё что только вздумается.
Перейдём к практике и рассмотрим класс-рефлектор для функций — ReflectionFunction.
Создадим новую функцию:
/**
* @param $a
* @param $b
* @return int
*/
function sum($a, $b)
{
return $a + $b;
}
Теперь создадим объект-рефлектор для неё:
$sumReflector = new ReflectionFunction('sum');
После чего нам становится доступен ряд методов у этого объекта, благодаря которым мы можем делать с этой функцией очень интересные вещи. Например, узнать в каком файле объявлена функция:
echo $sumReflector->getFileName();
или узнать строки её начала и конца:
echo $sumReflector->getStartLine();
echo $sumReflector->getEndLine();
как вы понимаете, этого уже достаточно для получения тела этой функции - мы можем просто прочитать эти строки из файла.
Ещё мы можем получить комментарий к функции в формате PHPDoc (почитайте о PHPDoc, если до сих пор этого не сделали):
echo $sumReflector->getDocComment();
Все методы мы рассматривать не будем, если стало интересно — почитайте документацию.
Рефлексия объектов
Помимо этого можно создавать рефлекторы объектов. Давайте попробуем создать объект-рефлектор для нашей сущности Article.
Сделаем это в контроллере для статей. И давайте сразу используем этот рефлектор для вывода свойств объекта Article.
src/MyProject/Controllers/ArticlesController.php
<?php
namespace MyProject\Controllers;
use MyProject\Models\Articles\Article;
use MyProject\View\View;
class ArticlesController
{
/** @var View */
private $view;
public function __construct()
{
$this->view = new View(__DIR__ . '/../../../templates');
}
public function view(int $articleId)
{
$article = Article::getById($articleId);
$reflector = new \ReflectionObject($article);
$properties = $reflector->getProperties();
var_dump($properties);
return;
...
Мы видим массив объектов-рефлекторов для свойств объекта (ReflectionProperty). Они содержат два свойства - имя свойства и имя класса, в котором оно объявлено. Обратите внимание, что свойство id успешно унаследовано от класса ActiveRecordEntity.
А теперь давайте просто создадим массив, содержащий только имена свойств, в виде строк.
src/MyProject/Controllers/ArticlesController.php
...
public function view(int $articleId)
{
$article = Article::getById($articleId);
$reflector = new \ReflectionObject($article);
$properties = $reflector->getProperties();
$propertiesNames = [];
foreach ($properties as $property) {
$propertiesNames[] = $property->getName();
}
var_dump($propertiesNames);
return;
...
Эти знания понадобятся нам в следующем уроке. А пока ещё несколько интересных методов для рефлектора объектов.
Получить все методы:
->getMethods()
Получить все константы:
->getConstants()
Создание нового объекта (даже с непубличным конструктором)
->newInstance()
Создание нового объекта без вызова конструктора (o_O)
->newInstanceWithoutConstructor()
Этих знаний достаточно, чтобы начать использовать Reflection API. Главное помните — использования этого инструмента следует по возможности избегать, так как это работает довольно медленно. Большинство задач можно решить без использования рефлексии, но знать о ней настоящий профи обязан. Иногда её использование позволяет создать довольно изящные решения, одно из которых мы рассмотрим в следующем уроке. А со всеми возможные рефлекторами можно ознакомиться в официальной документации. До встречи ;)
Комментарии