From a2c8988da4ca9bc96729c4777efee249473fde3a Mon Sep 17 00:00:00 2001
From: Dave Long <dave@longwaveconsulting.com>
Date: Wed, 14 Feb 2024 18:13:44 +0000
Subject: [PATCH] Issue #3420977 by larowlan, smustgrave, Berdir, mstrelan:
 Convert Mail plugin discovery to attributes

---
 core/lib/Drupal/Core/Mail/Attribute/Mail.php  | 43 +++++++++++++++++++
 core/lib/Drupal/Core/Mail/MailManager.php     |  3 +-
 .../Drupal/Core/Mail/Plugin/Mail/PhpMail.php  | 13 +++---
 .../Core/Mail/Plugin/Mail/SymfonyMailer.php   | 11 ++---
 .../Mail/Plugin/Mail/TestMailCollector.php    | 13 +++---
 .../src/Plugin/Mail/TestHtmlMailCollector.php | 13 +++---
 .../src/Plugin/Mail/TestPhpMailFailure.php    | 13 +++---
 7 files changed, 79 insertions(+), 30 deletions(-)
 create mode 100644 core/lib/Drupal/Core/Mail/Attribute/Mail.php

diff --git a/core/lib/Drupal/Core/Mail/Attribute/Mail.php b/core/lib/Drupal/Core/Mail/Attribute/Mail.php
new file mode 100644
index 000000000000..f0d2d1ee4c97
--- /dev/null
+++ b/core/lib/Drupal/Core/Mail/Attribute/Mail.php
@@ -0,0 +1,43 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\Core\Mail\Attribute;
+
+use Drupal\Component\Plugin\Attribute\Plugin;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+
+/**
+ * Defines a Mail attribute for plugin discovery.
+ *
+ * Plugin Namespace: Plugin\Mail
+ *
+ * For a working example, see \Drupal\Core\Mail\Plugin\Mail\PhpMail
+ *
+ * @see \Drupal\Core\Mail\MailInterface
+ * @see \Drupal\Core\Mail\MailManager
+ * @see plugin_api
+ */
+#[\Attribute(\Attribute::TARGET_CLASS)]
+class Mail extends Plugin {
+
+  /**
+   * Constructs a Mail attribute.
+   *
+   * @param string $id
+   *   The plugin ID.
+   * @param \Drupal\Core\StringTranslation\TranslatableMarkup $label
+   *   The label of the plugin.
+   * @param \Drupal\Core\StringTranslation\TranslatableMarkup|null $description
+   *   (optional) A description of the plugin.
+   * @param string|null $deriver
+   *   (optional) The deriver class.
+   */
+  public function __construct(
+    public readonly string $id,
+    public readonly TranslatableMarkup $label,
+    public readonly ?TranslatableMarkup $description = NULL,
+    public readonly ?string $deriver = NULL,
+  ) {}
+
+}
diff --git a/core/lib/Drupal/Core/Mail/MailManager.php b/core/lib/Drupal/Core/Mail/MailManager.php
index 9c725822d7d4..f3dc4803a9f7 100644
--- a/core/lib/Drupal/Core/Mail/MailManager.php
+++ b/core/lib/Drupal/Core/Mail/MailManager.php
@@ -6,6 +6,7 @@
 use Drupal\Component\Render\PlainTextOutput;
 use Drupal\Component\Utility\Html;
 use Drupal\Core\Logger\LoggerChannelFactoryInterface;
+use Drupal\Core\Mail\Attribute\Mail;
 use Drupal\Core\Messenger\MessengerTrait;
 use Drupal\Core\Plugin\DefaultPluginManager;
 use Drupal\Core\Cache\CacheBackendInterface;
@@ -79,7 +80,7 @@ class MailManager extends DefaultPluginManager implements MailManagerInterface {
    *   The renderer.
    */
   public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler, ConfigFactoryInterface $config_factory, LoggerChannelFactoryInterface $logger_factory, TranslationInterface $string_translation, RendererInterface $renderer) {
-    parent::__construct('Plugin/Mail', $namespaces, $module_handler, 'Drupal\Core\Mail\MailInterface', 'Drupal\Core\Annotation\Mail');
+    parent::__construct('Plugin/Mail', $namespaces, $module_handler, 'Drupal\Core\Mail\MailInterface', Mail::class, 'Drupal\Core\Annotation\Mail');
     $this->alterInfo('mail_backend_info');
     $this->setCacheBackend($cache_backend, 'mail_backend_plugins');
     $this->configFactory = $config_factory;
diff --git a/core/lib/Drupal/Core/Mail/Plugin/Mail/PhpMail.php b/core/lib/Drupal/Core/Mail/Plugin/Mail/PhpMail.php
index 09a56006a6a3..809420759969 100644
--- a/core/lib/Drupal/Core/Mail/Plugin/Mail/PhpMail.php
+++ b/core/lib/Drupal/Core/Mail/Plugin/Mail/PhpMail.php
@@ -2,9 +2,11 @@
 
 namespace Drupal\Core\Mail\Plugin\Mail;
 
+use Drupal\Core\Mail\Attribute\Mail;
 use Drupal\Core\Mail\MailFormatHelper;
 use Drupal\Core\Mail\MailInterface;
 use Drupal\Core\Site\Settings;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Symfony\Component\Mime\Header\Headers;
 use Symfony\Component\Mime\Header\UnstructuredHeader;
 
@@ -12,13 +14,12 @@
 
 /**
  * Defines the default Drupal mail backend, using PHP's native mail() function.
- *
- * @Mail(
- *   id = "php_mail",
- *   label = @Translation("Default PHP mailer"),
- *   description = @Translation("Sends the message as plain text, using PHP's native mail() function.")
- * )
  */
+#[Mail(
+  id: 'php_mail',
+  label: new TranslatableMarkup('Default PHP Mailer'),
+  description: new TranslatableMarkup("Sends the message as plain text, using PHP's native mail() function."),
+)]
 class PhpMail implements MailInterface {
 
   /**
diff --git a/core/lib/Drupal/Core/Mail/Plugin/Mail/SymfonyMailer.php b/core/lib/Drupal/Core/Mail/Plugin/Mail/SymfonyMailer.php
index ee9c6eeab400..509718dab1e9 100644
--- a/core/lib/Drupal/Core/Mail/Plugin/Mail/SymfonyMailer.php
+++ b/core/lib/Drupal/Core/Mail/Plugin/Mail/SymfonyMailer.php
@@ -3,9 +3,11 @@
 namespace Drupal\Core\Mail\Plugin\Mail;
 
 use Drupal\Component\Render\MarkupInterface;
+use Drupal\Core\Mail\Attribute\Mail;
 use Drupal\Core\Mail\MailFormatHelper;
 use Drupal\Core\Mail\MailInterface;
 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Utility\Error;
 use Psr\Log\LoggerInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -48,13 +50,12 @@
  *
  * @see https://symfony.com/doc/current/mailer.html#using-built-in-transports
  *
- * @Mail(
- *   id = "symfony_mailer",
- *   label = @Translation("Symfony mailer (Experimental)"),
- * )
- *
  * @internal
  */
+#[Mail(
+  id: 'symfony_mailer',
+  label: new TranslatableMarkup('Symfony mailer (Experimental)'),
+)]
 class SymfonyMailer implements MailInterface, ContainerFactoryPluginInterface {
 
   /**
diff --git a/core/lib/Drupal/Core/Mail/Plugin/Mail/TestMailCollector.php b/core/lib/Drupal/Core/Mail/Plugin/Mail/TestMailCollector.php
index 1f8326936908..d4a3fb6ece61 100644
--- a/core/lib/Drupal/Core/Mail/Plugin/Mail/TestMailCollector.php
+++ b/core/lib/Drupal/Core/Mail/Plugin/Mail/TestMailCollector.php
@@ -2,19 +2,20 @@
 
 namespace Drupal\Core\Mail\Plugin\Mail;
 
+use Drupal\Core\Mail\Attribute\Mail;
 use Drupal\Core\Mail\MailInterface;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
 
 /**
  * Defines a mail backend that captures sent messages in the state system.
  *
  * This class is for running tests or for development.
- *
- * @Mail(
- *   id = "test_mail_collector",
- *   label = @Translation("Mail collector"),
- *   description = @Translation("Does not send the message, but stores it in Drupal within the state system. Used for testing.")
- * )
  */
+#[Mail(
+  id: 'test_mail_collector',
+  label: new TranslatableMarkup('Mail collector'),
+  description: new TranslatableMarkup('Does not send the message, but stores it in Drupal within the state system. Used for testing.'),
+)]
 class TestMailCollector extends PhpMail implements MailInterface {
 
   /**
diff --git a/core/modules/system/tests/modules/mail_html_test/src/Plugin/Mail/TestHtmlMailCollector.php b/core/modules/system/tests/modules/mail_html_test/src/Plugin/Mail/TestHtmlMailCollector.php
index 43e413596d5f..36747a277c43 100644
--- a/core/modules/system/tests/modules/mail_html_test/src/Plugin/Mail/TestHtmlMailCollector.php
+++ b/core/modules/system/tests/modules/mail_html_test/src/Plugin/Mail/TestHtmlMailCollector.php
@@ -2,21 +2,22 @@
 
 namespace Drupal\mail_html_test\Plugin\Mail;
 
+use Drupal\Core\Mail\Attribute\Mail;
 use Drupal\Core\Mail\MailFormatHelper;
 use Drupal\Core\Mail\Plugin\Mail\TestMailCollector;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
 
 /**
  * Defines a mail backend that captures sent HTML messages in the state system.
  *
  * This class is for running tests or for development and does not convert HTML
  * to plaintext.
- *
- * @Mail(
- *   id = "test_html_mail_collector",
- *   label = @Translation("HTML mail collector"),
- *   description = @Translation("Does not send the message, but stores its HTML in Drupal within the state system. Used for testing.")
- * )
  */
+#[Mail(
+  id: 'test_html_mail_collector',
+  label: new TranslatableMarkup('HTML mail collector'),
+  description: new TranslatableMarkup('Does not send the message, but stores its HTML in Drupal within the state system. Used for testing.'),
+)]
 class TestHtmlMailCollector extends TestMailCollector {
 
   /**
diff --git a/core/modules/system/tests/modules/system_mail_failure_test/src/Plugin/Mail/TestPhpMailFailure.php b/core/modules/system/tests/modules/system_mail_failure_test/src/Plugin/Mail/TestPhpMailFailure.php
index 7e1c8c20a113..04a6d0c5503c 100644
--- a/core/modules/system/tests/modules/system_mail_failure_test/src/Plugin/Mail/TestPhpMailFailure.php
+++ b/core/modules/system/tests/modules/system_mail_failure_test/src/Plugin/Mail/TestPhpMailFailure.php
@@ -2,8 +2,10 @@
 
 namespace Drupal\system_mail_failure_test\Plugin\Mail;
 
+use Drupal\Core\Mail\Attribute\Mail;
 use Drupal\Core\Mail\Plugin\Mail\PhpMail;
 use Drupal\Core\Mail\MailInterface;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
 
 /**
  * Defines a mail sending implementation that always fails.
@@ -13,13 +15,12 @@
  * @code
  *   \Drupal::configFactory()->getEditable('system.mail')->set('interface.default', 'test_php_mail_failure')->save();
  * @endcode
- *
- * @Mail(
- *   id = "test_php_mail_failure",
- *   label = @Translation("Malfunctioning mail backend"),
- *   description = @Translation("An intentionally broken mail backend, used for tests.")
- * )
  */
+#[Mail(
+  id: 'test_php_mail_failure',
+  label: new TranslatableMarkup('Malfunctioning mail backend'),
+  description: new TranslatableMarkup('An intentionally broken mail backend, used for tests.'),
+)]
 class TestPhpMailFailure extends PhpMail implements MailInterface {
 
   /**
-- 
GitLab