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 / WP.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
WP.php
804 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 */
106 public static function add_admin_notice( $message, $class = self::ADMIN_NOTICE_INFO, $is_dismissible = true ) {
107
108 self::$admin_notices[] = array(
109 'message' => $message,
110 'class' => $class,
111 'is_dismissible' => (bool) $is_dismissible,
112 );
113 }
114
115 /**
116 * Display all notices.
117 *
118 * @since 1.0.0
119 * @since 1.5.0 Allow the notice to be dismissible, remove the id attribute, which is not unique.
120 */
121 public static function display_admin_notices() {
122
123 foreach ( (array) self::$admin_notices as $notice ) :
124 $dismissible = $notice['is_dismissible'] ? 'is-dismissible' : '';
125 ?>
126
127 <div class="notice wp-mail-smtp-notice <?php echo esc_attr( $notice['class'] ); ?> notice <?php echo esc_attr( $dismissible ); ?>">
128 <p>
129 <?php echo wp_kses_post( $notice['message'] ); ?>
130 </p>
131 </div>
132
133 <?php
134 endforeach;
135 }
136
137 /**
138 * Check whether WP_DEBUG is active.
139 *
140 * @since 1.0.0
141 *
142 * @return bool
143 */
144 public static function is_debug() {
145
146 return defined( 'WP_DEBUG' ) && WP_DEBUG;
147 }
148
149 /**
150 * Shortcut to global $wpdb.
151 *
152 * @since 1.0.0
153 *
154 * @return \wpdb
155 */
156 public static function wpdb() {
157 global $wpdb;
158
159 return $wpdb;
160 }
161
162 /**
163 * Get the postfix for assets files - ".min" or empty.
164 * ".min" if in production mode.
165 *
166 * @since 1.0.0
167 *
168 * @return string
169 */
170 public static function asset_min() {
171
172 $min = '.min';
173
174 if ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) {
175 $min = '';
176 }
177
178 return $min;
179 }
180
181 /**
182 * Check whether the string is a JSON or not.
183 *
184 * @since 1.5.0
185 *
186 * @param string $string String we want to test if it's json.
187 *
188 * @return bool
189 */
190 public static function is_json( $string ) {
191
192 return is_string( $string ) && is_array( json_decode( $string, true ) ) && ( json_last_error() === JSON_ERROR_NONE ) ? true : false;
193 }
194
195 /**
196 * Get the full date format as per WP options.
197 *
198 * @since 1.5.0
199 *
200 * @return string
201 */
202 public static function datetime_format() {
203
204 return sprintf( /* translators: %1$s - date, \a\t - specially escaped "at", %2$s - time. */
205 esc_html__( '%1$s \a\t %2$s', 'wp-mail-smtp' ),
206 get_option( 'date_format' ),
207 get_option( 'time_format' )
208 );
209 }
210
211 /**
212 * Get the full date form as per MySQL format.
213 *
214 * @since 1.5.0
215 *
216 * @return string
217 */
218 public static function datetime_mysql_format() {
219
220 return 'Y-m-d H:i:s';
221 }
222
223 /**
224 * Sanitize the value, similar to `sanitize_text_field()`, but a bit differently.
225 * It preserves `<` and `>` for non-HTML tags.
226 *
227 * @since 1.5.0
228 *
229 * @param string $value String we want to sanitize.
230 *
231 * @return string
232 */
233 public static function sanitize_value( $value ) {
234
235 // Remove HTML tags.
236 $filtered = wp_strip_all_tags( $value, false );
237 // Remove multi-lines/tabs.
238 $filtered = preg_replace( '/[\r\n\t ]+/', ' ', $filtered );
239 // Remove whitespaces.
240 $filtered = trim( $filtered );
241
242 // Remove octets.
243 $found = false;
244 while ( preg_match( '/%[a-f0-9]{2}/i', $filtered, $match ) ) {
245 $filtered = str_replace( $match[0], '', $filtered );
246 $found = true;
247 }
248
249 if ( $found ) {
250 // Strip out the whitespace that may now exist after removing the octets.
251 $filtered = trim( preg_replace( '/ +/', ' ', $filtered ) );
252 }
253
254 return $filtered;
255 }
256
257 /**
258 * Get default email address.
259 *
260 * This is the same code as used in WP core for getting the default email address.
261 *
262 * @see https://github.com/WordPress/WordPress/blob/master/wp-includes/pluggable.php#L332
263 *
264 * @since 2.2.0
265 * @since 2.3.0 In WP 5.5 the core code changed and is now using `network_home_url`.
266 *
267 * @return string
268 */
269 public static function get_default_email() {
270
271 if ( version_compare( get_bloginfo( 'version' ), '5.5-alpha', '<' ) ) {
272 $sitename = ! empty( $_SERVER['SERVER_NAME'] ) ?
273 strtolower( sanitize_text_field( wp_unslash( $_SERVER['SERVER_NAME'] ) ) ) :
274 wp_parse_url( get_home_url( get_current_blog_id() ), PHP_URL_HOST );
275 } else {
276 $sitename = wp_parse_url( network_home_url(), PHP_URL_HOST );
277 }
278
279 if ( 'www.' === substr( $sitename, 0, 4 ) ) {
280 $sitename = substr( $sitename, 4 );
281 }
282
283 return 'wordpress@' . $sitename;
284 }
285
286 /**
287 * Wrapper for the WP `admin_url` method that should be used in the plugin.
288 *
289 * We can filter into it, to maybe call `network_admin_url` for multisite support.
290 *
291 * @since 2.2.0
292 *
293 * @param string $path Optional path relative to the admin URL.
294 * @param string $scheme The scheme to use. Default is 'admin', which obeys force_ssl_admin() and is_ssl().
295 * 'http' or 'https' can be passed to force those schemes.
296 *
297 * @return string Admin URL link with optional path appended.
298 */
299 public static function admin_url( $path = '', $scheme = 'admin' ) {
300
301 return apply_filters( 'wp_mail_smtp_admin_url', \admin_url( $path, $scheme ), $path, $scheme );
302 }
303
304 /**
305 * Check if the global plugin option in a multisite should be used.
306 * If the global plugin option "multisite" is set and true.
307 *
308 * @since 2.2.0
309 *
310 * @return bool
311 */
312 public static function use_global_plugin_settings() {
313
314 if ( ! is_multisite() ) {
315 return false;
316 }
317
318 $main_site_options = get_blog_option( get_main_site_id(), Options::META_KEY, [] );
319
320 return ! empty( $main_site_options['general']['network_wide'] );
321 }
322
323 /**
324 * Returns Jed-formatted localization data.
325 * This code was taken from a function removed from WP core: `wp_get_jed_locale_data`.
326 *
327 * @since 2.6.0
328 *
329 * @param string $domain Translation domain.
330 *
331 * @return array
332 */
333 public static function get_jed_locale_data( $domain ) {
334
335 $translations = get_translations_for_domain( $domain );
336
337 $locale = array(
338 '' => array(
339 'domain' => $domain,
340 'lang' => is_admin() && function_exists( 'get_user_locale' ) ? get_user_locale() : get_locale(),
341 ),
342 );
343
344 if ( ! empty( $translations->headers['Plural-Forms'] ) ) {
345 $locale['']['plural_forms'] = $translations->headers['Plural-Forms'];
346 }
347
348 foreach ( $translations->entries as $entry ) {
349 $locale[ $entry->singular ] = $entry->translations;
350 }
351
352 return $locale;
353 }
354
355 /**
356 * Check if plugins is activated.
357 * Replacement for is_plugin_active function as it works only in admin area
358 *
359 * @since 2.8.0
360 *
361 * @param string $plugin_slug Plugin slug.
362 *
363 * @return bool
364 */
365 public static function is_plugin_activated( $plugin_slug ) {
366
367 static $active_plugins;
368
369 if ( ! isset( $active_plugins ) ) {
370 $active_plugins = (array) get_option( 'active_plugins', [] );
371
372 if ( is_multisite() ) {
373 $active_plugins = array_merge( $active_plugins, get_site_option( 'active_sitewide_plugins', [] ) );
374 }
375 }
376
377 return ( in_array( $plugin_slug, $active_plugins, true ) || array_key_exists( $plugin_slug, $active_plugins ) );
378 }
379
380 /**
381 * Get the ISO 639-2 Language Code from user/site locale.
382 *
383 * @see http://www.loc.gov/standards/iso639-2/php/code_list.php
384 *
385 * @since 2.8.0
386 *
387 * @return string
388 */
389 public static function get_language_code() {
390
391 $default_lang = 'en';
392 $locale = get_user_locale();
393
394 if ( ! empty( $locale ) ) {
395 $lang = explode( '_', $locale );
396 if ( ! empty( $lang ) && is_array( $lang ) ) {
397 $default_lang = strtolower( $lang[0] );
398 }
399 }
400
401 return $default_lang;
402 }
403
404 /**
405 * Get the certain date of a specified day in a specified format.
406 *
407 * @since 2.8.0
408 *
409 * @param string $period Supported values: start, end.
410 * @param string $timestamp Default is the current timestamp, if left empty.
411 * @param string $format Default is a MySQL format.
412 * @param bool $use_gmt_offset Use GTM offset.
413 *
414 * @return string
415 */
416 public static function get_day_period_date( $period, $timestamp = '', $format = 'Y-m-d H:i:s', $use_gmt_offset = false ) {
417
418 $date = '';
419
420 if ( empty( $timestamp ) ) {
421 $timestamp = time();
422 }
423
424 $offset_sec = $use_gmt_offset ? get_option( 'gmt_offset' ) * 3600 : 0;
425
426 switch ( $period ) {
427 case 'start_of_day':
428 $date = gmdate( $format, strtotime( 'today', $timestamp ) - $offset_sec );
429 break;
430
431 case 'end_of_day':
432 $date = gmdate( $format, strtotime( 'tomorrow', $timestamp ) - 1 - $offset_sec );
433 break;
434 }
435
436 return $date;
437 }
438
439 /**
440 * Returns extracted domain from email address.
441 *
442 * @since 2.8.0
443 *
444 * @param string $email Email address.
445 *
446 * @return string
447 */
448 public static function get_email_domain( $email ) {
449
450 return substr( strrchr( $email, '@' ), 1 );
451 }
452
453 /**
454 * Wrapper for set_time_limit to see if it is enabled.
455 *
456 * @since 2.8.0
457 *
458 * @param int $limit Time limit.
459 */
460 public static function set_time_limit( $limit = 0 ) {
461
462 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
463 @set_time_limit( $limit ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
464 }
465 }
466
467 /**
468 * Recursive arguments parsing.
469 *
470 * @since 2.8.0
471 *
472 * @param array $args Arguments.
473 * @param array $defaults Defaults.
474 *
475 * @return array
476 */
477 public static function parse_args_r( &$args, $defaults ) {
478
479 $args = (array) $args;
480 $defaults = (array) $defaults;
481 $r = $defaults;
482
483 foreach ( $args as $k => &$v ) {
484 if ( is_array( $v ) && isset( $r[ $k ] ) ) {
485 $r[ $k ] = self::parse_args_r( $v, $r[ $k ] );
486 } else {
487 $r[ $k ] = $v;
488 }
489 }
490
491 return $r;
492 }
493
494 /**
495 * True if WP is processing plugin related AJAX call.
496 *
497 * @since 3.0.0
498 *
499 * @return bool
500 */
501 public static function is_doing_self_ajax() {
502
503 // phpcs:ignore WordPress.Security.NonceVerification.Recommended
504 $action = isset( $_REQUEST['action'] ) ? sanitize_key( $_REQUEST['action'] ) : false;
505
506 return self::is_doing_ajax() && $action && substr( $action, 0, 12 ) === 'wp_mail_smtp';
507 }
508
509 /**
510 * Get the name of the plugin/theme/wp-core that initiated the desired function call.
511 *
512 * @since 3.0.0
513 *
514 * @param string $file_path The absolute path of a file that that called the desired function.
515 *
516 * @return string
517 */
518 public static function get_initiator_name( $file_path ) {
519
520 return self::get_initiator( $file_path )['name'];
521 }
522
523 /**
524 * Get the info of the plugin/theme/wp-core function.
525 *
526 * @since 3.5.0
527 *
528 * @param string $file_path The absolute path of the function location.
529 *
530 * @return array
531 */
532 public static function get_initiator( $file_path ) { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh
533
534 $cache_key = 'wp_mail_smtp_initiators_data';
535
536 // Mainly we have several initiators and we can cache them for better performance.
537 $initiators_cache = get_transient( $cache_key );
538 $initiators_cache = is_array( $initiators_cache ) ? $initiators_cache : [];
539
540 if ( isset( $initiators_cache[ $file_path ] ) ) {
541 return $initiators_cache[ $file_path ];
542 }
543
544 $initiator = self::get_initiator_plugin( $file_path );
545
546 // Change the initiator name if the email was sent from the reloaded method in the email controls.
547 if (
548 ! empty( $initiator ) &&
549 strpos( str_replace( '\\', '/', $file_path ), 'src/Pro/Emails/Control/Reload.php' )
550 ) {
551 $initiator['name'] = sprintf( /* translators: %s - plugin name. */
552 esc_html__( 'WP Core (%s)', 'wp-mail-smtp' ),
553 $initiator['name']
554 );
555 }
556
557 if ( empty( $initiator ) ) {
558 $initiator = self::get_initiator_plugin( $file_path, true );
559 }
560
561 if ( empty( $initiator ) ) {
562 $initiator = self::get_initiator_theme( $file_path );
563 }
564
565 if ( empty( $initiator ) ) {
566 $initiator = self::get_initiator_wp_core( $file_path );
567 }
568
569 if ( empty( $initiator ) ) {
570 $initiator = [];
571 $initiator['name'] = esc_html__( 'N/A', 'wp-mail-smtp' );
572 $initiator['slug'] = '';
573 $initiator['type'] = 'unknown';
574 }
575
576 $initiators_cache[ $file_path ] = $initiator;
577
578 set_transient( $cache_key, $initiators_cache, HOUR_IN_SECONDS );
579
580 return $initiator;
581 }
582
583 /**
584 * Get the initiator's data, if it's a plugin (or mu plugin).
585 *
586 * @since 3.0.0
587 *
588 * @param string $file_path The absolute path of a file.
589 * @param bool $check_mu_plugin Whether to check for mu plugins or not.
590 *
591 * @return false|array
592 */
593 private static function get_initiator_plugin( $file_path, $check_mu_plugin = false ) { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh, Generic.Metrics.CyclomaticComplexity.MaxExceeded
594
595 $constant = empty( $check_mu_plugin ) ? 'WP_PLUGIN_DIR' : 'WPMU_PLUGIN_DIR';
596
597 if ( ! defined( $constant ) ) {
598 return false;
599 }
600
601 $root = basename( constant( $constant ) );
602 $separator = defined( 'DIRECTORY_SEPARATOR' ) ? '\\' . DIRECTORY_SEPARATOR : '\/';
603
604 preg_match( "/$separator$root$separator(.[^$separator]+)($separator|\.php)/", $file_path, $result );
605
606 if ( ! empty( $result[1] ) ) {
607 if ( ! function_exists( 'get_plugins' ) ) {
608 include ABSPATH . '/wp-admin/includes/plugin.php';
609 }
610
611 $all_plugins = empty( $check_mu_plugin ) ? get_plugins() : get_mu_plugins();
612 $plugin_slug = $result[1];
613
614 foreach ( $all_plugins as $plugin => $plugin_data ) {
615 if (
616 1 === preg_match( "/^$plugin_slug(\/|\.php)/", $plugin ) &&
617 isset( $plugin_data['Name'] )
618 ) {
619 return [
620 'name' => $plugin_data['Name'],
621 'slug' => $plugin,
622 'type' => $check_mu_plugin ? 'mu-plugin' : 'plugin',
623 ];
624 }
625 }
626
627 return [
628 'name' => $result[1],
629 'slug' => '',
630 'type' => $check_mu_plugin ? 'mu-plugin' : 'plugin',
631 ];
632 }
633
634 return false;
635 }
636
637 /**
638 * Get the initiator's data, if it's a theme.
639 *
640 * @since 3.0.0
641 *
642 * @param string $file_path The absolute path of a file.
643 *
644 * @return false|array
645 */
646 private static function get_initiator_theme( $file_path ) {
647
648 if ( ! defined( 'WP_CONTENT_DIR' ) ) {
649 return false;
650 }
651
652 $root = basename( WP_CONTENT_DIR );
653 $separator = defined( 'DIRECTORY_SEPARATOR' ) ? '\\' . DIRECTORY_SEPARATOR : '\/';
654
655 preg_match( "/$separator$root{$separator}themes{$separator}(.[^$separator]+)/", $file_path, $result );
656
657 if ( ! empty( $result[1] ) ) {
658 $theme = wp_get_theme( $result[1] );
659
660 return [
661 'name' => method_exists( $theme, 'get' ) ? $theme->get( 'Name' ) : $result[1],
662 'slug' => $result[1],
663 'type' => 'theme',
664 ];
665 }
666
667 return false;
668 }
669
670 /**
671 * Return WP Core if the file path is from WP Core (wp-admin or wp-includes folders).
672 *
673 * @since 3.1.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_wp_core( $file_path ) {
680
681 if ( ! defined( 'ABSPATH' ) ) {
682 return false;
683 }
684
685 $wp_includes = defined( 'WPINC' ) ? trailingslashit( ABSPATH . WPINC ) : false;
686 $wp_admin = trailingslashit( ABSPATH . 'wp-admin' );
687
688 if (
689 strpos( $file_path, $wp_includes ) === 0 ||
690 strpos( $file_path, $wp_admin ) === 0
691 ) {
692 return [
693 'name' => esc_html__( 'WP Core', 'wp-mail-smtp' ),
694 'slug' => 'wp-core',
695 'type' => 'wp-core',
696 ];
697 }
698
699 return false;
700 }
701
702 /**
703 * Retrieves the timezone from site settings as a `DateTimeZone` object.
704 *
705 * Timezone can be based on a PHP timezone string or a ±HH:MM offset.
706 *
707 * We use `wp_timezone()` when it's available (WP 5.3+),
708 * otherwise fallback to the same code, copy-pasted.
709 *
710 * @since 3.0.2
711 *
712 * @return \DateTimeZone Timezone object.
713 */
714 public static function wp_timezone() {
715
716 if ( function_exists( 'wp_timezone' ) ) {
717 return wp_timezone();
718 }
719
720 return new \DateTimeZone( self::wp_timezone_string() );
721 }
722
723 /**
724 * Retrieves the timezone from site settings as a string.
725 *
726 * Uses the `timezone_string` option to get a proper timezone if available,
727 * otherwise falls back to an offset.
728 *
729 * We use `wp_timezone_string()` when it's available (WP 5.3+),
730 * otherwise fallback to the same code, copy-pasted.
731 *
732 * @since 3.0.2
733 *
734 * @return string PHP timezone string or a ±HH:MM offset.
735 */
736 public static function wp_timezone_string() {
737
738 if ( function_exists( 'wp_timezone_string' ) ) {
739 return wp_timezone_string();
740 }
741
742 $timezone_string = get_option( 'timezone_string' );
743
744 if ( $timezone_string ) {
745 return $timezone_string;
746 }
747
748 $offset = (float) get_option( 'gmt_offset' );
749 $hours = (int) $offset;
750 $minutes = ( $offset - $hours );
751
752 $sign = ( $offset < 0 ) ? '-' : '+';
753 $abs_hour = abs( $hours );
754 $abs_mins = abs( $minutes * 60 );
755 $tz_offset = sprintf( '%s%02d:%02d', $sign, $abs_hour, $abs_mins );
756
757 return $tz_offset;
758 }
759
760 /**
761 * Get wp remote response error message.
762 *
763 * @since 3.4.0
764 *
765 * @param array $response Response array.
766 */
767 public static function wp_remote_get_response_error_message( $response ) {
768
769 if ( is_wp_error( $response ) ) {
770 return '';
771 }
772
773 $body = wp_remote_retrieve_body( $response );
774 $message = wp_remote_retrieve_response_message( $response );
775 $code = wp_remote_retrieve_response_code( $response );
776 $description = '';
777
778 if ( ! empty( $body ) ) {
779 $description = is_string( $body ) ? $body : wp_json_encode( $body );
780 }
781
782 return Helpers::format_error_message( $message, $code, $description );
783 }
784
785 /**
786 * Clean variables using sanitize_text_field. Arrays are cleaned recursively.
787 * Non-string values are ignored.
788 *
789 * @since 3.7.0
790 *
791 * @param string|array $var Data to sanitize.
792 *
793 * @return string|array
794 */
795 public static function sanitize_text( $var ) {
796
797 if ( is_array( $var ) ) {
798 return array_map( [ __CLASS__, 'sanitize_text' ], $var );
799 } else {
800 return is_string( $var ) ? sanitize_text_field( $var ) : $var;
801 }
802 }
803 }
804