Completed
Push — add/search ( 19e5a5 )
by
unknown
10:06
created

Jetpack_Search::instance()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 5
nc 2
nop 0
dl 0
loc 9
rs 9.6666
c 0
b 0
f 0
1
<?php
2
3
class Jetpack_Search {
4
5
	protected $found_posts = 0;
6
7
	/**
8
	 * The maximum offset ('from' param), since deep pages get exponentially slower.
9
	 *
10
	 * @see https://www.elastic.co/guide/en/elasticsearch/guide/current/pagination.html
11
	 */
12
	protected $max_offset = 200;
13
14
	protected $search_result;
15
16
	protected $original_blog_id;
17
	protected $jetpack_blog_id;
18
19
	protected $aggregations = array();
20
	protected $max_aggregations_count = 100;
21
22
	protected static $instance;
23
24
	//Languages with custom analyzers, other languages are supported,
25
	// but are analyzed with the default analyzer.
26
	public static $analyzed_langs = array( 'ar', 'bg', 'ca', 'cs', 'da', 'de', 'el', 'en', 'es', 'eu', 'fa', 'fi', 'fr', 'he', 'hi', 'hu', 'hy', 'id', 'it', 'ja', 'ko', 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr', 'zh' );
27
28
	protected function __construct() {
29
		/* Don't do anything, needs to be initialized via instance() method */
30
	}
31
32
	public function __clone() {
33
		wp_die( "Please don't __clone Jetpack_Search" );
34
	}
35
36
	public function __wakeup() {
37
		wp_die( "Please don't __wakeup Jetpack_Search" );
38
	}
39
40
	/**
41
	 * Get singleton instance of Jetpack_Search
42
	 *
43
	 * Instantiates and sets up a new instance if needed, or returns the singleton
44
	 *
45
	 * @module search
46
	 *
47
	 * @return Jetpack_Search The Jetpack_Search singleton
48
	 */
49
	public static function instance() {
50
		if ( ! isset( self::$instance ) ) {
51
			self::$instance = new Jetpack_Search();
52
53
			self::$instance->setup();
54
		}
55
56
		return self::$instance;
57
	}
58
59
	/**
60
	 * Perform various setup tasks for the class
61
	 *
62
	 * Checks various pre-requisites and adds hooks
63
	 *
64
	 * @module search
65
	 */
66
	public function setup() {
67
		if ( ! Jetpack::is_active() ) {
68
			return;
69
		}
70
71
		$this->jetpack_blog_id = Jetpack::get_option( 'id' );
72
73
		if ( ! $this->jetpack_blog_id ) {
74
			return;
75
		}
76
77
		$this->init_hooks();
78
	}
79
80
	/**
81
	 * Setup the various hooks needed for the plugin to take over Search duties
82
	 *
83
	 * @module search
84
	 */
85
	public function init_hooks() {
86
		add_action( 'widgets_init', array( $this, 'action__widgets_init' ) );
87
88
		if ( ! is_admin() ) {
89
			add_filter( 'posts_pre_query', array( $this, 'filter__posts_pre_query' ), 10, 2 );
90
91
			add_filter( 'jetpack_search_es_wp_query_args', array( $this, 'filter__add_date_filter_to_query' ),  10, 2 );
92
		}
93
	}
94
95
	/*
96
	 * Run a search on the WP.com public API.
97
	 *
98
	 * @module search
99
	 *
100
	 * @param array $es_args Args conforming to the WP.com /sites/<blog_id>/search endpoint
101
	 *
102
	 * @return object|WP_Error The response from the public api, or a WP_Error
103
	 */
104
	public function search( array $es_args ) {
105
		$service_url = 'https://public-api.wordpress.com/rest/v1/sites/' . $this->jetpack_blog_id . '/search';
106
107
		$start_time = microtime( true );
108
109
		$request = wp_remote_post( $service_url, array(
110
			'headers' => array(
111
				'Content-Type' => 'application/json',
112
			),
113
			'timeout'    => 10,
114
			'user-agent' => 'jetpack_search',
115
			'body'       => json_encode( $es_args ),
116
		) );
117
118
		$end_time = microtime( true );
119
120
		if ( is_wp_error( $request ) ) {
121
			return $request;
122
		}
123
124
		$response = json_decode( wp_remote_retrieve_body( $request ), true );
125
126
		$took = is_array( $response ) && $response['took'] ? $response['took'] : null;
127
128
		$query = array(
129
			'args'          => $es_args,
130
			'response'      => $response,
131
			'response_code' => wp_remote_retrieve_response_code( $request ),
132
			'elapsed_time'   => ( $end_time - $start_time ) * 1000, // Convert from float seconds to ms
133
			'es_time'       => $took,
134
			'url'           => $service_url,
135
		);
136
137
		do_action( 'did_jetpack_search_query', $query );
138
139
		return $response;
140
	}
141
142
	/**
143
	 * Bypass the normal Search query and offload it to Jetpack servers
144
	 *
145
	 * This is the main hook of the plugin and is responsible for returning the posts that match the search query
146
	 *
147
	 * @module search
148
	 *
149
	 * @param array $posts Current array of posts (still pre-query)
150
	 * @param WP_Query $query The WP_Query being filtered
151
	 *
152
	 * @return array Array of matching posts
153
	 */
154
	public function filter__posts_pre_query( $posts, $query ) {
155
		if ( ! $query->is_main_query() || ! $query->is_search() ) {
156
			return $posts;
157
		}
158
159
		$this->do_search( $query );
160
161
		if ( ! is_array( $this->search_result ) ) {
162
			return $posts;
163
		}
164
165
		// If no results, nothing to do
166
		if ( ! count( $this->search_result['results']['hits'] ) ) {
167
			return array();
168
		}
169
170
		$post_ids = array();
171
172
		foreach ( $this->search_result['results']['hits'] as $result ) {
173
			$post_ids[] = (int) $result['fields']['post_id'];
174
		}
175
176
		// Query all posts now
177
		$args = array(
178
			'post__in' => $post_ids,
179
			'perm'     => 'readable',
180
		);
181
182
		$posts_query = new WP_Query( $args );
183
184
		// WP Core doesn't call the set_found_posts and its filters when filtering posts_pre_query like we do, so need to
185
		// do these manually
186
		$query->found_posts   = $this->found_posts;
187
		$query->max_num_pages = ceil( $this->found_posts / $query->get( 'posts_per_page' ) );
188
189
		return $posts_query->posts;
190
	}
191
192
	/**
193
	 * Build up the search, then run it against the Jetpack servers
194
	 *
195
	 * @param WP_Query $query The original WP_Query to use for the parameters of our search
196
	 */
197
	public function do_search( WP_Query $query ) {
198
		global $wpdb;
199
200
		if ( ! $query->is_main_query() || ! $query->is_search() ) {
201
			return;
202
		}
203
204
		$page = ( $query->get( 'paged' ) ) ? absint( $query->get( 'paged' ) ) : 1;
205
206
		$posts_per_page = $query->get( 'posts_per_page' );
207
208
		// ES API does not allow more than 15 results at a time
209
		if ( $posts_per_page > 15 ) {
210
			$posts_per_page = 15;
211
		}
212
213
		// Start building the WP-style search query args
214
		// They'll be translated to ES format args later
215
		$es_wp_query_args = array(
216
			'query'          => $query->get( 's' ),
217
			'posts_per_page' => $posts_per_page,
218
			'paged'          => $page,
219
			'orderby'        => $query->get( 'orderby' ),
220
			'order'          => $query->get( 'order' ),
221
		);
222
223
		if ( ! empty( $this->aggregations ) ) {
224
			$es_wp_query_args['aggregations'] = $this->aggregations;
225
		}
226
227
		// Did we query for authors?
228
		if ( $query->get( 'author_name' ) ) {
229
			$es_wp_query_args['author_name'] = $query->get( 'author_name' );
230
		}
231
232
		$es_wp_query_args['post_type'] = $this->get_es_wp_query_post_type_for_query( $query );
233
234
		$es_wp_query_args['terms']     = $this->get_es_wp_query_terms_for_query( $query );
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned correctly; expected 1 space but found 5 spaces

This check looks for improperly formatted assignments.

Every assignment must have exactly one space before and one space after the equals operator.

To illustrate:

$a = "a";
$ab = "ab";
$abc = "abc";

will have no issues, while

$a   = "a";
$ab  = "ab";
$abc = "abc";

will report issues in lines 1 and 2.

Loading history...
235
236
237
		/**
238
		 * Modify the search query parameters, such as controlling the post_type.
239
		 *
240
		 * These arguments are in the format of WP_Query arguments
241
		 *
242
		 * @module search
243
		 *
244
		 * @since 5.0.0
245
		 *
246
		 * @param array $es_wp_query_args The current query args, in WP_Query format
247
		 * @param WP_Query $query The original query object
248
		 */
249
		$es_wp_query_args = apply_filters( 'jetpack_search_es_wp_query_args', $es_wp_query_args, $query );
250
251
		// If page * posts_per_page is greater than our max offset, send a 404. This is necessary because the offset is
252
		// capped at $this->max_offset, so a high page would always return the last page of results otherwise
253
		if ( ( $es_wp_query_args['paged'] * $es_wp_query_args['posts_per_page'] ) > $this->max_offset ) {
254
			$query->set_404();
255
256
			return;
257
		}
258
259
		// If there were no post types returned, then 404 to avoid querying against non-public post types, which could
260
		// happen if we don't add the post type restriction to the ES query
261
		if ( empty( $es_wp_query_args['post_type'] ) ) {
262
			$query->set_404();
263
264
			return;
265
		}
266
267
		// Convert the WP-style args into ES args
268
		$es_query_args = $this->convert_wp_es_to_es_args( $es_wp_query_args );
269
270
		//Only trust ES to give us IDs, not the content since it is a mirror
271
		$es_query_args['fields'] = array(
272
			'post_id',
273
		);
274
275
		/**
276
		 * Modify the underlying ES query that is passed to the search endpoint. The returned args must represent a valid ES query
277
		 *
278
		 * This filter is harder to use if you're unfamiliar with ES, but allows complete control over the query
279
		 *
280
		 * @module search
281
		 *
282
		 * @since 5.0.0
283
		 *
284
		 * @param array $es_query_args The raw ES query args
285
		 * @param WP_Query $query The original query object
286
		 */
287
		$es_query_args = apply_filters( 'jetpack_search_es_query_args', $es_query_args, $query );
288
289
		// Do the actual search query!
290
		$this->search_result = $this->search( $es_query_args );
291
292
		if ( is_wp_error( $this->search_result ) || ! is_array( $this->search_result ) || empty( $this->search_result['results'] ) || empty( $this->search_result['results']['hits'] ) ) {
293
			$this->found_posts = 0;
294
295
			return;
296
		}
297
298
		// Total number of results for paging purposes. Capped at $this->>max_offset + $posts_per_page, as deep paging
299
		// gets quite expensive
300
		$this->found_posts = min( $this->search_result['results']['total'], $this->max_offset + $posts_per_page );
301
302
		return;
303
	}
304
305
	/**
306
	 * Given a WP_Query, convert its WP_Tax_Query (if present) into the WP-style ES term arguments for the search
307
	 *
308
	 * @module search
309
	 *
310
	 * @param WP_Query $query The original WP_Query object for which to parse the taxonomy query
311
	 *
312
	 * @return array The new WP-style ES arguments (that will be converted into 'real' ES arguments)
313
	 */
314
	public function get_es_wp_query_terms_for_query( WP_Query $query ) {
315
		$args = array();
316
317
		$the_tax_query = $query->tax_query;
318
319
		if ( ! $the_tax_query ) {
320
			return $args;
321
		}
322
323
324
		if ( ! $the_tax_query instanceof WP_Tax_Query || empty( $the_tax_query->queried_terms ) || ! is_array( $the_tax_query->queried_terms ) ) {
0 ignored issues
show
Bug introduced by
The class WP_Tax_Query does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
325
			return $args;
326
		}
327
328
		$args = array();
329
330
		foreach ( $the_tax_query->queries as $tax_query ) {
331
			// Right now we only support slugs...see note above
332
			if ( 'slug' !== $tax_query['field'] ) {
333
				continue;
334
			}
335
336
			$taxonomy = $tax_query['taxonomy'];
337
338
			if ( ! is_array( $args[ $taxonomy ] ) ) {
339
				$args[ $taxonomy ] = array();
340
			}
341
342
			$args[ $taxonomy ] = array_merge( $args[ $taxonomy ], $tax_query['terms'] );
343
		}
344
345
		return $args;
346
	}
347
348
	/**
349
	 * Parse out the post type from a WP_Query
350
	 *
351
	 * Only allows post types that are not marked as 'exclude_from_search'
352
	 *
353
	 * @module search
354
	 *
355
	 * @param WP_Query $query Original WP_Query object
356
	 *
357
	 * @return array Array of searchable post types corresponding to the original query
358
	 */
359
	public function get_es_wp_query_post_type_for_query( WP_Query $query ) {
360
		$post_types = $query->get( 'post_type' );
361
362
		// If we're searching 'any', we want to only pass searchable post types to ES
363
		if ( 'any' === $post_types ) {
364
			$post_types = array_values( get_post_types( array(
365
				'exclude_from_search' => false,
366
			) ) );
367
		}
368
369
		if ( ! is_array( $post_types ) ) {
370
			$post_types = array( $post_types );
371
		}
372
373
		$post_types = array_unique( $post_types );
374
375
		$sanitized_post_types = array();
376
377
		// Make sure the post types are queryable
378
		foreach ( $post_types as $post_type ) {
379
			if ( ! $post_type ) {
380
				continue;
381
			}
382
383
			$post_type_object = get_post_type_object( $post_type );
384
385
			if ( ! $post_type_object || $post_type_object->exclude_from_search ) {
386
				continue;
387
			}
388
389
			$sanitized_post_types[] = $post_type;
390
		}
391
392
		return $sanitized_post_types;
393
	}
394
395
	/**
396
	 * Initialze widgets for the Search module
397
	 *
398
	 * @module search
399
	 */
400
	public function action__widgets_init() {
401
		require_once( __DIR__ . '/class.jetpack-search-widget-filters.php' );
402
403
		register_widget( 'Jetpack_Search_Widget_Filters' );
404
	}
405
406
	/**
407
	 * Get the Elasticsearch result
408
	 *
409
	 * @module search
410
	 *
411
	 * @param bool $raw If true, does not check for WP_Error or return the 'results' array - the JSON decoded HTTP response
412
	 *
413
	 * @return array|bool The search results, or false if there was a failure
414
	 */
415
	public function get_search_result( $raw = false ) {
416
		if ( $raw ) {
417
			return $this->search_result;
418
		}
419
420
		return ( ! empty( $this->search_result ) && ! is_wp_error( $this->search_result ) && is_array( $this->search_result ) && ! empty( $this->search_result['results'] ) ) ? $this->search_result['results'] : false;
421
	}
422
423
	/**
424
	 * Add the date portion of a WP_Query onto the query args
425
	 *
426
	 * @param array $es_wp_query_args
427
	 * @param $query The original WP_Query
428
	 *
429
	 * @return array The es wp query args, with date filters added (as needed)
430
	 */
431
	public function filter__add_date_filter_to_query( array $es_wp_query_args, WP_Query $query ) {
432
		if ( $query->get( 'year' ) ) {
433
			if ( $query->get( 'monthnum' ) ) {
434
				// Padding
435
				$date_monthnum = sprintf( '%02d', $query->get( 'monthnum' ) );
436
437
				if ( $query->get( 'day' ) ) {
438
					// Padding
439
					$date_day = sprintf( '%02d', $query->get( 'day' ) );
440
441
					$date_start = $query->get( 'year' ) . '-' . $date_monthnum . '-' . $date_day . ' 00:00:00';
442
					$date_end   = $query->get( 'year' ) . '-' . $date_monthnum . '-' . $date_day . ' 23:59:59';
443
				} else {
444
					$days_in_month = date( 't', mktime( 0, 0, 0, $query->get( 'monthnum' ), 14, $query->get( 'year' ) ) ); // 14 = middle of the month so no chance of DST issues
445
446
					$date_start = $query->get( 'year' ) . '-' . $date_monthnum . '-01 00:00:00';
447
					$date_end   = $query->get( 'year' ) . '-' . $date_monthnum . '-' . $days_in_month . ' 23:59:59';
448
				}
449
			} else {
450
				$date_start = $query->get( 'year' ) . '-01-01 00:00:00';
451
				$date_end   = $query->get( 'year' ) . '-12-31 23:59:59';
452
			}
453
454
			$es_wp_query_args['date_range'] = array(
455
				'field' => 'date',
456
				'gte'   => $date_start,
457
				'lte'   => $date_end,
458
			);
459
		}
460
461
		return $es_wp_query_args;
462
	}
463
464
	/**
465
	 * Converts WP_Query style args to ES args
466
	 *
467
	 * @module search
468
	 *
469
	 * @param array $args Array of WP_Query style arguments
470
	 *
471
	 * @return array Array of ES style query arguments
472
	 */
473
	function convert_wp_es_to_es_args( array $args ) {
474
		jetpack_require_lib( 'jetpack-wpes-query-builder' );
475
476
		$builder = new Jetpack_WPES_Query_Builder();
477
478
		$defaults = array(
479
			'blog_id'        => get_current_blog_id(),
480
481
			'query'          => null,    // Search phrase
482
			'query_fields'   => array( 'title', 'content', 'author', 'tag', 'category' ),
483
484
			'post_type'      => null,  // string or an array
485
			'terms'          => array(), // ex: array( 'taxonomy-1' => array( 'slug' ), 'taxonomy-2' => array( 'slug-a', 'slug-b' ) )
486
487
			'author'         => null,    // id or an array of ids
488
			'author_name'    => array(), // string or an array
489
490
			'date_range'     => null,    // array( 'field' => 'date', 'gt' => 'YYYY-MM-dd', 'lte' => 'YYYY-MM-dd' ); date formats: 'YYYY-MM-dd' or 'YYYY-MM-dd HH:MM:SS'
491
492
			'orderby'        => null,    // Defaults to 'relevance' if query is set, otherwise 'date'. Pass an array for multiple orders.
493
			'order'          => 'DESC',
494
495
			'posts_per_page' => 10,
496
497
			'offset'         => null,
498
			'paged'          => null,
499
500
			/**
501
			 * Aggregations. Examples:
502
			 * array(
503
			 *     'Tag'       => array( 'type' => 'taxonomy', 'taxonomy' => 'post_tag', 'count' => 10 ) ),
504
			 *     'Post Type' => array( 'type' => 'post_type', 'count' => 10 ) ),
505
			 * );
506
			 */
507
			'aggregations'         => null,
508
		);
509
510
		$raw_args = $args; // Keep a copy
0 ignored issues
show
Unused Code introduced by
$raw_args 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...
511
512
		$args = wp_parse_args( $args, $defaults );
513
514
		$es_query_args = array(
515
			'blog_id' => absint( $args['blog_id'] ),
516
			'size'    => absint( $args['posts_per_page'] ),
517
		);
518
519
		// ES "from" arg (offset)
520
		if ( $args['offset'] ) {
521
			$es_query_args['from'] = absint( $args['offset'] );
522
		} elseif ( $args['paged'] ) {
523
			$es_query_args['from'] = max( 0, ( absint( $args['paged'] ) - 1 ) * $es_query_args['size'] );
524
		}
525
526
		// Limit the offset to $this->max_offset posts, as deep pages get exponentially slower
527
		// See https://www.elastic.co/guide/en/elasticsearch/guide/current/pagination.html
528
		$es_query_args['from'] = min( $es_query_args['from'], $this->max_offset );
529
530
		if ( ! is_array( $args['author_name'] ) ) {
531
			$args['author_name'] = array( $args['author_name'] );
532
		}
533
534
		// ES stores usernames, not IDs, so transform
535
		if ( ! empty( $args['author'] ) ) {
536
			if ( ! is_array( $args['author'] ) ) {
537
				$args['author'] = array( $args['author'] );
538
			}
539
540
			foreach ( $args['author'] as $author ) {
541
				$user = get_user_by( 'id', $author );
542
543
				if ( $user && ! empty( $user->user_login ) ) {
544
					$args['author_name'][] = $user->user_login;
545
				}
546
			}
547
		}
548
549
		//////////////////////////////////////////////////
550
		// Build the filters from the query elements.
551
		// Filters rock because they are cached from one query to the next
552
		// but they are cached as individual filters, rather than all combined together.
553
		// May get performance boost by also caching the top level boolean filter too.
554
		$filters = array();
555
556
		if ( $args['post_type'] ) {
557
			if ( ! is_array( $args['post_type'] ) ) {
558
				$args['post_type'] = array( $args['post_type'] );
559
			}
560
561
			$filters[] = array(
562
				'terms' => array(
563
					'post_type' => $args['post_type'],
564
				),
565
			);
566
		}
567
568
		if ( $args['author_name'] ) {
569
			$filters[] = array(
570
				'terms' => array(
571
					'author_login' => $args['author_name'],
572
				),
573
			);
574
		}
575
576
		if ( ! empty( $args['date_range'] ) && isset( $args['date_range']['field'] ) ) {
577
			$field = $args['date_range']['field'];
578
579
			unset( $args['date_range']['field'] );
580
581
			$filters[] = array(
582
				'range' => array(
583
					$field => $args['date_range'],
584
				),
585
			);
586
		}
587
588
		if ( is_array( $args['terms'] ) ) {
589
			foreach ( $args['terms'] as $tax => $terms ) {
590
				$terms = (array) $terms;
591
592
				if ( count( $terms ) && mb_strlen( $tax ) ) {
593 View Code Duplication
					switch ( $tax ) {
594
						case 'post_tag':
595
							$tax_fld = 'tag.slug';
596
597
							break;
598
599
						case 'category':
600
							$tax_fld = 'category.slug';
601
602
							break;
603
604
						default:
605
							$tax_fld = 'taxonomy.' . $tax . '.slug';
606
607
							break;
608
					}
609
610
					foreach ( $terms as $term ) {
611
						$filters[] = array(
612
							'term' => array(
613
								$tax_fld => $term,
614
							),
615
						);
616
					}
617
				}
618
			}
619
		}
620
621
		if ( $args['query'] ) {
622
			$query = array(
623
				'multi_match' => array(
624
					'query'    => $args['query'],
625
					'fields'   => $args['query_fields'],
626
					'operator' => 'and',
627
					'type'     => 'cross_fields',
628
				),
629
			);
630
631
			$builder->add_query( $query );
632
633
			Jetpack_Search::score_query_by_recency( $builder );
634
635
			if ( ! $args['orderby'] ) {
636
				$args['orderby'] = array( 'relevance' );
637
			}
638
		} else {
639
			if ( ! $args['orderby'] ) {
640
				$args['orderby'] = array( 'date' );
641
			}
642
		}
643
644
		// Validate the "order" field
645
		switch ( strtolower( $args['order'] ) ) {
646
			case 'asc':
647
				$args['order'] = 'asc';
648
				break;
649
650
			case 'desc':
651
			default:
652
				$args['order'] = 'desc';
653
				break;
654
		}
655
656
		$es_query_args['sort'] = array();
657
658
		foreach ( (array) $args['orderby'] as $orderby ) {
659
			// Translate orderby from WP field to ES field
660
			switch ( $orderby ) {
661
				case 'relevance' :
662
					//never order by score ascending
663
					$es_query_args['sort'][] = array(
664
						'_score' => array(
665
							'order' => 'desc',
666
						),
667
					);
668
669
					break;
670
671 View Code Duplication
				case 'date' :
672
					$es_query_args['sort'][] = array(
673
						'date' => array(
674
							'order' => $args['order'],
675
						),
676
					);
677
678
					break;
679
680 View Code Duplication
				case 'ID' :
681
					$es_query_args['sort'][] = array(
682
						'id' => array(
683
							'order' => $args['order'],
684
						),
685
					);
686
687
					break;
688
689
				case 'author' :
690
					$es_query_args['sort'][] = array(
691
						'author.raw' => array(
692
							'order' => $args['order'],
693
						),
694
					);
695
696
					break;
697
			} // End switch().
698
		} // End foreach().
699
700
		if ( empty( $es_query_args['sort'] ) ) {
701
			unset( $es_query_args['sort'] );
702
		}
703
704
		if ( ! empty( $filters ) && is_array( $filters ) ) {
705
			foreach ( $filters as $filter ) {
706
				$builder->add_filter( $filter );
707
			}
708
709
			$es_query_args['filter'] = $builder->build_filter();
710
		}
711
712
		$es_query_args['query'] = $builder->build_query();
713
714
		// Aggregations
715
		if ( ! empty( $args['aggregations'] ) ) {
716
			$this->add_aggregations_to_es_query_builder( $args['aggregations'], $builder );
0 ignored issues
show
Documentation introduced by
$args['aggregations'] is of type string, but the function expects a array.

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...
717
718
			$es_query_args['aggregations'] = $builder->build_aggregation();
719
		}
720
721
		return $es_query_args;
722
	}
723
724
	/**
725
	 * Given an array of aggregations, parse and add them onto the Jetpack_WPES_Query_Builder object for use in ES
726
	 *
727
	 * @module search
728
	 *
729
	 * @param array $aggregations Array of Aggregations (filters) to add to the Jetpack_WPES_Query_Builder
730
	 *
731
	 * @param Jetpack_WPES_Query_Builder $builder The builder instance that is creating the ES query
732
	 */
733
	public function add_aggregations_to_es_query_builder( array $aggregations, Jetpack_WPES_Query_Builder $builder ) {
734
		foreach ( $aggregations as $label => $aggregation ) {
735
			switch ( $aggregation['type'] ) {
736
				case 'taxonomy':
737
					$this->add_taxonomy_aggregation_to_es_query_builder( $aggregation, $label, $builder );
738
739
					break;
740
741
				case 'post_type':
742
					$this->add_post_type_aggregation_to_es_query_builder( $aggregation, $label, $builder );
743
744
					break;
745
746
				case 'date_histogram':
747
					$this->add_date_histogram_aggregation_to_es_query_builder( $aggregation, $label, $builder );
748
749
					break;
750
			}
751
		}
752
	}
753
754
	/**
755
	 * Given an individual taxonomy aggregation, add it to the Jetpack_WPES_Query_Builder object for use in ES
756
	 *
757
	 * @module search
758
	 *
759
	 * @param array $aggregation The aggregation to add to the query builder
760
	 * @param $label The 'label' (unique id) for this aggregation
761
	 * @param Jetpack_WPES_Query_Builder $builder The builder instance that is creating the ES query
762
	 */
763
	public function add_taxonomy_aggregation_to_es_query_builder( array $aggregation, $label, Jetpack_WPES_Query_Builder $builder ) {
764
		$field = null;
0 ignored issues
show
Unused Code introduced by
$field 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...
765
766
		switch ( $aggregation['taxonomy'] ) {
767
			case 'post_tag':
768
				$field = 'tag';
769
				break;
770
771
			case 'category':
772
				$field = 'category';
773
				break;
774
775
			default:
776
				$field = 'taxonomy.' . $aggregation['taxonomy'];
777
				break;
778
		}
779
780
		$builder->add_aggs( $label, array(
781
			'terms' => array(
782
				'field' => $field . '.slug',
783
				'size' => min( (int) $aggregation['count'], $this->max_aggregations_count ),
784
			),
785
		));
786
	}
787
788
	/**
789
	 * Given an individual post_type aggregation, add it to the Jetpack_WPES_Query_Builder object for use in ES
790
	 *
791
	 * @module search
792
	 *
793
	 * @param array $aggregation The aggregation to add to the query builder
794
	 * @param $label The 'label' (unique id) for this aggregation
795
	 * @param Jetpack_WPES_Query_Builder $builder The builder instance that is creating the ES query
796
	 */
797
	public function add_post_type_aggregation_to_es_query_builder( array $aggregation, $label, Jetpack_WPES_Query_Builder $builder ) {
798
		$builder->add_aggs( $label, array(
799
			'terms' => array(
800
				'field' => 'post_type',
801
				'size' => min( (int) $aggregation['count'], $this->max_aggregations_count ),
802
			),
803
		));
804
	}
805
806
	/**
807
	 * Given an individual date_histogram aggregation, add it to the Jetpack_WPES_Query_Builder object for use in ES
808
	 *
809
	 * @module search
810
	 *
811
	 * @param array $aggregation The aggregation to add to the query builder
812
	 * @param $label The 'label' (unique id) for this aggregation
813
	 * @param Jetpack_WPES_Query_Builder $builder The builder instance that is creating the ES query
814
	 */
815
	public function add_date_histogram_aggregation_to_es_query_builder( array $aggregation, $label, Jetpack_WPES_Query_Builder $builder ) {
816
		$builder->add_aggs( $label, array(
817
			'date_histogram' => array(
818
				'interval' => $aggregation['interval'],
819
				'field'    => ( ! empty( $aggregation['field'] ) && 'post_date_gmt' == $aggregation['field'] ) ? 'date_gmt' : 'date',
820
			),
821
		));
822
	}
823
824
	/**
825
	 * And an existing filter object with a list of additional filters.
826
	 *
827
	 * Attempts to optimize the filters somewhat.
828
	 *
829
	 * @module search
830
	 *
831
	 * @param array $curr_filter The existing filters to build upon
832
	 * @param array $filters The new filters to add
833
	 *
834
	 * @return array The resulting merged filters
835
	 */
836
	public static function and_es_filters( array $curr_filter, array $filters ) {
837
		if ( ! is_array( $curr_filter ) || isset( $curr_filter['match_all'] ) ) {
838
			if ( 1 === count( $filters ) ) {
839
				return $filters[0];
840
			}
841
842
			return array(
843
				'and' => $filters,
844
			);
845
		}
846
847
		return array(
848
			'and' => array_merge( array( $curr_filter ), $filters ),
849
		);
850
	}
851
852
	/**
853
	 * Add a recency score to a given Jetpack_WPES_Query_Builder object, for emphasizing newer posts in results
854
	 *
855
	 * Internally uses a gauss decay function
856
	 *
857
	 * @module search
858
	 *
859
	 * @param Jetpack_WPES_Query_Builder $builder The Jetpack_WPES_Query_Builder to add the recency score to
860
	 *
861
	 * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-function-score-query.html#function-decay
862
	 */
863
	public static function score_query_by_recency( Jetpack_WPES_Query_Builder &$builder ) {
864
		//Newer content gets weighted slightly higher
865
		$date_scale  = '360d';
866
		$date_decay  = 0.9;
867
		$date_origin = date( 'Y-m-d' );
868
869
		$builder->add_decay( 'gauss', array(
870
			'date_gmt' => array(
871
				'origin' => $date_origin,
872
				'scale'  => $date_scale,
873
				'decay'  => $date_decay,
874
			),
875
		));
876
	}
877
878
	/**
879
	 * Set the available filters for the search
880
	 *
881
	 * These get rendered via the Jetpack_Search_Widget_Filters() widget
882
	 *
883
	 * Behind the scenes, these are implemented using Elasticsearch Aggregations.
884
	 *
885
	 * If you do not require counts of how many documents match each filter, please consider using regular WP Query
886
	 * arguments instead, such as via the jetpack_search_es_wp_query_args filter
887
	 *
888
	 * @module search
889
	 *
890
	 * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations.html
891
	 *
892
	 * @param array $aggregations Array of filters (aggregations) to apply to the search
893
	 */
894
	public function set_filters( array $aggregations ) {
895
		$this->aggregations = $aggregations;
896
	}
897
898
	/**
899
	 * Set the search's facets (deprecated)
900
	 *
901
	 * @module search
902
	 *
903
	 * @deprecated 5.0 Please use Jetpack_Search::set_filters() instead
904
	 *
905
	 * @see Jetpack_Search::set_filters()
906
	 *
907
	 * @param array $facets Array of facets to apply to the search
908
	 */
909
	public function set_facets( array $facets ) {
910
		_deprecated_function( __METHOD__, 'jetpack-5.0', 'Jetpack_Search::set_filters()' );
911
912
		$this->set_filters( $facets );
913
	}
914
915
	/**
916
	 * Get the raw Aggregation results from the ES response
917
	 *
918
	 * @module search
919
	 *
920
	 * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations.html
921
	 *
922
	 * @return array Array of Aggregations performed on the search
923
	 */
924
	public function get_search_aggregations_results() {
925
		$aggregations = array();
926
927
		$search_result = $this->get_search_result();
928
929
		if ( ! empty( $search_result ) && ! empty( $search_result['aggregations'] ) ) {
930
			$aggregations = $search_result['aggregations'];
931
		}
932
933
		return $aggregations;
934
	}
935
936
	/**
937
	 * Get the raw Facet results from the ES response
938
	 *
939
	 * @module search
940
	 *
941
	 * @deprecated 5.0 Please use Jetpack_Search::get_search_aggregations_results() instead
942
	 *
943
	 * @see Jetpack_Search::get_search_aggregations_results()
944
	 *
945
	 * @return array Array of Facets performed on the search
946
	 */
947
	public function get_search_facets() {
948
		_deprecated_function( __METHOD__, 'jetpack-5.0', 'Jetpack_Search::get_search_aggregations_results()' );
949
950
		return $this->get_search_aggregations_results();
951
	}
952
953
	/**
954
	 * Get the results of the Filters performed, including the number of matching documents
955
	 *
956
	 * Returns an array of Filters (keyed by $label, as passed to Jetpack_Search::set_filters()), containing the Filter and all resulting
957
	 * matching buckets, the url for applying/removing each bucket, etc.
958
	 *
959
	 * NOTE - if this is called before the search is performed, an empty array will be returned. Use the $aggregations class
960
	 * member if you need to access the raw filters set in Jetpack_Search::set_filters()
961
	 *
962
	 * @module search
963
	 *
964
	 * @param WP_Query The optional original WP_Query to use for determining which filters are active. Defaults to the main query
965
	 *
966
	 * @return array Array of Filters applied and info about them
967
	 */
968
	public function get_filters( WP_Query $query = null ) {
969
		if ( ! $query instanceof WP_Query ) {
0 ignored issues
show
Bug introduced by
The class WP_Query does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
970
			global $wp_query;
971
972
			$query = $wp_query;
973
		}
974
975
		$aggregation_data = $this->aggregations;
976
977
		if ( empty( $aggregation_data ) ) {
978
			return $aggregation_data;
979
		}
980
981
		$aggregation_results = $this->get_search_aggregations_results();
982
983
		if ( ! $aggregation_results ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $aggregation_results of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
984
			return $aggregation_data;
985
		}
986
987
		// NOTE - Looping over the _results_, not the original configured aggregations, so we get the 'real' data from ES
988
		foreach ( $aggregation_results as $label => $aggregation ) {
989
			if ( empty( $aggregation ) ) {
990
				continue;
991
			}
992
993
			$type = $this->aggregations[ $label ]['type'];
994
995
			$aggregations_data[ $label ]['buckets'] = array();
0 ignored issues
show
Coding Style Comprehensibility introduced by
$aggregations_data was never initialized. Although not strictly required by PHP, it is generally a good practice to add $aggregations_data = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
996
997
			$existing_term_slugs = array();
998
999
			$tax_query_var = null;
1000
1001
			// Figure out which terms are active in the query, for this taxonomy
1002
			if ( 'taxonomy' === $this->aggregations[ $label ]['type'] ) {
1003
				$tax_query_var = $this->get_taxonomy_query_var(  $this->aggregations[ $label ]['taxonomy'] );
1004
1005
				if ( ! empty( $query->tax_query ) && ! empty( $query->tax_query->queries ) && is_array( $query->tax_query->queries ) ) {
1006
					foreach( $query->tax_query->queries as $tax_query ) {
1007
						if ( $this->aggregations[ $label ]['taxonomy'] === $tax_query['taxonomy'] &&
1008
						     'slug' === $tax_query['field'] &&
1009
						     is_array( $tax_query['terms'] ) ) {
1010
							$existing_term_slugs = array_merge( $existing_term_slugs, $tax_query['terms'] );
1011
						}
1012
					}
1013
				}
1014
			}
1015
1016
			// Now take the resulting found aggregation items and generate the additional info about them, such as
1017
			// activation/deactivation url, name, count, etc
1018
			$buckets = array();
1019
1020
			if ( ! empty( $aggregation['buckets'] ) ) {
1021
				$buckets = (array) $aggregation['buckets'];
1022
			}
1023
1024
			// Some aggregation types like date_histogram don't support the max results parameter
1025
			if ( is_int( $this->aggregations[ $label ]['count'] ) && count( $buckets ) > $this->aggregations[ $label ]['count'] ) {
1026
				$buckets = array_slice( $buckets, 0, $this->aggregations[ $label ]['count'] );
1027
			}
1028
1029
			foreach ( $buckets as $item ) {
1030
				$query_vars = array();
1031
1032
				$active     = false;
1033
				$remove_url = null;
1034
1035
				// What type was the original aggregation?
1036
				switch ( $type ) {
1037
					case 'taxonomy':
1038
						$taxonomy = $this->aggregations[ $label ]['taxonomy'];
1039
1040
						$term = get_term_by( 'slug', $item['key'], $taxonomy );
1041
1042
						if ( ! $term || ! $tax_query_var ) {
1043
							continue 2; // switch() is considered a looping structure
1044
						}
1045
1046
						$query_vars = array(
1047
							$tax_query_var => implode( '+', array_merge( $existing_term_slugs, array( $term->slug ) ) ),
1048
						);
1049
1050
						$name = $term->name;
1051
1052
						// Let's determine if this term is active or not
1053
1054
						if ( in_array( $item['key'], $existing_term_slugs, true ) ) {
1055
							$active = true;
1056
1057
							$slug_count = count( $existing_term_slugs );
1058
1059 View Code Duplication
							if ( $slug_count > 1 ) {
1060
								$remove_url = add_query_arg( $tax_query_var, urlencode( implode( '+', array_diff( $existing_term_slugs, array( $item['key'] ) ) ) ) );
1061
							} else {
1062
								$remove_url = remove_query_arg( $tax_query_var );
1063
							}
1064
						}
1065
1066
						break;
1067
1068
					case 'post_type':
1069
						$post_type = get_post_type_object( $item['key'] );
1070
1071
						if ( ! $post_type || $post_type->exclude_from_search ) {
1072
							continue 2;  // switch() is considered a looping structure
1073
						}
1074
1075
						$query_vars = array(
1076
							'post_type' => $item['key'],
1077
						);
1078
1079
						$name = $post_type->labels->singular_name;
1080
1081
						// Is this post type active on this search?
1082
						$post_types = $query->get( 'post_type' );
1083
1084
						if ( ! is_array( $post_types ) ) {
1085
							$post_types = array( $post_types );
1086
						}
1087
1088
						if ( in_array( $item['key'], $post_types ) ) {
1089
							$active = true;
1090
1091
							$post_type_count = count( $post_types );
1092
1093
							// For the right 'remove filter' url, we need to remove the post type from the array, or remove the param entirely if it's the only one
1094 View Code Duplication
							if ( $post_type_count > 1 ) {
1095
								$remove_url = add_query_arg( 'post_type', urlencode_deep( array_diff( $post_types, array( $item['key'] ) ) ) );
1096
							} else {
1097
								$remove_url = remove_query_arg( 'post_type' );
1098
							}
1099
						}
1100
1101
						break;
1102
1103
					case 'date_histogram':
1104
						$timestamp = $item['key'] / 1000;
1105
1106
						switch ( $this->aggregations[ $label ]['interval'] ) {
1107
							case 'year':
1108
								$year = (int) date( 'Y', $timestamp );
1109
1110
								$query_vars = array(
1111
									'year'     => $year,
1112
									'monthnum' => false,
1113
									'day'      => false,
1114
								);
1115
1116
								$name = $year;
1117
1118
								// Is this year currently selected?
1119
								if ( ! empty( $query->get( 'year' ) ) && $query->get( 'year' ) === $year ) {
1120
									$active = true;
1121
1122
									$remove_url = remove_query_arg( array( 'year', 'monthnum', 'day' ) );
1123
								}
1124
1125
								break;
1126
1127
							case 'month':
1128
								$year  = (int) date( 'Y', $timestamp );
1129
								$month = (int) date( 'n', $timestamp );
1130
1131
								$query_vars = array(
1132
									'year'     => $year,
1133
									'monthnum' => $month,
1134
									'day'      => false,
1135
								);
1136
1137
								$name = date( 'F Y', $timestamp );
1138
1139
								// Is this month currently selected?
1140
								if ( ! empty( $query->get( 'year' ) ) && $query->get( 'year' ) === $year &&
1141
								     ! empty( $query->get( 'monthnum' ) ) && $query->get( 'monthnum' ) === $month ) {
1142
									$active = true;
1143
1144
									$remove_url = remove_query_arg( array( 'monthnum', 'day' ) );
1145
								}
1146
1147
								break;
1148
1149
							case 'day':
1150
								$year  = (int) date( 'Y', $timestamp );
1151
								$month = (int) date( 'n', $timestamp );
1152
								$day   = (int) date( 'j', $timestamp );
1153
1154
								$query_vars = array(
1155
									'year'     => $year,
1156
									'monthnum' => $month,
1157
									'day'      => $day,
1158
								);
1159
1160
								$name = date( 'F jS, Y', $timestamp );
1161
1162
								// Is this day currently selected?
1163
								if ( ! empty( $query->get( 'year' ) ) && $query->get( 'year' ) === $year &&
1164
								     ! empty( $query->get( 'monthnum' ) ) && $query->get( 'month' ) === $month &&
1165
								     ! empty( $query->get( 'day' ) ) && $query->get( 'day' ) === $day ) {
1166
									$active = true;
1167
1168
									$remove_url = remove_query_arg( array( 'day' ) );
1169
								}
1170
1171
								break;
1172
1173
							default:
1174
								continue 3; // switch() is considered a looping structure
1175
						} // End switch().
1176
1177
						break;
1178
1179
					default:
1180
						//continue 2; // switch() is considered a looping structure
1181
				} // End switch().
1182
1183
				// Need to urlencode param values since add_query_arg doesn't
1184
				$url_params = urlencode_deep( $query_vars );
1185
1186
				$aggregations_data[ $label ]['buckets'][] = array(
0 ignored issues
show
Bug introduced by
The variable $aggregations_data does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1187
					'url'        => add_query_arg( $url_params ),
1188
					'query_vars' => $query_vars,
1189
					'name'       => $name,
0 ignored issues
show
Bug introduced by
The variable $name does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1190
					'count'      => $item['doc_count'],
1191
					'active'     => $active,
1192
					'remove_url' => $remove_url,
1193
					'type'       => $type,
1194
					'type_label' => $label,
1195
				);
1196
			} // End foreach().
1197
		} // End foreach().
1198
1199
		return $aggregations_data;
1200
	}
1201
1202
	/**
1203
	 * Get the results of the Facets performed
1204
	 *
1205
	 * @module search
1206
	 *
1207
	 * @deprecated 5.0 Please use Jetpack_Search::get_filters() instead
1208
	 *
1209
	 * @see Jetpack_Search::get_filters()
1210
	 *
1211
	 * @return array $facets Array of Facets applied and info about them
1212
	 */
1213
	public function get_search_facet_data() {
1214
		_deprecated_function( __METHOD__, 'jetpack-5.0', 'Jetpack_Search::get_filters()' );
1215
1216
		return $this->get_filters();
1217
	}
1218
1219
	/**
1220
	 * Get the Filters that are currently applied to this search
1221
	 *
1222
	 * @module search
1223
	 *
1224
	 * @param WP_Query $query An optional WP_Query object - defaults to global $wp_query
0 ignored issues
show
Documentation introduced by
Should the type for parameter $query not be null|WP_Query?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
1225
	 *
1226
	 * @return array Array if Filters that were applied
1227
	 */
1228
	public function get_active_filter_buckets( WP_Query $query = null ) {
1229
		$active_buckets = array();
1230
1231
		$filters = $this->get_filters();
1232
1233
		foreach( $filters as $filter ) {
1234
			foreach( $filter['buckets'] as $item ) {
1235
				if ( $item['active'] ) {
1236
					$active_buckets[] = $item;
1237
				}
1238
			}
1239
		}
1240
1241
		return $active_buckets;
1242
	}
1243
1244
	/**
1245
	 * Get the Filters that are currently applied to this search
1246
	 *
1247
	 * @module search
1248
	 *
1249
	 * @param WP_Query $query An optional WP_Query object - defaults to global $wp_query
0 ignored issues
show
Documentation introduced by
Should the type for parameter $query not be null|WP_Query?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
1250
	 *
1251
	 * @return array Array if Filters that were applied
1252
	 */
1253
	public function get_current_filters( WP_Query $query = null ) {
1254
		_deprecated_function( __METHOD__, 'jetpack-5.0', 'Jetpack_Search::get_active_filter_buckets()' );
1255
1256
		return $this->get_active_filter_buckets();
1257
	}
1258
1259
	/**
1260
	 * Calculate the right query var to use for a given taxonomy
1261
	 *
1262
	 * Allows custom code to modify the GET var that is used to represent a given taxonomy, via the jetpack_search_taxonomy_query_var filter
1263
	 *
1264
	 * @module search
1265
	 *
1266
	 * @param string $taxonomy_name The name of the taxonomy for which to get the query var
1267
	 *
1268
	 * @return bool|string The query var to use for this taxonomy, or false if none found
1269
	 */
1270
	public function get_taxonomy_query_var( $taxonomy_name ) {
1271
		$taxonomy = get_taxonomy( $taxonomy_name );
1272
1273
		if ( ! $taxonomy || is_wp_error( $taxonomy ) ) {
1274
			return false;
1275
		}
1276
1277
		/**
1278
		 * Modify the query var to use for a given taxonomy
1279
		 *
1280
		 * @module search
1281
		 *
1282
		 * @since 5.0.0
1283
		 *
1284
		 * @param string $query_var The current query_var for the taxonomy
1285
		 * @param string $taxonomy_name The taxonomy name
1286
		 */
1287
		return apply_filters( 'jetpack_search_taxonomy_query_var', $taxonomy->query_var, $taxonomy_name );
1288
	}
1289
}
1290