diff --git a/src/Queue/Task/EmailTask.php b/src/Queue/Task/EmailTask.php index 2a4c4b25..900b0c95 100644 --- a/src/Queue/Task/EmailTask.php +++ b/src/Queue/Task/EmailTask.php @@ -163,6 +163,17 @@ public function run(array $data, int $jobId): void { $this->mailer = $this->getMailer(); $settings = $data['settings'] + $this->defaults; + + foreach (['to', 'from', 'cc', 'bcc', 'replyTo', 'sender', 'returnPath'] as $addressMethod) { + if (!array_key_exists($addressMethod, $settings)) { + continue; + } + + $setter = 'set' . ucfirst($addressMethod); + $this->mailer->{$setter}(...$this->addressArguments($settings[$addressMethod])); + unset($settings[$addressMethod]); + } + foreach ($settings as $method => $setting) { $setter = 'set' . ucfirst($method); if (in_array($method, ['theme', 'template', 'layout'], true)) { @@ -206,6 +217,26 @@ public function run(array $data, int $jobId): void { $this->mailer->deliver((string)$message); } + /** + * Normalizes an address setting into positional arguments for setTo/setFrom/etc. + * + * List-shaped arrays are unpacked into positional arguments (matching the + * historical `call_user_func_array` behavior), while associative `email => name` + * maps are passed as a single positional argument so PHP 8 does not interpret + * their string keys as named parameters. + * + * @param mixed $setting + * + * @return array + */ + protected function addressArguments(mixed $setting): array { + if (is_array($setting) && $setting !== [] && array_is_list($setting)) { + return $setting; + } + + return [$setting]; + } + /** * Check if Mail class exists and create instance * diff --git a/tests/TestCase/Model/Table/QueuedJobsTableTest.php b/tests/TestCase/Model/Table/QueuedJobsTableTest.php index fbdddb55..25b43d18 100644 --- a/tests/TestCase/Model/Table/QueuedJobsTableTest.php +++ b/tests/TestCase/Model/Table/QueuedJobsTableTest.php @@ -250,7 +250,7 @@ public function testCreateWithObject() { 'some' => 'random', 'test' => 'data', ]; - $dto = new class() { + $dto = new class () { /** * @return string[] */ diff --git a/tests/TestCase/Queue/Task/EmailTaskTest.php b/tests/TestCase/Queue/Task/EmailTaskTest.php index 6aa74746..4eb79b16 100644 --- a/tests/TestCase/Queue/Task/EmailTaskTest.php +++ b/tests/TestCase/Queue/Task/EmailTaskTest.php @@ -208,6 +208,51 @@ public function testRunArrayEmailComplex() { $this->assertSame($settings['cc'][0], $debugEmail->getCc()); } + /** + * Address settings stored as associative `email => name` maps must be + * accepted without triggering PHP 8 "Unknown named parameter" errors. + * + * @return void + */ + public function testRunArrayAssociativeAddressMap() { + $settings = [ + 'from' => [ + 'sender@test.de' => 'Sender Name', + ], + 'to' => [ + 'recipient@test.de' => 'Recipient Name', + ], + 'cc' => [ + 'copy@test.de' => 'Copy Name', + 'copy-other@test.de' => 'Other Copy Name', + ], + 'bcc' => [ + 'bcc@test.de' => 'BCC Name', + ], + 'replyTo' => [ + 'reply@test.de' => 'Reply Name', + ], + ]; + + $data = [ + 'settings' => $settings, + 'content' => 'Foo Bar', + ]; + $this->Task->run($data, 0); + + $this->assertInstanceOf(Mailer::class, $this->Task->mailer); + + $mailer = $this->Task->mailer; + $this->assertSame(['sender@test.de' => 'Sender Name'], $mailer->getFrom()); + $this->assertSame(['recipient@test.de' => 'Recipient Name'], $mailer->getTo()); + $this->assertSame([ + 'copy@test.de' => 'Copy Name', + 'copy-other@test.de' => 'Other Copy Name', + ], $mailer->getCc()); + $this->assertSame(['bcc@test.de' => 'BCC Name'], $mailer->getBcc()); + $this->assertSame(['reply@test.de' => 'Reply Name'], $mailer->getReplyTo()); + } + /** * @return void */ diff --git a/tests/test_app/templates/Error/error500.php b/tests/test_app/templates/Error/error500.php index 9717c768..d7101d11 100644 --- a/tests/test_app/templates/Error/error500.php +++ b/tests/test_app/templates/Error/error500.php @@ -3,31 +3,31 @@ use Cake\Core\Configure; use Cake\Error\Debugger; -if (Configure::read('debug')) : +if (Configure::read('debug')) { $this->layout = 'dev_error'; $this->assign('title', $message); $this->assign('templateName', 'error500.ctp'); $this->start('file'); - if (!empty($error->queryString)) : ?> + if (!empty($error->queryString)) { ?>

SQL Query: queryString) ?>

- - params)) : ?> + params)) { ?> SQL Query Params: params) ?> - element('auto_table_warning'); - if (extension_loaded('xdebug')) : + if (extension_loaded('xdebug')) { xdebug_print_function_stack(); - endif; + } $this->end(); -endif; +} ?>