summaryrefslogtreecommitdiff
blob: 1dac29dcf0d564566b098453f77d85ac01369230 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
<?php
/**
 * Get search stats for use in the wp-admin dashboard.
 *
 * @package automattic/jetpack-search
 */

namespace Automattic\Jetpack\Search;

use Automattic\Jetpack\Connection\Client;
use Jetpack_Options;

/**
 * Search stats (e.g. post count, post type breakdown)
 */
class Stats {
	const CACHE_EXPIRY             = 5 * MINUTE_IN_SECONDS;
	const CACHE_GROUP              = 'jetpack_search';
	const COUNT_ESTIMATE_CACHE_KEY = 'count_estimate';

	/**
	 * Get stats from the WordPress.com API for the current blog ID.
	 */
	public function get_stats_from_wpcom() {
		$blog_id = Jetpack_Options::get_option( 'id' );

		if ( ! is_numeric( $blog_id ) ) {
			return null;
		}

		$response = Client::wpcom_json_api_request_as_blog(
			'/sites/' . (int) $blog_id . '/jetpack-search/stats',
			'2',
			array(),
			null,
			'wpcom'
		);

		return $response;
	}

	/**
	 * Estimate record counts via a local database query.
	 */
	public static function estimate_count() {
		$cached_value = wp_cache_get( self::COUNT_ESTIMATE_CACHE_KEY, self::CACHE_GROUP );
		if ( false !== $cached_value ) {
			return $cached_value;
		}

		global $wpdb;
		$indexable_statuses     = get_post_stati( array( 'public' => true ) );
		$unindexable_post_types = array_merge(
			// Explicitly exclude various post types registered by plugins.
			array(
				'elementor_library', // Used by Elementor.
				'jp_sitemap', // Used by Jetpack.
				'product_variation', // Used by Woocommerce.
				'redirect_rule', // Used by the Safe Redirect plugin.
				'reply', // Used by bbpress.
				'scheduled-action', // Used by Woocommerce.
			),
			get_post_types(
				array(
					'exclude_from_search' => true,
					'public'              => false,
				),
				'names',
				'or'
			)
		);

		$prep_for_query = function ( $string ) use ( $wpdb ) {
			// phpcs:ignore WordPress.DB.PreparedSQLPlaceholders.QuotedSimplePlaceholder -- This is used to sanitize post type names.
			return $wpdb->prepare( "'%s'", $string );
		};

		$statuses_list   = implode( ',', array_map( $prep_for_query, $indexable_statuses ) );
		$post_types_list = implode( ',', array_map( $prep_for_query, $unindexable_post_types ) );

		$count = (int) $wpdb->get_var(
			// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- This is properly prepared, but the query is constructed using variables.
			"SELECT COUNT(ID) FROM {$wpdb->posts} WHERE post_status IN ($statuses_list) AND post_type NOT IN ($post_types_list)"
		);

		wp_cache_set( self::COUNT_ESTIMATE_CACHE_KEY, $count, self::CACHE_GROUP, self::CACHE_EXPIRY );
		return $count;
	}
}