*/ final class SearchCommand implements CommandInterface { /** * @param SearchCriteriaBuilder|list $criteria */ public function __construct( private readonly SearchCriteriaBuilder|array $criteria = ['ALL'], private readonly IdentifierMode $identifierMode = IdentifierMode::Sequence, private readonly ?string $charset = 'UTF-8', ) {} public function name(): string { return 'SEARCH'; } public function allowedStates(): array { return [SessionState::Selected]; } public function encode(string $tag, SessionContext $context): RequestFrame { unset($tag, $context); $criteria = $this->normalizeCriteria( $this->criteria instanceof SearchCriteriaBuilder ? $this->criteria->toArray() : $this->criteria, ); $command = $this->identifierMode === IdentifierMode::Uid ? 'UID SEARCH' : 'SEARCH'; if ($this->charset !== null && $this->charset !== '') { $command .= ' CHARSET ' . strtoupper(trim($this->charset)); } $command .= ' ' . implode(' ', $criteria); return new RequestFrame($command); } public function handle(ResponseStream $responses, SessionContext $context): SearchResult { if ($context->selectedMailbox() === null) { throw new ImapException('SEARCH requires a selected mailbox.'); } $matches = []; foreach ($responses as $response) { if ($response instanceof UntaggedResponse && $response->label() === 'SEARCH') { $matches = $this->parseMatches($response->payloadTokens()); continue; } if ($response instanceof TaggedResponse) { if (!$response->isOk()) { throw new ImapException('SEARCH failed: ' . $response->text()); } return new SearchResult($matches, $this->identifierMode); } } throw new ImapException('SEARCH did not receive a tagged completion response.'); } /** * @param list $criteria * @return list */ private function normalizeCriteria(array $criteria): array { $normalized = []; foreach ($criteria as $criterion) { $criterion = trim($criterion); if ($criterion === '') { continue; } $normalized[] = $criterion; } return $normalized === [] ? ['ALL'] : $normalized; } /** * @param list $tokens * @return list */ private function parseMatches(array $tokens): array { $matches = []; foreach ($tokens as $token) { if (preg_match('/^[1-9]\d*$/', $token) !== 1) { continue; } $matches[] = (int) $token; } return $matches; } }