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} [] []
1

1 Answer 1

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.

5
  • 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
    Commented Feb 19, 2018 at 8:02
  • 2
    github.com/dustyman145/Magento-Extended-SMTP
    – DustyMan
    Commented Feb 19, 2018 at 14:18
  • 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
    Commented Jun 5, 2018 at 16:48
  • @DustyMan I am still facing issue after adding your plugin on Magento2.3.5 version. Error as Fatal error: Uncaught TypeError: Argument 1 passed to Magento\ExtendedSmtp\Plugin\Magento\Framework\Mail\Transport::sendSmtpMessage() must implement interface Magento\Framework\Mail\MessageInterface, null given, called in /var/www/magento/app/code/Magento/ExtendedSmtp/Plugin/Magento/Framework/Mail/Transport.php on line 45 and defined in /var/www/magento/app/code/Magento/ExtendedSmtp/Plugin/Magento/Framework/Mail/Transport.php:57
    – Rahul
    Commented Dec 21, 2020 at 19:27
  • @jamil Please provide updated solution.
    – Rahul
    Commented Dec 21, 2020 at 19:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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