From 6f9f937cfaf5e708837abf1a093a4b2a709b9024 Mon Sep 17 00:00:00 2001 From: Steve Bauman Date: Sun, 18 Jan 2026 13:08:02 -0500 Subject: [PATCH 1/5] Add method option to imap:watch --- src/Commands/WatchMailbox.php | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/Commands/WatchMailbox.php b/src/Commands/WatchMailbox.php index 45a1dac..9eefa0c 100644 --- a/src/Commands/WatchMailbox.php +++ b/src/Commands/WatchMailbox.php @@ -20,7 +20,7 @@ class WatchMailbox extends Command * * @var string */ - protected $signature = 'imap:watch {mailbox} {folder?} {--with=} {--timeout=30} {--attempts=5} {--debug=false}'; + protected $signature = 'imap:watch {mailbox} {folder?} {--method=idle} {--with=} {--timeout=30} {--attempts=5} {--debug=false}'; /** * The console command description. @@ -48,11 +48,18 @@ public function handle(LoopInterface $loop): void try { $folder = $this->folder($mailbox); - $folder->idle( - new HandleMessageReceived($this, $attempts, $lastReceivedAt), - new ConfigureIdleQuery($with), - $this->option('timeout') - ); + match ($this->option('method')) { + 'idle' => $folder->idle( + new HandleMessageReceived($this, $attempts, $lastReceivedAt), + new ConfigureIdleQuery($with), + $this->option('timeout'), + ), + 'poll' => $folder->poll( + new HandleMessageReceived($this, $attempts, $lastReceivedAt), + new ConfigureIdleQuery($with), + $this->option('timeout'), + ), + }; } catch (Exception $e) { if ($this->isMessageMissing($e)) { return; From f063b84156a5605577c25cc24bb4f15bc23620bf Mon Sep 17 00:00:00 2001 From: Steve Bauman Date: Sun, 18 Jan 2026 13:11:14 -0500 Subject: [PATCH 2/5] Throw exception when given invalid method option value --- src/Commands/WatchMailbox.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Commands/WatchMailbox.php b/src/Commands/WatchMailbox.php index 9eefa0c..316531e 100644 --- a/src/Commands/WatchMailbox.php +++ b/src/Commands/WatchMailbox.php @@ -12,6 +12,7 @@ use Illuminate\Console\Command; use Illuminate\Support\Facades\Event; use Illuminate\Support\Str; +use Symfony\Component\Console\Exception\InvalidOptionException; class WatchMailbox extends Command { @@ -59,6 +60,9 @@ public function handle(LoopInterface $loop): void new ConfigureIdleQuery($with), $this->option('timeout'), ), + default => throw new InvalidOptionException( + "Invalid method [{$this->option('method')}]. Valid options are [idle, poll]." + ), }; } catch (Exception $e) { if ($this->isMessageMissing($e)) { From 047be06702f577b5b9c2cefec45cf3eb80552d1e Mon Sep 17 00:00:00 2001 From: Steve Bauman Date: Sun, 18 Jan 2026 13:20:13 -0500 Subject: [PATCH 3/5] Move validation outside of callback --- src/Commands/WatchMailbox.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Commands/WatchMailbox.php b/src/Commands/WatchMailbox.php index 316531e..b392cc7 100644 --- a/src/Commands/WatchMailbox.php +++ b/src/Commands/WatchMailbox.php @@ -7,7 +7,6 @@ use DirectoryTree\ImapEngine\Laravel\Facades\Imap; use DirectoryTree\ImapEngine\Laravel\Support\LoopInterface; use DirectoryTree\ImapEngine\MailboxInterface; -use DirectoryTree\ImapEngine\Message; use Exception; use Illuminate\Console\Command; use Illuminate\Support\Facades\Event; @@ -35,6 +34,10 @@ class WatchMailbox extends Command */ public function handle(LoopInterface $loop): void { + if (! in_array($method = $this->option('method'), ['idle', 'poll'])) { + throw new InvalidOptionException("Invalid method [{$method}]. Valid options are [idle, poll]."); + } + $mailbox = Imap::mailbox($name = $this->argument('mailbox')); $with = explode(',', $this->option('with')); @@ -60,9 +63,6 @@ public function handle(LoopInterface $loop): void new ConfigureIdleQuery($with), $this->option('timeout'), ), - default => throw new InvalidOptionException( - "Invalid method [{$this->option('method')}]. Valid options are [idle, poll]." - ), }; } catch (Exception $e) { if ($this->isMessageMissing($e)) { From 8f0e3ef70d6eca922e9909d5327cce7a8078bc71 Mon Sep 17 00:00:00 2001 From: Steve Bauman Date: Sun, 18 Jan 2026 13:20:41 -0500 Subject: [PATCH 4/5] Add tests --- tests/Commands/WatchMailboxTest.php | 54 +++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/tests/Commands/WatchMailboxTest.php b/tests/Commands/WatchMailboxTest.php index 12f3807..d7f6878 100644 --- a/tests/Commands/WatchMailboxTest.php +++ b/tests/Commands/WatchMailboxTest.php @@ -15,6 +15,7 @@ use Illuminate\Support\Facades\Event; use InvalidArgumentException; use RuntimeException; +use Symfony\Component\Console\Exception\InvalidOptionException; use function Pest\Laravel\artisan; @@ -51,6 +52,35 @@ ); }); +it('can watch mailbox using method', function (string $method) { + Config::set('imap.mailboxes.test', [ + 'host' => 'localhost', + 'port' => 993, + 'encryption' => 'ssl', + 'username' => '', + 'password' => '', + ]); + + Imap::fake('test', folders: [ + new FakeFolder('inbox', messages: [ + $message = new FakeMessage(uid: 1), + ]), + ]); + + App::bind(LoopInterface::class, LoopFake::class); + + Event::fake(); + + artisan(WatchMailbox::class, [ + 'mailbox' => 'test', + '--method' => $method, + ])->assertSuccessful(); + + Event::assertDispatched( + fn (MessageReceived $event) => $event->message->is($message) + ); +})->with(['idle', 'poll']); + it('dispatches event when failure attempts have been reached', function () { Config::set('imap.mailboxes.test', [ 'host' => 'localhost', @@ -91,3 +121,27 @@ public function idle( && $event->exception->getMessage() === 'Simulated exception'; }); }); + +it('throws exception when invalid method is provided', function () { + Config::set('imap.mailboxes.test', [ + 'host' => 'localhost', + 'port' => 993, + 'encryption' => 'ssl', + 'username' => '', + 'password' => '', + ]); + + Imap::fake('test', folders: [ + new FakeFolder('inbox'), + ]); + + App::bind(LoopInterface::class, LoopFake::class); + + artisan(WatchMailbox::class, [ + 'mailbox' => 'test', + '--method' => 'invalid', + ]); +})->throws( + InvalidOptionException::class, + 'Invalid method [invalid]. Valid options are [idle, poll].' +); From d670dc13a9b0eedbc96feb7a1b46e74bade9199e Mon Sep 17 00:00:00 2001 From: Steve Bauman Date: Sun, 18 Jan 2026 13:24:02 -0500 Subject: [PATCH 5/5] Force ImapEngine v1.19 for polling feature --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 3bf738e..7753dff 100644 --- a/composer.json +++ b/composer.json @@ -17,7 +17,7 @@ ], "require": { "php": "^8.1", - "directorytree/imapengine": "^1.13.0", + "directorytree/imapengine": "^1.19.0", "illuminate/contracts": "^10.0|^11.0|^12.0" }, "require-dev": {