PluginProbe
Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder / 6.2.2
Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder v6.2.2
6.2.14 6.2.13 6.2.12 6.2.10 6.2.11 6.2.9 6.2.8 6.2.7 6.2.6 6.2.5 6.2.4 6.2.3 6.2.2 3.6.22 3.6.31 3.6.40 3.6.41 3.6.42 3.6.50 3.6.51 3.6.60 3.6.61 3.6.62 3.6.64 3.6.65 All 196 releases
fluentform / app / Services / Libraries / action-scheduler / classes / abstracts / ActionScheduler_Store.php

ActionScheduler_Store.php in Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder 6.2.2, at app/Services/Libraries/action-scheduler/classes/abstracts/ActionScheduler_Store.php

508 lines 14.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 // phpcs:disable WordPress.Security.EscapeOutput.ExceptionNotEscaped -- Third-party library (Action Scheduler)
3
4 /**
5 * Class ActionScheduler_Store
6 *
7 * @codeCoverageIgnore
8 */
9 abstract class ActionScheduler_Store extends ActionScheduler_Store_Deprecated {
10 const STATUS_COMPLETE = 'complete';
11 const STATUS_PENDING = 'pending';
12 const STATUS_RUNNING = 'in-progress';
13 const STATUS_FAILED = 'failed';
14 const STATUS_CANCELED = 'canceled';
15 const DEFAULT_CLASS = 'ActionScheduler_wpPostStore';
16
17 /**
18 * ActionScheduler_Store instance.
19 *
20 * @var ActionScheduler_Store
21 */
22 private static $store = null;
23
24 /**
25 * Maximum length of args.
26 *
27 * @var int
28 */
29 protected static $max_args_length = 191;
30
31 /**
32 * Save action.
33 *
34 * @param ActionScheduler_Action $action Action to save.
35 * @param null|DateTime $scheduled_date Optional Date of the first instance
36 * to store. Otherwise uses the first date of the action's
37 * schedule.
38 *
39 * @return int The action ID
40 */
41 abstract public function save_action( ActionScheduler_Action $action, ?DateTime $scheduled_date = null );
42
43 /**
44 * Get action.
45 *
46 * @param string $action_id Action ID.
47 *
48 * @return ActionScheduler_Action
49 */
50 abstract public function fetch_action( $action_id );
51
52 /**
53 * Find an action.
54 *
55 * Note: the query ordering changes based on the passed 'status' value.
56 *
57 * @param string $hook Action hook.
58 * @param array $params Parameters of the action to find.
59 *
60 * @return string|null ID of the next action matching the criteria or NULL if not found.
61 */
62 public function find_action( $hook, $params = array() ) {
63 $params = wp_parse_args(
64 $params,
65 array(
66 'args' => null,
67 'status' => self::STATUS_PENDING,
68 'group' => '',
69 )
70 );
71
72 // These params are fixed for this method.
73 $params['hook'] = $hook;
74 $params['orderby'] = 'date';
75 $params['per_page'] = 1;
76
77 if ( ! empty( $params['status'] ) ) {
78 if ( self::STATUS_PENDING === $params['status'] ) {
79 $params['order'] = 'ASC'; // Find the next action that matches.
80 } else {
81 $params['order'] = 'DESC'; // Find the most recent action that matches.
82 }
83 }
84
85 $results = $this->query_actions( $params );
86
87 return empty( $results ) ? null : $results[0];
88 }
89
90 /**
91 * Query for action count or list of action IDs.
92 *
93 * @since 3.3.0 $query['status'] accepts array of statuses instead of a single status.
94 *
95 * @param array $query {
96 * Query filtering options.
97 *
98 * @type string $hook The name of the actions. Optional.
99 * @type string|array $status The status or statuses of the actions. Optional.
100 * @type array $args The args array of the actions. Optional.
101 * @type DateTime $date The scheduled date of the action. Used in UTC timezone. Optional.
102 * @type string $date_compare Operator for selecting by $date param. Accepted values are '!=', '>', '>=', '<', '<=', '='. Defaults to '<='.
103 * @type DateTime $modified The last modified date of the action. Used in UTC timezone. Optional.
104 * @type string $modified_compare Operator for comparing $modified param. Accepted values are '!=', '>', '>=', '<', '<=', '='. Defaults to '<='.
105 * @type string $group The group the action belongs to. Optional.
106 * @type bool|int $claimed TRUE to find claimed actions, FALSE to find unclaimed actions, an int to find a specific claim ID. Optional.
107 * @type int $per_page Number of results to return. Defaults to 5.
108 * @type int $offset The query pagination offset. Defaults to 0.
109 * @type int $orderby Accepted values are 'hook', 'group', 'modified', 'date' or 'none'. Defaults to 'date'.
110 * @type string $order Accepted values are 'ASC' or 'DESC'. Defaults to 'ASC'.
111 * }
112 * @param string $query_type Whether to select or count the results. Default, select.
113 *
114 * @return string|array|null The IDs of actions matching the query. Null on failure.
115 */
116 abstract public function query_actions( $query = array(), $query_type = 'select' );
117
118 /**
119 * Run query to get a single action ID.
120 *
121 * @since 3.3.0
122 *
123 * @see ActionScheduler_Store::query_actions for $query arg usage but 'per_page' and 'offset' can't be used.
124 *
125 * @param array $query Query parameters.
126 *
127 * @return int|null
128 */
129 public function query_action( $query ) {
130 $query['per_page'] = 1;
131 $query['offset'] = 0;
132 $results = $this->query_actions( $query );
133
134 if ( empty( $results ) ) {
135 return null;
136 } else {
137 return (int) $results[0];
138 }
139 }
140
141 /**
142 * Get a count of all actions in the store, grouped by status
143 *
144 * @return array
145 */
146 abstract public function action_counts();
147
148 /**
149 * Get additional action counts.
150 *
151 * - add past-due actions
152 *
153 * @return array
154 */
155 public function extra_action_counts() {
156 $extra_actions = array();
157
158 $pastdue_action_counts = (int) $this->query_actions(
159 array(
160 'status' => self::STATUS_PENDING,
161 'date' => as_get_datetime_object(),
162 ),
163 'count'
164 );
165
166 if ( $pastdue_action_counts ) {
167 $extra_actions['past-due'] = $pastdue_action_counts;
168 }
169
170 /**
171 * Allows 3rd party code to add extra action counts (used in filters in the list table).
172 *
173 * @since 3.5.0
174 * @param $extra_actions array Array with format action_count_identifier => action count.
175 */
176 return apply_filters( 'action_scheduler_extra_action_counts', $extra_actions );
177 }
178
179 /**
180 * Cancel action.
181 *
182 * @param string $action_id Action ID.
183 */
184 abstract public function cancel_action( $action_id );
185
186 /**
187 * Delete action.
188 *
189 * @param string $action_id Action ID.
190 */
191 abstract public function delete_action( $action_id );
192
193 /**
194 * Get action's schedule or run timestamp.
195 *
196 * @param string $action_id Action ID.
197 *
198 * @return DateTime The date the action is schedule to run, or the date that it ran.
199 */
200 abstract public function get_date( $action_id );
201
202
203 /**
204 * Make a claim.
205 *
206 * @param int $max_actions Maximum number of actions to claim.
207 * @param DateTime|null $before_date Claim only actions schedule before the given date. Defaults to now.
208 * @param array $hooks Claim only actions with a hook or hooks.
209 * @param string $group Claim only actions in the given group.
210 *
211 * @return ActionScheduler_ActionClaim
212 */
213 abstract public function stake_claim( $max_actions = 10, ?DateTime $before_date = null, $hooks = array(), $group = '' );
214
215 /**
216 * Get claim count.
217 *
218 * @return int
219 */
220 abstract public function get_claim_count();
221
222 /**
223 * Release the claim.
224 *
225 * @param ActionScheduler_ActionClaim $claim Claim object.
226 */
227 abstract public function release_claim( ActionScheduler_ActionClaim $claim );
228
229 /**
230 * Un-claim the action.
231 *
232 * @param string $action_id Action ID.
233 */
234 abstract public function unclaim_action( $action_id );
235
236 /**
237 * Mark action as failed.
238 *
239 * @param string $action_id Action ID.
240 */
241 abstract public function mark_failure( $action_id );
242
243 /**
244 * Log action's execution.
245 *
246 * @param string $action_id Actoin ID.
247 */
248 abstract public function log_execution( $action_id );
249
250 /**
251 * Mark action as complete.
252 *
253 * @param string $action_id Action ID.
254 */
255 abstract public function mark_complete( $action_id );
256
257 /**
258 * Get action's status.
259 *
260 * @param string $action_id Action ID.
261 * @return string
262 */
263 abstract public function get_status( $action_id );
264
265 /**
266 * Get action's claim ID.
267 *
268 * @param string $action_id Action ID.
269 * @return mixed
270 */
271 abstract public function get_claim_id( $action_id );
272
273 /**
274 * Find actions by claim ID.
275 *
276 * @param string $claim_id Claim ID.
277 * @return array
278 */
279 abstract public function find_actions_by_claim_id( $claim_id );
280
281 /**
282 * Validate SQL operator.
283 *
284 * @param string $comparison_operator Operator.
285 * @return string
286 */
287 protected function validate_sql_comparator( $comparison_operator ) {
288 if ( in_array( $comparison_operator, array( '!=', '>', '>=', '<', '<=', '=' ), true ) ) {
289 return $comparison_operator;
290 }
291
292 return '=';
293 }
294
295 /**
296 * Get the time MySQL formatted date/time string for an action's (next) scheduled date.
297 *
298 * @param ActionScheduler_Action $action Action.
299 * @param null|DateTime $scheduled_date Action's schedule date (optional).
300 * @return string
301 */
302 protected function get_scheduled_date_string( ActionScheduler_Action $action, ?DateTime $scheduled_date = null ) {
303 $next = is_null( $scheduled_date ) ? $action->get_schedule()->get_date() : $scheduled_date;
304
305 if ( ! $next ) {
306 $next = date_create();
307 }
308
309 $next->setTimezone( new DateTimeZone( 'UTC' ) );
310
311 return $next->format( 'Y-m-d H:i:s' );
312 }
313
314 /**
315 * Get the time MySQL formatted date/time string for an action's (next) scheduled date.
316 *
317 * @param ActionScheduler_Action|null $action Action.
318 * @param null|DateTime $scheduled_date Action's scheduled date (optional).
319 * @return string
320 */
321 protected function get_scheduled_date_string_local( ActionScheduler_Action $action, ?DateTime $scheduled_date = null ) {
322 $next = is_null( $scheduled_date ) ? $action->get_schedule()->get_date() : $scheduled_date;
323
324 if ( ! $next ) {
325 $next = date_create();
326 }
327
328 ActionScheduler_TimezoneHelper::set_local_timezone( $next );
329 return $next->format( 'Y-m-d H:i:s' );
330 }
331
332 /**
333 * Validate that we could decode action arguments.
334 *
335 * @param mixed $args The decoded arguments.
336 * @param int $action_id The action ID.
337 *
338 * @throws ActionScheduler_InvalidActionException When the decoded arguments are invalid.
339 */
340 protected function validate_args( $args, $action_id ) {
341 // Ensure we have an array of args.
342 if ( ! is_array( $args ) ) {
343 throw ActionScheduler_InvalidActionException::from_decoding_args( $action_id );
344 }
345
346 // Validate JSON decoding if possible.
347 if ( function_exists( 'json_last_error' ) && JSON_ERROR_NONE !== json_last_error() ) {
348 throw ActionScheduler_InvalidActionException::from_decoding_args( $action_id, $args );
349 }
350 }
351
352 /**
353 * Validate a ActionScheduler_Schedule object.
354 *
355 * @param mixed $schedule The unserialized ActionScheduler_Schedule object.
356 * @param int $action_id The action ID.
357 *
358 * @throws ActionScheduler_InvalidActionException When the schedule is invalid.
359 */
360 protected function validate_schedule( $schedule, $action_id ) {
361 if ( empty( $schedule ) || ! is_a( $schedule, 'ActionScheduler_Schedule' ) ) {
362 throw ActionScheduler_InvalidActionException::from_schedule( $action_id, $schedule );
363 }
364 }
365
366 /**
367 * InnoDB indexes have a maximum size of 767 bytes by default, which is only 191 characters with utf8mb4.
368 *
369 * Previously, AS wasn't concerned about args length, as we used the (unindex) post_content column. However,
370 * with custom tables, we use an indexed VARCHAR column instead.
371 *
372 * @param ActionScheduler_Action $action Action to be validated.
373 * @throws InvalidArgumentException When json encoded args is too long.
374 */
375 protected function validate_action( ActionScheduler_Action $action ) {
376 if ( strlen( wp_json_encode( $action->get_args() ) ) > static::$max_args_length ) {
377 // translators: %d is a number (maximum length of action arguments).
378 throw new InvalidArgumentException( sprintf( __( 'ActionScheduler_Action::$args too long. To ensure the args column can be indexed, action args should not be more than %d characters when encoded as JSON.', 'action-scheduler' ), static::$max_args_length ) );
379 }
380 }
381
382 /**
383 * Cancel pending actions by hook.
384 *
385 * @since 3.0.0
386 *
387 * @param string $hook Hook name.
388 *
389 * @return void
390 */
391 public function cancel_actions_by_hook( $hook ) {
392 $action_ids = true;
393 while ( ! empty( $action_ids ) ) {
394 $action_ids = $this->query_actions(
395 array(
396 'hook' => $hook,
397 'status' => self::STATUS_PENDING,
398 'per_page' => 1000,
399 'orderby' => 'none',
400 )
401 );
402
403 $this->bulk_cancel_actions( $action_ids );
404 }
405 }
406
407 /**
408 * Cancel pending actions by group.
409 *
410 * @since 3.0.0
411 *
412 * @param string $group Group slug.
413 *
414 * @return void
415 */
416 public function cancel_actions_by_group( $group ) {
417 $action_ids = true;
418 while ( ! empty( $action_ids ) ) {
419 $action_ids = $this->query_actions(
420 array(
421 'group' => $group,
422 'status' => self::STATUS_PENDING,
423 'per_page' => 1000,
424 'orderby' => 'none',
425 )
426 );
427
428 $this->bulk_cancel_actions( $action_ids );
429 }
430 }
431
432 /**
433 * Cancel a set of action IDs.
434 *
435 * @since 3.0.0
436 *
437 * @param int[] $action_ids List of action IDs.
438 *
439 * @return void
440 */
441 private function bulk_cancel_actions( $action_ids ) {
442 foreach ( $action_ids as $action_id ) {
443 $this->cancel_action( $action_id );
444 }
445
446 do_action( 'action_scheduler_bulk_cancel_actions', $action_ids );
447 }
448
449 /**
450 * Get status labels.
451 *
452 * @return array<string, string>
453 */
454 public function get_status_labels() {
455 return array(
456 self::STATUS_COMPLETE => __( 'Complete', 'action-scheduler' ),
457 self::STATUS_PENDING => __( 'Pending', 'action-scheduler' ),
458 self::STATUS_RUNNING => __( 'In-progress', 'action-scheduler' ),
459 self::STATUS_FAILED => __( 'Failed', 'action-scheduler' ),
460 self::STATUS_CANCELED => __( 'Canceled', 'action-scheduler' ),
461 );
462 }
463
464 /**
465 * Check if there are any pending scheduled actions due to run.
466 *
467 * @return string
468 */
469 public function has_pending_actions_due() {
470 $pending_actions = $this->query_actions(
471 array(
472 'per_page' => 1,
473 'date' => as_get_datetime_object(),
474 'status' => self::STATUS_PENDING,
475 'orderby' => 'none',
476 ),
477 'count'
478 );
479
480 return ! empty( $pending_actions );
481 }
482
483 /**
484 * Callable initialization function optionally overridden in derived classes.
485 */
486 public function init() {}
487
488 /**
489 * Callable function to mark an action as migrated optionally overridden in derived classes.
490 *
491 * @param int $action_id Action ID.
492 */
493 public function mark_migrated( $action_id ) {}
494
495 /**
496 * Get instance.
497 *
498 * @return ActionScheduler_Store
499 */
500 public static function instance() {
501 if ( empty( self::$store ) ) {
502 $class = apply_filters( 'action_scheduler_store_class', self::DEFAULT_CLASS );
503 self::$store = new $class();
504 }
505 return self::$store;
506 }
507 }
508