PluginProbe
ElasticPress / 4.6.1
ElasticPress v4.6.1
5.3.5 5.3.4 3.6.5 3.6.6 4.0.0 4.0.1 4.1.0 4.2.0 4.2.1 4.2.2 4.3.0 4.3.1 4.4.0 4.4.1 4.5.0 4.5.1 4.5.2 4.6.0 4.6.1 4.7.0 4.7.1 4.7.2 5.0.0 5.0.1 5.0.2 All 108 releases
elasticpress / includes / utils.php

utils.php in ElasticPress 4.6.1, at includes/utils.php

821 lines 21.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * ElasticPress utility functions
4 *
5 * @since 3.0
6 * @package elasticpress
7 */
8
9 namespace ElasticPress\Utils;
10
11 use ElasticPress\IndexHelper;
12
13 if ( ! defined( 'ABSPATH' ) ) {
14 exit; // Exit if accessed directly.
15 }
16
17 /**
18 * Retrieve the EPIO subscription credentials.
19 *
20 * @since 2.5
21 * @return array
22 */
23 function get_epio_credentials() {
24 if ( defined( 'EP_CREDENTIALS' ) && EP_CREDENTIALS ) {
25 $raw_credentials = explode( ':', EP_CREDENTIALS );
26 if ( is_array( $raw_credentials ) && 2 === count( $raw_credentials ) ) {
27 $credentials = array(
28 'username' => $raw_credentials[0],
29 'token' => $raw_credentials[1],
30 );
31 }
32 $credentials = sanitize_credentials( $credentials );
33 } elseif ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK && is_epio() ) {
34 $credentials = sanitize_credentials( get_site_option( 'ep_credentials', false ) );
35 } elseif ( is_epio() ) {
36 $credentials = sanitize_credentials( get_option( 'ep_credentials', false ) );
37 } else {
38 $credentials = [
39 'username' => '',
40 'token' => '',
41 ];
42 }
43
44 if ( ! is_array( $credentials ) ) {
45 return [
46 'username' => '',
47 'token' => '',
48 ];
49 }
50
51 return $credentials;
52 }
53
54 /**
55 * Get WP capability needed for a user to interact with ElasticPress in the admin
56 *
57 * @since 4.5.0
58 * @return string
59 */
60 function get_capability() : string {
61 /**
62 * Filter the WP capability needed to interact with ElasticPress in the admin
63 *
64 * @since 4.5.0
65 * @hook ep_capability
66 * @param {bool} $capability Capability name. Defaults to `'elasticpress_manage'`
67 * @return {bool} New capability value
68 */
69 return apply_filters( 'ep_capability', 'manage_elasticpress' );
70 }
71
72 /**
73 * Get WP capability needed for a user to interact with ElasticPress in the network admin
74 *
75 * @since 4.5.0
76 * @return string
77 */
78 function get_network_capability() : string {
79 /**
80 * Filter the WP capability needed to interact with ElasticPress in the network admin
81 *
82 * @since 4.5.0
83 * @hook ep_network_capability
84 * @param {bool} $capability Capability name. Defaults to `'manage_network_elasticpress'`
85 * @return {bool} New capability value
86 */
87 return apply_filters( 'ep_network_capability', 'manage_network_elasticpress' );
88 }
89
90 /**
91 * Get mapped capabilities for post types
92 *
93 * @since 4.5.0
94 * @return array
95 */
96 function get_post_map_capabilities() : array {
97 $capability = get_capability();
98
99 return [
100 'edit_post' => $capability,
101 'edit_posts' => $capability,
102 'edit_others_posts' => $capability,
103 'publish_posts' => $capability,
104 'read_post' => $capability,
105 'read_private_posts' => $capability,
106 'delete_post' => $capability,
107 ];
108 }
109
110 /**
111 * Get shield credentials
112 *
113 * @since 3.0
114 * @return string|bool
115 */
116 function get_shield_credentials() {
117 if ( defined( 'ES_SHIELD' ) && ES_SHIELD ) {
118 return ES_SHIELD;
119 } elseif ( is_epio() ) {
120 $credentials = get_epio_credentials();
121
122 return $credentials['username'] . ':' . $credentials['token'];
123 }
124
125 return false;
126 }
127
128 /**
129 * Retrieve the appropriate index prefix. Will default to EP_INDEX_PREFIX constant if it exists
130 * AKA Subscription ID.
131 *
132 * @since 2.5
133 * @return string|bool
134 */
135 function get_index_prefix() {
136 if ( defined( 'EP_INDEX_PREFIX' ) && \EP_INDEX_PREFIX ) {
137 $prefix = \EP_INDEX_PREFIX;
138 } elseif ( is_epio() ) {
139 $credentials = get_epio_credentials();
140 $prefix = $credentials['username'];
141 if (
142 ( ! defined( 'EP_IS_NETWORK' ) || ! EP_IS_NETWORK ) &&
143 ( '-' !== substr( $prefix, - 1 ) )
144 ) {
145 $prefix .= '-';
146 }
147 } else {
148 $prefix = '';
149 }
150
151 /**
152 * Filter index prefix. Defaults to nothing
153 *
154 * @since 2.5
155 * @hook ep_index_prefix
156 * @param {string} $prefix Current prefix
157 * @return {string} New prefix
158 */
159 return apply_filters( 'ep_index_prefix', $prefix );
160 }
161
162 /**
163 * Check if the host is ElasticPress.io.
164 *
165 * @since 2.6
166 * @return bool
167 */
168 function is_epio() {
169 return filter_var( preg_match( '#elasticpress\.io#i', get_host() ), FILTER_VALIDATE_BOOLEAN );
170 }
171
172 /**
173 * Determine if we should index a blog/site
174 *
175 * @param int $blog_id Blog/site id.
176 * @since 3.2
177 * @return boolean
178 */
179 function is_site_indexable( $blog_id = null ) {
180 if ( is_multisite() ) {
181 $site = get_site( $blog_id );
182
183 $is_indexable = get_blog_option( (int) $blog_id, 'ep_indexable', 'yes' );
184
185 if ( 'no' === $is_indexable || $site['deleted'] || $site['archived'] || $site['spam'] ) {
186 return false;
187 }
188 }
189
190 return true;
191 }
192
193 /**
194 * Sanitize EPIO credentials prior to storing them.
195 *
196 * @param array $credentials Array containing username and token.
197 * @since 2.6
198 * @return array
199 */
200 function sanitize_credentials( $credentials ) {
201 if ( ! is_array( $credentials ) ) {
202 return [
203 'username' => '',
204 'token' => '',
205 ];
206 }
207
208 return [
209 'username' => ( isset( $credentials['username'] ) ) ? sanitize_text_field( $credentials['username'] ) : '',
210 'token' => ( isset( $credentials['token'] ) ) ? sanitize_text_field( $credentials['token'] ) : '',
211 ];
212 }
213
214 /**
215 * Determine if ElasticPress is in the middle of an index
216 *
217 * @since 3.0
218 * @return boolean
219 */
220 function is_indexing() {
221 /**
222 * Filter whether an index is occurring in dashboard or CLI
223 *
224 * @since 3.0
225 * @hook ep_is_indexing
226 * @param {bool} $indexing True for indexing
227 * @return {bool} New indexing value
228 */
229 return apply_filters( 'ep_is_indexing', ! empty( IndexHelper::factory()->get_index_meta() ) );
230 }
231
232 /**
233 * Check if wpcli indexing is occurring
234 *
235 * @since 3.0
236 * @return boolean
237 */
238 function is_indexing_wpcli() {
239 $index_meta = IndexHelper::factory()->get_index_meta();
240
241 /**
242 * Filter whether a CLI sync is occurring
243 *
244 * @since 3.0
245 * @hook ep_is_indexing_wpcli
246 * @param {bool} $indexing True for indexing
247 * @return {bool} New indexing value
248 */
249 return apply_filters( 'ep_is_indexing_wpcli', ( ! empty( $index_meta ) && 'cli' === $index_meta['method'] ) );
250 }
251
252 /**
253 * Retrieve the appropriate host. Will default to EP_HOST constant if it exists
254 *
255 * @since 2.1
256 * @return string|bool
257 */
258 function get_host() {
259
260 if ( defined( 'EP_HOST' ) && EP_HOST ) {
261 $host = EP_HOST;
262 } else {
263 $host = get_option( 'ep_host', false );
264 }
265
266 /**
267 * Filter ElasticPress host to use
268 *
269 * @since 2.1
270 * @hook ep_host
271 * @param {string} $host Current EP host
272 * @return {string} Host to use
273 */
274 return apply_filters( 'ep_host', $host );
275 }
276
277 /**
278 * Get a site. Wraps get_site for formatting purposes
279 *
280 * @param int $site_id Site/blog id
281 * @since 3.2
282 * @return array
283 */
284 function get_site( $site_id ) {
285 $site = \get_site( $site_id );
286
287 return [
288 'blog_id' => $site->blog_id,
289 'domain' => $site->domain,
290 'path' => $site->path,
291 'site_id' => $site->site_id,
292 'deleted' => $site->deleted,
293 'archived' => $site->archived,
294 'spam' => $site->spam,
295 ];
296 }
297
298 /**
299 * Wrapper function for get_sites - allows us to have one central place for the `ep_indexable_sites` filter
300 *
301 * @param int $limit The maximum amount of sites retrieved, Use 0 to return all sites.
302 * @since 3.0
303 * @return array
304 */
305 function get_sites( $limit = 0 ) {
306
307 if ( ! is_multisite() ) {
308 return [];
309 }
310
311 /**
312 * Filter arguments to use to query for sites on network
313 *
314 * @since 2.1
315 * @hook ep_indexable_sites_args
316 * @param {array} $args Array of args to query sites with. See WP_Site_Query
317 * @return {array} New arguments
318 */
319 $args = apply_filters(
320 'ep_indexable_sites_args',
321 array(
322 'limit' => $limit,
323 'number' => $limit,
324 )
325 );
326
327 $site_objects = \get_sites( $args );
328 $sites = [];
329
330 foreach ( $site_objects as $site ) {
331 $sites[] = get_site( $site->blog_id );
332 }
333
334 /**
335 * Filter indexable sites
336 *
337 * @since 3.0
338 * @hook ep_indexable_sites
339 * @param {array} $sites Current sites. Instances of WP_Site
340 * @return {array} New array of sites
341 */
342 return apply_filters( 'ep_indexable_sites', $sites );
343 }
344
345 /**
346 * Whether plugin is network activated
347 *
348 * Determines whether plugin is network activated or just on the local site.
349 *
350 * @since 3.0
351 * @param string $plugin the plugin base name.
352 * @return bool True if network activated or false.
353 */
354 function is_network_activated( $plugin ) {
355
356 $plugins = get_site_option( 'active_sitewide_plugins' );
357
358 if ( is_multisite() && isset( $plugins[ $plugin ] ) ) {
359 return true;
360 }
361
362 return false;
363 }
364
365
366 /**
367 * Performant utility function for building a term tree.
368 *
369 * Tree will look like this:
370 * [
371 * WP_Term(
372 * name
373 * slug
374 * children ->[
375 * WP_Term()
376 * ]
377 * ),
378 * WP_Term()
379 * ]
380 *
381 * @param array $all_terms Pass get_terms() as this argument where terms are objects NOT arrays.
382 * @param string|bool $orderby Can be count|name|false. This is how each tree branch will be ordered.
383 * @param string $order Can be asc|desc. This is the direction ordering will occur.
384 * @param bool $flat If false, a tree will be returned e.g. an array of top level terms
385 * which children linked within each node. If true, the tree will be
386 * "flattened".
387 * @since 2.5
388 * @return array
389 */
390 function get_term_tree( $all_terms, $orderby = 'count', $order = 'desc', $flat = false ) {
391 $terms_map = [];
392 $terms_tree = [];
393 $iteration_id = 0;
394
395 while ( true ) {
396 if ( empty( $all_terms ) ) {
397 break;
398 }
399
400 foreach ( $all_terms as $key => $term ) {
401 $iteration_id++;
402
403 if ( ! isset( $term->children ) ) {
404 $term->children = [];
405 }
406
407 if ( ! isset( $terms_map[ $term->term_id ] ) ) {
408 $terms_map[ $term->term_id ] = $term;
409 }
410
411 $parent_term = get_term( $term->parent, $term->taxonomy );
412
413 if ( empty( $term->parent ) || is_wp_error( $parent_term ) || ! $parent_term ) {
414 $term->level = 0;
415
416 if ( empty( $orderby ) ) {
417 $terms_tree[] = $term;
418 } elseif ( 'count' === $orderby ) {
419 /**
420 * We add this weird number to get past terms with the same count
421 */
422 $terms_tree[ ( ( $term->count * 10000000 ) + $iteration_id ) ] = $term;
423 } elseif ( 'name' === $orderby ) {
424 $terms_tree[ strtolower( $term->name ) ] = $term;
425 }
426
427 unset( $all_terms[ $key ] );
428 } else {
429 if ( ! empty( $terms_map[ $term->parent ] ) && isset( $terms_map[ $term->parent ]->level ) ) {
430
431 if ( empty( $orderby ) ) {
432 $terms_map[ $term->parent ]->children[] = $term;
433 } elseif ( 'count' === $orderby ) {
434 $terms_map[ $term->parent ]->children[ ( ( $term->count * 10000000 ) + $iteration_id ) ] = $term;
435 } elseif ( 'name' === $orderby ) {
436 $terms_map[ $term->parent ]->children[ $term->name ] = $term;
437 }
438
439 $parent_level = ( $terms_map[ $term->parent ]->level ) ? $terms_map[ $term->parent ]->level : 0;
440
441 $term->level = $parent_level + 1;
442 $term->parent_term = $terms_map[ $term->parent ];
443
444 unset( $all_terms[ $key ] );
445 }
446 }
447 }
448 }
449
450 if ( ! empty( $orderby ) ) {
451 if ( 'asc' === $order ) {
452 ksort( $terms_tree );
453 } else {
454 krsort( $terms_tree );
455 }
456
457 foreach ( $terms_map as $term ) {
458 if ( 'asc' === $order ) {
459 ksort( $term->children );
460 } else {
461 krsort( $term->children );
462 }
463
464 $term->children = array_values( $term->children );
465 }
466
467 $terms_tree = array_values( $terms_tree );
468 }
469
470 if ( $flat ) {
471 $flat_tree = [];
472
473 foreach ( $terms_tree as $term ) {
474 $flat_tree[] = $term;
475 $to_process = $term->children;
476 while ( ! empty( $to_process ) ) {
477 $term = array_shift( $to_process );
478 $flat_tree[] = $term;
479
480 if ( ! empty( $term->children ) ) {
481 $to_process = array_merge( $term->children, $to_process );
482 }
483 }
484 }
485
486 return $flat_tree;
487 }
488
489 return $terms_tree;
490 }
491
492 /**
493 * Returns the defaiult language for ES mapping.
494 *
495 * @return string Default EP language.
496 */
497 function get_language() {
498 if ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) {
499 $ep_language = get_site_option( 'ep_language' );
500 } else {
501 $ep_language = get_option( 'ep_language' );
502 }
503
504 $ep_language = ! empty( $ep_language ) ? $ep_language : get_locale();
505
506 /**
507 * Filter the default language to use at index time
508 *
509 * @since 3.1
510 * @param {string} The current language.
511 * @hook ep_default_language
512 * @return {string} New language
513 */
514 return apply_filters( 'ep_default_language', $ep_language );
515 }
516
517 /**
518 * Returns the status of an ongoing index operation.
519 *
520 * Returns the status of an ongoing index operation in array with the following fields:
521 * indexing | boolean | True if index operation is ongoing or false
522 * method | string | 'cli', 'web' or 'none'
523 * items_indexed | integer | Total number of items indexed
524 * total_items | integer | Total number of items indexed or -1 if not yet determined
525 * slug | string | The slug of the indexable
526 *
527 * @since 3.5.2
528 * @return array|boolean
529 */
530 function get_indexing_status() {
531
532 $index_status = false;
533
534 $index_meta = IndexHelper::factory()->get_index_meta();
535
536 if ( ! empty( $index_meta ) ) {
537 $index_status = $index_meta;
538
539 $index_status['indexing'] = true;
540
541 if ( ! empty( $index_meta['current_sync_item'] ) ) {
542 $index_status['items_indexed'] = $index_meta['current_sync_item']['synced'];
543 $index_status['url'] = $index_meta['current_sync_item']['url'] ?? ''; // Global indexables won't have a url.
544 $index_status['total_items'] = $index_meta['current_sync_item']['total'];
545 $index_status['slug'] = $index_meta['current_sync_item']['indexable'];
546 }
547
548 // Change method name for retrocompatibility.
549 // `dashboard` is used mainly because hooks names depend on that.
550 if ( ! empty( $index_status['method'] ) && 'dashboard' === $index_status['method'] ) {
551 $index_status['method'] = 'web';
552 }
553
554 if ( ! empty( $index_status['method'] ) && 'web' === $index_status['method'] ) {
555 $should_interrupt_sync = filter_var(
556 get_transient( 'ep_sync_interrupted' ),
557 FILTER_VALIDATE_BOOLEAN
558 );
559
560 $index_status['should_interrupt_sync'] = $should_interrupt_sync;
561 }
562 }
563
564 return $index_status;
565
566 }
567
568 /**
569 * Use the correct update option function depending on the context (multisite or not)
570 *
571 * @since 3.6.0
572 * @param string $option Name of the option to update.
573 * @param mixed $value Option value.
574 * @param mixed $autoload Whether to load the option when WordPress starts up.
575 * @return bool
576 */
577 function update_option( $option, $value, $autoload = null ) {
578 if ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) {
579 return \update_site_option( $option, $value );
580 }
581 return \update_option( $option, $value, $autoload );
582 }
583
584 /**
585 * Use the correct get option function depending on the context (multisite or not)
586 *
587 * @since 3.6.0
588 * @param string $option Name of the option to get.
589 * @param mixed $default_value Default value.
590 * @return bool
591 */
592 function get_option( $option, $default_value = false ) {
593 if ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) {
594 return \get_site_option( $option, $default_value );
595 }
596 return \get_option( $option, $default_value );
597 }
598
599 /**
600 * Use the correct delete option function depending on the context (multisite or not)
601 *
602 * @since 3.6.0
603 * @param string $option Name of the option to delete.
604 * @return bool
605 */
606 function delete_option( $option ) {
607 if ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) {
608 return \delete_site_option( $option );
609 }
610 return \delete_option( $option );
611 }
612
613 /**
614 * Check if queries for the current request are going to be integrated with
615 * ElasticPress.
616 *
617 * Public requests and REST API requests are integrated by default, but admin
618 * requests will only be integrated in if the `ep_admin_wp_query_integration`
619 * filter returns `true`, and and admin-ajax.php requests will only be
620 * integrated if the `ep_ajax_wp_query_integration` filter returns `true`.
621 *
622 * If specific types of requests are passed, true will only be returned if the
623 * current request also matches one of the passed types.
624 *
625 * This function is used by features to determine whether they should hook into
626 * the current request.
627 *
628 * @param string $context Slug of the feature that is performing the check.
629 * Passed to the `ep_is_integrated_request` filter.
630 * @param string[] $types Which types of request to check. Any of 'admin',
631 * 'ajax', 'public', and 'rest'. Defaults to all
632 * types.
633 * @return bool Whether the current request supports ElasticPress integration
634 * and is of a given type.
635 *
636 * @since 3.6.0
637 */
638 function is_integrated_request( $context, $types = [] ) {
639 if ( empty( $types ) ) {
640 $types = [ 'admin', 'ajax', 'public', 'rest' ];
641 }
642
643 $is_admin_request = is_admin();
644 $is_ajax_request = defined( 'DOING_AJAX' ) && DOING_AJAX;
645 $is_rest_request = defined( 'REST_REQUEST' ) && REST_REQUEST;
646 $is_integrated_admin_request = false;
647 $is_integrated_ajax_request = false;
648 $is_integrated_public_request = false;
649 $is_integrated_rest_request = false;
650
651 if ( $is_admin_request && ! $is_ajax_request && in_array( 'admin', $types, true ) ) {
652
653 /**
654 * Filter whether to integrate with admin queries.
655 *
656 * @hook ep_admin_wp_query_integration
657 * @param bool $integrate True to integrate.
658 * @return bool New value.
659 */
660 $is_integrated_admin_request = apply_filters( 'ep_admin_wp_query_integration', false );
661 }
662
663 if ( $is_ajax_request && in_array( 'ajax', $types, true ) ) {
664
665 /**
666 * Filter to integrate with admin ajax queries.
667 *
668 * @hook ep_ajax_wp_query_integration
669 * @param bool $integrate True to integrate.
670 * @return bool New value.
671 */
672 $is_integrated_ajax_request = apply_filters( 'ep_ajax_wp_query_integration', false );
673 }
674
675 if ( $is_rest_request && in_array( 'rest', $types, true ) ) {
676 $is_integrated_rest_request = true;
677 }
678
679 if ( ! $is_admin_request && ! $is_ajax_request && ! $is_rest_request && in_array( 'public', $types, true ) ) {
680 $is_integrated_public_request = true;
681 }
682
683 /**
684 * Is the current request any of the supported requests.
685 */
686 $is_integrated = (
687 $is_integrated_admin_request ||
688 $is_integrated_ajax_request ||
689 $is_integrated_public_request ||
690 $is_integrated_rest_request
691 );
692
693 /**
694 * Filter whether the queries for the current request should be integrated.
695 *
696 * @hook ep_is_integrated_request
697 * @param bool $is_integrated Whether queries for the request will be
698 * integrated.
699 * @param string $context Context for the original check. Usually the
700 * slug of the feature doing the check.
701 * @param array $types Which requests types are being checked.
702 * @return bool Whether queries for the request will be integrated.
703 *
704 * @since 3.6.2
705 */
706 return apply_filters( 'ep_is_integrated_request', $is_integrated, $context, $types );
707 }
708
709 /**
710 * Get asset info from extracted asset files
711 *
712 * @param string $slug Asset slug as defined in build/webpack configuration
713 * @param string $attribute Optional attribute to get. Can be version or dependencies
714 * @return string|array
715 */
716 function get_asset_info( $slug, $attribute = null ) {
717 if ( file_exists( EP_PATH . 'dist/js/' . $slug . '.asset.php' ) ) {
718 $asset = require EP_PATH . 'dist/js/' . $slug . '.asset.php';
719 } elseif ( file_exists( EP_PATH . 'dist/css/' . $slug . '.asset.php' ) ) {
720 $asset = require EP_PATH . 'dist/css/' . $slug . '.asset.php';
721 } else {
722 return null;
723 }
724
725 if ( ! empty( $attribute ) && isset( $asset[ $attribute ] ) ) {
726 return $asset[ $attribute ];
727 }
728
729 return $asset;
730 }
731
732 /**
733 * Return the Sync Page URL.
734 *
735 * @since 4.4.0
736 * @param boolean $do_sync Whether the link should or should not start a resync.
737 * @return string
738 */
739 function get_sync_url( bool $do_sync = false ) : string {
740 $page = 'admin.php?page=elasticpress-sync';
741 if ( $do_sync ) {
742 $page .= '&do_sync';
743 }
744 return ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) ?
745 network_admin_url( $page ) :
746 admin_url( $page );
747 }
748
749 /**
750 * Generate a common prefix to be used while generating a request ID.
751 *
752 * Uses the return of `get_index_prefix()` by default.
753 *
754 * @since 4.5.0
755 * @return string
756 */
757 function get_request_id_base() {
758 /**
759 * Filter the base of requests IDs. Uses the return of `get_index_prefix()` by default.
760 *
761 * @hook ep_request_id_base
762 * @since 4.5.0
763 * @param {string} $request_id_base Request ID base
764 * @return {string} New Request ID base
765 */
766 return apply_filters( 'ep_request_id_base', str_replace( '-', '', get_index_prefix() ) );
767 }
768
769 /**
770 * Generate a Request ID.
771 *
772 * The function concatenates the indices prefix to a random UUID4.
773 *
774 * @since 4.5.0
775 * @return string
776 */
777 function generate_request_id() : string {
778 $uuid = str_replace( '-', '', wp_generate_uuid4() );
779
780 /**
781 * Filter the ID generated to identify a request.
782 *
783 * @hook ep_request_id
784 * @since 4.5.0
785 * @param {string} $request_id Request ID. By default formed by the indices prefix and a random UUID4.
786 * @return {string} New Request ID
787 */
788 return apply_filters( 'ep_request_id', get_request_id_base() . $uuid );
789 }
790
791 /**
792 * Given an Elasticsearch response, try to find an error message.
793 *
794 * @since 4.6.0
795 * @param mixed $response The Elasticsearch response
796 * @return string
797 */
798 function get_elasticsearch_error_reason( $response ) : string {
799 if ( is_string( $response ) ) {
800 return $response;
801 }
802
803 if ( ! is_array( $response ) ) {
804 return var_export( $response, true ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions
805 }
806
807 if ( ! empty( $response['reason'] ) ) {
808 return (string) $response['reason'];
809 }
810
811 if ( ! empty( $response['result']['error'] ) && ! empty( $response['result']['error']['root_cause'][0]['reason'] ) ) {
812 return (string) $response['result']['error']['root_cause'][0]['reason'];
813 }
814
815 if ( ! empty( $response['result']['errors'] ) && ! empty( $response['result']['items'] ) && ! empty( $response['result']['items'][0]['index']['error']['reason'] ) ) {
816 return (string) $response['result']['items'][0]['index']['error']['reason'];
817 }
818
819 return '';
820 }
821