Intersection-тип A&B (PHP 8.1) - значення має відповідати всім перелічених типам одночасно. На практиці - об'єкт, що реалізує кілька інтерфейсів:
function process(Countable&Traversable $items): void
{
echo count($items);
foreach ($items as $item) { /* ... */ }
}
Раніше доводилося або обирати один інтерфейс і перевіряти другий вручну (instanceof), або створювати штучний інтерфейс CountableTraversable, який нічого не додає.
DNF-типи (Disjunctive Normal Form, PHP 8.2) - поєднання union і intersection: об'єднання груп, кожна з яких - перетин.
function save((Countable&ArrayAccess)|null $data): void {}
function cache((Stringable&JsonSerializable)|string $value): void {}
Правило DNF: перетини мають бути в дужках, і вони об'єднуються через |. Запис A&(B|C) недозволений - його треба переписати як (A&B)|(A&C).
Обмеження:
- Перетин - лише з класів і інтерфейсів, не зі скалярів (
int&stringне має сенсу). - Дублювання й надлишкові типи (
(A&B)|A) - помилка компіляції.
Коли потрібні:
- Функція справді покладається на кілька незалежних можливостей об'єкта (лічильність і ітерація, рядкове представлення й серіалізація).
- Бібліотеки з дрібними інтерфейсами-можливостями (принцип розділення інтерфейсів) - щоб вимагати рівно потрібні, а не один «товстий».
- Моки в тестах: PHPUnit створює об'єкти-перетини
MockObject&Service, і саме такий тип має змінна з моком.
Альтернатива: якщо одна й та сама комбінація інтерфейсів повторюється скрізь, можливо, це окреме поняття домену - і варто оголосити інтерфейс, що розширює обидва.