PluginProbe
Analytify – Google Analytics Dashboard For WordPress (GA4 analytics tracking) / trunk
Analytify – Google Analytics Dashboard For WordPress (GA4 analytics tracking) vtrunk
9.1.2 9.1.1 9.1.0 9.0.2 9.0.1 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.1.0 1.1.1 1.2.0 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.3.0 1.3.1 1.3.2 All 153 releases
wp-analytify / classes / analytify-mp-ga4.php

analytify-mp-ga4.php in Analytify – Google Analytics Dashboard For WordPress (GA4 analytics tracking) trunk, at classes/analytify-mp-ga4.php

307 lines 8.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName -- Historical filename matches class context.
2
3 // phpcs:disable Universal.Files.SeparateFunctionsFromOO.Mixed -- Function is part of the class API (global wrapper + class in one module file).
4 /**
5 * This file contains the class that makes the gtag api calls.
6 *
7 * @package WP_Analytify
8 * @since 1.0.0
9 */
10
11 if ( ! defined( 'ABSPATH' ) ) {
12 exit; // Exit if accessed directly.
13 }
14
15 if ( ! function_exists( 'analytify_mp_ga4_build_request_body' ) ) {
16 /**
17 * Build GA4 Measurement Protocol request body (client_id + events) after debug + filter hooks.
18 *
19 * @since 9.0.0
20 * @param string $client_id Client ID for the MP payload.
21 * @param array<int, mixed> $events Events list (GA4 name / params shape).
22 * @return array<string, mixed> Array with keys client_id and events.
23 */
24 function analytify_mp_ga4_build_request_body( $client_id, $events ) {
25 $events = is_array( $events ) ? $events : array();
26 $debug_mode = apply_filters( 'analytify_debug_mode', false );
27 if ( $debug_mode ) {
28 foreach ( $events as $index => $event ) {
29 if ( ! is_array( $event ) ) {
30 continue;
31 }
32 if ( ! isset( $event['params'] ) || ! is_array( $event['params'] ) ) {
33 $events[ $index ]['params'] = array();
34 }
35 $events[ $index ]['params']['debug_mode'] = 1;
36 }
37 }
38 $events = apply_filters( 'analytify_ga4_events_for_mp_api_call', $events );
39 return array(
40 'client_id' => (string) $client_id,
41 'events' => is_array( $events ) ? $events : array(),
42 );
43 }
44 }
45
46 if ( ! class_exists( 'Analytify_MP_GA4' ) ) {
47 /**
48 * Class that makes the gtag api calls.
49 *
50 * Use for server-side calls.
51 *
52 * @package WP_Analytify
53 * @since 1.0.0
54 */
55 class Analytify_MP_GA4 {
56
57 /**
58 * The Google Analytics API URL.
59 */
60 const GOOGLE_ANALYTICS_API_URL = 'https://www.google-analytics.com/mp/collect';
61
62 /**
63 * The single instance of the class.
64 *
65 * @var self|null
66 */
67 private static $instance = null;
68
69 /**
70 * Client ID
71 *
72 * @var string
73 */
74 private $client_id = null;
75
76 /**
77 * Measurement ID
78 *
79 * @var string
80 */
81 private $measurement_id = null;
82
83 /**
84 * API secret
85 *
86 * @var string
87 */
88 private $api_secret = null;
89
90 /**
91 * Analytify global object.
92 *
93 * @var mixed
94 */
95 private $wp_analytify = null;
96
97 /**
98 * Returns the single instance of the class.
99 *
100 * @return self Class instance
101 */
102 public static function get_instance(): self {
103 if ( empty( self::$instance ) ) {
104 self::$instance = new self();
105 }
106
107 return self::$instance;
108 }
109
110 /**
111 * Class constructor.
112 *
113 * @version 8.1.2
114 */
115 private function __construct() {
116 $this->wp_analytify = $GLOBALS['WP_ANALYTIFY'];
117
118 $raw_secret = $this->wp_analytify->settings->get_option( 'measurement_protocol_secret', 'wp-analytify-advanced', false );
119 // GA4 API returns { secretValue, name, ... }; ensure we store/use only the string (back-compat: if saved as array, use secretValue).
120 $this->api_secret = is_array( $raw_secret ) && isset( $raw_secret['secretValue'] ) && is_string( $raw_secret['secretValue'] )
121 ? trim( sanitize_text_field( $raw_secret['secretValue'] ) )
122 : ( is_string( $raw_secret ) ? trim( sanitize_text_field( $raw_secret ) ) : '' );
123 $this->measurement_id = WP_ANALYTIFY_FUNCTIONS::get_UA_code();
124 $this->client_id = $this->get_client_id();
125 }
126
127 /**
128 * Get client ID.
129 *
130 * @return string
131 */
132 private function get_client_id(): string {
133
134 $client_id = '';
135
136 if ( isset( $_COOKIE['_ga'] ) ) {
137
138 $client_id_raw = wp_unslash( $_COOKIE['_ga'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Cookie value is sanitized with wp_unslash
139 $parts = explode( '.', $client_id_raw );
140 $client_id = "{$parts[2]}.{$parts[3]}";
141
142 } else {
143
144 $client_id = ( 'on' === $this->wp_analytify->settings->get_option( 'user_advanced_keys', 'wp-analytify-advanced' ) ) ? $this->wp_analytify->settings->get_option( 'client_id', 'wp-analytify-advanced' ) : WP_ANALYTIFY_CLIENTID;
145
146 }
147
148 return $client_id;
149 }
150
151 /**
152 * Returns the Google Analytics API URL.
153 *
154 * @return string
155 */
156 private function api_url() {
157 $url = add_query_arg(
158 array(
159 'measurement_id' => $this->measurement_id,
160 'api_secret' => $this->api_secret,
161 ),
162 self::GOOGLE_ANALYTICS_API_URL
163 );
164 return esc_url_raw( $url );
165 }
166
167 /**
168 * Send data to Google Analytics.
169 *
170 * @param array<string, mixed> $events Data to send.
171 * @return bool
172 */
173 public function send_hit( $events ): bool {
174
175 if ( '' === (string) $this->measurement_id || '' === (string) $this->api_secret ) {
176 $logger = function_exists( 'analytify_get_logger' ) ? analytify_get_logger() : null;
177 if ( $logger && method_exists( $logger, 'warning' ) ) {
178 $logger->warning(
179 'Measurement Protocol send skipped: missing measurement ID or API secret.',
180 array(
181 'source' => 'send_hit',
182 'measurement_id' => $this->measurement_id,
183 'has_api_secret' => '' !== (string) $this->api_secret,
184 )
185 );
186 }
187 return false;
188 }
189
190 $url = $this->api_url();
191
192 $body = analytify_mp_ga4_build_request_body( $this->client_id, $events );
193 $json_body = wp_json_encode( $body );
194 $response = wp_remote_post(
195 $url,
196 array(
197 'timeout' => 5,
198 'body' => $json_body ? $json_body : '',
199 )
200 );
201 if ( is_wp_error( $response ) ) {
202 $logger = function_exists( 'analytify_get_logger' ) ? analytify_get_logger() : null;
203 if ( $logger && method_exists( $logger, 'error' ) ) {
204 $logger->error(
205 'Failed to send hit via Measurement Protocol.',
206 array(
207 'source' => 'send_hit',
208 'error' => $response->get_error_message(),
209 'measurement_id' => $this->measurement_id,
210 )
211 );
212 }
213 return false;
214 }
215
216 return true;
217 }
218 }
219 }
220
221 /**
222 * Uses the singleton pattern to call the api.
223 *
224 * @param array<string, mixed> $events Parameters to send to the API.
225 * @return bool
226 */
227 function analytify_mp_ga4( $events ): bool {
228 $instance = Analytify_MP_GA4::get_instance();
229 return $instance->send_hit( $events );
230 }
231
232 add_filter(
233 'analytify_ga4_events_for_mp_api_call',
234 static function ( $events ) {
235 if ( ! isset( $GLOBALS['WP_ANALYTIFY'] ) || ! method_exists( $GLOBALS['WP_ANALYTIFY'], 'analytify_ensure_tracking_dimensions' ) ) {
236 return $events;
237 }
238
239 $names = array();
240 foreach ( (array) $events as $event ) {
241 if ( empty( $event['params'] ) || ! is_array( $event['params'] ) ) {
242 continue;
243 }
244 foreach ( array_keys( $event['params'] ) as $key ) {
245 if ( is_string( $key ) && 0 === strpos( $key, 'wpa_' ) ) {
246 $names[] = $key;
247 }
248 }
249 }
250
251 if ( ! empty( $names ) ) {
252 $GLOBALS['WP_ANALYTIFY']->analytify_ensure_tracking_dimensions( $names );
253 }
254
255 return $events;
256 },
257 5
258 );
259
260 add_filter(
261 'analytify_gtag_configuration',
262 static function ( $configuration ) {
263 if ( ! is_array( $configuration ) || ! isset( $GLOBALS['WP_ANALYTIFY'] ) || ! method_exists( $GLOBALS['WP_ANALYTIFY'], 'analytify_ensure_tracking_dimensions' ) ) {
264 return $configuration;
265 }
266
267 $names = array();
268 foreach ( array_keys( $configuration ) as $key ) {
269 if ( is_string( $key ) && 0 === strpos( $key, 'wpa_' ) ) {
270 $names[] = $key;
271 }
272 }
273
274 if ( ! empty( $names ) ) {
275 $GLOBALS['WP_ANALYTIFY']->analytify_ensure_tracking_dimensions( $names );
276 }
277
278 return $configuration;
279 },
280 99
281 );
282
283 add_action(
284 'wp_enqueue_scripts',
285 static function () {
286 static $scheduled = false;
287
288 if ( $scheduled || ! isset( $GLOBALS['WP_ANALYTIFY'] ) || ! method_exists( $GLOBALS['WP_ANALYTIFY'], 'analytify_ensure_tracking_dimensions_for_enqueued_scripts' ) ) {
289 return;
290 }
291
292 $scheduled = true;
293 add_action(
294 'shutdown',
295 static function () {
296 if ( isset( $GLOBALS['WP_ANALYTIFY'] ) && method_exists( $GLOBALS['WP_ANALYTIFY'], 'analytify_ensure_tracking_dimensions_for_enqueued_scripts' ) ) {
297 $GLOBALS['WP_ANALYTIFY']->analytify_ensure_tracking_dimensions_for_enqueued_scripts();
298 }
299 },
300 99
301 );
302 },
303 999
304 );
305
306 // phpcs:enable Universal.Files.SeparateFunctionsFromOO.Mixed
307