Completed
Push — start/92-cycle ( 3fe444 )
by Jeremy
07:10
created

functions.global.php (5 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * This file is meant to be the home for any generic & reusable functions
4
 * that can be accessed anywhere within Jetpack.
5
 *
6
 * This file is loaded whether or not Jetpack is active.
7
 *
8
 * Please namespace with jetpack_
9
 *
10
 * @package Jetpack
11
 */
12
13
use Automattic\Jetpack\Connection\Client;
14
use Automattic\Jetpack\Device_Detection;
15
use Automattic\Jetpack\Redirect;
16
17
/**
18
 * Disable direct access.
19
 */
20
if ( ! defined( 'ABSPATH' ) ) {
21
	exit;
22
}
23
24
/**
25
 * Hook into Core's _deprecated_function
26
 * Add more details about when a deprecated function will be removed.
27
 *
28
 * @since 8.8.0
29
 *
30
 * @param string $function    The function that was called.
31
 * @param string $replacement Optional. The function that should have been called. Default null.
32
 * @param string $version     The version of Jetpack that deprecated the function.
33
 */
34 View Code Duplication
function jetpack_deprecated_function( $function, $replacement, $version ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
35
	// Bail early for non-Jetpack deprecations.
36
	if ( 0 !== strpos( $version, 'jetpack-' ) ) {
37
		return;
38
	}
39
40
	// Look for when a function will be removed based on when it was deprecated.
41
	$removed_version = jetpack_get_future_removed_version( $version );
42
43
	// If we could find a version, let's log a message about when removal will happen.
44
	if (
45
		! empty( $removed_version )
46
		&& ( defined( 'WP_DEBUG' ) && WP_DEBUG )
47
		/** This filter is documented in core/src/wp-includes/functions.php */
48
		&& apply_filters( 'deprecated_function_trigger_error', true )
49
	) {
50
		error_log( // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
51
			sprintf(
52
				/* Translators: 1. Function name. 2. Jetpack version number. */
53
				__( 'The %1$s function will be removed from the Jetpack plugin in version %2$s.', 'jetpack' ),
54
				$function,
55
				$removed_version
56
			)
57
		);
58
59
	}
60
}
61
add_action( 'deprecated_function_run', 'jetpack_deprecated_function', 10, 3 );
62
63
/**
64
 * Hook into Core's _deprecated_file
65
 * Add more details about when a deprecated file will be removed.
66
 *
67
 * @since 8.8.0
68
 *
69
 * @param string $file        The file that was called.
70
 * @param string $replacement The file that should have been included based on ABSPATH.
71
 * @param string $version     The version of WordPress that deprecated the file.
72
 * @param string $message     A message regarding the change.
73
 */
74 View Code Duplication
function jetpack_deprecated_file( $file, $replacement, $version, $message ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
75
	// Bail early for non-Jetpack deprecations.
76
	if ( 0 !== strpos( $version, 'jetpack-' ) ) {
77
		return;
78
	}
79
80
	// Look for when a file will be removed based on when it was deprecated.
81
	$removed_version = jetpack_get_future_removed_version( $version );
82
83
	// If we could find a version, let's log a message about when removal will happen.
84
	if (
85
		! empty( $removed_version )
86
		&& ( defined( 'WP_DEBUG' ) && WP_DEBUG )
87
		/** This filter is documented in core/src/wp-includes/functions.php */
88
		&& apply_filters( 'deprecated_file_trigger_error', true )
89
	) {
90
		error_log( // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
91
			sprintf(
92
				/* Translators: 1. File name. 2. Jetpack version number. */
93
				__( 'The %1$s file will be removed from the Jetpack plugin in version %2$s.', 'jetpack' ),
94
				$file,
95
				$removed_version
96
			)
97
		);
98
99
	}
100
}
101
add_action( 'deprecated_file_included', 'jetpack_deprecated_file', 10, 4 );
102
103
/**
104
 * Get the major version number of Jetpack 6 months after provided version.
105
 * Useful to indicate when a deprecated function will be removed from Jetpack.
106
 *
107
 * @since 8.8.0
108
 *
109
 * @param string $version The version of WordPress that deprecated the function.
110
 *
111
 * @return bool|float Return a Jetpack Major version number, or false.
112
 */
113
function jetpack_get_future_removed_version( $version ) {
114
	/*
115
	 * Extract the version number from a deprecation notice.
116
	 * (let's only keep the first decimal, e.g. 8.8 and not 8.8.0)
117
	 */
118
	preg_match( '#(([0-9]+\.([0-9]+))(?:\.[0-9]+)*)#', $version, $matches );
119
120
	if ( isset( $matches[2], $matches[3] ) ) {
121
		$deprecated_version = (float) $matches[2];
122
		$deprecated_minor   = (float) $matches[3];
123
124
		/*
125
		 * If the detected minor version number
126
		 * (e.g. "7" in "8.7")
127
		 * is higher than 9, we know the version number is malformed.
128
		 * Jetpack does not use semver yet.
129
		 * Bail.
130
		 */
131
		if ( 10 <= $deprecated_minor ) {
132
			return false;
133
		}
134
135
		// We'll remove the function from the code 6 months later, thus 6 major versions later.
136
		$removed_version = $deprecated_version + 0.6;
137
138
		return (float) $removed_version;
139
	}
140
141
	return false;
142
}
143
144
/**
145
 * Set the admin language, based on user language.
146
 *
147
 * @since 4.5.0
148
 * @deprecated 6.6.0 Use Core function instead.
149
 *
150
 * @return string
151
 */
152
function jetpack_get_user_locale() {
153
	_deprecated_function( __FUNCTION__, 'jetpack-6.6.0', 'get_user_locale' );
154
	return get_user_locale();
155
}
156
157
/**
158
 * Determine if this site is an Atomic site or not looking first at the 'at_options' option.
159
 * As a fallback, check for presence of wpcomsh plugin to determine if a current site has undergone AT.
160
 *
161
 * @since 4.8.1
162
 *
163
 * @return bool
164
 */
165
function jetpack_is_atomic_site() {
166
	$at_options = get_option( 'at_options', array() );
167
	return ! empty( $at_options ) || defined( 'WPCOMSH__PLUGIN_FILE' );
168
}
169
170
/**
171
 * Register post type for migration.
172
 *
173
 * @since 5.2
174
 */
175
function jetpack_register_migration_post_type() {
176
	register_post_type(
177
		'jetpack_migration',
178
		array(
179
			'supports'     => array(),
180
			'taxonomies'   => array(),
181
			'hierarchical' => false,
182
			'public'       => false,
183
			'has_archive'  => false,
184
			'can_export'   => true,
185
		)
186
	);
187
}
188
189
/**
190
 * Stores migration data in the database.
191
 *
192
 * @since 5.2
193
 *
194
 * @param string $option_name  Option name.
195
 * @param bool   $option_value Option value.
196
 *
197
 * @return int|WP_Error
198
 */
199
function jetpack_store_migration_data( $option_name, $option_value ) {
200
	jetpack_register_migration_post_type();
201
202
	$insert = array(
203
		'post_title'            => $option_name,
204
		'post_content_filtered' => $option_value,
205
		'post_type'             => 'jetpack_migration',
206
		'post_date'             => gmdate( 'Y-m-d H:i:s', time() ),
207
	);
208
209
	$post = get_page_by_title( $option_name, 'OBJECT', 'jetpack_migration' );
210
211
	if ( null !== $post ) {
212
		$insert['ID'] = $post->ID;
213
	}
214
215
	return wp_insert_post( $insert, true );
216
}
217
218
/**
219
 * Retrieves legacy image widget data.
220
 *
221
 * @since 5.2
222
 *
223
 * @param string $option_name Option name.
224
 *
225
 * @return mixed|null
226
 */
227
function jetpack_get_migration_data( $option_name ) {
228
	$post = get_page_by_title( $option_name, 'OBJECT', 'jetpack_migration' );
229
230
	return null !== $post ? maybe_unserialize( $post->post_content_filtered ) : null;
231
}
232
233
/**
234
 * Prints a TOS blurb used throughout the connection prompts.
235
 *
236
 * @since 5.3
237
 *
238
 * @echo string
239
 */
240
function jetpack_render_tos_blurb() {
241
	printf(
242
		wp_kses(
243
			/* Translators: placeholders are links. */
244
			__( 'By clicking the <strong>Set up Jetpack</strong> button, you agree to our <a href="%1$s" target="_blank" rel="noopener noreferrer">Terms of Service</a> and to <a href="%2$s" target="_blank" rel="noopener noreferrer">share details</a> with WordPress.com.', 'jetpack' ),
245
			array(
246
				'a'      => array(
247
					'href'   => array(),
248
					'target' => array(),
249
					'rel'    => array(),
250
				),
251
				'strong' => true,
252
			)
253
		),
254
		esc_url( Redirect::get_url( 'wpcom-tos' ) ),
255
		esc_url( Redirect::get_url( 'jetpack-support-what-data-does-jetpack-sync' ) )
256
	);
257
}
258
259
/**
260
 * Intervene upgrade process so Jetpack themes are downloaded with credentials.
261
 *
262
 * @since 5.3
263
 *
264
 * @param bool   $preempt Whether to preempt an HTTP request's return value. Default false.
265
 * @param array  $r       HTTP request arguments.
266
 * @param string $url     The request URL.
267
 *
268
 * @return array|bool|WP_Error
269
 */
270
function jetpack_theme_update( $preempt, $r, $url ) {
271
	if ( 0 === stripos( $url, JETPACK__WPCOM_JSON_API_BASE . '/rest/v1/themes/download' ) ) {
272
		$file = $r['filename'];
273
		if ( ! $file ) {
274
			return new WP_Error( 'problem_creating_theme_file', esc_html__( 'Problem creating file for theme download', 'jetpack' ) );
0 ignored issues
show
The call to WP_Error::__construct() has too many arguments starting with 'problem_creating_theme_file'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
275
		}
276
		$theme = pathinfo( wp_parse_url( $url, PHP_URL_PATH ), PATHINFO_FILENAME );
277
278
		// Remove filter to avoid endless loop since wpcom_json_api_request_as_blog uses this too.
279
		remove_filter( 'pre_http_request', 'jetpack_theme_update' );
280
		$result = Client::wpcom_json_api_request_as_blog(
281
			"themes/download/$theme.zip",
282
			'1.1',
283
			array(
284
				'stream'   => true,
285
				'filename' => $file,
286
			)
287
		);
288
289
		if ( 200 !== wp_remote_retrieve_response_code( $result ) ) {
290
			return new WP_Error( 'problem_fetching_theme', esc_html__( 'Problem downloading theme', 'jetpack' ) );
0 ignored issues
show
The call to WP_Error::__construct() has too many arguments starting with 'problem_fetching_theme'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
291
		}
292
		return $result;
293
	}
294
	return $preempt;
295
}
296
297
/**
298
 * Add the filter when a upgrade is going to be downloaded.
299
 *
300
 * @since 5.3
301
 *
302
 * @param bool $reply Whether to bail without returning the package. Default false.
303
 *
304
 * @return bool
305
 */
306
function jetpack_upgrader_pre_download( $reply ) {
307
	add_filter( 'pre_http_request', 'jetpack_theme_update', 10, 3 );
308
	return $reply;
309
}
310
311
add_filter( 'upgrader_pre_download', 'jetpack_upgrader_pre_download' );
312
313
/**
314
 * Wraps data in a way so that we can distinguish between objects and array and also prevent object recursion.
315
 *
316
 * @since 6.1.0
317
 *
318
 * @param array|obj $any        Source data to be cleaned up.
319
 * @param array     $seen_nodes Built array of nodes.
320
 *
321
 * @return array
322
 */
323 View Code Duplication
function jetpack_json_wrap( &$any, $seen_nodes = array() ) {
324
	if ( is_object( $any ) ) {
325
		$input        = get_object_vars( $any );
326
		$input['__o'] = 1;
327
	} else {
328
		$input = &$any;
329
	}
330
331
	if ( is_array( $input ) ) {
332
		$seen_nodes[] = &$any;
333
334
		$return = array();
335
336
		foreach ( $input as $k => &$v ) {
337
			if ( ( is_array( $v ) || is_object( $v ) ) ) {
338
				if ( in_array( $v, $seen_nodes, true ) ) {
339
					continue;
340
				}
341
				$return[ $k ] = jetpack_json_wrap( $v, $seen_nodes );
0 ignored issues
show
It seems like $v can also be of type object; however, jetpack_json_wrap() does only seem to accept array|object<obj>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
342
			} else {
343
				$return[ $k ] = $v;
344
			}
345
		}
346
347
		return $return;
348
	}
349
350
	return $any;
351
}
352
353
/**
354
 * Checks if the mime_content_type function is available and return it if so.
355
 *
356
 * The function mime_content_type is enabled by default in PHP, but can be disabled. We attempt to
357
 * enforce this via composer.json, but that won't be checked in majority of cases where
358
 * this would be happening.
359
 *
360
 * @since 7.8.0
361
 *
362
 * @param string $file File location.
363
 *
364
 * @return string|false MIME type or false if functionality is not available.
365
 */
366
function jetpack_mime_content_type( $file ) {
367
	if ( function_exists( 'mime_content_type' ) ) {
368
		return mime_content_type( $file );
369
	}
370
371
	return false;
372
}
373
374
/**
375
 * Checks that the mime type of the specified file is among those in a filterable list of mime types.
376
 *
377
 * @since 7.8.0
378
 *
379
 * @param string $file Path to file to get its mime type.
380
 *
381
 * @return bool
382
 */
383
function jetpack_is_file_supported_for_sideloading( $file ) {
384
	$type = jetpack_mime_content_type( $file );
385
386
	if ( ! $type ) {
387
		return false;
388
	}
389
390
	/**
391
	 * Filter the list of supported mime types for media sideloading.
392
	 *
393
	 * @since 4.0.0
394
	 *
395
	 * @module json-api
396
	 *
397
	 * @param array $supported_mime_types Array of the supported mime types for media sideloading.
398
	 */
399
	$supported_mime_types = apply_filters(
400
		'jetpack_supported_media_sideload_types',
401
		array(
402
			'image/png',
403
			'image/jpeg',
404
			'image/gif',
405
			'image/bmp',
406
			'video/quicktime',
407
			'video/mp4',
408
			'video/mpeg',
409
			'video/ogg',
410
			'video/3gpp',
411
			'video/3gpp2',
412
			'video/h261',
413
			'video/h262',
414
			'video/h264',
415
			'video/x-msvideo',
416
			'video/x-ms-wmv',
417
			'video/x-ms-asf',
418
		)
419
	);
420
421
	// If the type returned was not an array as expected, then we know we don't have a match.
422
	if ( ! is_array( $supported_mime_types ) ) {
423
		return false;
424
	}
425
426
	return in_array( $type, $supported_mime_types, true );
427
}
428
429
/**
430
 * Determine if the current User Agent matches the passed $kind
431
 *
432
 * @param string $kind Category of mobile device to check for.
433
 *                         Either: any, dumb, smart.
434
 * @param bool   $return_matched_agent Boolean indicating if the UA should be returned.
435
 *
436
 * @return bool|string Boolean indicating if current UA matches $kind. If
437
 *                              $return_matched_agent is true, returns the UA string
438
 */
439
function jetpack_is_mobile( $kind = 'any', $return_matched_agent = false ) {
440
441
	/**
442
	 * Filter the value of jetpack_is_mobile before it is calculated.
443
	 *
444
	 * Passing a truthy value to the filter will short-circuit determining the
445
	 * mobile type, returning the passed value instead.
446
	 *
447
	 * @since  4.2.0
448
	 *
449
	 * @param bool|string $matches Boolean if current UA matches $kind or not. If
450
	 *                             $return_matched_agent is true, should return the UA string
451
	 * @param string      $kind Category of mobile device being checked
452
	 * @param bool        $return_matched_agent Boolean indicating if the UA should be returned
453
	 */
454
	$pre = apply_filters( 'pre_jetpack_is_mobile', null, $kind, $return_matched_agent );
0 ignored issues
show
The call to apply_filters() has too many arguments starting with $kind.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
455
	if ( $pre ) {
456
		return $pre;
457
	}
458
459
	$return      = false;
460
	$device_info = Device_Detection::get_info();
461
462
	if ( 'any' === $kind ) {
463
		$return = $device_info['is_phone'];
464
	} elseif ( 'smart' === $kind ) {
465
		$return = $device_info['is_smartphone'];
466
	} elseif ( 'dumb' === $kind ) {
467
		$return = $device_info['is_phone'] && ! $device_info['is_smartphone'];
468
	}
469
470
	if ( $return_matched_agent && true === $return ) {
471
		$return = $device_info['is_phone_matched_ua'];
472
	}
473
474
	/**
475
	 * Filter the value of jetpack_is_mobile
476
	 *
477
	 * @since  4.2.0
478
	 *
479
	 * @param bool|string $matches Boolean if current UA matches $kind or not. If
480
	 *                             $return_matched_agent is true, should return the UA string
481
	 * @param string      $kind Category of mobile device being checked
482
	 * @param bool        $return_matched_agent Boolean indicating if the UA should be returned
483
	 */
484
	return apply_filters( 'jetpack_is_mobile', $return, $kind, $return_matched_agent );
0 ignored issues
show
The call to apply_filters() has too many arguments starting with $kind.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
485
}
486
487
/**
488
 * Determine whether the current request is for accessing the frontend.
489
 *
490
 * @return bool True if it's a frontend request, false otherwise.
491
 */
492
function jetpack_is_frontend() {
493
	$is_frontend = true;
494
495
	if (
496
		is_admin() ||
497
		wp_doing_ajax() ||
498
		wp_doing_cron() ||
499
		wp_is_json_request() ||
500
		wp_is_jsonp_request() ||
501
		wp_is_xml_request() ||
502
		is_feed() ||
503
		( defined( 'REST_REQUEST' ) && REST_REQUEST ) ||
504
		( defined( 'REST_API_REQUEST' ) && REST_API_REQUEST ) ||
505
		( defined( 'WP_CLI' ) && WP_CLI )
506
	) {
507
		$is_frontend = false;
508
	}
509
510
	/**
511
	 * Filter whether the current request is for accessing the frontend.
512
	 *
513
	 * @since  9.0.0
514
	 *
515
	 * @param bool $is_frontend Whether the current request is for accessing the frontend.
516
	 */
517
	return (bool) apply_filters( 'jetpack_is_frontend', $is_frontend );
518
}
519