Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
17 / 17
100.00% covered (success)
100.00%
3 / 3
CRAP
100.00% covered (success)
100.00%
1 / 1
WP_Mail
100.00% covered (success)
100.00%
17 / 17
100.00% covered (success)
100.00%
3 / 3
7
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 remove_bounced_destination_email_addresses
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
4
 cancel_sending_email_when_all_addresses_removed
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2/**
3 * Prevent sending email to bounced user.
4 * Log emails.
5 *
6 * @package brianhenryie/bh-wp-aws-ses-bounce-handler
7 */
8
9namespace BrianHenryIE\AWS_SES_Bounce_Handler\WP_Includes;
10
11use Psr\Log\LoggerInterface;
12use Psr\Log\LoggerAwareTrait;
13use WP_User;
14
15/**
16 * Filter wp_mail to remove the to: address, log to the PSR logger.
17 */
18class WP_Mail {
19    use LoggerAwareTrait;
20
21    /**
22     * Constructor.
23     *
24     * @param LoggerInterface $logger A PSR logger.
25     */
26    public function __construct( LoggerInterface $logger ) {
27        $this->setLogger( $logger );
28    }
29
30    /**
31     * Check is the destination address a bounced email address, remove the email address from the to: field if so.
32     * Log to the PSR logger when it happens.
33     *
34     * @hooked wp_mail
35     *
36     * @param array{to:string|string[],subject:string} $wp_mail_atts The to, subject, message, headers, attachments of the email being sent.
37     *
38     * @return array{to:string|string[],subject:string}
39     * @see wp_mail()
40     */
41    public function remove_bounced_destination_email_addresses( array $wp_mail_atts ): array {
42
43        $emails = (array) $wp_mail_atts['to'];
44
45        $valid_emails = array();
46
47        foreach ( $emails as $email ) {
48            $wp_user = get_user_by( 'email', $email );
49
50            if ( $wp_user instanceof WP_User ) {
51
52                $caps  = array_keys( $wp_user->caps );
53                $roles = $wp_user->roles;
54
55                if ( in_array( 'bounced_email', array_merge( $roles, $caps ), true ) ) {
56
57                    $this->logger->notice( "Attempting to email `wp_user:{$wp_user->ID}` with previously bounced email address. Email suppressed: {$wp_mail_atts['subject']}" );
58
59                    continue;
60                }
61            }
62
63            $valid_emails[] = $email;
64        }
65
66        $wp_mail_atts['to'] = $valid_emails;
67
68        return $wp_mail_atts;
69    }
70
71    /**
72     * If the above function removed all the to: addresses, cancel sending the email on the `pre_wp_mail` hook. This
73     * hook is not implemented in wp-offload-ses, hence the address checking/removal is done above, but is implemented
74     * in fluent-smtp, and it should be used where possible.
75     *
76     * @hooked pre_wp_mail
77     * @see wp_mail()
78     *
79     * @param ?mixed                    $cancel Any non-null value will cancel sending the email.
80     * @param array{to:string|string[]} $wp_mail_atts The to, subject, message, headers, attachments of the email being sent.
81     *
82     * @return ?mixed
83     */
84    public function cancel_sending_email_when_all_addresses_removed( $cancel, array $wp_mail_atts ) {
85
86        if ( empty( $wp_mail_atts['to'] ) ) {
87            return false;
88        }
89
90        return $cancel;
91    }
92}