PluginProbe ʕ •ᴥ•ʔ
WP STAGING – WordPress Backup, Restore, Migration & Clone / 4.9.2
WP STAGING – WordPress Backup, Restore, Migration & Clone v4.9.2
4.9.2 4.9.1 4.9.0 4.8.1 trunk 3.0.0 3.0.1 3.0.2 3.0.3 3.0.4 3.0.5 3.0.6 3.1.0 3.1.1 3.1.2 3.1.3 3.1.4 3.10.0 3.2.0 3.3.1 3.3.2 3.3.3 3.4.1 3.4.3 3.5.0 3.6.0 3.7.1 3.8.0 3.8.1 3.8.2 3.8.3 3.8.4 3.8.5 3.8.6 3.8.7 3.9.0 3.9.1 3.9.2 3.9.3 3.9.4 4.0.0 4.1.0 4.1.1 4.1.2 4.1.3 4.1.4 4.2.0 4.2.1 4.3.0 4.3.1 4.3.2 4.4.0 4.5.0 4.6.0 4.7.0 4.7.1 4.7.2 4.7.3 4.8.0
wp-staging / Framework / Analytics / AnalyticsSender.php
wp-staging / Framework / Analytics Last commit date
Actions 2 weeks ago AnalyticsCleanup.php 4 years ago AnalyticsConsent.php 1 week ago AnalyticsEventDto.php 2 weeks ago AnalyticsEventWithTimeDto.php 9 months ago AnalyticsGenericEventHandler.php 2 months ago AnalyticsSender.php 9 months ago WithAnalyticsAPI.php 9 months ago WithAnalyticsSiteInfo.php 4 months ago
AnalyticsSender.php
194 lines
1 <?php
2
3 namespace WPStaging\Framework\Analytics;
4
5 class AnalyticsSender
6 {
7 use WithAnalyticsAPI;
8
9 protected $consent;
10
11 private $corruptSettingsNotice;
12
13 public function __construct(AnalyticsConsent $consent)
14 {
15 $this->consent = $consent;
16 }
17
18 public function maybeSend()
19 {
20 // Early bail: Do not run on AJAX requests.
21 if (defined('DOING_AJAX') && DOING_AJAX) {
22 return;
23 }
24
25 // We store the analytics sending time at "wpstg_settings"
26 // since this runs on every request, and it's an autoloaded option.
27 // Also, the value we need to store in it is small.
28 $settings = get_option("wpstg_settings", []);
29
30 // convert settings from type object to array
31 if (is_object($settings)) {
32 $settings = json_decode(json_encode($settings), true);
33 }
34
35 // If still $settings is not array, bail
36 if (!is_array($settings)) {
37 return;
38 }
39
40 // Interval to wait before sending events.
41 $interval = ($this->isDev() ? 1 : 15) * MINUTE_IN_SECONDS;
42
43 // Early bail: Sent not so long ago.
44 if (isset($settings['lastAnalyticsSend']) && time() - $settings['lastAnalyticsSend'] - $interval < 0) {
45 return;
46 }
47
48 $settings['lastAnalyticsSend'] = time();
49
50 if (!update_option('wpstg_settings', $settings)) {
51 \WPStaging\functions\debug_log('WP STAGING: Could not update Analytics last sent time.', 'debug');
52 };
53
54 $this->sendAnalytics();
55 }
56
57 public function sendAnalytics()
58 {
59 global $wpdb;
60
61 $eventOptions = $wpdb->get_results("SELECT * FROM $wpdb->options WHERE `option_name` LIKE 'wpstg_analytics_event_%' LIMIT 0, 20");
62
63 // Nothing to send.
64 if (empty($eventOptions)) {
65 return;
66 }
67
68 // Early bail: User has not given consent to send analytics
69 if (!$this->consent->hasUserConsent()) {
70 return;
71 }
72
73 // Format the events to the expected format
74 $events = array_map(function ($eventOption) {
75 return json_decode($eventOption->option_value);
76 }, $eventOptions);
77
78 $this->setStaleEvents($events);
79
80 // Filter the events to send only those that are ready to be sent
81 $events = array_filter($events, function ($event) {
82 return $event->ready_to_send;
83 });
84
85 // Early bail: No events ready to be sent
86 if (empty($events)) {
87 return;
88 }
89
90 // Convert true to 1 and false to 0 for MySQL TinyInt
91 foreach ($events as &$event) {
92 foreach ($event as $property => &$value) {
93 if (is_bool($value)) {
94 $event->$property = (int)$value;
95 } elseif ($property === 'site_info') {
96 $siteInfo = &$value;
97
98 foreach ($siteInfo as $siteInfoProperty => &$siteInfoValue) {
99 if (is_bool($siteInfoValue)) {
100 $siteInfo->$siteInfoProperty = (int)$siteInfoValue;
101 }
102 }
103 }
104 }
105 }
106
107 // Delete the events, regardless of whether it succeeded or failed to send.
108 // This prevents events from hanging and being sent every time if they are in an invalid format or something goes wrong.
109 $idsToDelete = implode(',', array_map(function ($eventOption) {
110 return $eventOption->option_id;
111 }, $eventOptions));
112
113 if (!$wpdb->query("DELETE FROM $wpdb->options WHERE `option_id` IN ($idsToDelete)")) {
114 \WPStaging\functions\debug_log('WP STAGING Analytics Delete Sent Events Error: ' . $wpdb->last_error);
115 }
116
117 $body = wp_json_encode([
118 'events' => $events,
119 'site_hash' => $this->getSiteHash(),
120 ]);
121
122 $url = $this->getApiUrl('events');
123
124 // Early bail: Do not dispatch events when in dev mode, unless allowed.
125 if ($this->isDev() && !$this->canDevSendAnalytics()) {
126 return;
127 }
128
129 $response = wp_remote_post($url, [
130 'method' => 'POST',
131 'headers' => ['Content-Type' => 'application/json; charset=utf-8'],
132 'body' => $body,
133 'data_format' => 'body',
134 'timeout' => 10,
135 'redirection' => 5,
136 'httpversion' => '1.0',
137 'blocking' => true,
138 'sslverify' => false,
139 ]);
140
141 if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) {
142 if (wp_remote_retrieve_response_code($response) == 412) {
143 // The site hash does not exist in the Analytics database. We need to ask for consent again.
144 if ($this->consent->hasUserConsent()) {
145 try {
146 $this->consent->giveConsent();
147 } catch (\Exception $e) {
148 // We could not re-validate the consent.
149 // Don't do anything here.
150 // E.g. If user gives his consent but wp-staging.com is not reachable
151 // we should save the setting and fail silently. Next time wp-staging.com is available again we automatically can send usage information.
152 // No need then to ask again for consent.
153 }
154 }
155 }
156
157 $errorMessage = is_wp_error($response) ? $response->get_error_message() : wp_remote_retrieve_body($response);
158 \WPStaging\functions\debug_log('WP STAGING Analytics Send Error: ' . $errorMessage);
159 }
160 }
161
162 /**
163 * Mark as stale the events that started longer than 1 day ago but isn't ready to send yet
164 *
165 * @param $events
166 */
167 protected function setStaleEvents(&$events)
168 {
169 foreach ($events as &$event) {
170 // For newer event dtos i.e. remote sync
171 if (!$event->ready_to_send && isset($event->start_at) && $event->start_at < time() - 1 * DAY_IN_SECONDS) {
172 $event->ready_to_send = true;
173 $event->stale_at = time();
174 continue;
175 }
176
177 if (!$event->ready_to_send && $event->start_time < time() - 1 * DAY_IN_SECONDS) {
178 $event->ready_to_send = true;
179 $event->is_stale = true;
180 }
181 }
182 }
183
184 protected function isDev(): bool
185 {
186 return defined('WPSTG_IS_DEV') && WPSTG_IS_DEV;
187 }
188
189 protected function canDevSendAnalytics(): bool
190 {
191 return defined('WPSTG_DEV_SEND_ANALYTICS') && WPSTG_DEV_SEND_ANALYTICS;
192 }
193 }
194