Введение
Помимо многих проблем, в PHP существует проблема строгой типизации переменных и свойств классов, точнее её отсутствие. Более того, нет даже возможности однозначно задать какие будут свойства у объектов того или иного класса, пользуясь только синтаксисом и не прибегая к так называемым магическим методам (потому что любое свойство может быть удалено при помощи оператора unset
, а также к объекту может быть дописано несуществующее ранее свойство).
Однако при разработке часто возникает потребность в чётком знании, что можно ожидать от объекта, а чего можно не ожидать. Разумеется, можно пойти простым путём: сделать все свойства protected
и понаписать геттеров и сеттеров. Много бойлерплейта, хочется проще. Лично я пытался решить эту проблему с помощью трейтов, но выходило всё равно некрасиво. Так и появилась идея этого проекта…
Кому интересно, добро пожаловать под кат!
Описание
Проект PHP-DataGen[1] является утилитой — генератором кода PHP классов с определёнными свойствами и направлен на упрощение работы PHP программистов. Инструмент имеет как возможность управлять генерацией с помощью PHP скриптов, так и CLI для работы со встроенным парсером собственного языка (далее — PDGL).
Из соображений удобства использования целевой аудиторией (PHP программисты), для разработки был выбран язык PHP.
Версия последнего на данный момент релиза — v0.2.2-alpha
. В скором времени планируется версия v0.3-alpha
, в связи с расширением функционала. К моменту выхода стабильного релиза планируется переписать весь «низкокачественный» код (написанный без достаточной квалификации), но пока всё и так работает.
Краткий обзор
В рамках данного обзора я рассмотрю основные аспекты использования утилиты.
Проект рассматривается в состоянии последнего на данный момент коммита 55bb922f7b739477f8eebdfdf3664e25e57f9168
[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 — модуль, отвечающий за разбор кода;
- Builders — модуль, содержащий билдеры «сущностей», которыми оперирует компилятор;
- Models — модуль, содержащий модели «сущностей», которыми оперирует компилятор. Объекты классов этого модуля порождают классы модуля Builders;
- Compiler — модуль, отвечающий за генерацию кода на основе моделей.
Ход разработки
Если до появления цельной архитектуры я (безуспешно) пытался написать Parser, то сразу после её появления я взялся за модуль Builders. Затем были написаны модели и 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.
Автор: Эридан Доморацкий