01 · THE IDEAREAD → TRY → LEARN
The problem: one value, two promises
A union type such as A|B says a value may be either kind. Sometimes you need the opposite: a value that is both at once. Imagine a function that prints a line for a shop item. It needs a name and a price, and those two abilities live in two separate interfaces. Typing the parameter as just one interface would let objects through that cannot do the other job.
An intersection type joins class or interface names with &. The value must satisfy every part. The PHP manual lists intersection types as available since PHP 8.1.
A first intersection type
Here Product implements both interfaces, and the function demands both.
interface Named { public function getName(): string; }
interface Priced { public function getPrice(): int; }
final class Product implements Named, Priced {
public function __construct(private string $name, private int $price) {}
public function getName(): string { return $this->name; }
public function getPrice(): int { return $this->price; }
}
function describeItem(Named&Priced $item): string {
return $item->getName() . ' costs ' . $item->getPrice();
}
echo describeItem(new Product('Atlas', 1200)), "\n";Atlas costs 1200
Inside the function, PHP lets you call methods from both interfaces, because the type guarantees they exist.
What happens on a mismatch
Type declarations are checked at call time. If an object satisfies only one interface, PHP throws a TypeError. You can catch it like any other throwable.
interface Named { public function getName(): string; }
interface Priced { public function getPrice(): int; }
final class Label implements Named {
public function getName(): string { return 'Sale'; }
}
function describeItem(Named&Priced $item): string {
return $item->getName() . ' costs ' . $item->getPrice();
}
try {
echo describeItem(new Label()), "\n";
} catch (TypeError $e) {
echo get_class($e), "\n";
}TypeError
Passing null fails the same way, since null is not an instance of either interface.
Return types work too
Intersection types are not limited to parameters. They can describe return values, and the manual notes that type declarations also apply to properties.
interface Named { public function getName(): string; }
interface Priced { public function getPrice(): int; }
final class Product implements Named, Priced {
public function __construct(private string $name, private int $price) {}
public function getName(): string { return $this->name; }
public function getPrice(): int { return $this->price; }
}
function makePen(): Named&Priced { return new Product('Pen', 150); }
$item = makePen();
echo json_encode(['name' => $item->getName(), 'price' => $item->getPrice()], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";{"name":"Pen","price":150}
The caller sees only the two interface contracts, not the concrete class. That keeps code flexible.
Rules to remember
- Every part must be a class-type name, meaning a class or an interface. Scalars such as
int&Countableare rejected at compile time. self,parentandstaticcannot appear in an intersection.- Repeating a name, even with different letter case, is an error.
- Since PHP 8.2 you can combine intersections inside union types (DNF types), but this lesson stays with plain intersections.
Exercise contract
You will write summarizeCatalog(array $rows): array. Each row is [kind, name, price]. The starter already defines the interfaces and the classes Product (both interfaces), Label (only Named) and Fee (only Priced).
- Define a closure whose parameter is typed
Named&Pricedand that returnsname=price, for exampleAtlas=1200. - Build an object for each row. Unknown kinds produce
null. - Call the closure inside
try. On aTypeError, record the stringskippedinstead. - Return the list of strings in input order.
Boundary cases to think about: an empty list, a product priced 0 (it is valid and must not be skipped), and an unknown kind.
Recap
Named&Priced means both. PHP enforces it when the function is called and throws TypeError otherwise. Use it when a function genuinely needs two capabilities and no single interface describes both.
Functions · Arrays · Types
- Wrap objects implementing two local interfaces behind a JSON-callable exercise
- Implement and test a deterministic PHP 8.4 function