• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

CPS-IT / mailqueue / 20021782204

08 Dec 2025 08:36AM UTC coverage: 16.692% (+16.7%) from 0.0%
20021782204

push

github

web-flow
Merge pull request #185 from CPS-IT/task/tests

109 of 653 relevant lines covered (16.69%)

0.34 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/Classes/Command/FlushQueueCommand.php
1
<?php
2

3
declare(strict_types=1);
4

5
/*
6
 * This file is part of the TYPO3 CMS extension "mailqueue".
7
 *
8
 * It is free software; you can redistribute it and/or modify it under
9
 * the terms of the GNU General Public License, either version 2
10
 * of the License, or any later version.
11
 *
12
 * For the full copyright and license information, please read the
13
 * LICENSE.txt file that was distributed with this source code.
14
 *
15
 * The TYPO3 project - inspiring people to share!
16
 */
17

18
namespace CPSIT\Typo3Mailqueue\Command;
19

20
use CPSIT\Typo3Mailqueue\Exception;
21
use CPSIT\Typo3Mailqueue\Mail;
22
use Symfony\Component\Console;
23
use Symfony\Component\Mailer;
24
use TYPO3\CMS\Core;
25

26
/**
27
 * FlushQueueCommand
28
 *
29
 * @author Elias Häußler <e.haeussler@familie-redlich.de>
30
 * @license GPL-2.0-or-later
31
 */
32
#[Console\Attribute\AsCommand(
33
    name: 'mailqueue:flushqueue',
34
    description: 'Flush mail queue by sending mails using the configured real transport',
35
)]
36
final class FlushQueueCommand extends Console\Command\Command
37
{
38
    private ?Console\Output\OutputInterface $jsonOutput = null;
39
    private Console\Style\SymfonyStyle $io;
40

41
    public function __construct(
×
42
        private readonly Core\Mail\Mailer $mailer,
43
    ) {
44
        parent::__construct();
×
45
    }
46

47
    protected function configure(): void
×
48
    {
49
        $this->addOption(
×
50
            'json',
×
51
            'j',
×
52
            Console\Input\InputOption::VALUE_NONE,
×
53
            'Output results in JSON format',
×
54
        );
×
55
        $this->addOption(
×
56
            'limit',
×
57
            'l',
×
58
            Console\Input\InputOption::VALUE_REQUIRED,
×
59
            'Maximum number of mails to send in one iteration',
×
60
        );
×
61
        $this->addOption(
×
62
            'recover-timeout',
×
63
            'r',
×
64
            Console\Input\InputOption::VALUE_REQUIRED,
×
65
            'Timeout in seconds for recovering mails that have taken too long to send',
×
66
        );
×
67
    }
68

69
    protected function initialize(Console\Input\InputInterface $input, Console\Output\OutputInterface $output): void
×
70
    {
71
        if ($input->getOption('json')) {
×
72
            $this->jsonOutput = $output;
×
73

74
            if ($output instanceof Console\Output\ConsoleOutputInterface) {
×
75
                $output = $output->getErrorOutput();
×
76
            } else {
77
                $output = new Console\Output\NullOutput();
×
78
            }
79
        }
80

81
        $this->io = new Console\Style\SymfonyStyle($input, $output);
×
82
    }
83

84
    protected function execute(Console\Input\InputInterface $input, Console\Output\OutputInterface $output): int
×
85
    {
86
        $limit = $input->getOption('limit');
×
87
        $recoverTimeout = $input->getOption('recover-timeout');
×
88
        $transport = $this->mailer->getTransport();
×
89

90
        try {
91
            // Early return if unsupported mail transport is configured
92
            if (!($transport instanceof Mail\Transport\QueueableTransport)) {
×
93
                throw new Exception\CommandFailureException(
×
94
                    sprintf(
×
95
                        'The configured mail transport "%s" is not supported. Please configure a mail spooler as mail transport.',
×
96
                        $transport::class,
×
97
                    ),
×
98
                    self::INVALID,
×
99
                );
×
100
            }
101

102
            $mailQueue = $transport->getMailQueue()->get();
×
103
            $numberOfMailsInQueue = count($mailQueue);
×
104

105
            // Early return if mail queue is empty
106
            if ($numberOfMailsInQueue === 0) {
×
107
                $message = 'No mails are currently in queue.';
×
108

109
                $this->io->success($message);
×
110
                $this->writeJsonResult([
×
111
                    'result' => $message,
×
112
                ]);
×
113

114
                return self::SUCCESS;
×
115
            }
116

117
            // Limit number of sent mails from command option or send all mails
118
            if ($limit !== null) {
×
119
                $limit = (int)$limit;
×
120
            } else {
121
                $limit = $numberOfMailsInQueue;
×
122
            }
123

124
            // Early return if invalid limit is provided
125
            if ($limit < 1) {
×
126
                throw new Exception\CommandFailureException(
×
127
                    'Limit must be a number greater than or equal to 1.',
×
128
                    self::INVALID,
×
129
                );
×
130
            }
131

132
            $this->recover($recoverTimeout, $transport);
×
133
            $this->flushQueue($limit, $mailQueue, $transport);
×
134
        } catch (Exception\CommandFailureException $exception) {
×
135
            $this->io->error($exception->getMessage());
×
136
            $this->writeJsonResult([
×
137
                'error' => $exception->getMessage(),
×
138
            ]);
×
139

140
            return $exception->statusCode;
×
141
        }
142

143
        return self::SUCCESS;
×
144
    }
145

146
    /**
147
     * @throws Exception\CommandFailureException
148
     */
149
    private function recover(?string $recoverTimeout, Mail\Transport\QueueableTransport $transport): void
×
150
    {
151
        if ($recoverTimeout !== null) {
×
152
            $recoverTimeout = (int)$recoverTimeout;
×
153

154
            // Show warning if non-recoverable transport is used with recover timeout
155
            if (!($transport instanceof Mail\Transport\RecoverableTransport)) {
×
156
                $this->io->warning('You passed --recover-timeout to a non-recoverable mail transport.');
×
157
            }
158

159
            // Early return if invalid recover timeout is provided
160
            if ($recoverTimeout < 1) {
×
161
                throw new Exception\CommandFailureException(
×
162
                    'Recover timeout must be a number greater than or equal to 1.',
×
163
                    self::INVALID,
×
164
                );
×
165
            }
166
        }
167

168
        // Recover mails that have taken too long to send
169
        if ($transport instanceof Mail\Transport\RecoverableTransport) {
×
170
            if ($recoverTimeout !== null) {
×
171
                $transport->recover($recoverTimeout);
×
172
            } else {
173
                $transport->recover();
×
174
            }
175
        }
176
    }
177

178
    /**
179
     * @param list<Mail\Queue\MailQueueItem> $mailQueueItems
180
     * @throws Exception\CommandFailureException
181
     */
182
    private function flushQueue(int $limit, array $mailQueueItems, Mail\Transport\QueueableTransport $transport): void
×
183
    {
184
        $this->io->section('Flushing mail queue');
×
185

186
        $numberOfMailsInQueue = count($mailQueueItems);
×
187
        $realTransport = $this->mailer->getRealTransport();
×
188
        $progressBar = $this->io->createProgressBar($limit);
×
189

190
        // Send mails from queue
191
        foreach ($progressBar->iterate($mailQueueItems, $limit) as $i => $item) {
×
192
            if ($i >= $limit) {
×
193
                $progressBar->finish();
×
194
                break;
×
195
            }
196

197
            try {
198
                $transport->dequeue($item, $realTransport);
×
199
            } catch (Mailer\Exception\TransportExceptionInterface $exception) {
×
200
                $this->io->newLine(2);
×
201

202
                throw new Exception\CommandFailureException($exception->getMessage(), self::FAILURE);
×
203
            }
204

205
            --$numberOfMailsInQueue;
×
206
        }
207

208
        $this->io->newLine(2);
×
209

210
        if ($numberOfMailsInQueue > 0) {
×
211
            $this->io->success(
×
212
                sprintf(
×
213
                    'Successfully sent %d mail%s, %d mail%s still enqueued.',
×
214
                    $limit,
×
215
                    $limit === 1 ? '' : 's',
×
216
                    $numberOfMailsInQueue,
×
217
                    $numberOfMailsInQueue === 1 ? ' is' : 's are',
×
218
                ),
×
219
            );
×
220
        } else {
221
            $this->io->success(
×
222
                sprintf(
×
223
                    'Successfully flushed mail queue (sent %d mail%s).',
×
224
                    $limit,
×
225
                    $limit === 1 ? '' : 's',
×
226
                ),
×
227
            );
×
228
        }
229

230
        $this->writeJsonResult([
×
231
            'sent' => $limit,
×
232
            'remaining' => $numberOfMailsInQueue,
×
233
        ]);
×
234
    }
235

236
    /**
237
     * @param array<string, mixed> $json
238
     */
239
    private function writeJsonResult(array $json): void
×
240
    {
241
        if ($this->jsonOutput === null) {
×
242
            return;
×
243
        }
244

245
        $this->jsonOutput->writeln(
×
246
            json_encode($json, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR),
×
247
        );
×
248
    }
249
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc