wp_mail ()을 통해 멀티 파트 (텍스트 / html) 이메일을 보내면 도메인이 차단 될 수 있습니다.


37

요약

때문에 전송 WP 코어의 버그들로 여러 부분 과 함께 이메일 (HTML / 텍스트) wp_mail () (스팸 폴더에서 끝나는 이메일의 기회를 줄이기 위해)됩니다 아이러니하게도 도메인이 핫메일 (및 기타 Microsoft 이메일)에 의해 차단되면서 발생합니다.

이것은 누군가가 궁극적으로 핵심으로 구현 될 수있는 실행 가능한 솔루션을 찾도록 돕기 위해 세분화하려는 복잡한 문제입니다.

보람있는 독서가 될 것입니다. 의 시작하자...

버그

뉴스 레터 이메일이 스팸 폴더로 끝나지 않도록하는 가장 일반적인 조언은 여러 부분으로 된 메시지를 보내는 것입니다.

다중 부분 (mime)은 전자 메일 메시지의 HTML 및 TEXT 부분을 단일 전자 메일로 보내는 것을 말합니다. 클라이언트가 여러 부분으로 된 메시지를 받으면 HTML을 렌더링 할 수 있으면 HTML 버전을 수락하고 그렇지 않으면 일반 텍스트 버전을 표시합니다.

이것은 작동하는 것으로 입증되었습니다. Gmail로 보낼 때 모든 이메일은 기본받은 편지함을 통해 메시지를 여러 부분으로 변경할 때까지 스팸 폴더에 들어갔습니다. 좋은 물건.

이제 wp_mail ()을 통해 멀티 파트 메시지를 보낼 때 컨텐츠 유형 (multipart / *)을 경계와 함께 (사용자 정의로 설정 한 경우) 한 번,없는 경우와 함께 두 번 출력합니다. 이 동작은 전자 메일이 원시 메시지로 표시되고 모든 Microsoft (Hotmail, Outlook 등)를 포함한 일부 전자 메일에는 여러 부분으로 표시되지 않습니다 .

Microsoft는이 메시지를 정크로 표시하고, 수신되는 일부 메시지는 수동으로 플래그를 지정합니다. 불행히도 Microsoft 전자 메일 주소가 널리 사용됩니다. 가입자의 40 %가 사용합니다.

이것은 최근에 우리가 보낸 이메일 교환을 통해 Microsoft에 의해 확인됩니다.

메시지 플래그가 지정되면 도메인이 완전히 차단 됩니다. 이는 메시지가 스팸 폴더로 전송 되지 않으며 수신자에게 전혀 전달되지 않음 을 의미합니다.

지금까지 주요 도메인을 3 번 차단했습니다.

이것은 WP 코어의 버그이므로 멀티 파트 메시지를 보내는 모든 도메인이 차단되고 있습니다. 문제는 대부분의 웹 마스터가 이유를 모른다는 것입니다. 나는 연구를하고 포럼에서 다른 사용자가 이것을 논의 할 때 이것을 확인했습니다. 원시 코드를 탐구하고 이러한 유형의 전자 메일 메시지가 어떻게 작동하는지 잘 알고 있어야합니다.

코드로 나누자

핫메일 / 아웃룩 계정을 만듭니다. 그런 다음 다음 코드를 실행하십시오.

// Set $to to an hotmail.com or outlook.com email
$to = "YourEmail@hotmail.com";

$subject = 'wp_mail testing multipart';

$message = '------=_Part_18243133_1346573420.1408991447668
Content-Type: text/plain; charset=UTF-8

Hello world! This is plain text...


------=_Part_18243133_1346573420.1408991447668
Content-Type: text/html; charset=UTF-8

<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>

<p>Hello World! This is HTML...</p> 

</body>
</html>


------=_Part_18243133_1346573420.1408991447668--';

$headers = "MIME-Version: 1.0\r\n";
$headers .= "From: Foo <foo@bar.com>\r\n";
$headers .= 'Content-Type: multipart/alternative;boundary="----=_Part_18243133_1346573420.1408991447668"';


// send email
wp_mail( $to, $subject, $message, $headers );

기본 컨텐츠 유형 을 변경 하려면 다음을 사용하십시오.

add_filter( 'wp_mail_content_type', 'set_content_type' );
function set_content_type( $content_type ) {
    return 'multipart/alternative';
}

멀티 파트 메시지가 전송됩니다.

따라서 메시지의 전체 원시 소스를 확인하면 컨텐츠 유형이 경계없이 한 번 두 번 추가 된 것을 알 수 있습니다.

MIME-Version: 1.0
Content-Type: multipart/alternative;
         boundary="====f230673f9d7c359a81ffebccb88e5d61=="
MIME-Version: 1.0
Content-Type: multipart/alternative; charset=

그게 문제입니다.

문제의 원인은 다음과 pluggable.php같습니다.

// Set Content-Type and charset
    // If we don't have a content-type from the input headers
    if ( !isset( $content_type ) )
        $content_type = 'text/plain';

    /**
     * Filter the wp_mail() content type.
     *
     * @since 2.3.0
     *
     * @param string $content_type Default wp_mail() content type.
     */
    $content_type = apply_filters( 'wp_mail_content_type', $content_type );

    $phpmailer->ContentType = $content_type;

    // Set whether it's plaintext, depending on $content_type
    if ( 'text/html' == $content_type )
        $phpmailer->IsHTML( true );

    // If we don't have a charset from the input headers
    if ( !isset( $charset ) )
        $charset = get_bloginfo( 'charset' );

    // Set the content-type and charset

    /**
     * Filter the default wp_mail() charset.
     *
     * @since 2.3.0
     *
     * @param string $charset Default email charset.
     */
    $phpmailer->CharSet = apply_filters( 'wp_mail_charset', $charset );

    // Set custom headers
    if ( !empty( $headers ) ) {
        foreach( (array) $headers as $name => $content ) {
            $phpmailer->AddCustomHeader( sprintf( '%1$s: %2$s', $name, $content ) );
        }

        if ( false !== stripos( $content_type, 'multipart' ) && ! empty($boundary) )
            $phpmailer->AddCustomHeader( sprintf( "Content-Type: %s;\n\t boundary=\"%s\"", $content_type, $boundary ) );
    }

    if ( !empty( $attachments ) ) {
        foreach ( $attachments as $attachment ) {
            try {
                $phpmailer->AddAttachment($attachment);
            } catch ( phpmailerException $e ) {
                continue;
            }
        }
    }

잠재적 인 솔루션

당신이 궁금해 그래서, 왜에서이 문제를보고하지 않은 TRAC ? 나는 이미있다 . 놀랍게도, 5 년 전에 같은 문제를 설명 하는 다른 티켓 이 만들어졌습니다.

그것을 직면합시다, 그것은 반 십년되었습니다. 인터넷 시대에는 30에 가깝습니다. 문제는 분명히 버려졌으며 기본적으로 수정되지 않을 것입니다 (여기서 해결하지 않는 한).

여기 에서 솔루션을 제공 하는 훌륭한 스레드를 찾았 지만 그의 솔루션이 작동하는 동안 사용자 정의 $headers세트 가없는 이메일을 차단 합니다.

그것이 우리가 매번 추락하는 곳입니다. 멀티 파트 버전이 제대로 작동하고 일반 설정되지 않은 $headers메시지가 제대로 작동 하지 않거나 구절을 보지 못합니다.

우리가 생각 해낸 해결책은 다음과 같습니다.

if ( false !== stripos( $content_type, 'multipart' ) && ! empty($boundary) ) {
    $phpmailer->ContentType = $content_type . "; boundary=" . $boundary;
}
else {

        $content_type = apply_filters( 'wp_mail_content_type', $content_type );

    $phpmailer->ContentType = $content_type;

    // Set whether it's plaintext, depending on $content_type
    if ( 'text/html' == $content_type )
        $phpmailer->IsHTML( true );

    // If we don't have a charset from the input headers
    if ( !isset( $charset ) )
        $charset = get_bloginfo( 'charset' );
}

// Set the content-type and charset

/**
 * Filter the default wp_mail() charset.
 *
 * @since 2.3.0
 *
 * @param string $charset Default email charset.
 */
$phpmailer->CharSet = apply_filters( 'wp_mail_charset', $charset );

// Set custom headers
if ( !empty( $headers ) ) {
    foreach( (array) $headers as $name => $content ) {
        $phpmailer->AddCustomHeader( sprintf( '%1$s: %2$s', $name, $content ) );
    }

}

예, 핵심 파일을 편집하는 것은 금기이며, 앉으십시오 ... 이것은 필사적 인 수정이며 코어에 대한 수정을 제공하려는 시도가 잘못되었습니다.

수정 사항의 문제점은 새 등록, 댓글, 비밀번호 재설정 등과 같은 기본 이메일이 빈 메시지로 전달된다는 것입니다. 따라서 다중 부분 메시지를 보내지 만 다른 것은 보내지 않는 작동하는 wp_mail () 스크립트가 있습니다.

해야 할 일

여기서 목표 는 핵심 wp_mail () 함수 (사용자 정의 sendmail 함수가 아님 )를 사용하여 일반 (일반 텍스트) 메시지와 멀티 파트 메시지 모두 보내는 방법을 찾는 것 입니다.

이 문제를 해결하려고 할 때 발생하는 가장 큰 문제는 더미 메시지를 보내고 받는지 확인하고 기본적으로 아스피린 상자를 열고 기본적으로 Microsoft에 대한 저주를받는 데 소비하는 시간입니다. gremlin이 불행히도 WordPress 인 동안 IE 문제가 발생합니다.

최신 정보

@bonger가 게시 한 솔루션을 사용하면 $message콘텐츠 유형 키 대체가 포함 된 배열이 될 수 있습니다. 모든 시나리오에서 작동한다는 것을 확인했습니다.

현상금이 다가올 때까지이 질문을 계속 열어두고 문제에 대한 인식을 제고 할 수 있습니다. $message문자열이 될 수 있는 대체 솔루션을 자유롭게 게시하십시오 .


1
는 AS wp_mail()기능은 (그리고 다른 사람, 실패의 핵심 수정)에 대한 좋은 솔루션이없는 플러그 인 (WP - 콘텐츠 / MU-플러그인에) 반드시 사용 플러그인으로 교체를 정의되지 않는 이유는 무엇입니까? $phpmailer->ContentType = $content_type;(엘싱 대신) 설정 후 멀티 파트 / 경계 검사를 이동하지 않는 경우는 ?
bonger 2016 년

@bonger 솔루션을 자세히 설명하는 답변을 작성해 주시겠습니까?
Christine Cooper

1
wp_mailpluggable 이므로 코어를 편집 할 필요가 없습니다 . 플러그인에서 원래 기능을 복사하고 필요에 따라 편집하고 플러그인을 활성화하십시오. WordPress는 원본 기능 대신 편집 된 기능을 사용하므로 코어를 편집 할 필요가 없습니다.
gmazzap

@ChristineCooper 나는 당신이 테스트하는 것이 엄청나게 고통 스럽다는 것을 주저 하면서이 작업을 주저하지만, @ rmccue / @ MattyRob에 의해 trac에서 제안 된 패치 core.trac.wordpress.org/ticket/15448 을 보면 정말 좋은 방법입니다. 내가 그것을 바탕으로 테스트되지 않은 답변을 게시하겠습니다 ...
bonger

2
@ phpmailer에 간단하게 연결하고 $ phpmailer-> AltBody에서 텍스트 본문을 설정하면 @ChristineCooper가 동일한 오류가 발생합니까?
chifliiiii

답변:


15

다음 버전의 wp_mail()티켓은 https://core.trac.wordpress.org/ticket/15448 티켓에 @ rmccue / @ MattyRob 패치가 적용되고 4.2.2로 갱신되어 $message컨텐츠 유형을 포함하는 배열이 될 수 있습니다 키 대체

/**
 * Send mail, similar to PHP's mail
 *
 * A true return value does not automatically mean that the user received the
 * email successfully. It just only means that the method used was able to
 * process the request without any errors.
 *
 * Using the two 'wp_mail_from' and 'wp_mail_from_name' hooks allow from
 * creating a from address like 'Name <email@address.com>' when both are set. If
 * just 'wp_mail_from' is set, then just the email address will be used with no
 * name.
 *
 * The default content type is 'text/plain' which does not allow using HTML.
 * However, you can set the content type of the email by using the
 * 'wp_mail_content_type' filter.
 *
 * If $message is an array, the key of each is used to add as an attachment
 * with the value used as the body. The 'text/plain' element is used as the
 * text version of the body, with the 'text/html' element used as the HTML
 * version of the body. All other types are added as attachments.
 *
 * The default charset is based on the charset used on the blog. The charset can
 * be set using the 'wp_mail_charset' filter.
 *
 * @since 1.2.1
 *
 * @uses PHPMailer
 *
 * @param string|array $to Array or comma-separated list of email addresses to send message.
 * @param string $subject Email subject
 * @param string|array $message Message contents
 * @param string|array $headers Optional. Additional headers.
 * @param string|array $attachments Optional. Files to attach.
 * @return bool Whether the email contents were sent successfully.
 */
function wp_mail( $to, $subject, $message, $headers = '', $attachments = array() ) {
    // Compact the input, apply the filters, and extract them back out

    /**
     * Filter the wp_mail() arguments.
     *
     * @since 2.2.0
     *
     * @param array $args A compacted array of wp_mail() arguments, including the "to" email,
     *                    subject, message, headers, and attachments values.
     */
    $atts = apply_filters( 'wp_mail', compact( 'to', 'subject', 'message', 'headers', 'attachments' ) );

    if ( isset( $atts['to'] ) ) {
        $to = $atts['to'];
    }

    if ( isset( $atts['subject'] ) ) {
        $subject = $atts['subject'];
    }

    if ( isset( $atts['message'] ) ) {
        $message = $atts['message'];
    }

    if ( isset( $atts['headers'] ) ) {
        $headers = $atts['headers'];
    }

    if ( isset( $atts['attachments'] ) ) {
        $attachments = $atts['attachments'];
    }

    if ( ! is_array( $attachments ) ) {
        $attachments = explode( "\n", str_replace( "\r\n", "\n", $attachments ) );
    }
    global $phpmailer;

    // (Re)create it, if it's gone missing
    if ( ! ( $phpmailer instanceof PHPMailer ) ) {
        require_once ABSPATH . WPINC . '/class-phpmailer.php';
        require_once ABSPATH . WPINC . '/class-smtp.php';
        $phpmailer = new PHPMailer( true );
    }

    // Headers
    if ( empty( $headers ) ) {
        $headers = array();
    } else {
        if ( !is_array( $headers ) ) {
            // Explode the headers out, so this function can take both
            // string headers and an array of headers.
            $tempheaders = explode( "\n", str_replace( "\r\n", "\n", $headers ) );
        } else {
            $tempheaders = $headers;
        }
        $headers = array();
        $cc = array();
        $bcc = array();

        // If it's actually got contents
        if ( !empty( $tempheaders ) ) {
            // Iterate through the raw headers
            foreach ( (array) $tempheaders as $header ) {
                if ( strpos($header, ':') === false ) {
                    if ( false !== stripos( $header, 'boundary=' ) ) {
                        $parts = preg_split('/boundary=/i', trim( $header ) );
                        $boundary = trim( str_replace( array( "'", '"' ), '', $parts[1] ) );
                    }
                    continue;
                }
                // Explode them out
                list( $name, $content ) = explode( ':', trim( $header ), 2 );

                // Cleanup crew
                $name    = trim( $name    );
                $content = trim( $content );

                switch ( strtolower( $name ) ) {
                    // Mainly for legacy -- process a From: header if it's there
                    case 'from':
                        $bracket_pos = strpos( $content, '<' );
                        if ( $bracket_pos !== false ) {
                            // Text before the bracketed email is the "From" name.
                            if ( $bracket_pos > 0 ) {
                                $from_name = substr( $content, 0, $bracket_pos - 1 );
                                $from_name = str_replace( '"', '', $from_name );
                                $from_name = trim( $from_name );
                            }

                            $from_email = substr( $content, $bracket_pos + 1 );
                            $from_email = str_replace( '>', '', $from_email );
                            $from_email = trim( $from_email );

                        // Avoid setting an empty $from_email.
                        } elseif ( '' !== trim( $content ) ) {
                            $from_email = trim( $content );
                        }
                        break;
                    case 'content-type':
                        if ( is_array($message) ) {
                            // Multipart email, ignore the content-type header
                            break;
                        }
                        if ( strpos( $content, ';' ) !== false ) {
                            list( $type, $charset_content ) = explode( ';', $content );
                            $content_type = trim( $type );
                            if ( false !== stripos( $charset_content, 'charset=' ) ) {
                                $charset = trim( str_replace( array( 'charset=', '"' ), '', $charset_content ) );
                            } elseif ( false !== stripos( $charset_content, 'boundary=' ) ) {
                                $boundary = trim( str_replace( array( 'BOUNDARY=', 'boundary=', '"' ), '', $charset_content ) );
                                $charset = '';
                            }

                        // Avoid setting an empty $content_type.
                        } elseif ( '' !== trim( $content ) ) {
                            $content_type = trim( $content );
                        }
                        break;
                    case 'cc':
                        $cc = array_merge( (array) $cc, explode( ',', $content ) );
                        break;
                    case 'bcc':
                        $bcc = array_merge( (array) $bcc, explode( ',', $content ) );
                        break;
                    default:
                        // Add it to our grand headers array
                        $headers[trim( $name )] = trim( $content );
                        break;
                }
            }
        }
    }

    // Empty out the values that may be set
    $phpmailer->ClearAllRecipients();
    $phpmailer->ClearAttachments();
    $phpmailer->ClearCustomHeaders();
    $phpmailer->ClearReplyTos();

    $phpmailer->Body= '';
    $phpmailer->AltBody= '';

    // From email and name
    // If we don't have a name from the input headers
    if ( !isset( $from_name ) )
        $from_name = 'WordPress';

    /* If we don't have an email from the input headers default to wordpress@$sitename
     * Some hosts will block outgoing mail from this address if it doesn't exist but
     * there's no easy alternative. Defaulting to admin_email might appear to be another
     * option but some hosts may refuse to relay mail from an unknown domain. See
     * https://core.trac.wordpress.org/ticket/5007.
     */

    if ( !isset( $from_email ) ) {
        // Get the site domain and get rid of www.
        $sitename = strtolower( $_SERVER['SERVER_NAME'] );
        if ( substr( $sitename, 0, 4 ) == 'www.' ) {
            $sitename = substr( $sitename, 4 );
        }

        $from_email = 'wordpress@' . $sitename;
    }

    /**
     * Filter the email address to send from.
     *
     * @since 2.2.0
     *
     * @param string $from_email Email address to send from.
     */
    $phpmailer->From = apply_filters( 'wp_mail_from', $from_email );

    /**
     * Filter the name to associate with the "from" email address.
     *
     * @since 2.3.0
     *
     * @param string $from_name Name associated with the "from" email address.
     */
    $phpmailer->FromName = apply_filters( 'wp_mail_from_name', $from_name );

    // Set destination addresses
    if ( !is_array( $to ) )
        $to = explode( ',', $to );

    foreach ( (array) $to as $recipient ) {
        try {
            // Break $recipient into name and address parts if in the format "Foo <bar@baz.com>"
            $recipient_name = '';
            if( preg_match( '/(.*)<(.+)>/', $recipient, $matches ) ) {
                if ( count( $matches ) == 3 ) {
                    $recipient_name = $matches[1];
                    $recipient = $matches[2];
                }
            }
            $phpmailer->AddAddress( $recipient, $recipient_name);
        } catch ( phpmailerException $e ) {
            continue;
        }
    }

    // If we don't have a charset from the input headers
    if ( !isset( $charset ) )
        $charset = get_bloginfo( 'charset' );

    // Set the content-type and charset

    /**
     * Filter the default wp_mail() charset.
     *
     * @since 2.3.0
     *
     * @param string $charset Default email charset.
     */
    $phpmailer->CharSet = apply_filters( 'wp_mail_charset', $charset );

    // Set mail's subject and body
    $phpmailer->Subject = $subject;

    if ( is_string($message) ) {
        $phpmailer->Body = $message;

        // Set Content-Type and charset
        // If we don't have a content-type from the input headers
        if ( !isset( $content_type ) )
            $content_type = 'text/plain';

        /**
         * Filter the wp_mail() content type.
         *
         * @since 2.3.0
         *
         * @param string $content_type Default wp_mail() content type.
         */
        $content_type = apply_filters( 'wp_mail_content_type', $content_type );

        $phpmailer->ContentType = $content_type;

        // Set whether it's plaintext, depending on $content_type
        if ( 'text/html' == $content_type )
            $phpmailer->IsHTML( true );

        // For backwards compatibility, new multipart emails should use
        // the array style $message. This never really worked well anyway
        if ( false !== stripos( $content_type, 'multipart' ) && ! empty($boundary) )
            $phpmailer->AddCustomHeader( sprintf( "Content-Type: %s;\n\t boundary=\"%s\"", $content_type, $boundary ) );
    }
    elseif ( is_array($message) ) {
        foreach ($message as $type => $bodies) {
            foreach ((array) $bodies as $body) {
                if ($type === 'text/html') {
                    $phpmailer->Body = $body;
                }
                elseif ($type === 'text/plain') {
                    $phpmailer->AltBody = $body;
                }
                else {
                    $phpmailer->AddAttachment($body, '', 'base64', $type);
                }
            }
        }
    }

    // Add any CC and BCC recipients
    if ( !empty( $cc ) ) {
        foreach ( (array) $cc as $recipient ) {
            try {
                // Break $recipient into name and address parts if in the format "Foo <bar@baz.com>"
                $recipient_name = '';
                if( preg_match( '/(.*)<(.+)>/', $recipient, $matches ) ) {
                    if ( count( $matches ) == 3 ) {
                        $recipient_name = $matches[1];
                        $recipient = $matches[2];
                    }
                }
                $phpmailer->AddCc( $recipient, $recipient_name );
            } catch ( phpmailerException $e ) {
                continue;
            }
        }
    }

    if ( !empty( $bcc ) ) {
        foreach ( (array) $bcc as $recipient) {
            try {
                // Break $recipient into name and address parts if in the format "Foo <bar@baz.com>"
                $recipient_name = '';
                if( preg_match( '/(.*)<(.+)>/', $recipient, $matches ) ) {
                    if ( count( $matches ) == 3 ) {
                        $recipient_name = $matches[1];
                        $recipient = $matches[2];
                    }
                }
                $phpmailer->AddBcc( $recipient, $recipient_name );
            } catch ( phpmailerException $e ) {
                continue;
            }
        }
    }

    // Set to use PHP's mail()
    $phpmailer->IsMail();

    // Set custom headers
    if ( !empty( $headers ) ) {
        foreach ( (array) $headers as $name => $content ) {
            $phpmailer->AddCustomHeader( sprintf( '%1$s: %2$s', $name, $content ) );
        }
    }

    if ( !empty( $attachments ) ) {
        foreach ( $attachments as $attachment ) {
            try {
                $phpmailer->AddAttachment($attachment);
            } catch ( phpmailerException $e ) {
                continue;
            }
        }
    }

    /**
     * Fires after PHPMailer is initialized.
     *
     * @since 2.2.0
     *
     * @param PHPMailer &$phpmailer The PHPMailer instance, passed by reference.
     */
    do_action_ref_array( 'phpmailer_init', array( &$phpmailer ) );

    // Send!
    try {
        return $phpmailer->Send();
    } catch ( phpmailerException $e ) {
        return false;
    }
}

예를 들어 "wp-content / mu-plugins / functions.php"파일에 넣으면 WP 버전보다 우선합니다. 헤더를 엉망으로 만들지 않고 사용법이 좋습니다.

// Set $to to an hotmail.com or outlook.com email
$to = "YourEmail@hotmail.com";

$subject = 'wp_mail testing multipart';

$message['text/plain'] = 'Hello world! This is plain text...';
$message['text/html'] = '<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>

<p>Hello World! This is HTML...</p> 

</body>
</html>';

add_filter( 'wp_mail_from', $from_func = function ( $from_email ) { return 'foo@bar.com'; } );
add_filter( 'wp_mail_from_name', $from_name_func = function ( $from_name ) { return 'Foo'; } );

// send email
wp_mail( $to, $subject, $message );

remove_filter( 'wp_mail_from', $from_func );
remove_filter( 'wp_mail_from_name', $from_name_func );

실제 이메일로 테스트하지 않았습니다 ...


플러그인을 설치하고 테스트 코드를 실행하기 위해 이것을 추가했습니다. 효과가있었습니다. 기본 핵심 알림 (새 사용자 알림 등)을 테스트했으며 작동했습니다. 이번 주말에 계속 테스트를 수행하고 플러그인이 어떻게 작동하는지, 기본적으로 모든 것이 작동하는지 확인합니다. 메시지의 원시 데이터를 구체적으로 살펴 보겠습니다. 이 작업은 시간이 많이 걸리지 만 안심하되 완료되면 다시 알려 드리겠습니다. wp_mail ()이 작동하지 않는 시나리오가있는 경우 (그렇지 않은 경우) 알려주십시오. 이 답변에 감사드립니다.
Christine Cooper

좋은 점, 나는 출력을 눈여겨 보았고 좋아 보인다. 사실 패치는 wp_mail이 배열을 전달하는 경우 PHPMailer의 표준 견고한 처리를 사용하도록하고 그렇지 않으면 기본적으로 dodgy WP 항목으로 설정합니다 (이전 버전과의 호환성을 위해) 그래서 그것은 좋을 것입니다 (분명히 여기에서 패치 작성자에게 간다) ... 나는 지금부터 그것을 사용할 것입니다 (그리고 결국에는 복고풍 장착)-html / plain을 모두 사용하는 정보에 대해 감사드립니다. 스팸으로 타르 될 가능성 감소 ...
bonger

1
가능한 모든 시나리오에서 테스트했으며 훌륭하게 작동합니다. 내일 뉴스 레터를 발송할 예정이며 사용자로부터 불만이 접수되는지 확인합니다. 우리가해야 할 사소한 변경 사항은 배열이 db에 삽입 될 때 배열을 살균 / 비 살균하는 것입니다 (cron이 작은 덩어리로 보내는 db의 메시지를 메시지에 표시하십시오). 현상금이 소진 될 때까지이 질문을 계속 열어두고이 문제를 인식 할 수 있도록하겠습니다. 이 패치 나 대안이 코어에 추가되기를 바랍니다. 또는 더 중요한 이유는 무엇입니까? 그들은 무엇을 생각하고 있는가!
Christine Cooper

나는 당신이 연결된 trac 티켓을 업데이트했다는 것을 무작위로 알았습니다. 이 코드에 대한 업데이트입니까? 그렇다면이 답변을 최신 상태로 유지하기 위해 여기에서 답변을 편집하여이 업데이트를 게시 해 주시겠습니까? 대단히 감사합니다.
Christine Cooper

안녕, 아니 그것은 코드가 같은 ... 정확히 (그것의 희망 일부 주목을 받고있는)가 충돌없이 병합되도록 현재 트렁크에 대한 패치의 단지 새로 고침 없었다
bonger

4

이건 정말 모두에서 워드 프레스 버그가 아닙니다, 그것은이다 phpmailer당신이 보면 사용자 정의 헤더를 허용하지에 하나 ... class-phpmailer.php:

public function getMailMIME()
{
    $result = '';
    $ismultipart = true;
    switch ($this->message_type) {
        case 'inline':
            $result .= $this->headerLine('Content-Type', 'multipart/related;');
            $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
            break;
        case 'attach':
        case 'inline_attach':
        case 'alt_attach':
        case 'alt_inline_attach':
            $result .= $this->headerLine('Content-Type', 'multipart/mixed;');
            $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
            break;
        case 'alt':
        case 'alt_inline':
            $result .= $this->headerLine('Content-Type', 'multipart/alternative;');
            $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
            break;
        default:
            // Catches case 'plain': and case '':
            $result .= $this->textLine('Content-Type: ' . $this->ContentType . '; charset=' . $this->CharSet);
            $ismultipart = false;
            break;
    }

문제가되는 기본 사례는 경계가없는 문자 세트로 여분의 헤더 행을 출력하는 것입니다. 유일한 때문에 필터에 의해 내용 유형을 설정하는 것은 그 자체로이 문제를 해결하지 않습니다 alt여기 경우이 설정되어 message_type확인하여 AltBody컨텐츠 유형이 아닌 비어 있지 않습니다.

protected function setMessageType()
{
    $type = array();
    if ($this->alternativeExists()) {
        $type[] = 'alt';
    }
    if ($this->inlineImageExists()) {
        $type[] = 'inline';
    }
    if ($this->attachmentExists()) {
        $type[] = 'attach';
    }
    $this->message_type = implode('_', $type);
    if ($this->message_type == '') {
        $this->message_type = 'plain';
    }
}

public function alternativeExists()
{
    return !empty($this->AltBody);
}

결국 이것이 의미하는 것은 파일이나 인라인 이미지를 첨부 AltBody하거나을 설정하자마자 문제가되는 버그를 무시해야합니다. 또한 컨텐츠 유형 AltBody이로 설정 되 자마자 컨텐츠 유형을 명시 적으로 설정할 필요가 없음을 의미 multipart/alternative합니다 phpmailer.

따라서 간단한 대답은 다음과 같습니다.

add_action('phpmailer_init','wp_mail_set_text_body');
function wp_mail_set_text_body($phpmailer) {
     if (empty($phpmailer->AltBody)) {$phpmailer->AltBody = strip_tags($phpmailer->Body);}
}

그런 다음 헤더를 명시 적으로 설정할 필요가 없습니다. 간단하게 수행 할 수 있습니다.

 $message ='<html>
 <head>
     <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
 </head>
 <body>
     <p>Hello World! This is HTML...</p> 
 </body>
 </html>';

 wp_mail($to,$subject,$message);

불행히도 phpmailer클래스 의 많은 함수와 속성 은 보호되어 있습니다. 그렇지 않으면 유효한 대안은 보내기 전에 후크를 MIMEHeaders통해 속성을 확인하고 무시하는 것 phpmailer_init입니다.


2

방금 사용자가 WordPress 및 Im에서 HTML 템플릿을 사용하여 개발자 버전 에서 간단한 텍스트 대체를 추가 할 수 있는 플러그인출시 했습니다 . 나는 다음을 수행했으며 내 테스트에서 하나의 경계가 추가되고 이메일이 Hotmail에 제대로 도착하는 것을 볼 수 있습니다.

add_action( 'phpmailer_init', array($this->mailer, 'send_email' ) );

/**
* Modify php mailer body with final email
*
* @since 1.0.0
* @param object $phpmailer
*/
function send_email( $phpmailer ) {

    $message            =  $this->add_template( apply_filters( 'mailtpl/email_content', $phpmailer->Body ) );
    $phpmailer->AltBody =  $this->replace_placeholders( strip_tags($phpmailer->Body) );
    $phpmailer->Body    =  $this->replace_placeholders( $message );
}

기본적으로 phpmailer 객체를 수정하고 html 템플릿 안에 메시지를로드하고 Body 속성으로 설정하십시오. 또한 원래 메시지를 가져 와서 AltBody 속성을 설정했습니다.


2

내 간단한 해결책은 다음과 같이 html2text https://github.com/soundasleep/html2text 를 사용 하는 것입니다.

add_action( 'phpmailer_init', 'phpmailer_init' );

//http://wordpress.stackexchange.com/a/191974
//http://stackoverflow.com/a/2564472
function phpmailer_init( $phpmailer )
{
  if( $phpmailer->ContentType == 'text/html' ) {
    $phpmailer->AltBody = Html2Text\Html2Text::convert( $phpmailer->Body );
  }
}

여기 https://gist.github.com/ewake/6c4d22cd856456480bd77b988b5c9e80 도 요점입니다.


2

'phpmailer_init'후크를 사용하여 자신의 'AltBody'를 추가하는 사람은 다음을 수행하십시오.

대체 텍스트 본문은 수동으로 지우지 않는 한 여러 연속 메일이 전송 될 때 재사용 됩니다! WordPress는이 속성이 사용될 것으로 예상하지 않기 때문에 wp_mail ()에서 지우지 않습니다.

이로 인해 수신자가 메일을받지 못할 수 있습니다. 운 좋게도 HTML 사용 가능 메일 클라이언트를 사용하는 대부분의 사람들은 텍스트 버전을 볼 수 없지만 여전히 기본적으로 보안 문제입니다.

운 좋게도 쉽게 고칠 수 있습니다. 이것은 대체 바디 교체 비트를 포함합니다. Html2Text PHP 라이브러리가 필요합니다.

add_filter( 'wp_mail', 'wpse191923_force_phpmailer_reinit_for_multiple_mails', -1 );
function wpse191923_force_phpmailer_reinit_for_multiple_mails( $wp_mail_atts ) {
  global $phpmailer;

  if ( $phpmailer instanceof PHPMailer && $phpmailer->alternativeExists() ) {
    // AltBody property is set, so WordPress must already have used this
    // $phpmailer object just now to send mail, so let's
    // clear the AltBody property
    $phpmailer->AltBody = '';
  }

  // Return untouched atts
  return $wp_mail_atts;
}

add_action( 'phpmailer_init', 'wpse191923_phpmailer_init_altbody', 1000, 1 );
function wpse191923_phpmailer_init_altbody( $phpmailer ) {
  if ( ( $phpmailer->ContentType == 'text/html' ) && empty( $phpmailer->AltBody ) ) {
    if ( ! class_exists( 'Html2Text\Html2Text' ) ) {
      require_once( 'Html2Text.php' );
    }
    if ( ! class_exists( 'Html2Text\Html2TextException' ) ) {
      require_once( 'Html2TextException.php' );
    }
    $phpmailer->AltBody = Html2Text\Html2Text::convert( $phpmailer->Body );
  }
}

이 문제를 해결하기 위해 수정 한 WP 플러그인의 요지는 다음과 같습니다. https://gist.github.com/youri--/c4618740b7c50c549314eaebc9f78661

불행히도 나는 아직 언급 할 충분한 담당자가 없기 때문에 위에서 언급 한 후크를 사용하여 다른 솔루션에 대해서는 언급 할 수 없습니다.


1

이것은 초기 게시물에 대한 정확한 답변이 아닐 수도 있지만 대체 본문 설정과 관련하여 여기에 제공된 솔루션 중 일부에 대한 대안입니다

본질적으로, 나는 일부 변환 / 스트립 태그 및 기타에 의존하는 대신 HTML 부분에 별도의 altbody (예 : 일반 텍스트)를 추가로 설정하고 싶었습니다. 그래서 나는 잘 작동하는 것처럼 보였습니다.

/* setting the message parts for wp_mail()*/
$markup = array();
$markup['html'] = '<html>some html</html>';
$markup['plaintext'] = 'some plaintext';
/* message we are sending */    
$message = maybe_serialize($markup);


/* setting alt body distinctly */
add_action('phpmailer_init', array($this, 'set_alt_mail_body'));

function set_alt_mail_body($phpmailer){
    if( $phpmailer->ContentType == 'text/html' ) {
        $body_parts = maybe_unserialize($phpmailer->Body);

        if(!empty($body_parts['html'])){
            $phpmailer->MsgHTML($body_parts['html']);
        }

        if(!empty($body_parts['plaintext'])){
            $phpmailer->AltBody = $body_parts['plaintext'];
        }
    }   
}

0

Wordpress 코어에서 코드 충돌을 일으키지 않으려면 대안 또는 가장 간단한 해결책은 phpmailer_init에서 실제 메일을 보내기 전에 수행 할 작업을 추가 하는 것 wp_mail입니다. 설명을 단순화하려면 아래 코드 예제를 참조하십시오.

<?php 

$to = '';
$subject = '';
$from = '';
$body = 'The text html content, <html>...';

$headers = "FROM: {$from}";

add_action( 'phpmailer_init', function ( $phpmailer ) {
    $phpmailer->AltBody = 'The text plain content of your original text html content.';
} );

wp_mail($to, $subject, $body, $headers);

PHPMailer 클래스 AltBody속성에 내용을 추가 하면 기본 내용 유형이 자동으로 설정됩니다 multipart/alternative.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.