Let us build a small User class that uses what you learned in this chapter: a constructor, typed properties, private data, validation, methods and __toString().
Step 1: the data
<?php
class User
{
public function __construct(
private string $name,
private string $email,
) {}
public function name(): string
{
return $this->name;
}
}
$user = new User("Isha", "isha@example.com");
echo $user->name();
Isha
Step 2: validate in the constructor
A User should never exist with an invalid email.
<?php
class User
{
public function __construct(
private string $name,
private string $email,
) {
$this->name = trim($name);
if ($this->name === "") {
throw new InvalidArgumentException("Name is required.");
}
if (filter_var($email, FILTER_VALIDATE_EMAIL) === false) {
throw new InvalidArgumentException("Invalid email: $email");
}
}
}
try {
new User(" ", "isha@example.com");
} catch (InvalidArgumentException $e) {
echo $e->getMessage();
}
Name is required.
Step 3: behaviour
<?php
class User
{
private bool $active = true;
public function __construct(
private string $name,
private string $email,
) {}
public function name(): string
{
return $this->name;
}
public function email(): string
{
return $this->email;
}
public function deactivate(): void
{
$this->active = false;
}
public function isActive(): bool
{
return $this->active;
}
public function initials(): string
{
$parts = explode(" ", $this->name);
return strtoupper(implode("", array_map(fn($p) => $p[0], $parts)));
}
public function __toString(): string
{
return $this->name . " <" . $this->email . ">";
}
}
$user = new User("Isha Rao", "isha@example.com");
echo $user . "\n";
echo $user->initials() . "\n";
$user->deactivate();
var_dump($user->isActive());
Isha Rao <isha@example.com>
IR
bool(false)
What you used
- A constructor with promoted, typed, private properties.
- Validation that keeps the object always valid.
- Private data with public methods as the only way in.
__toString()to print the object.
Exercise 1: a Product class
Create a Product with a name and a price (both required). The price must be positive. Add a method priceWithTax(float $rate = 18): float and a __toString() that returns something like Pen: 12.00.
Show a possible solution
<?php
class Product
{
public function __construct(
private string $name,
private float $price,
) {
if ($price <= 0) {
throw new InvalidArgumentException("Price must be positive.");
}
}
public function priceWithTax(float $rate = 18): float
{
return round($this->price * (1 + $rate / 100), 2);
}
public function __toString(): string
{
return $this->name . ": " . number_format($this->price, 2);
}
}
$p = new Product("Pen", 12);
echo $p . "\n";
echo $p->priceWithTax();
Pen: 12.00
14.16
Exercise 2: a shopping cart class
Create a Cart class with add(string $item, float $price), total(): float and count(): int. Keep the items private.
Show a possible solution
<?php
class Cart
{
private array $items = [];
public function add(string $item, float $price): static
{
$this->items[] = ["item" => $item, "price" => $price];
return $this;
}
public function total(): float
{
return array_sum(array_column($this->items, "price"));
}
public function count(): int
{
return count($this->items);
}
}
$cart = (new Cart())->add("Pen", 12)->add("Book", 250);
echo $cart->count() . " items, total " . $cart->total();
2 items, total 262