HEX
Server: nginx
System: Linux pool195-106-36.bur.atomicsites.net 6.12.57+deb12-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.12.57-1~bpo12+1 (2025-11-17) x86_64
User: (0)
PHP: 8.3.32
Disabled: pcntl_fork
Upload Files
File: /wordpress/plugins/wp-cloud-client/1.0.0/src/Analytics/GoogleAnalytics4.php
<?php

declare(strict_types=1);

namespace VPlugins\WPCloudClient\Analytics;

use VPlugins\WPCloudClient\Settings\SettingsRepository;

/**
 * Injects Google Analytics 4 tracking script into the site's <head>.
 *
 * Supports two tracking IDs:
 *   1. A platform-connected ID supplied via the GA4_TRACKING_ID server variable.
 *   2. A user-supplied custom ID saved in the plugin settings.
 *
 * The production default ID (G-3KT4C16M4V) is reserved and cannot be set
 * by the user as a custom ID.
 */
class GoogleAnalytics4 {

	private const TRACKING_ID_PROD = 'G-3KT4C16M4V';

	private string $connectedTrackingId;

	/**
	 * Constructor.
	 *
	 * @param SettingsRepository $settings Plugin settings repository.
	 */
	public function __construct(
		private readonly SettingsRepository $settings,
	) {
		$this->connectedTrackingId = isset( $_SERVER['GA4_TRACKING_ID'] )
			? sanitize_text_field( (string) $_SERVER['GA4_TRACKING_ID'] )
			: '';
	}

	/**
	 * Register the analytics script hook.
	 *
	 * @return void
	 */
	public function register(): void {
		add_action( 'wp_head', [ $this, 'injectScript' ] );
	}

	/**
	 * Sanitizes and validates a user-submitted GA4 tracking ID.
	 *
	 * Called via register_setting sanitize_callback.
	 *
	 * @param string $input The raw tracking ID input.
	 * @return string The sanitized tracking ID.
	 */
	public function sanitizeTrackingId( string $input ): string {
		$input = strtoupper( trim( $input ) );

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

		if ( self::TRACKING_ID_PROD === $input ) {
			add_settings_error(
				'wpcc_settings',
				'wpcc_ga4_reserved',
				sprintf( '%s %s', $input, __( 'is already in use', 'wp-cloud-client' ) ),
				'error',
			);
			return $this->settings->getGa4TrackingId() ?? '';
		}

		if ( ! preg_match( '/^G-[A-Z0-9]{10}$/', $input ) ) {
			add_settings_error(
				'wpcc_settings',
				'wpcc_ga4_invalid',
				sprintf( '%s: %s', __( 'Invalid Google Analytics 4 Tracking ID', 'wp-cloud-client' ), $input ),
				'error',
			);
			return $this->settings->getGa4TrackingId() ?? '';
		}

		return $input;
	}

	/**
	 * Outputs the gtag script tags into wp_head.
	 */
	public function injectScript(): void {
		$trackingIds = $this->getTrackingIds();

		if ( empty( $trackingIds ) ) {
			return;
		}

		$siteId  = (string) ( $this->settings->getSiteId() ?? '' );
		$idsJson = wp_json_encode( $trackingIds );
		?>
		<script async src="https://www.googletagmanager.com/gtag/js?id=<?php echo esc_attr( $trackingIds[0] ); ?>"></script>
		<script>
			window.dataLayer = window.dataLayer || [];
			function gtag() { dataLayer.push(arguments); }
			var tracking_ids = <?php echo wp_json_encode( $trackingIds ); ?>;
			var site_id = '<?php echo esc_js( $siteId ); ?>';
			gtag('js', new Date());
			for (var i = 0; i < tracking_ids.length; i++) {
				gtag('config', tracking_ids[i], { 'dimension1': site_id });
				gtag('event', 'page_view',        { 'send_to': tracking_ids[i], 'dimension1': site_id });
				gtag('event', 'first_visit',      { 'send_to': tracking_ids[i], 'dimension1': site_id });
				gtag('event', 'engaged_sessions', { 'send_to': tracking_ids[i], 'dimension1': site_id });
				gtag('set', { 'siteSpeedSampleRate': 50 });
			}
		</script>
		<?php
	}

	/**
	 * Returns the active tracking ID list.
	 * Index 0 is always the platform/default ID; index 1 (if present) is the user-supplied custom ID.
	 */
	private function getTrackingIds(): array {
		$primary = '' !== $this->connectedTrackingId ? $this->connectedTrackingId : self::TRACKING_ID_PROD;
		$ids     = [ $primary ];

		$userTracking = $this->settings->getGa4TrackingId();
		if ( null !== $userTracking && $userTracking !== $primary ) {
			$ids[] = $userTracking;
		}

		return $ids;
	}
}