PHP-DataGen — генератор PHP классов со строго-типизированными свойствами +11
Из песочницы, Программирование, Компиляторы, PHP
Рекомендация: подборка платных и бесплатных курсов таргетированной рекламе - https://katalog-kursov.ru/
Введение
Помимо многих проблем, в PHP существует проблема строгой типизации переменных и свойств классов, точнее её отсутствие. Более того, нет даже возможности однозначно задать какие будут свойства у объектов того или иного класса, пользуясь только синтаксисом и не прибегая к так называемым магическим методам (потому что любое свойство может быть удалено при помощи оператора
unset
, а также к объекту может быть дописано несуществующее ранее свойство).
Однако при разработке часто возникает потребность в чётком знании, что можно ожидать от объекта, а чего можно не ожидать. Разумеется, можно пойти простым путём: сделать все свойства
protected
и понаписать геттеров и сеттеров. Много
бойлерплейта, хочется проще. Лично я пытался решить эту проблему с помощью трейтов, но выходило всё равно некрасиво. Так и появилась идея этого проекта…
Кому интересно, добро пожаловать под кат!
Описание
Проект PHP-DataGen
[1] является утилитой — генератором кода PHP классов со строго-типизированными свойствами и направлен на упрощение работы PHP программистов. Инструмент имеет как возможность управлять генерацией с помощью PHP скриптов, так и
CLI для работы со встроенным парсером собственного языка (далее —
PDGL).
Из соображений удобства использования целевой аудиторией (PHP программисты), для разработки был выбран язык PHP.
Версия последнего на данный момент релиза —
v0.3-alpha
. К моменту выхода стабильного релиза планируется переписать весь «низкокачественный» код (написанный без достаточной квалификации), но пока всё и так работает.
Краткий обзор
В рамках данного обзора я рассмотрю основные аспекты использования утилиты.
Проект рассматривается в состоянии коммита 75974bee3b4cccd1af1722acac775d68011f7fa6
[2].
CLI
На данный момент PHP-DataGen поддерживает 2 собственные команды:
compile и
build. Первая используется для поштучной компиляции файлов, вторая для компиляции всех файлов в проекте (директории). Использование команд максимально интуитивное и может быть изучено вручную благодаря библиотеке Symfony Console
[6], на которой основан CLI.
Также планируется добавить работу с файлом конфигурации для управления деталями процесса компиляции. При этом будет добавлено считывание файла конфигурации из корня проекта и команда
config для удобного изменения конфигурации проекта.
PDGL
До начала разработки было несколько идей по поводу внедрения утилиты в проекты:
- Чтение PHPDoc и других комментариев
- Введение специальных модификаторов и т.п. в обычный PHP код
- Создание собственного языка
Так как изначально проект создавался «по образу и подобию» утилиты
moc популярного C++ фреймворка Qt, в приоритете был второй вариант, однако после некоторых раздумий, первые два варианта были отброшены мной как неоправданно сложные для реализации.
PDGL предназначен для описания файлов PHP, которые получаются на выходе PHP-DataGen. Каждый файл можно представить в виде дерева, отдалённо напоминающего
абстрактное синтаксическое дерево[3], которое состоит лишь из трёх типов узлов: файл, класс, поле.
Все поддерживающиеся языком операторы представлены в файле
schema.md
[4] в корне проекта, но без описания, что делает тот или иной оператор. Операторы
namespace
и
use
работают также как и в обычном PHP, однако с классом, полями и их модификаторами всё не так просто.
Из модификаторов класса можно выделить лишь один нестандартный для PHP модификатор
final
, который также имеет вариацию
final!
. Дело в том, что результат работы PHP-DataGen — класс, который для работы должен быть расширен с помощью другого класса.
Модификатор
final
превращает класс в готовый для непосредственного использования, путём убирания префикса (по умолчанию, пока что без возможности изменения,
Data_
) и модификатора
abstract
итогового PHP класса.
Модификатор
final!
, который «под капотом» именуется не иначе как «final final» является дополнением к модификатору
final
(и не может быть использован без него) и добавляет к итоговому PHP классу модификатор
final
.
Поле класса
Синтаксис поля класса очень мало похож на синтаксис свойств PHP и даже больше, на мой взгляд, напоминает синтаксис свойств классов Kotlin.
Начнём с того, что написано в файле
schema.md
[4]:
// Field declaration
[direct] <val/var> <Field name>[: <Type name>[, <Validator names>]][ <:/</>= [`[``]]<Default value>[`[``]]];
А теперь по порядку (операторы выделены жирным, подстановки — курсивом):
- direct — модификатор. При наличии позволяет расширяющему классу обращаться к свойствам напрямую (устанавливает модификатор доступа
protected
вместо private
);
- val или var — оператор объявления поля. Если используется val — свойство недоступно для редактирования после установки в конструкторе, если var — доступно;
- Field name — название поля, указывается без характерного для PHP знака доллара (
$
);
- : — необязательный оператор двоеточия позволяет указать тип поля. Если не указан — тип поля считается
mixed
;
- Type name — название типа. Может быть одним из стандартных типов PHP (без учёта регистра) или названием класса. Если оканчивается знаком вопроса (например,
string?
), тогда поле может хранить также значение null
;
- , — необязательный оператор запятая позволяет указать после названия типа (или валидатора) также название валидатора;
- Validator name — название валидатора (см. следующий раздел);
<=
, := или = — оператор присваивания значения по-умолчанию. В вариации <=
присваивает значение при объявлении свойства. В вариации := присваивает значение при вызове конструктора без проверки типа и вызова валидаторов. В вариации = присваивает значение при вызове конструктора с проверкой типа и вызовом валидаторов;
- ` или ``` — см. Default value;
- Default value — значение поля по-умолчанию. Может быть окружено операторами ` или ``` при наличии точки с запятой (
;
) (кроме случаев, когда используется вариация оператора присваивания значения по-умолчанию <=
). Нет разницы в использовании ` или ```, если в значении по-умолчанию не присутствует символов обратного апострофа (`
), в этом случае необходимо использовать оператор ```.
Валидаторы
Для лучшей фильтрации возможных значений полей планируется ввести возможность добавлять свои валидаторы — функции проверки (или модификации) значения. При том что в коде PHP-DataGen обработка валидаторов присутствует, пока нет способа добавлять их. Это одна из возможностей, которые должны появиться с появлением чтения конфигурации.
Примеры работы
Некоторые примеры работы инструмента можно найти в репозитории. Среди них:
Примеры приведены в виде ссылок из-за большого объёма кода
Разработка
В самом начале реализации идеи начался длительный «ступор» связанный с отсутствием продуманной архитектуры и, как следствие, неправильно выбранным порядком разработки. Вскоре после появления ясности в голове — зарождения в голове архитектуры будущего инструмента, была начата разработка.
Архитектура
Упрощённая архитектура утилиты
На схеме выше изображена очень упрощённая архитектура PHP-DataGen. Она состоит из четырёх модулей:
- Parser — модуль, отвечающий за разбор кода;
- Building* — модуль, содержащий билдеры «сущностей», которыми оперирует компилятор;
- Models — модуль, содержащий модели «сущностей», которыми оперирует компилятор. Объекты классов этого модуля порождают классы модуля Building;
- Compiler — модуль, отвечающий за генерацию кода на основе моделей.
* — на схеме опечатка (не Builders, а Building).
Ход разработки
Если до появления цельной архитектуры я (безуспешно) пытался написать
Parser, то сразу после её появления я взялся за модуль
Building. Затем были написаны модели и
Compiler. Таким образом, уже через два дня появился рабочий прототип, позволяющий генерировать код с помощью PHP скрипта.
Далее предстояло написать
Parser. Из-за незнания об
абстрактных синтаксических деревьях[3], ломать голову пришлось долго. В итоге получился конечный автомат
[5], имеющий 3 состояния (которые также состоят из некоторых собственных состояний):
FileState,
ClassState и
FieldState. Каждое из этих состояний при помощи соответствующих билдеров создаёт модели для
Compiler.
Использованные при разработке библиотеки и инструменты
В проекте используются следующие библиотеки:
- Symfony Console[6] — для разработки CLI;
- Symfony Finder[7] — для поиска файлов при использовании CLI.
Проект разрабатывался при помощи следующих инструментов:
- Vim — редактор кода;
- Git — система контроля версий;
Также, при написании статьи, для рисования диаграмм, использовался онлайн сервис
Creately[8].
Ссылки
- php-datagen — GitHub;
75974bee3b4cccd1af1722acac775d68011f7fa6
— GitHub;
- Абстрактное синтаксическое дерево — Википедия;
- Файл schema.md — GitHub;
- Теория вычислений. Введение в конечные автоматы — Хабр;
- The Console Component — Symfony Docs;
- The Finder Component — Symfony Docs;
- Creately.
К сожалению, не доступен сервер mySQL