PluginProbe
Image Optimizer – Compress Images and Convert to WebP or AVIF / 1.5.3
Image Optimizer – Compress Images and Convert to WebP or AVIF v1.5.3
1.7.7 1.7.6 1.7.5 1.7.4 trunk 1.0.0 1.0.1 1.0.2 1.1.0 1.2.0 1.2.1 1.3.0 1.4.0 1.4.1 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 All 33 releases
image-optimization / vendor / woocommerce / action-scheduler / classes / data-stores / ActionScheduler_HybridStore.php

ActionScheduler_HybridStore.php in Image Optimizer – Compress Images and Convert to WebP or AVIF 1.5.3, at vendor/woocommerce/action-scheduler/classes/data-stores/ActionScheduler_HybridStore.php

434 lines 12.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 use ActionScheduler_Store as Store;
4 use Action_Scheduler\Migration\Runner;
5 use Action_Scheduler\Migration\Config;
6 use Action_Scheduler\Migration\Controller;
7
8 /**
9 * Class ActionScheduler_HybridStore
10 *
11 * A wrapper around multiple stores that fetches data from both.
12 *
13 * @since 3.0.0
14 */
15 class ActionScheduler_HybridStore extends Store {
16 const DEMARKATION_OPTION = 'action_scheduler_hybrid_store_demarkation';
17
18 /** @var ActionScheduler_Store */
19 private $primary_store;
20 /** @var ActionScheduler_Store */
21 private $secondary_store;
22 /** @var Action_Scheduler\Migration\Runner */
23 private $migration_runner;
24
25 /**
26 * @var int The dividing line between IDs of actions created
27 * by the primary and secondary stores.
28 *
29 * Methods that accept an action ID will compare the ID against
30 * this to determine which store will contain that ID. In almost
31 * all cases, the ID should come from the primary store, but if
32 * client code is bypassing the API functions and fetching IDs
33 * from elsewhere, then there is a chance that an unmigrated ID
34 * might be requested.
35 */
36 private $demarkation_id = 0;
37
38 /**
39 * ActionScheduler_HybridStore constructor.
40 *
41 * @param Config $config Migration config object.
42 */
43 public function __construct( Config $config = null ) {
44 $this->demarkation_id = (int) get_option( self::DEMARKATION_OPTION, 0 );
45 if ( empty( $config ) ) {
46 $config = Controller::instance()->get_migration_config_object();
47 }
48 $this->primary_store = $config->get_destination_store();
49 $this->secondary_store = $config->get_source_store();
50 $this->migration_runner = new Runner( $config );
51 }
52
53 /**
54 * Initialize the table data store tables.
55 *
56 * @codeCoverageIgnore
57 */
58 public function init() {
59 add_action( 'action_scheduler/created_table', [ $this, 'set_autoincrement' ], 10, 2 );
60 $this->primary_store->init();
61 $this->secondary_store->init();
62 remove_action( 'action_scheduler/created_table', [ $this, 'set_autoincrement' ], 10 );
63 }
64
65 /**
66 * When the actions table is created, set its autoincrement
67 * value to be one higher than the posts table to ensure that
68 * there are no ID collisions.
69 *
70 * @param string $table_name Table name.
71 * @param string $table_suffix Suffix of table name.
72 *
73 * @return void
74 * @codeCoverageIgnore
75 */
76 public function set_autoincrement( $table_name, $table_suffix ) {
77 if ( ActionScheduler_StoreSchema::ACTIONS_TABLE === $table_suffix ) {
78 if ( empty( $this->demarkation_id ) ) {
79 $this->demarkation_id = $this->set_demarkation_id();
80 }
81 /** @var \wpdb $wpdb */
82 global $wpdb;
83 /**
84 * A default date of '0000-00-00 00:00:00' is invalid in MySQL 5.7 when configured with
85 * sql_mode including both STRICT_TRANS_TABLES and NO_ZERO_DATE.
86 */
87 $default_date = new DateTime( 'tomorrow' );
88 $null_action = new ActionScheduler_NullAction();
89 $date_gmt = $this->get_scheduled_date_string( $null_action, $default_date );
90 $date_local = $this->get_scheduled_date_string_local( $null_action, $default_date );
91
92 $row_count = $wpdb->insert(
93 $wpdb->{ActionScheduler_StoreSchema::ACTIONS_TABLE},
94 [
95 'action_id' => $this->demarkation_id,
96 'hook' => '',
97 'status' => '',
98 'scheduled_date_gmt' => $date_gmt,
99 'scheduled_date_local' => $date_local,
100 'last_attempt_gmt' => $date_gmt,
101 'last_attempt_local' => $date_local,
102 ]
103 );
104 if ( $row_count > 0 ) {
105 $wpdb->delete(
106 $wpdb->{ActionScheduler_StoreSchema::ACTIONS_TABLE},
107 [ 'action_id' => $this->demarkation_id ]
108 );
109 }
110 }
111 }
112
113 /**
114 * Store the demarkation id in WP options.
115 *
116 * @param int $id The ID to set as the demarkation point between the two stores
117 * Leave null to use the next ID from the WP posts table.
118 *
119 * @return int The new ID.
120 *
121 * @codeCoverageIgnore
122 */
123 private function set_demarkation_id( $id = null ) {
124 if ( empty( $id ) ) {
125 /** @var \wpdb $wpdb */
126 global $wpdb;
127 $id = (int) $wpdb->get_var( "SELECT MAX(ID) FROM $wpdb->posts" );
128 $id ++;
129 }
130 update_option( self::DEMARKATION_OPTION, $id );
131
132 return $id;
133 }
134
135 /**
136 * Find the first matching action from the secondary store.
137 * If it exists, migrate it to the primary store immediately.
138 * After it migrates, the secondary store will logically contain
139 * the next matching action, so return the result thence.
140 *
141 * @param string $hook Action's hook.
142 * @param array $params Action's arguments.
143 *
144 * @return string
145 */
146 public function find_action( $hook, $params = [] ) {
147 $found_unmigrated_action = $this->secondary_store->find_action( $hook, $params );
148 if ( ! empty( $found_unmigrated_action ) ) {
149 $this->migrate( [ $found_unmigrated_action ] );
150 }
151
152 return $this->primary_store->find_action( $hook, $params );
153 }
154
155 /**
156 * Find actions matching the query in the secondary source first.
157 * If any are found, migrate them immediately. Then the secondary
158 * store will contain the canonical results.
159 *
160 * @param array $query Query arguments.
161 * @param string $query_type Whether to select or count the results. Default, select.
162 *
163 * @return int[]
164 */
165 public function query_actions( $query = [], $query_type = 'select' ) {
166 $found_unmigrated_actions = $this->secondary_store->query_actions( $query, 'select' );
167 if ( ! empty( $found_unmigrated_actions ) ) {
168 $this->migrate( $found_unmigrated_actions );
169 }
170
171 return $this->primary_store->query_actions( $query, $query_type );
172 }
173
174 /**
175 * Get a count of all actions in the store, grouped by status
176 *
177 * @return array Set of 'status' => int $count pairs for statuses with 1 or more actions of that status.
178 */
179 public function action_counts() {
180 $unmigrated_actions_count = $this->secondary_store->action_counts();
181 $migrated_actions_count = $this->primary_store->action_counts();
182 $actions_count_by_status = array();
183
184 foreach ( $this->get_status_labels() as $status_key => $status_label ) {
185
186 $count = 0;
187
188 if ( isset( $unmigrated_actions_count[ $status_key ] ) ) {
189 $count += $unmigrated_actions_count[ $status_key ];
190 }
191
192 if ( isset( $migrated_actions_count[ $status_key ] ) ) {
193 $count += $migrated_actions_count[ $status_key ];
194 }
195
196 $actions_count_by_status[ $status_key ] = $count;
197 }
198
199 $actions_count_by_status = array_filter( $actions_count_by_status );
200
201 return $actions_count_by_status;
202 }
203
204 /**
205 * If any actions would have been claimed by the secondary store,
206 * migrate them immediately, then ask the primary store for the
207 * canonical claim.
208 *
209 * @param int $max_actions Maximum number of actions to claim.
210 * @param null|DateTime $before_date Latest timestamp of actions to claim.
211 * @param string[] $hooks Hook of actions to claim.
212 * @param string $group Group of actions to claim.
213 *
214 * @return ActionScheduler_ActionClaim
215 */
216 public function stake_claim( $max_actions = 10, DateTime $before_date = null, $hooks = array(), $group = '' ) {
217 $claim = $this->secondary_store->stake_claim( $max_actions, $before_date, $hooks, $group );
218
219 $claimed_actions = $claim->get_actions();
220 if ( ! empty( $claimed_actions ) ) {
221 $this->migrate( $claimed_actions );
222 }
223
224 $this->secondary_store->release_claim( $claim );
225
226 return $this->primary_store->stake_claim( $max_actions, $before_date, $hooks, $group );
227 }
228
229 /**
230 * Migrate a list of actions to the table data store.
231 *
232 * @param array $action_ids List of action IDs.
233 */
234 private function migrate( $action_ids ) {
235 $this->migration_runner->migrate_actions( $action_ids );
236 }
237
238 /**
239 * Save an action to the primary store.
240 *
241 * @param ActionScheduler_Action $action Action object to be saved.
242 * @param DateTime $date Optional. Schedule date. Default null.
243 *
244 * @return int The action ID
245 */
246 public function save_action( ActionScheduler_Action $action, DateTime $date = null ) {
247 return $this->primary_store->save_action( $action, $date );
248 }
249
250 /**
251 * Retrieve an existing action whether migrated or not.
252 *
253 * @param int $action_id Action ID.
254 */
255 public function fetch_action( $action_id ) {
256 $store = $this->get_store_from_action_id( $action_id, true );
257 if ( $store ) {
258 return $store->fetch_action( $action_id );
259 } else {
260 return new ActionScheduler_NullAction();
261 }
262 }
263
264 /**
265 * Cancel an existing action whether migrated or not.
266 *
267 * @param int $action_id Action ID.
268 */
269 public function cancel_action( $action_id ) {
270 $store = $this->get_store_from_action_id( $action_id );
271 if ( $store ) {
272 $store->cancel_action( $action_id );
273 }
274 }
275
276 /**
277 * Delete an existing action whether migrated or not.
278 *
279 * @param int $action_id Action ID.
280 */
281 public function delete_action( $action_id ) {
282 $store = $this->get_store_from_action_id( $action_id );
283 if ( $store ) {
284 $store->delete_action( $action_id );
285 }
286 }
287
288 /**
289 * Get the schedule date an existing action whether migrated or not.
290 *
291 * @param int $action_id Action ID.
292 */
293 public function get_date( $action_id ) {
294 $store = $this->get_store_from_action_id( $action_id );
295 if ( $store ) {
296 return $store->get_date( $action_id );
297 } else {
298 return null;
299 }
300 }
301
302 /**
303 * Mark an existing action as failed whether migrated or not.
304 *
305 * @param int $action_id Action ID.
306 */
307 public function mark_failure( $action_id ) {
308 $store = $this->get_store_from_action_id( $action_id );
309 if ( $store ) {
310 $store->mark_failure( $action_id );
311 }
312 }
313
314 /**
315 * Log the execution of an existing action whether migrated or not.
316 *
317 * @param int $action_id Action ID.
318 */
319 public function log_execution( $action_id ) {
320 $store = $this->get_store_from_action_id( $action_id );
321 if ( $store ) {
322 $store->log_execution( $action_id );
323 }
324 }
325
326 /**
327 * Mark an existing action complete whether migrated or not.
328 *
329 * @param int $action_id Action ID.
330 */
331 public function mark_complete( $action_id ) {
332 $store = $this->get_store_from_action_id( $action_id );
333 if ( $store ) {
334 $store->mark_complete( $action_id );
335 }
336 }
337
338 /**
339 * Get an existing action status whether migrated or not.
340 *
341 * @param int $action_id Action ID.
342 */
343 public function get_status( $action_id ) {
344 $store = $this->get_store_from_action_id( $action_id );
345 if ( $store ) {
346 return $store->get_status( $action_id );
347 }
348 return null;
349 }
350
351 /**
352 * Return which store an action is stored in.
353 *
354 * @param int $action_id ID of the action.
355 * @param bool $primary_first Optional flag indicating search the primary store first.
356 * @return ActionScheduler_Store
357 */
358 protected function get_store_from_action_id( $action_id, $primary_first = false ) {
359 if ( $primary_first ) {
360 $stores = [
361 $this->primary_store,
362 $this->secondary_store,
363 ];
364 } elseif ( $action_id < $this->demarkation_id ) {
365 $stores = [
366 $this->secondary_store,
367 $this->primary_store,
368 ];
369 } else {
370 $stores = [
371 $this->primary_store,
372 ];
373 }
374
375 foreach ( $stores as $store ) {
376 $action = $store->fetch_action( $action_id );
377 if ( ! is_a( $action, 'ActionScheduler_NullAction' ) ) {
378 return $store;
379 }
380 }
381 return null;
382 }
383
384 /**
385 * * * * * * * * * * * * * * * * * * * * * * * * * * *
386 * All claim-related functions should operate solely
387 * on the primary store.
388 * * * * * * * * * * * * * * * * * * * * * * * * * * *
389 */
390
391 /**
392 * Get the claim count from the table data store.
393 */
394 public function get_claim_count() {
395 return $this->primary_store->get_claim_count();
396 }
397
398 /**
399 * Retrieve the claim ID for an action from the table data store.
400 *
401 * @param int $action_id Action ID.
402 */
403 public function get_claim_id( $action_id ) {
404 return $this->primary_store->get_claim_id( $action_id );
405 }
406
407 /**
408 * Release a claim in the table data store.
409 *
410 * @param ActionScheduler_ActionClaim $claim Claim object.
411 */
412 public function release_claim( ActionScheduler_ActionClaim $claim ) {
413 $this->primary_store->release_claim( $claim );
414 }
415
416 /**
417 * Release claims on an action in the table data store.
418 *
419 * @param int $action_id Action ID.
420 */
421 public function unclaim_action( $action_id ) {
422 $this->primary_store->unclaim_action( $action_id );
423 }
424
425 /**
426 * Retrieve a list of action IDs by claim.
427 *
428 * @param int $claim_id Claim ID.
429 */
430 public function find_actions_by_claim_id( $claim_id ) {
431 return $this->primary_store->find_actions_by_claim_id( $claim_id );
432 }
433 }
434