Boss: typed repository
A repository is where database rows become domain objects, which makes it the boundary this whole world has been about. Get it right and nothing above it has to wonder whether a price is a string, whether a list has gaps, or whether a product might secretly be null.
This one gets it wrong in every way World 2 covers. Every column arrives as a string, and an @var tag pretends otherwise. A list of mugs comes back keyed by ID. get() promises a Product and can return null. page() returns a Page of who knows what, for page number zero if you ask.
Catalogue (read only) is written against the honest version, and PHPStan reports where the two disagree.
Your checklist
| Level | Ask of ProductRepository |
|---|---|
| 2.1 array is a shrug | Does every array say what's in it, and is a list really a list? |
| 2.2 Refined scalar types | Do page numbers and prices say which ints they allow? |
| 2.3 Array shapes | Is every raw row converted and checked at the boundary, rather than trusted? |
| 2.4 Type aliases | If you find yourself writing the same shape twice, does it have one name? |
| 2.5 Generics | Does page() say what its Page holds? |
| 2.6 Narrowing and assertions | Is every null dealt with, and every @var claim replaced with a check? |
Start with Analyse, then work down the checklist. The tests cover the runtime behaviour, including rows that aren't products.