PluginProbe ʕ •ᴥ•ʔ
Microsoft Clarity / 0.10.29
Microsoft Clarity v0.10.29
0.10.29 0.10.28 0.10.27 0.10.26 0.10.25 0.10.24 0.8.0 0.9.0 0.9.1 0.9.2 0.9.3 0.9.4 trunk 0.10.0 0.10.1 0.10.10 0.10.11 0.10.12 0.10.13 0.10.14 0.10.15 0.10.16 0.10.17 0.10.18 0.10.19 0.10.2 0.10.20 0.10.21 0.10.22 0.10.23 0.10.3 0.10.4 0.10.5 0.10.6 0.10.7 0.10.8 0.10.9 0.2 0.4 0.5 0.6 0.6.1 0.7 0.7.1 0.7.2 0.7.3 0.7.4 0.7.5
microsoft-clarity / clarity-server-analytics.php
microsoft-clarity Last commit date
includes 16 hours ago js 1 week ago LICENSE.txt 5 years ago clarity-collect-batch.php 6 months ago clarity-collect-storage.php 6 months ago clarity-hooks.php 6 months ago clarity-page.php 1 week ago clarity-server-analytics.php 2 months ago clarity.php 16 hours ago index.php 5 years ago readme.txt 16 hours ago
clarity-server-analytics.php
154 lines
1 <?php
2
3 //
4 // Configurations & Constants
5 //
6
7 const CLARITY_COLLECT_ENDPOINT = 'https://ai.clarity.ms/collect';
8
9 require_once plugin_dir_path(__FILE__) . '/clarity-collect-storage.php';
10 require_once plugin_dir_path(__FILE__) . '/clarity-collect-batch.php';
11
12 /**
13 * Collects and sends Clarity events in batches.
14 */
15 function clarity_collect_event()
16 {
17 try {
18 if (is_admin()) {
19 return;
20 }
21
22 if (!isset($_SERVER['REQUEST_METHOD']) || $_SERVER['REQUEST_METHOD'] !== 'GET') {
23 return;
24 }
25
26 $clarity_project_id = get_option('clarity_project_id');
27 $clarity_wp_site = get_option('clarity_wordpress_site_id');
28
29 // Ensure required identifiers are present
30 if (empty($clarity_project_id) || empty($clarity_wp_site)) {
31 return;
32 }
33
34 // Construct and buffer the collect event payload for batch sending
35 $event = clarity_construct_collect_event($clarity_project_id);
36 clarity_insert_collect_event($event);
37 } catch (\Throwable $e) {
38 // Silently fail on any error (incl. fatals)
39 }
40 }
41
42 add_action('shutdown', 'clarity_collect_event');
43
44 //
45 // Helper Functions
46 //
47
48 /**
49 * Constructs the event payload for the collect endpoint.
50 *
51 * @param string $clarity_project_id The Clarity project ID.
52 * @return array The constructed event payload.
53 */
54 function clarity_construct_collect_event($clarity_project_id)
55 {
56 $envelope = array(
57 'projectId' => $clarity_project_id,
58 'sessionId' => clarity_safe_get_session_token(),
59 'version' => get_installed_plugin_version(),
60 );
61
62 $analytics = array(
63 'time' => time(),
64 'ip' => clarity_get_ip_address(),
65 'ua' => isset($_SERVER['HTTP_USER_AGENT']) ? sanitize_text_field($_SERVER['HTTP_USER_AGENT']) : 'Unknown',
66 'url' => home_url($_SERVER['REQUEST_URI']),
67 'method' => isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : 'Unknown',
68 'response_content_type' => clarity_get_response_content_type(),
69 );
70
71 $payload = array(
72 'envelope' => $envelope,
73 'analytics' => $analytics,
74 );
75
76 return $payload;
77 }
78
79 /**
80 * Safely retrieves the WordPress session token without triggering a fatal
81 * error when pluggable functions (like wp_parse_auth_cookies) are not yet loaded.
82 *
83 * @return string The session token, or an empty string when unavailable.
84 */
85 function clarity_safe_get_session_token()
86 {
87 try {
88 return (string) wp_get_session_token();
89 } catch (\Throwable $e) {
90 return '';
91 }
92 }
93
94 /**
95 * Sends a batch of events to the Clarity collect endpoint.
96 *
97 * @param array $events The batch of events to send.
98 */
99 function clarity_send_collect_event_batch($events)
100 {
101 $request = array(
102 'body' => json_encode($events),
103 'headers' => array('Content-Type' => 'application/json'),
104 'blocking' => false,
105 'timeout' => '1',
106 'redirection' => '5',
107 'httpversion' => '1.0'
108 );
109
110 wp_remote_post(CLARITY_COLLECT_ENDPOINT, $request);
111 }
112
113 /**
114 * Retrieves the response content type from headers.
115 *
116 * @return string The sanitized content type, or empty string if not found.
117 */
118 function clarity_get_response_content_type()
119 {
120 $headers = headers_list();
121 $contentType = '';
122
123 foreach ($headers as $header) {
124 if (strncasecmp($header, 'Content-Type:', 13) === 0) {
125 $contentType = trim(substr($header, 13));
126 break;
127 }
128 }
129
130 return $contentType;
131 }
132
133 /**
134 * Retrieves the client's IP address, excluding private and reserved ranges.
135 */
136 function clarity_get_ip_address()
137 {
138 foreach (array('HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'REMOTE_ADDR') as $key) {
139 if (empty($_SERVER[$key])) {
140 continue;
141 }
142
143 foreach (explode(',', $_SERVER[$key]) as $ip) {
144 $ip = trim($ip);
145
146 if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
147 return $ip;
148 }
149 }
150 }
151
152 return 'Unknown';
153 }
154