2

I started developing Magento 2.2 modules about 3 months ago. I did a very simple SMTP module by following what some open source projects did. I could use a free plugin but the real goal here is to learn Magento 2. My module is hosted on a private GitHub repository and gets updated by running composer upgrade on the magento 2 server. I can see the custom fields I created in the backend but whenever magento send a message an error is thrown. At first I thought the problem was with my module but it seems like my Transport class isn't even called. Here is my main module's files.

composer.json

{
    "name": "mymodule/module-smtp",
    "description": "SMTP Extension for Magento 2 to help the configuration of the SMTP (Simple Mail Transfer Protocol) server.",
    "type": "magento2-module",
    "version": "1.1.0",
    "require": {
        "php": "7.0.2|7.0.4|~7.0.6|~7.1.0",
        "magento/framework": "101.0.*",
        "magento/module-config": "101.0.*",
        "magento/module-store": "100.2.*"
    },
    "license": [
        "proprietary"
    ],
    "minimum-stability": "stable",
    "autoload": {
        "files": [
            "registration.php"
        ],
        "psr-4": {
            "MyModule\\Smtp\\": ""
        }
    }
}

registration.php

<?php

\Magento\Framework\Component\ComponentRegistrar::register(
    \Magento\Framework\Component\ComponentRegistrar::MODULE,
    'MyModule_Smtp',
    __DIR__
);

etc/di.xml

<?xml version="1.0" encoding="utf-8"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <preference for="Magento\Framework\Mail\Transport" type="MyModule\Smtp\Plugin\Magento\Framework\Mail\Transport" />
</config>

etc/module.xml

<?xml version="1.0" encoding="utf-8"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
    <module name="MyModule_Smtp" setup_version="1.0.0">
        <sequence>
            <module name="Magento_Backend"/>
        </sequence>
    </module>
</config>

Plugin/Magento/Framework/Mail/Transport.php

<?php
namespace MyModule\Smtp\Plugin\Magento\Framework\Mail;

class Transport implements \Magento\Framework\Mail\TransportInterface
{
    /**
     * @var \Magento\Framework\Mail\MessageInterface
     */
    protected $_message;

    /**
     * @var \MyModule\Smtp\Helper\Data
     */
    private $_dataHelper;

    /**
     * @var \Magento\Framework\Encryption\EncryptorInterface
     */
    private $_encryptor;

    /**
     * @param \Magento\Framework\Mail\MessageInterface $message
     * @param \MyModule\Smtp\Helper\Data $dataHelper
     * @param \Magento\Framework\Encryption\EncryptorInterface $encryptor
     */
    public function __construct(
        \Magento\Framework\Mail\MessageInterface $message,
        \MyModule\Smtp\Helper\Data $dataHelper,
        \Magento\Framework\Encryption\EncryptorInterface $encryptor)
    {

        $this->_dataHelper = $dataHelper;
        $this->_encryptor = $encryptor;
        $this->_message = $message;
    }

    /**
     * Send a mail using this transport
     *
     * @return void
     */
    public function sendMessage()
    {
        if ($this->_dataHelper->isConfigurationEnabled()) {
            $this->useSmtp();
        } else {
            $this->useSendMail();
        }
    }

    /**
     * Send mail using a custom SMTP server
     *
     * @return void
     * @throws \Magento\Framework\Exception\MailException
     */
    public function useSmtp()
    {
        $host = $this->_dataHelper->getHost();
        $config = array('port' => $this->_dataHelper->getPort());

        $auth = $this->_dataHelper->getAuthentication();
        if ($auth === 'plain' || $auth === 'login' || $auth === 'cram-md5') {
            $config['auth'] = $auth;
            $config['username'] = $this->_dataHelper->getUsername();
            $config['password'] = $this->_encryptor->decrypt($this->_dataHelper->getpassword());
        }

        $ssl = $this->_dataHelper->getProtocol();
        if ($ssl === 'ssl' || $ssl === 'tls') {
            $config['ssl'] = $ssl;
        }

        $zendMail = new \Zend_Mail_Transport_Smtp($host, $config);

        try {
            $zendMail->send($this->_message);
        } catch (\Exception $e) {
            throw new \Magento\Framework\Exception\MailException(new \Magento\Framework\Phrase($e->getMessage()), $e);
        }
    }

    /**
     * Send mail using PHP mail function
     *
     * @return void
     * @throws \Magento\Framework\Exception\MailException
     */
    public function useSendMail()
    {
        $zendMail = new \Zend_Mail_Transport_Sendmail();

        try {
            $zendMail->send($this->_message);
        } catch (\Exception $e) {
            throw new \Magento\Framework\Exception\MailException(new \Magento\Framework\Phrase($e->getMessage()), $e);
        }
    }

    /**
     * @inheritdoc
     */
    public function getMessage()
    {
        return $this->_message;
    }
}

The only error reported by magento is the following.

system.log

[2017-11-21 23:50:55] main.CRITICAL: Exception message: Unable to send mail.
Trace: #0 /var/www/html/vendor/magento/framework/Interception/Interceptor.php(58): Magento\Email\Model\Transport->sendMessage()
#1 /var/www/html/vendor/magento/framework/Interception/Interceptor.php(138): Magento\Email\Model\Transport\Interceptor->___callParent('sendMessage', Array)
#2 /var/www/html/vendor/magento/module-email/Model/Mail/TransportInterfacePlugin.php(48): Magento\Email\Model\Transport\Interceptor->Magento\Framework\Interception\{closure}()
#3 /var/www/html/vendor/magento/framework/Interception/Interceptor.php(135): Magento\Email\Model\Mail\TransportInterfacePlugin->aroundSendMessage(Object(Magento\Email\Model\Transport\Interceptor), Object(Closure))
#4 /var/www/html/vendor/magento/framework/Interception/Interceptor.php(153): Magento\Email\Model\Transport\Interceptor->Magento\Framework\Interception\{closure}()
#5 /var/www/html/generated/code/Magento/Email/Model/Transport/Interceptor.php(26): Magento\Email\Model\Transport\Interceptor->___callPlugins('sendMessage', Array, NULL)
#6 /var/www/html/vendor/magento/module-user/Model/User.php(414): Magento\Email\Model\Transport\Interceptor->sendMessage()
#7 /var/www/html/vendor/magento/module-user/Controller/Adminhtml/Auth/Forgotpassword.php(73): Magento\User\Model\User->sendPasswordResetConfirmationEmail()
#8 /var/www/html/vendor/magento/framework/App/Action/Action.php(107): Magento\User\Controller\Adminhtml\Auth\Forgotpassword->execute()
#9 /var/www/html/vendor/magento/module-backend/App/AbstractAction.php(229): Magento\Framework\App\Action\Action->dispatch(Object(Magento\Framework\App\Request\Http))
#10 /var/www/html/vendor/magento/framework/Interception/Interceptor.php(58): Magento\Backend\App\AbstractAction->dispatch(Object(Magento\Framework\App\Request\Http))
#11 /var/www/html/vendor/magento/framework/Interception/Interceptor.php(138): Magento\User\Controller\Adminhtml\Auth\Forgotpassword\Interceptor->___callParent('dispatch', Array)
#12 /var/www/html/vendor/magento/module-backend/App/Action/Plugin/Authentication.php(143): Magento\User\Controller\Adminhtml\Auth\Forgotpassword\Interceptor->Magento\Framework\Interception\{closure}(Object(Magento\Framework\App\Request\$
#13 /var/www/html/vendor/magento/framework/Interception/Interceptor.php(135): Magento\Backend\App\Action\Plugin\Authentication->aroundDispatch(Object(Magento\User\Controller\Adminhtml\Auth\Forgotpassword\Interceptor), Object(Closure), O$
#14 /var/www/html/vendor/magento/framework/Interception/Interceptor.php(153): Magento\User\Controller\Adminhtml\Auth\Forgotpassword\Interceptor->Magento\Framework\Interception\{closure}(Object(Magento\Framework\App\Request\Http))
#15 /var/www/html/generated/code/Magento/User/Controller/Adminhtml/Auth/Forgotpassword/Interceptor.php(26): Magento\User\Controller\Adminhtml\Auth\Forgotpassword\Interceptor->___callPlugins('dispatch', Array, NULL)
#16 /var/www/html/vendor/magento/framework/App/FrontController.php(55): Magento\User\Controller\Adminhtml\Auth\Forgotpassword\Interceptor->dispatch(Object(Magento\Framework\App\Request\Http))
#17 /var/www/html/vendor/magento/framework/Interception/Interceptor.php(58): Magento\Framework\App\FrontController->dispatch(Object(Magento\Framework\App\Request\Http))
#18 /var/www/html/vendor/magento/framework/Interception/Interceptor.php(138): Magento\Framework\App\FrontController\Interceptor->___callParent('dispatch', Array)
#19 /var/www/html/vendor/magento/framework/Interception/Interceptor.php(153): Magento\Framework\App\FrontController\Interceptor->Magento\Framework\Interception\{closure}(Object(Magento\Framework\App\Request\Http))
#20 /var/www/html/generated/code/Magento/Framework/App/FrontController/Interceptor.php(26): Magento\Framework\App\FrontController\Interceptor->___callPlugins('dispatch', Array, Array)
#21 /var/www/html/vendor/magento/framework/App/Http.php(135): Magento\Framework\App\FrontController\Interceptor->dispatch(Object(Magento\Framework\App\Request\Http))
#22 /var/www/html/vendor/magento/framework/App/Bootstrap.php(256): Magento\Framework\App\Http->launch()
#23 /var/www/html/index.php(39): Magento\Framework\App\Bootstrap->run(Object(Magento\Framework\App\Http))
#24 {main} [] []
4

After hours of struggles I decided to install Xdebug on my IDE. If you are not using Xdebug already I recommend you to take a look. I followed my request line by line to finally catch the culprit. When sending an Email magento 2 doesn't use "\Magento\Framework\Mail\Transport" instead it uses "\Magento\Email\Model\Transport".

Instead of overriding both of them in my di.xml I decided to wrap a plugin around "\Magento\Framework\Mail\TransportInterface" since all Transport classes implement this interface.

I believe the reason as why Magento_Mail module uses it's own Transport model instead of relying on the Framework Transport model is because the Magento_Mail module adds more options like the possibility to set a return path.

| improve this answer | |
  • As I am struggling with the same problem I was wondering how you have written that plugin of yours. Do you have any example code which I (and other users) may find helpful? – Chuvisco Feb 19 '18 at 8:02
  • 2
  • After an impromptu upgrade form 2.2.3->2.2.4 had to change my di.xml as suggested here <preference for="\Magento\Framework\Mail\TransportInterface" otherwise email was being sent from "apache". Still working on some issues with the sender name but an improvement so far. – jamil Jun 5 '18 at 16:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.