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 / AnalyticsEventDto.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
AnalyticsEventDto.php
252 lines
1 <?php
2
3 namespace WPStaging\Framework\Analytics;
4
5 use WPStaging\Core\WPStaging;
6 use WPStaging\Framework\Filesystem\DebugLogReader;
7
8 abstract class AnalyticsEventDto implements \JsonSerializable
9 {
10 use WithAnalyticsSiteInfo;
11
12 /** @var string Which action is triggering this Analytics, eg: backup creation, staging push, etc */
13 protected $event;
14
15 /** @var string The Job ID or similar. */
16 protected $job_identifier;
17
18 /** @var string A unique hash that prevents duplicated events in a scenario where this database is restored in another site and the events are sent again. */
19 protected $event_hash;
20
21 /** @var bool Whether this job has naturally finished. Eg: Came to the expected ending. */
22 protected $is_finished = false;
23
24 /** @var bool Whether this job has started but did not finish during an expected time-frame. */
25 protected $is_stale = false;
26
27 /** @var bool Whether this job terminated in error. */
28 protected $is_error = false;
29
30 /** @var bool Whether this job has been cancelled by the user. */
31 protected $is_cancelled = false;
32
33 /** @var bool Whether the requirement check has failed for this job. */
34 protected $is_requirement_check_fail = false;
35
36 /** @var string The reason for the requirement check fail, if so. */
37 protected $requirement_fail_reason = '';
38
39 /** @var string The error message as shown in the front-end, if event terminated in error. */
40 protected $error_message;
41
42 /** @var string The last lines from the debug log file. */
43 protected $last_debug_logs;
44
45 /** @var bool An internal flag to check if this event is ready to be sent. */
46 protected $ready_to_send = false;
47
48 /** @var int A UNIX timestamp for when this event started. */
49 protected $start_time;
50
51 /** @var int A UNIX timestamp for when this event ended. */
52 protected $end_time;
53
54 /** @var int The duration in seconds between the start and end of the event. */
55 protected $duration;
56
57 /** @var array A collection of generic site information. */
58 protected $site_info;
59
60 public function __construct()
61 {
62 $this->event = $this->getEventAction();
63 $this->site_info = $this->getAnalyticsSiteInfo();
64 }
65
66 #[\ReturnTypeWillChange]
67 public function jsonSerialize()
68 {
69 return get_object_vars($this);
70 }
71
72 /**
73 * @return string The name of this analytics event.
74 */
75 abstract public function getEventAction();
76
77 public function enqueueStartEvent($jobId, $eventData)
78 {
79 $this->job_identifier = $jobId;
80 $this->start_time = time();
81 $this->event_hash = microtime(true) . rand();
82
83 try {
84 $this->saveEvent($jobId, $this);
85 } catch (\Exception $e) {
86 \WPStaging\functions\debug_log("WP STAGING: Could not register start event analytics data for job ID $jobId.", 'debug', false);
87 }
88 }
89
90 public function enqueueFinishEvent($jobId, $eventData, $eventOverrides = [])
91 {
92 try {
93 $event = $this->getEventByJobId($jobId);
94 } catch (\Exception $e) {
95 \WPStaging\functions\debug_log("WP STAGING: Could not register finish event analytics data for job ID $jobId", 'debug', false);
96
97 return;
98 }
99
100 $event->is_finished = true;
101 $event->end_time = time();
102 $event->duration = time() - $event->start_time;
103 $event->ready_to_send = true;
104
105 // Allow concrete instances of this abstract class to modify this event.
106 foreach ($eventOverrides as $key => $value) {
107 $event->$key = $value;
108 }
109
110 try {
111 $this->saveEvent($jobId, $event);
112 } catch (\Exception $e) {
113 \WPStaging\functions\debug_log("WP STAGING: Could not save finish event analytics data for job ID $jobId.", 'debug', false);
114 }
115 }
116
117 /**
118 * Cancel event is static as it's a generic event not related to any specific type of event.
119 */
120 public static function enqueueCancelEvent($jobId)
121 {
122 try {
123 $event = static::getEventByJobId($jobId);
124 } catch (\Exception $e) {
125 \WPStaging\functions\debug_log("WP STAGING: Could not register cancel event analytics data for job ID $jobId", 'debug', false);
126
127 return;
128 }
129
130 // Early bail: Already cancelled
131 if ($event->is_cancelled) {
132 return;
133 }
134
135 /*
136 * The Cancel routine may be called automatically when an error occurs
137 * to perform cleanup tasks, so let's not register the cancel event
138 * if this event is being triggered by a job that already has an error.
139 */
140 if ($event->is_error) {
141 return;
142 }
143
144 $event->is_finished = false;
145 $event->is_cancelled = true;
146 $event->end_time = time();
147 $event->duration = time() - $event->start_time;
148 $event->ready_to_send = true;
149
150 try {
151 static::saveEvent($jobId, $event);
152 } catch (\Exception $e) {
153 \WPStaging\functions\debug_log("WP STAGING: Could not save cancel event analytics data for job ID $jobId.", 'debug', false);
154 }
155 }
156
157 /**
158 * Error event is static as it's a generic event not related to any specific type of event.
159 */
160 public static function enqueueErrorEvent($jobId, $errorMessage)
161 {
162 try {
163 $event = static::getEventByJobId($jobId);
164 } catch (\Exception $e) {
165 \WPStaging\functions\debug_log("WP STAGING: Could not register cancel event analytics data for job ID $jobId", 'debug', false);
166
167 return;
168 }
169
170 $lastDebugLogErrors = WPStaging::make(DebugLogReader::class)->getLastLogEntries(8 * KB_IN_BYTES);
171
172 $event->is_finished = false;
173 $event->is_error = true;
174 $event->error_message = $errorMessage;
175 $event->last_debug_logs = $lastDebugLogErrors;
176 $event->end_time = time();
177 $event->duration = time() - $event->start_time;
178 $event->ready_to_send = true;
179
180 try {
181 static::saveEvent($jobId, $event);
182 } catch (\Exception $e) {
183 \WPStaging\functions\debug_log("WP STAGING: Could not save cancel event analytics data for job ID $jobId.", 'debug', false);
184 }
185 }
186
187 protected static function getEventByJobId($jobId)
188 {
189 if (!$event = get_option("wpstg_analytics_event_$jobId")) {
190 throw new \UnexpectedValueException();
191 }
192
193 $event = json_decode($event);
194
195 if (empty($event) || !is_object($event)) {
196 throw new \UnexpectedValueException();
197 }
198
199 return $event;
200 }
201
202 protected static function saveEvent($jobId, $event)
203 {
204 $event = wp_json_encode($event);
205
206 if (!update_option("wpstg_analytics_event_$jobId", $event, false)) {
207 throw new \UnexpectedValueException();
208 }
209 }
210
211 /**
212 * @param mixed $eventData
213 * @param string $key
214 * @param mixed $default
215 * @return mixed
216 */
217 protected function getEventDataValue($eventData, string $key, $default = null)
218 {
219 if (is_array($eventData) && array_key_exists($key, $eventData)) {
220 return $eventData[$key];
221 }
222
223 if (!is_object($eventData)) {
224 return $default;
225 }
226
227 $publicProperties = get_object_vars($eventData);
228 if (array_key_exists($key, $publicProperties)) {
229 return $publicProperties[$key];
230 }
231
232 $getter = 'get' . ucfirst($key);
233 if (method_exists($eventData, $getter)) {
234 return $eventData->{$getter}();
235 }
236
237 $isser = 'is' . ucfirst($key);
238 if (method_exists($eventData, $isser)) {
239 return $eventData->{$isser}();
240 }
241
242 return $default;
243 }
244
245 protected function getStagingEngine($eventData): string
246 {
247 $engine = $this->getEventDataValue($eventData, 'stagingEngine', 'legacy');
248
249 return in_array($engine, ['legacy', 'next_gen'], true) ? $engine : 'legacy';
250 }
251 }
252