PHP SwiftMailer StringReplacementFilter 错误解决方法

PHP SwiftMailer StringReplacementFilter bug workaround

尝试实施临时解决方法来处理这个奇怪的 SwiftMailer 错误:

https://github.com/swiftmailer/swiftmailer/issues/762

When reading a file that has a length of exactly n*8192 bytes (n >= 1), the last > value of $bytes is an empty string which triggers the error.

@raffomania 对 GitHub 的评论:

we found that the following adaption of AbstractFilterableInputStream.write would fix the problem for us:

public function write($bytes)
{
    $this->_writeBuffer .= $bytes;
    if (empty($this->_writeBuffer)) {
        return;
    }
    foreach ($this->_filters as $filter) {
        if ($filter->shouldBuffer($this->_writeBuffer)) {
            return;
        }
    }
    $this->_doWrite($this->_writeBuffer);

    return ++$this->_sequence;
}

我想扩展 AbstractFilterableInputStream class 并在 SwiftMailer 调用 AbstractFilterableInputStream 时调用这个修改后的写入方法。

我正在使用 Laravel 框架。

解决此问题的最佳方法是分叉并修复 swiftmailer,并使用您自己的分叉版本的 swiftmailer。但是,如果您不想这样做,此修复程序会相当冗长,但它应该会起作用。试一试。如果有任何问题,请告诉我。

1. Create app/Mail/CustomFileByteStream.php:这是为了拥有自己的版本write

<?php

namespace App\Mail;

/**
 * Allows reading and writing of bytes to and from a file.
 *
 * @author Chris Corbyn
 */
class CustomFileByteStream extends \Swift_ByteStream_FileByteStream
{
    public function write($bytes)
    {
        $this->_writeBuffer .= $bytes;
        if (empty($this->_writeBuffer)) {
            return;
        }
        foreach ($this->_filters as $filter) {
            if ($filter->shouldBuffer($this->_writeBuffer)) {
                return;
            }
        }
        $this->_doWrite($this->_writeBuffer);

        return ++$this->_sequence;
    }
}

2。创建 app/Mail/CustomSwiftAttachment.php:以便它使用自定义 FileByteStream

<?php

namespace App\Mail;

/**
 * Attachment class for attaching files to a {@link Swift_Mime_Message}.
 *
 * @author Chris Corbyn
 */
class CustomSwiftAttachment extends \Swift_Attachment
{
    /**
     * Create a new Attachment from a filesystem path.
     *
     * @param string $path
     * @param string $contentType optional
     *
     * @return Swift_Mime_Attachment
     */
    public static function fromPath($path, $contentType = null)
    {
        return self::newInstance()->setFile(
            new CustomFileByteStream($path),
            $contentType
            );
    }
}

3。创建 app/Mail/CustomSwiftImage.php:以便它使用自定义 FileByteStream

<?php

namespace App\Mail;

/**
 * An image, embedded in a multipart message.
 *
 * @author Chris Corbyn
 */
class CustomSwiftImage extends \Swift_Image
{
    /**
     * Create a new Image from a filesystem path.
     *
     * @param string $path
     *
     * @return Swift_Image
     */
    public static function fromPath($path)
    {
        $image = self::newInstance()->setFile(
            new CustomFileByteStream($path)
            );

        return $image;
    }
}

4.创建 app/Mail/Message.php: 以便它使用您自己的自定义 Swift_ImageSwift_Attachment

<?php

namespace App\Mail;

use Illuminate\Mail\Message as DefaultMessage;

class Message extends DefaultMessage
{
    /**
     * Create a Swift Attachment instance.
     *
     * @param  string  $file
     * @return CustomSwiftAttachment
     */
    protected function createAttachmentFromPath($file)
    {
        return CustomSwiftAttachment::fromPath($file);
    }

    /**
     * Embed a file in the message and get the CID.
     *
     * @param  string  $file
     * @return string
     */
    public function embed($file)
    {
        return $this->swift->embed(CustomSwiftImage::fromPath($file));
    }
}

5.创建app/Mail/Mailer.php:所以它使用你的自定义Messageclass

<?php

namespace App\Mail;

use Swift_Message;
use Illuminate\Mail\Mailer as DefaultMailer;

class Mailer extends DefaultMailer
{
    /**
     * Create a new message instance. Notice this is complete replacement of parent's version.
     * We uses our own "Message" class instead of theirs.
     *
     * @return \Illuminate\Mail\Message
     */
    protected function createMessage()
    {
        $message = new Message(new Swift_Message);

        // If a global from address has been specified we will set it on every message
        // instances so the developer does not have to repeat themselves every time
        // they create a new message. We will just go ahead and push the address.
        if (! empty($this->from['address'])) {
            $message->from($this->from['address'], $this->from['name']);
        }

        return $message;
    }
}

6.创建 app/Mail/MailServiceProvider.php: 所以它使用你的自定义 Mailer class

<?php

namespace App\Mail;

use Illuminate\Mail\MailServiceProvider as DefaultMailServiceProvider;
use App\Mail\Mailer;

/**
 * This mail service provider is almost identical with the illuminate version, with the exception that
 * we are hijacking with our own Message class
 */
class MailServiceProvider extends DefaultMailServiceProvider
{
    /**
     * Complete replacement of parent register class so we can
     * overwrite the use of mailer class. Notice the "Mailer" class is points to our own
     * version of mailer, so we can hijack the message class.
     *
     * @return void
     */
    public function register()
    {
        $this->registerSwiftMailer();

        $this->app->singleton('mailer', function ($app) {
            // Once we have create the mailer instance, we will set a container instance
            // on the mailer. This allows us to resolve mailer classes via containers
            // for maximum testability on said classes instead of passing Closures.
            $mailer = new Mailer(
                $app['view'], $app['swift.mailer'], $app['events']
            );

            $this->setMailerDependencies($mailer, $app);

            // If a "from" address is set, we will set it on the mailer so that all mail
            // messages sent by the applications will utilize the same "from" address
            // on each one, which makes the developer's life a lot more convenient.
            $from = $app['config']['mail.from'];

            if (is_array($from) && isset($from['address'])) {
                $mailer->alwaysFrom($from['address'], $from['name']);
            }

            $to = $app['config']['mail.to'];

            if (is_array($to) && isset($to['address'])) {
                $mailer->alwaysTo($to['address'], $to['name']);
            }

            return $mailer;
        });
    }
}

7.编辑 config/app.php

  • 注释掉Illuminate\Mail\MailServiceProvider::class,
  • App\Mail\MailServiceProvider::class,
  • 上面的行下面添加这个

所以这将使用您的自定义 MailServiceProvider。在任何时候,如果你想恢复这个,只需删除上面的所有文件并恢复这个编辑。


调用顺序:

所以给你。这应该劫持 MailServiceProvider 以使用您自己的自定义 Swift_ByteStream_FileByteStream。希望没有错别字!