Квест → Как хакнуть форму
Прошли: 77
РНР 7.2 не за горами, и эта версия принесёт нам такие изменения в типах, как возможность указать тип object в сигнатуре методов, или как расширение типа параметра.
Они подтверждают желание сообщества PHP укрепить систему типов в PHP и улучшить безопасность типов.
В Libcast (видеохостинг), мы ценим эти изменения, которые позволяют полагаться на IDE, которая отображает ошибки типов при вводе кода и проверяет типы во время компиляции, тем самым уменьшая время, необходимое для поиска и исправления ошибки.
Дженерики (Generics) - это фича, которая, как мы надеемся, скоро появятся в PHP и позволит создавать универсальные контейнеры указанного типа.
Generic-классы позволяют объявить универсальный контейнер, тип которого должен быть задан во время использования (нельзя использовать общий класс - сам generic).
Для специализации generic-класса, может быть использован любой тип, если он соответствует сигнатуре в универсальном (generic) классе.
Generic-класс может использовать несколько generic-типов, или быть частично специализирован, задав только часть generic-типов.
Generics-RFC всё ещё в черновике и не был принят.
Hack/HHVM уже реализовал дженерики.
ircmaxell провели эксперимент среди пользователей РНР, как говорится "just for fun" (не используйте это в продакшен, конечно).
Дженерики будут полезны в PHP для таких контейнеров: абстрактный тип данных (стек, очередь, карта ...), и контейнеров вашей предметной области (список дней, группа людей ...).
Также они позволяют иметь более точные сигнатуры: на данный момент, PHP имеет iterable для возможности объявить коллекцию в сигнатуре, но объявление generic-iterable позволит сказать, что мы просим коллекцию именно книг.
Мы создали заглушку для нового 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
{
/** @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
{
/** @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);
}
}
Дженерики связаны со всеми концепциями типизации, о которых мы, возможно, поговорим в следующих статьях.
Ссылки:
Идея клевая, но придется тогда пожертвовать скоростью работы или переходить на использование компиляции.
Ну не совсем.
Пре-компиляция в байт-код и сейчас есть. Если при пре-компиляции будут генерироваться готовые конечные классы, которые используются в данном приложении (а скорее всего так и будут реализовывать), то скорость исполнения не увеличится. Немного возрастёт время пре-компиляции в байт-код, но это будет на столько не существенно, что не отразится на скорости в общем. А с учётом того, что пре-компилированный код кешируется, то разницы не будет вообще.