generated from Nodarx/template
86 lines
2.8 KiB
PHP
86 lines
2.8 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace KTXM\ProviderImap\Client\Protocol;
|
|
|
|
use Generator;
|
|
use KTXM\ProviderImap\Client\Command\CommandInterface;
|
|
use KTXM\ProviderImap\Client\ImapException;
|
|
use KTXM\ProviderImap\Client\Protocol\Response\TaggedResponse;
|
|
use KTXM\ProviderImap\Client\Protocol\Response\UntaggedResponse;
|
|
use KTXM\ProviderImap\Client\SessionContext;
|
|
use KTXM\ProviderImap\Client\SessionState;
|
|
use Psr\Log\LoggerInterface;
|
|
|
|
final class CommandExecutor
|
|
{
|
|
public function __construct(
|
|
private readonly ProtocolReader $reader,
|
|
private readonly ProtocolWriter $writer,
|
|
private readonly TagGenerator $tags = new TagGenerator(),
|
|
private readonly ?LoggerInterface $logger = null,
|
|
) {}
|
|
|
|
/**
|
|
* @template TResult
|
|
* @param CommandInterface<TResult> $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<SessionState> $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;
|
|
}
|
|
}
|
|
}
|
|
} |