PluginProbe
Import WP – CSV & XML Import Export for WordPress / 2.7.3
Import WP – CSV & XML Import Export for WordPress v2.7.3
2.15.1 2.15.0 2.14.24 2.14.23 2.7.0 2.7.1 2.7.10 2.7.11 2.7.12 2.7.13 2.7.14 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7 2.7.8 2.7.9 2.8.0 2.8.1 2.8.2 2.8.3 2.9.0 2.9.1 All 144 releases
jc-importer / class / Common / Importer / State / ImporterState.php

ImporterState.php in Import WP – CSV & XML Import Export for WordPress 2.7.3, at class/Common/Importer/State/ImporterState.php

400 lines 10.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace ImportWP\Common\Importer\State;
4
5 use ImportWP\Common\Util\Logger;
6
7 class ImporterState
8 {
9 private $importer_id;
10 private $user;
11 protected $data;
12
13 public function __construct($importer_id, $user)
14 {
15 $this->importer_id = $importer_id;
16 $this->user = $user;
17 }
18
19 public function init($session)
20 {
21 $state = self::wait_for_lock_and_get_state($this->importer_id, $this->user, [
22 'id' => $session,
23 'version' => 2,
24 'status' => 'init',
25 'section' => 'import',
26 'message' => '',
27 'progress' => [
28 'import' => [
29 'start' => 0,
30 'end' => 0,
31 'current_row' => 0,
32 ],
33 'delete' => [
34 'start' => 0,
35 'end' => 0,
36 'current_row' => 0,
37 ]
38 ],
39 'updated' => time(),
40 'timestamp' => time(),
41 'duration' => 0
42 ]);
43
44 if (!$state || !isset($state['status'])) {
45 throw new \Exception("Invalid importer state");
46 }
47
48 $this->populate($state);
49
50 if (!$this->validate($session)) {
51 throw new \Exception("Importer session has changed");
52 }
53 }
54
55 public function populate($state)
56 {
57 foreach ($state as $name => $value) {
58 $this->data[$name] = $value;
59 }
60 }
61
62 public function has_status($status)
63 {
64 if (!isset($this->data['status'])) {
65 return false;
66 }
67
68 if (is_array($status)) {
69 return in_array($this->data['status'], $status);
70 }
71
72 return $this->data['status'] == $status;
73 }
74
75 public function has_section($section)
76 {
77 if (!isset($this->data['section'])) {
78 return false;
79 }
80
81 if (is_array($section)) {
82 return in_array($this->data['section'], $section);
83 }
84
85 return $this->data['section'] == $section;
86 }
87
88 public function get_session()
89 {
90 return isset($this->data['id']) ? $this->data['id'] : false;
91 }
92
93 public function get_status()
94 {
95 return isset($this->data['status']) ? $this->data['status'] : false;
96 }
97
98 public function validate($session_id)
99 {
100 $valid = $this->has_status('init') || $this->get_session() == $session_id;
101 if (!$valid) {
102 Logger::write("state -invalid -check={$session_id} -current={$this->get_session()}");
103 }
104 return $valid;
105 }
106
107 public function get_raw()
108 {
109 return $this->data;
110 }
111
112 public function get_importer_id()
113 {
114 return $this->importer_id;
115 }
116
117 public function get_section()
118 {
119 if (!isset($this->data['section'])) {
120 return false;
121 }
122
123 return $this->data['section'];
124 }
125
126 public function get_progress($section = null)
127 {
128 if (is_null($section)) {
129 $section = $this->get_section();
130 }
131
132 return $this->data['progress'][$section];
133 }
134
135 /**
136 * Update live state data using callback
137 *
138 * @param Closure[array]:array $state
139 *
140 * @return $this
141 */
142 public function update($callback)
143 {
144 $raw = self::wait_for_lock($this->importer_id, $this->user, function () use ($callback) {
145 $state = self::get_state($this->importer_id);
146
147 if (is_callable($callback)) {
148 $state = call_user_func($callback, $state);
149 }
150
151 self::set_state($this->importer_id, $state);
152 do_action('iwp/importer/status/save', $state);
153 return $state;
154 });
155
156 $this->populate($raw);
157
158 return $this;
159 }
160
161 /**
162 * Log fatal error
163 *
164 * @param \Exception $error
165 * @return $this
166 */
167 public function error($error)
168 {
169 return $this->update(function ($data) use ($error) {
170
171 $data['status'] = 'error';
172 $data['message'] = $error->getMessage();
173 $data['duration'] = floatval($data['duration']) + Logger::timer();
174
175 return $data;
176 });
177 }
178
179 public static function wait_for_lock($id, $user, $callback)
180 {
181
182 $start = microtime(true);
183
184 do {
185 list($has_lock, $result) = self::try_get_lock($id, $user, $callback);
186 } while (!$has_lock && (microtime(true) - $start < 30));
187
188 if (!$has_lock) {
189 throw new \Exception("Unable to get lock");
190 }
191
192 return $result;
193 }
194
195 public static function wait_for_lock_and_get_state($id, $user, $default = false)
196 {
197 return self::wait_for_lock($id, $user, function () use ($id, $default) {
198 return self::get_state($id, $default);
199 });
200 }
201
202 /**
203 * Get importer lock to update state
204 *
205 * @param mixed $user current id
206 * @param mixed $callback Callback triggered when we have a lock.
207 * @param int $wait_in_microseconds Time to wait in microseconds before retrying
208 *
209 * @return false|array
210 */
211 public static function try_get_lock($id, $user, $callback)
212 {
213 $has_lock = self::get_lock($id, $user);
214 if ($has_lock) {
215
216 $result = $has_lock;
217
218 if (is_callable($callback)) {
219 $result = call_user_func($callback);
220 }
221
222 self::reset_lock($id);
223
224 return [true, $result];
225 } else {
226
227 return [false, null];
228 }
229 }
230
231 /**
232 * Update importer state
233 *
234 * @param mixed $state State to overwrite
235 *
236 * @return void
237 */
238 public static function set_state($id, $state)
239 {
240 $state['updated'] = time();
241 self::update_option('iwp_importer_state_' . $id, maybe_serialize($state));
242 Logger::info('set_state -current=' . $state['id']);
243 }
244
245 /**
246 * Get last importer state
247 *
248 * @return array Importer state
249 */
250 public static function get_state($id, $default = false)
251 {
252 $state = self::get_option('iwp_importer_state_' . $id);
253 if (!$state) {
254 $state = $default;
255 if ($state !== false) {
256 self::set_state($id, $state);
257 }
258 }
259
260 if (isset($state['id'])) {
261 Logger::info('get_state -current=' . $state['id']);
262 } else {
263 Logger::info('get_state -current=n/a');
264 }
265
266 return $state;
267 }
268
269
270 public static function get_lock($id, $user, $timeout_in_seconds = 10)
271 {
272 /**
273 * @var \WPDB $wpdb
274 */
275 global $wpdb;
276
277 $lock_option_id = sprintf("iwp_importer_lock_%d", $id);
278 $existing = self::get_option($lock_option_id, false);
279
280 if ($existing !== false) {
281 if (is_multisite()) {
282 $query = $wpdb->prepare(
283 "UPDATE {$wpdb->sitemeta} SET meta_value=%s WHERE site_id=%d AND meta_key=%s AND meta_value=''",
284 $user,
285 $wpdb->siteid,
286 $lock_option_id
287 );
288 } else {
289 $query = $wpdb->prepare(
290 "UPDATE {$wpdb->options} SET option_value=%s WHERE option_name=%s AND option_value=''",
291 $user,
292 $lock_option_id
293 );
294 }
295 } else {
296 return self::update_option($lock_option_id, '');
297 }
298
299 $result = $wpdb->query($query);
300
301 if ($result) {
302
303 self::touch_lock($id);
304 } else {
305
306 // Clear lock if timestamp is greater than timeout.
307 $last_locked_time = (int)self::get_option('iwp_importer_lock_timestamp_' . $id, 0);
308 if (current_time('timestamp') > $last_locked_time + $timeout_in_seconds) {
309 self::reset_lock($id);
310 }
311 }
312
313 return $result;
314 }
315
316 public static function reset_lock($id)
317 {
318 self::update_option('iwp_importer_lock_' . $id);
319 self::touch_lock($id);
320 }
321
322 public static function touch_lock($id)
323 {
324 self::update_option('iwp_importer_lock_timestamp_' . $id, current_time('timestamp'));
325 }
326
327 public static function get_option($key, $default = false)
328 {
329 /**
330 * @var \WPDB $wpdb
331 */
332 global $wpdb;
333
334 if (is_multisite()) {
335 $query = $wpdb->prepare("SELECT meta_id as id, meta_value as data FROM {$wpdb->sitemeta} WHERE site_id=%s AND meta_key=%s LIMIT 1", [$wpdb->siteid, $key]);
336 } else {
337 $query = $wpdb->prepare("SELECT option_id as id, option_value as data FROM {$wpdb->options} WHERE option_name=%s LIMIT 1", [$key]);
338 }
339
340 $result = $wpdb->get_row($query, ARRAY_A);
341 if (!$result) {
342 return $default;
343 }
344
345 if (is_serialized($result['data'])) {
346 return unserialize($result['data']);
347 }
348
349 return $result['data'];
350 }
351
352 public static function update_option($key, $value = '')
353 {
354 /**
355 * @var \WPDB $wpdb
356 */
357 global $wpdb;
358
359 $result = self::get_option($key);
360
361 if (is_multisite()) {
362
363 if ($result !== false) {
364 $result = $wpdb->update($wpdb->sitemeta, ['meta_value' => $value], ['meta_key' => $key, 'site_id' => $wpdb->siteid], ['%s'], ['%s', '%s']);
365 } else {
366 $result = $wpdb->insert($wpdb->sitemeta, ['meta_value' => $value, 'meta_key' => $key, 'site_id' => $wpdb->siteid], ['%s', '%s', '%s']);
367 }
368 } else {
369 if ($result !== false) {
370 $result = $wpdb->update($wpdb->options, ['option_value' => $value], ['option_name' => $key], ['%s'], ['%s']);
371 } else {
372 $result = $wpdb->insert($wpdb->options, ['option_value' => $value, 'option_name' => $key], ['%s', '%s']);
373 }
374 }
375
376 return $result;
377 }
378
379 public static function clear_options($id)
380 {
381
382 // clear existing
383 delete_site_option('iwp_importer_config_' . $id);
384 delete_site_option('iwp_importer_state_' . $id);
385 delete_site_option('iwp_importer_lock_' . $id);
386 delete_site_option('iwp_importer_lock_timestamp_' . $id);
387
388 /**
389 * @var \WPDB $wpdb
390 */
391 global $wpdb;
392
393 if (is_multisite()) {
394 $wpdb->query("DELETE FROM {$wpdb->sitemeta} WHERE meta_key LIKE 'iwp\_importer\_state\_" . $id . "\_%'");
395 } else {
396 $wpdb->query("DELETE FROM {$wpdb->options} WHERE option_name LIKE 'iwp\_importer\_state\_" . $id . "\_%'");
397 }
398 }
399 }
400