*/ final class HealthCheck { /** * Api class * * @since 4.0.0 * @access private * @var Api $api Api class. */ private $api; /** * CMP vendors cache * * @since 4.0.0 * @var array|null */ private $cmp_vendors_cache = null; /** * Initialize the class * * @since 4.0.0 */ public function __construct() { $this->api = new Api(); } /** * Get all health check issues * * @since 4.0.0 * @return array> */ public function get_issues() { $issues = array(); // Check presized divs (Core Web Vitals) $core_web_vitals_issue = $this->check_core_web_vitals(); if ( $core_web_vitals_issue ) { $issues[] = $core_web_vitals_issue; } // Check Ads.txt $adstxt_issue = $this->check_adstxt(); if ( $adstxt_issue ) { $issues[] = $adstxt_issue; } // Check Ad Formats $ad_formats_issue = $this->check_ad_formats(); if ( $ad_formats_issue ) { $issues[] = $ad_formats_issue; } // Check CMP vendors $cmp_issue = $this->check_cmp(); if ( $cmp_issue ) { $issues[] = $cmp_issue; } // Check hosted ads.txt redirect $ads_txt_redirect_issue = $this->check_hosted_adstxt_redirect(); if ( $ads_txt_redirect_issue ) { $issues[] = $ads_txt_redirect_issue; } // Suggest migration to hosted ads.txt $ads_txt_migration_issue = $this->check_adstxt_migration(); if ( $ads_txt_migration_issue ) { $issues[] = $ads_txt_migration_issue; } return $issues; } /** * Check Core Web Vitals / Presized Divs * * Checks if all available presized divs are activated. * Returns an issue if: * - Feature is disabled, OR * - Feature is enabled but not all presized divs are activated * * @since 4.0.0 * @return array|null */ public function check_core_web_vitals() { $presized_div_active = Options::get( 'presizeddiv-active', false ); // Get available presized divs from Actirise /** @var array> $available_divs */ $available_divs = Options::get( 'presizeddiv-actirise', array() ); // If no presized divs available, no issue if ( empty( $available_divs ) ) { return null; } // If feature is disabled, all divs are inactive if ( ! $presized_div_active ) { return array( 'type' => 'core_web_vitals', 'count' => count( $available_divs ), ); } // Feature is enabled, check selected/activated presized divs /** @var array> $selected_divs */ $selected_divs = Options::get( 'presizeddiv-selected', array() ); // Count inactive presized divs by comparing available vs selected $inactive_count = 0; foreach ( $available_divs as $available_div ) { $slot_name = isset( $available_div['slotName'] ) ? $available_div['slotName'] : ''; if ( empty( $slot_name ) ) { continue; } // Check if this div is in selected and active $is_active = false; foreach ( $selected_divs as $selected_div ) { if ( isset( $selected_div['slotName'] ) && $selected_div['slotName'] === $slot_name ) { $is_active = isset( $selected_div['active'] ) && $selected_div['active'] === true; break; } } if ( ! $is_active ) { ++$inactive_count; } } if ( $inactive_count > 0 ) { return array( 'type' => 'core_web_vitals', 'count' => $inactive_count, ); } return null; } /** * Check Ads.txt configuration * * @since 4.0.0 * @return array|null */ public function check_adstxt() { if ( Options::get( 'adstxt-active', false ) === false && Options::get( 'adstxt-update', false ) === true ) { $missing_count = Options::get( 'adstxt-missing-count', 0 ); $blacklist_count = Options::get( 'adstxt-blacklist-count', 0 ); return array( 'type' => 'adstxt', 'missing' => $missing_count, 'blacklist' => $blacklist_count, ); } return null; } /** * Check Ad Formats configuration * * @since 4.0.0 * @return array|null */ public function check_ad_formats() { $xpath_failed_active_config = Options::get( 'xpath_failed_active_config', 0 ); // Handle boolean true case (should be treated as 1) if ( true === $xpath_failed_active_config ) { $xpath_failed_active_config = 1; } // Convert to int if it's numeric if ( is_numeric( $xpath_failed_active_config ) ) { $xpath_failed_active_config = (int) $xpath_failed_active_config; } if ( $xpath_failed_active_config > 0 ) { return array( 'type' => 'ad_formats', 'count' => $xpath_failed_active_config, ); } return null; } /** * Check CMP vendors configuration * * @since 4.0.0 * @return array|null */ public function check_cmp() { // Only check if using own CMP (fastCMP disabled) if ( Options::get( 'fastcmp-enabled', false ) !== false ) { return null; } // Get current vendors $vendors = $this->get_cmp_vendors(); if ( empty( $vendors ) ) { return null; } // Get saved snapshot $saved_vendors = Options::get( 'cmp_vendors_snapshot', array() ); if ( ! is_array( $saved_vendors ) || empty( $saved_vendors ) ) { // No snapshot saved, consider it as changed return array( 'type' => 'cmp', ); } // Extract vendor IDs from current vendors $current_vendor_ids = array_map( function ( $vendor ) { if ( ! is_array( $vendor ) || ! isset( $vendor['id'] ) ) { return null; } return is_string( $vendor['id'] ) || is_int( $vendor['id'] ) ? (int) $vendor['id'] : null; }, $vendors ); // Remove null values $current_vendor_ids = array_values( array_filter( $current_vendor_ids ) ); // Sort for comparison sort( $current_vendor_ids ); sort( $saved_vendors ); // Compare if ( $saved_vendors !== $current_vendor_ids ) { return array( 'type' => 'cmp', ); } return null; } /** * Get CMP vendors from API * * @since 4.0.0 * @return array */ private function get_cmp_vendors() { // Return cached value if already fetched if ( $this->cmp_vendors_cache !== null ) { return $this->cmp_vendors_cache; } /** @var string $token */ $token = Options::get( 'settings-analytics-token', '' ); $response = $this->api->get( 'api', 'cmp_vendors', array(), $token ); if ( is_wp_error( $response ) || ! is_array( $response ) || empty( $response ) ) { $this->cmp_vendors_cache = array(); return array(); } $this->cmp_vendors_cache = $response; return $response; } /** * Check hosted ads.txt redirect * * Returns an issue if hosted ads.txt is enabled but the redirect * is obstructed by a local file or a conflicting third-party rule. * * @since 3.4.2 * @return array|null */ public function check_hosted_adstxt_redirect() { $hosted_enabled = Options::get( 'hosted-adstxt-enabled', false ); if ( ! $hosted_enabled ) { return null; } $redirect_error = Options::get( 'hosted-adstxt-redirect-error', false ); if ( empty( $redirect_error ) || ! is_array( $redirect_error ) ) { return null; } $error_type = isset( $redirect_error['error_type'] ) ? $redirect_error['error_type'] : ''; if ( 'file_exists' === $error_type || 'third_party' === $error_type ) { return array( 'type' => 'ads_txt_redirect', 'error_type' => $error_type, ); } return null; } /** * Check if publisher should migrate to hosted ads.txt * * Returns an issue if ads.txt is active but not using hosted mode, * suggesting the publisher migrate for automatic seller updates. * * @since 3.4.2 * @return array|null */ public function check_adstxt_migration() { $hosted_enabled = Options::get( 'hosted-adstxt-enabled', false ); if ( $hosted_enabled ) { return null; } // Check if hosted ads.txt feature is available for this website $website_settings = Options::get( 'website-settings', array() ); $hosted_adstxt_available = is_array( $website_settings ) && isset( $website_settings['hosted_adstxt'] ) && $website_settings['hosted_adstxt']; if ( $hosted_adstxt_available ) { return array( 'type' => 'ads_txt_migration', ); } return null; } }