JezK
Edit File: Conversion_Tracking.php
<?php /** * Class Google\Site_Kit\Core\Conversion_Tracking * * @package Google\Site_Kit\Core\Modules * @copyright 2024 Google LLC * @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 * @link https://sitekit.withgoogle.com * * phpcs:disable PHPCS.Commenting.RequireDocTagDescription -- Pre-existing violations; tracked for follow-up cleanup. */ namespace Google\Site_Kit\Core\Conversion_Tracking; use Google\Site_Kit\Context; use Google\Site_Kit\Core\Assets\Script; use Google\Site_Kit\Core\Conversion_Tracking\Conversion_Event_Providers\Contact_Form_7; use Google\Site_Kit\Core\Conversion_Tracking\Conversion_Event_Providers\Content_Events; use Google\Site_Kit\Core\Conversion_Tracking\Conversion_Event_Providers\Easy_Digital_Downloads; use Google\Site_Kit\Core\Conversion_Tracking\Conversion_Event_Providers\Mailchimp; use Google\Site_Kit\Core\Conversion_Tracking\Conversion_Event_Providers\Ninja_Forms; use Google\Site_Kit\Core\Conversion_Tracking\Conversion_Event_Providers\OptinMonster; use Google\Site_Kit\Core\Conversion_Tracking\Conversion_Event_Providers\PopupMaker; use Google\Site_Kit\Core\Conversion_Tracking\Conversion_Event_Providers\WooCommerce; use Google\Site_Kit\Core\Conversion_Tracking\Conversion_Event_Providers\WPForms; use Google\Site_Kit\Core\Storage\Options; use Google\Site_Kit\Core\Tags\GTag; use Google\Site_Kit\Core\Tracking\Feature_Metrics_Trait; use Google\Site_Kit\Core\Tracking\Provides_Feature_Metrics; use Google\Site_Kit\Core\Util\Feature_Flags; use Google\Site_Kit\Core\Util\Method_Proxy_Trait; use LogicException; /** * Class for managing conversion tracking. * * @since 1.126.0 * @access private * @ignore */ class Conversion_Tracking implements Provides_Feature_Metrics { use Feature_Metrics_Trait; use Method_Proxy_Trait; /** * Context object. * * @var Context */ private $context; /** * Conversion_Tracking_Settings instance. * * @since 1.127.0 * @var Conversion_Tracking_Settings */ protected $conversion_tracking_settings; /** * REST_Conversion_Tracking_Controller instance. * * @since 1.127.0 * @var REST_Conversion_Tracking_Controller */ protected $rest_conversion_tracking_controller; /** * Supported conversion event providers. * * @since 1.126.0 * @since 1.130.0 Added Ninja Forms class. * @since 1.186.0 Added Content_Events class. * @var array */ public static $providers = array( Contact_Form_7::CONVERSION_EVENT_PROVIDER_SLUG => Contact_Form_7::class, Content_Events::CONVERSION_EVENT_PROVIDER_SLUG => Content_Events::class, Easy_Digital_Downloads::CONVERSION_EVENT_PROVIDER_SLUG => Easy_Digital_Downloads::class, Mailchimp::CONVERSION_EVENT_PROVIDER_SLUG => Mailchimp::class, Ninja_Forms::CONVERSION_EVENT_PROVIDER_SLUG => Ninja_Forms::class, OptinMonster::CONVERSION_EVENT_PROVIDER_SLUG => OptinMonster::class, PopupMaker::CONVERSION_EVENT_PROVIDER_SLUG => PopupMaker::class, WooCommerce::CONVERSION_EVENT_PROVIDER_SLUG => WooCommerce::class, WPForms::CONVERSION_EVENT_PROVIDER_SLUG => WPForms::class, ); /** * Constructor. * * @since 1.126.0 * * @param Context $context Plugin context. * @param Options $options Optional. Option API instance. Default is a new instance. */ public function __construct( Context $context, ?Options $options = null ) { $this->context = $context; $options = $options ?: new Options( $context ); $this->conversion_tracking_settings = new Conversion_Tracking_Settings( $options ); $this->rest_conversion_tracking_controller = new REST_Conversion_Tracking_Controller( $this->conversion_tracking_settings ); } /** * Registers the class functionality. * * @since 1.126.0 */ public function register() { $this->conversion_tracking_settings->register(); $this->rest_conversion_tracking_controller->register(); $this->register_feature_metrics(); add_filter( 'googlesitekit_inline_base_data', $this->get_method_proxy( 'inline_js_base_data' ) ); if ( ! $this->conversion_tracking_settings->is_conversion_tracking_enabled() ) { return; } add_action( 'wp_enqueue_scripts', fn () => $this->maybe_enqueue_scripts(), 30 ); $active_providers = $this->get_active_providers(); array_walk( $active_providers, function ( Conversion_Events_Provider $active_provider ) { $active_provider->register_hooks(); } ); } /** * Enqueues conversion tracking scripts if conditions are satisfied. */ protected function maybe_enqueue_scripts() { if ( // Do nothing if neither Ads nor Analytics *web* snippet has been inserted. ! ( did_action( 'googlesitekit_ads_init_tag' ) || did_action( 'googlesitekit_analytics-4_init_tag' ) ) ) { return; } $active_providers = $this->get_active_providers(); array_walk( $active_providers, function ( Conversion_Events_Provider $active_provider ) { $script_asset = $active_provider->register_script(); if ( $script_asset instanceof Script ) { $script_asset->enqueue(); } } ); $gtag_event = ' window._googlesitekit = window._googlesitekit || {}; window._googlesitekit.throttledEvents = []; window._googlesitekit.gtagEvent = (name, data) => { var key = JSON.stringify( { name, data } ); if ( !! window._googlesitekit.throttledEvents[ key ] ) { return; } window._googlesitekit.throttledEvents[ key ] = true; setTimeout( () => { delete window._googlesitekit.throttledEvents[ key ]; }, 5 ); gtag( "event", name, { ...data, event_source: "site-kit" } ); }; '; if ( function_exists( 'edd_get_currency' ) ) { $gtag_event .= "window._googlesitekit.easyDigitalDownloadsCurrency = '" . edd_get_currency() . "';"; } if ( Feature_Flags::enabled( 'gtagUserData' ) ) { $gtag_event .= 'window._googlesitekit.gtagUserData = true;'; } wp_add_inline_script( GTag::HANDLE, preg_replace( '/\s+/', ' ', $gtag_event ) ); } /** * Adds the active event provider flags and slugs to the inline base data. * * @since 1.181.0 * @since 1.187.0 Added the slugs of the active conversion event providers. * * @param array $data Inline base data. * @return array Filtered $data. */ protected function inline_js_base_data( $data ) { $active_categories = $this->get_active_provider_categories(); $active_providers = $this->get_active_providers(); $data['hasActiveLeadEventProviders'] = in_array( Conversion_Events_Provider::CATEGORY_LEAD, $active_categories, true ); $data['hasActiveEcommerceEventProviders'] = in_array( Conversion_Events_Provider::CATEGORY_ECOMMERCE, $active_categories, true ); $active_ecommerce_providers = count( array_filter( $active_providers, fn( $provider ) => Conversion_Events_Provider::CATEGORY_ECOMMERCE === $provider->get_category() ) ); $data['hasMultipleActiveEcommerceEventProviders'] = $active_ecommerce_providers > 1; $data['activeConversionEventProviders'] = array_keys( $active_providers ); return $data; } /** * Gets the instances of active conversion event providers. * * @since 1.126.0 * * @return array List of active Conversion_Events_Provider instances. * @throws LogicException Thrown if an invalid conversion event provider class name is provided. */ public function get_active_providers() { $active_providers = array(); foreach ( self::$providers as $provider_slug => $provider_class ) { if ( ! is_string( $provider_class ) || ! $provider_class ) { throw new LogicException( sprintf( 'A conversion event provider class name is required to instantiate a provider: %s', esc_html( $provider_slug ) ) ); } if ( ! class_exists( $provider_class ) ) { throw new LogicException( sprintf( "The '%s' class does not exist", esc_html( $provider_class ) ) ); } if ( ! is_subclass_of( $provider_class, Conversion_Events_Provider::class ) ) { throw new LogicException( sprintf( "The '%1\$s' class must extend the base conversion event provider class: %2\$s", esc_html( $provider_class ), Conversion_Events_Provider::class ) ); } $instance = new $provider_class( $this->context ); if ( $instance->is_active() ) { $active_providers[ $provider_slug ] = $instance; } } return $active_providers; } /** * Gets the unique categories of active conversion event providers. * * @since 1.182.0 * * @return array List of unique active provider category strings, constrained to known categories. */ public function get_active_provider_categories() { $categories = array_map( fn( Conversion_Events_Provider $provider ) => $provider->get_category(), $this->get_active_providers() ); return array_values( array_unique( array_intersect( $categories, array( Conversion_Events_Provider::CATEGORY_LEAD, Conversion_Events_Provider::CATEGORY_ECOMMERCE ) ) ) ); } /** * Returns events supported by active providers from the conversion tracking infrastructure. * * @since 1.163.0 Moved this method here from the Ads class. * * @return array Array of supported conversion events, or empty array. */ public function get_supported_conversion_events() { $providers = $this->get_active_providers(); if ( empty( $providers ) ) { return array(); } $events = array(); foreach ( $providers as $provider ) { $events = array_merge( $events, array_values( $provider->get_event_names() ) ); } return array_unique( $events ); } /** * Returns enhanced conversion events supported by active providers from the conversion tracking infrastructure. * * @since 1.165.0 * * @return array Array of supported enhanced conversion events, or empty array. */ public function get_enhanced_conversion_events() { $providers = $this->get_active_providers(); if ( empty( $providers ) ) { return array(); } $events = array(); foreach ( $providers as $provider ) { $supported_enhanced_events = array_intersect( $provider->get_enhanced_event_names(), $provider->get_event_names() ); $events = array_merge( $events, array_values( $supported_enhanced_events ) ); } return array_unique( $events ); } /** * Returns conversion events directly tracked by Site Kit's Plugin Conversion Reporting feature. * * Unlike get_supported_conversion_events(), this excludes events that are delegated to a * third-party add-on (e.g. Google Analytics for WooCommerce), so it accurately reflects * only what Site Kit itself tracks. * * @since 1.182.0 * * @return array Array of Site Kit-tracked conversion events, or empty array. */ public function get_site_kit_supported_conversion_events() { $providers = $this->get_active_providers(); if ( empty( $providers ) ) { return array(); } $events = array(); foreach ( $providers as $provider ) { $events = array_merge( $events, array_values( $provider->get_site_kit_event_names() ) ); } return array_unique( $events ); } /** * Returns enhanced conversion events directly tracked by Site Kit's Plugin Conversion Reporting feature. * * Unlike get_enhanced_conversion_events(), this excludes events that are delegated to a * third-party add-on (e.g. Google Analytics for WooCommerce), so it accurately reflects * only what Site Kit itself tracks. * * @since 1.182.0 * * @return array Array of Site Kit-tracked enhanced conversion events, or empty array. */ public function get_site_kit_enhanced_conversion_events() { $providers = $this->get_active_providers(); if ( empty( $providers ) ) { return array(); } $events = array(); foreach ( $providers as $provider ) { $supported_enhanced_events = array_intersect( $provider->get_enhanced_event_names(), $provider->get_site_kit_event_names() ); $events = array_merge( $events, array_values( $supported_enhanced_events ) ); } return array_unique( $events ); } /** * Gets an array of internal feature metrics. * * @since 1.163.0 * * @return array */ public function get_feature_metrics() { return array( 'conversion_tracking_enabled' => $this->conversion_tracking_settings->is_conversion_tracking_enabled(), 'conversion_tracking_providers' => array_keys( $this->get_active_providers() ), 'conversion_tracking_events' => $this->get_site_kit_supported_conversion_events(), 'conversion_tracking_events_enh' => $this->get_site_kit_enhanced_conversion_events(), ); } }