The backend mailer class is just an implementation detail of your own Mail class - it shouldn't be necessary to have intimate knowledge of such details, but because it's a mandatory constructor argument this is currently unavoidable. It should be possible to change the mailer implementation and make no changes to your application/usage code (except in tests).
Another disadvantage to everything-as-constructor-arguments is that all classes in use are created eagerly. If the Mail class were to be instanciated but is ultimately unused - the Mailer object would also have been instanciated and unused. In this case that class doesn't do anything - but if it were also creating dependencies on construction, and if there is any setup logic executed this can easily mean a tree of objects get created before they need to be used. A practical example of how this can be a problem would be if the config is modified after construction but before first use:
$mail = new Mail(new PHPMailer);
...
Config::write('mail.isHtml', false); // would have no effect
$mail->sendMail(...);
The above example probably does not match your usage here - but may be applicable to other scenarios.
So, rather than having the mailer a mandatory constructor argument, have a method to get/set the mailer instance, generally this is an easier pattern to work with, example:
class Mail {
protected $mail;
public function mailer($instance = null) {
if ($instance !== null) {
$this->mail = $instance;
}
if (!$this->mail) {
$this->mail = new PHPMailer();
$this->mail->isHTML(Config::read('mail.isHtml'));
$this->mail->setFrom(Config::read('mail.fromEmail'), Config::read('mail.fromName'));
}
return $this->mail;
}
public function sendMail($to, $subject, $body, $plainText) {
$mailer = $this->mailer();
$mailer->addAddress($to);
$mailer->Subject = $subject;
$mailer->Body = $body;
$mailer->AltBody = $plainText;
return $mailer->send();
}
}
As well as making using the mail class easier, this has the advantage of the backend mailer class only being constructed on first use, it also makes testing easier:
/**
* Verify the headers and body when sendMethod is called
**/
public function testExample() {
$mailer = $this->getMockBuilder('PHPMailer')
->setMethods(['mailSend'])
->getMock();
$mailer->expects($this->once())
->method('mailSend')
->with(['expected header', 'expected body']);
$mailer->isHtml(false);
$mailer->setFrom('[email protected]', 'Example Example');
$mail = new Mail();
$mail->mailer($mailer);
$mail->sendMail('[email protected]', 'Subject', '<p>Body</p>', 'Body');
}
There are other minor details which could change - but adopting this pattern will generally lead to more maintainable code.
$mailer = new PHPMailer;
and$test = new lib\Mail($mailer);
\$\endgroup\$