PHP 1614 ~ 5 мин.

PHP 8: Объединение типов (Union Types)

PHP 8: Объединение типов (Union Types)

PHP прошел долгий путь с типами. Теперь у нас есть скалярные типы, возвращаемые типы, типы допускающие значение NULL и теперь PHP 8.0 будет поддерживать объединение типов (Union Types).

В версиях до PHP 8.0 вы могли объявить только один тип для свойств, параметров и возвращаемых типов. PHP 7.1 и более поздние версии имеют типы, допускающие значение null, что означает, что вы можете объявить тип с помощью null  объявления типа, аналогичного ?string.

Начиная с PHP 8.0, вы можете объявлять более одного типа для аргументов, возвращаемых типов и свойств класса:

class Example {
    private int|float $foo;
    public function sum(float|int $bar): int|float {
        return $this->foo + $bar;
    }
}

Т.е. мы можем теперь более лучше объявлять несколько типов,  разделеные знаком вертикальной черты ( | ).
Подобно типам, применяемым в предыдущих версиях PHP, теперь PHP будет следить за тем, чтобы параметры функции, возвращаемые типы и свойства класса принадлежали одному из типов, объявленных в определении. Все определения типов будут проверяться во время выполнения, поэтому PHP выдаст ошибку, если мы попытаемся передать/вернуть недопустимый тип.

Типы объединений до PHP 8.0

Обнуляемые типы

В PHP уже есть поддержка типов, допускающих значение null; Вы можете объявить тип как обнуляемый, поставив знак ? перед типом. Например, если определенное свойство может быть и string или null, вы можете объявить его как ?string. В PHP 8.0 такая запись типов string|null будет функционально эквивалентен ?string.

Тип iterable

Существует также псевдотип iterable, который функционально эквивалентен array|Traversable.

Комментарии PHPDoc

В стандарте PHPDoc была поддержка Union Types. Вы могли использовать комментарий PHPDoc для объявления типов, разрешенных для определенного блока кода:

class Example {  

 /**  
  * @var int|float  
  */  
  private $foo;  

  /**  
   * @param int|float $bar  
   * @return int|float  
   */  
  public function sum(int $bar): int {  
    return $this->foo + $bar;  
  }
 }

 

Типы объединений в PHP 8.0

Начиная с PHP 8.0, теперь можно объявлять любое количество произвольных типов для свойств, аргументов и возвращаемых типов. Есть несколько исключений, которые не имеют смысла использовать вместе, например string|void.

Теперь вы можете полностью избавиться от комментариев PHPDoc типа @var, @param, и @return в пользу типов, применяемых в самом коде. Это, безусловно, поможет очистить шаблонные комментарии, которые были только потому, что их нельзя было объявить раньше в коде.

Существующие типы, допускающие значение NULL, не удаляются и не устаревают. Вы можете продолжать использовать ?string в качестве сокращения для string|null.

Тип iterable тоже не убрали и будет функционально равен array|Traversable.

В RFC далее объясняются некоторые ограничения и другие улучшения для типов Union:

Не разрешен тип void

В PHP уже есть поддержка типа void. Он разрешен только как возвращаемый тип, потому что использование void в другом месте не имеет никакого смысла. Либо функция не должна возвращать никакого значения, либо вызывать return без указания значения.

В PHP 8.0 Union Types нельзя комбинировать тип void с любым другим типом. Например, не допускается следующее:

function foo(): void|null {}


Специальный тип false

В Ядре PHP и в многих старых библиотеках return false указывает на отрицательный результат. Например, если у вас есть функция, которая загружает использование учетной записи по ее идентификатору, return false может указывать на то, что пользователь не существует.

Чтобы помочь принятию типов объединения, разрешено использовать false как часть типов объединения:

public function getUser(int $id): User|false {
     //...
}

Есть несколько основных функций PHP, которые также следуют тому же шаблону. Например, функция strpos() Возвращает int позицию первого вхождения подстроки или false если ничего не было найдено:

strpos(string $haystack, string $needle, int $offset = 0): int|false

Итак, что нужно учитывать:

  • false нельзя использовать как самостоятельный тип. Это означает, что объявление типа как public false $foo не допускается. Вы все еще можете использовать тип bool для этого. (Правка от автора: Начиная с PHP 8.2 разрешено использование false в качестве отдельного типа
  • Псевдотип false разрешен везде, где разрешены типы: свойства класса, аргументы функций и возвращаемые типы.
  • Если используется тип bool, тогда false нельзя использовать в объявлении того же типа.

Типы, допускающие значение null (?Type) и null нельзя смешивать

Хотя ?string это сокращение для string|null, эти два обозначения нельзя смешивать. Если ваше объявление типа имеет более одного типа и null, оно должно быть объявлено следующим образом:

Type1|Type2|null

И ни в коем случае не должно быть объявлено двусмысленным объявлением:

?Type1|Type2


Повторяющиеся типы не допускаются

Вы не можете объявить int|int или, int|INT поскольку это по сути одно и то же. Более того запись int|?int тоже не допускается.

Последнее приведет к синтаксической ошибке, а первое выдаст эту ошибку:

Fatal error: Duplicate type ... is redundant in ... on line ...

 

Недопустимы избыточные типы

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

  • bool|false не допускается, потому что false это тоже тип bool
  • object нельзя использовать с именем класса, потому что все объекты класса тоже имеют тип object
  • iterable нельзя использовать с array или Traversable потому что iterable это уже тип объединения array|Traversable.
  • Имена классов можно использовать, даже если одно расширяет другое.

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

Вот некоторые примеры ошибок:

function foo(): bool|false {} // Fatal error: Duplicate type false is redundant in ... on line ...
function foo(): DateTime|object {} // Fatal error: Type DateTime|object contains both object and a class type, which is redundant in ... on line ...
function foo(): iterable|array {} // Fatal error: Type iterable|array contains both iterable and array, which is redundant in ... on line ...

А вот такая запись вполне пройдет:

class A{}
class B extends A{}
function foo(): A|B {}


Типовая дисперсия

Дисперсия Union Types следует принципу LSP (SOLID). Это делается для того, чтобы все подклассы и реализации интерфейса не изменяли поведение программы и по-прежнему придерживались контракта. Это применяется в PHP даже без Union Types. 

  • Параметр является контрвариантным: типы могут быть расширены с помощью супертипа.
  • Типы возвращаемых значений являются ковариантными: типы могут быть ограничены подтипом.
  • Инвариантные типы свойств: типы не могут быть изменены на подтип или супертип.

bool и false

В объединенных типах PHP считается false подтипом bool. Это позволяет варьировать следующим образом:

class FooParent {
    public function foo(int|false $a): int|bool {}
}

class FooChild extends FooParent{
    public function foo(int|bool $b): int|false {}  
}


Влияние обратной совместимости  

Union Types - это новая функция в PHP, и ее новый синтаксис не нарушает существующие функции. Поскольку это изменение синтаксиса в движке, эту функциональность нельзя перенести в более старые версии с помощью полифилов.

Что думаешь?

Категории
  • PHP 65
  • Заметки 15
  • Безопасность 3
  • Флуд 2
  • Nginx 2
  • ИТ новости 2
  • Видео 1
  • Docker 1
  • Roadmap 1
  • Архитектура 0

Хочешь поддержать сайт?

Делаем из мухи слона

sergeymukhin.com

персональный блог о веб-разработке от Сергея Мухина. Блог был основан в 2018 году, и собирался уделять основное внимание последним тенденциям, учебным пособиям, а также советам и рекомендациям, позволяющим начинающим девелоперам встать быстрее на правильную дорогу веб разработки, но что-то пошло не так 😃

Релизы PHP 8.4

Дата Релиз
8 Июня 2024 Альфа 1
20 Июня 2024 Альфа 2
04 Июля 2024 Альфа 3
16 Июля 2024 Feature freeze
18 Июля 2024 Бета 1
01 Августа 2024 Бета 2
15 Августа 2024 Бета 3
29 Августа 2024 RC 1
12 Сентября 2024 RC 2
26 Сентября 2024 RC 3
10 Октября 2024 RC 4
24 Октября 2024 RC 5
07 Ноября 2024 RC 6
21 Ноября 2024 GA

Что нового?