PluginProbe
ElasticPress / 4.7.0
ElasticPress v4.7.0
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.7.0, at includes/utils.php

882 lines 23.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 return true;
182 }
183
184 $site = get_site( $blog_id );
185
186 $is_indexable = get_site_meta( $site['blog_id'], 'ep_indexable', true );
187
188 return 'no' !== $is_indexable && ! $site['deleted'] && ! $site['archived'] && ! $site['spam'];
189 }
190
191 /**
192 * Sanitize EPIO credentials prior to storing them.
193 *
194 * @param array $credentials Array containing username and token.
195 * @since 2.6
196 * @return array
197 */
198 function sanitize_credentials( $credentials ) {
199 if ( ! is_array( $credentials ) ) {
200 return [
201 'username' => '',
202 'token' => '',
203 ];
204 }
205
206 return [
207 'username' => ( isset( $credentials['username'] ) ) ? sanitize_text_field( $credentials['username'] ) : '',
208 'token' => ( isset( $credentials['token'] ) ) ? sanitize_text_field( $credentials['token'] ) : '',
209 ];
210 }
211
212 /**
213 * Determine if ElasticPress is in the middle of an index
214 *
215 * @since 3.0
216 * @return boolean
217 */
218 function is_indexing() {
219 /**
220 * Filter whether an index is occurring in dashboard or CLI
221 *
222 * @since 3.0
223 * @hook ep_is_indexing
224 * @param {bool} $indexing True for indexing
225 * @return {bool} New indexing value
226 */
227 return apply_filters( 'ep_is_indexing', ! empty( IndexHelper::factory()->get_index_meta() ) );
228 }
229
230 /**
231 * Check if wpcli indexing is occurring
232 *
233 * @since 3.0
234 * @return boolean
235 */
236 function is_indexing_wpcli() {
237 $index_meta = IndexHelper::factory()->get_index_meta();
238
239 /**
240 * Filter whether a CLI sync is occurring
241 *
242 * @since 3.0
243 * @hook ep_is_indexing_wpcli
244 * @param {bool} $indexing True for indexing
245 * @return {bool} New indexing value
246 */
247 return apply_filters( 'ep_is_indexing_wpcli', ( ! empty( $index_meta ) && 'cli' === $index_meta['method'] ) );
248 }
249
250 /**
251 * Retrieve the appropriate host. Will default to EP_HOST constant if it exists
252 *
253 * @since 2.1
254 * @return string|bool
255 */
256 function get_host() {
257
258 if ( defined( 'EP_HOST' ) && EP_HOST ) {
259 $host = EP_HOST;
260 } else {
261 $host = get_option( 'ep_host', false );
262 }
263
264 /**
265 * Filter ElasticPress host to use
266 *
267 * @since 2.1
268 * @hook ep_host
269 * @param {string} $host Current EP host
270 * @return {string} Host to use
271 */
272 return apply_filters( 'ep_host', $host );
273 }
274
275 /**
276 * Get a site. Wraps get_site for formatting purposes
277 *
278 * @param int $site_id Site/blog id
279 * @since 3.2
280 * @return array
281 */
282 function get_site( $site_id ) {
283 $site = \get_site( $site_id );
284
285 return [
286 'blog_id' => $site->blog_id,
287 'domain' => $site->domain,
288 'path' => $site->path,
289 'site_id' => $site->site_id,
290 'deleted' => $site->deleted,
291 'archived' => $site->archived,
292 'spam' => $site->spam,
293 ];
294 }
295
296 /**
297 * Wrapper function for get_sites - allows us to have one central place for the `ep_indexable_sites` filter
298 *
299 * @param int $limit The maximum amount of sites retrieved, Use 0 to return all sites.
300 * @param bool $only_indexable Whether should be returned only indexable sites or not.
301 * @since 3.0, 4.7.0 added `$only_indexable`
302 * @return array
303 */
304 function get_sites( $limit = 0, $only_indexable = false ) {
305 if ( ! is_multisite() ) {
306 return [];
307 }
308
309 $args = [
310 'limit' => $limit,
311 'number' => $limit,
312 ];
313
314 if ( $only_indexable ) {
315 $args = array_merge(
316 $args,
317 [
318 'spam' => 0,
319 'deleted' => 0,
320 'archived' => 0,
321 'meta_query' => [
322 'relation' => 'OR',
323 [
324 'key' => 'ep_indexable',
325 'value' => 'no',
326 'compare' => '!=',
327 ],
328 [
329 'key' => 'ep_indexable',
330 'compare' => 'NOT EXISTS',
331 ],
332 ],
333 ]
334 );
335 }
336
337 /**
338 * Filter arguments to use to query for sites on network
339 *
340 * @since 2.1
341 * @hook ep_indexable_sites_args
342 * @param {array} $args Array of args to query sites with. See WP_Site_Query
343 * @return {array} New arguments
344 */
345 $args = apply_filters( 'ep_indexable_sites_args', $args );
346
347 $site_objects = \get_sites( $args );
348 $sites = [];
349
350 foreach ( $site_objects as $site ) {
351 $sites[] = get_site( $site->blog_id );
352 }
353
354 /**
355 * Filter indexable sites
356 *
357 * @since 3.0
358 * @hook ep_indexable_sites
359 * @param {array} $sites Current sites. Instances of WP_Site
360 * @return {array} New array of sites
361 */
362 return apply_filters( 'ep_indexable_sites', $sites );
363 }
364
365 /**
366 * Whether plugin is network activated
367 *
368 * Determines whether plugin is network activated or just on the local site.
369 *
370 * @since 3.0
371 * @param string $plugin the plugin base name.
372 * @return bool True if network activated or false.
373 */
374 function is_network_activated( $plugin ) {
375
376 $plugins = get_site_option( 'active_sitewide_plugins' );
377
378 if ( is_multisite() && isset( $plugins[ $plugin ] ) ) {
379 return true;
380 }
381
382 return false;
383 }
384
385
386 /**
387 * Performant utility function for building a term tree.
388 *
389 * Tree will look like this:
390 * [
391 * WP_Term(
392 * name
393 * slug
394 * children ->[
395 * WP_Term()
396 * ]
397 * ),
398 * WP_Term()
399 * ]
400 *
401 * @param array $all_terms Pass get_terms() as this argument where terms are objects NOT arrays.
402 * @param string|bool $orderby Can be count|name|false. This is how each tree branch will be ordered.
403 * @param string $order Can be asc|desc. This is the direction ordering will occur.
404 * @param bool $flat If false, a tree will be returned e.g. an array of top level terms
405 * which children linked within each node. If true, the tree will be
406 * "flattened".
407 * @since 2.5
408 * @return array
409 */
410 function get_term_tree( $all_terms, $orderby = 'count', $order = 'desc', $flat = false ) {
411 $terms_map = [];
412 $terms_tree = [];
413 $iteration_id = 0;
414
415 while ( true ) {
416 if ( empty( $all_terms ) ) {
417 break;
418 }
419
420 foreach ( $all_terms as $key => $term ) {
421 $iteration_id++;
422
423 if ( ! isset( $term->children ) ) {
424 $term->children = [];
425 }
426
427 if ( ! isset( $terms_map[ $term->term_id ] ) ) {
428 $terms_map[ $term->term_id ] = $term;
429 }
430
431 $parent_term = get_term( $term->parent, $term->taxonomy );
432
433 if ( empty( $term->parent ) || is_wp_error( $parent_term ) || ! $parent_term ) {
434 $term->level = 0;
435
436 if ( empty( $orderby ) ) {
437 $terms_tree[] = $term;
438 } elseif ( 'count' === $orderby ) {
439 /**
440 * We add this weird number to get past terms with the same count
441 */
442 $terms_tree[ ( ( $term->count * 10000000 ) + $iteration_id ) ] = $term;
443 } elseif ( 'name' === $orderby ) {
444 $terms_tree[ strtolower( $term->name ) ] = $term;
445 }
446
447 unset( $all_terms[ $key ] );
448 } else {
449 if ( ! empty( $terms_map[ $term->parent ] ) && isset( $terms_map[ $term->parent ]->level ) ) {
450
451 if ( empty( $orderby ) ) {
452 $terms_map[ $term->parent ]->children[] = $term;
453 } elseif ( 'count' === $orderby ) {
454 $terms_map[ $term->parent ]->children[ ( ( $term->count * 10000000 ) + $iteration_id ) ] = $term;
455 } elseif ( 'name' === $orderby ) {
456 $terms_map[ $term->parent ]->children[ $term->name ] = $term;
457 }
458
459 $parent_level = ( $terms_map[ $term->parent ]->level ) ? $terms_map[ $term->parent ]->level : 0;
460
461 $term->level = $parent_level + 1;
462 $term->parent_term = $terms_map[ $term->parent ];
463
464 unset( $all_terms[ $key ] );
465 }
466 }
467 }
468 }
469
470 if ( ! empty( $orderby ) ) {
471 if ( 'asc' === $order ) {
472 ksort( $terms_tree );
473 } else {
474 krsort( $terms_tree );
475 }
476
477 foreach ( $terms_map as $term ) {
478 if ( 'asc' === $order ) {
479 ksort( $term->children );
480 } else {
481 krsort( $term->children );
482 }
483
484 $term->children = array_values( $term->children );
485 }
486
487 $terms_tree = array_values( $terms_tree );
488 }
489
490 if ( $flat ) {
491 $flat_tree = [];
492
493 foreach ( $terms_tree as $term ) {
494 $flat_tree[] = $term;
495 $to_process = $term->children;
496 while ( ! empty( $to_process ) ) {
497 $term = array_shift( $to_process );
498 $flat_tree[] = $term;
499
500 if ( ! empty( $term->children ) ) {
501 $to_process = array_merge( $term->children, $to_process );
502 }
503 }
504 }
505
506 return $flat_tree;
507 }
508
509 return $terms_tree;
510 }
511
512 /**
513 * Returns the default language for ES mapping.
514 *
515 * @return string Default EP language.
516 */
517 function get_language() {
518 $ep_language = get_option( 'ep_language' );
519 $ep_language = ! empty( $ep_language ) ? $ep_language : 'site-default';
520
521 /**
522 * Filter the default language to use at index time
523 *
524 * @since 3.1
525 * @param {string} The current language.
526 * @hook ep_default_language
527 * @return {string} New language
528 */
529 return apply_filters( 'ep_default_language', $ep_language );
530 }
531
532 /**
533 * Returns the status of an ongoing index operation.
534 *
535 * Returns the status of an ongoing index operation in array with the following fields:
536 * indexing | boolean | True if index operation is ongoing or false
537 * method | string | 'cli', 'web' or 'none'
538 * items_indexed | integer | Total number of items indexed
539 * total_items | integer | Total number of items indexed or -1 if not yet determined
540 * slug | string | The slug of the indexable
541 *
542 * @since 3.5.2
543 * @return array|boolean
544 */
545 function get_indexing_status() {
546
547 $index_status = false;
548
549 $index_meta = IndexHelper::factory()->get_index_meta();
550
551 if ( ! empty( $index_meta ) ) {
552 $index_status = $index_meta;
553
554 $index_status['indexing'] = true;
555
556 if ( ! empty( $index_meta['current_sync_item'] ) ) {
557 $index_status['items_indexed'] = $index_meta['current_sync_item']['synced'];
558 $index_status['url'] = $index_meta['current_sync_item']['url'] ?? ''; // Global indexables won't have a url.
559 $index_status['total_items'] = $index_meta['current_sync_item']['total'];
560 $index_status['slug'] = $index_meta['current_sync_item']['indexable'];
561 }
562
563 // Change method name for retrocompatibility.
564 // `dashboard` is used mainly because hooks names depend on that.
565 if ( ! empty( $index_status['method'] ) && 'dashboard' === $index_status['method'] ) {
566 $index_status['method'] = 'web';
567 }
568
569 if ( ! empty( $index_status['method'] ) && 'web' === $index_status['method'] ) {
570 $should_interrupt_sync = filter_var(
571 get_transient( 'ep_sync_interrupted' ),
572 FILTER_VALIDATE_BOOLEAN
573 );
574
575 $index_status['should_interrupt_sync'] = $should_interrupt_sync;
576 }
577 }
578
579 return $index_status;
580
581 }
582
583 /**
584 * Use the correct update option function depending on the context (multisite or not)
585 *
586 * @since 3.6.0
587 * @param string $option Name of the option to update.
588 * @param mixed $value Option value.
589 * @param mixed $autoload Whether to load the option when WordPress starts up.
590 * @return bool
591 */
592 function update_option( $option, $value, $autoload = null ) {
593 if ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) {
594 return \update_site_option( $option, $value );
595 }
596 return \update_option( $option, $value, $autoload );
597 }
598
599 /**
600 * Use the correct get option function depending on the context (multisite or not)
601 *
602 * @since 3.6.0
603 * @param string $option Name of the option to get.
604 * @param mixed $default_value Default value.
605 * @return bool
606 */
607 function get_option( $option, $default_value = false ) {
608 if ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) {
609 return \get_site_option( $option, $default_value );
610 }
611 return \get_option( $option, $default_value );
612 }
613
614 /**
615 * Use the correct delete option function depending on the context (multisite or not)
616 *
617 * @since 3.6.0
618 * @param string $option Name of the option to delete.
619 * @return bool
620 */
621 function delete_option( $option ) {
622 if ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) {
623 return \delete_site_option( $option );
624 }
625 return \delete_option( $option );
626 }
627
628 /**
629 * Check if queries for the current request are going to be integrated with
630 * ElasticPress.
631 *
632 * Public requests and REST API requests are integrated by default, but admin
633 * requests will only be integrated in if the `ep_admin_wp_query_integration`
634 * filter returns `true`, and and admin-ajax.php requests will only be
635 * integrated if the `ep_ajax_wp_query_integration` filter returns `true`.
636 *
637 * If specific types of requests are passed, true will only be returned if the
638 * current request also matches one of the passed types.
639 *
640 * This function is used by features to determine whether they should hook into
641 * the current request.
642 *
643 * @param string $context Slug of the feature that is performing the check.
644 * Passed to the `ep_is_integrated_request` filter.
645 * @param string[] $types Which types of request to check. Any of 'admin',
646 * 'ajax', 'public', and 'rest'. Defaults to all
647 * types.
648 * @return bool Whether the current request supports ElasticPress integration
649 * and is of a given type.
650 *
651 * @since 3.6.0
652 */
653 function is_integrated_request( $context, $types = [] ) {
654 if ( empty( $types ) ) {
655 $types = [ 'admin', 'ajax', 'public', 'rest' ];
656 }
657
658 $is_admin_request = is_admin();
659 $is_ajax_request = defined( 'DOING_AJAX' ) && DOING_AJAX;
660 $is_rest_request = defined( 'REST_REQUEST' ) && REST_REQUEST;
661 $is_integrated_admin_request = false;
662 $is_integrated_ajax_request = false;
663 $is_integrated_public_request = false;
664 $is_integrated_rest_request = false;
665
666 if ( $is_admin_request && ! $is_ajax_request && in_array( 'admin', $types, true ) ) {
667
668 /**
669 * Filter whether to integrate with admin queries.
670 *
671 * @hook ep_admin_wp_query_integration
672 * @param bool $integrate True to integrate.
673 * @return bool New value.
674 */
675 $is_integrated_admin_request = apply_filters( 'ep_admin_wp_query_integration', false );
676 }
677
678 if ( $is_ajax_request && in_array( 'ajax', $types, true ) ) {
679
680 /**
681 * Filter to integrate with admin ajax queries.
682 *
683 * @hook ep_ajax_wp_query_integration
684 * @param bool $integrate True to integrate.
685 * @return bool New value.
686 */
687 $is_integrated_ajax_request = apply_filters( 'ep_ajax_wp_query_integration', false );
688 }
689
690 if ( $is_rest_request && in_array( 'rest', $types, true ) ) {
691 $is_integrated_rest_request = true;
692 }
693
694 if ( ! $is_admin_request && ! $is_ajax_request && ! $is_rest_request && in_array( 'public', $types, true ) ) {
695 $is_integrated_public_request = true;
696 }
697
698 /**
699 * Is the current request any of the supported requests.
700 */
701 $is_integrated = (
702 $is_integrated_admin_request ||
703 $is_integrated_ajax_request ||
704 $is_integrated_public_request ||
705 $is_integrated_rest_request
706 );
707
708 /**
709 * Filter whether the queries for the current request should be integrated.
710 *
711 * @hook ep_is_integrated_request
712 * @param bool $is_integrated Whether queries for the request will be
713 * integrated.
714 * @param string $context Context for the original check. Usually the
715 * slug of the feature doing the check.
716 * @param array $types Which requests types are being checked.
717 * @return bool Whether queries for the request will be integrated.
718 *
719 * @since 3.6.2
720 */
721 return apply_filters( 'ep_is_integrated_request', $is_integrated, $context, $types );
722 }
723
724 /**
725 * Get asset info from extracted asset files
726 *
727 * @param string $slug Asset slug as defined in build/webpack configuration
728 * @param string $attribute Optional attribute to get. Can be version or dependencies
729 * @return string|array
730 */
731 function get_asset_info( $slug, $attribute = null ) {
732 if ( file_exists( EP_PATH . 'dist/js/' . $slug . '.asset.php' ) ) {
733 $asset = require EP_PATH . 'dist/js/' . $slug . '.asset.php';
734 } elseif ( file_exists( EP_PATH . 'dist/css/' . $slug . '.asset.php' ) ) {
735 $asset = require EP_PATH . 'dist/css/' . $slug . '.asset.php';
736 } else {
737 return null;
738 }
739
740 if ( ! empty( $attribute ) && isset( $asset[ $attribute ] ) ) {
741 return $asset[ $attribute ];
742 }
743
744 return $asset;
745 }
746
747 /**
748 * Return the Sync Page URL.
749 *
750 * @since 4.4.0
751 * @param boolean $do_sync Whether the link should or should not start a resync.
752 * @return string
753 */
754 function get_sync_url( bool $do_sync = false ) : string {
755 $page = 'admin.php?page=elasticpress-sync';
756 if ( $do_sync ) {
757 $page .= '&do_sync';
758 }
759 return ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) ?
760 network_admin_url( $page ) :
761 admin_url( $page );
762 }
763
764 /**
765 * Generate a common prefix to be used while generating a request ID.
766 *
767 * Uses the return of `get_index_prefix()` by default.
768 *
769 * @since 4.5.0
770 * @return string
771 */
772 function get_request_id_base() {
773 /**
774 * Filter the base of requests IDs. Uses the return of `get_index_prefix()` by default.
775 *
776 * @hook ep_request_id_base
777 * @since 4.5.0
778 * @param {string} $request_id_base Request ID base
779 * @return {string} New Request ID base
780 */
781 return apply_filters( 'ep_request_id_base', str_replace( '-', '', get_index_prefix() ) );
782 }
783
784 /**
785 * Generate a Request ID.
786 *
787 * The function concatenates the indices prefix to a random UUID4.
788 *
789 * @since 4.5.0
790 * @return string
791 */
792 function generate_request_id() : string {
793 $uuid = str_replace( '-', '', wp_generate_uuid4() );
794
795 /**
796 * Filter the ID generated to identify a request.
797 *
798 * @hook ep_request_id
799 * @since 4.5.0
800 * @param {string} $request_id Request ID. By default formed by the indices prefix and a random UUID4.
801 * @return {string} New Request ID
802 */
803 return apply_filters( 'ep_request_id', get_request_id_base() . $uuid );
804 }
805
806 /**
807 * Given an Elasticsearch response, try to find an error message.
808 *
809 * @since 4.6.0
810 * @param mixed $response The Elasticsearch response
811 * @return string
812 */
813 function get_elasticsearch_error_reason( $response ) : string {
814 if ( is_string( $response ) ) {
815 return $response;
816 }
817
818 if ( ! is_array( $response ) ) {
819 return var_export( $response, true ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions
820 }
821
822 if ( ! empty( $response['reason'] ) ) {
823 return (string) $response['reason'];
824 }
825
826 if ( ! empty( $response['result']['error'] ) && ! empty( $response['result']['error']['root_cause'][0]['reason'] ) ) {
827 return (string) $response['result']['error']['root_cause'][0]['reason'];
828 }
829
830 if ( ! empty( $response['result']['errors'] ) && ! empty( $response['result']['items'] ) && ! empty( $response['result']['items'][0]['index']['error']['reason'] ) ) {
831 return (string) $response['result']['items'][0]['index']['error']['reason'];
832 }
833
834 return '';
835 }
836
837 /**
838 * Use the correct set_transient option function depending on the context (multisite or not)
839 *
840 * @since 4.7.0
841 * @param string $transient Transient name. Expected to not be SQL-escaped.
842 * Must be 172 characters or fewer in length.
843 * @param mixed $value Transient value. Must be serializable if non-scalar.
844 * Expected to not be SQL-escaped.
845 * @param int $expiration Optional. Time until expiration in seconds. Default 0 (no expiration).
846 * @return bool True if the value was set, false otherwise.
847 */
848 function set_transient( $transient, $value, $expiration = 0 ) {
849 if ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) {
850 return \set_site_transient( $transient, $value, $expiration );
851 }
852 return \set_transient( $transient, $value, $expiration );
853 }
854
855 /**
856 * Use the correct get_transient function depending on the context (multisite or not)
857 *
858 * @since 4.7.0
859 * @param string $transient Transient name. Expected to not be SQL-escaped.
860 * @return mixed Value of transient.
861 */
862 function get_transient( $transient ) {
863 if ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) {
864 return \get_site_transient( $transient );
865 }
866 return \get_transient( $transient );
867 }
868
869 /**
870 * Use the correct delete_transient function depending on the context (multisite or not)
871 *
872 * @since 4.7.0
873 * @param string $transient Transient name. Expected to not be SQL-escaped.
874 * @return bool True if the transient was deleted, false otherwise.
875 */
876 function delete_transient( $transient ) {
877 if ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) {
878 return \delete_site_transient( $transient );
879 }
880 return \delete_transient( $transient );
881 }
882