From d6f7a348c04252d65a405feeefb922142284d96c Mon Sep 17 00:00:00 2001 From: Rebecca Hum <rebecca.hum@automattic.com> Date: Wed, 25 Oct 2023 15:58:02 -0600 Subject: [PATCH] CANTINA-953: Add `vip_block_wp_mail` filter (#4975) * Add vip_block_wp_mail filter * Adds tests Add tests * PHP 8.2 fix for dynamic property PHP 8.2 fix for dynamic property --- tests/mock-constants.php | 16 ++++++ tests/test-vip-mail.php | 107 +++++++++++++++++++++++++++++++++++++-- vip-mail.php | 19 ++++++- 3 files changed, 137 insertions(+), 5 deletions(-) diff --git a/tests/mock-constants.php b/tests/mock-constants.php index 835f70083a..88a169aefc 100644 --- a/tests/mock-constants.php +++ b/tests/mock-constants.php @@ -200,3 +200,19 @@ function constant( $constant ) { return Constant_Mocker::constant( $constant ); } } + +namespace Automattic\VIP\Mail { + use Automattic\Test\Constant_Mocker; + + function define( $constant, $value ) { + return Constant_Mocker::define( $constant, $value ); + } + + function defined( $constant ) { + return Constant_Mocker::defined( $constant ); + } + + function constant( $constant ) { + return Constant_Mocker::constant( $constant ); + } +} diff --git a/tests/test-vip-mail.php b/tests/test-vip-mail.php index d73a4d0561..535e17f5d3 100644 --- a/tests/test-vip-mail.php +++ b/tests/test-vip-mail.php @@ -1,19 +1,28 @@ <?php +namespace Automattic\VIP\Mail; use PHPMailer\PHPMailer\PHPMailer; +use Automattic\Test\Constant_Mocker; // phpcs:disable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase -- PHPMailer does not follow the conventions // phpcs:disable WordPressVIPMinimum.Functions.RestrictedFunctions.wp_mail_wp_mail -- we are testing it -class VIP_Mail_Test extends WP_UnitTestCase { +class VIP_Mail_Test extends \WP_UnitTestCase { public function setUp(): void { parent::setUp(); reset_phpmailer_instance(); - if ( ! defined( 'USE_VIP_PHPMAILER' ) ) { - define( 'USE_VIP_PHPMAILER', true ); + if ( ! Constant_Mocker::defined( 'USE_VIP_PHPMAILER' ) ) { + Constant_Mocker::define( 'USE_VIP_PHPMAILER', true ); } } + public function tearDown(): void { + parent::tearDown(); + + Constant_Mocker::clear(); + remove_all_filters( 'vip_block_wp_mail' ); + } + public function test__all_smtp_servers__not_array() { $GLOBALS['all_smtp_servers'] = false; @@ -67,7 +76,7 @@ public function test__has_tracking_header_with_key() { } public function test_load_VIP_PHPMailer() { - $this->assertTrue( class_exists( 'VIP_PHPMailer', false ) ); + $this->assertTrue( class_exists( '\Automattic\VIP\Mail\VIP_PHPMailer', false ) ); } /** @@ -132,4 +141,94 @@ public function test_filter_removal(): void { self::assertEquals( $expected, $actual ); } + + public function test_noop_mailer__filter_only() { + set_error_handler( // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_set_error_handler + static function ( $errno, $errstr ) { + restore_error_handler(); + throw new \Exception( $errstr, $errno ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped + }, + E_ALL + ); + + add_filter( 'vip_block_wp_mail', '__return_true' ); + + $this->expectException( \Exception::class ); + $this->expectExceptionMessage( 'VIP_Noop_Mailer::send: skipped sending email with subject `Test` to test@example.com' ); + + wp_mail( 'test@example.com', 'Test', 'Should not be sent' ); + + restore_error_handler(); + } + + public function test_noop_mailer__constant_true_filter_false() { + set_error_handler( // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_set_error_handler + static function ( $errno, $errstr ) { + restore_error_handler(); + throw new \Exception( $errstr, $errno ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped + }, + E_ALL + ); + + Constant_Mocker::define( 'VIP_BLOCK_WP_MAIL', true ); + add_filter( 'vip_block_wp_mail', '__return_false' ); + + $this->expectException( \Exception::class ); + $this->expectExceptionMessage( 'VIP_Noop_Mailer::send: skipped sending email with subject `Test` to test@example.com' ); + + wp_mail( 'test@example.com', 'Test', 'Should not be sent' ); + + restore_error_handler(); + } + + public function test_noop_mailer__constant_only() { + set_error_handler( // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_set_error_handler + static function ( $errno, $errstr ) { + restore_error_handler(); + throw new \Exception( $errstr, $errno ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped + }, + E_ALL + ); + + Constant_Mocker::define( 'VIP_BLOCK_WP_MAIL', true ); + + $this->expectException( \Exception::class ); + $this->expectExceptionMessage( 'VIP_Noop_Mailer::send: skipped sending email with subject `Test` to test@example.com' ); + + wp_mail( 'test@example.com', 'Test', 'Should not be sent' ); + + restore_error_handler(); + } + + public function test_noop_mailer__constant_and_filter_false() { + Constant_Mocker::define( 'VIP_BLOCK_WP_MAIL', false ); + add_filter( 'vip_block_wp_mail', '__return_false' ); + + $body = 'Testing should send'; + wp_mail( 'test@example.com', 'Test', $body ); + + $mailer = tests_retrieve_phpmailer_instance(); + + $this->assertEquals( $body, $mailer->Body ); + } + + public function test_noop_mailer__constant_false_filter_true() { + set_error_handler( // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_set_error_handler + static function ( $errno, $errstr ) { + restore_error_handler(); + throw new \Exception( $errstr, $errno ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped + }, + E_ALL + ); + + Constant_Mocker::define( 'VIP_BLOCK_WP_MAIL', false ); + add_filter( 'vip_block_wp_mail', '__return_true' ); + + $this->expectException( \Exception::class ); + $this->expectExceptionMessage( 'VIP_Noop_Mailer::send: skipped sending email with subject `Test` to test@example.com' ); + + wp_mail( 'test@example.com', 'Test', 'Should not be sent' ); + + restore_error_handler(); + } } diff --git a/vip-mail.php b/vip-mail.php index 14a030cc7f..9726374494 100644 --- a/vip-mail.php +++ b/vip-mail.php @@ -10,6 +10,7 @@ // phpcs:disable Generic.Files.OneObjectStructurePerFile.MultipleFound -- needs refactoring // phpcs:disable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase -- PHPMailer does not follow the conventions +namespace Automattic\VIP\Mail; use PHPMailer\PHPMailer\PHPMailer; @@ -45,6 +46,17 @@ protected static function isPermittedPath( $path ) { } class VIP_Noop_Mailer { + + /** + * @var string + */ + public $subject; + + /** + * @var string + */ + public $recipients; + public function __construct( $phpmailer ) { $this->subject = $phpmailer->Subject ?? '[No Subject]'; $this->recipients = implode( ', ', array_keys( $phpmailer->getAllRecipientAddresses() ) ); @@ -80,7 +92,12 @@ private function __construct() { * @param PHPMailer $phpmailer */ public function phpmailer_init( &$phpmailer ): void { - if ( defined( 'VIP_BLOCK_WP_MAIL' ) && true === constant( 'VIP_BLOCK_WP_MAIL' ) ) { + if ( defined( 'VIP_BLOCK_WP_MAIL' ) && true === constant( 'VIP_BLOCK_WP_MAIL' ) ) { // Constant will take precedence over filter + $phpmailer = new VIP_Noop_Mailer( $phpmailer ); + return; + } + + if ( true === apply_filters( 'vip_block_wp_mail', false ) ) { $phpmailer = new VIP_Noop_Mailer( $phpmailer ); return; }