Многие PHP разработчики хотели бы видеть в PHP поддержку дженериков, и я в том числе. RFC по их добавлению был создан ещё в 2016 году, но до сих пор не принял окончательный вид. Я рассмотрел несколько вариантов решений поддержки дженериков в синтаксисе PHP, но не нашёл рабочей версии, которой мог бы воспользоваться обычный разработчик. В итоге я решил, что могу сам попробовать реализовать такое решение на PHP. Скриншот выше — реальный пример того, что у меня получилось. Если хочется сразу попробовать, то вот библиотека mrsuh/php-generics и репо, в котором можно поиграться. В качестве способа реализации дженериков я выбрал мономорфизацию. Цитата отсюда. Оригинал тут. Кратко: Подробный алгоритм. Нужно подключить библиотеку как зависимость composer (минимальная версия PHP 7.4). Добавить ещё одну директорию ("cache/") в composer autoload PSR-4 для сгенерированных классов.
Она обязательно должна идти перед основной директорией.
composer.json Для примера нужно добавить несколько PHP файлов: src/Box.php src/Usage.php bin/test.php Сгенерировать конкретные классы из классов дженериков командой composer dump-generics. Что делает скрипт composer dump-generics: В данном случае должны быть сгенерированы: cache/BoxForInt.php cache/BoxForString.php cache/Usage.php Сгенерировать актуальный vendor/autoload.php файл командой composer dump-autoload. Запустить скрипт. Composer autoload сначала будет проверять, есть ли класс в директории "cache", а уже потом в директории "src". Пример с кодом выше можно посмотреть тут.
Больше примеров можно посмотреть тут. В RFC не определён конкретный синтаксис, поэтому я взял тот, который реализовывал Никита Попов. Пример синтаксиса: Для парсинга кода пришлось допилить nikic/php-parser. Вот тут можно посмотреть изменения грамматики, которые пришлось внести для поддержки дженериков. Внутри парсера используется PHP реализация YACC. Реализация алгоритма YACC (LALR) и существующий синтаксис PHP не дают возможности использовать некоторые вещи, потому что они могут вызывать коллизии при генерации синтаксического анализатора. Пример коллизии: Варианты решения можно почитать тут. Поэтому на данный момент вложенные дженерики не поддерживаются. Пример класса, который использует дженерики: Пример класса дженерика: Все конкретные классы генерируются заранее, и их можно кешировать (не должно влиять на производительность). Генерация множества конкретных классов должна негативно сказываться на производительности при: Думаю, всё индивидуально, и нужно проверять на конкретном проекте. Магия с автозагрузкой сгенерированных конкретных классов будет работать только с composer autoload.
Если вы напрямую подключите класс с дженериком через require, то у вас ничего не будет работать из-за ошибки синтаксиса.
PhpUnit по своим соображениям подключает файлы тестов только через require.
Поэтому использовать классы дженериков внутри тестов PhpUnit не получится. PhpStorm
Не поддерживает синтаксис дженериков, потому что даже RFC ещё не до конца сформирован.
Также PhpStorm не имеет работающего плагина для подключения LSP, чтобы иметь возможность поддерживаеть синтаксисы сторонних языков.
От поддержки Hack (который уже поддерживает дженерики) отказались. VSCode
Поддерживает синтаксис дженериков после установки плагина для Hack.
Нет автодополнения. PHP выполняет проверки типов в runtime. Значит, все аргументы дженериков должны быть доступны через reflection в runtime. А этого не может быть, потому что информация о аргументах дженериков после генерации конкретных классов стирается. T должен быть подклассом или имплементировать интерфейс TInterface. Особенности: Особенности: Особенности: Особенности: Test/Item.php Test/Test.php test.php Отличие от mrsuh/php-generics: Думаю, у меня получилось то, чего я хотел: библиотека легко устанавливается и может использоваться на реальных проектах. Расстраивает то, что по понятным причинам популярные IDE не поддерживают в полной мере новый синтаксис дженериков, поэтому сейчас пользоваться им сложно. Если у вас будут предложения или вопросы, можете оставлять их тут или в комментариях. Web Developer