Как заглянуть в «мозги» PHP-кода и зачем вам это нужно
Вы когда-нибудь задумывались, как работают такие инструменты, как PHPStan, Psalm или Rector? Как они «понимают», что вы забыли инициализировать переменную или что ваш код можно автоматически обновить до версии PHP 8.3? Магия здесь ни при чем. За кулисами большинства крутых инструментов в экосистеме PHP стоит один и тот же «серый кардинал» — PHP-Parser от Никиты Попова (nikic).
Если вы до сих пор воспринимаете PHP-код просто как набор строк в текстовом файле, приготовьтесь: сегодня мы научимся разбирать его на атомы и собирать обратно.
Что это такое и почему это важно?
PHP-Parser — это библиотека, написанная на самом PHP, которая превращает обычный исходный код в абстрактное синтаксическое дерево (AST).
Представьте, что код — это предложение на человеческом языке. Для компьютера это просто последовательность символов. Но парсер превращает эту последовательность в иерархическую структуру, где понятно: вот это — функция, у неё есть имя, три параметра и тело, состоящее из пяти выражений.
Зачем это нужно обычному разработчику?
- Статический анализ: Поиск багов без запуска кода.
- Автоматический рефакторинг: Массовое переименование методов или изменение сигнатур во всем проекте.
- Генерация кода: Создание DTO, мапперов или прокси-классов на лету.
- Защита кода: Написание собственных правил (линтеров), которые запрещают использовать определенные конструкции.
Как это работает на практике
Давайте отбросим теорию и посмотрим, как легко начать. Установка стандартная:
composer require nikic/php-parser
1. Превращаем код в дерево
Самое простое — распарсить файл и посмотреть, что у него внутри. Библиотека предоставляет NodeDumper, который визуализирует структуру кода.
use PhpParser\ParserFactory;
use PhpParser\NodeDumper;
$code = '<?php echo "Hello World!";';
$parser = (new ParserFactory())->createForNewestSupportedVersion();
$ast = $parser->parse($code);
$dumper = new NodeDumper;
echo $dumper->dump($ast);
На выходе вы увидите дерево узлов (Stmt_Echo, Scalar_String), где каждый узел знает свою позицию в файле, комментарии рядом и другие метаданные.
2. Манипуляция кодом: «Хирургия» в реальном времени
Самая мощная фишка PHP-Parser — это возможность изменять AST. Допустим, вы хотите во всем проекте найти функции и очистить их тела (например, для создания заглушек/stubs).
Для этого используется NodeTraverser и «посетители» (Visitors). Это классический паттерн проектирования, который позволяет обходить дерево и реагировать на нужные узлы.
use PhpParser\Node;
use PhpParser\Node\Stmt\Function_;
use PhpParser\NodeTraverser;
use PhpParser\NodeVisitorAbstract;
$traverser = new NodeTraverser();
$traverser->addVisitor(new class extends NodeVisitorAbstract {
public function enterNode(Node $node) {
// Если встретили функцию — обнуляем её содержимое
if ($node instanceof Function_) {
$node->stmts = [];
}
}
});
$ast = $traverser->traverse($ast);
3. Обратная трансформация: Из дерева в код
После того как вы изменили дерево, его нужно превратить обратно в PHP-файл. За это отвечает PrettyPrinter.
use PhpParser\PrettyPrinter;
$prettyPrinter = new PrettyPrinter\Standard;
echo $prettyPrinter->prettyPrintFile($ast);
Интересно, что PHP-Parser умеет сохранять форматирование оригинала при частичном изменении дерева. Это критично для инструментов автоматического рефакторинга, чтобы ваш git diff не превратился в кошмар из-за лишних пробелов.
Технические «вкусности»
Проект развивается уже много лет и вобрал в себя массу крутых инженерных решений:
- Поддержка версий: Вы можете запускать парсер на PHP 7.4, но при этом анализировать код, написанный для PHP 8.4. Это делает его идеальным для инструментов миграции.
- Обработка ошибок: Даже если в коде есть синтаксическая ошибка, парсер не «падает», а пытается построить частичное дерево. Это позволяет IDE подсвечивать ошибки, не теряя структуру остального файла.
- Разрешение имен (Name Resolution): Библиотека умеет превращать относительные имена классов (например,
User) в полные (App\Entity\User), учитывая всеuseиnamespaceв начале файла. - Вычисление констант: Парсер может вычислить значение выражения
2 + 2или__DIR__ . '/config.php'прямо во время анализа, не исполняя сам код.
Где это применить завтра?
Если вы не пишете свой PHPStan, это не значит, что библиотека вам не пригодится. Вот пара идей для реальных проектов:
- Автоматическая документация: Просканируйте свои контроллеры и сгенерируйте Swagger-спецификацию на основе атрибутов и типов возвращаемых значений.
- Проверка архитектуры: Напишите скрипт, который запрещает импортировать классы из слоя
Infrastructureв слойDomain. Это будет работать быстрее и надежнее, чем поиск по строкам. - Миграция данных: Если вам нужно перенести логику из старых массивов в новые типизированные объекты в тысячах файлов, PHP-Parser сделает это за пару секунд.
Итог: Стоит ли изучать?
PHP-Parser — это не просто библиотека, это фундамент современного PHP-сообщества. Понимание того, как работает AST, переводит разработчика с уровня «я пишу код» на уровень «я создаю инструменты, которые пишут код».
Даже если вы не планируете писать сложные анализаторы, знание этого инструмента поможет вам лучше понимать, как работают ваши любимые IDE и плагины. А в нужный момент, когда возникнет задача по массовому изменению кодовой базы, вы будете знать, что у вас в руках есть скальпель, а не топор.
Попробовать проект можно на GitHub. Документация там отличная, а количество примеров позволит влиться в тему за один вечер.
