protected function applyValue($input, Context $ctx) { if ($input === null) { goto done; } $input = parent::applyValue($input, $ctx); if (!$ctx->nodeValid()) { return $input; } $itemsLen = count($this->items); $expectedKey = 0; $keysValid = true; $keySequenceValid = null; $idx = 0; foreach ($this->checkIterator($input) as list($key, $idx, $item, $itemChecker)) { if ($keySequenceValid === null) { $keySequenceValid = $key === 0; } // 1-indexing is for humans, 0-indexing is for computers $ctx->push($key, $key + 1); try { // PHP will always cast stringy keys to ints if possible. if (is_string($key)) { $ctx->addReason($this, ['id' => 'list.invalidKey']); $keysValid = false; goto next_item; } if ($key !== $expectedKey++) { $keySequenceValid = false; } if (!$itemChecker) { $ctx->addReason($this, ['id' => 'list.invalid']); } else { $input[$key] = $itemChecker->apply($item, $ctx); } } finally { $ctx->pop($key); } next_item: } if ($keysValid && $keySequenceValid === false) { $ctx->addReason($this, ['id' => 'list.keySequence', 'params' => ['first' => '0']]); } if ($itemsLen && $idx + 1 < $itemsLen) { $ctx->addReason($this, ['id' => 'list.missingIndex', 'params' => ['first' => '0']]); } done: return $input; }