> */ final class ListCommand implements CommandInterface { private readonly ListSelectionOptions $selectionOptions; private readonly ListReturnOptions $returnOptions; private readonly ListResponseParser $listResponseParser; private readonly StatusResponseParser $statusResponseParser; public function __construct( private readonly string $reference = '', private readonly string $pattern = '*', ?ListSelectionOptions $selectionOptions = null, ?ListReturnOptions $returnOptions = null, ) { $this->selectionOptions = $selectionOptions ?? ListSelectionOptions::none(); $this->returnOptions = $returnOptions ?? ListReturnOptions::none(); $this->listResponseParser = new ListResponseParser(); $this->statusResponseParser = new StatusResponseParser(); } public function name(): string { return 'LIST'; } public function allowedStates(): array { return [ SessionState::Authenticated, SessionState::Selected, ]; } public function encode(string $tag, SessionContext $context): RequestFrame { unset($tag, $context); $command = 'LIST'; $selectionOptions = $this->selectionOptions->toCommand(); if ($selectionOptions !== null) { $command .= ' ' . $selectionOptions; } $command .= sprintf( ' %s %s', StringEncoder::quote($this->reference), StringEncoder::quote($this->pattern), ); $returnOptions = $this->returnOptions->toCommand(); if ($returnOptions !== null) { $command .= ' RETURN ' . $returnOptions; } return new RequestFrame($command); } public function handle(ResponseStream $responses, SessionContext $context): Generator { unset($context); if (!$this->returnOptions->hasStatus()) { foreach ($responses as $response) { if ($response instanceof UntaggedResponse && $response->label() === 'LIST') { yield $this->listResponseParser->parse($response->payload()); continue; } if ($response instanceof TaggedResponse) { CompletionChecker::assertSuccess($this->name(), $response); return; } } throw new ImapException('LIST did not receive a tagged completion response.'); } $mailboxes = []; $statuses = []; foreach ($responses as $response) { if ($response instanceof UntaggedResponse && $response->label() === 'LIST') { $mailbox = $this->listResponseParser->parse($response->payload()); $mailboxes[$mailbox->name()] = $this->applyStatus( $mailbox, $statuses[$mailbox->name()] ?? [], ); continue; } if ($response instanceof UntaggedResponse && $response->label() === 'STATUS') { [$mailboxName, $status] = $this->statusResponseParser->parse($response->payload()); $statuses[$mailboxName] = $status; if (isset($mailboxes[$mailboxName])) { $mailboxes[$mailboxName] = $this->applyStatus($mailboxes[$mailboxName], $status); } continue; } if ($response instanceof TaggedResponse) { CompletionChecker::assertSuccess($this->name(), $response); foreach ($mailboxes as $mailbox) { yield $mailbox; } return; } } throw new ImapException('LIST did not receive a tagged completion response.'); } /** * @param array $status */ private function applyStatus(Mailbox $mailbox, array $status): Mailbox { return $mailbox->fromStatus(new MailboxStatusResult($mailbox->name(), $status)); } }