Completed
Branch FET-Wait-List (1a4c1b)
by
unknown
164:09 queued 153:47
created

EE_Cron_Tasks   C

Complexity

Total Complexity 57

Size/Duplication

Total Lines 584
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 13

Importance

Changes 0
Metric Value
dl 0
loc 584
rs 6.433
c 0
b 0
f 0
wmc 57
lcom 2
cbo 13

15 Methods

Rating   Name   Duplication   Size   Complexity  
A instance() 0 7 2
B __construct() 0 58 5
C log_scheduled_ee_crons() 0 30 8
B reschedule_cron_for_transactions_if_maintenance_mode() 0 31 5
A schedule_update_transaction_with_payment() 0 17 3
A setup_update_for_transaction_with_payment() 0 12 2
C update_transaction_with_payment() 0 41 7
A schedule_expired_transaction_check() 0 15 3
A expired_transaction_check() 0 11 2
C process_expired_transactions() 0 95 11
A clean_out_junk_transactions() 0 8 2
A clean_out_old_gateway_logs() 0 10 2
A schedule_finalize_abandoned_transactions_check() 0 4 1
A check_for_abandoned_transactions() 0 4 1
A finalize_abandoned_transactions() 0 19 3

How to fix   Complexity   

Complex Class

Complex classes like EE_Cron_Tasks often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use EE_Cron_Tasks, and based on these observations, apply Extract Interface, too.

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