php 62 lines · 8 steps

An immutable typed collection in PHP

A final PHP class wraps Product objects in an immutable, iterable, countable collection keyed by SKU.

Explained by highlit
1<?php
2 
3declare(strict_types=1);
4 
5namespace App\Collection;
6 
7use App\Entity\Product;
8use ArrayIterator;
9use Countable;
10use InvalidArgumentException;
11use IteratorAggregate;
12use Traversable;
13 
14final class ProductCollection implements IteratorAggregate, Countable
15{
16 private array $products = [];
17 
18 public function __construct(Product ...$products)
19 {
20 foreach ($products as $product) {
21 $this->products[$product->getSku()] = $product;
22 }
23 }
24 
25 public function add(Product $product): self
26 {
27 $clone = clone $this;
28 $clone->products[$product->getSku()] = $product;
29 
30 return $clone;
31 }
32 
33 public function get(string $sku): Product
34 {
35 return $this->products[$sku]
36 ?? throw new InvalidArgumentException("No product with SKU {$sku}.");
37 }
38 
39 public function filter(callable $predicate): self
40 {
41 return new self(...array_filter($this->products, $predicate));
42 }
43 
44 public function totalPrice(): int
45 {
46 return array_reduce(
47 $this->products,
48 static fn (int $carry, Product $p): int => $carry + $p->getPriceInCents(),
49 0,
50 );
51 }
52 
53 public function getIterator(): Traversable
54 {
55 return new ArrayIterator(array_values($this->products));
56 }
57 
58 public function count(): int
59 {
60 return count($this->products);
61 }
62}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Cloning before mutation keeps a collection immutable so shared references never surprise you.
  2. 2Implementing IteratorAggregate and Countable lets your class behave like a native array in loops and count().
  3. 3Keying entries by a domain identifier like SKU gives free deduplication and O(1) lookup.

Related explainers

Share this explainer

Here's the card — post it anywhere.

An immutable typed collection in PHP — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code