Types make classes safer. You have seen typed properties and typed parameters separately. Here they work together.
Typed properties and methods
<?php
declare(strict_types=1);
class Invoice
{
public function __construct(
private int $id,
private float $amount,
private ?string $note = null,
) {}
public function summary(): string
{
return "Invoice #{$this->id}: " . number_format($this->amount, 2);
}
}
echo (new Invoice(7, 1234.5))->summary();
Invoice #7: 1,234.50
A class as a type
A parameter can require an object of a certain class.
<?php
class Customer
{
public function __construct(public string $name) {}
}
class Order
{
public function __construct(private Customer $customer) {}
public function describe(): string
{
return "Order for " . $this->customer->name;
}
}
echo (new Order(new Customer("Ana")))->describe();
Order for Ana
If you pass anything that is not a Customer, PHP throws a TypeError.
<?php
class Customer {}
function show(Customer $c): string
{
return "ok";
}
try {
show("Ana");
} catch (TypeError $e) {
echo substr($e->getMessage(), 0, 52);
}
show(): Argument #1 ($c) must be of type Customer, s
self and static as return types
self is the class itself. static is the class that was called (it works with inheritance).
<?php
class Money
{
public function __construct(public readonly int $cents) {}
public function plus(Money $other): self
{
return new self($this->cents + $other->cents);
}
}
echo (new Money(150))->plus(new Money(250))->cents;
400
Interfaces as types
Typing against an interface means “anything that can do this”. The code works with any class that implements it. Interfaces are in Interfaces.
<?php
interface Shape
{
public function area(): float;
}
class Square implements Shape
{
public function __construct(private float $side) {}
public function area(): float
{
return $this->side ** 2;
}
}
function printArea(Shape $shape): void
{
echo "Area: " . $shape->area();
}
printArea(new Square(3));
Area: 9
Nullable and union class types
<?php
class Logger {}
class Service
{
public function __construct(private ?Logger $logger = null) {}
public function hasLogger(): bool
{
return $this->logger !== null;
}
}
var_dump((new Service())->hasLogger());
bool(false)
Typed properties and arrays
PHP cannot say “array of Product”. Use array and document the items.
<?php
class Cart
{
/** @var Product[] */
private array $items = [];
}
Why types help
- Wrong values are rejected at the boundary, with a clear message.
- Editors can complete names and warn about mistakes.
- A class’s signature tells you how to use it, without reading its body.