Completed
Push — fix/instant-search-non-ascii ( b82854...f54339 )
by
unknown
18:41 queued 10:23
created

modules/search/class.jetpack-search-helpers.php (1 issue)

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
 * Jetpack Search: Jetpack_Search_Helpers class
4
 *
5
 * @package    Jetpack
6
 * @subpackage Jetpack Search
7
 * @since      5.8.0
8
 */
9
10
use Automattic\Jetpack\Constants;
11
12
require_once dirname( __FILE__ ) . '/class-jetpack-search-options.php';
13
14
/**
15
 * Various helper functions for reuse throughout the Jetpack Search code.
16
 *
17
 * @since 5.8.0
18
 */
19
class Jetpack_Search_Helpers {
20
21
	/**
22
	 * The search widget's base ID.
23
	 *
24
	 * @since 5.8.0
25
	 * @var string
26
	 */
27
	const FILTER_WIDGET_BASE = 'jetpack-search-filters';
28
29
	/**
30
	 * Create a URL for the current search that doesn't include the "paged" parameter.
31
	 *
32
	 * @since 5.8.0
33
	 *
34
	 * @return string The search URL.
35
	 */
36
	static function get_search_url() {
37
		$query_args = stripslashes_deep( $_GET );
38
39
		// Handle the case where a permastruct is being used, such as /search/{$query}
40
		if ( ! isset( $query_args['s'] ) ) {
41
			$query_args['s'] = get_search_query();
42
		}
43
44
		if ( isset( $query_args['paged'] ) ) {
45
			unset( $query_args['paged'] );
46
		}
47
48
		$query = http_build_query( $query_args );
49
50
		return home_url( "?{$query}" );
51
	}
52
53
	/**
54
	 * Wraps add_query_arg() with the URL defaulting to the current search URL.
55
	 *
56
	 * @see   add_query_arg()
57
	 *
58
	 * @since 5.8.0
59
	 *
60
	 * @param string|array $key   Either a query variable key, or an associative array of query variables.
61
	 * @param string       $value Optional. A query variable value.
62
	 * @param bool|string  $url   Optional. A URL to act upon. Defaults to the current search URL.
63
	 *
64
	 * @return string New URL query string (unescaped).
65
	 */
66
	static function add_query_arg( $key, $value = false, $url = false ) {
67
		$url = empty( $url ) ? self::get_search_url() : $url;
68
		if ( is_array( $key ) ) {
69
			return add_query_arg( $key, $url );
70
		}
71
72
		return add_query_arg( $key, $value, $url );
73
	}
74
75
	/**
76
	 * Wraps remove_query_arg() with the URL defaulting to the current search URL.
77
	 *
78
	 * @see   remove_query_arg()
79
	 *
80
	 * @since 5.8.0
81
	 *
82
	 * @param string|array $key   Query key or keys to remove.
83
	 * @param bool|string  $query Optional. A URL to act upon.  Defaults to the current search URL.
84
	 *
85
	 * @return string New URL query string (unescaped).
86
	 */
87
	static function remove_query_arg( $key, $url = false ) {
88
		$url = empty( $url ) ? self::get_search_url() : $url;
89
90
		return remove_query_arg( $key, $url );
91
	}
92
93
	/**
94
	 * Returns the name of the search widget's option.
95
	 *
96
	 * @since 5.8.0
97
	 *
98
	 * @return string The search widget option name.
99
	 */
100
	static function get_widget_option_name() {
101
		return sprintf( 'widget_%s', self::FILTER_WIDGET_BASE );
102
	}
103
104
	/**
105
	 * Returns the search widget instances from the widget's option.
106
	 *
107
	 * @since 5.8.0
108
	 *
109
	 * @return array The widget options.
110
	 */
111
	static function get_widgets_from_option() {
112
		$widget_options = get_option( self::get_widget_option_name(), array() );
113
114
		// We don't need this
115
		if ( ! empty( $widget_options ) && isset( $widget_options['_multiwidget'] ) ) {
116
			unset( $widget_options['_multiwidget'] );
117
		}
118
119
		return $widget_options;
120
	}
121
122
	/**
123
	 * Returns the widget ID (widget base plus the numeric ID).
124
	 *
125
	 * @param int $number The widget's numeric ID.
126
	 *
127
	 * @return string The widget's numeric ID prefixed with the search widget base.
128
	 */
129
	static function build_widget_id( $number ) {
130
		return sprintf( '%s-%d', self::FILTER_WIDGET_BASE, $number );
131
	}
132
133
	/**
134
	 * Wrapper for is_active_widget() with the other parameters automatically supplied.
135
	 *
136
	 * @see   is_active_widget()
137
	 *
138
	 * @since 5.8.0
139
	 *
140
	 * @param int $widget_id Widget ID.
141
	 *
142
	 * @return bool Whether the widget is active or not.
143
	 */
144
	static function is_active_widget( $widget_id ) {
145
		return (bool) is_active_widget( false, $widget_id, self::FILTER_WIDGET_BASE, true );
146
	}
147
148
	/**
149
	 * Returns an array of the filters from all active search widgets.
150
	 *
151
	 * @since 5.8.0
152
	 *
153
	 * @param array|null $allowed_widget_ids array of allowed widget IDs.
154
	 *
155
	 * @return array Active filters.
156
	 */
157
	public static function get_filters_from_widgets( $allowed_widget_ids = null ) {
158
		$filters = array();
159
160
		$widget_options = self::get_widgets_from_option();
161
		if ( empty( $widget_options ) ) {
162
			return $filters;
163
		}
164
165
		foreach ( (array) $widget_options as $number => $settings ) {
166
			$widget_id = self::build_widget_id( $number );
167
			if ( ! self::is_active_widget( $widget_id ) || empty( $settings['filters'] ) ) {
168
				continue;
169
			}
170
			if ( isset( $allowed_widget_ids ) && ! in_array( $widget_id, $allowed_widget_ids, true ) ) {
171
				continue;
172
			}
173
174
			foreach ( (array) $settings['filters'] as $widget_filter ) {
175
				$widget_filter['widget_id'] = $widget_id;
176
177
				if ( empty( $widget_filter['name'] ) ) {
178
					$widget_filter['name'] = self::generate_widget_filter_name( $widget_filter );
179
				}
180
181
				$key = sprintf( '%s_%d', $widget_filter['type'], count( $filters ) );
182
183
				$filters[ $key ] = $widget_filter;
184
			}
185
		}
186
187
		return $filters;
188
	}
189
190
	/**
191
	 * Get the localized default label for a date filter.
192
	 *
193
	 * @since 5.8.0
194
	 *
195
	 * @param string $type       Date type, either year or month.
196
	 * @param bool   $is_updated Whether the filter was updated or not (adds "Updated" to the end).
197
	 *
198
	 * @return string The filter label.
199
	 */
200
	static function get_date_filter_type_name( $type, $is_updated = false ) {
201
		switch ( $type ) {
202 View Code Duplication
			case 'year':
203
				$string = ( $is_updated )
204
					? esc_html_x( 'Year Updated', 'label for filtering posts', 'jetpack' )
205
					: esc_html_x( 'Year', 'label for filtering posts', 'jetpack' );
206
				break;
207
			case 'month':
208 View Code Duplication
			default:
209
				$string = ( $is_updated )
210
					? esc_html_x( 'Month Updated', 'label for filtering posts', 'jetpack' )
211
					: esc_html_x( 'Month', 'label for filtering posts', 'jetpack' );
212
				break;
213
		}
214
215
		return $string;
216
	}
217
218
	/**
219
	 * Creates a default name for a filter. Used when the filter label is blank.
220
	 *
221
	 * @since 5.8.0
222
	 *
223
	 * @param array $widget_filter The filter to generate the title for.
224
	 *
225
	 * @return string The suggested filter name.
226
	 */
227
	static function generate_widget_filter_name( $widget_filter ) {
228
		$name = '';
229
230
		switch ( $widget_filter['type'] ) {
231
			case 'post_type':
232
				$name = _x( 'Post Types', 'label for filtering posts', 'jetpack' );
233
				break;
234
235
			case 'date_histogram':
236
				$modified_fields = array(
237
					'post_modified',
238
					'post_modified_gmt',
239
				);
240
				switch ( $widget_filter['interval'] ) {
241
					case 'year':
242
						$name = self::get_date_filter_type_name(
243
							'year',
244
							in_array( $widget_filter['field'], $modified_fields )
245
						);
246
						break;
247
					case 'month':
248
					default:
249
						$name = self::get_date_filter_type_name(
250
							'month',
251
							in_array( $widget_filter['field'], $modified_fields )
252
						);
253
						break;
254
				}
255
				break;
256
257
			case 'taxonomy':
258
				$tax = get_taxonomy( $widget_filter['taxonomy'] );
259
				if ( ! $tax ) {
260
					break;
261
				}
262
263
				if ( isset( $tax->label ) ) {
264
					$name = $tax->label;
265
				} elseif ( isset( $tax->labels ) && isset( $tax->labels->name ) ) {
266
					$name = $tax->labels->name;
267
				}
268
				break;
269
		}
270
271
		return $name;
272
	}
273
274
	/**
275
	 * Whether we should rerun a search in the customizer preview or not.
276
	 *
277
	 * @since 5.8.0
278
	 *
279
	 * @return bool
280
	 */
281
	static function should_rerun_search_in_customizer_preview() {
282
		// Only update when in a customizer preview and data is being posted.
283
		// Check for $_POST removes an extra update when the customizer loads.
284
		//
285
		// Note: We use $GLOBALS['wp_customize'] here instead of is_customize_preview() to support unit tests.
286
		if ( ! isset( $GLOBALS['wp_customize'] ) || ! $GLOBALS['wp_customize']->is_preview() || empty( $_POST ) ) {
287
			return false;
288
		}
289
290
		return true;
291
	}
292
293
	/**
294
	 * Since PHP's built-in array_diff() works by comparing the values that are in array 1 to the other arrays,
295
	 * if there are less values in array 1, it's possible to get an empty diff where one might be expected.
296
	 *
297
	 * @since 5.8.0
298
	 *
299
	 * @param array $array_1
300
	 * @param array $array_2
301
	 *
302
	 * @return array
303
	 */
304
	static function array_diff( $array_1, $array_2 ) {
305
		// If the array counts are the same, then the order doesn't matter. If the count of
306
		// $array_1 is higher than $array_2, that's also fine. If the count of $array_2 is higher,
307
		// we need to swap the array order though.
308
		if ( count( $array_1 ) !== count( $array_2 ) && count( $array_2 ) > count( $array_1 ) ) {
309
			$temp    = $array_1;
310
			$array_1 = $array_2;
311
			$array_2 = $temp;
312
		}
313
314
		// Disregard keys
315
		return array_values( array_diff( $array_1, $array_2 ) );
316
	}
317
318
	/**
319
	 * Given the widget instance, will return true when selected post types differ from searchable post types.
320
	 *
321
	 * @since 5.8.0
322
	 *
323
	 * @param array $post_types An array of post types.
324
	 *
325
	 * @return bool
326
	 */
327
	static function post_types_differ_searchable( $post_types ) {
328
		if ( empty( $post_types ) ) {
329
			return false;
330
		}
331
332
		$searchable_post_types = get_post_types( array( 'exclude_from_search' => false ) );
333
		$diff_of_searchable    = self::array_diff( $searchable_post_types, (array) $post_types );
334
335
		return ! empty( $diff_of_searchable );
336
	}
337
338
	/**
339
	 * Given the array of post types, will return true when these differ from the current search query.
340
	 *
341
	 * @since 5.8.0
342
	 *
343
	 * @param array $post_types An array of post types.
344
	 *
345
	 * @return bool
346
	 */
347
	static function post_types_differ_query( $post_types ) {
348
		if ( empty( $post_types ) ) {
349
			return false;
350
		}
351
352
		if ( empty( $_GET['post_type'] ) ) {
353
			$post_types_from_query = array();
354
		} elseif ( is_array( $_GET['post_type'] ) ) {
355
			$post_types_from_query = $_GET['post_type'];
356
		} else {
357
			$post_types_from_query = (array) explode( ',', $_GET['post_type'] );
358
		}
359
360
		$post_types_from_query = array_map( 'trim', $post_types_from_query );
361
362
		$diff_query = self::array_diff( (array) $post_types, $post_types_from_query );
363
364
		return ! empty( $diff_query );
365
	}
366
367
	/**
368
	 * Determine what Tracks value should be used when updating a widget.
369
	 *
370
	 * @since 5.8.0
371
	 *
372
	 * @param mixed $old_value The old option value.
373
	 * @param mixed $new_value The new option value.
374
	 *
375
	 * @return array|false False if the widget wasn't updated, otherwise an array of the Tracks action and widget properties.
376
	 */
377
	static function get_widget_tracks_value( $old_value, $new_value ) {
378
		$old_value = (array) $old_value;
379
		if ( isset( $old_value['_multiwidget'] ) ) {
380
			unset( $old_value['_multiwidget'] );
381
		}
382
383
		$new_value = (array) $new_value;
384
		if ( isset( $new_value['_multiwidget'] ) ) {
385
			unset( $new_value['_multiwidget'] );
386
		}
387
388
		$old_keys = array_keys( $old_value );
389
		$new_keys = array_keys( $new_value );
390
391
		if ( count( $new_keys ) > count( $old_keys ) ) { // This is the case for a widget being added
392
			$diff   = self::array_diff( $new_keys, $old_keys );
393
			$action = 'widget_added';
394
			$widget = empty( $diff ) || ! isset( $new_value[ $diff[0] ] )
395
				? false
396
				: $new_value[ $diff[0] ];
397
		} elseif ( count( $old_keys ) > count( $new_keys ) ) { // This is the case for a widget being deleted
398
			$diff   = self::array_diff( $old_keys, $new_keys );
399
			$action = 'widget_deleted';
400
			$widget = empty( $diff ) || ! isset( $old_value[ $diff[0] ] )
401
				? false
402
				: $old_value[ $diff[0] ];
403
		} else {
404
			$action = 'widget_updated';
405
			$widget = false;
406
407
			// This is a bit crazy. Since there can be multiple widgets stored in a single option,
408
			// we need to diff the old and new values to figure out which widget was updated.
409
			foreach ( $new_value as $key => $new_instance ) {
410
				if ( ! isset( $old_value[ $key ] ) ) {
411
					continue;
412
				}
413
				$old_instance = $old_value[ $key ];
414
415
				// First, let's test the keys of each instance
416
				$diff = self::array_diff( array_keys( $new_instance ), array_keys( $old_instance ) );
417
				if ( ! empty( $diff ) ) {
418
					$widget = $new_instance;
419
					break;
420
				}
421
422
				// Next, lets's loop over each value and compare it
423
				foreach ( $new_instance as $k => $v ) {
424
					if ( is_scalar( $v ) && (string) $v !== (string) $old_instance[ $k ] ) {
425
						$widget = $new_instance;
426
						break;
427
					}
428
429
					if ( 'filters' == $k ) {
430
						if ( count( $new_instance['filters'] ) != count( $old_instance['filters'] ) ) {
431
							$widget = $new_instance;
432
							break;
433
						}
434
435
						foreach ( $v as $filter_key => $new_filter_value ) {
436
							$diff = self::array_diff( $new_filter_value, $old_instance['filters'][ $filter_key ] );
437
							if ( ! empty( $diff ) ) {
438
								$widget = $new_instance;
439
								break;
440
							}
441
						}
442
					}
443
				}
444
			}
445
		}
446
447
		if ( empty( $action ) || empty( $widget ) ) {
448
			return false;
449
		}
450
451
		return array(
452
			'action' => $action,
453
			'widget' => self::get_widget_properties_for_tracks( $widget ),
454
		);
455
	}
456
457
	/**
458
	 * Creates the widget properties for sending to Tracks.
459
	 *
460
	 * @since 5.8.0
461
	 *
462
	 * @param array $widget The widget instance.
463
	 *
464
	 * @return array The widget properties.
465
	 */
466
	static function get_widget_properties_for_tracks( $widget ) {
467
		$sanitized = array();
468
469
		foreach ( (array) $widget as $key => $value ) {
470
			if ( '_multiwidget' == $key ) {
471
				continue;
472
			}
473
474
			if ( is_scalar( $value ) ) {
475
				$key               = str_replace( '-', '_', sanitize_key( $key ) );
476
				$key               = "widget_{$key}";
477
				$sanitized[ $key ] = $value;
478
			}
479
		}
480
481
		$filters_properties = ! empty( $widget['filters'] )
482
			? self::get_filter_properties_for_tracks( $widget['filters'] )
483
			: array();
484
485
		return array_merge( $sanitized, $filters_properties );
486
	}
487
488
	/**
489
	 * Creates the filter properties for sending to Tracks.
490
	 *
491
	 * @since 5.8.0
492
	 *
493
	 * @param array $filters An array of filters.
494
	 *
495
	 * @return array The filter properties.
496
	 */
497
	static function get_filter_properties_for_tracks( $filters ) {
498
		if ( empty( $filters ) ) {
499
			return $filters;
500
		}
501
502
		$filters_properties = array(
503
			'widget_filter_count' => count( $filters ),
504
		);
505
506
		foreach ( $filters as $filter ) {
507
			if ( empty( $filter['type'] ) ) {
508
				continue;
509
			}
510
511
			$key = sprintf( 'widget_filter_type_%s', $filter['type'] );
512
			if ( isset( $filters_properties[ $key ] ) ) {
513
				$filters_properties[ $key ] ++;
514
			} else {
515
				$filters_properties[ $key ] = 1;
516
			}
517
		}
518
519
		return $filters_properties;
520
	}
521
522
	/**
523
	 * Gets the active post types given a set of filters.
524
	 *
525
	 * @since 5.8.0
526
	 *
527
	 * @param array $filters The active filters for the current query.
528
	 *
529
	 * @return array The active post types.
530
	 */
531
	public static function get_active_post_types( $filters ) {
532
		$active_post_types = array();
533
534
		foreach ( $filters as $item ) {
535
			if ( ( 'post_type' == $item['type'] ) && isset( $item['query_vars']['post_type'] ) ) {
536
				$active_post_types[] = $item['query_vars']['post_type'];
537
			}
538
		}
539
540
		return $active_post_types;
541
	}
542
543
	/**
544
	 * Sets active to false on all post type buckets.
545
	 *
546
	 * @since 5.8.0
547
	 *
548
	 * @param array $filters The available filters for the current query.
549
	 *
550
	 * @return array The filters for the current query with modified active field.
551
	 */
552
	public static function remove_active_from_post_type_buckets( $filters ) {
553
		$modified = $filters;
554
		foreach ( $filters as $key => $filter ) {
555
			if ( 'post_type' === $filter['type'] && ! empty( $filter['buckets'] ) ) {
556
				foreach ( $filter['buckets'] as $k => $bucket ) {
557
					$bucket['active']                  = false;
558
					$modified[ $key ]['buckets'][ $k ] = $bucket;
559
				}
560
			}
561
		}
562
563
		return $modified;
564
	}
565
566
	/**
567
	 * Given a url and an array of post types, will ensure that the post types are properly applied to the URL as args.
568
	 *
569
	 * @since 5.8.0
570
	 *
571
	 * @param string $url        The URL to add post types to.
572
	 * @param array  $post_types An array of post types that should be added to the URL.
573
	 *
574
	 * @return string The URL with added post types.
575
	 */
576
	public static function add_post_types_to_url( $url, $post_types ) {
577
		$url = Jetpack_Search_Helpers::remove_query_arg( 'post_type', $url );
578
		if ( empty( $post_types ) ) {
579
			return $url;
580
		}
581
582
		$url = Jetpack_Search_Helpers::add_query_arg(
583
			'post_type',
584
			implode( ',', $post_types ),
585
			$url
586
		);
587
588
		return $url;
589
	}
590
591
	/**
592
	 * Since we provide support for the widget restricting post types by adding the selected post types as
593
	 * active filters, if removing a post type filter would result in there no longer be post_type args in the URL,
594
	 * we need to be sure to add them back.
595
	 *
596
	 * @since 5.8.0
597
	 *
598
	 * @param array $filters    An array of possible filters for the current query.
599
	 * @param array $post_types The post types to ensure are on the link.
600
	 *
601
	 * @return array The updated array of filters with post typed added to the remove URLs.
602
	 */
603
	public static function ensure_post_types_on_remove_url( $filters, $post_types ) {
604
		$modified = $filters;
605
606
		foreach ( (array) $filters as $filter_key => $filter ) {
607
			if ( 'post_type' !== $filter['type'] || empty( $filter['buckets'] ) ) {
608
				$modified[ $filter_key ] = $filter;
609
				continue;
610
			}
611
612
			foreach ( (array) $filter['buckets'] as $bucket_key => $bucket ) {
613
				if ( empty( $bucket['remove_url'] ) ) {
614
					continue;
615
				}
616
617
				$parsed = wp_parse_url( $bucket['remove_url'] );
618
				if ( ! $parsed ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $parsed of type string|false is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
619
					continue;
620
				}
621
622
				$query = array();
623
				if ( ! empty( $parsed['query'] ) ) {
624
					wp_parse_str( $parsed['query'], $query );
625
				}
626
627
				if ( empty( $query['post_type'] ) ) {
628
					$modified[ $filter_key ]['buckets'][ $bucket_key ]['remove_url'] = self::add_post_types_to_url(
629
						$bucket['remove_url'],
630
						$post_types
631
					);
632
				}
633
			}
634
		}
635
636
		return $modified;
637
	}
638
639
	/**
640
	 * Wraps a WordPress filter called "jetpack_search_disable_widget_filters" that allows
641
	 * developers to disable filters supplied by the search widget. Useful if filters are
642
	 * being defined at the code level.
643
	 *
644
	 * @since 5.8.0
645
	 *
646
	 * @return bool
647
	 */
648
	public static function are_filters_by_widget_disabled() {
649
		/**
650
		 * Allows developers to disable filters being set by widget, in favor of manually
651
		 * setting filters via `Jetpack_Search::set_filters()`.
652
		 *
653
		 * @module search
654
		 *
655
		 * @since  5.7.0
656
		 *
657
		 * @param bool false
658
		 */
659
		return apply_filters( 'jetpack_search_disable_widget_filters', false );
660
	}
661
662
	/**
663
	 * Returns the maximum posts per page for a search query.
664
	 *
665
	 * @since 5.8.0
666
	 *
667
	 * @return int
668
	 */
669
	public static function get_max_posts_per_page() {
670
		return Jetpack_Search_Options::site_has_vip_index() ? 1000 : 100;
671
	}
672
673
	/**
674
	 * Returns the maximum offset for a search query.
675
	 *
676
	 * @since 5.8.0
677
	 *
678
	 * @return int
679
	 */
680
	public static function get_max_offset() {
681
		return Jetpack_Search_Options::site_has_vip_index() ? 9000 : 1000;
682
	}
683
684
	/**
685
	 * Returns the maximum offset for a search query.
686
	 *
687
	 * @since 8.4.0
688
	 * @param string $locale    A potentially valid locale string.
689
	 *
690
	 * @return bool
691
	 */
692
	public static function is_valid_locale( $locale ) {
693
		if ( ! class_exists( 'GP_Locales' ) ) {
694
			if ( defined( 'JETPACK__GLOTPRESS_LOCALES_PATH' ) && file_exists( JETPACK__GLOTPRESS_LOCALES_PATH ) ) {
695
				require JETPACK__GLOTPRESS_LOCALES_PATH;
696
			} else {
697
				// Assume locale to be valid if we can't check with GlotPress.
698
				return true;
699
			}
700
		}
701
		return false !== GP_Locales::by_field( 'wp_locale', $locale );
702
	}
703
704
	/**
705
	 * Get the version number to use when loading the file. Allows us to bypass cache when developing.
706
	 *
707
	 * @since 8.6.0
708
	 * @param string $file Path of the file we are looking for.
709
	 * @return string $script_version Version number.
710
	 */
711
	public static function get_asset_version( $file ) {
712
		return Jetpack::is_development_version() && file_exists( JETPACK__PLUGIN_DIR . $file )
713
			? filemtime( JETPACK__PLUGIN_DIR . $file )
714
			: JETPACK__VERSION;
715
	}
716
717
718
	/**
719
	 * Generates a customizer settings ID for a given post type.
720
	 *
721
	 * @since 8.8.0
722
	 * @param object $post_type Post type object returned from get_post_types.
723
	 * @return string $customizer_id Customizer setting ID.
724
	 */
725
	public static function generate_post_type_customizer_id( $post_type ) {
726
		return Jetpack_Search_Options::OPTION_PREFIX . 'disable_post_type_' . $post_type->name;
727
	}
728
729
	/**
730
	 * Generates an array of post types associated with their customizer IDs.
731
	 *
732
	 * @since 8.8.0
733
	 * @return array $ids Post type => post type customizer ID object.
734
	 */
735
	public static function generate_post_type_customizer_ids() {
736
		return array_map(
737
			array( 'self', 'generate_post_type_customizer_id' ),
738
			get_post_types( array( 'exclude_from_search' => false ), 'objects' )
739
		);
740
	}
741
742
	/**
743
	 * Sanitizes a checkbox value for writing to the database.
744
	 *
745
	 * @since 8.9.0
746
	 *
747
	 * @param any $value from the customizer form.
748
	 * @return string either '0' or '1'.
749
	 */
750
	public static function sanitize_checkbox_value( $value ) {
751
		return true === $value ? '1' : '0';
752
	}
753
754
	/**
755
	 * Sanitizes a checkbox value for rendering the Customizer.
756
	 *
757
	 * @since 8.9.0
758
	 *
759
	 * @param any $value from the database.
760
	 * @return boolean
761
	 */
762
	public static function sanitize_checkbox_value_for_js( $value ) {
763
		return '1' === $value;
764
	}
765
}
766