Отсутствие коллекций — боль в заднице PHP. На данный момент нет удобного способа обеспечить безопасность типов для наборов объектов. Я постоянно создаю коллекции, но это означает, что нужно создавать новый класс каждый раз, когда нужна безопасность типов для набора данных.

Добавление на уровне языка поддержки дженериков или типизированных массивов - было бы наилучшим решением. Но, реализовать их сложно. Надеюсь, что когда-нибудь это случится, но, вероятно, этого придется ждать несколько лет.

Свято место пусто не бывает, гитхаб пестрит самодельными решениями, призванными устранить этот недостаток. Большая часть из них представляет собой представление коллекции в виде абстрактного класса, реализующего интерфейсы: Countable, IteratorAggregate, JsonSerializable. В классах предлагается расширить один-два метода отвечающие за проверку типа добавляемого значения. Но все это выглядит довольно кустарно.

Главный минус: такие коллекции создают лишнюю связанность наследованием (один из сильнейших видов связанности), которая проникает в каждую щель вашего приложения, ставя вас в зависимость от пакета стороннего вендора. Пакета, обновлявшегося, быть может, года три назад. Пакета, который неизвестно сколько еще просуществует, будет ли обновлять код в соответствии с новыми возможностями языка. А если и будут, не появится ли в нем изменение, которое обрушит ваше приложение.

Разработка решения

На днях появилась мысль, как решить эту задачу с помощью генерации кода: Предположим у нас есть папка с объектами в которой находится объект name\space\SomeObject. Полного имени класса этого объекта нам достаточно, чтобы создать для нее класс коллекции name\space\collections\SomeObjectCollection. В чем плюсы такого решения:

  1. Такая коллекция может гораздо в большей степени строго-типизированной. Кроме пыхи, которая за строгую типизаицию скажет спасибо увеличением производительности, вы получите удобные подсказки и проверку валидности типов на уровне IDE, меньше ругани со стороны линтеров.

  2. Отсутствие зависимости от сторонних пакетов: т.к. код получен генерацией, а не расширением шаблона. Единственная связь, которую потребует класс коллекции – связь с классом, экземпляры которого коллекция будет содержать.

  3. Ускорение работы. Команда в CLI с указанием одного имени класса в качестве аргумента быстрее, чем расширение абстрактных шаблонов.

Не найдя в интернете подходящего решения под свою задумку, решил написать его сам. Разработка пакета заняла всего один вечер. Когда стал глубже погружаться в тему, и узнавать больше требований к коллекциям, о которых говорили в интернете, понял, что на доработку и оформление пакета потребуется потратить еще один вечер.

Результат работы расположен здесь: https://bitbucket.org/pantagruel74/collectiongenerator/src/master/
Там же, в readme файле можно найти подробное описание API коллекций.

В composer пакет добавлен под псевдонимом:
pantagruel74/collection-generator

Использование

Внутри пакета находится класс, Pantagruel74\CollectionGenerator\CollectionGenerator с методом generateForClass(string $class): void .

Метод принимает в себя строку – полное имя класса, к которому хотим создать коллекцию. В качестве результата создает (если еще не создана) в директории класса папку collections и генерирует в ней файл класса-коллекции.

Самый удобный способ использования – создать новую команду под генерацию в CLI той среды, в которой вы работаете, принимающую имя класса в качестве аргумента, и использующую ColectionGenerator внутри этой команды. Можно использовать и в отдельном скрипте, без CLI, но это потребует ручного подключения autoloader’а

Пример команды на Yii2

namespace app\application\commands; 

use Pantagruel74\CollectionGenerator\CollectionGenerator;
use yii\console\Controller; 

class CodegenController extends Controller
{
public function actionCollection($class)

{
(new CollectionGenerator())->generateForClass($class);
}
}

В результате выполнения команды:

php yii codegen/collection app\domains\orders\entities\OrderEntity

будет сгенерирован класс

app\domains\orders\entities\collections\OrderEntityCollection .

Заключение

Это мой первый опыт в создании open-source пакетов. Будут рад любой обратной связи, критике или советам по улучшению кода.

Ссылки

Подробную инструкцию и API коллекций посмотрите в репозитории
Packagist

Комментарии (4)


  1. Hett
    15.06.2022 19:05
    +2

    Не строго, а статически типизированные.


  1. pOmelchenko
    15.06.2022 23:22

    ...


  1. Bone
    16.06.2022 08:29
    +1

    Полезная вещь, буду пробовать.


  1. Stems
    16.06.2022 09:49
    +3

    PHPStorm поддерживает дженерики, и через psalm можно валилидировать.