Here is a class written in an old style. Your job is to rewrite it with modern PHP. Try it first, then compare.
The old code
<?php
class Order
{
var $id;
var $status;
var $items;
function Order($id, $items)
{
$this->id = $id;
$this->items = $items;
$this->status = "pending";
}
function total()
{
$t = 0;
foreach ($this->items as $i) {
$t = $t + $i["price"] * $i["qty"];
}
return $t;
}
function statusLabel()
{
switch ($this->status) {
case "pending":
return "Waiting";
case "paid":
return "Paid";
default:
return "Unknown";
}
}
}
What is wrong with it
varand untyped properties, and an old-style constructor name.- No types on parameters or return values.
- Public data anyone can change.
- A status as a free string:
"piad"would be accepted silently. switchwith loose comparison.
Exercise: rewrite it
Rewrite the class in modern PHP. Use declare(strict_types=1), an enum for the status, a constructor with promoted readonly or private properties, types everywhere, array_reduce() or array_sum(), and match for the label.
Show a possible solution
<?php
declare(strict_types=1);
enum OrderStatus: string
{
case Pending = "pending";
case Paid = "paid";
public function label(): string
{
return match ($this) {
self::Pending => "Waiting",
self::Paid => "Paid",
};
}
}
final class Order
{
/**
* @param array<int, array{price: float, qty: int}> $items
*/
public function __construct(
public readonly int $id,
private array $items,
private OrderStatus $status = OrderStatus::Pending,
) {}
public function total(): float
{
return array_reduce(
$this->items,
fn(float $sum, array $item): float => $sum + $item["price"] * $item["qty"],
0.0
);
}
public function status(): OrderStatus
{
return $this->status;
}
public function markPaid(): void
{
$this->status = OrderStatus::Paid;
}
}
$order = new Order(7, [
["price" => 12.0, "qty" => 5],
["price" => 250.0, "qty" => 2],
]);
echo $order->total() . " " . $order->status()->label() . "\n";
$order->markPaid();
echo $order->status()->label();
560 Waiting
Paid
What changed
| Old | Modern |
|---|---|
var $id; | A promoted public readonly int $id |
| Constructor named after the class | __construct() |
| Free string status | OrderStatus enum |
switch | match on the enum |
| A manual loop | array_reduce() |
| No types | Types on every parameter, property and return |
Exercise 2: spot the PHP 8 features
Look at the finished class. List at least six features that did not exist in PHP 7.0.
Show the answer
Constructor property promotion (8.0), readonly properties (8.1), enums (8.1), match (8.0), trailing commas in parameter lists (8.0), the : string backed enum (8.1), and readonly with promotion (8.1). Typed properties (7.4) and arrow functions (7.4) arrived earlier in PHP 7.4.