Proposal replace PEAR mailer classes in core extension
I'm proposing that we add a new core extension that swaps out the existing Pear Mailer classes with a more modern and better supported library.
Per the above comment the reason is largely that I think PEAR mail is not well supported or future proofed - but here are some other advantages of modern libraries - these apply to PhpMailer but may equally apply to others
- SMTP failover - phpmailer (& others) allow you to specify more than 1 smtp server and will use a different one if the first is down
- SMTP auth over LOGIN, PLAIN, CRAM-MD5, XOAUTH2.
- Multilanguage support for error messages.
- Better developer docs
- More widely used used - ie by WP, Joomla and Drupal.
Contenders
PhpMailer SwiftMailer Zeta-components Omnimail
I've mostly focussed on PhpMailer as it seems to be the most prevalent without any obvious downsides but there isn't that much in it. Regardless, I think agreeing a package & that we should do this is the hardest part of this task.
https://php.libhunt.com/compare-phpmailer-vs-swiftmailer https://alexwebdevelop.com/phpmailer-tutorial/ Google trends https://trends.google.com/trends/explore?date=all&q=phpmailer,swiftmailer
I'm not sold on the Omnimail library as better than just PhpMailer - it adds a few more variants - but it wraps them (which is good) but such that some options are not available - eg https://github.com/shahariaazam/smtp-mailer/pull/1
The Omnimail maintainer has been good at merging PRs but it's not that active - so if we went that way it would be because we feel it's better than writing a wrapper ourselves.
Implementation
The process for swapping out a mailer class is pretty simple - ie
/**
* Implements hook_civicrm_container().
*/
function omnimail_civicrm_container(\Symfony\Component\DependencyInjection\ContainerBuilder $container) {
$container->setDefinition('pear_mail', new Definition('Civi\Omnimail'))
->setFactory('Civi\Omnimail\MailFactory::getFactoryMailer')->setPublic(TRUE);
}
The challenge is that the signature is a bit Pear-shaped (hah) - so we need a class that does the mapping. Note that for the first one we can assume an array in our implementation.
* @param mixed $recipients Either a comma-seperated list of recipients
* (RFC822 compliant), or an array of recipients,
* each RFC822 valid. This may contain recipients not
* specified in the headers, for Bcc:, resending
* messages, etc.
*
* @param array $headers The array of headers to send with the mail, in an
* associative array, where the array key is the
* header name (ie, 'Subject'), and the array value
* is the header value (ie, 'test'). The header
* produced from those values would be 'Subject:
* test'.
*
* @param string $body The full text of the message body, including any
* Mime parts, etc.
*
* @return mixed Returns true on success, or a PEAR_Error
* containing a descriptive error message on
* failure.
Preliminary work
We want to fix it to throw an exception rather than return a PEAR_Error on fail as the latter if VERY Pear-shaped. We can just catch any exception.