Эта статья - первая из серии, посвященной отсутствующим битам PHP.
В последующих статьях будет рассказано о других концепциях типизации, таких как средства доступа и типы данных.

PHP 7.2 не за горами, принося связанные с типом изменения, такие как тип object в сигнатуре или расширение типа параметра.
Они подтверждают желание сообщества PHP укрепить систему типов PHP и повысить безопасность типов.

В Libcast мы ценим эти изменения, которые позволяют полагаться на IDE для отображения ошибок типов при вводе и проверки типов во время компиляции, что сокращает время, необходимое для поиска и исправления ошибки. < br /> Generics - это функция, которая, как мы надеемся, скоро появится в PHP и позволит использовать универсальные контейнеры данного типа.

Дженерики

Общие классы позволяют объявить общий контейнер, который должен быть специализированным при использовании (вы не можете использовать общий класс напрямую).
Любой тип может использоваться для специализации универсальный класс при условии, что они соблюдают сигнатуры, используемые в универсальном классе.

Универсальный класс может использовать несколько универсальных типов, а затем быть частично специализированным, исправляя только подмножество универсальных типов.

Дженерики в PHP

RFC находится в стадии разработки, но еще не принят.

Hack / HHVM уже принял на вооружение дженерики.

ircmaxell » провел эксперимент в пользовательской среде PHP для развлечения (конечно, не используйте это в продукте).

Обобщения были бы полезны в PHP для контейнеров: абстрактного типа данных (стек, очередь, карта,…) и контейнеров домена (набор дней, группа людей,…).
Они также позволили бы более точные подписи: at на этот раз у PHP есть iterable для объявления коллекции в подписи, но общее объявление iterable<Book> позволит сказать, что мы запрашиваем только коллекцию книг.

Случай для дженериков в Libcast

Мы создали макет нового API защищенной видеоплатформы Libcast, в котором нам нужно генерировать данные случайным образом, но иногда в соответствии с заданным распределением.

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

Мы также хотели бы высмеивать поддельных посетителей в соответствии с реальным географическим распределением нашей текущей базы посетителей.

Мы видим возможность извлечь ту часть, где выбор производится по распределению в общем классе.

Вот упрощенный первый снимок для обработки процитированных примеров:

// Generates IP addresses
final class IpGenerator
{
    public function generate(string $countryCode = 'FR'): Ip
    {
        // ...
    }
}
final class DistributedIpGenerator
{
    /** @var IpGenerator */
    private $generator;
    /** @var array */
    private $distribution;
    public function __construct(IpGenerator $generator, array $distribution)
    {
        $this->generator = $generator;
        $this->distribution = $distribution;
    }
    public function generate(): Ip
    {
        // Pick a country code wisely
        // $countryCode = ...
        return $this->generator->generate($countryCode);
    }
}
// Generate domain events
final class EventGenerator
{
    public function generate(string $eventType): Event
    {
    }
}
final class DistributedEventGenerator
{
    /** @var EventGenerator */
    private $generator;
    /** @var array */
    private $distribution;
    public function __construct(EventGenerator $generator, array $distribution)
    {
        $this->generator = $generator;
        $this->distribution = $distribution;
    }
    public function generate(): Event
    {
        // Pick an event type wisely
        // $eventType = ...
        return $this->generator->generate($eventType);
    }
}

Классы DistributedIpGenerator и DistributedEventGenerator почти идентичны, за исключением сигнатур и возвращаемых типов.
Если бы у нас были дженерики, мы могли бы использовать один класс:

class DistributedGenerator<DataType>
{
    /** @var callable */
    private $generator;
    /** @var array */
    private $distribution;
    public function __construct(callable $generator, array $distribution)
    {
        $this->generator = $generator;
        $this->distribution = $distribution;
    }
    public function generate(): DataType
    {
        // Pick a value wisely
        // $value = ...
        return $this->generator($value);
    }
}

Этот универсальный класс мог бы быть еще более специализированным, если бы мы хотели сгенерировать событие заданного типа: UserEvent, VideoEvent,….
Обратите внимание, что вы также можете включить тип генератора в список универсальных типов, но нам, возможно, придется обрабатывать несколько реализаций распределенного генератора:

class DistributedGenerator<GeneratorType, DataType>
{
    /** @var GeneratorType */
    private $generator;
    /** @var array */
    private $distribution;
    public function __construct(GeneratorType $generator, array $distribution)
    {
        $this->generator = $generator;
        $this->distribution = $distribution;
    }
    public function generate(): DataType
    {
        // Pick a value wisely
        // $value = ...
        // This part would change for each GeneratorType:
        return $this->generator->whatever($value);
    }
}

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

Ссылки: