PluginProbe ʕ •ᴥ•ʔ
WP Mail SMTP by WPForms – The Most Popular SMTP and Email Log Plugin / 4.3.0
WP Mail SMTP by WPForms – The Most Popular SMTP and Email Log Plugin v4.3.0
0.9.6 1.0.0 1.0.1 1.0.2 1.1.0 1.2.0 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.3.0 1.3.1 1.3.2 1.3.3 1.4.0 1.4.1 1.4.2 1.5.0 1.5.1 1.5.2 1.6.0 1.6.2 1.7.0 1.7.1 1.8.0 1.8.1 1.9.0 2.0.0 2.0.1 2.1.1 2.2.1 2.3.1 2.4.0 2.5.0 2.5.1 2.6.0 2.7.0 2.8.0 2.9.0 3.0.1 3.0.2 3.0.3 3.1.0 3.10.0 3.11.0 3.11.1 3.2.0 3.2.1 3.3.0 3.4.0 3.5.0 3.5.1 3.5.2 3.6.1 3.7.0 3.8.0 3.8.2 3.9.0 4.0.1 4.1.0 4.1.1 4.2.0 4.3.0 4.4.0 4.5.0 4.6.0 4.7.0 4.7.1 4.8.0 trunk 0.10.0 0.10.1 0.11.1 0.11.2 0.3.1 0.3.2 0.4 0.4.1 0.4.2 0.5.0 0.5.1 0.5.2 0.6 0.7 0.8 0.8.2 0.8.3 0.8.4 0.8.5 0.8.6 0.8.7 0.9.0 0.9.1 0.9.2 0.9.3 0.9.4 0.9.5
wp-mail-smtp / src / MailCatcherTrait.php
wp-mail-smtp / src Last commit date
Admin 1 year ago Compatibility 1 year ago Helpers 1 year ago Providers 1 year ago Queue 1 year ago Reports 1 year ago Tasks 1 year ago UsageTracking 1 year ago AbstractConnection.php 1 year ago Conflicts.php 1 year ago Connect.php 1 year ago Connection.php 1 year ago ConnectionInterface.php 1 year ago ConnectionsManager.php 1 year ago Core.php 1 year ago DBRepair.php 1 year ago Debug.php 1 year ago Geo.php 1 year ago MailCatcher.php 1 year ago MailCatcherInterface.php 1 year ago MailCatcherTrait.php 1 year ago MailCatcherV6.php 1 year ago Migration.php 1 year ago MigrationAbstract.php 1 year ago Migrations.php 1 year ago OptimizedEmailSending.php 1 year ago Options.php 1 year ago Processor.php 1 year ago SiteHealth.php 1 year ago Upgrade.php 1 year ago Uploads.php 1 year ago WP.php 1 year ago WPMailArgs.php 1 year ago WPMailInitiator.php 1 year ago
MailCatcherTrait.php
549 lines
1 <?php
2
3 namespace WPMailSMTP;
4
5 use Exception;
6 use WPMailSMTP\Admin\DebugEvents\DebugEvents;
7 use WPMailSMTP\Providers\MailerAbstract;
8
9 /**
10 * Trait MailCatcherTrait.
11 *
12 * @since 3.7.0
13 */
14 trait MailCatcherTrait {
15
16 /**
17 * Debug output buffer.
18 *
19 * @since 3.3.0
20 *
21 * @var array
22 */
23 private $debug_output_buffer = [];
24
25 /**
26 * Debug event ID.
27 *
28 * @since 3.5.0
29 *
30 * @var int
31 */
32 private $debug_event_id = false;
33
34 /**
35 * Whether the current email is a test email.
36 *
37 * @since 3.5.0
38 *
39 * @var bool
40 */
41 private $is_test_email = false;
42
43 /**
44 * Whether the current email is a Setup Wizard test email.
45 *
46 * @since 3.5.0
47 *
48 * @var bool
49 */
50 private $is_setup_wizard_test_email = false;
51
52 /**
53 * Whether the current email is blocked to be sent.
54 *
55 * @since 3.8.0
56 *
57 * @var bool
58 */
59 private $is_emailing_blocked = false;
60
61 /**
62 * Holds the most recent error message.
63 *
64 * @since 3.7.0
65 *
66 * @var string
67 */
68 protected $latest_error = '';
69
70 /**
71 * Modify the default send() behaviour.
72 * For those mailers, that relies on PHPMailer class - call it directly.
73 * For others - init the correct provider and process it.
74 *
75 * @since 1.0.0
76 * @since 1.4.0 Process "Do Not Send" option, but always allow test email.
77 *
78 * @throws Exception When sending via PhpMailer fails for some reason.
79 *
80 * @return bool
81 */
82 public function send() { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh
83
84 // Reset email related variables.
85 $this->debug_event_id = false;
86 $this->is_test_email = false;
87 $this->is_setup_wizard_test_email = false;
88 $this->is_emailing_blocked = false;
89 $this->latest_error = '';
90
91 if ( wp_mail_smtp()->is_blocked() ) {
92 $this->is_emailing_blocked = true;
93 }
94
95 // Always allow a test email - check for the specific header.
96 foreach ( (array) $this->getCustomHeaders() as $header ) {
97 if (
98 ! empty( $header[0] ) &&
99 ! empty( $header[1] ) &&
100 $header[0] === 'X-Mailer-Type'
101 ) {
102 if ( trim( $header[1] ) === 'WPMailSMTP/Admin/Test' ) {
103 $this->is_emailing_blocked = false;
104 $this->is_test_email = true;
105 } elseif ( trim( $header[1] ) === 'WPMailSMTP/Admin/SetupWizard/Test' ) {
106 $this->is_setup_wizard_test_email = true;
107 }
108 }
109 }
110
111 // Do not send emails if admin desired that.
112 if ( $this->is_emailing_blocked ) {
113 return false;
114 }
115
116 // If it's not a test email,
117 // check if the email should be enqueued
118 // instead of being sent immediately.
119 if ( ! $this->is_test_email && ! $this->is_setup_wizard_test_email ) {
120
121 /**
122 * Filters whether an email should be enqueued or sent immediately.
123 *
124 * @since 4.0.0
125 *
126 * @param bool $should_enqueue Whether to enqueue an email, or send it.
127 * @param array $wp_mail_args Original arguments of the `wp_mail` call.
128 */
129 $should_enqueue_email = apply_filters(
130 'wp_mail_smtp_mail_catcher_send_enqueue_email',
131 false,
132 wp_mail_smtp()->get_processor()->get_filtered_wp_mail_args()
133 );
134
135 $queue = wp_mail_smtp()->get_queue();
136
137 // If we should enqueue the email,
138 // and the email has been enqueued,
139 // bail.
140 if ( $should_enqueue_email && $queue->enqueue_email() ) {
141 return true;
142 }
143 }
144
145 $connection = wp_mail_smtp()->get_connections_manager()->get_mail_connection();
146 $mailer_slug = $connection->get_mailer_slug();
147
148 // Define a custom header, that will be used to identify the plugin and the mailer.
149 // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
150 $this->XMailer = 'WPMailSMTP/Mailer/' . $mailer_slug . ' ' . WPMS_PLUGIN_VER;
151
152 // Use the default PHPMailer, as we inject our settings there for certain providers.
153 if (
154 $mailer_slug === 'mail' ||
155 $mailer_slug === 'smtp' ||
156 $mailer_slug === 'pepipost'
157 ) {
158 return $this->smtp_send( $connection );
159 } else {
160 return $this->api_send( $connection );
161 }
162 }
163
164 /**
165 * Send email via SMTP.
166 *
167 * @since 4.0.0
168 *
169 * @param ConnectionInterface $connection The connection object.
170 *
171 * @throws Exception When sending via PhpMailer fails for some reason.
172 *
173 * @return bool
174 */
175 private function smtp_send( $connection ) { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh
176
177 $mailer_slug = $connection->get_mailer_slug();
178
179 // phpcs:disable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
180 try {
181 if ( DebugEvents::is_debug_enabled() && ! $this->is_test_email ) {
182 $this->SMTPDebug = 3;
183 $this->Debugoutput = [ $this, 'debug_output_callback' ];
184 }
185
186 /**
187 * Fires before email pre send via SMTP.
188 *
189 * Allow to hook early to catch any early failed emails.
190 *
191 * @since 2.9.0
192 *
193 * @param MailCatcherInterface $mailcatcher The MailCatcher object.
194 */
195 do_action( 'wp_mail_smtp_mailcatcher_smtp_pre_send_before', $this );
196
197 // Prepare all the headers.
198 if ( ! $this->preSend() ) {
199 $this->throw_exception( $this->ErrorInfo );
200 }
201
202 /**
203 * Fires before email send via SMTP.
204 *
205 * Allow to hook after all the preparation before the actual sending.
206 *
207 * @since 2.9.0
208 *
209 * @param MailCatcherInterface $mailcatcher The MailCatcher object.
210 */
211 do_action( 'wp_mail_smtp_mailcatcher_smtp_send_before', $this );
212
213 if ( ! $this->postSend() ) {
214 $this->throw_exception( $this->ErrorInfo );
215 }
216
217 DebugEvents::add_debug(
218 esc_html__( 'An email request was sent.', 'wp-mail-smtp' )
219 );
220
221 return true;
222 } catch ( Exception $e ) {
223 $this->mailHeader = '';
224
225 // We need this to append SMTP error to the `PHPMailer::ErrorInfo` property.
226 $this->setError( $e->getMessage() );
227
228 // Set the debug error, but not for default PHP mailer.
229 if ( $mailer_slug !== 'mail' ) {
230 $error_message = 'Mailer: ' . esc_html( wp_mail_smtp()->get_providers()->get_options( $mailer_slug )->get_title() ) . "\r\n" . $this->ErrorInfo;
231
232 $this->debug_event_id = Debug::set( $error_message );
233 $this->latest_error = $error_message;
234
235 if ( DebugEvents::is_debug_enabled() && ! empty( $this->debug_output_buffer ) ) {
236 $debug_message = $error_message . "\r\n" . esc_html__( 'Debug Output:', 'wp-mail-smtp' ) . "\r\n";
237 $debug_message .= implode( "\r\n", $this->debug_output_buffer );
238
239 $this->debug_event_id = DebugEvents::add_debug( $debug_message );
240 }
241 }
242
243 /**
244 * Fires after email sent failure via SMTP.
245 *
246 * @since 3.5.0
247 *
248 * @param string $error_message Error message.
249 * @param MailCatcherInterface $mailcatcher The MailCatcher object.
250 * @param string $mailer_slug Current mailer name.
251 */
252 do_action( 'wp_mail_smtp_mailcatcher_send_failed', $this->ErrorInfo, $this, $mailer_slug );
253
254 if ( $this->exceptions ) {
255 throw $e;
256 }
257
258 return false;
259 } finally {
260
261 // Clear debug output buffer.
262 $this->debug_output_buffer = [];
263 }
264 // phpcs:enable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
265 }
266
267 /**
268 * Send email via API integration.
269 *
270 * @since 4.0.0
271 *
272 * @param ConnectionInterface $connection The connection object.
273 *
274 * @throws Exception When sending fails for some reason.
275 *
276 * @return bool
277 */
278 private function api_send( $connection ) { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh
279
280 $mailer_slug = $connection->get_mailer_slug();
281
282 // phpcs:disable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
283 try {
284 // We need this so that the \PHPMailer class will correctly prepare all the headers.
285 $this->Mailer = 'mail';
286
287 /**
288 * Fires before email pre send.
289 *
290 * Allow to hook early to catch any early failed emails.
291 *
292 * @since 2.9.0
293 *
294 * @param MailCatcherInterface $mailcatcher The MailCatcher object.
295 */
296 do_action( 'wp_mail_smtp_mailcatcher_pre_send_before', $this );
297
298 // Prepare everything (including the message) for sending.
299 if ( ! $this->preSend() ) {
300 $this->throw_exception( $this->ErrorInfo );
301 }
302
303 $mailer = wp_mail_smtp()->get_providers()->get_mailer( $mailer_slug, $this, $connection );
304
305 if ( ! $mailer ) {
306 $this->throw_exception( esc_html__( 'The selected mailer not found.', 'wp-mail-smtp' ) );
307 }
308
309 if ( ! $mailer->is_php_compatible() ) {
310 $this->throw_exception( esc_html__( 'The selected mailer is not compatible with your PHP version.', 'wp-mail-smtp' ) );
311 }
312
313 /**
314 * Fires before email send.
315 *
316 * Allows to hook after all the preparation before the actual sending.
317 *
318 * @since 3.3.0
319 *
320 * @param MailerAbstract $mailer The Mailer object.
321 */
322 do_action( 'wp_mail_smtp_mailcatcher_send_before', $mailer );
323
324 /*
325 * Send the actual email.
326 * We reuse everything, that was preprocessed for usage in \PHPMailer.
327 */
328 $mailer->send();
329
330 $is_sent = $mailer->is_email_sent();
331
332 /**
333 * Fires after email send.
334 *
335 * Allow to perform any actions with the data.
336 *
337 * @since 3.5.0
338 *
339 * @param MailerAbstract $mailer The Mailer object.
340 * @param MailCatcherInterface $mailcatcher The MailCatcher object.
341 */
342 do_action( 'wp_mail_smtp_mailcatcher_send_after', $mailer, $this );
343
344 if ( $is_sent !== true ) {
345 $this->throw_exception( $mailer->get_response_error() );
346 }
347
348 // Clear debug messages if email is successfully sent.
349 Debug::clear();
350
351 return true;
352 } catch ( Exception $e ) {
353 // Add mailer to the beginning and save to display later.
354 $message = 'Mailer: ' . esc_html( wp_mail_smtp()->get_providers()->get_options( $mailer_slug )->get_title() ) . "\r\n";
355
356 $conflicts = new Conflicts();
357
358 if ( $conflicts->is_detected() ) {
359 $conflict_plugin_names = implode( ', ', $conflicts->get_all_conflict_names() );
360 $message .= 'Conflicts: ' . esc_html( $conflict_plugin_names ) . "\r\n";
361 }
362
363 $error_message = $message . $e->getMessage();
364 $this->debug_event_id = Debug::set( $error_message );
365 $this->latest_error = $error_message;
366
367 /**
368 * Fires after email sent failure.
369 *
370 * @since 3.5.0
371 *
372 * @param string $error_message Error message.
373 * @param MailCatcherInterface $mailcatcher The MailCatcher object.
374 * @param string $mailer_slug Current mailer name.
375 */
376 do_action( 'wp_mail_smtp_mailcatcher_send_failed', $e->getMessage(), $this, $mailer_slug );
377
378 if ( $this->exceptions ) {
379 throw $e;
380 }
381
382 return false;
383 }
384 // phpcs:enable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
385 }
386
387 /**
388 * Create a unique ID to use for multipart email boundaries.
389 *
390 * @since 2.4.0
391 *
392 * @return string
393 */
394 public function generate_id() {
395
396 return $this->generateId();
397 }
398
399 /**
400 * Debug output callback.
401 * Save debugging info to buffer array.
402 *
403 * @since 3.3.0
404 *
405 * @param string $str Message.
406 * @param int $level Debug level.
407 */
408 public function debug_output_callback( $str, $level ) {
409
410 /*
411 * Filter out all higher levels than 3.
412 * SMTPDebug level 3 is commands, data and connection status.
413 */
414 if ( $level > 3 ) {
415 return;
416 }
417
418 $this->debug_output_buffer[] = trim( $str, "\r\n" );
419 }
420
421 /**
422 * Get debug event ID.
423 *
424 * @since 3.5.0
425 *
426 * @return bool|int
427 */
428 public function get_debug_event_id() {
429
430 return $this->debug_event_id;
431 }
432
433 /**
434 * Whether the current email is a test email.
435 *
436 * @since 3.5.0
437 *
438 * @return bool
439 */
440 public function is_test_email() {
441
442 return $this->is_test_email;
443 }
444
445 /**
446 * Whether the current email is a Setup Wizard test email.
447 *
448 * @since 3.5.0
449 *
450 * @return bool
451 */
452 public function is_setup_wizard_test_email() {
453
454 return $this->is_setup_wizard_test_email;
455 }
456
457 /**
458 * Whether the current email is blocked to be sent.
459 *
460 * @since 3.8.0
461 *
462 * @return bool
463 */
464 public function is_emailing_blocked() {
465
466 return $this->is_emailing_blocked;
467 }
468
469 /**
470 * Return the list of properties representing
471 * this class' state.
472 *
473 * @since 4.0.0
474 *
475 * @return array State of this class.
476 */
477 private function get_state_properties() {
478
479 return [
480 'CharSet',
481 'ContentType',
482 'Encoding',
483 'CustomHeader',
484 'Subject',
485 'Body',
486 'AltBody',
487 'ReplyTo',
488 'to',
489 'cc',
490 'bcc',
491 'attachment',
492 ];
493 }
494
495 /**
496 * Return an array of relevant properties.
497 *
498 * @since 4.0.0
499 *
500 * @return array State of this class.
501 */
502 public function get_state() {
503
504 $state = [];
505
506 foreach ( $this->get_state_properties() as $property ) {
507 $state[ $property ] = $this->{$property};
508 }
509
510 return $state;
511 }
512
513 /**
514 * Set properties from a provided array of data.
515 *
516 * @since 4.0.0
517 *
518 * @param array $state Array of properties to apply.
519 */
520 public function set_state( $state ) { // phpcs:ignore Generic.Metrics.NestingLevel.MaxExceeded
521
522 // Filter out non-allowed properties.
523 $state = array_intersect_key(
524 $state,
525 array_flip( $this->get_state_properties() )
526 );
527
528 foreach ( $state as $property => $value ) {
529 if ( $property !== 'attachment' ) {
530 $this->{$property} = $value;
531 } else {
532 // Handle potential I/O exceptions
533 // in PHPMailer when attaching files.
534 $this->clearAttachments();
535
536 foreach ( $state['attachment'] as $attachment ) {
537 [ $path, , $name ] = $attachment;
538
539 try {
540 $this->addAttachment( $path, $name );
541 } catch ( Exception $e ) {
542 continue;
543 }
544 }
545 }
546 }
547 }
548 }
549