$command * @return TResult */ public function perform(CommandInterface $command, SessionContext $context): mixed { $this->assertState($command->allowedStates(), $context->state(), $command->name()); $this->logger?->debug('IMAP command execution started: {command} (state={state})', [ 'command' => $command->name(), 'state' => $context->state()->value, ]); $tag = $this->tags->next(); $frame = $command->encode($tag, $context); $this->writer->write($tag, $frame); return $command->handle(new ResponseStream(function () use ($tag, $context): Generator { yield from $this->responsesUntilCompletion($tag, $context); }), $context); } /** * @param list $allowedStates */ private function assertState(array $allowedStates, SessionState $currentState, string $commandName): void { foreach ($allowedStates as $allowedState) { if ($allowedState === $currentState) { return; } } throw new ImapException(sprintf( 'Command %s is not allowed while session is in state %s.', $commandName, $currentState->value, )); } private function responsesUntilCompletion(string $tag, SessionContext $context): Generator { while (true) { $response = $this->reader->readResponse(); if ($response instanceof UntaggedResponse && $response->label() === 'CAPABILITY') { $context->replaceCapabilities(...$response->payloadTokens()); } yield $response; if ($response instanceof TaggedResponse && $response->tag() === $tag) { $this->logger?->debug('IMAP command execution completed: tag={tag} status={status}', [ 'tag' => $tag, 'status' => $response->status(), ]); return; } } } }