levelmax World 1 Strict Types
  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. B
L
Level 1.B ~20 min Target: PHPStan level 5

Boss: tighten a checkout service

Every codebase has one of these: a service written before the rest of the code grew types, which now sits between modern classes and makes all of them less trustworthy. Checkout takes $gateway, which could be anything. It returns an Order, or false, and false might mean "already paid" or "card declined". It also tries to change an order that can't be changed.

Nothing here is new. This is World 1 in one class.

Your checklist

Level Ask of Checkout
1.1 strict_types Is the file strict, so a string can't sneak in as a number?
1.2 Scalar and return types Does every parameter and return say what it is?
1.3 never, void, and static Does each signature tell the truth about what comes back?
1.4 Nullable versus union Is there one honest type for each outcome, rather than Order or false?
1.5 Readonly properties Does it create a new order, rather than editing one it doesn't own?
1.6 Enums Does it compare statuses as cases, not strings?

Start with Analyse. PHPStan will point at most of the problems, and the tests cover the rest.

CHALLENGE

Checkout predates everything in this world. It's untyped, it compares an enum to a string, it tries to change a readonly order, and it returns false for two very different failures.

Bring src/Checkout.php up to World 1 standards, so the tests pass and PHPStan is clean at level 5:

  • strict types, a final class, and a typed PaymentGateway dependency
  • pay(Order $order, string $token): Order, returning a new paid order and leaving the original alone
  • only a pending order can be paid: anything else throws a DomainException before the card is charged
  • a declined payment throws PaymentDeclined
Running on PHP 8.3…

Run the tests, run PHPStan, or submit for both. Ctrl/⌘ + Enter submits from the editor.