Move heavy lifting of wp_mail() to child class of PHPMailer
|Reported by:||stephenharris||Owned by:|
If a plug-in is sending an e-mail, the class PHPMailer has a lot of useful methods (e.g. addStringAttachment()), but these are not available when using wp_mail(), which is a requirement to work with numerous other plug-ins owing to the hooks it triggers.
wp_mail() does a number of things:
- Instantiates a PHPMailer instance
- Sets default values (e.g. "from" headers, charset, content type etc. )
- Parses the passed arguments and feeds it to the PHPMalier instance.
- Executes a "pre-send" routine, (e.g. triggering hooks wp_mail_from, phpmailer_init etc)
- Sends the e-mail
The attached patch does a number of things:
- Defines a WPMailer class ( a child of PHPMailer)
- Defines a WPMailerFactory class which creates an instance with appropriate default values
- Defines 'helper' methods which do the 'heavy lifting' of (3) above
- Overrides the preSend method of PHPMailer to execute the 'pre-send routine' present in wp_mail() (i.e. (4) above)
- Refactors wp_mail() to "operate" WPMailer() instance
The result is that developers can either use wp_mail() or $wpmailer = WPMailerFactory::getMailer() to send e-mails, and both will behave identically (in terms of default values, and hooks being triggered), while maintaining backwards compatibility.
Why just not use phpmailer_init?
This hook is very useful, but offers no context in which wp_mail() is called. As an example, suppose a plug-ins sends an e-mail with an "on-the-fly" purchase receipt attached. At phpmailer_init I don't know the purchase ID from which to generate and attach the receipt.
Class/method naming standards
I've used PHPMailer's naming standards which I understand conflicts slightly with WordPress' naming standards. A future iteration of this patch could well change this if that is deemed best.
The global $phpmailer is redundant, as the factory creates a fresh instance for each request. Or at least it would. The only reason the patch still uses this global, is that all the relevant unit tests pass without any further changes. Subject to this ticket being accepted in principle, these tests should be updated along with the patch.
Assuming wp_mail() hasn't been overriden by a plug-in/theme, then the is no change in behaviour. If it has been overridden, it's clear from the original function that the $_GLOBAL['phpmailer'] should not be expected to exist, nor even the required classes to be loaded. As such they can be expected to operate independently of the changes made here, which are non-destructive.
For me, the mail group unit tests pass with 1 skipped. For some reason some tests failed (e.g. Tests_DB::test_bail()), but these failed even without this patch.