Search code examples
phpswiftmailerzend-mail

Zend\Mail fails with Gmail, but SwiftMailer lacks a file transport for development/testing. what to do?


I have successfully used Zend_Mail from ZF1 to connect to a GMail account using SMTP, but have not been able to get it to work with Zend\Mail. The following produced a "Connection timed out" exception:

use Zend\Mail\Message;
use Zend\Mail\Transport\Smtp as SmtpTransport;
use Zend\Mail\Transport\SmtpOptions;

$transport = new SmtpTransport();
$options   = new SmtpOptions([
    'host' => 'smtp.gmail.com',
    'connection_class' => 'login',
    'connection_config' => [
        'username' => '[email protected]',
        'password' => 'redacted',
        'port' => 587,
        'ssl' => 'tls',
    ],
]);
$transport->setOptions($options);
$message = new Message();
$message->addTo('[email protected]')
    ->setFrom('[email protected]')
    ->setSubject('hello, fool!')
    ->setBody("testing one two three! time is now ".date('r'));

$transport->send($message);

However! The very same connection parameters work fine when I do the equivalent of the above with PHP Swiftmailer, and also with Python, and also with the older Zend_Mail. All of this of course in the same environment (my Ubuntu 14.04 box). So, I think there must be an issue with Zend\Mail -- or perhaps more accurately Zend\Mail\Transport\Smtp.

Unfortunately I am not enough of a hero (and lack the skills) to dig into Zend\Mail\Transport\Smtp and fix it, so I turned next to Swiftmailer (http://swiftmailer.org/). And it works just great,

$transport = Swift_SmtpTransport::newInstance('smtp.gmail.com', 587, 'tls');
$transport->setUsername('[email protected]')
->setPassword('redacted');
$mailer = Swift_Mailer::newInstance($transport);
$message = Swift_Message::newInstance();

$message->setSubject("testing from Swiftmailer")
    ->setFrom("[email protected]")
    ->setTo('[email protected]')
    ->setBody("yadda yadda blah blah blah!");
$result = $mailer->send($message); // nice and easy!

but lacks one nice feature: Zend\Mail has a File transport that simply dumps your message to a file, very handy for development and testing, when you don't really want to send email messages to anyone.

What to do? I am familiar with the configuration trick of setting an "environment" environment variable to "development", "production", etc., and making my code decide how to configure itself accordingly. But in my thought experiments so far, this gets a little awkward.

One idea: subclass Swift_Mailer and override send() to simply write the message to disk, and consult the $environment to decide whether to instantiate the real thing or the subclass that doesn't really send.

But I would love to hear some other ideas and suggestions.


Solution

  • For swiftmailer you can use SpoolTransport with a FileSpool to store messages to filesystem. Example (by Norio Suzuki):

    /**
     * 270-transport-spool-file.php
     */
    require_once '../vendor/autoload.php';
    require_once './config.php';
    // POINT of this sample
    $path = FILE_SPOOL_PATH;
    $spool = new Swift_FileSpool($path);
    $transport = Swift_SpoolTransport::newInstance($spool);
    $mailer = Swift_Mailer::newInstance($transport);
    $message = Swift_Message::newInstance();
    $message
        ->setFrom(MAIL_FROM)
        ->setTo(MAIL_TO)
        ->setSubject('SpoolTransport (file) sample')
        ->setBody('This is a mail.')
    ;
    // Serialized Swift_Message objects were spooled to $path
    $result = $mailer->send($message);