Completed
Push — update/only-hide-cards-if-rewi... ( 1d27cc...55885e )
by
unknown
215:14 queued 204:17
created

Jetpack_Search_Helpers::get_widget_tracks_value()   C

Complexity

Conditions 21
Paths 80

Size

Total Lines 80
Code Lines 51

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 21
eloc 51
nc 80
nop 2
dl 0
loc 80
rs 5.0057
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
class Jetpack_Search_Helpers {
4
	const FILTER_WIDGET_BASE = 'jetpack-search-filters';
5
6
	static function get_search_url() {
7
		$query_args = $_GET;
8
9
		// Handle the case where a permastruct is being used, such as /search/{$query}
10
		if ( ! isset( $query_args['s'] ) ) {
11
			$query_args['s'] = get_search_query();
12
		}
13
14
		if ( isset( $query_args['paged'] ) ) {
15
			unset( $query_args['paged'] );
16
		}
17
18
		$query = http_build_query( $query_args );
19
		return home_url( "?{$query}" );
20
	}
21
22
	static function add_query_arg( $key, $value = false, $url = false ) {
23
		$url = empty( $url ) ? self::get_search_url() : $url;
24
		if ( is_array( $key ) ) {
25
			return add_query_arg( $key, $url );
26
		}
27
28
		return add_query_arg( $key, $value, $url );
29
	}
30
31
	static function remove_query_arg( $key, $url = false ) {
32
		$url = empty( $url ) ? self::get_search_url() : $url;
33
		return remove_query_arg( $key, $url );
34
	}
35
36
	static function get_widget_option_name() {
37
		return sprintf( 'widget_%s', self::FILTER_WIDGET_BASE );
38
	}
39
40
	static function get_widgets_from_option() {
41
		$widget_options = get_option( self::get_widget_option_name(), array() );
42
43
		// We don't need this
44
		if ( ! empty( $widget_options ) && isset( $widget_options['_multiwidget'] ) ) {
45
			unset( $widget_options['_multiwidget'] );
46
		}
47
48
		return $widget_options;
49
	}
50
51
	static function build_widget_id( $number ) {
52
		return sprintf( '%s-%d', self::FILTER_WIDGET_BASE, $number );
53
	}
54
55
	static function is_active_widget( $widget_id ) {
56
		return (bool) is_active_widget( false, $widget_id, self::FILTER_WIDGET_BASE );
57
	}
58
59
	static function get_filters_from_widgets() {
60
		$filters = array();
61
62
		$widget_options = self::get_widgets_from_option();
63
		if ( empty( $widget_options ) ) {
64
			return $filters;
65
		}
66
67
		foreach ( (array) $widget_options as $number => $settings ) {
68
			$widget_id = self::build_widget_id( $number );
69
			if ( ! self::is_active_widget( $widget_id ) || empty( $settings['filters'] ) ) {
70
				continue;
71
			}
72
73
			foreach ( (array) $settings['filters'] as $widget_filter ) {
74
				$widget_filter['widget_id'] = $widget_id;
75
				$key = sprintf( '%s_%d', $widget_filter['type'], count( $filters ) );
76
77
				if ( empty( $widget_filter['name'] ) ) {
78
					$widget_filter['name'] = self::generate_widget_filter_name( $widget_filter );
79
				}
80
81
				$filters[ $key ] = $widget_filter;
82
			}
83
		}
84
85
		return $filters;
86
	}
87
88
	static function generate_widget_filter_name( $widget_filter ) {
89
		$name = '';
90
91
		switch ( $widget_filter['type'] ) {
92
			case 'post_type':
93
				$name = _x( 'Post Types', 'label for filtering posts', 'jetpack' );
94
				break;
95
			case 'date_histogram':
96
				switch ( $widget_filter['field'] ) {
97
					case 'post_date':
98 View Code Duplication
					case 'post_date_gmt':
99
						switch ( $widget_filter['interval'] ) {
100
							case 'month':
101
								$name = _x( 'Month', 'label for filtering posts', 'jetpack' );
102
								break;
103
							case 'year':
104
								$name = _x( 'Year', 'label for filtering posts', 'jetpack' );
105
								break;
106
						}
107
						break;
108
					case 'post_modified':
109 View Code Duplication
					case 'post_modified_gmt':
110
						switch ( $widget_filter['interval'] ) {
111
							case 'month':
112
								$name = _x( 'Month Updated', 'label for filtering posts', 'jetpack' );
113
								break;
114
							case 'year':
115
								$name = _x( 'Year Updated', 'label for filtering posts', 'jetpack' );
116
								break;
117
						}
118
						break;
119
				}
120
				break;
121
			case 'taxonomy':
122
				$tax = get_taxonomy( $widget_filter['taxonomy'] );
123
				if ( ! $tax ) {
124
					break;
125
				}
126
127
				if ( isset( $tax->label ) ) {
128
					$name = $tax->label;
129
				} else if ( isset( $tax->labels ) && isset( $tax->labels->name ) ) {
130
					$name = $tax->labels->name;
131
				}
132
				break;
133
		}
134
135
		return $name;
136
	}
137
138
	/**
139
	 * Whether we should rerun a search in the customizer preview or not.
140
	 *
141
	 * @since 5.8.0
142
	 *
143
	 * @return bool
144
	 */
145
	static function should_rerun_search_in_customizer_preview() {
146
		// Only update when in a customizer preview and data is being posted.
147
		// Check for $_POST removes an extra update when the customizer loads.
148
		//
149
		// Note: We use $GLOBALS['wp_customize'] here instead of is_customize_preview() to support unit tests.
150
		if ( ! isset( $GLOBALS['wp_customize'] ) || ! $GLOBALS['wp_customize']->is_preview() || empty( $_POST ) ) {
151
			return false;
152
		}
153
154
		return true;
155
	}
156
157
	/**
158
	 * Since PHP's built-in array_diff() works by comparing the values that are in array 1 to the other arrays,
159
	 * if there are less values in array 1, it's possible to get an empty diff where one might be expected.
160
	 *
161
	 * @since 5.8.0
162
	 *
163
	 * @param array $array_1
164
	 * @param array $array_2
165
	 *
166
	 * @return array
167
	 */
168
	static function array_diff( $array_1, $array_2 ) {
169
		// If the array counts are the same, then the order doesn't matter. If the count of
170
		// $array_1 is higher than $array_2, that's also fine. If the count of $array_2 is higher,
171
		// we need to swap the array order though.
172
		if ( count( $array_1 ) != count( $array_2 ) && count( $array_2 ) > count( $array_1 ) ) {
173
			$temp = $array_1;
174
			$array_1 = $array_2;
175
			$array_2 = $temp;
176
		}
177
178
		// Disregard keys
179
		return array_values( array_diff( $array_1, $array_2 ) );
180
	}
181
182
	/**
183
	 * Given the widget instance, will return true when selected post types differ from searchable post types.
184
	 *
185
	 * @since 5.8.0
186
	 *
187
	 * @param array $instance
188
	 * @return bool
189
	 */
190
	static function post_types_differ_searchable( $instance ) {
191
		if ( empty( $instance['post_types'] ) ) {
192
			return false;
193
		}
194
195
		$searchable_post_types = get_post_types( array( 'exclude_from_search' => false ) );
196
		$diff_of_searchable = self::array_diff( $searchable_post_types, (array) $instance['post_types'] );
197
198
		return ! empty( $diff_of_searchable );
199
	}
200
201
	/**
202
	 * Given the widget instance, will return true when selected post types differ from the post type filters
203
	 * applied to the search.
204
	 *
205
	 * @since 5.8.0
206
	 *
207
	 * @param array $instance
208
	 * @return bool
209
	 */
210
	static function post_types_differ_query( $instance ) {
211
		if ( empty( $instance['post_types'] ) ) {
212
			return false;
213
		}
214
215
		if ( empty( $_GET['post_type'] ) ) {
216
			$post_types_from_query = array();
217
		} else if ( is_array( $_GET['post_type'] ) ) {
218
			$post_types_from_query = $_GET['post_type'];
219
		} else {
220
			$post_types_from_query = (array) explode( ',',  $_GET['post_type'] );
221
		}
222
223
		$post_types_from_query = array_map( 'trim', $post_types_from_query );
224
225
		$diff_query = self::array_diff( (array) $instance['post_types'], $post_types_from_query );
226
		return ! empty( $diff_query );
227
	}
228
229
	static function get_widget_tracks_value( $old_value, $new_value ) {
230
		$old_value = (array) $old_value;
231
		if ( isset( $old_value['_multiwidget'] ) ) {
232
			unset( $old_value['_multiwidget'] );
233
		}
234
235
		$new_value = (array) $new_value;
236
		if ( isset( $new_value['_multiwidget'] ) ) {
237
			unset( $new_value['_multiwidget'] );
238
		}
239
240
		$action = '';
0 ignored issues
show
Unused Code introduced by
$action is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
241
		$old_keys = array_keys( $old_value );
242
		$new_keys = array_keys( $new_value );
243
244
		if ( count( $new_keys ) > count( $old_keys ) ) { // This is the case for a widget being added
245
			$diff = self::array_diff( $new_keys, $old_keys );
246
			$action = 'widget_added';
247
			$widget = empty( $diff ) || ! isset( $new_value[ $diff[0] ] )
248
				? false
249
				: $new_value[ $diff[0] ];
250
		} else if ( count( $old_keys ) > count( $new_keys ) ) { // This is the case for a widget being deleted
251
			$diff = self::array_diff( $old_keys, $new_keys );
252
			$action = 'widget_deleted';
253
			$widget = empty( $diff ) || ! isset( $old_value[ $diff[0] ] )
254
				? false
255
				: $old_value[ $diff[0] ];
256
		} else {
257
			$action = 'widget_updated';
258
			$widget = false;
259
260
			// This is a bit crazy. Since there can be multiple widgets stored in a single option,
261
			// we need to diff the old and new values to figure out which widget was updated.
262
			foreach ( $new_value as $key => $new_instance ) {
263
				if ( ! isset( $old_value[ $key ] ) ) {
264
					continue;
265
				}
266
				$old_instance = $old_value[ $key ];
267
268
				// First, let's test the keys of each instance
269
				$diff = self::array_diff( array_keys( $new_instance ), array_keys( $old_instance ) );
270
				if ( ! empty( $diff ) ) {
271
					$widget = $new_instance;
272
					break;
273
				}
274
275
				// Next, lets's loop over each value and compare it
276
				foreach ( $new_instance as $k => $v ) {
277
					if ( is_scalar( $v ) && (string) $v !== (string) $old_instance[ $k ] ) {
278
						$widget = $new_instance;
279
						break;
280
					}
281
282
					if ( 'filters' == $k ) {
283
						if ( count( $new_instance['filters'] ) != count( $old_instance['filters'] ) ) {
284
							$widget = $new_instance;
285
							break;
286
						}
287
288
						foreach ( $v as $filter_key => $new_filter_value ) {
289
							$diff = self::array_diff( $new_filter_value, $old_instance[ 'filters' ][ $filter_key ] );
290
							if ( ! empty( $diff ) ) {
291
								$widget = $new_instance;
292
								break;
293
							}
294
						}
295
					}
296
				}
297
			}
298
		}
299
300
		if ( empty( $action ) || empty( $widget ) ) {
301
			return false;
302
		}
303
304
		return array(
305
			'action' => $action,
306
			'widget' => self::get_widget_properties_for_tracks( $widget ),
307
		);
308
	}
309
310
	static function get_widget_properties_for_tracks( $widget ) {
311
		$sanitized = array();
312
313
		foreach ( (array) $widget as $key => $value ) {
314
			if ( '_multiwidget' == $key ) {
315
				continue;
316
			}
317
			if ( is_scalar( $value ) ) {
318
				$key = str_replace( '-', '_', sanitize_key( $key ) );
319
				$key = "widget_{$key}";
320
				$sanitized[ $key ] = $value;
321
			}
322
		}
323
324
		$filters_properties = ! empty( $widget['filters'] )
325
			? self::get_filter_properties_for_tracks( $widget['filters'] )
326
			: array();
327
328
		return array_merge( $sanitized, $filters_properties );
329
	}
330
331
	static function get_filter_properties_for_tracks( $filters ) {
332
		if ( empty( $filters ) ) {
333
			return $filters;
334
		}
335
336
		$filters_properties = array(
337
			'widget_filter_count' => count( $filters ),
338
		);
339
340
		foreach ( $filters as $filter ) {
341
			if ( empty( $filter['type'] ) ) {
342
				continue;
343
			}
344
345
			$key = sprintf( 'widget_filter_type_%s', $filter['type'] );
346
			if ( isset( $filters_properties[ $key ] ) ) {
347
				$filters_properties[ $key ]++;
348
			} else {
349
				$filters_properties[ $key ] = 1;
350
			}
351
		}
352
353
		return $filters_properties;
354
	}
355
356
	/**
357
	 * Gets the active post types given a set of filters.
358
	 *
359
	 * @param array $filters The active filters for the current query.
360
	 * @param array $default_post_types The default post types.
361
	 *
362
	 * @return array
363
	 */
364
	public static function get_active_post_types( $filters, $default_post_types ) {
365
		$active_post_types = array();
366
		foreach( $filters as $item ) {
367
			if ( ( 'post_type' == $item['type'] ) && isset( $item['query_vars']['post_type'] ) ) {
368
				$active_post_types[] = $item['query_vars']['post_type'];
369
			}
370
		}
371
		return $active_post_types;
372
	}
373
374
	/**
375
	 * Sets active to false on all post type buckets.
376
	 *
377
	 * @param array $filters The available filters for the current query.
378
	 *
379
	 * @return array $modified The filters for the current query with modified active field.
380
	 */
381
	public static function remove_active_from_post_type_buckets( $filters ) {
382
		$modified = $filters;
383
		foreach ( $filters as $key => $filter ) {
384
			if ( 'post_type' === $filter['type'] && ! empty( $filter['buckets'] ) ) {
385
				foreach ( $filter['buckets'] as $k => $bucket ) {
386
					$bucket['active']                  = false;
387
					$modified[ $key ]['buckets'][ $k ] = $bucket;
388
				}
389
			}
390
		}
391
392
		return $modified;
393
	}
394
395
	/**
396
	 * Given a url and an array of post types, will ensure that the post types are properly applied to the URL as args.
397
	 *
398
	 * @param string $url        The URL to add post types to.
399
	 * @param array  $post_types An array of post types that should be added to the URL.
400
	 *
401
	 * @return string $url The URL with added post types.
402
	 */
403
	public static function add_post_types_to_url( $url, $post_types ) {
404
		$url = Jetpack_Search_Helpers::remove_query_arg( 'post_type', $url );
0 ignored issues
show
Documentation introduced by
$url is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
405
		if ( empty( $post_types ) ) {
406
			return $url;
407
		}
408
409
		$url = Jetpack_Search_Helpers::add_query_arg(
410
			'post_type',
411
			implode( ',', $post_types ),
0 ignored issues
show
Documentation introduced by
implode(',', $post_types) is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
412
			$url
413
		);
414
415
		return $url;
416
	}
417
418
	/**
419
	 * Since we provide support for the widget restricting post types by adding the selected post types as
420
	 * active filters, if removing a post type filter would result in there no longer be post_type args in the URL,
421
	 * we need to be sure to add them back.
422
	 *
423
	 * @param array $filters    An array of possible filters for the current query.
424
	 * @param array $post_types The post types to ensure are on the link.
425
	 *
426
	 * @return array $modified The updated array of filters with post typed added to the remove URLs.
427
	 */
428
	public static function ensure_post_types_on_remove_url( $filters, $post_types ) {
429
		$modified = $filters;
430
431
		foreach ( (array) $filters as $filter_key => $filter ) {
432
			if ( 'post_type' !== $filter['type'] || empty( $filter['buckets'] ) ) {
433
				$modified[ $filter_key ] = $filter;
434
				continue;
435
			}
436
437
			foreach ( (array) $filter['buckets'] as $bucket_key => $bucket ) {
438
				if ( empty( $bucket['remove_url'] ) ) {
439
					continue;
440
				}
441
442
				$parsed = wp_parse_url( $bucket['remove_url'] );
443
				if ( ! $parsed ) {
444
					continue;
445
				}
446
447
				$query = array();
448
				if ( ! empty( $parsed['query'] ) ) {
449
					wp_parse_str( $parsed['query'], $query );
450
				}
451
452
				if ( empty( $query['post_type'] ) ) {
453
					$modified[ $filter_key ]['buckets'][ $bucket_key ]['remove_url'] = self::add_post_types_to_url(
454
						$bucket['remove_url'],
455
						$post_types
456
					);
457
				}
458
			}
459
		}
460
461
		return $modified;
462
	}
463
464
	/**
465
	 * Given an array of filters or active buckets, will filter out any that are post types.
466
	 *
467
	 * @param array $filters The array of filters or active buckets.
468
	 * @return array
469
	 */
470
	public static function filter_post_types( $filters ) {
471
		$no_post_types = array();
472
473
		foreach ( (array) $filters as $key => $filter ) {
474
			if ( empty( $filter['type'] ) || 'post_type' !== $filter['type'] ) {
475
				if ( is_int( $key ) ) {
476
					$no_post_types[] = $filter;
477
				} else {
478
					$no_post_types[ $key ] = $filter;
479
				}
480
			}
481
		}
482
483
		return $no_post_types;
484
	}
485
486
	/**
487
	 * Returns a boolen for whether the current site has a VIP index.
488
	 *
489
	 * @return bool
490
	 */
491
	public static function site_has_vip_index() {
492
		$has_vip_index = (
493
			Jetpack_Constants::is_defined( 'JETPACK_SEARCH_VIP_INDEX' ) &&
494
			Jetpack_Constants::get_constant( 'JETPACK_SEARCH_VIP_INDEX' )
495
		);
496
497
		/**
498
		 * Allows developers to filter whether the current site has a VIP index.
499
		 *
500
		 * @module search
501
		 *
502
		 * @since 5.8.0
503
		 *
504
		 * @param bool $has_vip_index Whether the current site has a VIP index.
505
		 */
506
		return apply_filters( 'jetpack_search_has_vip_index', $has_vip_index );
507
	}
508
509
	/**
510
	 * Returns the maximum posts per page for a search query
511
	 *
512
	 * @return int
513
	 */
514
	public static function get_max_posts_per_page() {
515
		return self::site_has_vip_index() ? 1000 : 100;
516
	}
517
518
	/**
519
	 * Returns the maximum offset for a search query
520
	 *
521
	 * @return int
522
	 */
523
	public static function get_max_offset() {
524
		return self::site_has_vip_index() ? 9000 : 1000;
525
	}
526
}
527