Level 9 requires every mixed to be narrowed before use, which is a different order of work from level 8.
// level 8: fine
function handle(mixed $payload): void
{
$this->process($payload['id']);
}
// level 9: Cannot access offset 'id' on mixed
function handle(mixed $payload): void
{
if (! is_array($payload) || ! isset($payload['id'])) {
throw new InvalidPayload();
}
$this->process($payload['id']);
}
Every boundary that decodes JSON, reads a configuration value or unserialises something produces a mixed, so the level is really asking for a parse step at every one. That is the right design and it is a quarter of work rather than a fortnight, which is why most codebases stop at 8 and treat 9 as a direction.