PluginProbe ʕ •ᴥ•ʔ
WP Mail SMTP by WPForms – The Most Popular SMTP and Email Log Plugin / 4.7.0
WP Mail SMTP by WPForms – The Most Popular SMTP and Email Log Plugin v4.7.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 / WP.php
wp-mail-smtp / src Last commit date
Admin 6 months ago Compatibility 6 months ago Helpers 6 months ago Providers 6 months ago Queue 6 months ago Reports 6 months ago Tasks 6 months ago UsageTracking 6 months ago AbstractConnection.php 6 months ago Conflicts.php 6 months ago Connect.php 6 months ago Connection.php 6 months ago ConnectionInterface.php 6 months ago ConnectionsManager.php 6 months ago Core.php 6 months ago DBRepair.php 6 months ago Debug.php 6 months ago Geo.php 6 months ago MailCatcher.php 6 months ago MailCatcherInterface.php 6 months ago MailCatcherTrait.php 6 months ago MailCatcherV6.php 6 months ago Migration.php 6 months ago MigrationAbstract.php 6 months ago Migrations.php 6 months ago OptimizedEmailSending.php 6 months ago Options.php 6 months ago Processor.php 6 months ago SiteHealth.php 6 months ago Upgrade.php 6 months ago Uploads.php 6 months ago WP.php 6 months ago WPMailArgs.php 6 months ago WPMailInitiator.php 6 months ago
WP.php
895 lines
1 <?php
2
3 namespace WPMailSMTP;
4
5 use WPMailSMTP\Helpers\Helpers;
6
7 /**
8 * Class WP provides WordPress shortcuts.
9 *
10 * @since 1.0.0
11 */
12 class WP {
13
14 /**
15 * The "queue" of notices.
16 *
17 * @since 1.0.0
18 *
19 * @var array
20 */
21 protected static $admin_notices = [];
22
23 /**
24 * CSS class for a success notice.
25 *
26 * @since 1.0.0
27 *
28 * @var string
29 */
30 const ADMIN_NOTICE_SUCCESS = 'notice-success';
31
32 /**
33 * CSS class for an error notice.
34 *
35 * @since 1.0.0
36 *
37 * @var string
38 */
39 const ADMIN_NOTICE_ERROR = 'notice-error';
40
41 /**
42 * CSS class for an info notice.
43 *
44 * @since 1.0.0
45 *
46 * @var string
47 */
48 const ADMIN_NOTICE_INFO = 'notice-info';
49
50 /**
51 * CSS class for a warning notice.
52 *
53 * @since 1.0.0
54 *
55 * @var string
56 */
57 const ADMIN_NOTICE_WARNING = 'notice-warning';
58
59 /**
60 * Cross-platform line break.
61 *
62 * @since 3.4.0
63 *
64 * @var string
65 */
66 const EOL = "\r\n";
67
68 /**
69 * True if WP is processing an AJAX call.
70 *
71 * @since 1.0.0
72 *
73 * @return bool
74 */
75 public static function is_doing_ajax() {
76
77 if ( function_exists( 'wp_doing_ajax' ) ) {
78 return wp_doing_ajax();
79 }
80
81 return ( defined( 'DOING_AJAX' ) && DOING_AJAX );
82 }
83
84 /**
85 * True if I am in the Admin Panel, not doing AJAX.
86 *
87 * @since 1.0.0
88 *
89 * @return bool
90 */
91 public static function in_wp_admin() {
92
93 return ( is_admin() && ! self::is_doing_ajax() );
94 }
95
96 /**
97 * Add a notice to the "queue of notices".
98 *
99 * @since 1.0.0
100 * @since 1.5.0 Added `$is_dismissible` param.
101 *
102 * @param string $message Message text (HTML is OK).
103 * @param string $class Display class (severity).
104 * @param bool $is_dismissible Whether the message should be dismissible.
105 * @param string $key Unique key for the notice. If defined, dismissible notice will be dismissed permanently.
106 */
107 public static function add_admin_notice( $message, $class = self::ADMIN_NOTICE_INFO, $is_dismissible = true, $key = '' ) {
108
109 self::$admin_notices[] = [
110 'message' => $message,
111 'class' => $class,
112 'is_dismissible' => (bool) $is_dismissible,
113 'key' => sanitize_key( $key ),
114 ];
115 }
116
117 /**
118 * Display all notices.
119 *
120 * @since 1.0.0
121 * @since 1.5.0 Allow the notice to be dismissible, remove the id attribute, which is not unique.
122 */
123 public static function display_admin_notices() {
124
125 $has_notices = false;
126
127 foreach ( (array) self::$admin_notices as $notice ) :
128 $is_dismissible = $notice['is_dismissible'];
129 $dismissible = $is_dismissible ? 'is-dismissible' : '';
130
131 if (
132 $is_dismissible &&
133 ! empty( $notice['key'] ) &&
134 (bool) get_user_meta( get_current_user_id(), "wp_mail_smtp_notice_{$notice['key']}_dismissed", true )
135 ) {
136 continue;
137 }
138
139 $has_notices = true;
140 ?>
141
142 <div class="notice wp-mail-smtp-notice <?php echo esc_attr( $notice['class'] ); ?> <?php echo esc_attr( $dismissible ); ?>" <?php echo ! empty( $notice['key'] ) ? 'data-notice="' . esc_attr( $notice['key'] ) . '"' : ''; ?>>
143 <p>
144 <?php echo wp_kses_post( $notice['message'] ); ?>
145 </p>
146 </div>
147
148 <?php
149 endforeach;
150
151 if ( $has_notices ) {
152 wp_enqueue_script(
153 'wp-mail-smtp-admin-notices',
154 wp_mail_smtp()->assets_url . '/js/smtp-admin-notices' . self::asset_min() . '.js',
155 [ 'jquery' ],
156 WPMS_PLUGIN_VER,
157 true
158 );
159
160 wp_localize_script(
161 'wp-mail-smtp-admin-notices',
162 'wp_mail_smtp_admin_notices',
163 [
164 'nonce' => wp_create_nonce( 'wp-mail-smtp-admin' ),
165 ]
166 );
167 }
168 }
169
170 /**
171 * Check whether WP_DEBUG is active.
172 *
173 * @since 1.0.0
174 *
175 * @return bool
176 */
177 public static function is_debug() {
178
179 return defined( 'WP_DEBUG' ) && WP_DEBUG;
180 }
181
182 /**
183 * Shortcut to global $wpdb.
184 *
185 * @since 1.0.0
186 *
187 * @return \wpdb
188 */
189 public static function wpdb() {
190 global $wpdb;
191
192 return $wpdb;
193 }
194
195 /**
196 * Get the postfix for assets files - ".min" or empty.
197 * ".min" if in production mode.
198 *
199 * @since 1.0.0
200 *
201 * @return string
202 */
203 public static function asset_min() {
204
205 $min = '.min';
206
207 if ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) {
208 $min = '';
209 }
210
211 return $min;
212 }
213
214 /**
215 * Check whether the string is a JSON or not.
216 *
217 * @since 1.5.0
218 *
219 * @param string $string String we want to test if it's json.
220 *
221 * @return bool
222 */
223 public static function is_json( $string ) {
224
225 return is_string( $string ) && is_array( json_decode( $string, true ) ) && ( json_last_error() === JSON_ERROR_NONE ) ? true : false;
226 }
227
228 /**
229 * Get the full date format as per WP options.
230 *
231 * @since 1.5.0
232 *
233 * @return string
234 */
235 public static function datetime_format() {
236
237 return sprintf( /* translators: %1$s - date, \a\t - specially escaped "at", %2$s - time. */
238 esc_html__( '%1$s \a\t %2$s', 'wp-mail-smtp' ),
239 get_option( 'date_format' ),
240 get_option( 'time_format' )
241 );
242 }
243
244 /**
245 * Get the full date form as per MySQL format.
246 *
247 * @since 1.5.0
248 *
249 * @return string
250 */
251 public static function datetime_mysql_format() {
252
253 return 'Y-m-d H:i:s';
254 }
255
256 /**
257 * Sanitize the value, similar to `sanitize_text_field()`, but a bit differently.
258 * It preserves `<` and `>` for non-HTML tags.
259 *
260 * @since 1.5.0
261 *
262 * @param string $value String we want to sanitize.
263 *
264 * @return string
265 */
266 public static function sanitize_value( $value ) {
267
268 // Remove HTML tags.
269 $filtered = wp_strip_all_tags( $value, false );
270 // Remove multi-lines/tabs.
271 $filtered = preg_replace( '/[\r\n\t ]+/', ' ', $filtered );
272 // Remove whitespaces.
273 $filtered = trim( $filtered );
274
275 // Remove octets.
276 $found = false;
277 while ( preg_match( '/%[a-f0-9]{2}/i', $filtered, $match ) ) {
278 $filtered = str_replace( $match[0], '', $filtered );
279 $found = true;
280 }
281
282 if ( $found ) {
283 // Strip out the whitespace that may now exist after removing the octets.
284 $filtered = trim( preg_replace( '/ +/', ' ', $filtered ) );
285 }
286
287 return $filtered;
288 }
289
290 /**
291 * Get default email address.
292 *
293 * This is the same code as used in WP core for getting the default email address.
294 *
295 * @see https://github.com/WordPress/WordPress/blob/master/wp-includes/pluggable.php#L332
296 *
297 * @since 2.2.0
298 * @since 2.3.0 In WP 5.5 the core code changed and is now using `network_home_url`.
299 *
300 * @return string
301 */
302 public static function get_default_email() {
303
304 if ( version_compare( get_bloginfo( 'version' ), '5.5-alpha', '<' ) ) {
305 $sitename = ! empty( $_SERVER['SERVER_NAME'] ) ?
306 strtolower( sanitize_text_field( wp_unslash( $_SERVER['SERVER_NAME'] ) ) ) :
307 wp_parse_url( get_home_url( get_current_blog_id() ), PHP_URL_HOST );
308 } else {
309 $sitename = wp_parse_url( network_home_url(), PHP_URL_HOST );
310 }
311
312 if ( 'www.' === substr( $sitename, 0, 4 ) ) {
313 $sitename = substr( $sitename, 4 );
314 }
315
316 return 'wordpress@' . $sitename;
317 }
318
319 /**
320 * Wrapper for the WP `admin_url` method that should be used in the plugin.
321 *
322 * We can filter into it, to maybe call `network_admin_url` for multisite support.
323 *
324 * @since 2.2.0
325 *
326 * @param string $path Optional path relative to the admin URL.
327 * @param string $scheme The scheme to use. Default is 'admin', which obeys force_ssl_admin() and is_ssl().
328 * 'http' or 'https' can be passed to force those schemes.
329 *
330 * @return string Admin URL link with optional path appended.
331 */
332 public static function admin_url( $path = '', $scheme = 'admin' ) {
333
334 return apply_filters( 'wp_mail_smtp_admin_url', \admin_url( $path, $scheme ), $path, $scheme );
335 }
336
337 /**
338 * Check if the global plugin option in a multisite should be used.
339 * If the global plugin option "multisite" is set and true.
340 *
341 * @since 2.2.0
342 *
343 * @return bool
344 */
345 public static function use_global_plugin_settings() {
346
347 if ( ! is_multisite() ) {
348 return false;
349 }
350
351 $main_site_options = get_blog_option( get_main_site_id(), Options::META_KEY, [] );
352
353 return ! empty( $main_site_options['general']['network_wide'] );
354 }
355
356 /**
357 * Returns Jed-formatted localization data.
358 * This code was taken from a function removed from WP core: `wp_get_jed_locale_data`.
359 *
360 * @since 2.6.0
361 *
362 * @param string $domain Translation domain.
363 *
364 * @return array
365 */
366 public static function get_jed_locale_data( $domain ) {
367
368 $translations = get_translations_for_domain( $domain );
369
370 $locale = array(
371 '' => array(
372 'domain' => $domain,
373 'lang' => is_admin() && function_exists( 'get_user_locale' ) ? get_user_locale() : get_locale(),
374 ),
375 );
376
377 if ( ! empty( $translations->headers['Plural-Forms'] ) ) {
378 $locale['']['plural_forms'] = $translations->headers['Plural-Forms'];
379 }
380
381 foreach ( $translations->entries as $entry ) {
382 $locale[ $entry->singular ] = $entry->translations;
383 }
384
385 return $locale;
386 }
387
388 /**
389 * Check if plugins is activated.
390 * Replacement for is_plugin_active function as it works only in admin area
391 *
392 * @since 2.8.0
393 *
394 * @param string $plugin_slug Plugin slug.
395 *
396 * @return bool
397 */
398 public static function is_plugin_activated( $plugin_slug ) {
399
400 static $active_plugins;
401
402 if ( ! isset( $active_plugins ) ) {
403 $active_plugins = (array) get_option( 'active_plugins', [] );
404
405 if ( is_multisite() ) {
406 $active_plugins = array_merge( $active_plugins, get_site_option( 'active_sitewide_plugins', [] ) );
407 }
408 }
409
410 return ( in_array( $plugin_slug, $active_plugins, true ) || array_key_exists( $plugin_slug, $active_plugins ) );
411 }
412
413 /**
414 * Get the ISO 639-2 Language Code from user/site locale.
415 *
416 * @see http://www.loc.gov/standards/iso639-2/php/code_list.php
417 *
418 * @since 2.8.0
419 *
420 * @return string
421 */
422 public static function get_language_code() {
423
424 $default_lang = 'en';
425 $locale = get_user_locale();
426
427 if ( ! empty( $locale ) ) {
428 $lang = explode( '_', $locale );
429 if ( ! empty( $lang ) && is_array( $lang ) ) {
430 $default_lang = strtolower( $lang[0] );
431 }
432 }
433
434 return $default_lang;
435 }
436
437 /**
438 * Get the certain date of a specified day in a specified format.
439 *
440 * @since 2.8.0
441 *
442 * @param string $period Supported values: start, end.
443 * @param string $timestamp Default is the current timestamp, if left empty.
444 * @param string $format Default is a MySQL format.
445 * @param bool $use_gmt_offset Use GTM offset.
446 *
447 * @return string
448 */
449 public static function get_day_period_date( $period, $timestamp = '', $format = 'Y-m-d H:i:s', $use_gmt_offset = false ) {
450
451 $date = '';
452
453 if ( empty( $timestamp ) ) {
454 $timestamp = time();
455 }
456
457 $offset_sec = $use_gmt_offset ? get_option( 'gmt_offset' ) * 3600 : 0;
458
459 switch ( $period ) {
460 case 'start_of_day':
461 $date = gmdate( $format, strtotime( 'today', $timestamp ) - $offset_sec );
462 break;
463
464 case 'end_of_day':
465 $date = gmdate( $format, strtotime( 'tomorrow', $timestamp ) - 1 - $offset_sec );
466 break;
467 }
468
469 return $date;
470 }
471
472 /**
473 * Returns extracted domain from email address.
474 *
475 * @since 2.8.0
476 *
477 * @param string $email Email address.
478 *
479 * @return string
480 */
481 public static function get_email_domain( $email ) {
482
483 return substr( strrchr( $email, '@' ), 1 );
484 }
485
486 /**
487 * Wrapper for set_time_limit to see if it is enabled.
488 *
489 * @since 2.8.0
490 *
491 * @param int $limit Time limit.
492 */
493 public static function set_time_limit( $limit = 0 ) {
494
495 if ( function_exists( 'set_time_limit' ) && false === strpos( ini_get( 'disable_functions' ), 'set_time_limit' ) && ! ini_get( 'safe_mode' ) ) { // phpcs:ignore PHPCompatibility.IniDirectives.RemovedIniDirectives.safe_modeDeprecatedRemoved
496 @set_time_limit( $limit ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
497 }
498 }
499
500 /**
501 * Recursive arguments parsing.
502 *
503 * @since 2.8.0
504 *
505 * @param array $args Arguments.
506 * @param array $defaults Defaults.
507 *
508 * @return array
509 */
510 public static function parse_args_r( &$args, $defaults ) {
511
512 $args = (array) $args;
513 $defaults = (array) $defaults;
514 $r = $defaults;
515
516 foreach ( $args as $k => &$v ) {
517 if ( is_array( $v ) && isset( $r[ $k ] ) ) {
518 $r[ $k ] = self::parse_args_r( $v, $r[ $k ] );
519 } else {
520 $r[ $k ] = $v;
521 }
522 }
523
524 return $r;
525 }
526
527 /**
528 * True if WP is processing plugin related AJAX call.
529 *
530 * @since 3.0.0
531 *
532 * @return bool
533 */
534 public static function is_doing_self_ajax() {
535
536 // phpcs:ignore WordPress.Security.NonceVerification.Recommended
537 $action = isset( $_REQUEST['action'] ) ? sanitize_key( $_REQUEST['action'] ) : false;
538
539 return self::is_doing_ajax() && $action && substr( $action, 0, 12 ) === 'wp_mail_smtp';
540 }
541
542 /**
543 * Get the name of the plugin/theme/wp-core that initiated the desired function call.
544 *
545 * @since 3.0.0
546 *
547 * @param string $file_path The absolute path of a file that that called the desired function.
548 *
549 * @return string
550 */
551 public static function get_initiator_name( $file_path ) {
552
553 return self::get_initiator( $file_path )['name'];
554 }
555
556 /**
557 * Get the info of the plugin/theme/wp-core function.
558 *
559 * @since 3.5.0
560 *
561 * @param string $file_path The absolute path of the function location.
562 *
563 * @return array
564 */
565 public static function get_initiator( $file_path ) { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh
566
567 $cache_key = 'wp_mail_smtp_initiators_data';
568
569 // Mainly we have several initiators and we can cache them for better performance.
570 $initiators_cache = get_transient( $cache_key );
571 $initiators_cache = is_array( $initiators_cache ) ? $initiators_cache : [];
572
573 if ( isset( $initiators_cache[ $file_path ] ) ) {
574 return $initiators_cache[ $file_path ];
575 }
576
577 $initiator = self::get_initiator_plugin( $file_path );
578
579 // Change the initiator name if the email was sent from the reloaded method in the email controls.
580 if (
581 ! empty( $initiator ) &&
582 strpos( str_replace( '\\', '/', $file_path ), 'src/Pro/Emails/Control/Reload.php' )
583 ) {
584 $initiator['name'] = sprintf( /* translators: %s - plugin name. */
585 esc_html__( 'WP Core (%s)', 'wp-mail-smtp' ),
586 $initiator['name']
587 );
588 }
589
590 if ( empty( $initiator ) ) {
591 $initiator = self::get_initiator_plugin( $file_path, true );
592 }
593
594 if ( empty( $initiator ) ) {
595 $initiator = self::get_initiator_theme( $file_path );
596 }
597
598 if ( empty( $initiator ) ) {
599 $initiator = self::get_initiator_wp_core( $file_path );
600 }
601
602 if ( empty( $initiator ) ) {
603 $initiator = [];
604 $initiator['name'] = esc_html__( 'N/A', 'wp-mail-smtp' );
605 $initiator['slug'] = '';
606 $initiator['type'] = 'unknown';
607 }
608
609 $initiators_cache[ $file_path ] = $initiator;
610
611 set_transient( $cache_key, $initiators_cache, HOUR_IN_SECONDS );
612
613 return $initiator;
614 }
615
616 /**
617 * Get the initiator's data, if it's a plugin (or mu plugin).
618 *
619 * @since 3.0.0
620 *
621 * @param string $file_path The absolute path of a file.
622 * @param bool $check_mu_plugin Whether to check for mu plugins or not.
623 *
624 * @return false|array
625 */
626 private static function get_initiator_plugin( $file_path, $check_mu_plugin = false ) { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh, Generic.Metrics.CyclomaticComplexity.MaxExceeded
627
628 $constant = empty( $check_mu_plugin ) ? 'WP_PLUGIN_DIR' : 'WPMU_PLUGIN_DIR';
629
630 if ( ! defined( $constant ) ) {
631 return false;
632 }
633
634 $root = basename( constant( $constant ) );
635 $separator = defined( 'DIRECTORY_SEPARATOR' ) ? '\\' . DIRECTORY_SEPARATOR : '\/';
636
637 preg_match( "/$separator$root$separator(.[^$separator]+)($separator|\.php)/", $file_path, $result );
638
639 if ( ! empty( $result[1] ) ) {
640 if ( ! function_exists( 'get_plugins' ) ) {
641 include ABSPATH . '/wp-admin/includes/plugin.php';
642 }
643
644 $all_plugins = empty( $check_mu_plugin ) ? get_plugins() : get_mu_plugins();
645 $plugin_slug = $result[1];
646
647 foreach ( $all_plugins as $plugin => $plugin_data ) {
648 if (
649 1 === preg_match( "/^$plugin_slug(\/|\.php)/", $plugin ) &&
650 isset( $plugin_data['Name'] )
651 ) {
652 return [
653 'name' => $plugin_data['Name'],
654 'slug' => $plugin,
655 'type' => $check_mu_plugin ? 'mu-plugin' : 'plugin',
656 ];
657 }
658 }
659
660 return [
661 'name' => $result[1],
662 'slug' => '',
663 'type' => $check_mu_plugin ? 'mu-plugin' : 'plugin',
664 ];
665 }
666
667 return false;
668 }
669
670 /**
671 * Get the initiator's data, if it's a theme.
672 *
673 * @since 3.0.0
674 *
675 * @param string $file_path The absolute path of a file.
676 *
677 * @return false|array
678 */
679 private static function get_initiator_theme( $file_path ) {
680
681 if ( ! defined( 'WP_CONTENT_DIR' ) ) {
682 return false;
683 }
684
685 $root = basename( WP_CONTENT_DIR );
686 $separator = defined( 'DIRECTORY_SEPARATOR' ) ? '\\' . DIRECTORY_SEPARATOR : '\/';
687
688 preg_match( "/$separator$root{$separator}themes{$separator}(.[^$separator]+)/", $file_path, $result );
689
690 if ( ! empty( $result[1] ) ) {
691 $theme = wp_get_theme( $result[1] );
692
693 return [
694 'name' => method_exists( $theme, 'get' ) ? $theme->get( 'Name' ) : $result[1],
695 'slug' => $result[1],
696 'type' => 'theme',
697 ];
698 }
699
700 return false;
701 }
702
703 /**
704 * Return WP Core if the file path is from WP Core (wp-admin or wp-includes folders).
705 *
706 * @since 3.1.0
707 *
708 * @param string $file_path The absolute path of a file.
709 *
710 * @return false|array
711 */
712 private static function get_initiator_wp_core( $file_path ) {
713
714 if ( ! defined( 'ABSPATH' ) ) {
715 return false;
716 }
717
718 $wp_includes = defined( 'WPINC' ) ? trailingslashit( ABSPATH . WPINC ) : false;
719 $wp_admin = trailingslashit( ABSPATH . 'wp-admin' );
720
721 if (
722 strpos( $file_path, $wp_includes ) === 0 ||
723 strpos( $file_path, $wp_admin ) === 0
724 ) {
725 return [
726 'name' => esc_html__( 'WP Core', 'wp-mail-smtp' ),
727 'slug' => 'wp-core',
728 'type' => 'wp-core',
729 ];
730 }
731
732 return false;
733 }
734
735 /**
736 * Retrieves the timezone from site settings as a `DateTimeZone` object.
737 *
738 * Timezone can be based on a PHP timezone string or a ±HH:MM offset.
739 *
740 * We use `wp_timezone()` when it's available (WP 5.3+),
741 * otherwise fallback to the same code, copy-pasted.
742 *
743 * @since 3.0.2
744 *
745 * @return \DateTimeZone Timezone object.
746 */
747 public static function wp_timezone() {
748
749 if ( function_exists( 'wp_timezone' ) ) {
750 return wp_timezone();
751 }
752
753 return new \DateTimeZone( self::wp_timezone_string() );
754 }
755
756 /**
757 * Retrieves the timezone from site settings as a string.
758 *
759 * Uses the `timezone_string` option to get a proper timezone if available,
760 * otherwise falls back to an offset.
761 *
762 * We use `wp_timezone_string()` when it's available (WP 5.3+),
763 * otherwise fallback to the same code, copy-pasted.
764 *
765 * @since 3.0.2
766 *
767 * @return string PHP timezone string or a ±HH:MM offset.
768 */
769 public static function wp_timezone_string() {
770
771 if ( function_exists( 'wp_timezone_string' ) ) {
772 return wp_timezone_string();
773 }
774
775 $timezone_string = get_option( 'timezone_string' );
776
777 if ( $timezone_string ) {
778 return $timezone_string;
779 }
780
781 $offset = (float) get_option( 'gmt_offset' );
782 $hours = (int) $offset;
783 $minutes = ( $offset - $hours );
784
785 $sign = ( $offset < 0 ) ? '-' : '+';
786 $abs_hour = abs( $hours );
787 $abs_mins = abs( $minutes * 60 );
788 $tz_offset = sprintf( '%s%02d:%02d', $sign, $abs_hour, $abs_mins );
789
790 return $tz_offset;
791 }
792
793 /**
794 * Get wp remote response error message.
795 *
796 * @since 3.4.0
797 *
798 * @param array $response Response array.
799 */
800 public static function wp_remote_get_response_error_message( $response ) {
801
802 if ( is_wp_error( $response ) ) {
803 return '';
804 }
805
806 $body = wp_remote_retrieve_body( $response );
807 $message = wp_remote_retrieve_response_message( $response );
808 $code = wp_remote_retrieve_response_code( $response );
809 $description = '';
810
811 if ( ! empty( $body ) ) {
812 $description = is_string( $body ) ? $body : wp_json_encode( $body );
813 }
814
815 return Helpers::format_error_message( $message, $code, $description );
816 }
817
818 /**
819 * Clean variables using sanitize_text_field. Arrays are cleaned recursively.
820 * Non-string values are ignored.
821 *
822 * @since 3.7.0
823 *
824 * @param string|array $var Data to sanitize.
825 *
826 * @return string|array
827 */
828 public static function sanitize_text( $var ) {
829
830 if ( is_array( $var ) ) {
831 return array_map( [ __CLASS__, 'sanitize_text' ], $var );
832 } else {
833 return is_string( $var ) ? sanitize_text_field( $var ) : $var;
834 }
835 }
836
837 /**
838 * Get the current site URL,
839 * or the network URL if using network-wide settings.
840 *
841 * @since 4.4.0
842 *
843 * @return string
844 */
845 public static function get_site_url() {
846
847 $site_id = null;
848
849 if ( self::use_global_plugin_settings() ) {
850 $site_id = get_main_site_id();
851 }
852
853 /**
854 * Whether to return the unfiltered site URL.
855 *
856 * @since 4.6.0
857 *
858 * @param bool $unfiltered Whether to return the unfiltered site URL.
859 *
860 * @return bool
861 */
862 if ( apply_filters( 'wp_mail_smtp_wp_get_site_url_unfiltered', false ) ) {
863 return self::get_raw_site_url( $site_id );
864 }
865
866 return get_site_url( $site_id );
867 }
868
869 /**
870 * Get the raw/unfiltered site URL.
871 *
872 * @since 4.6.0
873 *
874 * @param int $site_id The site ID.
875 *
876 * @return string
877 */
878 private static function get_raw_site_url( $site_id ) {
879
880 if ( empty( $site_id ) || ! is_multisite() ) {
881 $url = get_option( 'siteurl' );
882 } else {
883 switch_to_blog( $site_id );
884
885 $url = get_option( 'siteurl' );
886
887 restore_current_blog();
888 }
889
890 $url = set_url_scheme( $url );
891
892 return $url;
893 }
894 }
895