Completed
Branch GDPR/consent (c6029e)
by
unknown
44:48 queued 29:41
created

EE_Cron_Tasks::instance()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 0
dl 0
loc 7
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
use EventEspresso\core\exceptions\InvalidDataTypeException;
4
use EventEspresso\core\exceptions\InvalidInterfaceException;
5
use EventEspresso\core\services\loaders\Loader;
6
use EventEspresso\core\services\loaders\LoaderFactory;
7
8
/**
9
 * Class EE_Cron_Tasks
10
 *
11
 * @package               Event Espresso
12
 * @subpackage            core
13
 * @author                Brent Christensen
14
 */
15
class EE_Cron_Tasks extends EE_Base
16
{
17
18
    /**
19
     * WordPress doesn't allow duplicate crons within 10 minutes of the original,
20
     * so we'll set our retry time for just over 10 minutes to avoid that
21
     */
22
    const reschedule_timeout = 605;
23
24
25
    /**
26
     * @var EE_Cron_Tasks
27
     */
28
    private static $_instance;
29
30
31
    /**
32
     * @return EE_Cron_Tasks
33
     * @throws ReflectionException
34
     * @throws EE_Error
35
     * @throws InvalidArgumentException
36
     * @throws InvalidInterfaceException
37
     * @throws InvalidDataTypeException
38
     */
39
    public static function instance()
40
    {
41
        if (! self::$_instance instanceof EE_Cron_Tasks) {
42
            self::$_instance = new self();
43
        }
44
        return self::$_instance;
45
    }
46
47
48
    /**
49
     * @access private
50
     * @throws InvalidDataTypeException
51
     * @throws InvalidInterfaceException
52
     * @throws InvalidArgumentException
53
     * @throws EE_Error
54
     * @throws ReflectionException
55
     */
56
    private function __construct()
57
    {
58
        do_action('AHEE_log', __CLASS__, __FUNCTION__);
59
        // verify that WP Cron is enabled
60
        if (defined('DISABLE_WP_CRON')
61
            && DISABLE_WP_CRON
62
            && is_admin()
63
            && ! get_option('ee_disabled_wp_cron_check')
64
        ) {
65
            /**
66
             * This needs to be delayed until after the config is loaded because EE_Cron_Tasks is constructed before
67
             * config is loaded.
68
             * This is intentionally using a anonymous function so that its not easily de-registered.  Client code
69
             * wanting to not have this functionality can just register its own action at a priority after this one to
70
             * reverse any changes.
71
             */
72
            add_action(
73
                'AHEE__EE_System__load_core_configuration__complete',
74
                function () {
75
                    EE_Registry::instance()->NET_CFG->core->do_messages_on_same_request = true;
76
                    EE_Registry::instance()->NET_CFG->update_config(true, false);
77
                    add_option('ee_disabled_wp_cron_check', 1, '', false);
78
                }
79
            );
80
        }
81
        // UPDATE TRANSACTION WITH PAYMENT
82
        add_action(
83
            'AHEE__EE_Cron_Tasks__update_transaction_with_payment_2',
84
            array('EE_Cron_Tasks', 'setup_update_for_transaction_with_payment'),
85
            10,
86
            2
87
        );
88
        // ABANDONED / EXPIRED TRANSACTION CHECK
89
        add_action(
90
            'AHEE__EE_Cron_Tasks__expired_transaction_check',
91
            array('EE_Cron_Tasks', 'expired_transaction_check'),
92
            10,
93
            1
94
        );
95
        // CLEAN OUT JUNK TRANSACTIONS AND RELATED DATA
96
        add_action(
97
            'AHEE__EE_Cron_Tasks__clean_up_junk_transactions',
98
            array('EE_Cron_Tasks', 'clean_out_junk_transactions')
99
        );
100
        // logging
101
        add_action(
102
            'AHEE__EE_System__load_core_configuration__complete',
103
            array('EE_Cron_Tasks', 'log_scheduled_ee_crons')
104
        );
105
        EE_Registry::instance()->load_lib('Messages_Scheduler');
106
        // clean out old gateway logs
107
        add_action(
108
            'AHEE_EE_Cron_Tasks__clean_out_old_gateway_logs',
109
            array('EE_Cron_Tasks', 'clean_out_old_gateway_logs')
110
        );
111
    }
112
113
114
    /**
115
     * @access protected
116
     * @return void
117
     */
118
    public static function log_scheduled_ee_crons()
119
    {
120
        $ee_crons = array(
121
            'AHEE__EE_Cron_Tasks__update_transaction_with_payment',
122
            'AHEE__EE_Cron_Tasks__finalize_abandoned_transactions',
123
            'AHEE__EE_Cron_Tasks__clean_up_junk_transactions',
124
        );
125
        $crons = (array) get_option('cron');
126
        if (! is_array($crons)) {
127
            return;
128
        }
129
        foreach ($crons as $timestamp => $cron) {
130
            /** @var array[] $cron */
131
            foreach ($ee_crons as $ee_cron) {
132
                if (isset($cron[ $ee_cron ]) && is_array($cron[ $ee_cron ])) {
133
                    do_action('AHEE_log', __CLASS__, __FUNCTION__, $ee_cron, 'scheduled EE cron');
134
                    foreach ($cron[ $ee_cron ] as $ee_cron_details) {
135
                        if (! empty($ee_cron_details['args'])) {
136
                            do_action(
137
                                'AHEE_log',
138
                                __CLASS__,
139
                                __FUNCTION__,
140
                                print_r($ee_cron_details['args'], true),
141
                                "{$ee_cron} args"
142
                            );
143
                        }
144
                    }
145
                }
146
            }
147
        }
148
    }
149
150
151
    /**
152
     * reschedule_cron_for_transactions_if_maintenance_mode
153
     * if Maintenance Mode is active, this will reschedule a cron to run again in 10 minutes
154
     *
155
     * @param string $cron_task
156
     * @param array  $TXN_IDs
157
     * @return bool
158
     * @throws DomainException
159
     */
160
    public static function reschedule_cron_for_transactions_if_maintenance_mode($cron_task, array $TXN_IDs)
161
    {
162
        if (! method_exists('EE_Cron_Tasks', $cron_task)) {
163
            throw new DomainException(
164
                sprintf(
165
                    __('"%1$s" is not valid method on EE_Cron_Tasks.', 'event_espresso'),
166
                    $cron_task
167
                )
168
            );
169
        }
170
        // reschedule the cron if we can't hit the db right now
171
        if (! EE_Maintenance_Mode::instance()->models_can_query()) {
172
            foreach ($TXN_IDs as $TXN_ID => $additional_vars) {
173
                // ensure $additional_vars is an array
174
                $additional_vars = is_array($additional_vars) ? $additional_vars : array($additional_vars);
175
                // reset cron job for the TXN
176
                call_user_func_array(
177
                    array('EE_Cron_Tasks', $cron_task),
178
                    array_merge(
179
                        array(
180
                            time() + (10 * MINUTE_IN_SECONDS),
181
                            $TXN_ID,
182
                        ),
183
                        $additional_vars
184
                    )
185
                );
186
            }
187
            return true;
188
        }
189
        return false;
190
    }
191
192
193
194
195
    /****************  UPDATE TRANSACTION WITH PAYMENT ****************/
196
197
198
    /**
199
     * array of TXN IDs and the payment
200
     *
201
     * @var array
202
     */
203
    protected static $_update_transactions_with_payment = array();
204
205
206
    /**
207
     * schedule_update_transaction_with_payment
208
     * sets a wp_schedule_single_event() for updating any TXNs that may
209
     * require updating due to recently received payments
210
     *
211
     * @param int $timestamp
212
     * @param int $TXN_ID
213
     * @param int $PAY_ID
214
     */
215
    public static function schedule_update_transaction_with_payment(
216
        $timestamp,
217
        $TXN_ID,
218
        $PAY_ID
219
    ) {
220
        do_action('AHEE_log', __CLASS__, __FUNCTION__);
221
        // validate $TXN_ID and $timestamp
222
        $TXN_ID = absint($TXN_ID);
223
        $timestamp = absint($timestamp);
224
        if ($TXN_ID && $timestamp) {
225
            wp_schedule_single_event(
226
                $timestamp,
227
                'AHEE__EE_Cron_Tasks__update_transaction_with_payment_2',
228
                array($TXN_ID, $PAY_ID)
229
            );
230
        }
231
    }
232
233
234
    /**
235
     * setup_update_for_transaction_with_payment
236
     * this is the callback for the action hook:
237
     * 'AHEE__EE_Cron_Tasks__update_transaction_with_payment'
238
     * which is setup by EE_Cron_Tasks::schedule_update_transaction_with_payment().
239
     * The passed TXN_ID and associated payment gets added to an array, and then
240
     * the EE_Cron_Tasks::update_transaction_with_payment() function is hooked into
241
     * 'shutdown' which will actually handle the processing of any
242
     * transactions requiring updating, because doing so now would be too early
243
     * and the required resources may not be available
244
     *
245
     * @param int $TXN_ID
246
     * @param int $PAY_ID
247
     */
248
    public static function setup_update_for_transaction_with_payment($TXN_ID = 0, $PAY_ID = 0)
249
    {
250
        do_action('AHEE_log', __CLASS__, __FUNCTION__, $TXN_ID, '$TXN_ID');
251
        if (absint($TXN_ID)) {
252
            self::$_update_transactions_with_payment[ $TXN_ID ] = $PAY_ID;
253
            add_action(
254
                'shutdown',
255
                array('EE_Cron_Tasks', 'update_transaction_with_payment'),
256
                5
257
            );
258
        }
259
    }
260
261
262
    /**
263
     * update_transaction_with_payment
264
     * loops through the self::$_abandoned_transactions array
265
     * and attempts to finalize any TXNs that have not been completed
266
     * but have had their sessions expired, most likely due to a user not
267
     * returning from an off-site payment gateway
268
     *
269
     * @throws EE_Error
270
     * @throws DomainException
271
     * @throws InvalidDataTypeException
272
     * @throws InvalidInterfaceException
273
     * @throws InvalidArgumentException
274
     * @throws ReflectionException
275
     * @throws RuntimeException
276
     */
277
    public static function update_transaction_with_payment()
278
    {
279
        do_action('AHEE_log', __CLASS__, __FUNCTION__);
280
        if (// are there any TXNs that need cleaning up ?
281
            empty(self::$_update_transactions_with_payment)
282
            // reschedule the cron if we can't hit the db right now
283
            || EE_Cron_Tasks::reschedule_cron_for_transactions_if_maintenance_mode(
284
                'schedule_update_transaction_with_payment',
285
                self::$_update_transactions_with_payment
286
            )
287
        ) {
288
            return;
289
        }
290
        /** @type EE_Payment_Processor $payment_processor */
291
        $payment_processor = EE_Registry::instance()->load_core('Payment_Processor');
292
        // set revisit flag for payment processor
293
        $payment_processor->set_revisit();
294
        // load EEM_Transaction
295
        EE_Registry::instance()->load_model('Transaction');
296
        foreach (self::$_update_transactions_with_payment as $TXN_ID => $PAY_ID) {
297
            // reschedule the cron if we can't hit the db right now
298
            if (! EE_Maintenance_Mode::instance()->models_can_query()) {
299
                // reset cron job for updating the TXN
300
                EE_Cron_Tasks::schedule_update_transaction_with_payment(
301
                    time() + EE_Cron_Tasks::reschedule_timeout,
302
                    $TXN_ID,
303
                    $PAY_ID
304
                );
305
                continue;
306
            }
307
            $transaction = EEM_Transaction::instance()->get_one_by_ID($TXN_ID);
308
            $payment = EEM_Payment::instance()->get_one_by_ID($PAY_ID);
309
            // verify transaction
310
            if ($transaction instanceof EE_Transaction && $payment instanceof EE_Payment) {
311
                // now try to update the TXN with any payments
312
                $payment_processor->update_txn_based_on_payment($transaction, $payment, true, true);
313
            }
314
            unset(self::$_update_transactions_with_payment[ $TXN_ID ]);
315
        }
316
    }
317
318
319
320
    /************  END OF UPDATE TRANSACTION WITH PAYMENT  ************/
321
322
323
    /*****************  EXPIRED TRANSACTION CHECK *****************/
324
325
326
    /**
327
     * array of TXN IDs
328
     *
329
     * @var array
330
     */
331
    protected static $_expired_transactions = array();
332
333
334
    /**
335
     * schedule_expired_transaction_check
336
     * sets a wp_schedule_single_event() for following up on TXNs after their session has expired
337
     *
338
     * @param int $timestamp
339
     * @param int $TXN_ID
340
     */
341
    public static function schedule_expired_transaction_check(
342
        $timestamp,
343
        $TXN_ID
344
    ) {
345
        // validate $TXN_ID and $timestamp
346
        $TXN_ID = absint($TXN_ID);
347
        $timestamp = absint($timestamp);
348
        if ($TXN_ID && $timestamp) {
349
            wp_schedule_single_event(
350
                $timestamp,
351
                'AHEE__EE_Cron_Tasks__expired_transaction_check',
352
                array($TXN_ID)
353
            );
354
        }
355
    }
356
357
358
    /**
359
     * expired_transaction_check
360
     * this is the callback for the action hook:
361
     * 'AHEE__EE_Cron_Tasks__transaction_session_expiration_check'
362
     * which is utilized by wp_schedule_single_event()
363
     * in \EED_Single_Page_Checkout::_initialize_transaction().
364
     * The passed TXN_ID gets added to an array, and then the
365
     * process_expired_transactions() function is hooked into
366
     * 'AHEE__EE_System__core_loaded_and_ready' which will actually handle the
367
     * processing of any failed transactions, because doing so now would be
368
     * too early and the required resources may not be available
369
     *
370
     * @param int $TXN_ID
371
     */
372
    public static function expired_transaction_check($TXN_ID = 0)
373
    {
374
        if (absint($TXN_ID)) {
375
            self::$_expired_transactions[ $TXN_ID ] = $TXN_ID;
376
            add_action(
377
                'shutdown',
378
                array('EE_Cron_Tasks', 'process_expired_transactions'),
379
                5
380
            );
381
        }
382
    }
383
384
385
    /**
386
     * process_expired_transactions
387
     * loops through the self::$_expired_transactions array and processes any failed TXNs
388
     *
389
     * @throws EE_Error
390
     * @throws InvalidDataTypeException
391
     * @throws InvalidInterfaceException
392
     * @throws InvalidArgumentException
393
     * @throws ReflectionException
394
     * @throws DomainException
395
     * @throws RuntimeException
396
     */
397
    public static function process_expired_transactions()
398
    {
399
        if (// are there any TXNs that need cleaning up ?
400
            empty(self::$_expired_transactions)
401
            // reschedule the cron if we can't hit the db right now
402
            || EE_Cron_Tasks::reschedule_cron_for_transactions_if_maintenance_mode(
403
                'schedule_expired_transaction_check',
404
                self::$_expired_transactions
405
            )
406
        ) {
407
            return;
408
        }
409
        /** @type EE_Transaction_Processor $transaction_processor */
410
        $transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
411
        // set revisit flag for txn processor
412
        $transaction_processor->set_revisit();
413
        // load EEM_Transaction
414
        EE_Registry::instance()->load_model('Transaction');
415
        foreach (self::$_expired_transactions as $TXN_ID) {
416
            $transaction = EEM_Transaction::instance()->get_one_by_ID($TXN_ID);
417
            // verify transaction and whether it is failed or not
418
            if ($transaction instanceof EE_Transaction) {
419
                switch ($transaction->status_ID()) {
420
                    // Completed TXNs
421
                    case EEM_Transaction::complete_status_code:
422
                        do_action(
423
                            'AHEE__EE_Cron_Tasks__process_expired_transactions__completed_transaction',
424
                            $transaction
425
                        );
426
                        break;
427
                    // Overpaid TXNs
428
                    case EEM_Transaction::overpaid_status_code:
429
                        do_action(
430
                            'AHEE__EE_Cron_Tasks__process_expired_transactions__overpaid_transaction',
431
                            $transaction
432
                        );
433
                        break;
434
                    // Incomplete TXNs
435
                    case EEM_Transaction::incomplete_status_code:
436
                        do_action(
437
                            'AHEE__EE_Cron_Tasks__process_expired_transactions__incomplete_transaction',
438
                            $transaction
439
                        );
440
                        // todo : move business logic into EE_Transaction_Processor for finalizing abandoned transactions
441
                        break;
442
                    // Abandoned TXNs
443
                    case EEM_Transaction::abandoned_status_code:
444
                        // run hook before updating transaction, primarily so
445
                        // EED_Ticket_Sales_Monitor::process_abandoned_transactions() can release reserved tickets
446
                        do_action(
447
                            'AHEE__EE_Cron_Tasks__process_expired_transactions__abandoned_transaction',
448
                            $transaction
449
                        );
450
                        // don't finalize the TXN if it has already been completed
451
                        if ($transaction->all_reg_steps_completed() !== true) {
452
                            /** @type EE_Payment_Processor $payment_processor */
453
                            $payment_processor = EE_Registry::instance()->load_core('Payment_Processor');
454
                            // let's simulate an IPN here which will trigger any notifications that need to go out
455
                            $payment_processor->update_txn_based_on_payment(
456
                                $transaction,
457
                                $transaction->last_payment(),
458
                                true,
459
                                true
460
                            );
461
                        }
462
                        break;
463
                    // Failed TXNs
464
                    case EEM_Transaction::failed_status_code:
465
                        do_action(
466
                            'AHEE__EE_Cron_Tasks__process_expired_transactions__failed_transaction',
467
                            $transaction
468
                        );
469
                        // todo :
470
                        // perform garbage collection here and remove clean_out_junk_transactions()
471
                        // $registrations = $transaction->registrations();
472
                        // if (! empty($registrations)) {
473
                        //     foreach ($registrations as $registration) {
474
                        //         if ($registration instanceof EE_Registration) {
475
                        //             $delete_registration = true;
476
                        //             if ($registration->attendee() instanceof EE_Attendee) {
477
                        //                 $delete_registration = false;
478
                        //             }
479
                        //             if ($delete_registration) {
480
                        //                 $registration->delete_permanently();
481
                        //                 $registration->delete_related_permanently();
482
                        //             }
483
                        //         }
484
                        //     }
485
                        // }
486
                        break;
487
                }
488
            }
489
            unset(self::$_expired_transactions[ $TXN_ID ]);
490
        }
491
    }
492
493
494
495
    /*************  END OF EXPIRED TRANSACTION CHECK  *************/
496
497
498
    /************* START CLEAN UP BOT TRANSACTIONS **********************/
499
500
501
    /**
502
     * callback for 'AHEE__EE_Cron_Tasks__clean_up_junk_transactions'
503
     * which is setup during activation to run on an hourly cron
504
     *
505
     * @throws EE_Error
506
     * @throws InvalidArgumentException
507
     * @throws InvalidDataTypeException
508
     * @throws InvalidInterfaceException
509
     * @throws DomainException
510
     */
511
    public static function clean_out_junk_transactions()
512
    {
513
        if (EE_Maintenance_Mode::instance()->models_can_query()) {
514
            EED_Ticket_Sales_Monitor::reset_reservation_counts();
515
            EEM_Transaction::instance('')->delete_junk_transactions();
516
            EEM_Registration::instance('')->delete_registrations_with_no_transaction();
517
            EEM_Line_Item::instance('')->delete_line_items_with_no_transaction();
518
        }
519
    }
520
521
522
    /**
523
     * Deletes old gateway logs. After about a week we usually don't need them for debugging. But folks can filter that.
524
     *
525
     * @throws EE_Error
526
     * @throws InvalidDataTypeException
527
     * @throws InvalidInterfaceException
528
     * @throws InvalidArgumentException
529
     */
530
    public static function clean_out_old_gateway_logs()
531
    {
532
        if (EE_Maintenance_Mode::instance()->models_can_query()) {
533
            $reg_config = LoaderFactory::getLoader()->load('EE_Registration_Config');
534
            $time_diff_for_comparison = apply_filters(
535
                'FHEE__EE_Cron_Tasks__clean_out_old_gateway_logs__time_diff_for_comparison',
536
                '-' . $reg_config->gateway_log_lifespan
537
            );
538
            EEM_Change_Log::instance()->delete_gateway_logs_older_than(new DateTime($time_diff_for_comparison));
539
        }
540
    }
541
542
543
    /*****************  FINALIZE ABANDONED TRANSACTIONS *****************/
544
545
546
    /**
547
     * @var array
548
     */
549
    protected static $_abandoned_transactions = array();
550
551
552
    /**
553
     * @deprecated
554
     * @param int $timestamp
555
     * @param int $TXN_ID
556
     */
557
    public static function schedule_finalize_abandoned_transactions_check($timestamp, $TXN_ID)
558
    {
559
        EE_Cron_Tasks::schedule_expired_transaction_check($timestamp, $TXN_ID);
560
    }
561
562
563
    /**
564
     * @deprecated
565
     * @param int $TXN_ID
566
     */
567
    public static function check_for_abandoned_transactions($TXN_ID = 0)
568
    {
569
        EE_Cron_Tasks::expired_transaction_check($TXN_ID);
570
    }
571
572
573
    /**
574
     * @deprecated
575
     * @throws EE_Error
576
     * @throws DomainException
577
     * @throws InvalidDataTypeException
578
     * @throws InvalidInterfaceException
579
     * @throws InvalidArgumentException
580
     * @throws ReflectionException
581
     * @throws RuntimeException
582
     */
583
    public static function finalize_abandoned_transactions()
584
    {
585
        do_action('AHEE_log', __CLASS__, __FUNCTION__);
586
        if (// are there any TXNs that need cleaning up ?
587
            empty(self::$_abandoned_transactions)
588
            // reschedule the cron if we can't hit the db right now
589
            || EE_Cron_Tasks::reschedule_cron_for_transactions_if_maintenance_mode(
590
                'schedule_expired_transaction_check',
591
                self::$_abandoned_transactions
592
            )
593
        ) {
594
            return;
595
        }
596
        // combine our arrays of transaction IDs
597
        self::$_expired_transactions = self::$_abandoned_transactions + self::$_expired_transactions;
598
        // and deal with abandoned transactions here now...
599
        EE_Cron_Tasks::process_expired_transactions();
600
    }
601
602
603
    /*************  END OF FINALIZE ABANDONED TRANSACTIONS  *************/
604
}
605