From c6e58be2eb14de19c1669883722f008fd34858e7 Mon Sep 17 00:00:00 2001 From: Mikk Tendermann Date: Fri, 7 Feb 2025 17:02:14 +0200 Subject: [PATCH 1/3] Make TestMailer proxying --- src/Codeception/Lib/Connector/Yii2.php | 28 ++----- .../Lib/Connector/Yii2/TestMailer.php | 73 ++++++++++++++++++- 2 files changed, 78 insertions(+), 23 deletions(-) diff --git a/src/Codeception/Lib/Connector/Yii2.php b/src/Codeception/Lib/Connector/Yii2.php index 1a76275..d47e65a 100644 --- a/src/Codeception/Lib/Connector/Yii2.php +++ b/src/Codeception/Lib/Connector/Yii2.php @@ -15,6 +15,7 @@ use yii\base\Security; use yii\base\UserException; use yii\mail\MessageInterface; +use yii\symfonymailer\Mailer; use yii\web\Application; use yii\web\ErrorHandler; use yii\web\IdentityInterface; @@ -422,33 +423,20 @@ protected function encodeCookies( */ protected function mockMailer(array $config): array { - // options that make sense for mailer mock - $allowedOptions = [ - 'htmlLayout', - 'textLayout', - 'messageConfig', - 'messageClass', - 'useFileTransport', - 'fileTransportPath', - 'fileTransportCallback', - 'view', - 'viewPath', - ]; - $mailerConfig = [ 'class' => TestMailer::class, 'callback' => function (MessageInterface $message) { $this->emails[] = $message; - } + }, + 'mailer' => [ + 'class' => Mailer::class, + ] ]; - if (isset($config['components']['mailer']) && is_array($config['components']['mailer'])) { - foreach ($config['components']['mailer'] as $name => $value) { - if (in_array($name, $allowedOptions, true)) { - $mailerConfig[$name] = $value; - } - } + if (isset($config['components']['mailer'])) { + $mailerConfig['mailer'] = $config['components']['mailer']; } + $config['components']['mailer'] = $mailerConfig; return $config; diff --git a/src/Codeception/Lib/Connector/Yii2/TestMailer.php b/src/Codeception/Lib/Connector/Yii2/TestMailer.php index 16e254d..f82f3ec 100644 --- a/src/Codeception/Lib/Connector/Yii2/TestMailer.php +++ b/src/Codeception/Lib/Connector/Yii2/TestMailer.php @@ -1,23 +1,90 @@ mailer = Instance::ensure($this->mailer, MailerInterface::class); + } + + /** + * @param string $name + * @param array $params + * @return mixed + */ + public function __call($name, $params) + { + try { + return parent::__call($name, $params); + } catch (UnknownMethodException $e) { + return call_user_func_array([$this->mailer, $name], $params); + } + } + + /** + * @param string $name + * @return mixed + */ + public function __get($name) + { + try { + return parent::__get($name); + } catch (UnknownPropertyException $e) { + return $this->mailer->{$name}; + } + } + + /** + * @param string $name + * @param mixed $value + */ + public function __set($name, $value) + { + try { + parent::__set($name, $value); + } catch (UnknownPropertyException $e) { + $this->mailer->{$name} = $value; + } + } + + /** + * @inheritdoc + */ + public function compose($view = null, array $params = []) + { + $message = $this->mailer->compose($view, $params); + $message->mailer = $this; + return $message; + } + protected function sendMessage($message) { call_user_func($this->callback, $message); return true; } - + protected function saveMessage($message) { call_user_func($this->callback, $message); From 56715b5ec907278ab64cea16656bdfcd1b5fbbe8 Mon Sep 17 00:00:00 2001 From: Mikk Tendermann Date: Mon, 10 Feb 2025 13:15:34 +0200 Subject: [PATCH 2/3] Use NullTransport to not send out emails Co-authored-by: Sam Mousa --- src/Codeception/Lib/Connector/Yii2.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Codeception/Lib/Connector/Yii2.php b/src/Codeception/Lib/Connector/Yii2.php index d47e65a..a2c11ab 100644 --- a/src/Codeception/Lib/Connector/Yii2.php +++ b/src/Codeception/Lib/Connector/Yii2.php @@ -430,6 +430,7 @@ protected function mockMailer(array $config): array }, 'mailer' => [ 'class' => Mailer::class, + 'transportFactory' => new NullTransportFactory() ] ]; From 37da9318813bcfdf608628143519ff2bc8aa03fe Mon Sep 17 00:00:00 2001 From: Mikk Tendermann Date: Mon, 10 Feb 2025 15:58:04 +0200 Subject: [PATCH 3/3] Add checks to check if message has property for mailer We cannot blanket add `$transport` field to Mailer, as not all implementations may have it add basic docker-compose file that helps run tests and phpstan --- .env-example | 2 + .gitignore | 1 + Dockerfile | 11 +++++ composer.json | 3 +- docker-compose.yml | 41 +++++++++++++++++++ src/Codeception/Lib/Connector/Yii2.php | 19 ++++++--- .../Lib/Connector/Yii2/TestMailer.php | 25 ++++++++--- 7 files changed, 91 insertions(+), 11 deletions(-) create mode 100644 .env-example create mode 100644 Dockerfile create mode 100644 docker-compose.yml diff --git a/.env-example b/.env-example new file mode 100644 index 0000000..8a64187 --- /dev/null +++ b/.env-example @@ -0,0 +1,2 @@ +COMPOSER_HOME=~/.config/composer +UID=1000 \ No newline at end of file diff --git a/.gitignore b/.gitignore index 31a345f..c9b4bcd 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ tests/_support tests/_output tests/cases/yii2-app-advanced/_data/db.sqlite +.env diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..bf3b009 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,11 @@ +ARG PHP_VERSION="8.3-alpine" + +FROM php:$PHP_VERSION + +ADD https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/ + +RUN chmod +x /usr/local/bin/install-php-extensions \ + && apk add --no-cache git \ + && install-php-extensions gd intl zip intl pcov @composer + +WORKDIR /var/www/html \ No newline at end of file diff --git a/composer.json b/composer.json index a447850..640301c 100644 --- a/composer.json +++ b/composer.json @@ -29,7 +29,8 @@ "codemix/yii2-localeurls": "^1.7", "codeception/module-asserts": ">= 3.0", "codeception/module-filesystem": "> 3.0", - "phpstan/phpstan": "^1.10" + "phpstan/phpstan": "^1.10", + "yiisoft/yii2-swiftmailer": "^2.0.0" }, "autoload":{ "classmap": ["src/"] diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..4350f7f --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,41 @@ +services: + + php80: &phpbase + user: ${UID} + volumes: + - ${COMPOSER_HOME}:/.config/composer:delegated + # Mount source-code for development + - ./:/var/www/html + env_file: + - .env + environment: + - XDEBUG_CONFIG=client_host=host.docker.internal + extra_hosts: + - "host.docker.internal:host-gateway" + build: + context: ./ + dockerfile: Dockerfile + args: + - PHP_VERSION=8.0-alpine + + php81: + <<: *phpbase + build: + args: + - PHP_VERSION=8.1-alpine + php82: + <<: *phpbase + build: + args: + - PHP_VERSION=8.2-alpine + + php83: + <<: *phpbase + build: + args: + - PHP_VERSION=8.3-alpine + php84: + <<: *phpbase + build: + args: + - PHP_VERSION=8.4-alpine diff --git a/src/Codeception/Lib/Connector/Yii2.php b/src/Codeception/Lib/Connector/Yii2.php index a2c11ab..9a0dd15 100644 --- a/src/Codeception/Lib/Connector/Yii2.php +++ b/src/Codeception/Lib/Connector/Yii2.php @@ -14,8 +14,8 @@ use yii\base\ExitException; use yii\base\Security; use yii\base\UserException; +use yii\helpers\ArrayHelper; use yii\mail\MessageInterface; -use yii\symfonymailer\Mailer; use yii\web\Application; use yii\web\ErrorHandler; use yii\web\IdentityInterface; @@ -429,13 +429,22 @@ protected function mockMailer(array $config): array $this->emails[] = $message; }, 'mailer' => [ - 'class' => Mailer::class, - 'transportFactory' => new NullTransportFactory() + 'class' => \yii\symfonymailer\Mailer::class ] ]; - if (isset($config['components']['mailer'])) { - $mailerConfig['mailer'] = $config['components']['mailer']; + if (isset($config['components']['mailer']) && is_array($config['components']['mailer'])) { + $mailerConfig['mailer'] = ArrayHelper::merge($mailerConfig['mailer'], $config['components']['mailer']); + } + + /** + * Set transports only if known mailers that have this field is used + */ + if ($mailerConfig['mailer']['class'] instanceof \yii\symfonymailer\Mailer) { + $mailerConfig['mailer']['transport'] = \Symfony\Component\Mailer\Transport\NullTransport::class; + } + if ($mailerConfig['mailer']['class'] instanceof \yii\swiftmailer\Mailer) { + $mailerConfig['mailer']['transport'] = \Swift_Transport_NullTransport::class; } $config['components']['mailer'] = $mailerConfig; diff --git a/src/Codeception/Lib/Connector/Yii2/TestMailer.php b/src/Codeception/Lib/Connector/Yii2/TestMailer.php index f82f3ec..ba3dc1f 100644 --- a/src/Codeception/Lib/Connector/Yii2/TestMailer.php +++ b/src/Codeception/Lib/Connector/Yii2/TestMailer.php @@ -1,11 +1,15 @@ mailer = Instance::ensure($this->mailer, MailerInterface::class); @@ -72,20 +76,31 @@ public function __set($name, $value) /** * @inheritdoc */ - public function compose($view = null, array $params = []) + public function compose($view = null, array $params = []): MessageInterface { $message = $this->mailer->compose($view, $params); - $message->mailer = $this; + + if ($message instanceof BaseObject && $message->canSetProperty('mailer')) { + /** @phpstan-ignore property.notFound */ + $message->mailer = $this; + } else { + $reflection = new ReflectionClass($message); + if ($reflection->hasProperty('mailer')) { + /** @phpstan-ignore property.notFound */ + $message->mailer = $this; + } + } + return $message; } - protected function sendMessage($message) + protected function sendMessage($message): bool { call_user_func($this->callback, $message); return true; } - protected function saveMessage($message) + protected function saveMessage($message): bool { call_user_func($this->callback, $message); return true;