web-vitals.php

I’ll build this as a full advanced performance/web-vitals.php module for your RX WordPress theme, including Core Web Vitals collection, resource hints, preload helpers, script strategy controls, REST/AJAX reporting, admin-safe toggles, and frontend JS output.

Create this file:

rx-theme/inc/performance/web-vitals.php

Then include it from your functions.php like this:

require_once get_template_directory() . '/inc/performance/web-vitals.php';

This module uses modern WordPress script strategy support such as defer, which WordPress added to wp_enqueue_script() / wp_register_script() arguments, and it also uses WordPress resource hints through wp_resource_hints. The web-vitals package is currently available as version 5.2.0 on npm, and the library supports reporting metrics such as LCP, CLS, INP, FCP and TTFB.

<?php
/**
 * RX Theme - Advanced Performance & Core Web Vitals Module
 *
 * File: inc/performance/web-vitals.php
 *
 * Features:
 * - Core Web Vitals frontend collection
 * - LCP, CLS, INP, FCP, TTFB, long task and navigation timing capture
 * - REST API endpoint for real user monitoring
 * - AJAX fallback endpoint
 * - Local database logging with retention control
 * - Resource hints: dns-prefetch, preconnect, prefetch, prerender
 * - Font preload
 * - CSS preload
 * - JS preload
 * - Hero image preload
 * - Fetch priority support
 * - Lazy-loading helpers
 * - Async/defer script helpers
 * - Disable emojis, embeds, dashicons for guests
 * - Remove unnecessary WordPress header noise
 * - Performance body class
 * - Debug mode for admins
 * - Safe defaults with filters
 *
 * Add in functions.php:
 * require_once get_template_directory() . '/inc/performance/web-vitals.php';
 */

defined( 'ABSPATH' ) || exit;

if ( ! class_exists( 'RX_Theme_Web_Vitals' ) ) {

	final class RX_Theme_Web_Vitals {

		const VERSION          = '1.0.0';
		const OPTION_KEY       = 'rx_theme_web_vitals_settings';
		const TABLE_SLUG       = 'rx_web_vitals';
		const REST_NAMESPACE   = 'rx-theme/v1';
		const REST_ROUTE       = '/web-vitals';
		const NONCE_ACTION     = 'rx_web_vitals_nonce_action';
		const NONCE_NAME       = 'rx_web_vitals_nonce';
		const TRANSIENT_PREFIX = 'rx_wv_';

		private static $instance = null;

		private $settings = array();

		public static function instance() {
			if ( null === self::$instance ) {
				self::$instance = new self();
			}

			return self::$instance;
		}

		private function __construct() {
			$this->settings = $this->get_settings();

			add_action( 'after_setup_theme', array( $this, 'theme_supports' ), 20 );
			add_action( 'init', array( $this, 'maybe_create_table' ) );
			add_action( 'init', array( $this, 'register_hooks' ) );
		}

		public function register_hooks() {
			if ( is_admin() && ! wp_doing_ajax() ) {
				return;
			}

			add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_assets' ), 1 );
			add_action( 'wp_head', array( $this, 'print_early_resource_hints' ), 1 );
			add_action( 'wp_head', array( $this, 'print_preloads' ), 2 );
			add_action( 'wp_head', array( $this, 'print_critical_runtime_config' ), 3 );
			add_action( 'wp_footer', array( $this, 'print_web_vitals_collector' ), 99 );

			add_filter( 'wp_resource_hints', array( $this, 'resource_hints' ), 10, 2 );
			add_filter( 'script_loader_tag', array( $this, 'script_loader_tag' ), 10, 3 );
			add_filter( 'style_loader_tag', array( $this, 'style_loader_tag' ), 10, 4 );
			add_filter( 'wp_get_attachment_image_attributes', array( $this, 'image_attributes' ), 10, 3 );
			add_filter( 'body_class', array( $this, 'body_class' ) );

			add_action( 'rest_api_init', array( $this, 'register_rest_routes' ) );
			add_action( 'wp_ajax_rx_web_vitals', array( $this, 'ajax_receive_web_vitals' ) );
			add_action( 'wp_ajax_nopriv_rx_web_vitals', array( $this, 'ajax_receive_web_vitals' ) );

			if ( ! empty( $this->settings['cleanup_head'] ) ) {
				$this->cleanup_wordpress_head();
			}

			if ( ! empty( $this->settings['disable_emojis'] ) ) {
				$this->disable_emojis();
			}

			if ( ! empty( $this->settings['disable_embeds'] ) ) {
				$this->disable_embeds();
			}

			if ( ! empty( $this->settings['disable_dashicons_for_guests'] ) ) {
				add_action( 'wp_enqueue_scripts', array( $this, 'disable_dashicons_for_guests' ), 100 );
			}
		}

		public function theme_supports() {
			add_theme_support( 'html5', array( 'script', 'style' ) );
			add_theme_support( 'responsive-embeds' );
			add_theme_support( 'align-wide' );
		}

		public function default_settings() {
			return array(
				'enabled'                       => true,
				'debug'                         => false,
				'sample_rate'                   => 1.0,
				'storage_enabled'               => true,
				'storage_retention_days'        => 30,
				'use_rest'                      => true,
				'use_ajax_fallback'             => true,
				'cleanup_head'                  => true,
				'disable_emojis'                => true,
				'disable_embeds'                => false,
				'disable_dashicons_for_guests'  => true,
				'defer_theme_scripts'           => true,
				'async_external_scripts'        => false,
				'preload_web_vitals_library'    => true,
				'web_vitals_library_url'        => 'https://unpkg.com/web-vitals@5/dist/web-vitals.attribution.iife.js',
				'preconnect_urls'               => array(
					'https://fonts.googleapis.com',
					'https://fonts.gstatic.com',
				),
				'dns_prefetch_urls'             => array(
					'//fonts.googleapis.com',
					'//fonts.gstatic.com',
				),
				'prefetch_urls'                 => array(),
				'prerender_urls'                => array(),
				'preload_fonts'                 => array(),
				'preload_styles'                => array(),
				'preload_scripts'               => array(),
				'preload_images'                => array(),
				'critical_css'                  => '',
				'max_payload_bytes'             => 12000,
				'bot_protection'                => true,
				'allowed_metrics'               => array(
					'CLS',
					'FCP',
					'INP',
					'LCP',
					'TTFB',
					'NavigationTiming',
					'LongTask',
					'ResourceTiming',
				),
			);
		}

		public function get_settings() {
			$defaults = $this->default_settings();
			$saved    = get_option( self::OPTION_KEY, array() );

			if ( ! is_array( $saved ) ) {
				$saved = array();
			}

			$settings = wp_parse_args( $saved, $defaults );

			/**
			 * Filter RX Web Vitals settings.
			 *
			 * Example:
			 * add_filter('rx_theme_web_vitals_settings', function($settings) {
			 *     $settings['debug'] = true;
			 *     return $settings;
			 * });
			 */
			return apply_filters( 'rx_theme_web_vitals_settings', $settings );
		}

		public function maybe_create_table() {
			if ( empty( $this->settings['storage_enabled'] ) ) {
				return;
			}

			$created = get_option( 'rx_theme_web_vitals_table_created' );

			if ( self::VERSION === $created ) {
				return;
			}

			global $wpdb;

			$table_name      = $this->get_table_name();
			$charset_collate = $wpdb->get_charset_collate();

			$sql = "CREATE TABLE {$table_name} (
				id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
				created_at DATETIME NOT NULL,
				metric_name VARCHAR(40) NOT NULL,
				metric_value DOUBLE NULL,
				metric_rating VARCHAR(20) NULL,
				metric_delta DOUBLE NULL,
				metric_id VARCHAR(120) NULL,
				page_url TEXT NULL,
				page_path TEXT NULL,
				referrer TEXT NULL,
				user_agent TEXT NULL,
				connection_type VARCHAR(60) NULL,
				navigation_type VARCHAR(80) NULL,
				device_memory VARCHAR(30) NULL,
				hardware_concurrency VARCHAR(30) NULL,
				effective_type VARCHAR(30) NULL,
				attribution LONGTEXT NULL,
				extra LONGTEXT NULL,
				ip_hash VARCHAR(128) NULL,
				PRIMARY KEY  (id),
				KEY metric_name (metric_name),
				KEY metric_rating (metric_rating),
				KEY created_at (created_at)
			) {$charset_collate};";

			require_once ABSPATH . 'wp-admin/includes/upgrade.php';
			dbDelta( $sql );

			update_option( 'rx_theme_web_vitals_table_created', self::VERSION, false );

			$this->cleanup_old_rows();
		}

		private function get_table_name() {
			global $wpdb;
			return $wpdb->prefix . self::TABLE_SLUG;
		}

		public function enqueue_assets() {
			if ( empty( $this->settings['enabled'] ) ) {
				return;
			}

			if ( $this->is_ignored_request() ) {
				return;
			}

			$library_url = esc_url_raw( $this->settings['web_vitals_library_url'] );

			wp_register_script(
				'rx-web-vitals-lib',
				$library_url,
				array(),
				null,
				array(
					'in_footer' => true,
					'strategy'  => 'defer',
				)
			);

			wp_enqueue_script( 'rx-web-vitals-lib' );

			wp_register_script(
				'rx-web-vitals-runtime',
				'',
				array( 'rx-web-vitals-lib' ),
				self::VERSION,
				array(
					'in_footer' => true,
					'strategy'  => 'defer',
				)
			);

			wp_enqueue_script( 'rx-web-vitals-runtime' );
		}

		public function print_critical_runtime_config() {
			if ( empty( $this->settings['enabled'] ) ) {
				return;
			}

			$config = array(
				'restUrl'         => esc_url_raw( rest_url( self::REST_NAMESPACE . self::REST_ROUTE ) ),
				'ajaxUrl'         => esc_url_raw( admin_url( 'admin-ajax.php' ) ),
				'nonce'           => wp_create_nonce( 'wp_rest' ),
				'ajaxNonce'       => wp_create_nonce( self::NONCE_ACTION ),
				'useRest'         => (bool) $this->settings['use_rest'],
				'useAjaxFallback' => (bool) $this->settings['use_ajax_fallback'],
				'sampleRate'      => (float) $this->settings['sample_rate'],
				'debug'           => (bool) $this->settings['debug'],
				'page'            => array(
					'url'       => $this->current_url(),
					'path'      => wp_parse_url( $this->current_url(), PHP_URL_PATH ),
					'isHome'    => is_front_page(),
					'isSingle'  => is_single(),
					'isPage'    => is_page(),
					'isArchive' => is_archive(),
					'postId'    => get_queried_object_id(),
				),
			);

			echo "\n<script id=\"rx-web-vitals-config\">\n";
			echo 'window.RX_WEB_VITALS_CONFIG = ' . wp_json_encode( $config ) . ';';
			echo "\n</script>\n";

			$critical_css = trim( (string) $this->settings['critical_css'] );

			if ( '' !== $critical_css ) {
				echo "\n<style id=\"rx-critical-css\">\n";
				echo wp_strip_all_tags( $critical_css );
				echo "\n</style>\n";
			}
		}

		public function print_web_vitals_collector() {
			if ( empty( $this->settings['enabled'] ) ) {
				return;
			}

			if ( $this->is_ignored_request() ) {
				return;
			}
			?>
<script id="rx-web-vitals-collector">
(function () {
	'use strict';

	var config = window.RX_WEB_VITALS_CONFIG || {};
	var sampled = Math.random() <= (Number(config.sampleRate || 1));

	if (!sampled) {
		return;
	}

	var queue = [];
	var sentIds = {};
	var maxQueue = 80;

	function log() {
		if (config.debug && window.console && console.log) {
			console.log.apply(console, arguments);
		}
	}

	function nowISO() {
		try {
			return new Date().toISOString();
		} catch (e) {
			return '';
		}
	}

	function safeString(value, max) {
		if (typeof value === 'undefined' || value === null) {
			return '';
		}

		value = String(value);

		if (value.length > max) {
			return value.slice(0, max);
		}

		return value;
	}

	function getConnection() {
		var c = navigator.connection || navigator.mozConnection || navigator.webkitConnection || {};
		return {
			effectiveType: safeString(c.effectiveType, 30),
			type: safeString(c.type, 60),
			downlink: c.downlink || null,
			rtt: c.rtt || null,
			saveData: !!c.saveData
		};
	}

	function getDevice() {
		return {
			deviceMemory: navigator.deviceMemory || null,
			hardwareConcurrency: navigator.hardwareConcurrency || null,
			language: navigator.language || '',
			platform: navigator.platform || '',
			cookieEnabled: !!navigator.cookieEnabled,
			visibilityState: document.visibilityState || ''
		};
	}

	function getNavigationType() {
		try {
			var nav = performance.getEntriesByType('navigation');
			if (nav && nav[0] && nav[0].type) {
				return nav[0].type;
			}
		} catch (e) {}

		if (performance && performance.navigation) {
			var map = {
				0: 'navigate',
				1: 'reload',
				2: 'back_forward',
				255: 'reserved'
			};

			return map[performance.navigation.type] || 'unknown';
		}

		return 'unknown';
	}

	function cleanAttribution(attribution) {
		if (!attribution || typeof attribution !== 'object') {
			return {};
		}

		var clean = {};
		var allowed = [
			'element',
			'eventType',
			'eventTarget',
			'loadState',
			'url',
			'largestShiftTarget',
			'largestShiftTime',
			'largestShiftValue',
			'largestShiftSource',
			'largestShiftEntry',
			'lcpEntry',
			'lcpResourceEntry',
			'lcpResourceLoadDelay',
			'lcpResourceLoadDuration',
			'lcpElementRenderDelay',
			'lcpElement',
			'timeToFirstByte',
			'resourceLoadDelay',
			'resourceLoadDuration',
			'elementRenderDelay',
			'interactionTarget',
			'interactionType',
			'interactionTime',
			'inputDelay',
			'processingDuration',
			'presentationDelay',
			'nextPaintTime'
		];

		allowed.forEach(function (key) {
			if (Object.prototype.hasOwnProperty.call(attribution, key)) {
				var value = attribution[key];

				if (value && typeof value === 'object') {
					try {
						value = JSON.stringify(value);
					} catch (e) {
						value = String(value);
					}
				}

				clean[key] = safeString(value, 1000);
			}
		});

		return clean;
	}

	function buildPayload(metric, extra) {
		var connection = getConnection();
		var device = getDevice();

		return {
			createdAt: nowISO(),
			name: safeString(metric.name || extra.name || '', 40),
			value: Number(metric.value || extra.value || 0),
			rating: safeString(metric.rating || extra.rating || '', 20),
			delta: Number(metric.delta || 0),
			id: safeString(metric.id || '', 120),
			navigationType: safeString(metric.navigationType || getNavigationType(), 80),
			pageUrl: safeString((config.page && config.page.url) || location.href, 2000),
			pagePath: safeString((config.page && config.page.path) || location.pathname, 1000),
			referrer: safeString(document.referrer || '', 2000),
			userAgent: safeString(navigator.userAgent || '', 600),
			connectionType: connection.type,
			effectiveType: connection.effectiveType,
			deviceMemory: safeString(device.deviceMemory, 30),
			hardwareConcurrency: safeString(device.hardwareConcurrency, 30),
			attribution: cleanAttribution(metric.attribution || {}),
			extra: extra || {}
		};
	}

	function sendBeaconPayload(payload) {
		var body = JSON.stringify(payload);

		if (navigator.sendBeacon && config.restUrl && config.useRest) {
			try {
				var blob = new Blob([body], { type: 'application/json' });
				if (navigator.sendBeacon(config.restUrl, blob)) {
					return true;
				}
			} catch (e) {}
		}

		return false;
	}

	function sendFetchPayload(payload) {
		if (!config.restUrl || !config.useRest || !window.fetch) {
			return Promise.reject();
		}

		return fetch(config.restUrl, {
			method: 'POST',
			credentials: 'same-origin',
			keepalive: true,
			headers: {
				'Content-Type': 'application/json',
				'X-WP-Nonce': config.nonce || ''
			},
			body: JSON.stringify(payload)
		});
	}

	function sendAjaxPayload(payload) {
		if (!config.ajaxUrl || !config.useAjaxFallback || !window.fetch) {
			return Promise.reject();
		}

		var form = new FormData();
		form.append('action', 'rx_web_vitals');
		form.append('nonce', config.ajaxNonce || '');
		form.append('payload', JSON.stringify(payload));

		return fetch(config.ajaxUrl, {
			method: 'POST',
			credentials: 'same-origin',
			keepalive: true,
			body: form
		});
	}

	function flush(payload) {
		if (!payload || !payload.name) {
			return;
		}

		var dedupeKey = payload.name + ':' + payload.id + ':' + payload.value;

		if (sentIds[dedupeKey]) {
			return;
		}

		sentIds[dedupeKey] = true;

		log('[RX Web Vitals]', payload.name, payload.value, payload.rating, payload);

		if (sendBeaconPayload(payload)) {
			return;
		}

		sendFetchPayload(payload).catch(function () {
			return sendAjaxPayload(payload);
		}).catch(function () {
			queue.push(payload);

			if (queue.length > maxQueue) {
				queue.shift();
			}
		});
	}

	function report(metric, extra) {
		flush(buildPayload(metric || {}, extra || {}));
	}

	function observeLongTasks() {
		if (!('PerformanceObserver' in window)) {
			return;
		}

		try {
			var observer = new PerformanceObserver(function (list) {
				list.getEntries().forEach(function (entry) {
					if (entry.duration >= 50) {
						report({
							name: 'LongTask',
							value: entry.duration,
							rating: entry.duration > 200 ? 'poor' : 'needs-improvement',
							id: 'longtask-' + Math.round(entry.startTime)
						}, {
							name: 'LongTask',
							startTime: entry.startTime,
							duration: entry.duration,
							entryType: entry.entryType
						});
					}
				});
			});

			observer.observe({ type: 'longtask', buffered: true });
		} catch (e) {}
	}

	function reportNavigationTiming() {
		try {
			var nav = performance.getEntriesByType('navigation');

			if (!nav || !nav[0]) {
				return;
			}

			var n = nav[0];

			var metrics = {
				dns: n.domainLookupEnd - n.domainLookupStart,
				connect: n.connectEnd - n.connectStart,
				tls: n.secureConnectionStart > 0 ? n.connectEnd - n.secureConnectionStart : 0,
				request: n.responseStart - n.requestStart,
				response: n.responseEnd - n.responseStart,
				domInteractive: n.domInteractive,
				domContentLoaded: n.domContentLoadedEventEnd - n.domContentLoadedEventStart,
				loadEvent: n.loadEventEnd - n.loadEventStart,
				transferSize: n.transferSize || 0,
				encodedBodySize: n.encodedBodySize || 0,
				decodedBodySize: n.decodedBodySize || 0,
				type: n.type || ''
			};

			report({
				name: 'NavigationTiming',
				value: n.loadEventEnd || n.duration || 0,
				rating: 'info',
				id: 'navigation-' + Date.now()
			}, metrics);
		} catch (e) {}
	}

	function reportResourceTiming() {
		try {
			var resources = performance.getEntriesByType('resource') || [];
			var slow = resources
				.filter(function (r) {
					return r.duration >= 500;
				})
				.slice(0, 10)
				.map(function (r) {
					return {
						name: safeString(r.name, 300),
						initiatorType: safeString(r.initiatorType, 40),
						duration: Math.round(r.duration),
						transferSize: r.transferSize || 0,
						encodedBodySize: r.encodedBodySize || 0,
						decodedBodySize: r.decodedBodySize || 0
					};
				});

			if (slow.length) {
				report({
					name: 'ResourceTiming',
					value: slow.length,
					rating: 'info',
					id: 'resource-' + Date.now()
				}, {
					slowResources: slow
				});
			}
		} catch (e) {}
	}

	function initWebVitals() {
		var wv = window.webVitals;

		if (!wv) {
			log('[RX Web Vitals] webVitals library not found');
			return;
		}

		var options = {
			reportAllChanges: true
		};

		if (typeof wv.onCLS === 'function') {
			wv.onCLS(report, options);
		}

		if (typeof wv.onFCP === 'function') {
			wv.onFCP(report, options);
		}

		if (typeof wv.onINP === 'function') {
			wv.onINP(report, options);
		}

		if (typeof wv.onLCP === 'function') {
			wv.onLCP(report, options);
		}

		if (typeof wv.onTTFB === 'function') {
			wv.onTTFB(report, options);
		}

		observeLongTasks();

		window.addEventListener('load', function () {
			setTimeout(reportNavigationTiming, 0);
			setTimeout(reportResourceTiming, 1500);
		});
	}

	function retryInit(count) {
		count = count || 0;

		if (window.webVitals) {
			initWebVitals();
			return;
		}

		if (count < 30) {
			setTimeout(function () {
				retryInit(count + 1);
			}, 100);
		}
	}

	retryInit(0);

	window.addEventListener('online', function () {
		var copy = queue.slice();
		queue = [];
		copy.forEach(flush);
	});

	document.addEventListener('visibilitychange', function () {
		if (document.visibilityState === 'hidden' && queue.length) {
			var copy = queue.slice();
			queue = [];
			copy.forEach(function (payload) {
				sendBeaconPayload(payload);
			});
		}
	});
})();
</script>
			<?php
		}

		public function register_rest_routes() {
			register_rest_route(
				self::REST_NAMESPACE,
				self::REST_ROUTE,
				array(
					'methods'             => 'POST',
					'callback'            => array( $this, 'rest_receive_web_vitals' ),
					'permission_callback' => '__return_true',
					'args'                => array(),
				)
			);
		}

		public function rest_receive_web_vitals( WP_REST_Request $request ) {
			if ( empty( $this->settings['enabled'] ) ) {
				return new WP_REST_Response(
					array(
						'success' => false,
						'message' => 'disabled',
					),
					403
				);
			}

			if ( $this->is_bot_request() ) {
				return new WP_REST_Response(
					array(
						'success' => false,
						'message' => 'ignored',
					),
					202
				);
			}

			$raw = $request->get_body();

			if ( strlen( $raw ) > absint( $this->settings['max_payload_bytes'] ) ) {
				return new WP_REST_Response(
					array(
						'success' => false,
						'message' => 'payload-too-large',
					),
					413
				);
			}

			$data = json_decode( $raw, true );

			if ( ! is_array( $data ) ) {
				$data = $request->get_json_params();
			}

			$result = $this->handle_metric_payload( $data );

			return new WP_REST_Response( $result, ! empty( $result['success'] ) ? 200 : 400 );
		}

		public function ajax_receive_web_vitals() {
			if ( empty( $this->settings['enabled'] ) ) {
				wp_send_json_error( array( 'message' => 'disabled' ), 403 );
			}

			check_ajax_referer( self::NONCE_ACTION, 'nonce' );

			$payload = isset( $_POST['payload'] ) ? wp_unslash( $_POST['payload'] ) : '';

			if ( strlen( $payload ) > absint( $this->settings['max_payload_bytes'] ) ) {
				wp_send_json_error( array( 'message' => 'payload-too-large' ), 413 );
			}

			$data = json_decode( $payload, true );

			$result = $this->handle_metric_payload( $data );

			if ( ! empty( $result['success'] ) ) {
				wp_send_json_success( $result );
			}

			wp_send_json_error( $result, 400 );
		}

		private function handle_metric_payload( $data ) {
			if ( ! is_array( $data ) ) {
				return array(
					'success' => false,
					'message' => 'invalid-payload',
				);
			}

			$metric_name = isset( $data['name'] ) ? sanitize_key( $data['name'] ) : '';

			if ( '' === $metric_name ) {
				return array(
					'success' => false,
					'message' => 'missing-metric',
				);
			}

			$allowed = array_map( 'sanitize_key', (array) $this->settings['allowed_metrics'] );

			if ( ! in_array( $metric_name, $allowed, true ) ) {
				return array(
					'success' => false,
					'message' => 'metric-not-allowed',
				);
			}

			$value = isset( $data['value'] ) ? (float) $data['value'] : 0;

			if ( $value < 0 ) {
				$value = 0;
			}

			$clean = array(
				'created_at'           => current_time( 'mysql', true ),
				'metric_name'          => $metric_name,
				'metric_value'         => $value,
				'metric_rating'        => isset( $data['rating'] ) ? sanitize_text_field( $data['rating'] ) : '',
				'metric_delta'         => isset( $data['delta'] ) ? (float) $data['delta'] : 0,
				'metric_id'            => isset( $data['id'] ) ? sanitize_text_field( $data['id'] ) : '',
				'page_url'             => isset( $data['pageUrl'] ) ? esc_url_raw( $data['pageUrl'] ) : '',
				'page_path'            => isset( $data['pagePath'] ) ? sanitize_text_field( $data['pagePath'] ) : '',
				'referrer'             => isset( $data['referrer'] ) ? esc_url_raw( $data['referrer'] ) : '',
				'user_agent'           => isset( $data['userAgent'] ) ? sanitize_text_field( $data['userAgent'] ) : '',
				'connection_type'      => isset( $data['connectionType'] ) ? sanitize_text_field( $data['connectionType'] ) : '',
				'navigation_type'      => isset( $data['navigationType'] ) ? sanitize_text_field( $data['navigationType'] ) : '',
				'device_memory'        => isset( $data['deviceMemory'] ) ? sanitize_text_field( $data['deviceMemory'] ) : '',
				'hardware_concurrency' => isset( $data['hardwareConcurrency'] ) ? sanitize_text_field( $data['hardwareConcurrency'] ) : '',
				'effective_type'       => isset( $data['effectiveType'] ) ? sanitize_text_field( $data['effectiveType'] ) : '',
				'attribution'          => isset( $data['attribution'] ) ? wp_json_encode( $this->recursive_sanitize( $data['attribution'] ) ) : '',
				'extra'                => isset( $data['extra'] ) ? wp_json_encode( $this->recursive_sanitize( $data['extra'] ) ) : '',
				'ip_hash'              => $this->get_ip_hash(),
			);

			if ( ! empty( $this->settings['storage_enabled'] ) ) {
				$this->insert_metric( $clean );
			}

			do_action( 'rx_theme_web_vitals_metric_received', $clean, $data );

			return array(
				'success' => true,
				'message' => 'stored',
			);
		}

		private function insert_metric( $data ) {
			global $wpdb;

			$table_name = $this->get_table_name();

			$wpdb->insert(
				$table_name,
				$data,
				array(
					'%s',
					'%s',
					'%f',
					'%s',
					'%f',
					'%s',
					'%s',
					'%s',
					'%s',
					'%s',
					'%s',
					'%s',
					'%s',
					'%s',
					'%s',
					'%s',
					'%s',
				)
			);

			if ( mt_rand( 1, 100 ) === 1 ) {
				$this->cleanup_old_rows();
			}
		}

		private function cleanup_old_rows() {
			if ( empty( $this->settings['storage_enabled'] ) ) {
				return;
			}

			$days = absint( $this->settings['storage_retention_days'] );

			if ( $days < 1 ) {
				$days = 30;
			}

			global $wpdb;

			$table_name = $this->get_table_name();
			$date       = gmdate( 'Y-m-d H:i:s', time() - ( DAY_IN_SECONDS * $days ) );

			$wpdb->query(
				$wpdb->prepare(
					"DELETE FROM {$table_name} WHERE created_at < %s",
					$date
				)
			);
		}

		private function recursive_sanitize( $value ) {
			if ( is_array( $value ) ) {
				$clean = array();

				foreach ( $value as $key => $item ) {
					$clean_key           = is_string( $key ) ? sanitize_key( $key ) : absint( $key );
					$clean[ $clean_key ] = $this->recursive_sanitize( $item );
				}

				return $clean;
			}

			if ( is_bool( $value ) ) {
				return $value;
			}

			if ( is_numeric( $value ) ) {
				return $value + 0;
			}

			return sanitize_text_field( (string) $value );
		}

		public function resource_hints( $urls, $relation_type ) {
			if ( empty( $this->settings['enabled'] ) ) {
				return $urls;
			}

			$map = array(
				'dns-prefetch' => 'dns_prefetch_urls',
				'preconnect'   => 'preconnect_urls',
				'prefetch'     => 'prefetch_urls',
				'prerender'    => 'prerender_urls',
			);

			if ( empty( $map[ $relation_type ] ) ) {
				return $urls;
			}

			$key = $map[ $relation_type ];

			foreach ( (array) $this->settings[ $key ] as $url ) {
				$url = trim( (string) $url );

				if ( '' === $url ) {
					continue;
				}

				if ( 'preconnect' === $relation_type ) {
					$urls[] = array(
						'href'        => esc_url( $url ),
						'crossorigin' => 'anonymous',
					);
				} else {
					$urls[] = esc_url( $url );
				}
			}

			return array_unique( $urls, SORT_REGULAR );
		}

		public function print_early_resource_hints() {
			if ( empty( $this->settings['enabled'] ) ) {
				return;
			}

			if ( ! empty( $this->settings['preload_web_vitals_library'] ) && ! empty( $this->settings['web_vitals_library_url'] ) ) {
				printf(
					'<link rel="preload" href="%s" as="script" crossorigin="anonymous">' . "\n",
					esc_url( $this->settings['web_vitals_library_url'] )
				);
			}
		}

		public function print_preloads() {
			if ( empty( $this->settings['enabled'] ) ) {
				return;
			}

			foreach ( (array) $this->settings['preload_fonts'] as $font_url ) {
				$font_url = esc_url( $font_url );

				if ( $font_url ) {
					printf(
						'<link rel="preload" href="%s" as="font" type="%s" crossorigin="anonymous">' . "\n",
						$font_url,
						esc_attr( $this->guess_font_mime_type( $font_url ) )
					);
				}
			}

			foreach ( (array) $this->settings['preload_styles'] as $style_url ) {
				$style_url = esc_url( $style_url );

				if ( $style_url ) {
					printf(
						'<link rel="preload" href="%s" as="style">' . "\n",
						$style_url
					);
				}
			}

			foreach ( (array) $this->settings['preload_scripts'] as $script_url ) {
				$script_url = esc_url( $script_url );

				if ( $script_url ) {
					printf(
						'<link rel="preload" href="%s" as="script" crossorigin="anonymous">' . "\n",
						$script_url
					);
				}
			}

			foreach ( (array) $this->settings['preload_images'] as $image_url ) {
				$image_url = esc_url( $image_url );

				if ( $image_url ) {
					printf(
						'<link rel="preload" href="%s" as="image" fetchpriority="high">' . "\n",
						$image_url
					);
				}
			}
		}

		private function guess_font_mime_type( $url ) {
			$path = wp_parse_url( $url, PHP_URL_PATH );
			$ext  = strtolower( pathinfo( (string) $path, PATHINFO_EXTENSION ) );

			switch ( $ext ) {
				case 'woff2':
					return 'font/woff2';

				case 'woff':
					return 'font/woff';

				case 'ttf':
					return 'font/ttf';

				case 'otf':
					return 'font/otf';

				default:
					return 'font/woff2';
			}
		}

		public function script_loader_tag( $tag, $handle, $src ) {
			if ( is_admin() ) {
				return $tag;
			}

			$defer_handles = apply_filters(
				'rx_theme_defer_script_handles',
				array(
					'rx-web-vitals-lib',
					'rx-web-vitals-runtime',
				)
			);

			$async_handles = apply_filters(
				'rx_theme_async_script_handles',
				array()
			);

			if ( in_array( $handle, $defer_handles, true ) && false === strpos( $tag, ' defer' ) ) {
				$tag = str_replace( '<script ', '<script defer ', $tag );
			}

			if ( in_array( $handle, $async_handles, true ) && false === strpos( $tag, ' async' ) ) {
				$tag = str_replace( '<script ', '<script async ', $tag );
			}

			if ( false !== strpos( $src, 'unpkg.com' ) || false !== strpos( $src, 'cdn.jsdelivr.net' ) ) {
				if ( false === strpos( $tag, ' crossorigin=' ) ) {
					$tag = str_replace( '<script ', '<script crossorigin="anonymous" ', $tag );
				}
			}

			return $tag;
		}

		public function style_loader_tag( $html, $handle, $href, $media ) {
			$preload_handles = apply_filters( 'rx_theme_preload_style_handles', array() );

			if ( in_array( $handle, $preload_handles, true ) ) {
				$html = sprintf(
					'<link rel="preload" href="%1$s" as="style" onload="this.onload=null;this.rel=\'stylesheet\'">%2$s<noscript><link rel="stylesheet" href="%1$s"></noscript>',
					esc_url( $href ),
					"\n"
				);
			}

			return $html;
		}

		public function image_attributes( $attr, $attachment, $size ) {
			if ( is_admin() ) {
				return $attr;
			}

			if ( empty( $attr['loading'] ) ) {
				$attr['loading'] = 'lazy';
			}

			if ( empty( $attr['decoding'] ) ) {
				$attr['decoding'] = 'async';
			}

			if ( is_singular() && in_the_loop() && is_main_query() ) {
				static $first_content_image = false;

				if ( false === $first_content_image ) {
					$first_content_image = true;

					$attr['loading']       = 'eager';
					$attr['fetchpriority'] = 'high';
				}
			}

			return $attr;
		}

		public function body_class( $classes ) {
			$classes[] = 'rx-performance-enabled';

			if ( ! empty( $this->settings['debug'] ) ) {
				$classes[] = 'rx-performance-debug';
			}

			return $classes;
		}

		private function cleanup_wordpress_head() {
			remove_action( 'wp_head', 'wp_generator' );
			remove_action( 'wp_head', 'wlwmanifest_link' );
			remove_action( 'wp_head', 'rsd_link' );
			remove_action( 'wp_head', 'wp_shortlink_wp_head' );
			remove_action( 'wp_head', 'adjacent_posts_rel_link_wp_head' );
			remove_action( 'wp_head', 'rest_output_link_wp_head' );
			remove_action( 'wp_head', 'wp_oembed_add_discovery_links' );
			remove_action( 'template_redirect', 'rest_output_link_header', 11 );
		}

		private function disable_emojis() {
			remove_action( 'wp_head', 'print_emoji_detection_script', 7 );
			remove_action( 'admin_print_scripts', 'print_emoji_detection_script' );
			remove_action( 'wp_print_styles', 'print_emoji_styles' );
			remove_action( 'admin_print_styles', 'print_emoji_styles' );
			remove_filter( 'the_content_feed', 'wp_staticize_emoji' );
			remove_filter( 'comment_text_rss', 'wp_staticize_emoji' );
			remove_filter( 'wp_mail', 'wp_staticize_emoji_for_email' );

			add_filter( 'emoji_svg_url', '__return_false' );
		}

		private function disable_embeds() {
			wp_deregister_script( 'wp-embed' );
		}

		public function disable_dashicons_for_guests() {
			if ( ! is_user_logged_in() ) {
				wp_dequeue_style( 'dashicons' );
				wp_deregister_style( 'dashicons' );
			}
		}

		private function current_url() {
			$scheme = is_ssl() ? 'https://' : 'http://';
			$host   = isset( $_SERVER['HTTP_HOST'] ) ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_HOST'] ) ) : '';
			$uri    = isset( $_SERVER['REQUEST_URI'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '';

			return esc_url_raw( $scheme . $host . $uri );
		}

		private function get_ip_hash() {
			$ip = '';

			if ( ! empty( $_SERVER['HTTP_CF_CONNECTING_IP'] ) ) {
				$ip = sanitize_text_field( wp_unslash( $_SERVER['HTTP_CF_CONNECTING_IP'] ) );
			} elseif ( ! empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
				$parts = explode( ',', sanitize_text_field( wp_unslash( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) );
				$ip    = trim( $parts[0] );
			} elseif ( ! empty( $_SERVER['REMOTE_ADDR'] ) ) {
				$ip = sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) );
			}

			if ( '' === $ip ) {
				return '';
			}

			return hash_hmac( 'sha256', $ip, wp_salt( 'auth' ) );
		}

		private function is_bot_request() {
			if ( empty( $this->settings['bot_protection'] ) ) {
				return false;
			}

			$user_agent = isset( $_SERVER['HTTP_USER_AGENT'] ) ? strtolower( sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ) ) : '';

			if ( '' === $user_agent ) {
				return true;
			}

			$bots = array(
				'bot',
				'crawl',
				'spider',
				'slurp',
				'mediapartners',
				'facebookexternalhit',
				'whatsapp',
				'telegrambot',
				'linkedinbot',
				'pingdom',
				'lighthouse',
				'pagespeed',
				'gtmetrix',
				'uptimerobot',
			);

			foreach ( $bots as $bot ) {
				if ( false !== strpos( $user_agent, $bot ) ) {
					return true;
				}
			}

			return false;
		}

		private function is_ignored_request() {
			if ( is_feed() || is_robots() || is_trackback() || wp_doing_cron() ) {
				return true;
			}

			if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {
				return true;
			}

			return false;
		}

		public static function get_recent_metrics( $limit = 100 ) {
			global $wpdb;

			$instance = self::instance();
			$table    = $instance->get_table_name();
			$limit    = absint( $limit );

			if ( $limit < 1 ) {
				$limit = 100;
			}

			return $wpdb->get_results(
				$wpdb->prepare(
					"SELECT * FROM {$table} ORDER BY created_at DESC LIMIT %d",
					$limit
				),
				ARRAY_A
			);
		}

		public static function get_metric_summary( $days = 7 ) {
			global $wpdb;

			$instance = self::instance();
			$table    = $instance->get_table_name();
			$days     = absint( $days );

			if ( $days < 1 ) {
				$days = 7;
			}

			$since = gmdate( 'Y-m-d H:i:s', time() - ( DAY_IN_SECONDS * $days ) );

			return $wpdb->get_results(
				$wpdb->prepare(
					"SELECT 
						metric_name,
						metric_rating,
						COUNT(*) as total,
						AVG(metric_value) as avg_value,
						MIN(metric_value) as min_value,
						MAX(metric_value) as max_value
					FROM {$table}
					WHERE created_at >= %s
					GROUP BY metric_name, metric_rating
					ORDER BY metric_name ASC, total DESC",
					$since
				),
				ARRAY_A
			);
		}
	}

	RX_Theme_Web_Vitals::instance();
}

/**
 * Helper: add preconnect URL.
 */
if ( ! function_exists( 'rx_theme_add_preconnect' ) ) {
	function rx_theme_add_preconnect( $url ) {
		add_filter(
			'rx_theme_web_vitals_settings',
			function ( $settings ) use ( $url ) {
				$settings['preconnect_urls'][] = esc_url_raw( $url );
				return $settings;
			}
		);
	}
}

/**
 * Helper: add dns-prefetch URL.
 */
if ( ! function_exists( 'rx_theme_add_dns_prefetch' ) ) {
	function rx_theme_add_dns_prefetch( $url ) {
		add_filter(
			'rx_theme_web_vitals_settings',
			function ( $settings ) use ( $url ) {
				$settings['dns_prefetch_urls'][] = esc_url_raw( $url );
				return $settings;
			}
		);
	}
}

/**
 * Helper: preload font.
 */
if ( ! function_exists( 'rx_theme_preload_font' ) ) {
	function rx_theme_preload_font( $url ) {
		add_filter(
			'rx_theme_web_vitals_settings',
			function ( $settings ) use ( $url ) {
				$settings['preload_fonts'][] = esc_url_raw( $url );
				return $settings;
			}
		);
	}
}

/**
 * Helper: preload image.
 */
if ( ! function_exists( 'rx_theme_preload_image' ) ) {
	function rx_theme_preload_image( $url ) {
		add_filter(
			'rx_theme_web_vitals_settings',
			function ( $settings ) use ( $url ) {
				$settings['preload_images'][] = esc_url_raw( $url );
				return $settings;
			}
		);
	}
}

/**
 * Helper: preload script.
 */
if ( ! function_exists( 'rx_theme_preload_script' ) ) {
	function rx_theme_preload_script( $url ) {
		add_filter(
			'rx_theme_web_vitals_settings',
			function ( $settings ) use ( $url ) {
				$settings['preload_scripts'][] = esc_url_raw( $url );
				return $settings;
			}
		);
	}
}

/**
 * Helper: preload style.
 */
if ( ! function_exists( 'rx_theme_preload_style' ) ) {
	function rx_theme_preload_style( $url ) {
		add_filter(
			'rx_theme_web_vitals_settings',
			function ( $settings ) use ( $url ) {
				$settings['preload_styles'][] = esc_url_raw( $url );
				return $settings;
			}
		);
	}
}

/**
 * Helper: get RX Web Vitals summary.
 */
if ( ! function_exists( 'rx_theme_get_web_vitals_summary' ) ) {
	function rx_theme_get_web_vitals_summary( $days = 7 ) {
		if ( ! class_exists( 'RX_Theme_Web_Vitals' ) ) {
			return array();
		}

		return RX_Theme_Web_Vitals::get_metric_summary( $days );
	}
}

For your functions.php, add this line only once:

require_once get_template_directory() . '/inc/performance/web-vitals.php';

Optional custom settings example:

add_filter( 'rx_theme_web_vitals_settings', function ( $settings ) {
	$settings['debug']      = false;
	$settings['sample_rate'] = 1.0;

	$settings['preconnect_urls'][] = 'https://static-assets-cdnrx.rxharun.com';
	$settings['dns_prefetch_urls'][] = '//static-assets-cdnrx.rxharun.com';

	$settings['preload_images'][] = 'https://rxharun.com/wp-content/uploads/hero.webp';

	return $settings;
} );

Important: after adding this file, clear all cache from WordPress cache plugin, Cloudflare/CDN, browser cache, then test in Chrome DevTools, PageSpeed Insights, and your database table: wp_rx_web_vitals.

Leave a Reply

Your email address will not be published. Required fields are marked *