From 594cd89e406e01967b301d94eaa7154ce14cc8ab Mon Sep 17 00:00:00 2001 From: barbosa89 Date: Mon, 27 Oct 2025 17:12:21 -0500 Subject: [PATCH 01/11] refactor: use Arr utility for port handling in TransportFactory --- src/Mail/TransportFactory.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Mail/TransportFactory.php b/src/Mail/TransportFactory.php index 81eefb42..2fedfa6d 100644 --- a/src/Mail/TransportFactory.php +++ b/src/Mail/TransportFactory.php @@ -7,6 +7,7 @@ use InvalidArgumentException; use Phenix\Mail\Constants\MailerType; use Phenix\Mail\Transports\LogTransport; +use Phenix\Util\Arr; use SensitiveParameter; use Symfony\Component\Mailer\Bridge\Amazon\Transport\SesSmtpTransport; use Symfony\Component\Mailer\Bridge\Resend\Transport\ResendApiTransport; @@ -34,7 +35,7 @@ private static function createSmtpTransport(#[SensitiveParameter] array $config) $scheme = 'smtp'; if (! empty($config['encryption']) && $config['encryption'] === 'tls') { - $scheme = ($config['port'] === 465) ? 'smtps' : 'smtp'; + $scheme = (Arr::get($config, 'port') === 465) ? 'smtps' : 'smtp'; } $dsn = new Dsn( @@ -42,7 +43,7 @@ private static function createSmtpTransport(#[SensitiveParameter] array $config) $config['host'], $config['username'] ?? null, $config['password'] ?? null, - $config['port'] ?? null, + Arr::has($config, 'port') ? (int) Arr::get($config, 'port') : null, $config ); From d4fd29b60f4b7543fd2c3ee0c29692b48bd6e680 Mon Sep 17 00:00:00 2001 From: barbosa89 Date: Mon, 27 Oct 2025 17:18:24 -0500 Subject: [PATCH 02/11] fix: clear View cache during TestCase teardown --- src/Testing/TestCase.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Testing/TestCase.php b/src/Testing/TestCase.php index b8f2a99f..cf67704e 100644 --- a/src/Testing/TestCase.php +++ b/src/Testing/TestCase.php @@ -12,6 +12,7 @@ use Phenix\Facades\Event; use Phenix\Facades\Mail; use Phenix\Facades\Queue; +use Phenix\Facades\View; use Phenix\Testing\Concerns\InteractWithResponses; use Phenix\Testing\Concerns\RefreshDatabase; use Symfony\Component\Console\Tester\CommandTester; @@ -50,6 +51,7 @@ protected function tearDown(): void Event::resetFaking(); Queue::resetFaking(); Mail::resetSendingLog(); + View::clearCache(); $this->app = null; } From 7f9cb4fa87022d20e294df80b797d415ee9c009b Mon Sep 17 00:00:00 2001 From: barbosa89 Date: Tue, 28 Oct 2025 07:47:32 -0500 Subject: [PATCH 03/11] refactor: move View cache clearing to setUp method --- src/Testing/TestCase.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Testing/TestCase.php b/src/Testing/TestCase.php index cf67704e..547f67d0 100644 --- a/src/Testing/TestCase.php +++ b/src/Testing/TestCase.php @@ -42,6 +42,8 @@ protected function setUp(): void if (in_array(RefreshDatabase::class, $uses, true) && method_exists($this, 'refreshDatabase')) { $this->refreshDatabase(); } + + View::clearCache(); } protected function tearDown(): void @@ -51,7 +53,6 @@ protected function tearDown(): void Event::resetFaking(); Queue::resetFaking(); Mail::resetSendingLog(); - View::clearCache(); $this->app = null; } From 54ca9407a8b6eba5a929643bf28332b52aeced3a Mon Sep 17 00:00:00 2001 From: barbosa89 Date: Tue, 28 Oct 2025 07:47:42 -0500 Subject: [PATCH 04/11] fix: ensure .env file is loaded correctly in AppBuilder and use Str utility for path handling in Environment --- src/AppBuilder.php | 2 +- src/Runtime/Environment.php | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/AppBuilder.php b/src/AppBuilder.php index e7b6817a..c67310ed 100644 --- a/src/AppBuilder.php +++ b/src/AppBuilder.php @@ -13,7 +13,7 @@ public static function build(string|null $path = null, string|null $env = null): { $app = new App($path ?? dirname(__DIR__)); - Environment::load($env); + Environment::load('.env', $env); putenv('PHENIX_BASE_PATH=' . base_path()); $_ENV['PHENIX_BASE_PATH'] = base_path(); diff --git a/src/Runtime/Environment.php b/src/Runtime/Environment.php index 798a207b..c51b8978 100644 --- a/src/Runtime/Environment.php +++ b/src/Runtime/Environment.php @@ -5,6 +5,7 @@ namespace Phenix\Runtime; use Dotenv\Dotenv; +use Phenix\Util\Str; class Environment { @@ -12,7 +13,7 @@ public static function load(string|null $fileName = null, string|null $environme { $fileName ??= '.env'; $fileName .= $environment ? ".{$environment}" : ''; - $fileNamePath = base_path() . DIRECTORY_SEPARATOR . $fileName; + $fileNamePath = Str::finish(base_path(), DIRECTORY_SEPARATOR) . $fileName; if (file_exists($fileNamePath)) { Dotenv::createImmutable(base_path(), $fileName)->load(); From 95ac22476c2d96a5bf8bb071775dcca72f5c8d3c Mon Sep 17 00:00:00 2001 From: barbosa89 Date: Tue, 28 Oct 2025 17:58:08 -0500 Subject: [PATCH 05/11] refactor: update Mailer and MailManager to return Future from send method --- src/Facades/Mail.php | 11 ++++--- src/Mail/Contracts/Mailer.php | 3 +- src/Mail/MailManager.php | 5 +-- src/Mail/Mailer.php | 22 +++++++------ src/Tasks/AbstractWorker.php | 2 +- src/Tasks/Worker.php | 2 +- src/Tasks/WorkerPool.php | 2 +- src/Testing/TestMail.php | 9 +++-- tests/Unit/Mail/MailTest.php | 62 +++++++++++++++++++---------------- 9 files changed, 63 insertions(+), 55 deletions(-) diff --git a/src/Facades/Mail.php b/src/Facades/Mail.php index 7387ee98..acc37251 100644 --- a/src/Facades/Mail.php +++ b/src/Facades/Mail.php @@ -4,18 +4,19 @@ namespace Phenix\Facades; -use Phenix\Mail\Constants\MailerType; -use Phenix\Mail\Contracts\Mailable as MailableContract; -use Phenix\Mail\Contracts\Mailer; -use Phenix\Mail\MailManager; +use Amp\Future; use Phenix\Runtime\Facade; +use Phenix\Mail\MailManager; use Phenix\Testing\TestMail; +use Phenix\Mail\Contracts\Mailer; +use Phenix\Mail\Constants\MailerType; +use Phenix\Mail\Contracts\Mailable as MailableContract; /** * @method static Mailer mailer(MailerType|null $mailerType = null) * @method static Mailer using(MailerType $mailerType) * @method static Mailer to(array|string $to) - * @method static void send(MailableContract $mailable) + * @method static Future send(MailableContract $mailable) * @method static Mailer fake(MailerType|null $mailerType = null) * @method static array getSendingLog(MailerType|null $mailerType = null) * @method static void resetSendingLog(MailerType|null $mailerType = null) diff --git a/src/Mail/Contracts/Mailer.php b/src/Mail/Contracts/Mailer.php index cdb97402..c6608bd5 100644 --- a/src/Mail/Contracts/Mailer.php +++ b/src/Mail/Contracts/Mailer.php @@ -4,6 +4,7 @@ namespace Phenix\Mail\Contracts; +use Amp\Future; use Phenix\Mail\Mailable; interface Mailer @@ -14,7 +15,7 @@ public function cc(array|string $cc): self; public function bcc(array|string $bcc): self; - public function send(Mailable $mailable): void; + public function send(Mailable $mailable): Future; public function getSendingLog(): array; diff --git a/src/Mail/MailManager.php b/src/Mail/MailManager.php index 7ff99453..8665deb8 100644 --- a/src/Mail/MailManager.php +++ b/src/Mail/MailManager.php @@ -4,6 +4,7 @@ namespace Phenix\Mail; +use Amp\Future; use Phenix\Mail\Constants\MailerType; use Phenix\Mail\Contracts\Mailer as MailerContract; use Phenix\Mail\Mailers\Resend; @@ -44,9 +45,9 @@ public function to(array|string $to): MailerContract return $this->mailer()->to($to); } - public function send(Mailable $mailable): void + public function send(Mailable $mailable): Future { - $this->mailer()->send($mailable); + return $this->mailer()->send($mailable); } public function fake(MailerType|null $mailerType = null): void diff --git a/src/Mail/Mailer.php b/src/Mail/Mailer.php index 0a0ffcf2..f96c0b76 100644 --- a/src/Mail/Mailer.php +++ b/src/Mail/Mailer.php @@ -4,13 +4,15 @@ namespace Phenix\Mail; -use Phenix\Mail\Contracts\Mailable; -use Phenix\Mail\Contracts\Mailer as MailerContract; -use Phenix\Mail\Tasks\SendEmail; +use Amp\Future; +use Phenix\Tasks\WorkerPool; +use SensitiveParameter; use Phenix\Tasks\Result; use Phenix\Tasks\Worker; -use SensitiveParameter; +use Phenix\Mail\Tasks\SendEmail; +use Phenix\Mail\Contracts\Mailable; use Symfony\Component\Mime\Address; +use Phenix\Mail\Contracts\Mailer as MailerContract; abstract class Mailer implements MailerContract { @@ -57,7 +59,7 @@ public function bcc(array|string $bcc): self return $this; } - public function send(Mailable $mailable): void + public function send(Mailable $mailable): Future { $mailable->from($this->from) ->to($this->to) @@ -67,22 +69,22 @@ public function send(Mailable $mailable): void $email = $mailable->toMail(); - /** @var Result $result */ - [$result] = Worker::batch([ + $execution = (new WorkerPool())->submitTask( new SendEmail( $email, $this->config, $this->serviceConfig, - ), - ]); + ) + ); if ($this->config['transport'] === 'log') { $this->sendingLog[] = [ 'mailable' => $mailable::class, 'email' => $email, - 'success' => $result->isSuccess(), ]; } + + return $execution->getFuture(); } public function getSendingLog(): array diff --git a/src/Tasks/AbstractWorker.php b/src/Tasks/AbstractWorker.php index 2886026b..bc62cb78 100644 --- a/src/Tasks/AbstractWorker.php +++ b/src/Tasks/AbstractWorker.php @@ -56,7 +56,7 @@ public function run(): array )); } - abstract protected function submitTask(Task $parallelTask): Worker\Execution; + abstract public function submitTask(Task $parallelTask): Worker\Execution; protected function finalize(): void { diff --git a/src/Tasks/Worker.php b/src/Tasks/Worker.php index 9b2f32c7..9b928969 100644 --- a/src/Tasks/Worker.php +++ b/src/Tasks/Worker.php @@ -19,7 +19,7 @@ public function __construct() $this->worker = Workers\createWorker(); } - protected function submitTask(Task $parallelTask): Workers\Execution + public function submitTask(Task $parallelTask): Workers\Execution { $timeout = new TimeoutCancellation($parallelTask->getTimeout()); diff --git a/src/Tasks/WorkerPool.php b/src/Tasks/WorkerPool.php index 3bbf6ff1..f5b0dbf6 100644 --- a/src/Tasks/WorkerPool.php +++ b/src/Tasks/WorkerPool.php @@ -12,7 +12,7 @@ class WorkerPool extends AbstractWorker { - protected function submitTask(Task $parallelTask): Worker\Execution + public function submitTask(Task $parallelTask): Worker\Execution { /** @var Pool $pool */ $pool = App::make(Pool::class); diff --git a/src/Testing/TestMail.php b/src/Testing/TestMail.php index d8cf98f5..e8f207d0 100644 --- a/src/Testing/TestMail.php +++ b/src/Testing/TestMail.php @@ -7,6 +7,7 @@ use Closure; use Phenix\Data\Collection; use Phenix\Mail\Contracts\Mailable; +use Phenix\Util\Arr; use PHPUnit\Framework\Assert; class TestMail @@ -32,7 +33,7 @@ public function toBeSent(Closure|null $closure = null): void $matches = $this->filterByMailable($this->mailable); if ($closure) { - Assert::assertTrue($closure($matches->first())); + Assert::assertTrue($closure($matches)); } else { Assert::assertNotEmpty($matches, "Failed asserting that mailable '{$this->mailable}' was sent at least once."); } @@ -43,10 +44,8 @@ public function toNotBeSent(Closure|null $closure = null): void $matches = $this->filterByMailable($this->mailable); if ($closure) { - Assert::assertFalse($closure($matches->first())); + Assert::assertTrue($closure($matches)); } else { - $matches = $matches->filter(fn (array $item): bool => $item['success'] === false); - Assert::assertEmpty($matches, "Failed asserting that mailable '{$this->mailable}' was NOT sent."); } } @@ -65,7 +64,7 @@ private function filterByMailable(string $mailable): Collection $filtered = []; foreach ($this->log as $record) { - if (($record['mailable'] ?? null) === $mailable) { + if (Arr::get($record, 'mailable') === $mailable) { $filtered[] = $record; } } diff --git a/tests/Unit/Mail/MailTest.php b/tests/Unit/Mail/MailTest.php index c1e65b4e..2ab70c04 100644 --- a/tests/Unit/Mail/MailTest.php +++ b/tests/Unit/Mail/MailTest.php @@ -2,6 +2,7 @@ declare(strict_types=1); +use Phenix\Data\Collection; use Phenix\Facades\Config; use Phenix\Facades\Mail; use Phenix\Mail\Constants\MailerType; @@ -13,6 +14,7 @@ use Phenix\Mail\TransportFactory; use Phenix\Mail\Transports\LogTransport; use Phenix\Tasks\Result; +use Phenix\Util\Arr; use Symfony\Component\Mailer\Bridge\Amazon\Transport\SesSmtpTransport; use Symfony\Component\Mailer\Bridge\Resend\Transport\ResendApiTransport; use Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport; @@ -140,19 +142,22 @@ public function build(): self } }; + $missingMailable = new class () extends Mailable { + public function build(): self + { + return $this->view('emails.welcome') + ->subject('It will not be sent'); + } + }; + Mail::to($email)->send($mailable); Mail::expect($mailable)->toBeSent(); - - Mail::expect($mailable)->toBeSent(function (array $matches): bool { - return $matches['success'] === true; - }); - + Mail::expect($mailable)->toBeSent(fn (Collection $matches): bool => Arr::get($matches->first(), 'mailable') === $mailable::class); Mail::expect($mailable)->toBeSentTimes(1); - Mail::expect($mailable)->toNotBeSent(); - Mail::expect($mailable)->toNotBeSent(function (array $matches): bool { - return $matches['success'] === false; - }); + + Mail::expect($missingMailable)->toNotBeSent(); + Mail::expect($missingMailable)->toNotBeSent(fn (Collection $matches): bool => $matches->isEmpty()); Mail::resetSendingLog(); @@ -184,15 +189,8 @@ public function build(): self Mail::to($email)->send($mailable); Mail::expect($mailable)->toBeSent(); - - Mail::expect($mailable)->toBeSent(function (array $matches): bool { - return $matches['success'] === true; - }); - + Mail::expect($mailable)->toBeSent(fn (Collection $matches): bool => Arr::get($matches->first(), 'mailable') === $mailable::class); Mail::expect($mailable)->toBeSentTimes(1); - Mail::expect($mailable)->toNotBeSent(function (array $matches): bool { - return $matches['success'] === false; - }); }); it('send email successfully using smtp mailer with sender defined in mailable', function (): void { @@ -246,8 +244,9 @@ public function build(): self Mail::to($email)->send($mailable); - Mail::expect($mailable)->toBeSent(function (array $matches): bool { - $email = $matches['email'] ?? null; + Mail::expect($mailable)->toBeSent(function (Collection $matches): bool { + $firstMatch = $matches->first(); + $email = $firstMatch['email'] ?? null; if (! $email) { return false; @@ -287,8 +286,9 @@ public function build(): self ->cc($cc) ->send($mailable); - Mail::expect($mailable)->toBeSent(function (array $matches) use ($cc): bool { - $email = $matches['email'] ?? null; + Mail::expect($mailable)->toBeSent(function (Collection $matches) use ($cc): bool { + $firstMatch = $matches->first(); + $email = $firstMatch['email'] ?? null; if (! $email) { return false; @@ -329,8 +329,9 @@ public function build(): self ->bcc($bcc) ->send($mailable); - Mail::expect($mailable)->toBeSent(function (array $matches) use ($bcc): bool { - $email = $matches['email'] ?? null; + Mail::expect($mailable)->toBeSent(function (Collection $matches) use ($bcc): bool { + $firstMatch = $matches->first(); + $email = $firstMatch['email'] ?? null; if (! $email) { return false; @@ -370,8 +371,9 @@ public function build(): self Mail::to($to) ->send($mailable); - Mail::expect($mailable)->toBeSent(function (array $matches): bool { - $email = $matches['email'] ?? null; + Mail::expect($mailable)->toBeSent(function (Collection $matches): bool { + $firstMatch = $matches->first(); + $email = $firstMatch['email'] ?? null; if (! $email) { return false; @@ -415,8 +417,9 @@ public function build(): self Mail::to($to)->send($mailable); - Mail::expect($mailable)->toBeSent(function (array $matches): bool { - $email = $matches['email'] ?? null; + Mail::expect($mailable)->toBeSent(function (Collection $matches): bool { + $firstMatch = $matches->first(); + $email = $firstMatch['email'] ?? null; if (! $email) { return false; } @@ -535,8 +538,9 @@ public function build(): self Mail::to($to)->send($mailable); - Mail::expect($mailable)->toBeSent(function (array $matches): bool { - $email = $matches['email'] ?? null; + Mail::expect($mailable)->toBeSent(function (Collection $matches): bool { + $firstMatch = $matches->first(); + $email = $firstMatch['email'] ?? null; if (! $email) { return false; From f2b9095c986e41b5f45f188e38739c2af5e18544 Mon Sep 17 00:00:00 2001 From: barbosa89 Date: Wed, 29 Oct 2025 12:18:41 -0500 Subject: [PATCH 06/11] refactor: replace Worker::batch with Worker::awaitAll for improved task handling --- src/Console/Commands/ViewCache.php | 2 +- src/Crypto/Crypto.php | 29 ++++++++++++++--------------- src/Crypto/Hash.php | 19 +++++++++---------- src/Tasks/AbstractWorker.php | 10 +++++----- src/Tasks/Contracts/Worker.php | 4 ++-- src/Tasks/Task.php | 7 +------ src/Tasks/Worker.php | 2 +- src/Tasks/WorkerPool.php | 20 +++++++++++++------- 8 files changed, 46 insertions(+), 47 deletions(-) diff --git a/src/Console/Commands/ViewCache.php b/src/Console/Commands/ViewCache.php index 1b258de2..e912784e 100644 --- a/src/Console/Commands/ViewCache.php +++ b/src/Console/Commands/ViewCache.php @@ -45,7 +45,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $this->compile(Config::get('view.path')); - WorkerPool::batch($this->tasks); + WorkerPool::awaitAll($this->tasks); $output->writeln('All views were compiled successfully!.'); diff --git a/src/Crypto/Crypto.php b/src/Crypto/Crypto.php index e198e916..2a550c80 100644 --- a/src/Crypto/Crypto.php +++ b/src/Crypto/Crypto.php @@ -11,7 +11,6 @@ use Phenix\Crypto\Tasks\Decrypt; use Phenix\Crypto\Tasks\Encrypt; use Phenix\Tasks\Result; -use Phenix\Tasks\Worker; use SensitiveParameter; class Crypto implements CipherContract, StringCipher @@ -26,14 +25,14 @@ public function __construct( public function encrypt(#[SensitiveParameter] object|array|string $value, bool $serialize = true): string { + $task = new Encrypt( + key: $this->key, + value: $value, + serialize: $serialize + ); + /** @var Result $result */ - [$result] = Worker::batch([ - new Encrypt( - key: $this->key, - value: $value, - serialize: $serialize - ), - ]); + $result = $task->output(); if ($result->isFailure()) { throw new EncryptException($result->message()); @@ -49,14 +48,14 @@ public function encryptString(#[SensitiveParameter] string $value): string public function decrypt(string $payload, bool $unserialize = true): object|array|string { + $task = new Decrypt( + key: $this->key, + value: $payload, + unserialize: $unserialize + ); + /** @var Result $result */ - [$result] = Worker::batch([ - new Decrypt( - key: $this->key, - value: $payload, - unserialize: $unserialize - ), - ]); + $result = $task->output(); if ($result->isFailure()) { throw new DecryptException($result->message()); diff --git a/src/Crypto/Hash.php b/src/Crypto/Hash.php index d7c17aaf..d8c03cdf 100644 --- a/src/Crypto/Hash.php +++ b/src/Crypto/Hash.php @@ -9,37 +9,36 @@ use Phenix\Crypto\Tasks\GeneratePasswordHash; use Phenix\Crypto\Tasks\VerifyPasswordHash; use Phenix\Tasks\Result; -use Phenix\Tasks\Worker; use SensitiveParameter; class Hash implements HasherContract { public function make(#[SensitiveParameter] string $password): string { + $task = new GeneratePasswordHash($password); + /** @var Result $result */ - [$result] = Worker::batch([ - new GeneratePasswordHash($password), - ]); + $result = $task->output(); return $result->output(); } public function verify(string $hash, #[SensitiveParameter] string $password): bool { + $task = new VerifyPasswordHash($hash, $password); + /** @var Result $result */ - [$result] = Worker::batch([ - new VerifyPasswordHash($hash, $password), - ]); + $result = $task->output(); return $result->output(); } public function needsRehash(string $hash): bool { + $task = new CheckNeedsRehash($hash); + /** @var Result $result */ - [$result] = Worker::batch([ - new CheckNeedsRehash($hash), - ]); + $result = $task->output(); return $result->output(); } diff --git a/src/Tasks/AbstractWorker.php b/src/Tasks/AbstractWorker.php index bc62cb78..0a9c667d 100644 --- a/src/Tasks/AbstractWorker.php +++ b/src/Tasks/AbstractWorker.php @@ -26,12 +26,12 @@ public function __construct() * @param array $tasks * @return array */ - public static function batch(array $tasks): array + public static function awaitAll(array $tasks): array { $pool = new static(); foreach ($tasks as $task) { - $pool->submit($task); + $pool->push($task); } $results = $pool->run(); @@ -41,9 +41,9 @@ public static function batch(array $tasks): array return $results; } - public function submit(Task $parallelTask): self + public function push(Task $parallelTask): self { - $this->tasks[] = $this->submitTask($parallelTask); + $this->tasks[] = $this->prepareTask($parallelTask); return $this; } @@ -56,7 +56,7 @@ public function run(): array )); } - abstract public function submitTask(Task $parallelTask): Worker\Execution; + abstract public function prepareTask(Task $parallelTask): Worker\Execution; protected function finalize(): void { diff --git a/src/Tasks/Contracts/Worker.php b/src/Tasks/Contracts/Worker.php index ed332ab2..30818ee6 100644 --- a/src/Tasks/Contracts/Worker.php +++ b/src/Tasks/Contracts/Worker.php @@ -6,7 +6,7 @@ interface Worker { - public function submit(Task $parallelTask): self; + public function push(Task $parallelTask): self; public function run(): array; @@ -14,5 +14,5 @@ public function run(): array; * @param Task[] $tasks * @return array */ - public static function batch(array $tasks): array; + public static function awaitAll(array $tasks): array; } diff --git a/src/Tasks/Task.php b/src/Tasks/Task.php index 2c0d1094..727607bf 100644 --- a/src/Tasks/Task.php +++ b/src/Tasks/Task.php @@ -53,12 +53,7 @@ public function run(Channel $channel, Cancellation $cancellation): mixed public function output(): Result { - /** @var Result $result */ - [$result] = Worker::batch([ - $this, - ]); - - return $result; + return WorkerPool::submit($this)->await(); } public function setTimeout(int $timeout): void diff --git a/src/Tasks/Worker.php b/src/Tasks/Worker.php index 9b928969..8052d2d8 100644 --- a/src/Tasks/Worker.php +++ b/src/Tasks/Worker.php @@ -19,7 +19,7 @@ public function __construct() $this->worker = Workers\createWorker(); } - public function submitTask(Task $parallelTask): Workers\Execution + public function prepareTask(Task $parallelTask): Workers\Execution { $timeout = new TimeoutCancellation($parallelTask->getTimeout()); diff --git a/src/Tasks/WorkerPool.php b/src/Tasks/WorkerPool.php index f5b0dbf6..74d80440 100644 --- a/src/Tasks/WorkerPool.php +++ b/src/Tasks/WorkerPool.php @@ -4,21 +4,27 @@ namespace Phenix\Tasks; -use Amp\Parallel\Worker; -use Amp\Parallel\Worker\WorkerPool as Pool; +use Amp\Future; +use Amp\Parallel\Worker\Execution; use Amp\TimeoutCancellation; -use Phenix\App; +use Phenix\Facades\Worker; use Phenix\Tasks\Contracts\Task; class WorkerPool extends AbstractWorker { - public function submitTask(Task $parallelTask): Worker\Execution + public function prepareTask(Task $parallelTask): Execution { - /** @var Pool $pool */ - $pool = App::make(Pool::class); + $timeout = new TimeoutCancellation($parallelTask->getTimeout()); + + return Worker::submit($parallelTask, $timeout); + } + public static function submit(Task $parallelTask): Future + { $timeout = new TimeoutCancellation($parallelTask->getTimeout()); - return $pool->submit($parallelTask, $timeout); + $execution = Worker::submit($parallelTask, $timeout); + + return $execution->getFuture(); } } From beaf189ac9882db360f7cf94e71982baf5d5b6d6 Mon Sep 17 00:00:00 2001 From: barbosa89 Date: Wed, 29 Oct 2025 12:20:10 -0500 Subject: [PATCH 07/11] refactor: streamline Mailer send method by using WorkerPool::submit for task execution --- src/Facades/Mail.php | 8 ++--- src/Mail/Mailer.php | 12 +++---- tests/Unit/Mail/MailTest.php | 63 ++++++++++++++++++++++++++++++------ 3 files changed, 63 insertions(+), 20 deletions(-) diff --git a/src/Facades/Mail.php b/src/Facades/Mail.php index acc37251..d486ee61 100644 --- a/src/Facades/Mail.php +++ b/src/Facades/Mail.php @@ -5,12 +5,12 @@ namespace Phenix\Facades; use Amp\Future; -use Phenix\Runtime\Facade; -use Phenix\Mail\MailManager; -use Phenix\Testing\TestMail; -use Phenix\Mail\Contracts\Mailer; use Phenix\Mail\Constants\MailerType; use Phenix\Mail\Contracts\Mailable as MailableContract; +use Phenix\Mail\Contracts\Mailer; +use Phenix\Mail\MailManager; +use Phenix\Runtime\Facade; +use Phenix\Testing\TestMail; /** * @method static Mailer mailer(MailerType|null $mailerType = null) diff --git a/src/Mail/Mailer.php b/src/Mail/Mailer.php index f96c0b76..0bd1bfd4 100644 --- a/src/Mail/Mailer.php +++ b/src/Mail/Mailer.php @@ -5,14 +5,12 @@ namespace Phenix\Mail; use Amp\Future; +use Phenix\Mail\Contracts\Mailable; +use Phenix\Mail\Contracts\Mailer as MailerContract; +use Phenix\Mail\Tasks\SendEmail; use Phenix\Tasks\WorkerPool; use SensitiveParameter; -use Phenix\Tasks\Result; -use Phenix\Tasks\Worker; -use Phenix\Mail\Tasks\SendEmail; -use Phenix\Mail\Contracts\Mailable; use Symfony\Component\Mime\Address; -use Phenix\Mail\Contracts\Mailer as MailerContract; abstract class Mailer implements MailerContract { @@ -69,7 +67,7 @@ public function send(Mailable $mailable): Future $email = $mailable->toMail(); - $execution = (new WorkerPool())->submitTask( + $future = WorkerPool::submit( new SendEmail( $email, $this->config, @@ -84,7 +82,7 @@ public function send(Mailable $mailable): Future ]; } - return $execution->getFuture(); + return $future; } public function getSendingLog(): array diff --git a/tests/Unit/Mail/MailTest.php b/tests/Unit/Mail/MailTest.php index 2ab70c04..ff820ccd 100644 --- a/tests/Unit/Mail/MailTest.php +++ b/tests/Unit/Mail/MailTest.php @@ -150,7 +150,12 @@ public function build(): self } }; - Mail::to($email)->send($mailable); + $future = Mail::to($email)->send($mailable); + + /** @var Result $result */ + $result = $future->await(); + + expect($result->isSuccess())->toBeTrue(); Mail::expect($mailable)->toBeSent(); Mail::expect($mailable)->toBeSent(fn (Collection $matches): bool => Arr::get($matches->first(), 'mailable') === $mailable::class); @@ -186,7 +191,12 @@ public function build(): self } }; - Mail::to($email)->send($mailable); + $future = Mail::to($email)->send($mailable); + + /** @var Result $result */ + $result = $future->await(); + + expect($result->isSuccess())->toBeTrue(); Mail::expect($mailable)->toBeSent(); Mail::expect($mailable)->toBeSent(fn (Collection $matches): bool => Arr::get($matches->first(), 'mailable') === $mailable::class); @@ -214,7 +224,12 @@ public function build(): self } }; - Mail::send($mailable); + $future = Mail::send($mailable); + + /** @var Result $result */ + $result = $future->await(); + + expect($result->isSuccess())->toBeTrue(); Mail::expect($mailable)->toBeSent(); }); @@ -242,7 +257,12 @@ public function build(): self } }; - Mail::to($email)->send($mailable); + $future = Mail::to($email)->send($mailable); + + /** @var Result $result */ + $result = $future->await(); + + expect($result->isSuccess())->toBeTrue(); Mail::expect($mailable)->toBeSent(function (Collection $matches): bool { $firstMatch = $matches->first(); @@ -282,10 +302,15 @@ public function build(): self } }; - Mail::to($to) + $future = Mail::to($to) ->cc($cc) ->send($mailable); + /** @var Result $result */ + $result = $future->await(); + + expect($result->isSuccess())->toBeTrue(); + Mail::expect($mailable)->toBeSent(function (Collection $matches) use ($cc): bool { $firstMatch = $matches->first(); $email = $firstMatch['email'] ?? null; @@ -325,10 +350,15 @@ public function build(): self } }; - Mail::to($to) + $future = Mail::to($to) ->bcc($bcc) ->send($mailable); + /** @var Result $result */ + $result = $future->await(); + + expect($result->isSuccess())->toBeTrue(); + Mail::expect($mailable)->toBeSent(function (Collection $matches) use ($bcc): bool { $firstMatch = $matches->first(); $email = $firstMatch['email'] ?? null; @@ -368,9 +398,14 @@ public function build(): self } }; - Mail::to($to) + $future = Mail::to($to) ->send($mailable); + /** @var Result $result */ + $result = $future->await(); + + expect($result->isSuccess())->toBeTrue(); + Mail::expect($mailable)->toBeSent(function (Collection $matches): bool { $firstMatch = $matches->first(); $email = $firstMatch['email'] ?? null; @@ -415,7 +450,12 @@ public function build(): self } }; - Mail::to($to)->send($mailable); + $future = Mail::to($to)->send($mailable); + + /** @var Result $result */ + $result = $future->await(); + + expect($result->isSuccess())->toBeTrue(); Mail::expect($mailable)->toBeSent(function (Collection $matches): bool { $firstMatch = $matches->first(); @@ -462,7 +502,12 @@ public function build(): self } }; - Mail::to($to)->send($mailable); + $future = Mail::to($to)->send($mailable); + + /** @var Result $result */ + $result = $future->await(); + + expect($result->isSuccess())->toBeFalse(); Mail::expect($mailable)->toNotBeSent(); })->throws(InvalidArgumentException::class); From 906be24de97bff9c3acefe3f3ffc1cac0cac8cf8 Mon Sep 17 00:00:00 2001 From: barbosa89 Date: Wed, 29 Oct 2025 12:20:23 -0500 Subject: [PATCH 08/11] feat: add Worker facade for managing parallel tasks in the framework --- src/Facades/Worker.php | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 src/Facades/Worker.php diff --git a/src/Facades/Worker.php b/src/Facades/Worker.php new file mode 100644 index 00000000..84b61d04 --- /dev/null +++ b/src/Facades/Worker.php @@ -0,0 +1,35 @@ + Date: Wed, 29 Oct 2025 12:20:40 -0500 Subject: [PATCH 09/11] refactor: remove unnecessary clear and stop calls in ParallelQueueTest --- tests/Unit/Queue/ParallelQueueTest.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tests/Unit/Queue/ParallelQueueTest.php b/tests/Unit/Queue/ParallelQueueTest.php index 5f492347..b4f75bd1 100644 --- a/tests/Unit/Queue/ParallelQueueTest.php +++ b/tests/Unit/Queue/ParallelQueueTest.php @@ -251,9 +251,6 @@ // Processor should still be running expect($parallelQueue->isProcessing())->ToBeTrue(); - - $parallelQueue->clear(); - $parallelQueue->stop(); }); it('automatically disables processing when no tasks are available to reserve', function (): void { @@ -357,8 +354,6 @@ // All tasks should eventually be processed or re-enqueued appropriately $this->assertGreaterThanOrEqual(0, $parallelQueue->size()); - - $parallelQueue->clear(); }); it('handles concurrent task reservation attempts correctly', function (): void { From 7e564e9e06109f9d486f62d47454d33609280ad3 Mon Sep 17 00:00:00 2001 From: barbosa89 Date: Wed, 29 Oct 2025 14:08:23 -0500 Subject: [PATCH 10/11] refactor: change prepareTask method visibility to protected in Worker and WorkerPool classes --- src/Tasks/AbstractWorker.php | 2 +- src/Tasks/Worker.php | 2 +- src/Tasks/WorkerPool.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Tasks/AbstractWorker.php b/src/Tasks/AbstractWorker.php index 0a9c667d..3aeb0c1f 100644 --- a/src/Tasks/AbstractWorker.php +++ b/src/Tasks/AbstractWorker.php @@ -56,7 +56,7 @@ public function run(): array )); } - abstract public function prepareTask(Task $parallelTask): Worker\Execution; + abstract protected function prepareTask(Task $parallelTask): Worker\Execution; protected function finalize(): void { diff --git a/src/Tasks/Worker.php b/src/Tasks/Worker.php index 8052d2d8..1af6b8e9 100644 --- a/src/Tasks/Worker.php +++ b/src/Tasks/Worker.php @@ -19,7 +19,7 @@ public function __construct() $this->worker = Workers\createWorker(); } - public function prepareTask(Task $parallelTask): Workers\Execution + protected function prepareTask(Task $parallelTask): Workers\Execution { $timeout = new TimeoutCancellation($parallelTask->getTimeout()); diff --git a/src/Tasks/WorkerPool.php b/src/Tasks/WorkerPool.php index 74d80440..7eb6365c 100644 --- a/src/Tasks/WorkerPool.php +++ b/src/Tasks/WorkerPool.php @@ -12,7 +12,7 @@ class WorkerPool extends AbstractWorker { - public function prepareTask(Task $parallelTask): Execution + protected function prepareTask(Task $parallelTask): Execution { $timeout = new TimeoutCancellation($parallelTask->getTimeout()); From d61bec5f76ca82863b6526bdd148fafe653bc942 Mon Sep 17 00:00:00 2001 From: barbosa89 Date: Wed, 29 Oct 2025 14:19:42 -0500 Subject: [PATCH 11/11] test: add unit test for running task from standalone worker --- tests/Unit/Tasks/WorkerTest.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 tests/Unit/Tasks/WorkerTest.php diff --git a/tests/Unit/Tasks/WorkerTest.php b/tests/Unit/Tasks/WorkerTest.php new file mode 100644 index 00000000..0c4f91f2 --- /dev/null +++ b/tests/Unit/Tasks/WorkerTest.php @@ -0,0 +1,17 @@ +push($task)->run(); + + expect($result->isSuccess())->toBeTrue(); + expect($result->output())->toBe('Task completed successfully'); +});