Completed
Branch yet-another-batch-of-ui-fixes (371f49)
by
unknown
30:27 queued 22:46
created
core/db_classes/EE_Message.class.php 2 patches
Spacing   +10 added lines, -10 removed lines patch added patch discarded remove patch
@@ -51,7 +51,7 @@  discard block
 block discarded – undo
51 51
     {
52 52
         $has_object = parent::_check_for_object($props_n_values, __CLASS__);
53 53
         // if object doesn't exist, let's generate a unique token on instantiation so that its available even before saving to db.
54
-        if (! $has_object) {
54
+        if ( ! $has_object) {
55 55
             EE_Registry::instance()->load_helper('URL');
56 56
             $props_n_values['MSG_token'] = EEH_URL::generate_unique_token();
57 57
         }
@@ -219,7 +219,7 @@  discard block
 block discarded – undo
219 219
     {
220 220
         $label_type = $plural ? 'plural' : 'singular';
221 221
         $messenger = $this->messenger_object();
222
-        return $messenger instanceof EE_messenger ? $messenger->label[ $label_type ] : $this->messenger();
222
+        return $messenger instanceof EE_messenger ? $messenger->label[$label_type] : $this->messenger();
223 223
     }
224 224
 
225 225
 
@@ -336,7 +336,7 @@  discard block
 block discarded – undo
336 336
                 $this->messenger(),
337 337
                 $this->message_type()
338 338
             );
339
-            if (! $valid && $throw_exceptions) {
339
+            if ( ! $valid && $throw_exceptions) {
340 340
                 throw new EE_Error(
341 341
                     sprintf(
342 342
                         esc_html__(
@@ -366,7 +366,7 @@  discard block
 block discarded – undo
366 366
         $label_type = $plural ? 'plural' : 'singular';
367 367
         $message_type = $this->message_type_object();
368 368
         return $message_type instanceof EE_message_type
369
-            ? $message_type->label[ $label_type ]
369
+            ? $message_type->label[$label_type]
370 370
             : str_replace(
371 371
                 '_',
372 372
                 ' ',
@@ -397,7 +397,7 @@  discard block
 block discarded – undo
397 397
         /** @type EE_Message_Resource_Manager $message_resource_manager */
398 398
         $message_resource_manager = EE_Registry::instance()->load_lib('Message_Resource_Manager');
399 399
         $contexts = $message_resource_manager->get_all_contexts();
400
-        return isset($contexts[ $this->context() ]) ? $contexts[ $this->context() ] : $this->context();
400
+        return isset($contexts[$this->context()]) ? $contexts[$this->context()] : $this->context();
401 401
     }
402 402
 
403 403
 
@@ -452,7 +452,7 @@  discard block
 block discarded – undo
452 452
      */
453 453
     public function recipient_object()
454 454
     {
455
-        if (! $this->recipient_type() || ! $this->recipient_ID()) {
455
+        if ( ! $this->recipient_type() || ! $this->recipient_ID()) {
456 456
             return null;
457 457
         }
458 458
 
@@ -736,13 +736,13 @@  discard block
 block discarded – undo
736 736
         /**
737 737
          * This is deprecated functionality that will be removed eventually but included here now for backward compat.
738 738
          */
739
-        if (! empty($this->template_pack)) {
739
+        if ( ! empty($this->template_pack)) {
740 740
             return $this->template_pack;
741 741
         }
742 742
         /** @type EE_Message_Template_Group $grp */
743 743
         $grp = $this->get_first_related('Message_Template_Group');
744 744
         // if no group then let's try to get the first related group by internal messenger and message type (will use global grp).
745
-        if (! $grp instanceof EE_Message_Template_Group) {
745
+        if ( ! $grp instanceof EE_Message_Template_Group) {
746 746
             $grp = EEM_Message_Template_Group::instance()->get_one(
747 747
                 array(
748 748
                     array(
@@ -768,7 +768,7 @@  discard block
 block discarded – undo
768 768
         /**
769 769
          * This is deprecated functionality that will be removed eventually but included here now for backward compat.
770 770
          */
771
-        if (! empty($this->template_variation)) {
771
+        if ( ! empty($this->template_variation)) {
772 772
             return $this->template_variation;
773 773
         }
774 774
 
@@ -776,7 +776,7 @@  discard block
 block discarded – undo
776 776
         $grp = $this->get_first_related('Message_Template_Group');
777 777
 
778 778
         // if no group then let's try to get the first related group by internal messenger and message type (will use global grp).
779
-        if (! $grp instanceof EE_Message_Template_Group) {
779
+        if ( ! $grp instanceof EE_Message_Template_Group) {
780 780
             $grp = EEM_Message_Template_Group::instance()->get_one(
781 781
                 array(
782 782
                     array(
Please login to merge, or discard this patch.
Indentation   +867 added lines, -867 removed lines patch added patch discarded remove patch
@@ -9,875 +9,875 @@
 block discarded – undo
9 9
  */
10 10
 class EE_Message extends EE_Base_Class implements EEI_Admin_Links
11 11
 {
12
-    /**
13
-     * @deprecated 4.9.0  Added for backward compat with add-on's
14
-     * @type null
15
-     */
16
-    public $template_pack;
17
-
18
-    /**
19
-     * @deprecated 4.9.0 Added for backward compat with add-on's
20
-     * @type null
21
-     */
22
-    public $template_variation;
23
-
24
-    /**
25
-     * @deprecated 4.9.0 Added for backward compat with add-on's
26
-     * @type string
27
-     */
28
-    public $content = '';
29
-
30
-
31
-    /**
32
-     * @type EE_messenger $_messenger
33
-     */
34
-    protected $_messenger = null;
35
-
36
-    /**
37
-     * @type EE_message_type $_message_type
38
-     */
39
-    protected $_message_type = null;
40
-
41
-
42
-    /**
43
-     * @param array  $props_n_values
44
-     * @param string $timezone
45
-     * @param array  $date_formats incoming date formats in an array.  First value is the date_format, second is time
46
-     *                             format.
47
-     * @return EE_Message
48
-     */
49
-    public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
50
-    {
51
-        $has_object = parent::_check_for_object($props_n_values, __CLASS__);
52
-        // if object doesn't exist, let's generate a unique token on instantiation so that its available even before saving to db.
53
-        if (! $has_object) {
54
-            EE_Registry::instance()->load_helper('URL');
55
-            $props_n_values['MSG_token'] = EEH_URL::generate_unique_token();
56
-        }
57
-        return $has_object ? $has_object : new self($props_n_values, false, $timezone, $date_formats);
58
-    }
59
-
60
-
61
-    /**
62
-     * @param array  $props_n_values
63
-     * @param string $timezone
64
-     * @return EE_Message
65
-     */
66
-    public static function new_instance_from_db($props_n_values = array(), $timezone = null)
67
-    {
68
-        return new self($props_n_values, true, $timezone);
69
-    }
70
-
71
-
72
-    /**
73
-     * Gets MSG_token
74
-     *
75
-     * @return int
76
-     */
77
-    public function MSG_token()
78
-    {
79
-        return $this->get('MSG_token');
80
-    }
81
-
82
-
83
-    /**
84
-     * Sets MSG_token
85
-     *
86
-     * @param int $MSG_token
87
-     */
88
-    public function set_MSG_token($MSG_token)
89
-    {
90
-        $this->set('MSG_token', $MSG_token);
91
-    }
92
-
93
-
94
-    /**
95
-     * Gets GRP_ID
96
-     *
97
-     * @return int
98
-     */
99
-    public function GRP_ID()
100
-    {
101
-        return $this->get('GRP_ID');
102
-    }
103
-
104
-
105
-    /**
106
-     * Sets GRP_ID
107
-     *
108
-     * @param int $GRP_ID
109
-     */
110
-    public function set_GRP_ID($GRP_ID)
111
-    {
112
-        $this->set('GRP_ID', $GRP_ID);
113
-    }
114
-
115
-
116
-    /**
117
-     * Gets TXN_ID
118
-     *
119
-     * @return int
120
-     */
121
-    public function TXN_ID()
122
-    {
123
-        return $this->get('TXN_ID');
124
-    }
125
-
126
-
127
-    /**
128
-     * Sets TXN_ID
129
-     *
130
-     * @param int $TXN_ID
131
-     */
132
-    public function set_TXN_ID($TXN_ID)
133
-    {
134
-        $this->set('TXN_ID', $TXN_ID);
135
-    }
136
-
137
-
138
-    /**
139
-     * Gets messenger
140
-     *
141
-     * @return string
142
-     */
143
-    public function messenger()
144
-    {
145
-        return $this->get('MSG_messenger');
146
-    }
147
-
148
-
149
-    /**
150
-     * Sets messenger
151
-     *
152
-     * @param string $messenger
153
-     */
154
-    public function set_messenger($messenger)
155
-    {
156
-        $this->set('MSG_messenger', $messenger);
157
-    }
158
-
159
-
160
-    /**
161
-     * Returns corresponding messenger object for the set messenger on this message
162
-     *
163
-     * @return EE_messenger | null
164
-     */
165
-    public function messenger_object()
166
-    {
167
-        return $this->_messenger;
168
-    }
169
-
170
-
171
-    /**
172
-     * Sets messenger
173
-     *
174
-     * @param EE_messenger $messenger
175
-     */
176
-    public function set_messenger_object(EE_messenger $messenger)
177
-    {
178
-        $this->_messenger = $messenger;
179
-    }
180
-
181
-
182
-    /**
183
-     * validates messenger
184
-     *
185
-     * @param bool $throw_exceptions
186
-     * @return bool
187
-     * @throws \EE_Error
188
-     */
189
-    public function valid_messenger($throw_exceptions = false)
190
-    {
191
-        if ($this->_messenger instanceof EE_messenger) {
192
-            return true;
193
-        }
194
-        if ($throw_exceptions) {
195
-            throw new EE_Error(
196
-                sprintf(
197
-                    esc_html__(
198
-                        'The "%1$s" messenger set for this message is missing or invalid. Please double-check the spelling and verify that the correct files exist.',
199
-                        'event_espresso'
200
-                    ),
201
-                    $this->messenger()
202
-                )
203
-            );
204
-        }
205
-        return false;
206
-    }
207
-
208
-
209
-    /**
210
-     * This returns the set localized label for the messenger on this message.
211
-     * Note, if unable to retrieve the EE_messenger object then will just return the messenger slug saved
212
-     * with this message.
213
-     *
214
-     * @param   bool $plural whether to return the plural label or not.
215
-     * @return string
216
-     */
217
-    public function messenger_label($plural = false)
218
-    {
219
-        $label_type = $plural ? 'plural' : 'singular';
220
-        $messenger = $this->messenger_object();
221
-        return $messenger instanceof EE_messenger ? $messenger->label[ $label_type ] : $this->messenger();
222
-    }
223
-
224
-
225
-    /**
226
-     * Gets message_type
227
-     *
228
-     * @return string
229
-     */
230
-    public function message_type()
231
-    {
232
-        return $this->get('MSG_message_type');
233
-    }
234
-
235
-
236
-    /**
237
-     * Sets message_type
238
-     *
239
-     * @param string $message_type
240
-     */
241
-    public function set_message_type($message_type)
242
-    {
243
-        $this->set('MSG_message_type', $message_type);
244
-    }
245
-
246
-
247
-    /**
248
-     * Returns the message type object for the set message type on this message
249
-     *
250
-     * @return EE_message_type | null
251
-     */
252
-    public function message_type_object()
253
-    {
254
-        return $this->_message_type;
255
-    }
256
-
257
-
258
-    /**
259
-     * Sets message_type
260
-     *
261
-     * @param EE_message_type $message_type
262
-     * @param bool            $set_priority   This indicates whether to set the priority to whatever the priority is on
263
-     *                                        the message type or not.
264
-     */
265
-    public function set_message_type_object(EE_message_type $message_type, $set_priority = false)
266
-    {
267
-        $this->_message_type = $message_type;
268
-        if ($set_priority) {
269
-            $this->set_priority($this->_message_type->get_priority());
270
-        }
271
-    }
272
-
273
-
274
-    /**
275
-     * validates message_type
276
-     *
277
-     * @param bool $throw_exceptions
278
-     * @return bool
279
-     * @throws \EE_Error
280
-     */
281
-    public function valid_message_type($throw_exceptions = false)
282
-    {
283
-        if ($this->_message_type instanceof EE_message_type) {
284
-            return true;
285
-        }
286
-        if ($throw_exceptions) {
287
-            throw new EE_Error(
288
-                sprintf(
289
-                    esc_html__(
290
-                        'The %1$s message type set for this message is missing or invalid. Please double-check the spelling and verify that the correct files exist.',
291
-                        'event_espresso'
292
-                    ),
293
-                    $this->message_type()
294
-                )
295
-            );
296
-        }
297
-        return false;
298
-    }
299
-
300
-
301
-    /**
302
-     * validates messenger and message_type (that they are valid EE_messenger and EE_message_type objects).
303
-     *
304
-     * @param bool $throw_exceptions
305
-     * @return bool
306
-     * @throws \EE_Error
307
-     */
308
-    public function is_valid($throw_exceptions = false)
309
-    {
310
-        if ($this->valid_messenger($throw_exceptions) && $this->valid_message_type($throw_exceptions)) {
311
-            return true;
312
-        }
313
-        return false;
314
-    }
315
-
316
-
317
-    /**
318
-     * This validates whether the internal messenger and message type objects are valid for sending.
319
-     * Three checks are done:
320
-     * 1. There is a valid messenger object.
321
-     * 2. There is a valid message type object.
322
-     * 3. The message type object is active for the messenger.
323
-     *
324
-     * @throws EE_Error  But only if $throw_exceptions is set to true.
325
-     * @param bool $throw_exceptions
326
-     * @return bool
327
-     */
328
-    public function is_valid_for_sending_or_generation($throw_exceptions = false)
329
-    {
330
-        $valid = false;
331
-        if ($this->is_valid($throw_exceptions)) {
332
-            /** @var EE_Message_Resource_Manager $message_resource_manager */
333
-            $message_resource_manager = EE_Registry::instance()->load_lib('Message_Resource_Manager');
334
-            $valid = $message_resource_manager->is_message_type_active_for_messenger(
335
-                $this->messenger(),
336
-                $this->message_type()
337
-            );
338
-            if (! $valid && $throw_exceptions) {
339
-                throw new EE_Error(
340
-                    sprintf(
341
-                        esc_html__(
342
-                            'The %1$s message type is not a valid message type for the %2$s messenger so it will not be sent.',
343
-                            'event_espresso'
344
-                        ),
345
-                        $this->message_type(),
346
-                        $this->messenger()
347
-                    )
348
-                );
349
-            }
350
-        }
351
-        return $valid;
352
-    }
353
-
354
-
355
-    /**
356
-     * This returns the set localized label for the message type on this message.
357
-     * Note, if unable to retrieve the EE_message_type object then will just return the message type slug saved
358
-     * with this message.
359
-     *
360
-     * @param   bool $plural whether to return the plural label or not.
361
-     * @return string
362
-     */
363
-    public function message_type_label($plural = false)
364
-    {
365
-        $label_type = $plural ? 'plural' : 'singular';
366
-        $message_type = $this->message_type_object();
367
-        return $message_type instanceof EE_message_type
368
-            ? $message_type->label[ $label_type ]
369
-            : str_replace(
370
-                '_',
371
-                ' ',
372
-                $this->message_type()
373
-            );
374
-    }
375
-
376
-
377
-    /**
378
-     * Gets context
379
-     *
380
-     * @return string
381
-     */
382
-    public function context()
383
-    {
384
-        return $this->get('MSG_context');
385
-    }
386
-
387
-
388
-    /**
389
-     * This returns the corresponding localized label for the given context slug, if possible from installed message
390
-     * types. Otherwise, this will just return the set context slug on this object.
391
-     *
392
-     * @return string
393
-     */
394
-    public function context_label()
395
-    {
396
-        /** @type EE_Message_Resource_Manager $message_resource_manager */
397
-        $message_resource_manager = EE_Registry::instance()->load_lib('Message_Resource_Manager');
398
-        $contexts = $message_resource_manager->get_all_contexts();
399
-        return isset($contexts[ $this->context() ]) ? $contexts[ $this->context() ] : $this->context();
400
-    }
401
-
402
-
403
-    /**
404
-     * Sets context
405
-     *
406
-     * @param string $context
407
-     */
408
-    public function set_context($context)
409
-    {
410
-        $this->set('MSG_context', $context);
411
-    }
412
-
413
-
414
-    /**
415
-     * Gets recipient_ID
416
-     *
417
-     * @return int
418
-     */
419
-    public function recipient_ID()
420
-    {
421
-        return $this->get('MSG_recipient_ID');
422
-    }
423
-
424
-
425
-    /**
426
-     * Sets recipient_ID
427
-     *
428
-     * @param string $recipient_ID
429
-     */
430
-    public function set_recipient_ID($recipient_ID)
431
-    {
432
-        $this->set('MSG_recipient_ID', $recipient_ID);
433
-    }
434
-
435
-
436
-    /**
437
-     * Gets recipient_type
438
-     *
439
-     * @return string
440
-     */
441
-    public function recipient_type()
442
-    {
443
-        return $this->get('MSG_recipient_type');
444
-    }
445
-
446
-
447
-    /**
448
-     * Return the related object matching the recipient type and ID.
449
-     *
450
-     * @return EE_Base_Class | null
451
-     */
452
-    public function recipient_object()
453
-    {
454
-        if (! $this->recipient_type() || ! $this->recipient_ID()) {
455
-            return null;
456
-        }
457
-
458
-        return $this->get_first_related($this->recipient_type());
459
-    }
460
-
461
-
462
-    /**
463
-     * Sets recipient_type
464
-     *
465
-     * @param string $recipient_type
466
-     */
467
-    public function set_recipient_type($recipient_type)
468
-    {
469
-        $this->set('MSG_recipient_type', $recipient_type);
470
-    }
471
-
472
-
473
-    /**
474
-     * Gets content
475
-     *
476
-     * @return string
477
-     */
478
-    public function content()
479
-    {
480
-        return $this->get('MSG_content');
481
-    }
482
-
483
-
484
-    /**
485
-     * Sets content
486
-     *
487
-     * @param string $content
488
-     */
489
-    public function set_content($content)
490
-    {
491
-        $this->set('MSG_content', $content);
492
-    }
493
-
494
-
495
-    /**
496
-     * Gets subject
497
-     *
498
-     * @return string
499
-     */
500
-    public function subject()
501
-    {
502
-        return $this->get('MSG_subject');
503
-    }
504
-
505
-
506
-    /**
507
-     * Sets subject
508
-     *
509
-     * @param string $subject
510
-     */
511
-    public function set_subject($subject)
512
-    {
513
-        $this->set('MSG_subject', $subject);
514
-    }
515
-
516
-
517
-    /**
518
-     * Gets to
519
-     *
520
-     * @return string
521
-     */
522
-    public function to()
523
-    {
524
-        $to = $this->get('MSG_to');
525
-        return empty($to) ? esc_html__('No recipient', 'event_espresso') : $to;
526
-    }
527
-
528
-
529
-    /**
530
-     * Sets to
531
-     *
532
-     * @param string $to
533
-     */
534
-    public function set_to($to)
535
-    {
536
-        $this->set('MSG_to', $to);
537
-    }
538
-
539
-
540
-    /**
541
-     * Gets from
542
-     *
543
-     * @return string
544
-     */
545
-    public function from()
546
-    {
547
-        return $this->get('MSG_from');
548
-    }
549
-
550
-
551
-    /**
552
-     * Sets from
553
-     *
554
-     * @param string $from
555
-     */
556
-    public function set_from($from)
557
-    {
558
-        $this->set('MSG_from', $from);
559
-    }
560
-
561
-
562
-    /**
563
-     * Gets priority
564
-     *
565
-     * @return int
566
-     */
567
-    public function priority()
568
-    {
569
-        return $this->get('MSG_priority');
570
-    }
571
-
572
-
573
-    /**
574
-     * Sets priority
575
-     * Note.  Send Now Messengers always override any priority that may be set on a Message.  So
576
-     * this method calls the send_now method to verify that.
577
-     *
578
-     * @param int $priority
579
-     */
580
-    public function set_priority($priority)
581
-    {
582
-        $priority = $this->send_now() ? EEM_Message::priority_high : $priority;
583
-        parent::set('MSG_priority', $priority);
584
-    }
585
-
586
-
587
-    /**
588
-     * Overrides parent::set method so we can capture any sets for priority.
589
-     *
590
-     * @see parent::set() for phpdocs
591
-     * @param string $field_name
592
-     * @param mixed  $field_value
593
-     * @param bool   $use_default
594
-     * @throws EE_Error
595
-     */
596
-    public function set($field_name, $field_value, $use_default = false)
597
-    {
598
-        if ($field_name === 'MSG_priority') {
599
-            $this->set_priority($field_value);
600
-        }
601
-        parent::set($field_name, $field_value, $use_default);
602
-    }
603
-
604
-
605
-    /**
606
-     * @return bool
607
-     * @throws \EE_Error
608
-     */
609
-    public function send_now()
610
-    {
611
-        $send_now = $this->valid_messenger() && $this->messenger_object()->send_now() ? EEM_Message::priority_high
612
-            : $this->priority();
613
-        return $send_now === EEM_Message::priority_high ? true : false;
614
-    }
615
-
616
-
617
-    /**
618
-     * Gets STS_ID
619
-     *
620
-     * @return string
621
-     */
622
-    public function STS_ID()
623
-    {
624
-        return $this->get('STS_ID');
625
-    }
626
-
627
-
628
-    /**
629
-     * Sets STS_ID
630
-     *
631
-     * @param string $STS_ID
632
-     */
633
-    public function set_STS_ID($STS_ID)
634
-    {
635
-        $this->set('STS_ID', $STS_ID);
636
-    }
637
-
638
-
639
-    /**
640
-     * Gets created
641
-     *
642
-     * @return string
643
-     */
644
-    public function created()
645
-    {
646
-        return $this->get('MSG_created');
647
-    }
648
-
649
-
650
-    /**
651
-     * Sets created
652
-     *
653
-     * @param string $created
654
-     */
655
-    public function set_created($created)
656
-    {
657
-        $this->set('MSG_created', $created);
658
-    }
659
-
660
-
661
-    /**
662
-     * Gets modified
663
-     *
664
-     * @return string
665
-     */
666
-    public function modified()
667
-    {
668
-        return $this->get('MSG_modified');
669
-    }
670
-
671
-
672
-    /**
673
-     * Sets modified
674
-     *
675
-     * @param string $modified
676
-     */
677
-    public function set_modified($modified)
678
-    {
679
-        $this->set('MSG_modified', $modified);
680
-    }
681
-
682
-
683
-    /**
684
-     * Sets generation data for this message.
685
-     *
686
-     * @param mixed $data
687
-     */
688
-    public function set_generation_data($data)
689
-    {
690
-        $this->set_field_or_extra_meta('MSG_generation_data', $data);
691
-    }
692
-
693
-
694
-    /**
695
-     * Returns any set generation data for this message.
696
-     *
697
-     * @return mixed|null
698
-     */
699
-    public function get_generation_data()
700
-    {
701
-        return $this->get_field_or_extra_meta('MSG_generation_data');
702
-    }
703
-
704
-
705
-    /**
706
-     * Gets any error message.
707
-     *
708
-     * @return mixed|null
709
-     */
710
-    public function error_message()
711
-    {
712
-        return $this->get_field_or_extra_meta('MSG_error');
713
-    }
714
-
715
-
716
-    /**
717
-     * Sets an error message.
718
-     *
719
-     * @param $message
720
-     * @return bool|int
721
-     */
722
-    public function set_error_message($message)
723
-    {
724
-        return $this->set_field_or_extra_meta('MSG_error', $message);
725
-    }
726
-
727
-
728
-    /**
729
-     * This retrieves the associated template pack with this message.
730
-     *
731
-     * @return EE_Messages_Template_Pack | null
732
-     */
733
-    public function get_template_pack()
734
-    {
735
-        /**
736
-         * This is deprecated functionality that will be removed eventually but included here now for backward compat.
737
-         */
738
-        if (! empty($this->template_pack)) {
739
-            return $this->template_pack;
740
-        }
741
-        /** @type EE_Message_Template_Group $grp */
742
-        $grp = $this->get_first_related('Message_Template_Group');
743
-        // if no group then let's try to get the first related group by internal messenger and message type (will use global grp).
744
-        if (! $grp instanceof EE_Message_Template_Group) {
745
-            $grp = EEM_Message_Template_Group::instance()->get_one(
746
-                array(
747
-                    array(
748
-                        'MTP_messenger'    => $this->messenger(),
749
-                        'MTP_message_type' => $this->message_type(),
750
-                        'MTP_is_global'    => true,
751
-                    ),
752
-                )
753
-            );
754
-        }
755
-
756
-        return $grp instanceof EE_Message_Template_Group ? $grp->get_template_pack() : null;
757
-    }
758
-
759
-
760
-    /**
761
-     * Retrieves the variation used for generating this message.
762
-     *
763
-     * @return string
764
-     */
765
-    public function get_template_pack_variation()
766
-    {
767
-        /**
768
-         * This is deprecated functionality that will be removed eventually but included here now for backward compat.
769
-         */
770
-        if (! empty($this->template_variation)) {
771
-            return $this->template_variation;
772
-        }
773
-
774
-        /** @type EE_Message_Template_Group $grp */
775
-        $grp = $this->get_first_related('Message_Template_Group');
776
-
777
-        // if no group then let's try to get the first related group by internal messenger and message type (will use global grp).
778
-        if (! $grp instanceof EE_Message_Template_Group) {
779
-            $grp = EEM_Message_Template_Group::instance()->get_one(
780
-                array(
781
-                    array(
782
-                        'MTP_messenger'    => $this->messenger(),
783
-                        'MTP_message_type' => $this->message_type(),
784
-                        'MTP_is_global'    => true,
785
-                    ),
786
-                )
787
-            );
788
-        }
789
-
790
-        return $grp instanceof EE_Message_Template_Group ? $grp->get_template_pack_variation() : '';
791
-    }
792
-
793
-    /**
794
-     * Return the link to the admin details for the object.
795
-     *
796
-     * @return string
797
-     */
798
-    public function get_admin_details_link()
799
-    {
800
-        EE_Registry::instance()->load_helper('URL');
801
-        EE_Registry::instance()->load_helper('MSG_Template');
802
-        switch ($this->STS_ID()) {
803
-            case EEM_Message::status_failed:
804
-            case EEM_Message::status_debug_only:
805
-                return EEH_MSG_Template::generate_error_display_trigger($this);
806
-                break;
807
-
808
-            case EEM_Message::status_sent:
809
-                return EEH_MSG_Template::generate_browser_trigger($this);
810
-                break;
811
-
812
-            default:
813
-                return '';
814
-        }
815
-    }
816
-
817
-    /**
818
-     * Returns the link to the editor for the object.  Sometimes this is the same as the details.
819
-     *
820
-     * @return string
821
-     */
822
-    public function get_admin_edit_link()
823
-    {
824
-        return $this->get_admin_details_link();
825
-    }
826
-
827
-    /**
828
-     * Returns the link to a settings page for the object.
829
-     *
830
-     * @return string
831
-     */
832
-    public function get_admin_settings_link()
833
-    {
834
-        EE_Registry::instance()->load_helper('URL');
835
-        return EEH_URL::add_query_args_and_nonce(
836
-            array(
837
-                'page'   => 'espresso_messages',
838
-                'action' => 'settings',
839
-            ),
840
-            admin_url('admin.php')
841
-        );
842
-    }
843
-
844
-    /**
845
-     * Returns the link to the "overview" for the object (typically the "list table" view).
846
-     *
847
-     * @return string
848
-     */
849
-    public function get_admin_overview_link()
850
-    {
851
-        EE_Registry::instance()->load_helper('URL');
852
-        return EEH_URL::add_query_args_and_nonce(
853
-            array(
854
-                'page'   => 'espresso_messages',
855
-                'action' => 'default',
856
-            ),
857
-            admin_url('admin.php')
858
-        );
859
-    }
860
-
861
-
862
-    /**
863
-     * This sets the EEM_Message::status_messenger_executing class on the message and the appropriate error message for
864
-     * it.
865
-     * Note this also SAVES the current message object to the db because it adds an error message to accompany the
866
-     * status.
867
-     *
868
-     */
869
-    public function set_messenger_is_executing()
870
-    {
871
-        $this->set_STS_ID(EEM_Message::status_messenger_executing);
872
-        $this->set_error_message(
873
-            esc_html__(
874
-                'A message with this status indicates that there was a problem that occurred while the message was being
12
+	/**
13
+	 * @deprecated 4.9.0  Added for backward compat with add-on's
14
+	 * @type null
15
+	 */
16
+	public $template_pack;
17
+
18
+	/**
19
+	 * @deprecated 4.9.0 Added for backward compat with add-on's
20
+	 * @type null
21
+	 */
22
+	public $template_variation;
23
+
24
+	/**
25
+	 * @deprecated 4.9.0 Added for backward compat with add-on's
26
+	 * @type string
27
+	 */
28
+	public $content = '';
29
+
30
+
31
+	/**
32
+	 * @type EE_messenger $_messenger
33
+	 */
34
+	protected $_messenger = null;
35
+
36
+	/**
37
+	 * @type EE_message_type $_message_type
38
+	 */
39
+	protected $_message_type = null;
40
+
41
+
42
+	/**
43
+	 * @param array  $props_n_values
44
+	 * @param string $timezone
45
+	 * @param array  $date_formats incoming date formats in an array.  First value is the date_format, second is time
46
+	 *                             format.
47
+	 * @return EE_Message
48
+	 */
49
+	public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
50
+	{
51
+		$has_object = parent::_check_for_object($props_n_values, __CLASS__);
52
+		// if object doesn't exist, let's generate a unique token on instantiation so that its available even before saving to db.
53
+		if (! $has_object) {
54
+			EE_Registry::instance()->load_helper('URL');
55
+			$props_n_values['MSG_token'] = EEH_URL::generate_unique_token();
56
+		}
57
+		return $has_object ? $has_object : new self($props_n_values, false, $timezone, $date_formats);
58
+	}
59
+
60
+
61
+	/**
62
+	 * @param array  $props_n_values
63
+	 * @param string $timezone
64
+	 * @return EE_Message
65
+	 */
66
+	public static function new_instance_from_db($props_n_values = array(), $timezone = null)
67
+	{
68
+		return new self($props_n_values, true, $timezone);
69
+	}
70
+
71
+
72
+	/**
73
+	 * Gets MSG_token
74
+	 *
75
+	 * @return int
76
+	 */
77
+	public function MSG_token()
78
+	{
79
+		return $this->get('MSG_token');
80
+	}
81
+
82
+
83
+	/**
84
+	 * Sets MSG_token
85
+	 *
86
+	 * @param int $MSG_token
87
+	 */
88
+	public function set_MSG_token($MSG_token)
89
+	{
90
+		$this->set('MSG_token', $MSG_token);
91
+	}
92
+
93
+
94
+	/**
95
+	 * Gets GRP_ID
96
+	 *
97
+	 * @return int
98
+	 */
99
+	public function GRP_ID()
100
+	{
101
+		return $this->get('GRP_ID');
102
+	}
103
+
104
+
105
+	/**
106
+	 * Sets GRP_ID
107
+	 *
108
+	 * @param int $GRP_ID
109
+	 */
110
+	public function set_GRP_ID($GRP_ID)
111
+	{
112
+		$this->set('GRP_ID', $GRP_ID);
113
+	}
114
+
115
+
116
+	/**
117
+	 * Gets TXN_ID
118
+	 *
119
+	 * @return int
120
+	 */
121
+	public function TXN_ID()
122
+	{
123
+		return $this->get('TXN_ID');
124
+	}
125
+
126
+
127
+	/**
128
+	 * Sets TXN_ID
129
+	 *
130
+	 * @param int $TXN_ID
131
+	 */
132
+	public function set_TXN_ID($TXN_ID)
133
+	{
134
+		$this->set('TXN_ID', $TXN_ID);
135
+	}
136
+
137
+
138
+	/**
139
+	 * Gets messenger
140
+	 *
141
+	 * @return string
142
+	 */
143
+	public function messenger()
144
+	{
145
+		return $this->get('MSG_messenger');
146
+	}
147
+
148
+
149
+	/**
150
+	 * Sets messenger
151
+	 *
152
+	 * @param string $messenger
153
+	 */
154
+	public function set_messenger($messenger)
155
+	{
156
+		$this->set('MSG_messenger', $messenger);
157
+	}
158
+
159
+
160
+	/**
161
+	 * Returns corresponding messenger object for the set messenger on this message
162
+	 *
163
+	 * @return EE_messenger | null
164
+	 */
165
+	public function messenger_object()
166
+	{
167
+		return $this->_messenger;
168
+	}
169
+
170
+
171
+	/**
172
+	 * Sets messenger
173
+	 *
174
+	 * @param EE_messenger $messenger
175
+	 */
176
+	public function set_messenger_object(EE_messenger $messenger)
177
+	{
178
+		$this->_messenger = $messenger;
179
+	}
180
+
181
+
182
+	/**
183
+	 * validates messenger
184
+	 *
185
+	 * @param bool $throw_exceptions
186
+	 * @return bool
187
+	 * @throws \EE_Error
188
+	 */
189
+	public function valid_messenger($throw_exceptions = false)
190
+	{
191
+		if ($this->_messenger instanceof EE_messenger) {
192
+			return true;
193
+		}
194
+		if ($throw_exceptions) {
195
+			throw new EE_Error(
196
+				sprintf(
197
+					esc_html__(
198
+						'The "%1$s" messenger set for this message is missing or invalid. Please double-check the spelling and verify that the correct files exist.',
199
+						'event_espresso'
200
+					),
201
+					$this->messenger()
202
+				)
203
+			);
204
+		}
205
+		return false;
206
+	}
207
+
208
+
209
+	/**
210
+	 * This returns the set localized label for the messenger on this message.
211
+	 * Note, if unable to retrieve the EE_messenger object then will just return the messenger slug saved
212
+	 * with this message.
213
+	 *
214
+	 * @param   bool $plural whether to return the plural label or not.
215
+	 * @return string
216
+	 */
217
+	public function messenger_label($plural = false)
218
+	{
219
+		$label_type = $plural ? 'plural' : 'singular';
220
+		$messenger = $this->messenger_object();
221
+		return $messenger instanceof EE_messenger ? $messenger->label[ $label_type ] : $this->messenger();
222
+	}
223
+
224
+
225
+	/**
226
+	 * Gets message_type
227
+	 *
228
+	 * @return string
229
+	 */
230
+	public function message_type()
231
+	{
232
+		return $this->get('MSG_message_type');
233
+	}
234
+
235
+
236
+	/**
237
+	 * Sets message_type
238
+	 *
239
+	 * @param string $message_type
240
+	 */
241
+	public function set_message_type($message_type)
242
+	{
243
+		$this->set('MSG_message_type', $message_type);
244
+	}
245
+
246
+
247
+	/**
248
+	 * Returns the message type object for the set message type on this message
249
+	 *
250
+	 * @return EE_message_type | null
251
+	 */
252
+	public function message_type_object()
253
+	{
254
+		return $this->_message_type;
255
+	}
256
+
257
+
258
+	/**
259
+	 * Sets message_type
260
+	 *
261
+	 * @param EE_message_type $message_type
262
+	 * @param bool            $set_priority   This indicates whether to set the priority to whatever the priority is on
263
+	 *                                        the message type or not.
264
+	 */
265
+	public function set_message_type_object(EE_message_type $message_type, $set_priority = false)
266
+	{
267
+		$this->_message_type = $message_type;
268
+		if ($set_priority) {
269
+			$this->set_priority($this->_message_type->get_priority());
270
+		}
271
+	}
272
+
273
+
274
+	/**
275
+	 * validates message_type
276
+	 *
277
+	 * @param bool $throw_exceptions
278
+	 * @return bool
279
+	 * @throws \EE_Error
280
+	 */
281
+	public function valid_message_type($throw_exceptions = false)
282
+	{
283
+		if ($this->_message_type instanceof EE_message_type) {
284
+			return true;
285
+		}
286
+		if ($throw_exceptions) {
287
+			throw new EE_Error(
288
+				sprintf(
289
+					esc_html__(
290
+						'The %1$s message type set for this message is missing or invalid. Please double-check the spelling and verify that the correct files exist.',
291
+						'event_espresso'
292
+					),
293
+					$this->message_type()
294
+				)
295
+			);
296
+		}
297
+		return false;
298
+	}
299
+
300
+
301
+	/**
302
+	 * validates messenger and message_type (that they are valid EE_messenger and EE_message_type objects).
303
+	 *
304
+	 * @param bool $throw_exceptions
305
+	 * @return bool
306
+	 * @throws \EE_Error
307
+	 */
308
+	public function is_valid($throw_exceptions = false)
309
+	{
310
+		if ($this->valid_messenger($throw_exceptions) && $this->valid_message_type($throw_exceptions)) {
311
+			return true;
312
+		}
313
+		return false;
314
+	}
315
+
316
+
317
+	/**
318
+	 * This validates whether the internal messenger and message type objects are valid for sending.
319
+	 * Three checks are done:
320
+	 * 1. There is a valid messenger object.
321
+	 * 2. There is a valid message type object.
322
+	 * 3. The message type object is active for the messenger.
323
+	 *
324
+	 * @throws EE_Error  But only if $throw_exceptions is set to true.
325
+	 * @param bool $throw_exceptions
326
+	 * @return bool
327
+	 */
328
+	public function is_valid_for_sending_or_generation($throw_exceptions = false)
329
+	{
330
+		$valid = false;
331
+		if ($this->is_valid($throw_exceptions)) {
332
+			/** @var EE_Message_Resource_Manager $message_resource_manager */
333
+			$message_resource_manager = EE_Registry::instance()->load_lib('Message_Resource_Manager');
334
+			$valid = $message_resource_manager->is_message_type_active_for_messenger(
335
+				$this->messenger(),
336
+				$this->message_type()
337
+			);
338
+			if (! $valid && $throw_exceptions) {
339
+				throw new EE_Error(
340
+					sprintf(
341
+						esc_html__(
342
+							'The %1$s message type is not a valid message type for the %2$s messenger so it will not be sent.',
343
+							'event_espresso'
344
+						),
345
+						$this->message_type(),
346
+						$this->messenger()
347
+					)
348
+				);
349
+			}
350
+		}
351
+		return $valid;
352
+	}
353
+
354
+
355
+	/**
356
+	 * This returns the set localized label for the message type on this message.
357
+	 * Note, if unable to retrieve the EE_message_type object then will just return the message type slug saved
358
+	 * with this message.
359
+	 *
360
+	 * @param   bool $plural whether to return the plural label or not.
361
+	 * @return string
362
+	 */
363
+	public function message_type_label($plural = false)
364
+	{
365
+		$label_type = $plural ? 'plural' : 'singular';
366
+		$message_type = $this->message_type_object();
367
+		return $message_type instanceof EE_message_type
368
+			? $message_type->label[ $label_type ]
369
+			: str_replace(
370
+				'_',
371
+				' ',
372
+				$this->message_type()
373
+			);
374
+	}
375
+
376
+
377
+	/**
378
+	 * Gets context
379
+	 *
380
+	 * @return string
381
+	 */
382
+	public function context()
383
+	{
384
+		return $this->get('MSG_context');
385
+	}
386
+
387
+
388
+	/**
389
+	 * This returns the corresponding localized label for the given context slug, if possible from installed message
390
+	 * types. Otherwise, this will just return the set context slug on this object.
391
+	 *
392
+	 * @return string
393
+	 */
394
+	public function context_label()
395
+	{
396
+		/** @type EE_Message_Resource_Manager $message_resource_manager */
397
+		$message_resource_manager = EE_Registry::instance()->load_lib('Message_Resource_Manager');
398
+		$contexts = $message_resource_manager->get_all_contexts();
399
+		return isset($contexts[ $this->context() ]) ? $contexts[ $this->context() ] : $this->context();
400
+	}
401
+
402
+
403
+	/**
404
+	 * Sets context
405
+	 *
406
+	 * @param string $context
407
+	 */
408
+	public function set_context($context)
409
+	{
410
+		$this->set('MSG_context', $context);
411
+	}
412
+
413
+
414
+	/**
415
+	 * Gets recipient_ID
416
+	 *
417
+	 * @return int
418
+	 */
419
+	public function recipient_ID()
420
+	{
421
+		return $this->get('MSG_recipient_ID');
422
+	}
423
+
424
+
425
+	/**
426
+	 * Sets recipient_ID
427
+	 *
428
+	 * @param string $recipient_ID
429
+	 */
430
+	public function set_recipient_ID($recipient_ID)
431
+	{
432
+		$this->set('MSG_recipient_ID', $recipient_ID);
433
+	}
434
+
435
+
436
+	/**
437
+	 * Gets recipient_type
438
+	 *
439
+	 * @return string
440
+	 */
441
+	public function recipient_type()
442
+	{
443
+		return $this->get('MSG_recipient_type');
444
+	}
445
+
446
+
447
+	/**
448
+	 * Return the related object matching the recipient type and ID.
449
+	 *
450
+	 * @return EE_Base_Class | null
451
+	 */
452
+	public function recipient_object()
453
+	{
454
+		if (! $this->recipient_type() || ! $this->recipient_ID()) {
455
+			return null;
456
+		}
457
+
458
+		return $this->get_first_related($this->recipient_type());
459
+	}
460
+
461
+
462
+	/**
463
+	 * Sets recipient_type
464
+	 *
465
+	 * @param string $recipient_type
466
+	 */
467
+	public function set_recipient_type($recipient_type)
468
+	{
469
+		$this->set('MSG_recipient_type', $recipient_type);
470
+	}
471
+
472
+
473
+	/**
474
+	 * Gets content
475
+	 *
476
+	 * @return string
477
+	 */
478
+	public function content()
479
+	{
480
+		return $this->get('MSG_content');
481
+	}
482
+
483
+
484
+	/**
485
+	 * Sets content
486
+	 *
487
+	 * @param string $content
488
+	 */
489
+	public function set_content($content)
490
+	{
491
+		$this->set('MSG_content', $content);
492
+	}
493
+
494
+
495
+	/**
496
+	 * Gets subject
497
+	 *
498
+	 * @return string
499
+	 */
500
+	public function subject()
501
+	{
502
+		return $this->get('MSG_subject');
503
+	}
504
+
505
+
506
+	/**
507
+	 * Sets subject
508
+	 *
509
+	 * @param string $subject
510
+	 */
511
+	public function set_subject($subject)
512
+	{
513
+		$this->set('MSG_subject', $subject);
514
+	}
515
+
516
+
517
+	/**
518
+	 * Gets to
519
+	 *
520
+	 * @return string
521
+	 */
522
+	public function to()
523
+	{
524
+		$to = $this->get('MSG_to');
525
+		return empty($to) ? esc_html__('No recipient', 'event_espresso') : $to;
526
+	}
527
+
528
+
529
+	/**
530
+	 * Sets to
531
+	 *
532
+	 * @param string $to
533
+	 */
534
+	public function set_to($to)
535
+	{
536
+		$this->set('MSG_to', $to);
537
+	}
538
+
539
+
540
+	/**
541
+	 * Gets from
542
+	 *
543
+	 * @return string
544
+	 */
545
+	public function from()
546
+	{
547
+		return $this->get('MSG_from');
548
+	}
549
+
550
+
551
+	/**
552
+	 * Sets from
553
+	 *
554
+	 * @param string $from
555
+	 */
556
+	public function set_from($from)
557
+	{
558
+		$this->set('MSG_from', $from);
559
+	}
560
+
561
+
562
+	/**
563
+	 * Gets priority
564
+	 *
565
+	 * @return int
566
+	 */
567
+	public function priority()
568
+	{
569
+		return $this->get('MSG_priority');
570
+	}
571
+
572
+
573
+	/**
574
+	 * Sets priority
575
+	 * Note.  Send Now Messengers always override any priority that may be set on a Message.  So
576
+	 * this method calls the send_now method to verify that.
577
+	 *
578
+	 * @param int $priority
579
+	 */
580
+	public function set_priority($priority)
581
+	{
582
+		$priority = $this->send_now() ? EEM_Message::priority_high : $priority;
583
+		parent::set('MSG_priority', $priority);
584
+	}
585
+
586
+
587
+	/**
588
+	 * Overrides parent::set method so we can capture any sets for priority.
589
+	 *
590
+	 * @see parent::set() for phpdocs
591
+	 * @param string $field_name
592
+	 * @param mixed  $field_value
593
+	 * @param bool   $use_default
594
+	 * @throws EE_Error
595
+	 */
596
+	public function set($field_name, $field_value, $use_default = false)
597
+	{
598
+		if ($field_name === 'MSG_priority') {
599
+			$this->set_priority($field_value);
600
+		}
601
+		parent::set($field_name, $field_value, $use_default);
602
+	}
603
+
604
+
605
+	/**
606
+	 * @return bool
607
+	 * @throws \EE_Error
608
+	 */
609
+	public function send_now()
610
+	{
611
+		$send_now = $this->valid_messenger() && $this->messenger_object()->send_now() ? EEM_Message::priority_high
612
+			: $this->priority();
613
+		return $send_now === EEM_Message::priority_high ? true : false;
614
+	}
615
+
616
+
617
+	/**
618
+	 * Gets STS_ID
619
+	 *
620
+	 * @return string
621
+	 */
622
+	public function STS_ID()
623
+	{
624
+		return $this->get('STS_ID');
625
+	}
626
+
627
+
628
+	/**
629
+	 * Sets STS_ID
630
+	 *
631
+	 * @param string $STS_ID
632
+	 */
633
+	public function set_STS_ID($STS_ID)
634
+	{
635
+		$this->set('STS_ID', $STS_ID);
636
+	}
637
+
638
+
639
+	/**
640
+	 * Gets created
641
+	 *
642
+	 * @return string
643
+	 */
644
+	public function created()
645
+	{
646
+		return $this->get('MSG_created');
647
+	}
648
+
649
+
650
+	/**
651
+	 * Sets created
652
+	 *
653
+	 * @param string $created
654
+	 */
655
+	public function set_created($created)
656
+	{
657
+		$this->set('MSG_created', $created);
658
+	}
659
+
660
+
661
+	/**
662
+	 * Gets modified
663
+	 *
664
+	 * @return string
665
+	 */
666
+	public function modified()
667
+	{
668
+		return $this->get('MSG_modified');
669
+	}
670
+
671
+
672
+	/**
673
+	 * Sets modified
674
+	 *
675
+	 * @param string $modified
676
+	 */
677
+	public function set_modified($modified)
678
+	{
679
+		$this->set('MSG_modified', $modified);
680
+	}
681
+
682
+
683
+	/**
684
+	 * Sets generation data for this message.
685
+	 *
686
+	 * @param mixed $data
687
+	 */
688
+	public function set_generation_data($data)
689
+	{
690
+		$this->set_field_or_extra_meta('MSG_generation_data', $data);
691
+	}
692
+
693
+
694
+	/**
695
+	 * Returns any set generation data for this message.
696
+	 *
697
+	 * @return mixed|null
698
+	 */
699
+	public function get_generation_data()
700
+	{
701
+		return $this->get_field_or_extra_meta('MSG_generation_data');
702
+	}
703
+
704
+
705
+	/**
706
+	 * Gets any error message.
707
+	 *
708
+	 * @return mixed|null
709
+	 */
710
+	public function error_message()
711
+	{
712
+		return $this->get_field_or_extra_meta('MSG_error');
713
+	}
714
+
715
+
716
+	/**
717
+	 * Sets an error message.
718
+	 *
719
+	 * @param $message
720
+	 * @return bool|int
721
+	 */
722
+	public function set_error_message($message)
723
+	{
724
+		return $this->set_field_or_extra_meta('MSG_error', $message);
725
+	}
726
+
727
+
728
+	/**
729
+	 * This retrieves the associated template pack with this message.
730
+	 *
731
+	 * @return EE_Messages_Template_Pack | null
732
+	 */
733
+	public function get_template_pack()
734
+	{
735
+		/**
736
+		 * This is deprecated functionality that will be removed eventually but included here now for backward compat.
737
+		 */
738
+		if (! empty($this->template_pack)) {
739
+			return $this->template_pack;
740
+		}
741
+		/** @type EE_Message_Template_Group $grp */
742
+		$grp = $this->get_first_related('Message_Template_Group');
743
+		// if no group then let's try to get the first related group by internal messenger and message type (will use global grp).
744
+		if (! $grp instanceof EE_Message_Template_Group) {
745
+			$grp = EEM_Message_Template_Group::instance()->get_one(
746
+				array(
747
+					array(
748
+						'MTP_messenger'    => $this->messenger(),
749
+						'MTP_message_type' => $this->message_type(),
750
+						'MTP_is_global'    => true,
751
+					),
752
+				)
753
+			);
754
+		}
755
+
756
+		return $grp instanceof EE_Message_Template_Group ? $grp->get_template_pack() : null;
757
+	}
758
+
759
+
760
+	/**
761
+	 * Retrieves the variation used for generating this message.
762
+	 *
763
+	 * @return string
764
+	 */
765
+	public function get_template_pack_variation()
766
+	{
767
+		/**
768
+		 * This is deprecated functionality that will be removed eventually but included here now for backward compat.
769
+		 */
770
+		if (! empty($this->template_variation)) {
771
+			return $this->template_variation;
772
+		}
773
+
774
+		/** @type EE_Message_Template_Group $grp */
775
+		$grp = $this->get_first_related('Message_Template_Group');
776
+
777
+		// if no group then let's try to get the first related group by internal messenger and message type (will use global grp).
778
+		if (! $grp instanceof EE_Message_Template_Group) {
779
+			$grp = EEM_Message_Template_Group::instance()->get_one(
780
+				array(
781
+					array(
782
+						'MTP_messenger'    => $this->messenger(),
783
+						'MTP_message_type' => $this->message_type(),
784
+						'MTP_is_global'    => true,
785
+					),
786
+				)
787
+			);
788
+		}
789
+
790
+		return $grp instanceof EE_Message_Template_Group ? $grp->get_template_pack_variation() : '';
791
+	}
792
+
793
+	/**
794
+	 * Return the link to the admin details for the object.
795
+	 *
796
+	 * @return string
797
+	 */
798
+	public function get_admin_details_link()
799
+	{
800
+		EE_Registry::instance()->load_helper('URL');
801
+		EE_Registry::instance()->load_helper('MSG_Template');
802
+		switch ($this->STS_ID()) {
803
+			case EEM_Message::status_failed:
804
+			case EEM_Message::status_debug_only:
805
+				return EEH_MSG_Template::generate_error_display_trigger($this);
806
+				break;
807
+
808
+			case EEM_Message::status_sent:
809
+				return EEH_MSG_Template::generate_browser_trigger($this);
810
+				break;
811
+
812
+			default:
813
+				return '';
814
+		}
815
+	}
816
+
817
+	/**
818
+	 * Returns the link to the editor for the object.  Sometimes this is the same as the details.
819
+	 *
820
+	 * @return string
821
+	 */
822
+	public function get_admin_edit_link()
823
+	{
824
+		return $this->get_admin_details_link();
825
+	}
826
+
827
+	/**
828
+	 * Returns the link to a settings page for the object.
829
+	 *
830
+	 * @return string
831
+	 */
832
+	public function get_admin_settings_link()
833
+	{
834
+		EE_Registry::instance()->load_helper('URL');
835
+		return EEH_URL::add_query_args_and_nonce(
836
+			array(
837
+				'page'   => 'espresso_messages',
838
+				'action' => 'settings',
839
+			),
840
+			admin_url('admin.php')
841
+		);
842
+	}
843
+
844
+	/**
845
+	 * Returns the link to the "overview" for the object (typically the "list table" view).
846
+	 *
847
+	 * @return string
848
+	 */
849
+	public function get_admin_overview_link()
850
+	{
851
+		EE_Registry::instance()->load_helper('URL');
852
+		return EEH_URL::add_query_args_and_nonce(
853
+			array(
854
+				'page'   => 'espresso_messages',
855
+				'action' => 'default',
856
+			),
857
+			admin_url('admin.php')
858
+		);
859
+	}
860
+
861
+
862
+	/**
863
+	 * This sets the EEM_Message::status_messenger_executing class on the message and the appropriate error message for
864
+	 * it.
865
+	 * Note this also SAVES the current message object to the db because it adds an error message to accompany the
866
+	 * status.
867
+	 *
868
+	 */
869
+	public function set_messenger_is_executing()
870
+	{
871
+		$this->set_STS_ID(EEM_Message::status_messenger_executing);
872
+		$this->set_error_message(
873
+			esc_html__(
874
+				'A message with this status indicates that there was a problem that occurred while the message was being
875 875
                 processed by the messenger.  It is still possible that the message was sent successfully, but at some
876 876
                 point during the processing there was a failure.  This usually is indicative of a timeout issue with PHP 
877 877
                 or memory limits being reached.  If you see this repeatedly you may want to consider upgrading the memory 
878 878
                 available to PHP on your server.',
879
-                'event_espresso'
880
-            )
881
-        );
882
-    }
879
+				'event_espresso'
880
+			)
881
+		);
882
+	}
883 883
 }
Please login to merge, or discard this patch.
core/db_classes/EE_Message_Template.class.php 2 patches
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -42,7 +42,7 @@
 block discarded – undo
42 42
      */
43 43
     public function set_group_template_id($GRP_ID = false)
44 44
     {
45
-        if (! $GRP_ID) {
45
+        if ( ! $GRP_ID) {
46 46
             throw new EE_Error(esc_html__('Missing required value for the message template group id', 'event_espresso'));
47 47
         }
48 48
         $this->set('GRP_ID', $GRP_ID);
Please login to merge, or discard this patch.
Indentation   +169 added lines, -169 removed lines patch added patch discarded remove patch
@@ -12,173 +12,173 @@
 block discarded – undo
12 12
  */
13 13
 class EE_Message_Template extends EE_Base_Class
14 14
 {
15
-    /**
16
-     * @param array  $props_n_values
17
-     * @param string $timezone
18
-     * @return EE_Message_Template|mixed
19
-     */
20
-    public static function new_instance($props_n_values = array(), $timezone = '')
21
-    {
22
-        $has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone);
23
-        return $has_object ? $has_object : new self($props_n_values, false, $timezone);
24
-    }
25
-
26
-
27
-    /**
28
-     * @param array  $props_n_values
29
-     * @param string $timezone
30
-     * @return EE_Message_Template
31
-     */
32
-    public static function new_instance_from_db($props_n_values = array(), $timezone = '')
33
-    {
34
-        return new self($props_n_values, true, $timezone);
35
-    }
36
-
37
-
38
-    /**
39
-     * @param bool $GRP_ID
40
-     * @throws EE_Error
41
-     */
42
-    public function set_group_template_id($GRP_ID = false)
43
-    {
44
-        if (! $GRP_ID) {
45
-            throw new EE_Error(esc_html__('Missing required value for the message template group id', 'event_espresso'));
46
-        }
47
-        $this->set('GRP_ID', $GRP_ID);
48
-    }
49
-
50
-
51
-    /**
52
-     * get Group ID
53
-     *
54
-     * @access public
55
-     * @return int
56
-     */
57
-    public function GRP_ID()
58
-    {
59
-        return $this->get('GRP_ID');
60
-    }
61
-
62
-
63
-    /**
64
-     * get User ID
65
-     *
66
-     * @access public
67
-     * @return int
68
-     */
69
-    public function user()
70
-    {
71
-        return $this->get_first_related('Message_Template_Group')->get('MTP_user_id');
72
-    }
73
-
74
-
75
-    /**
76
-     * get Message Messenger
77
-     *
78
-     * @access public
79
-     * @return string
80
-     */
81
-    public function messenger()
82
-    {
83
-        return $this->get_first_related('Message_Template_Group')->messenger();
84
-    }
85
-
86
-
87
-    /**
88
-     * get Message Messenger OBJECT
89
-     *
90
-     * @access public
91
-     * @return object Messenger Object for the given messenger
92
-     */
93
-    public function messenger_obj()
94
-    {
95
-        return $this->get_first_related('Message_Template_Group')->messenger_obj();
96
-    }
97
-
98
-
99
-    /**
100
-     * get Message Type
101
-     *
102
-     * @access public
103
-     * @return string
104
-     */
105
-    public function message_type()
106
-    {
107
-        return $this->get_first_related('Message_Template_Group')->message_type();
108
-    }
109
-
110
-
111
-    /**
112
-     * get Message type OBJECT
113
-     *
114
-     * @access public
115
-     * @return object  Message Type object for the given message type
116
-     */
117
-    public function message_type_obj()
118
-    {
119
-        return $this->get_first_related('Message_Template_Group')->message_type_obj();
120
-    }
121
-
122
-
123
-    /**
124
-     * This returns the set context array configured in the message type object
125
-     *
126
-     * @access public
127
-     * @return array array of contexts and their configuration.
128
-     */
129
-    public function contexts_config()
130
-    {
131
-        return $this->get_first_related('Message_Template_Group')->contexts_config();
132
-    }
133
-
134
-
135
-    /**
136
-     * This returns the context_label for contexts as set in the message type object
137
-     *
138
-     * @access public
139
-     * @return string label for "context"
140
-     */
141
-    public function context_label()
142
-    {
143
-        return $this->get_first_related('Message_Template_Group')->context_label();
144
-    }
145
-
146
-
147
-    /**
148
-     * this returns if the template group this template belongs to is global
149
-     *
150
-     * @return boolean true if it is, false if it isn't
151
-     */
152
-    public function is_global()
153
-    {
154
-        return $this->get_first_related('Message_Template_Group')->is_global();
155
-    }
156
-
157
-
158
-    /**
159
-     * this returns if the template group this template belongs to is active (i.e. turned "on" or not)
160
-     *
161
-     * @return boolean true if it is, false if it isn't
162
-     */
163
-    public function is_active()
164
-    {
165
-        return $this->get_first_related('Message_Template_Group')->is_active();
166
-    }
167
-
168
-
169
-    /**
170
-     * This will return an array of shortcodes => labels from the messenger and message_type objects associated with
171
-     * this template.
172
-     *
173
-     * @access public
174
-     * @param string $context what context we're going to return shortcodes for
175
-     * @param array  $fields  what fields we're returning valid shortcodes for.  If empty then we assume all fields are
176
-     *                        to be merged and returned.
177
-     * @return mixed (array|bool) an array of shortcodes in the format array( '[shortcode] => 'label') OR FALSE if no
178
-     *               shortcodes found.
179
-     */
180
-    public function get_shortcodes($context, $fields = array())
181
-    {
182
-        return $this->get_first_related('Message_Template_Group')->get_shortcodes($context, $fields);
183
-    }
15
+	/**
16
+	 * @param array  $props_n_values
17
+	 * @param string $timezone
18
+	 * @return EE_Message_Template|mixed
19
+	 */
20
+	public static function new_instance($props_n_values = array(), $timezone = '')
21
+	{
22
+		$has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone);
23
+		return $has_object ? $has_object : new self($props_n_values, false, $timezone);
24
+	}
25
+
26
+
27
+	/**
28
+	 * @param array  $props_n_values
29
+	 * @param string $timezone
30
+	 * @return EE_Message_Template
31
+	 */
32
+	public static function new_instance_from_db($props_n_values = array(), $timezone = '')
33
+	{
34
+		return new self($props_n_values, true, $timezone);
35
+	}
36
+
37
+
38
+	/**
39
+	 * @param bool $GRP_ID
40
+	 * @throws EE_Error
41
+	 */
42
+	public function set_group_template_id($GRP_ID = false)
43
+	{
44
+		if (! $GRP_ID) {
45
+			throw new EE_Error(esc_html__('Missing required value for the message template group id', 'event_espresso'));
46
+		}
47
+		$this->set('GRP_ID', $GRP_ID);
48
+	}
49
+
50
+
51
+	/**
52
+	 * get Group ID
53
+	 *
54
+	 * @access public
55
+	 * @return int
56
+	 */
57
+	public function GRP_ID()
58
+	{
59
+		return $this->get('GRP_ID');
60
+	}
61
+
62
+
63
+	/**
64
+	 * get User ID
65
+	 *
66
+	 * @access public
67
+	 * @return int
68
+	 */
69
+	public function user()
70
+	{
71
+		return $this->get_first_related('Message_Template_Group')->get('MTP_user_id');
72
+	}
73
+
74
+
75
+	/**
76
+	 * get Message Messenger
77
+	 *
78
+	 * @access public
79
+	 * @return string
80
+	 */
81
+	public function messenger()
82
+	{
83
+		return $this->get_first_related('Message_Template_Group')->messenger();
84
+	}
85
+
86
+
87
+	/**
88
+	 * get Message Messenger OBJECT
89
+	 *
90
+	 * @access public
91
+	 * @return object Messenger Object for the given messenger
92
+	 */
93
+	public function messenger_obj()
94
+	{
95
+		return $this->get_first_related('Message_Template_Group')->messenger_obj();
96
+	}
97
+
98
+
99
+	/**
100
+	 * get Message Type
101
+	 *
102
+	 * @access public
103
+	 * @return string
104
+	 */
105
+	public function message_type()
106
+	{
107
+		return $this->get_first_related('Message_Template_Group')->message_type();
108
+	}
109
+
110
+
111
+	/**
112
+	 * get Message type OBJECT
113
+	 *
114
+	 * @access public
115
+	 * @return object  Message Type object for the given message type
116
+	 */
117
+	public function message_type_obj()
118
+	{
119
+		return $this->get_first_related('Message_Template_Group')->message_type_obj();
120
+	}
121
+
122
+
123
+	/**
124
+	 * This returns the set context array configured in the message type object
125
+	 *
126
+	 * @access public
127
+	 * @return array array of contexts and their configuration.
128
+	 */
129
+	public function contexts_config()
130
+	{
131
+		return $this->get_first_related('Message_Template_Group')->contexts_config();
132
+	}
133
+
134
+
135
+	/**
136
+	 * This returns the context_label for contexts as set in the message type object
137
+	 *
138
+	 * @access public
139
+	 * @return string label for "context"
140
+	 */
141
+	public function context_label()
142
+	{
143
+		return $this->get_first_related('Message_Template_Group')->context_label();
144
+	}
145
+
146
+
147
+	/**
148
+	 * this returns if the template group this template belongs to is global
149
+	 *
150
+	 * @return boolean true if it is, false if it isn't
151
+	 */
152
+	public function is_global()
153
+	{
154
+		return $this->get_first_related('Message_Template_Group')->is_global();
155
+	}
156
+
157
+
158
+	/**
159
+	 * this returns if the template group this template belongs to is active (i.e. turned "on" or not)
160
+	 *
161
+	 * @return boolean true if it is, false if it isn't
162
+	 */
163
+	public function is_active()
164
+	{
165
+		return $this->get_first_related('Message_Template_Group')->is_active();
166
+	}
167
+
168
+
169
+	/**
170
+	 * This will return an array of shortcodes => labels from the messenger and message_type objects associated with
171
+	 * this template.
172
+	 *
173
+	 * @access public
174
+	 * @param string $context what context we're going to return shortcodes for
175
+	 * @param array  $fields  what fields we're returning valid shortcodes for.  If empty then we assume all fields are
176
+	 *                        to be merged and returned.
177
+	 * @return mixed (array|bool) an array of shortcodes in the format array( '[shortcode] => 'label') OR FALSE if no
178
+	 *               shortcodes found.
179
+	 */
180
+	public function get_shortcodes($context, $fields = array())
181
+	{
182
+		return $this->get_first_related('Message_Template_Group')->get_shortcodes($context, $fields);
183
+	}
184 184
 }
Please login to merge, or discard this patch.
core/db_classes/EE_Attendee.class.php 2 patches
Spacing   +12 added lines, -12 removed lines patch added patch discarded remove patch
@@ -36,16 +36,16 @@  discard block
 block discarded – undo
36 36
      */
37 37
     protected function __construct($fieldValues = null, $bydb = false, $timezone = null, $date_formats = array())
38 38
     {
39
-        if (! isset($fieldValues['ATT_full_name'])) {
40
-            $fname = isset($fieldValues['ATT_fname']) ? $fieldValues['ATT_fname'] . ' ' : '';
39
+        if ( ! isset($fieldValues['ATT_full_name'])) {
40
+            $fname = isset($fieldValues['ATT_fname']) ? $fieldValues['ATT_fname'].' ' : '';
41 41
             $lname = isset($fieldValues['ATT_lname']) ? $fieldValues['ATT_lname'] : '';
42
-            $fieldValues['ATT_full_name'] = $fname . $lname;
42
+            $fieldValues['ATT_full_name'] = $fname.$lname;
43 43
         }
44
-        if (! isset($fieldValues['ATT_slug'])) {
44
+        if ( ! isset($fieldValues['ATT_slug'])) {
45 45
             // $fieldValues['ATT_slug'] = sanitize_key(wp_generate_password(20));
46 46
             $fieldValues['ATT_slug'] = sanitize_title($fieldValues['ATT_full_name']);
47 47
         }
48
-        if (! isset($fieldValues['ATT_short_bio']) && isset($fieldValues['ATT_bio'])) {
48
+        if ( ! isset($fieldValues['ATT_short_bio']) && isset($fieldValues['ATT_bio'])) {
49 49
             $fieldValues['ATT_short_bio'] = substr($fieldValues['ATT_bio'], 0, 50);
50 50
         }
51 51
         parent::__construct($fieldValues, $bydb, $timezone, $date_formats);
@@ -325,7 +325,7 @@  discard block
 block discarded – undo
325 325
         $initial_address_fields = array('ATT_address', 'ATT_address2', 'ATT_city',);
326 326
         foreach ($initial_address_fields as $address_field_name) {
327 327
             $address_fields_value = $this->get($address_field_name);
328
-            if (! empty($address_fields_value)) {
328
+            if ( ! empty($address_fields_value)) {
329 329
                 $full_address_array[] = $address_fields_value;
330 330
             }
331 331
         }
@@ -340,7 +340,7 @@  discard block
 block discarded – undo
340 340
         }
341 341
         // lastly get the xip
342 342
         $zip_value = $this->zip();
343
-        if (! empty($zip_value)) {
343
+        if ( ! empty($zip_value)) {
344 344
             $full_address_array[] = $zip_value;
345 345
         }
346 346
         return $full_address_array;
@@ -622,18 +622,18 @@  discard block
 block discarded – undo
622 622
     public function billing_info_for_payment_method($payment_method)
623 623
     {
624 624
         $pm_type = $payment_method->type_obj();
625
-        if (! $pm_type instanceof EE_PMT_Base) {
625
+        if ( ! $pm_type instanceof EE_PMT_Base) {
626 626
             return null;
627 627
         }
628 628
         $billing_info = $this->get_post_meta($this->get_billing_info_postmeta_name($payment_method), true);
629
-        if (! $billing_info) {
629
+        if ( ! $billing_info) {
630 630
             return null;
631 631
         }
632 632
         $billing_form = $pm_type->billing_form();
633 633
         // double-check the form isn't totally hidden, in which case pretend there is no form
634 634
         $form_totally_hidden = true;
635 635
         foreach ($billing_form->inputs_in_subsections() as $input) {
636
-            if (! $input->get_display_strategy() instanceof EE_Hidden_Display_Strategy) {
636
+            if ( ! $input->get_display_strategy() instanceof EE_Hidden_Display_Strategy) {
637 637
                 $form_totally_hidden = false;
638 638
                 break;
639 639
             }
@@ -660,7 +660,7 @@  discard block
 block discarded – undo
660 660
     public function get_billing_info_postmeta_name($payment_method)
661 661
     {
662 662
         if ($payment_method->type_obj() instanceof EE_PMT_Base) {
663
-            return 'billing_info_' . $payment_method->type_obj()->system_name();
663
+            return 'billing_info_'.$payment_method->type_obj()->system_name();
664 664
         }
665 665
         return null;
666 666
     }
@@ -677,7 +677,7 @@  discard block
 block discarded – undo
677 677
      */
678 678
     public function save_and_clean_billing_info_for_payment_method($billing_form, $payment_method)
679 679
     {
680
-        if (! $billing_form instanceof EE_Billing_Attendee_Info_Form) {
680
+        if ( ! $billing_form instanceof EE_Billing_Attendee_Info_Form) {
681 681
             EE_Error::add_error(esc_html__('Cannot save billing info because there is none.', 'event_espresso'));
682 682
             return false;
683 683
         }
Please login to merge, or discard this patch.
Indentation   +768 added lines, -768 removed lines patch added patch discarded remove patch
@@ -24,772 +24,772 @@
 block discarded – undo
24 24
  */
25 25
 class EE_Attendee extends EE_CPT_Base implements EEI_Contact, EEI_Address, EEI_Admin_Links, EEI_Attendee
26 26
 {
27
-    /**
28
-     * Sets some dynamic defaults
29
-     *
30
-     * @param array  $fieldValues
31
-     * @param bool   $bydb
32
-     * @param string $timezone
33
-     * @param array  $date_formats
34
-     * @throws EE_Error
35
-     */
36
-    protected function __construct($fieldValues = null, $bydb = false, $timezone = null, $date_formats = array())
37
-    {
38
-        if (! isset($fieldValues['ATT_full_name'])) {
39
-            $fname = isset($fieldValues['ATT_fname']) ? $fieldValues['ATT_fname'] . ' ' : '';
40
-            $lname = isset($fieldValues['ATT_lname']) ? $fieldValues['ATT_lname'] : '';
41
-            $fieldValues['ATT_full_name'] = $fname . $lname;
42
-        }
43
-        if (! isset($fieldValues['ATT_slug'])) {
44
-            // $fieldValues['ATT_slug'] = sanitize_key(wp_generate_password(20));
45
-            $fieldValues['ATT_slug'] = sanitize_title($fieldValues['ATT_full_name']);
46
-        }
47
-        if (! isset($fieldValues['ATT_short_bio']) && isset($fieldValues['ATT_bio'])) {
48
-            $fieldValues['ATT_short_bio'] = substr($fieldValues['ATT_bio'], 0, 50);
49
-        }
50
-        parent::__construct($fieldValues, $bydb, $timezone, $date_formats);
51
-    }
52
-
53
-
54
-    /**
55
-     * @param array  $props_n_values          incoming values
56
-     * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
57
-     *                                        used.)
58
-     * @param array  $date_formats            incoming date_formats in an array where the first value is the
59
-     *                                        date_format and the second value is the time format
60
-     * @return EE_Attendee
61
-     * @throws EE_Error
62
-     */
63
-    public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
64
-    {
65
-        $has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
66
-        return $has_object ? $has_object : new self($props_n_values, false, $timezone, $date_formats);
67
-    }
68
-
69
-
70
-    /**
71
-     * @param array  $props_n_values  incoming values from the database
72
-     * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
73
-     *                                the website will be used.
74
-     * @return EE_Attendee
75
-     */
76
-    public static function new_instance_from_db($props_n_values = array(), $timezone = null)
77
-    {
78
-        return new self($props_n_values, true, $timezone);
79
-    }
80
-
81
-
82
-    /**
83
-     *        Set Attendee First Name
84
-     *
85
-     * @access        public
86
-     * @param string $fname
87
-     * @throws EE_Error
88
-     */
89
-    public function set_fname($fname = '')
90
-    {
91
-        $this->set('ATT_fname', $fname);
92
-    }
93
-
94
-
95
-    /**
96
-     *        Set Attendee Last Name
97
-     *
98
-     * @access        public
99
-     * @param string $lname
100
-     * @throws EE_Error
101
-     */
102
-    public function set_lname($lname = '')
103
-    {
104
-        $this->set('ATT_lname', $lname);
105
-    }
106
-
107
-
108
-    /**
109
-     *        Set Attendee Address
110
-     *
111
-     * @access        public
112
-     * @param string $address
113
-     * @throws EE_Error
114
-     */
115
-    public function set_address($address = '')
116
-    {
117
-        $this->set('ATT_address', $address);
118
-    }
119
-
120
-
121
-    /**
122
-     *        Set Attendee Address2
123
-     *
124
-     * @access        public
125
-     * @param        string $address2
126
-     * @throws EE_Error
127
-     */
128
-    public function set_address2($address2 = '')
129
-    {
130
-        $this->set('ATT_address2', $address2);
131
-    }
132
-
133
-
134
-    /**
135
-     *        Set Attendee City
136
-     *
137
-     * @access        public
138
-     * @param        string $city
139
-     * @throws EE_Error
140
-     */
141
-    public function set_city($city = '')
142
-    {
143
-        $this->set('ATT_city', $city);
144
-    }
145
-
146
-
147
-    /**
148
-     *        Set Attendee State ID
149
-     *
150
-     * @access        public
151
-     * @param        int $STA_ID
152
-     * @throws EE_Error
153
-     */
154
-    public function set_state($STA_ID = 0)
155
-    {
156
-        $this->set('STA_ID', $STA_ID);
157
-    }
158
-
159
-
160
-    /**
161
-     *        Set Attendee Country ISO Code
162
-     *
163
-     * @access        public
164
-     * @param        string $CNT_ISO
165
-     * @throws EE_Error
166
-     */
167
-    public function set_country($CNT_ISO = '')
168
-    {
169
-        $this->set('CNT_ISO', $CNT_ISO);
170
-    }
171
-
172
-
173
-    /**
174
-     *        Set Attendee Zip/Postal Code
175
-     *
176
-     * @access        public
177
-     * @param        string $zip
178
-     * @throws EE_Error
179
-     */
180
-    public function set_zip($zip = '')
181
-    {
182
-        $this->set('ATT_zip', $zip);
183
-    }
184
-
185
-
186
-    /**
187
-     *        Set Attendee Email Address
188
-     *
189
-     * @access        public
190
-     * @param        string $email
191
-     * @throws EE_Error
192
-     */
193
-    public function set_email($email = '')
194
-    {
195
-        $this->set('ATT_email', $email);
196
-    }
197
-
198
-
199
-    /**
200
-     *        Set Attendee Phone
201
-     *
202
-     * @access        public
203
-     * @param        string $phone
204
-     * @throws EE_Error
205
-     */
206
-    public function set_phone($phone = '')
207
-    {
208
-        $this->set('ATT_phone', $phone);
209
-    }
210
-
211
-
212
-    /**
213
-     *        set deleted
214
-     *
215
-     * @access        public
216
-     * @param        bool $ATT_deleted
217
-     * @throws EE_Error
218
-     */
219
-    public function set_deleted($ATT_deleted = false)
220
-    {
221
-        $this->set('ATT_deleted', $ATT_deleted);
222
-    }
223
-
224
-
225
-    /**
226
-     * Returns the value for the post_author id saved with the cpt
227
-     *
228
-     * @since 4.5.0
229
-     * @return int
230
-     * @throws EE_Error
231
-     */
232
-    public function wp_user()
233
-    {
234
-        return $this->get('ATT_author');
235
-    }
236
-
237
-
238
-    /**
239
-     *        get Attendee First Name
240
-     *
241
-     * @access        public
242
-     * @return string
243
-     * @throws EE_Error
244
-     */
245
-    public function fname()
246
-    {
247
-        return $this->get('ATT_fname');
248
-    }
249
-
250
-
251
-    /**
252
-     * echoes out the attendee's first name
253
-     *
254
-     * @return void
255
-     * @throws EE_Error
256
-     */
257
-    public function e_full_name()
258
-    {
259
-        echo esc_html($this->full_name());
260
-    }
261
-
262
-
263
-    /**
264
-     * Returns the first and last name concatenated together with a space.
265
-     *
266
-     * @param bool $apply_html_entities
267
-     * @return string
268
-     * @throws EE_Error
269
-     */
270
-    public function full_name($apply_html_entities = false)
271
-    {
272
-        $full_name = array(
273
-            $this->fname(),
274
-            $this->lname(),
275
-        );
276
-        $full_name = array_filter($full_name);
277
-        $full_name = implode(' ', $full_name);
278
-        return $apply_html_entities ? htmlentities($full_name, ENT_QUOTES, 'UTF-8') : $full_name;
279
-    }
280
-
281
-
282
-    /**
283
-     * This returns the value of the `ATT_full_name` field which is usually equivalent to calling `full_name()` unless
284
-     * the post_title field has been directly modified in the db for the post (espresso_attendees post type) for this
285
-     * attendee.
286
-     *
287
-     * @param bool $apply_html_entities
288
-     * @return string
289
-     * @throws EE_Error
290
-     */
291
-    public function ATT_full_name($apply_html_entities = false)
292
-    {
293
-        return $apply_html_entities
294
-            ? htmlentities($this->get('ATT_full_name'), ENT_QUOTES, 'UTF-8')
295
-            : $this->get('ATT_full_name');
296
-    }
297
-
298
-
299
-    /**
300
-     *        get Attendee Last Name
301
-     *
302
-     * @access        public
303
-     * @return string
304
-     * @throws EE_Error
305
-     */
306
-    public function lname()
307
-    {
308
-        return $this->get('ATT_lname');
309
-    }
310
-
311
-
312
-    /**
313
-     * get Attendee bio
314
-     *
315
-     * @access public
316
-     * @return string
317
-     * @throws EE_Error
318
-     */
319
-    public function bio()
320
-    {
321
-        return $this->get('ATT_bio');
322
-    }
323
-
324
-
325
-    /**
326
-     * get Attendee short bio
327
-     *
328
-     * @access public
329
-     * @return string
330
-     * @throws EE_Error
331
-     */
332
-    public function short_bio()
333
-    {
334
-        return $this->get('ATT_short_bio');
335
-    }
336
-
337
-
338
-    /**
339
-     * Gets the attendee's full address as an array so client code can decide hwo to display it
340
-     *
341
-     * @return array numerically indexed, with each part of the address that is known.
342
-     * Eg, if the user only responded to state and country,
343
-     * it would be array(0=>'Alabama',1=>'USA')
344
-     * @return array
345
-     * @throws EE_Error
346
-     */
347
-    public function full_address_as_array()
348
-    {
349
-        $full_address_array = array();
350
-        $initial_address_fields = array('ATT_address', 'ATT_address2', 'ATT_city',);
351
-        foreach ($initial_address_fields as $address_field_name) {
352
-            $address_fields_value = $this->get($address_field_name);
353
-            if (! empty($address_fields_value)) {
354
-                $full_address_array[] = $address_fields_value;
355
-            }
356
-        }
357
-        // now handle state and country
358
-        $state_obj = $this->state_obj();
359
-        if ($state_obj instanceof EE_State) {
360
-            $full_address_array[] = $state_obj->name();
361
-        }
362
-        $country_obj = $this->country_obj();
363
-        if ($country_obj instanceof EE_Country) {
364
-            $full_address_array[] = $country_obj->name();
365
-        }
366
-        // lastly get the xip
367
-        $zip_value = $this->zip();
368
-        if (! empty($zip_value)) {
369
-            $full_address_array[] = $zip_value;
370
-        }
371
-        return $full_address_array;
372
-    }
373
-
374
-
375
-    /**
376
-     *        get Attendee Address
377
-     *
378
-     * @return string
379
-     * @throws EE_Error
380
-     */
381
-    public function address()
382
-    {
383
-        return $this->get('ATT_address');
384
-    }
385
-
386
-
387
-    /**
388
-     *        get Attendee Address2
389
-     *
390
-     * @return string
391
-     * @throws EE_Error
392
-     */
393
-    public function address2()
394
-    {
395
-        return $this->get('ATT_address2');
396
-    }
397
-
398
-
399
-    /**
400
-     *        get Attendee City
401
-     *
402
-     * @return string
403
-     * @throws EE_Error
404
-     */
405
-    public function city()
406
-    {
407
-        return $this->get('ATT_city');
408
-    }
409
-
410
-
411
-    /**
412
-     *        get Attendee State ID
413
-     *
414
-     * @return string
415
-     * @throws EE_Error
416
-     */
417
-    public function state_ID()
418
-    {
419
-        return $this->get('STA_ID');
420
-    }
421
-
422
-
423
-    /**
424
-     * @return string
425
-     * @throws EE_Error
426
-     */
427
-    public function state_abbrev()
428
-    {
429
-        return $this->state_obj() instanceof EE_State ? $this->state_obj()->abbrev() : '';
430
-    }
431
-
432
-
433
-    /**
434
-     * Gets the state set to this attendee
435
-     *
436
-     * @return EE_State
437
-     * @throws EE_Error
438
-     */
439
-    public function state_obj()
440
-    {
441
-        return $this->get_first_related('State');
442
-    }
443
-
444
-
445
-    /**
446
-     * Returns the state's name, otherwise 'Unknown'
447
-     *
448
-     * @return string
449
-     * @throws EE_Error
450
-     */
451
-    public function state_name()
452
-    {
453
-        if ($this->state_obj()) {
454
-            return $this->state_obj()->name();
455
-        } else {
456
-            return '';
457
-        }
458
-    }
459
-
460
-
461
-    /**
462
-     * either displays the state abbreviation or the state name, as determined
463
-     * by the "FHEE__EEI_Address__state__use_abbreviation" filter.
464
-     * defaults to abbreviation
465
-     *
466
-     * @return string
467
-     * @throws EE_Error
468
-     */
469
-    public function state()
470
-    {
471
-        if (apply_filters('FHEE__EEI_Address__state__use_abbreviation', true, $this->state_obj())) {
472
-            return $this->state_abbrev();
473
-        }
474
-        return $this->state_name();
475
-    }
476
-
477
-
478
-    /**
479
-     *    get Attendee Country ISO Code
480
-     *
481
-     * @return string
482
-     * @throws EE_Error
483
-     */
484
-    public function country_ID()
485
-    {
486
-        return $this->get('CNT_ISO');
487
-    }
488
-
489
-
490
-    /**
491
-     * Gets country set for this attendee
492
-     *
493
-     * @return EE_Country
494
-     * @throws EE_Error
495
-     */
496
-    public function country_obj()
497
-    {
498
-        return $this->get_first_related('Country');
499
-    }
500
-
501
-
502
-    /**
503
-     * Returns the country's name if known, otherwise 'Unknown'
504
-     *
505
-     * @return string
506
-     * @throws EE_Error
507
-     */
508
-    public function country_name()
509
-    {
510
-        if ($this->country_obj()) {
511
-            return $this->country_obj()->name();
512
-        }
513
-        return '';
514
-    }
515
-
516
-
517
-    /**
518
-     * either displays the country ISO2 code or the country name, as determined
519
-     * by the "FHEE__EEI_Address__country__use_abbreviation" filter.
520
-     * defaults to abbreviation
521
-     *
522
-     * @return string
523
-     * @throws EE_Error
524
-     */
525
-    public function country()
526
-    {
527
-        if (apply_filters('FHEE__EEI_Address__country__use_abbreviation', true, $this->country_obj())) {
528
-            return $this->country_ID();
529
-        }
530
-        return $this->country_name();
531
-    }
532
-
533
-
534
-    /**
535
-     *        get Attendee Zip/Postal Code
536
-     *
537
-     * @return string
538
-     * @throws EE_Error
539
-     */
540
-    public function zip()
541
-    {
542
-        return $this->get('ATT_zip');
543
-    }
544
-
545
-
546
-    /**
547
-     *        get Attendee Email Address
548
-     *
549
-     * @return string
550
-     * @throws EE_Error
551
-     */
552
-    public function email()
553
-    {
554
-        return $this->get('ATT_email');
555
-    }
556
-
557
-
558
-    /**
559
-     *        get Attendee Phone #
560
-     *
561
-     * @return string
562
-     * @throws EE_Error
563
-     */
564
-    public function phone()
565
-    {
566
-        return $this->get('ATT_phone');
567
-    }
568
-
569
-
570
-    /**
571
-     *    get deleted
572
-     *
573
-     * @return        bool
574
-     * @throws EE_Error
575
-     */
576
-    public function deleted()
577
-    {
578
-        return $this->get('ATT_deleted');
579
-    }
580
-
581
-
582
-    /**
583
-     * Gets registrations of this attendee
584
-     *
585
-     * @param array $query_params
586
-     * @return EE_Registration[]
587
-     * @throws EE_Error
588
-     */
589
-    public function get_registrations($query_params = array())
590
-    {
591
-        return $this->get_many_related('Registration', $query_params);
592
-    }
593
-
594
-
595
-    /**
596
-     * Gets the most recent registration of this attendee
597
-     *
598
-     * @return EE_Registration
599
-     * @throws EE_Error
600
-     */
601
-    public function get_most_recent_registration()
602
-    {
603
-        return $this->get_first_related(
604
-            'Registration',
605
-            array('order_by' => array('REG_date' => 'DESC'))
606
-        ); // null, 'REG_date', 'DESC', '=', 'OBJECT_K');
607
-    }
608
-
609
-
610
-    /**
611
-     * Gets the most recent registration for this attend at this event
612
-     *
613
-     * @param int $event_id
614
-     * @return EE_Registration
615
-     * @throws EE_Error
616
-     */
617
-    public function get_most_recent_registration_for_event($event_id)
618
-    {
619
-        return $this->get_first_related(
620
-            'Registration',
621
-            array(array('EVT_ID' => $event_id), 'order_by' => array('REG_date' => 'DESC'))
622
-        );
623
-    }
624
-
625
-
626
-    /**
627
-     * returns any events attached to this attendee ($_Event property);
628
-     *
629
-     * @return array
630
-     * @throws EE_Error
631
-     */
632
-    public function events()
633
-    {
634
-        return $this->get_many_related('Event');
635
-    }
636
-
637
-
638
-    /**
639
-     * Gets the billing info array where keys match espresso_reg_page_billing_inputs(),
640
-     * and keys are their cleaned values. @see EE_Attendee::save_and_clean_billing_info_for_payment_method() which was
641
-     * used to save the billing info
642
-     *
643
-     * @param EE_Payment_Method $payment_method the _gateway_name property on the gateway class
644
-     * @return EE_Form_Section_Proper|null
645
-     * @throws EE_Error
646
-     */
647
-    public function billing_info_for_payment_method($payment_method)
648
-    {
649
-        $pm_type = $payment_method->type_obj();
650
-        if (! $pm_type instanceof EE_PMT_Base) {
651
-            return null;
652
-        }
653
-        $billing_info = $this->get_post_meta($this->get_billing_info_postmeta_name($payment_method), true);
654
-        if (! $billing_info) {
655
-            return null;
656
-        }
657
-        $billing_form = $pm_type->billing_form();
658
-        // double-check the form isn't totally hidden, in which case pretend there is no form
659
-        $form_totally_hidden = true;
660
-        foreach ($billing_form->inputs_in_subsections() as $input) {
661
-            if (! $input->get_display_strategy() instanceof EE_Hidden_Display_Strategy) {
662
-                $form_totally_hidden = false;
663
-                break;
664
-            }
665
-        }
666
-        if ($form_totally_hidden) {
667
-            return null;
668
-        }
669
-        if ($billing_form instanceof EE_Form_Section_Proper) {
670
-            $billing_form->receive_form_submission(array($billing_form->name() => $billing_info), false);
671
-        }
672
-
673
-        return $billing_form;
674
-    }
675
-
676
-
677
-    /**
678
-     * Gets the postmeta key that holds this attendee's billing info for the
679
-     * specified payment method
680
-     *
681
-     * @param EE_Payment_Method $payment_method
682
-     * @return string
683
-     * @throws EE_Error
684
-     */
685
-    public function get_billing_info_postmeta_name($payment_method)
686
-    {
687
-        if ($payment_method->type_obj() instanceof EE_PMT_Base) {
688
-            return 'billing_info_' . $payment_method->type_obj()->system_name();
689
-        }
690
-        return null;
691
-    }
692
-
693
-
694
-    /**
695
-     * Saves the billing info to the attendee. @see EE_Attendee::billing_info_for_payment_method() which is used to
696
-     * retrieve it
697
-     *
698
-     * @param EE_Billing_Attendee_Info_Form $billing_form
699
-     * @param EE_Payment_Method             $payment_method
700
-     * @return boolean
701
-     * @throws EE_Error
702
-     */
703
-    public function save_and_clean_billing_info_for_payment_method($billing_form, $payment_method)
704
-    {
705
-        if (! $billing_form instanceof EE_Billing_Attendee_Info_Form) {
706
-            EE_Error::add_error(esc_html__('Cannot save billing info because there is none.', 'event_espresso'));
707
-            return false;
708
-        }
709
-        $billing_form->clean_sensitive_data();
710
-        return update_post_meta(
711
-            $this->ID(),
712
-            $this->get_billing_info_postmeta_name($payment_method),
713
-            $billing_form->input_values(true)
714
-        );
715
-    }
716
-
717
-
718
-    /**
719
-     * Return the link to the admin details for the object.
720
-     *
721
-     * @return string
722
-     * @throws EE_Error
723
-     * @throws InvalidArgumentException
724
-     * @throws InvalidDataTypeException
725
-     * @throws InvalidInterfaceException
726
-     * @throws ReflectionException
727
-     */
728
-    public function get_admin_details_link()
729
-    {
730
-        return $this->get_admin_edit_link();
731
-    }
732
-
733
-
734
-    /**
735
-     * Returns the link to the editor for the object.  Sometimes this is the same as the details.
736
-     *
737
-     * @return string
738
-     * @throws EE_Error
739
-     * @throws InvalidArgumentException
740
-     * @throws ReflectionException
741
-     * @throws InvalidDataTypeException
742
-     * @throws InvalidInterfaceException
743
-     */
744
-    public function get_admin_edit_link()
745
-    {
746
-        EE_Registry::instance()->load_helper('URL');
747
-        return EEH_URL::add_query_args_and_nonce(
748
-            array(
749
-                'page'   => 'espresso_registrations',
750
-                'action' => 'edit_attendee',
751
-                'post'   => $this->ID(),
752
-            ),
753
-            admin_url('admin.php')
754
-        );
755
-    }
756
-
757
-
758
-    /**
759
-     * Returns the link to a settings page for the object.
760
-     *
761
-     * @return string
762
-     * @throws EE_Error
763
-     * @throws InvalidArgumentException
764
-     * @throws InvalidDataTypeException
765
-     * @throws InvalidInterfaceException
766
-     * @throws ReflectionException
767
-     */
768
-    public function get_admin_settings_link()
769
-    {
770
-        return $this->get_admin_edit_link();
771
-    }
772
-
773
-
774
-    /**
775
-     * Returns the link to the "overview" for the object (typically the "list table" view).
776
-     *
777
-     * @return string
778
-     * @throws EE_Error
779
-     * @throws InvalidArgumentException
780
-     * @throws ReflectionException
781
-     * @throws InvalidDataTypeException
782
-     * @throws InvalidInterfaceException
783
-     */
784
-    public function get_admin_overview_link()
785
-    {
786
-        EE_Registry::instance()->load_helper('URL');
787
-        return EEH_URL::add_query_args_and_nonce(
788
-            array(
789
-                'page'   => 'espresso_registrations',
790
-                'action' => 'contact_list',
791
-            ),
792
-            admin_url('admin.php')
793
-        );
794
-    }
27
+	/**
28
+	 * Sets some dynamic defaults
29
+	 *
30
+	 * @param array  $fieldValues
31
+	 * @param bool   $bydb
32
+	 * @param string $timezone
33
+	 * @param array  $date_formats
34
+	 * @throws EE_Error
35
+	 */
36
+	protected function __construct($fieldValues = null, $bydb = false, $timezone = null, $date_formats = array())
37
+	{
38
+		if (! isset($fieldValues['ATT_full_name'])) {
39
+			$fname = isset($fieldValues['ATT_fname']) ? $fieldValues['ATT_fname'] . ' ' : '';
40
+			$lname = isset($fieldValues['ATT_lname']) ? $fieldValues['ATT_lname'] : '';
41
+			$fieldValues['ATT_full_name'] = $fname . $lname;
42
+		}
43
+		if (! isset($fieldValues['ATT_slug'])) {
44
+			// $fieldValues['ATT_slug'] = sanitize_key(wp_generate_password(20));
45
+			$fieldValues['ATT_slug'] = sanitize_title($fieldValues['ATT_full_name']);
46
+		}
47
+		if (! isset($fieldValues['ATT_short_bio']) && isset($fieldValues['ATT_bio'])) {
48
+			$fieldValues['ATT_short_bio'] = substr($fieldValues['ATT_bio'], 0, 50);
49
+		}
50
+		parent::__construct($fieldValues, $bydb, $timezone, $date_formats);
51
+	}
52
+
53
+
54
+	/**
55
+	 * @param array  $props_n_values          incoming values
56
+	 * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
57
+	 *                                        used.)
58
+	 * @param array  $date_formats            incoming date_formats in an array where the first value is the
59
+	 *                                        date_format and the second value is the time format
60
+	 * @return EE_Attendee
61
+	 * @throws EE_Error
62
+	 */
63
+	public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
64
+	{
65
+		$has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
66
+		return $has_object ? $has_object : new self($props_n_values, false, $timezone, $date_formats);
67
+	}
68
+
69
+
70
+	/**
71
+	 * @param array  $props_n_values  incoming values from the database
72
+	 * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
73
+	 *                                the website will be used.
74
+	 * @return EE_Attendee
75
+	 */
76
+	public static function new_instance_from_db($props_n_values = array(), $timezone = null)
77
+	{
78
+		return new self($props_n_values, true, $timezone);
79
+	}
80
+
81
+
82
+	/**
83
+	 *        Set Attendee First Name
84
+	 *
85
+	 * @access        public
86
+	 * @param string $fname
87
+	 * @throws EE_Error
88
+	 */
89
+	public function set_fname($fname = '')
90
+	{
91
+		$this->set('ATT_fname', $fname);
92
+	}
93
+
94
+
95
+	/**
96
+	 *        Set Attendee Last Name
97
+	 *
98
+	 * @access        public
99
+	 * @param string $lname
100
+	 * @throws EE_Error
101
+	 */
102
+	public function set_lname($lname = '')
103
+	{
104
+		$this->set('ATT_lname', $lname);
105
+	}
106
+
107
+
108
+	/**
109
+	 *        Set Attendee Address
110
+	 *
111
+	 * @access        public
112
+	 * @param string $address
113
+	 * @throws EE_Error
114
+	 */
115
+	public function set_address($address = '')
116
+	{
117
+		$this->set('ATT_address', $address);
118
+	}
119
+
120
+
121
+	/**
122
+	 *        Set Attendee Address2
123
+	 *
124
+	 * @access        public
125
+	 * @param        string $address2
126
+	 * @throws EE_Error
127
+	 */
128
+	public function set_address2($address2 = '')
129
+	{
130
+		$this->set('ATT_address2', $address2);
131
+	}
132
+
133
+
134
+	/**
135
+	 *        Set Attendee City
136
+	 *
137
+	 * @access        public
138
+	 * @param        string $city
139
+	 * @throws EE_Error
140
+	 */
141
+	public function set_city($city = '')
142
+	{
143
+		$this->set('ATT_city', $city);
144
+	}
145
+
146
+
147
+	/**
148
+	 *        Set Attendee State ID
149
+	 *
150
+	 * @access        public
151
+	 * @param        int $STA_ID
152
+	 * @throws EE_Error
153
+	 */
154
+	public function set_state($STA_ID = 0)
155
+	{
156
+		$this->set('STA_ID', $STA_ID);
157
+	}
158
+
159
+
160
+	/**
161
+	 *        Set Attendee Country ISO Code
162
+	 *
163
+	 * @access        public
164
+	 * @param        string $CNT_ISO
165
+	 * @throws EE_Error
166
+	 */
167
+	public function set_country($CNT_ISO = '')
168
+	{
169
+		$this->set('CNT_ISO', $CNT_ISO);
170
+	}
171
+
172
+
173
+	/**
174
+	 *        Set Attendee Zip/Postal Code
175
+	 *
176
+	 * @access        public
177
+	 * @param        string $zip
178
+	 * @throws EE_Error
179
+	 */
180
+	public function set_zip($zip = '')
181
+	{
182
+		$this->set('ATT_zip', $zip);
183
+	}
184
+
185
+
186
+	/**
187
+	 *        Set Attendee Email Address
188
+	 *
189
+	 * @access        public
190
+	 * @param        string $email
191
+	 * @throws EE_Error
192
+	 */
193
+	public function set_email($email = '')
194
+	{
195
+		$this->set('ATT_email', $email);
196
+	}
197
+
198
+
199
+	/**
200
+	 *        Set Attendee Phone
201
+	 *
202
+	 * @access        public
203
+	 * @param        string $phone
204
+	 * @throws EE_Error
205
+	 */
206
+	public function set_phone($phone = '')
207
+	{
208
+		$this->set('ATT_phone', $phone);
209
+	}
210
+
211
+
212
+	/**
213
+	 *        set deleted
214
+	 *
215
+	 * @access        public
216
+	 * @param        bool $ATT_deleted
217
+	 * @throws EE_Error
218
+	 */
219
+	public function set_deleted($ATT_deleted = false)
220
+	{
221
+		$this->set('ATT_deleted', $ATT_deleted);
222
+	}
223
+
224
+
225
+	/**
226
+	 * Returns the value for the post_author id saved with the cpt
227
+	 *
228
+	 * @since 4.5.0
229
+	 * @return int
230
+	 * @throws EE_Error
231
+	 */
232
+	public function wp_user()
233
+	{
234
+		return $this->get('ATT_author');
235
+	}
236
+
237
+
238
+	/**
239
+	 *        get Attendee First Name
240
+	 *
241
+	 * @access        public
242
+	 * @return string
243
+	 * @throws EE_Error
244
+	 */
245
+	public function fname()
246
+	{
247
+		return $this->get('ATT_fname');
248
+	}
249
+
250
+
251
+	/**
252
+	 * echoes out the attendee's first name
253
+	 *
254
+	 * @return void
255
+	 * @throws EE_Error
256
+	 */
257
+	public function e_full_name()
258
+	{
259
+		echo esc_html($this->full_name());
260
+	}
261
+
262
+
263
+	/**
264
+	 * Returns the first and last name concatenated together with a space.
265
+	 *
266
+	 * @param bool $apply_html_entities
267
+	 * @return string
268
+	 * @throws EE_Error
269
+	 */
270
+	public function full_name($apply_html_entities = false)
271
+	{
272
+		$full_name = array(
273
+			$this->fname(),
274
+			$this->lname(),
275
+		);
276
+		$full_name = array_filter($full_name);
277
+		$full_name = implode(' ', $full_name);
278
+		return $apply_html_entities ? htmlentities($full_name, ENT_QUOTES, 'UTF-8') : $full_name;
279
+	}
280
+
281
+
282
+	/**
283
+	 * This returns the value of the `ATT_full_name` field which is usually equivalent to calling `full_name()` unless
284
+	 * the post_title field has been directly modified in the db for the post (espresso_attendees post type) for this
285
+	 * attendee.
286
+	 *
287
+	 * @param bool $apply_html_entities
288
+	 * @return string
289
+	 * @throws EE_Error
290
+	 */
291
+	public function ATT_full_name($apply_html_entities = false)
292
+	{
293
+		return $apply_html_entities
294
+			? htmlentities($this->get('ATT_full_name'), ENT_QUOTES, 'UTF-8')
295
+			: $this->get('ATT_full_name');
296
+	}
297
+
298
+
299
+	/**
300
+	 *        get Attendee Last Name
301
+	 *
302
+	 * @access        public
303
+	 * @return string
304
+	 * @throws EE_Error
305
+	 */
306
+	public function lname()
307
+	{
308
+		return $this->get('ATT_lname');
309
+	}
310
+
311
+
312
+	/**
313
+	 * get Attendee bio
314
+	 *
315
+	 * @access public
316
+	 * @return string
317
+	 * @throws EE_Error
318
+	 */
319
+	public function bio()
320
+	{
321
+		return $this->get('ATT_bio');
322
+	}
323
+
324
+
325
+	/**
326
+	 * get Attendee short bio
327
+	 *
328
+	 * @access public
329
+	 * @return string
330
+	 * @throws EE_Error
331
+	 */
332
+	public function short_bio()
333
+	{
334
+		return $this->get('ATT_short_bio');
335
+	}
336
+
337
+
338
+	/**
339
+	 * Gets the attendee's full address as an array so client code can decide hwo to display it
340
+	 *
341
+	 * @return array numerically indexed, with each part of the address that is known.
342
+	 * Eg, if the user only responded to state and country,
343
+	 * it would be array(0=>'Alabama',1=>'USA')
344
+	 * @return array
345
+	 * @throws EE_Error
346
+	 */
347
+	public function full_address_as_array()
348
+	{
349
+		$full_address_array = array();
350
+		$initial_address_fields = array('ATT_address', 'ATT_address2', 'ATT_city',);
351
+		foreach ($initial_address_fields as $address_field_name) {
352
+			$address_fields_value = $this->get($address_field_name);
353
+			if (! empty($address_fields_value)) {
354
+				$full_address_array[] = $address_fields_value;
355
+			}
356
+		}
357
+		// now handle state and country
358
+		$state_obj = $this->state_obj();
359
+		if ($state_obj instanceof EE_State) {
360
+			$full_address_array[] = $state_obj->name();
361
+		}
362
+		$country_obj = $this->country_obj();
363
+		if ($country_obj instanceof EE_Country) {
364
+			$full_address_array[] = $country_obj->name();
365
+		}
366
+		// lastly get the xip
367
+		$zip_value = $this->zip();
368
+		if (! empty($zip_value)) {
369
+			$full_address_array[] = $zip_value;
370
+		}
371
+		return $full_address_array;
372
+	}
373
+
374
+
375
+	/**
376
+	 *        get Attendee Address
377
+	 *
378
+	 * @return string
379
+	 * @throws EE_Error
380
+	 */
381
+	public function address()
382
+	{
383
+		return $this->get('ATT_address');
384
+	}
385
+
386
+
387
+	/**
388
+	 *        get Attendee Address2
389
+	 *
390
+	 * @return string
391
+	 * @throws EE_Error
392
+	 */
393
+	public function address2()
394
+	{
395
+		return $this->get('ATT_address2');
396
+	}
397
+
398
+
399
+	/**
400
+	 *        get Attendee City
401
+	 *
402
+	 * @return string
403
+	 * @throws EE_Error
404
+	 */
405
+	public function city()
406
+	{
407
+		return $this->get('ATT_city');
408
+	}
409
+
410
+
411
+	/**
412
+	 *        get Attendee State ID
413
+	 *
414
+	 * @return string
415
+	 * @throws EE_Error
416
+	 */
417
+	public function state_ID()
418
+	{
419
+		return $this->get('STA_ID');
420
+	}
421
+
422
+
423
+	/**
424
+	 * @return string
425
+	 * @throws EE_Error
426
+	 */
427
+	public function state_abbrev()
428
+	{
429
+		return $this->state_obj() instanceof EE_State ? $this->state_obj()->abbrev() : '';
430
+	}
431
+
432
+
433
+	/**
434
+	 * Gets the state set to this attendee
435
+	 *
436
+	 * @return EE_State
437
+	 * @throws EE_Error
438
+	 */
439
+	public function state_obj()
440
+	{
441
+		return $this->get_first_related('State');
442
+	}
443
+
444
+
445
+	/**
446
+	 * Returns the state's name, otherwise 'Unknown'
447
+	 *
448
+	 * @return string
449
+	 * @throws EE_Error
450
+	 */
451
+	public function state_name()
452
+	{
453
+		if ($this->state_obj()) {
454
+			return $this->state_obj()->name();
455
+		} else {
456
+			return '';
457
+		}
458
+	}
459
+
460
+
461
+	/**
462
+	 * either displays the state abbreviation or the state name, as determined
463
+	 * by the "FHEE__EEI_Address__state__use_abbreviation" filter.
464
+	 * defaults to abbreviation
465
+	 *
466
+	 * @return string
467
+	 * @throws EE_Error
468
+	 */
469
+	public function state()
470
+	{
471
+		if (apply_filters('FHEE__EEI_Address__state__use_abbreviation', true, $this->state_obj())) {
472
+			return $this->state_abbrev();
473
+		}
474
+		return $this->state_name();
475
+	}
476
+
477
+
478
+	/**
479
+	 *    get Attendee Country ISO Code
480
+	 *
481
+	 * @return string
482
+	 * @throws EE_Error
483
+	 */
484
+	public function country_ID()
485
+	{
486
+		return $this->get('CNT_ISO');
487
+	}
488
+
489
+
490
+	/**
491
+	 * Gets country set for this attendee
492
+	 *
493
+	 * @return EE_Country
494
+	 * @throws EE_Error
495
+	 */
496
+	public function country_obj()
497
+	{
498
+		return $this->get_first_related('Country');
499
+	}
500
+
501
+
502
+	/**
503
+	 * Returns the country's name if known, otherwise 'Unknown'
504
+	 *
505
+	 * @return string
506
+	 * @throws EE_Error
507
+	 */
508
+	public function country_name()
509
+	{
510
+		if ($this->country_obj()) {
511
+			return $this->country_obj()->name();
512
+		}
513
+		return '';
514
+	}
515
+
516
+
517
+	/**
518
+	 * either displays the country ISO2 code or the country name, as determined
519
+	 * by the "FHEE__EEI_Address__country__use_abbreviation" filter.
520
+	 * defaults to abbreviation
521
+	 *
522
+	 * @return string
523
+	 * @throws EE_Error
524
+	 */
525
+	public function country()
526
+	{
527
+		if (apply_filters('FHEE__EEI_Address__country__use_abbreviation', true, $this->country_obj())) {
528
+			return $this->country_ID();
529
+		}
530
+		return $this->country_name();
531
+	}
532
+
533
+
534
+	/**
535
+	 *        get Attendee Zip/Postal Code
536
+	 *
537
+	 * @return string
538
+	 * @throws EE_Error
539
+	 */
540
+	public function zip()
541
+	{
542
+		return $this->get('ATT_zip');
543
+	}
544
+
545
+
546
+	/**
547
+	 *        get Attendee Email Address
548
+	 *
549
+	 * @return string
550
+	 * @throws EE_Error
551
+	 */
552
+	public function email()
553
+	{
554
+		return $this->get('ATT_email');
555
+	}
556
+
557
+
558
+	/**
559
+	 *        get Attendee Phone #
560
+	 *
561
+	 * @return string
562
+	 * @throws EE_Error
563
+	 */
564
+	public function phone()
565
+	{
566
+		return $this->get('ATT_phone');
567
+	}
568
+
569
+
570
+	/**
571
+	 *    get deleted
572
+	 *
573
+	 * @return        bool
574
+	 * @throws EE_Error
575
+	 */
576
+	public function deleted()
577
+	{
578
+		return $this->get('ATT_deleted');
579
+	}
580
+
581
+
582
+	/**
583
+	 * Gets registrations of this attendee
584
+	 *
585
+	 * @param array $query_params
586
+	 * @return EE_Registration[]
587
+	 * @throws EE_Error
588
+	 */
589
+	public function get_registrations($query_params = array())
590
+	{
591
+		return $this->get_many_related('Registration', $query_params);
592
+	}
593
+
594
+
595
+	/**
596
+	 * Gets the most recent registration of this attendee
597
+	 *
598
+	 * @return EE_Registration
599
+	 * @throws EE_Error
600
+	 */
601
+	public function get_most_recent_registration()
602
+	{
603
+		return $this->get_first_related(
604
+			'Registration',
605
+			array('order_by' => array('REG_date' => 'DESC'))
606
+		); // null, 'REG_date', 'DESC', '=', 'OBJECT_K');
607
+	}
608
+
609
+
610
+	/**
611
+	 * Gets the most recent registration for this attend at this event
612
+	 *
613
+	 * @param int $event_id
614
+	 * @return EE_Registration
615
+	 * @throws EE_Error
616
+	 */
617
+	public function get_most_recent_registration_for_event($event_id)
618
+	{
619
+		return $this->get_first_related(
620
+			'Registration',
621
+			array(array('EVT_ID' => $event_id), 'order_by' => array('REG_date' => 'DESC'))
622
+		);
623
+	}
624
+
625
+
626
+	/**
627
+	 * returns any events attached to this attendee ($_Event property);
628
+	 *
629
+	 * @return array
630
+	 * @throws EE_Error
631
+	 */
632
+	public function events()
633
+	{
634
+		return $this->get_many_related('Event');
635
+	}
636
+
637
+
638
+	/**
639
+	 * Gets the billing info array where keys match espresso_reg_page_billing_inputs(),
640
+	 * and keys are their cleaned values. @see EE_Attendee::save_and_clean_billing_info_for_payment_method() which was
641
+	 * used to save the billing info
642
+	 *
643
+	 * @param EE_Payment_Method $payment_method the _gateway_name property on the gateway class
644
+	 * @return EE_Form_Section_Proper|null
645
+	 * @throws EE_Error
646
+	 */
647
+	public function billing_info_for_payment_method($payment_method)
648
+	{
649
+		$pm_type = $payment_method->type_obj();
650
+		if (! $pm_type instanceof EE_PMT_Base) {
651
+			return null;
652
+		}
653
+		$billing_info = $this->get_post_meta($this->get_billing_info_postmeta_name($payment_method), true);
654
+		if (! $billing_info) {
655
+			return null;
656
+		}
657
+		$billing_form = $pm_type->billing_form();
658
+		// double-check the form isn't totally hidden, in which case pretend there is no form
659
+		$form_totally_hidden = true;
660
+		foreach ($billing_form->inputs_in_subsections() as $input) {
661
+			if (! $input->get_display_strategy() instanceof EE_Hidden_Display_Strategy) {
662
+				$form_totally_hidden = false;
663
+				break;
664
+			}
665
+		}
666
+		if ($form_totally_hidden) {
667
+			return null;
668
+		}
669
+		if ($billing_form instanceof EE_Form_Section_Proper) {
670
+			$billing_form->receive_form_submission(array($billing_form->name() => $billing_info), false);
671
+		}
672
+
673
+		return $billing_form;
674
+	}
675
+
676
+
677
+	/**
678
+	 * Gets the postmeta key that holds this attendee's billing info for the
679
+	 * specified payment method
680
+	 *
681
+	 * @param EE_Payment_Method $payment_method
682
+	 * @return string
683
+	 * @throws EE_Error
684
+	 */
685
+	public function get_billing_info_postmeta_name($payment_method)
686
+	{
687
+		if ($payment_method->type_obj() instanceof EE_PMT_Base) {
688
+			return 'billing_info_' . $payment_method->type_obj()->system_name();
689
+		}
690
+		return null;
691
+	}
692
+
693
+
694
+	/**
695
+	 * Saves the billing info to the attendee. @see EE_Attendee::billing_info_for_payment_method() which is used to
696
+	 * retrieve it
697
+	 *
698
+	 * @param EE_Billing_Attendee_Info_Form $billing_form
699
+	 * @param EE_Payment_Method             $payment_method
700
+	 * @return boolean
701
+	 * @throws EE_Error
702
+	 */
703
+	public function save_and_clean_billing_info_for_payment_method($billing_form, $payment_method)
704
+	{
705
+		if (! $billing_form instanceof EE_Billing_Attendee_Info_Form) {
706
+			EE_Error::add_error(esc_html__('Cannot save billing info because there is none.', 'event_espresso'));
707
+			return false;
708
+		}
709
+		$billing_form->clean_sensitive_data();
710
+		return update_post_meta(
711
+			$this->ID(),
712
+			$this->get_billing_info_postmeta_name($payment_method),
713
+			$billing_form->input_values(true)
714
+		);
715
+	}
716
+
717
+
718
+	/**
719
+	 * Return the link to the admin details for the object.
720
+	 *
721
+	 * @return string
722
+	 * @throws EE_Error
723
+	 * @throws InvalidArgumentException
724
+	 * @throws InvalidDataTypeException
725
+	 * @throws InvalidInterfaceException
726
+	 * @throws ReflectionException
727
+	 */
728
+	public function get_admin_details_link()
729
+	{
730
+		return $this->get_admin_edit_link();
731
+	}
732
+
733
+
734
+	/**
735
+	 * Returns the link to the editor for the object.  Sometimes this is the same as the details.
736
+	 *
737
+	 * @return string
738
+	 * @throws EE_Error
739
+	 * @throws InvalidArgumentException
740
+	 * @throws ReflectionException
741
+	 * @throws InvalidDataTypeException
742
+	 * @throws InvalidInterfaceException
743
+	 */
744
+	public function get_admin_edit_link()
745
+	{
746
+		EE_Registry::instance()->load_helper('URL');
747
+		return EEH_URL::add_query_args_and_nonce(
748
+			array(
749
+				'page'   => 'espresso_registrations',
750
+				'action' => 'edit_attendee',
751
+				'post'   => $this->ID(),
752
+			),
753
+			admin_url('admin.php')
754
+		);
755
+	}
756
+
757
+
758
+	/**
759
+	 * Returns the link to a settings page for the object.
760
+	 *
761
+	 * @return string
762
+	 * @throws EE_Error
763
+	 * @throws InvalidArgumentException
764
+	 * @throws InvalidDataTypeException
765
+	 * @throws InvalidInterfaceException
766
+	 * @throws ReflectionException
767
+	 */
768
+	public function get_admin_settings_link()
769
+	{
770
+		return $this->get_admin_edit_link();
771
+	}
772
+
773
+
774
+	/**
775
+	 * Returns the link to the "overview" for the object (typically the "list table" view).
776
+	 *
777
+	 * @return string
778
+	 * @throws EE_Error
779
+	 * @throws InvalidArgumentException
780
+	 * @throws ReflectionException
781
+	 * @throws InvalidDataTypeException
782
+	 * @throws InvalidInterfaceException
783
+	 */
784
+	public function get_admin_overview_link()
785
+	{
786
+		EE_Registry::instance()->load_helper('URL');
787
+		return EEH_URL::add_query_args_and_nonce(
788
+			array(
789
+				'page'   => 'espresso_registrations',
790
+				'action' => 'contact_list',
791
+			),
792
+			admin_url('admin.php')
793
+		);
794
+	}
795 795
 }
Please login to merge, or discard this patch.
core/EE_Payment_Processor.core.php 2 patches
Spacing   +5 added lines, -5 removed lines patch added patch discarded remove patch
@@ -33,7 +33,7 @@  discard block
 block discarded – undo
33 33
     public static function instance()
34 34
     {
35 35
         // check if class object is instantiated
36
-        if (! self::$_instance instanceof EE_Payment_Processor) {
36
+        if ( ! self::$_instance instanceof EE_Payment_Processor) {
37 37
             self::$_instance = new self();
38 38
         }
39 39
         return self::$_instance;
@@ -170,7 +170,7 @@  discard block
 block discarded – undo
170 170
         /** @type \EE_Transaction $transaction */
171 171
         $transaction = EEM_Transaction::instance()->ensure_is_obj($transaction);
172 172
         $primary_reg = $transaction->primary_registration();
173
-        if (! $primary_reg instanceof EE_Registration) {
173
+        if ( ! $primary_reg instanceof EE_Registration) {
174 174
             throw new EE_Error(
175 175
                 sprintf(
176 176
                     esc_html__(
@@ -265,7 +265,7 @@  discard block
 block discarded – undo
265 265
                         EEM_Change_Log::instance()->log(
266 266
                             EEM_Change_Log::type_gateway,
267 267
                             array(
268
-                                'message'     => 'IPN Exception: ' . $e->getMessage(),
268
+                                'message'     => 'IPN Exception: '.$e->getMessage(),
269 269
                                 'current_url' => EEH_URL::current_url(),
270 270
                                 'payment'     => $e->getPaymentProperties(),
271 271
                                 'IPN_data'    => $e->getIpnData(),
@@ -309,7 +309,7 @@  discard block
 block discarded – undo
309 309
                         EEM_Change_Log::instance()->log(
310 310
                             EEM_Change_Log::type_gateway,
311 311
                             array(
312
-                                'message'     => 'IPN Exception: ' . $e->getMessage(),
312
+                                'message'     => 'IPN Exception: '.$e->getMessage(),
313 313
                                 'current_url' => EEH_URL::current_url(),
314 314
                                 'payment'     => $e->getPaymentProperties(),
315 315
                                 'IPN_data'    => $e->getIpnData(),
@@ -380,7 +380,7 @@  discard block
 block discarded – undo
380 380
     {
381 381
         $return_data = array();
382 382
         foreach ($request_data as $key => $value) {
383
-            $return_data[ $this->_remove_unusable_characters($key) ] = $this->_remove_unusable_characters(
383
+            $return_data[$this->_remove_unusable_characters($key)] = $this->_remove_unusable_characters(
384 384
                 $value
385 385
             );
386 386
         }
Please login to merge, or discard this patch.
Indentation   +842 added lines, -842 removed lines patch added patch discarded remove patch
@@ -17,846 +17,846 @@
 block discarded – undo
17 17
  */
18 18
 class EE_Payment_Processor extends EE_Processor_Base implements ResettableInterface
19 19
 {
20
-    /**
21
-     * @var EE_Payment_Processor $_instance
22
-     * @access    private
23
-     */
24
-    private static $_instance;
25
-
26
-
27
-    /**
28
-     * @singleton method used to instantiate class object
29
-     * @access    public
30
-     * @return EE_Payment_Processor instance
31
-     */
32
-    public static function instance()
33
-    {
34
-        // check if class object is instantiated
35
-        if (! self::$_instance instanceof EE_Payment_Processor) {
36
-            self::$_instance = new self();
37
-        }
38
-        return self::$_instance;
39
-    }
40
-
41
-
42
-    /**
43
-     * @return EE_Payment_Processor
44
-     */
45
-    public static function reset()
46
-    {
47
-        self::$_instance = null;
48
-        return self::instance();
49
-    }
50
-
51
-
52
-    /**
53
-     *private constructor to prevent direct creation
54
-     *
55
-     * @Constructor
56
-     * @access private
57
-     */
58
-    private function __construct()
59
-    {
60
-        do_action('AHEE__EE_Payment_Processor__construct');
61
-        add_action('http_api_curl', array($this, '_curl_requests_to_paypal_use_tls'), 10, 3);
62
-    }
63
-
64
-
65
-    /**
66
-     * Using the selected gateway, processes the payment for that transaction, and updates the transaction
67
-     * appropriately. Saves the payment that is generated
68
-     *
69
-     * @param EE_Payment_Method    $payment_method
70
-     * @param EE_Transaction       $transaction
71
-     * @param float|null                $amount       if only part of the transaction is to be paid for, how much.
72
-     *                                           Leave null if payment is for the full amount owing
73
-     * @param EE_Billing_Info_Form|null $billing_form (or probably null, if it's an offline or offsite payment method).
74
-     *                                           Receive_form_submission() should have
75
-     *                                           already been called on the billing form
76
-     *                                           (ie, its inputs should have their normalized values set).
77
-     * @param string|null          $return_url   string used mostly by offsite gateways to specify
78
-     *                                           where to go AFTER the offsite gateway
79
-     * @param string               $method       like 'CART', indicates who the client who called this was
80
-     * @param bool                 $by_admin     TRUE if payment is being attempted from the admin
81
-     * @param bool                 $update_txn   whether or not to call
82
-     *                                           EE_Transaction_Processor::update_transaction_and_registrations_after_checkout_or_payment()
83
-     * @param string               $cancel_url   URL to return to if off-site payments are cancelled
84
-     * @return EE_Payment
85
-     * @throws EE_Error
86
-     * @throws InvalidArgumentException
87
-     * @throws ReflectionException
88
-     * @throws RuntimeException
89
-     * @throws InvalidDataTypeException
90
-     * @throws InvalidInterfaceException
91
-     */
92
-    public function process_payment(
93
-        EE_Payment_Method $payment_method,
94
-        EE_Transaction $transaction,
95
-        ?float $amount = null,
96
-        ?EE_Billing_Info_Form $billing_form = null,
97
-        ?string $return_url = null,
98
-        string $method = 'CART',
99
-        bool $by_admin = false,
100
-        bool $update_txn = true,
101
-        string $cancel_url = ''
102
-    ): ?EE_Payment {
103
-        if ((float) $amount < 0) {
104
-            throw new EE_Error(
105
-                sprintf(
106
-                    esc_html__(
107
-                        'Attempting to make a payment for a negative amount of %1$d for transaction %2$d. That should be a refund',
108
-                        'event_espresso'
109
-                    ),
110
-                    $amount,
111
-                    $transaction->ID()
112
-                )
113
-            );
114
-        }
115
-        // verify payment method
116
-        $payment_method = EEM_Payment_Method::instance()->ensure_is_obj(
117
-            $payment_method,
118
-            true
119
-        );
120
-        // verify transaction
121
-        EEM_Transaction::instance()->ensure_is_obj($transaction);
122
-        $transaction->set_payment_method_ID($payment_method->ID());
123
-        // verify payment method type
124
-        if ($payment_method->type_obj() instanceof EE_PMT_Base) {
125
-            $payment = $payment_method->type_obj()->process_payment(
126
-                $transaction,
127
-                min($amount, $transaction->remaining()), // make sure we don't overcharge
128
-                $billing_form,
129
-                $return_url,
130
-                add_query_arg(array('ee_cancel_payment' => true), $cancel_url),
131
-                $method,
132
-                $by_admin
133
-            );
134
-            // check if payment method uses an off-site gateway
135
-            if ($payment_method->type_obj()->payment_occurs() !== EE_PMT_Base::offsite) {
136
-                // don't process payments for off-site gateways yet because no payment has occurred yet
137
-                $this->update_txn_based_on_payment($transaction, $payment, $update_txn);
138
-            }
139
-            return $payment;
140
-        }
141
-        EE_Error::add_error(
142
-            sprintf(
143
-                esc_html__(
144
-                    'A valid payment method could not be determined due to a technical issue.%sPlease try again or contact %s for assistance.',
145
-                    'event_espresso'
146
-                ),
147
-                '<br/>',
148
-                EE_Registry::instance()->CFG->organization->get_pretty('email')
149
-            ),
150
-            __FILE__,
151
-            __FUNCTION__,
152
-            __LINE__
153
-        );
154
-        return null;
155
-    }
156
-
157
-
158
-    /**
159
-     * @param EE_Transaction|int $transaction
160
-     * @param EE_Payment_Method  $payment_method
161
-     * @return string
162
-     * @throws EE_Error
163
-     * @throws InvalidArgumentException
164
-     * @throws InvalidDataTypeException
165
-     * @throws InvalidInterfaceException
166
-     */
167
-    public function get_ipn_url_for_payment_method($transaction, $payment_method)
168
-    {
169
-        /** @type \EE_Transaction $transaction */
170
-        $transaction = EEM_Transaction::instance()->ensure_is_obj($transaction);
171
-        $primary_reg = $transaction->primary_registration();
172
-        if (! $primary_reg instanceof EE_Registration) {
173
-            throw new EE_Error(
174
-                sprintf(
175
-                    esc_html__(
176
-                        'Cannot get IPN URL for transaction with ID %d because it has no primary registration',
177
-                        'event_espresso'
178
-                    ),
179
-                    $transaction->ID()
180
-                )
181
-            );
182
-        }
183
-        $payment_method = EEM_Payment_Method::instance()->ensure_is_obj(
184
-            $payment_method,
185
-            true
186
-        );
187
-        $url = add_query_arg(
188
-            array(
189
-                'e_reg_url_link'    => $primary_reg->reg_url_link(),
190
-                'ee_payment_method' => $payment_method->slug(),
191
-            ),
192
-            EE_Registry::instance()->CFG->core->txn_page_url()
193
-        );
194
-        return $url;
195
-    }
196
-
197
-
198
-    /**
199
-     * Process the IPN. Firstly, we'll hope we put the standard args into the IPN URL so
200
-     * we can easily find what registration the IPN is for and what payment method.
201
-     * However, if not, we'll give all payment methods a chance to claim it and process it.
202
-     * If a payment is found for the IPN info, it is saved.
203
-     *
204
-     * @param array              $_req_data            form post data
205
-     * @param EE_Transaction|int $transaction          optional (or a transactions id)
206
-     * @param EE_Payment_Method  $payment_method       (or a slug or id of one)
207
-     * @param boolean            $update_txn           whether or not to call
208
-     *                                                 EE_Transaction_Processor::update_transaction_and_registrations_after_checkout_or_payment()
209
-     * @param bool               $separate_IPN_request whether the IPN uses a separate request (true, like PayPal)
210
-     *                                                 or is processed manually (false, like Authorize.net)
211
-     * @throws EE_Error
212
-     * @throws Exception
213
-     * @return EE_Payment
214
-     * @throws \RuntimeException
215
-     * @throws \ReflectionException
216
-     * @throws \InvalidArgumentException
217
-     * @throws InvalidInterfaceException
218
-     * @throws InvalidDataTypeException
219
-     */
220
-    public function process_ipn(
221
-        $_req_data,
222
-        $transaction = null,
223
-        $payment_method = null,
224
-        $update_txn = true,
225
-        $separate_IPN_request = true
226
-    ) {
227
-        EE_Registry::instance()->load_model('Change_Log');
228
-        $_req_data = $this->_remove_unusable_characters_from_array((array) $_req_data);
229
-        EE_Processor_Base::set_IPN($separate_IPN_request);
230
-        $obj_for_log = null;
231
-        if ($transaction instanceof EE_Transaction) {
232
-            $obj_for_log = $transaction;
233
-            if ($payment_method instanceof EE_Payment_Method) {
234
-                $obj_for_log = EEM_Payment::instance()->get_one(
235
-                    array(
236
-                        array('TXN_ID' => $transaction->ID(), 'PMD_ID' => $payment_method->ID()),
237
-                        'order_by' => array('PAY_timestamp' => 'desc'),
238
-                    )
239
-                );
240
-            }
241
-        } elseif ($payment_method instanceof EE_Payment) {
242
-            $obj_for_log = $payment_method;
243
-        }
244
-        $log = EEM_Change_Log::instance()->log(
245
-            EEM_Change_Log::type_gateway,
246
-            array('IPN data received' => $_req_data),
247
-            $obj_for_log
248
-        );
249
-        try {
250
-            /**
251
-             * @var EE_Payment $payment
252
-             */
253
-            $payment = null;
254
-            if ($transaction && $payment_method) {
255
-                /** @type EE_Transaction $transaction */
256
-                $transaction = EEM_Transaction::instance()->ensure_is_obj($transaction);
257
-                /** @type EE_Payment_Method $payment_method */
258
-                $payment_method = EEM_Payment_Method::instance()->ensure_is_obj($payment_method);
259
-                if ($payment_method->type_obj() instanceof EE_PMT_Base) {
260
-                    try {
261
-                        $payment = $payment_method->type_obj()->handle_ipn($_req_data, $transaction);
262
-                        $log->set_object($payment);
263
-                    } catch (EventEspresso\core\exceptions\IpnException $e) {
264
-                        EEM_Change_Log::instance()->log(
265
-                            EEM_Change_Log::type_gateway,
266
-                            array(
267
-                                'message'     => 'IPN Exception: ' . $e->getMessage(),
268
-                                'current_url' => EEH_URL::current_url(),
269
-                                'payment'     => $e->getPaymentProperties(),
270
-                                'IPN_data'    => $e->getIpnData(),
271
-                            ),
272
-                            $obj_for_log
273
-                        );
274
-                        return $e->getPayment();
275
-                    }
276
-                } else {
277
-                    // not a payment
278
-                    EE_Error::add_error(
279
-                        sprintf(
280
-                            esc_html__(
281
-                                'A valid payment method could not be determined due to a technical issue.%sPlease refresh your browser and try again or contact %s for assistance.',
282
-                                'event_espresso'
283
-                            ),
284
-                            '<br/>',
285
-                            EE_Registry::instance()->CFG->organization->get_pretty('email')
286
-                        ),
287
-                        __FILE__,
288
-                        __FUNCTION__,
289
-                        __LINE__
290
-                    );
291
-                }
292
-            } else {
293
-                // that's actually pretty ok. The IPN just wasn't able
294
-                // to identify which transaction or payment method this was for
295
-                // give all active payment methods a chance to claim it
296
-                $active_payment_methods = EEM_Payment_Method::instance()->get_all_active();
297
-                foreach ($active_payment_methods as $active_payment_method) {
298
-                    try {
299
-                        $payment = $active_payment_method->type_obj()->handle_unclaimed_ipn($_req_data);
300
-                        $payment_method = $active_payment_method;
301
-                        EEM_Change_Log::instance()->log(
302
-                            EEM_Change_Log::type_gateway,
303
-                            array('IPN data' => $_req_data),
304
-                            $payment
305
-                        );
306
-                        break;
307
-                    } catch (EventEspresso\core\exceptions\IpnException $e) {
308
-                        EEM_Change_Log::instance()->log(
309
-                            EEM_Change_Log::type_gateway,
310
-                            array(
311
-                                'message'     => 'IPN Exception: ' . $e->getMessage(),
312
-                                'current_url' => EEH_URL::current_url(),
313
-                                'payment'     => $e->getPaymentProperties(),
314
-                                'IPN_data'    => $e->getIpnData(),
315
-                            ),
316
-                            $obj_for_log
317
-                        );
318
-                        return $e->getPayment();
319
-                    } catch (EE_Error $e) {
320
-                        // that's fine- it apparently couldn't handle the IPN
321
-                    }
322
-                }
323
-            }
324
-            // EEM_Payment_Log::instance()->log("got to 7",$transaction,$payment_method);
325
-            if ($payment instanceof EE_Payment) {
326
-                $payment->save();
327
-                //  update the TXN
328
-                $this->update_txn_based_on_payment(
329
-                    $transaction,
330
-                    $payment,
331
-                    $update_txn,
332
-                    $separate_IPN_request
333
-                );
334
-            } else {
335
-                // we couldn't find the payment for this IPN... let's try and log at least SOMETHING
336
-                if ($payment_method) {
337
-                    EEM_Change_Log::instance()->log(
338
-                        EEM_Change_Log::type_gateway,
339
-                        array('IPN data' => $_req_data),
340
-                        $payment_method
341
-                    );
342
-                } elseif ($transaction) {
343
-                    EEM_Change_Log::instance()->log(
344
-                        EEM_Change_Log::type_gateway,
345
-                        array('IPN data' => $_req_data),
346
-                        $transaction
347
-                    );
348
-                }
349
-            }
350
-            return $payment;
351
-        } catch (EE_Error $e) {
352
-            do_action(
353
-                'AHEE__log',
354
-                __FILE__,
355
-                __FUNCTION__,
356
-                sprintf(
357
-                    esc_html__(
358
-                        'Error occurred while receiving IPN. Transaction: %1$s, req data: %2$s. The error was "%3$s"',
359
-                        'event_espresso'
360
-                    ),
361
-                    print_r($transaction, true),
362
-                    print_r($_req_data, true),
363
-                    $e->getMessage()
364
-                )
365
-            );
366
-            throw $e;
367
-        }
368
-    }
369
-
370
-
371
-    /**
372
-     * Removes any non-printable illegal characters from the input,
373
-     * which might cause a raucous when trying to insert into the database
374
-     *
375
-     * @param  array $request_data
376
-     * @return array
377
-     */
378
-    protected function _remove_unusable_characters_from_array(array $request_data)
379
-    {
380
-        $return_data = array();
381
-        foreach ($request_data as $key => $value) {
382
-            $return_data[ $this->_remove_unusable_characters($key) ] = $this->_remove_unusable_characters(
383
-                $value
384
-            );
385
-        }
386
-        return $return_data;
387
-    }
388
-
389
-
390
-    /**
391
-     * Removes any non-printable illegal characters from the input,
392
-     * which might cause a raucous when trying to insert into the database
393
-     *
394
-     * @param string $request_data
395
-     * @return string
396
-     */
397
-    protected function _remove_unusable_characters($request_data)
398
-    {
399
-        return preg_replace('/[^[:print:]]/', '', $request_data);
400
-    }
401
-
402
-
403
-    /**
404
-     * Should be called just before displaying the payment attempt results to the user,
405
-     * when the payment attempt has finished. Some payment methods may have special
406
-     * logic to perform here. For example, if process_payment() happens on a special request
407
-     * and then the user is redirected to a page that displays the payment's status, this
408
-     * should be called while loading the page that displays the payment's status. If the user is
409
-     * sent to an offsite payment provider, this should be called upon returning from that offsite payment
410
-     * provider.
411
-     *
412
-     * @param EE_Transaction|int $transaction
413
-     * @param bool               $update_txn whether or not to call
414
-     *                                       EE_Transaction_Processor::update_transaction_and_registrations_after_checkout_or_payment()
415
-     * @return EE_Payment
416
-     * @throws EE_Error
417
-     * @throws InvalidArgumentException
418
-     * @throws ReflectionException
419
-     * @throws RuntimeException
420
-     * @throws InvalidDataTypeException
421
-     * @throws InvalidInterfaceException
422
-     * @deprecated 4.6.24 method is no longer used. Instead it is up to client code, like SPCO,
423
-     *                                       to call handle_ipn() for offsite gateways that don't receive separate IPNs
424
-     */
425
-    public function finalize_payment_for($transaction, $update_txn = true)
426
-    {
427
-        /** @var $transaction EE_Transaction */
428
-        $transaction = EEM_Transaction::instance()->ensure_is_obj($transaction);
429
-        $last_payment_method = $transaction->payment_method();
430
-        if ($last_payment_method instanceof EE_Payment_Method) {
431
-            $payment = $last_payment_method->type_obj()->finalize_payment_for($transaction);
432
-            $this->update_txn_based_on_payment($transaction, $payment, $update_txn);
433
-            return $payment;
434
-        }
435
-        return null;
436
-    }
437
-
438
-
439
-    /**
440
-     * Processes a direct refund request, saves the payment, and updates the transaction appropriately.
441
-     *
442
-     * @param EE_Payment_Method $payment_method
443
-     * @param EE_Payment        $payment_to_refund
444
-     * @param array             $refund_info
445
-     * @return EE_Payment
446
-     * @throws EE_Error
447
-     * @throws InvalidArgumentException
448
-     * @throws ReflectionException
449
-     * @throws RuntimeException
450
-     * @throws InvalidDataTypeException
451
-     * @throws InvalidInterfaceException
452
-     */
453
-    public function process_refund(
454
-        EE_Payment_Method $payment_method,
455
-        EE_Payment $payment_to_refund,
456
-        array $refund_info = array()
457
-    ) {
458
-        if ($payment_method instanceof EE_Payment_Method && $payment_method->type_obj()->supports_sending_refunds()) {
459
-            $payment_method->type_obj()->process_refund($payment_to_refund, $refund_info);
460
-            $this->update_txn_based_on_payment($payment_to_refund->transaction(), $payment_to_refund);
461
-        }
462
-        return $payment_to_refund;
463
-    }
464
-
465
-
466
-    /**
467
-     * This should be called each time there may have been an update to a
468
-     * payment on a transaction (ie, we asked for a payment to process a
469
-     * payment for a transaction, or we told a payment method about an IPN, or
470
-     * we told a payment method to
471
-     * "finalize_payment_for" (a transaction), or we told a payment method to
472
-     * process a refund. This should handle firing the correct hooks to
473
-     * indicate
474
-     * what exactly happened and updating the transaction appropriately). This
475
-     * could be integrated directly into EE_Transaction upon save, but we want
476
-     * this logic to be separate from 'normal' plain-jane saving and updating
477
-     * of transactions and payments, and to be tied to payment processing.
478
-     * Note: this method DOES NOT save the payment passed into it. It is the responsibility
479
-     * of previous code to decide whether or not to save (because the payment passed into
480
-     * this method might be a temporary, never-to-be-saved payment from an offline gateway,
481
-     * in which case we only want that payment object for some temporary usage during this request,
482
-     * but we don't want it to be saved).
483
-     *
484
-     * @param EE_Transaction|int $transaction
485
-     * @param EE_Payment         $payment
486
-     * @param boolean            $update_txn
487
-     *                        whether or not to call
488
-     *                        EE_Transaction_Processor::
489
-     *                        update_transaction_and_registrations_after_checkout_or_payment()
490
-     *                        (you can save 1 DB query if you know you're going
491
-     *                        to save it later instead)
492
-     * @param bool               $IPN
493
-     *                        if processing IPNs or other similar payment
494
-     *                        related activities that occur in alternate
495
-     *                        requests than the main one that is processing the
496
-     *                        TXN, then set this to true to check whether the
497
-     *                        TXN is locked before updating
498
-     * @throws EE_Error
499
-     * @throws InvalidArgumentException
500
-     * @throws ReflectionException
501
-     * @throws RuntimeException
502
-     * @throws InvalidDataTypeException
503
-     * @throws InvalidInterfaceException
504
-     */
505
-    public function update_txn_based_on_payment($transaction, $payment, $update_txn = true, $IPN = false)
506
-    {
507
-        $do_action = 'AHEE__EE_Payment_Processor__update_txn_based_on_payment__not_successful';
508
-        /** @type EE_Transaction $transaction */
509
-        $transaction = EEM_Transaction::instance()->ensure_is_obj($transaction);
510
-        // can we freely update the TXN at this moment?
511
-        if ($IPN && $transaction->is_locked()) {
512
-            // don't update the transaction at this exact moment
513
-            // because the TXN is active in another request
514
-            EE_Cron_Tasks::schedule_update_transaction_with_payment(
515
-                time(),
516
-                $transaction->ID(),
517
-                $payment->ID()
518
-            );
519
-        } else {
520
-            // verify payment and that it has been saved
521
-            if ($payment instanceof EE_Payment && $payment->ID()) {
522
-                if (
523
-                    $payment->payment_method() instanceof EE_Payment_Method
524
-                    && $payment->payment_method()->type_obj() instanceof EE_PMT_Base
525
-                ) {
526
-                    $payment->payment_method()->type_obj()->update_txn_based_on_payment($payment);
527
-                    // update TXN registrations with payment info
528
-                    $this->process_registration_payments($transaction, $payment);
529
-                }
530
-                $do_action = $payment->just_approved()
531
-                    ? 'AHEE__EE_Payment_Processor__update_txn_based_on_payment__successful'
532
-                    : $do_action;
533
-            } else {
534
-                // send out notifications
535
-                add_filter('FHEE__EED_Messages___maybe_registration__deliver_notifications', '__return_true');
536
-                $do_action = 'AHEE__EE_Payment_Processor__update_txn_based_on_payment__no_payment_made';
537
-            }
538
-            if ($payment instanceof EE_Payment && $payment->status() !== EEM_Payment::status_id_failed) {
539
-                /** @type EE_Transaction_Payments $transaction_payments */
540
-                $transaction_payments = EE_Registry::instance()->load_class('Transaction_Payments');
541
-                // set new value for total paid
542
-                $transaction_payments->calculate_total_payments_and_update_status($transaction);
543
-                // call EE_Transaction_Processor::update_transaction_and_registrations_after_checkout_or_payment() ???
544
-                if ($update_txn) {
545
-                    $this->_post_payment_processing($transaction, $payment, $IPN);
546
-                }
547
-            }
548
-            // granular hook for others to use.
549
-            do_action($do_action, $transaction, $payment);
550
-            do_action('AHEE_log', __CLASS__, __FUNCTION__, $do_action, '$do_action');
551
-            // global hook for others to use.
552
-            do_action('AHEE__EE_Payment_Processor__update_txn_based_on_payment', $transaction, $payment);
553
-        }
554
-    }
555
-
556
-
557
-    /**
558
-     * update registrations REG_paid field after successful payment and link registrations with payment
559
-     *
560
-     * @param EE_Transaction    $transaction
561
-     * @param EE_Payment        $payment
562
-     * @param EE_Registration[] $registrations
563
-     * @throws EE_Error
564
-     * @throws InvalidArgumentException
565
-     * @throws RuntimeException
566
-     * @throws InvalidDataTypeException
567
-     * @throws InvalidInterfaceException
568
-     */
569
-    public function process_registration_payments(
570
-        EE_Transaction $transaction,
571
-        EE_Payment $payment,
572
-        array $registrations = array()
573
-    ) {
574
-        // only process if payment was successful
575
-        if ($payment->status() !== EEM_Payment::status_id_approved) {
576
-            return;
577
-        }
578
-        // EEM_Registration::instance()->show_next_x_db_queries();
579
-        if (empty($registrations)) {
580
-            // find registrations with monies owing that can receive a payment
581
-            $registrations = $transaction->registrations(
582
-                array(
583
-                    array(
584
-                        // only these reg statuses can receive payments
585
-                        'STS_ID'           => array('IN', EEM_Registration::reg_statuses_that_allow_payment()),
586
-                        'REG_final_price'  => array('!=', 0),
587
-                        'REG_final_price*' => array('!=', 'REG_paid', true),
588
-                    ),
589
-                )
590
-            );
591
-        }
592
-        // still nothing ??!??
593
-        if (empty($registrations)) {
594
-            return;
595
-        }
596
-        // todo: break out the following logic into a separate strategy class
597
-        // todo: named something like "Sequential_Reg_Payment_Strategy"
598
-        // todo: which would apply payments using the capitalist "first come first paid" approach
599
-        // todo: then have another strategy class like "Distributed_Reg_Payment_Strategy"
600
-        // todo: which would be the socialist "everybody gets a piece of pie" approach,
601
-        // todo: which would be better for deposits, where you want a bit of the payment applied to each registration
602
-        $refund = $payment->is_a_refund();
603
-        // how much is available to apply to registrations?
604
-        $available_payment_amount = abs($payment->amount());
605
-        foreach ($registrations as $registration) {
606
-            if ($registration instanceof EE_Registration) {
607
-                // nothing left?
608
-                if ($available_payment_amount <= 0) {
609
-                    break;
610
-                }
611
-                if ($refund) {
612
-                    $available_payment_amount = $this->process_registration_refund(
613
-                        $registration,
614
-                        $payment,
615
-                        $available_payment_amount
616
-                    );
617
-                } else {
618
-                    $available_payment_amount = $this->process_registration_payment(
619
-                        $registration,
620
-                        $payment,
621
-                        $available_payment_amount
622
-                    );
623
-                }
624
-            }
625
-        }
626
-        if (
627
-            $available_payment_amount > 0
628
-            && apply_filters(
629
-                'FHEE__EE_Payment_Processor__process_registration_payments__display_notifications',
630
-                false
631
-            )
632
-        ) {
633
-            EE_Error::add_attention(
634
-                sprintf(
635
-                    esc_html__(
636
-                        'A remainder of %1$s exists after applying this payment to Registration(s) %2$s.%3$sPlease verify that the original payment amount of %4$s is correct. If so, you should edit this payment and select at least one additional registration in the "Registrations to Apply Payment to" section, so that the remainder of this payment can be applied to the additional registration(s).',
637
-                        'event_espresso'
638
-                    ),
639
-                    EEH_Template::format_currency($available_payment_amount),
640
-                    implode(', ', array_keys($registrations)),
641
-                    '<br/>',
642
-                    EEH_Template::format_currency($payment->amount())
643
-                ),
644
-                __FILE__,
645
-                __FUNCTION__,
646
-                __LINE__
647
-            );
648
-        }
649
-    }
650
-
651
-
652
-    /**
653
-     * update registration REG_paid field after successful payment and link registration with payment
654
-     *
655
-     * @param EE_Registration $registration
656
-     * @param EE_Payment      $payment
657
-     * @param float           $available_payment_amount
658
-     * @return float
659
-     * @throws EE_Error
660
-     * @throws InvalidArgumentException
661
-     * @throws RuntimeException
662
-     * @throws InvalidDataTypeException
663
-     * @throws InvalidInterfaceException
664
-     */
665
-    public function process_registration_payment(
666
-        EE_Registration $registration,
667
-        EE_Payment $payment,
668
-        $available_payment_amount = 0.00
669
-    ) {
670
-        $owing = $registration->final_price() - $registration->paid();
671
-        if ($owing > 0) {
672
-            // don't allow payment amount to exceed the available payment amount, OR the amount owing
673
-            $payment_amount = min($available_payment_amount, $owing);
674
-            // update $available_payment_amount
675
-            $available_payment_amount -= $payment_amount;
676
-            // calculate and set new REG_paid
677
-            $registration->set_paid($registration->paid() + $payment_amount);
678
-            // now save it
679
-            $this->_apply_registration_payment($registration, $payment, $payment_amount);
680
-        }
681
-        return $available_payment_amount;
682
-    }
683
-
684
-
685
-    /**
686
-     * update registration REG_paid field after successful payment and link registration with payment
687
-     *
688
-     * @param EE_Registration $registration
689
-     * @param EE_Payment      $payment
690
-     * @param float           $payment_amount
691
-     * @return void
692
-     * @throws EE_Error
693
-     * @throws InvalidArgumentException
694
-     * @throws InvalidDataTypeException
695
-     * @throws InvalidInterfaceException
696
-     */
697
-    protected function _apply_registration_payment(
698
-        EE_Registration $registration,
699
-        EE_Payment $payment,
700
-        $payment_amount = 0.00
701
-    ) {
702
-        // find any existing reg payment records for this registration and payment
703
-        $existing_reg_payment = EEM_Registration_Payment::instance()->get_one(
704
-            array(array('REG_ID' => $registration->ID(), 'PAY_ID' => $payment->ID()))
705
-        );
706
-        // if existing registration payment exists
707
-        if ($existing_reg_payment instanceof EE_Registration_Payment) {
708
-            // then update that record
709
-            $existing_reg_payment->set_amount($payment_amount);
710
-            $existing_reg_payment->save();
711
-        } else {
712
-            // or add new relation between registration and payment and set amount
713
-            $registration->_add_relation_to(
714
-                $payment,
715
-                'Payment',
716
-                array('RPY_amount' => $payment_amount)
717
-            );
718
-            // make it stick
719
-            $registration->save();
720
-        }
721
-    }
722
-
723
-
724
-    /**
725
-     * update registration REG_paid field after refund and link registration with payment
726
-     *
727
-     * @param EE_Registration $registration
728
-     * @param EE_Payment      $payment
729
-     * @param float           $available_refund_amount - IMPORTANT !!! SEND AVAILABLE REFUND AMOUNT AS A POSITIVE NUMBER
730
-     * @return float
731
-     * @throws EE_Error
732
-     * @throws InvalidArgumentException
733
-     * @throws RuntimeException
734
-     * @throws InvalidDataTypeException
735
-     * @throws InvalidInterfaceException
736
-     */
737
-    public function process_registration_refund(
738
-        EE_Registration $registration,
739
-        EE_Payment $payment,
740
-        $available_refund_amount = 0.00
741
-    ) {
742
-        // EEH_Debug_Tools::printr( $payment->amount(), '$payment->amount()', __FILE__, __LINE__ );
743
-        if ($registration->paid() > 0) {
744
-            // ensure $available_refund_amount is NOT negative
745
-            $available_refund_amount = (float) abs($available_refund_amount);
746
-            // don't allow refund amount to exceed the available payment amount, OR the amount paid
747
-            $refund_amount = min($available_refund_amount, (float) $registration->paid());
748
-            // update $available_payment_amount
749
-            $available_refund_amount -= $refund_amount;
750
-            // calculate and set new REG_paid
751
-            $registration->set_paid($registration->paid() - $refund_amount);
752
-            // convert payment amount back to a negative value for storage in the db
753
-            $refund_amount = (float) abs($refund_amount) * -1;
754
-            // now save it
755
-            $this->_apply_registration_payment($registration, $payment, $refund_amount);
756
-        }
757
-        return $available_refund_amount;
758
-    }
759
-
760
-
761
-    /**
762
-     * Process payments and transaction after payment process completed.
763
-     * ultimately this will send the TXN and payment details off so that notifications can be sent out.
764
-     * if this request happens to be processing an IPN,
765
-     * then we will also set the Payment Options Reg Step to completed,
766
-     * and attempt to completely finalize the TXN if all of the other Reg Steps are completed as well.
767
-     *
768
-     * @param EE_Transaction $transaction
769
-     * @param EE_Payment     $payment
770
-     * @param bool           $IPN
771
-     * @throws EE_Error
772
-     * @throws InvalidArgumentException
773
-     * @throws ReflectionException
774
-     * @throws RuntimeException
775
-     * @throws InvalidDataTypeException
776
-     * @throws InvalidInterfaceException
777
-     */
778
-    protected function _post_payment_processing(EE_Transaction $transaction, EE_Payment $payment, $IPN = false)
779
-    {
780
-        /** @type EE_Transaction_Processor $transaction_processor */
781
-        $transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
782
-        // is the Payment Options Reg Step completed ?
783
-        $payment_options_step_completed = $transaction->reg_step_completed('payment_options');
784
-        // if the Payment Options Reg Step is completed...
785
-        $revisit = $payment_options_step_completed === true;
786
-        // then this is kinda sorta a revisit with regards to payments at least
787
-        $transaction_processor->set_revisit($revisit);
788
-        // if this is an IPN, let's consider the Payment Options Reg Step completed if not already
789
-        if (
790
-            $IPN
791
-            && $payment_options_step_completed !== true
792
-            && ($payment->is_approved() || $payment->is_pending())
793
-        ) {
794
-            $payment_options_step_completed = $transaction->set_reg_step_completed(
795
-                'payment_options'
796
-            );
797
-        }
798
-        // maybe update status, but don't save transaction just yet
799
-        $transaction->update_status_based_on_total_paid(false);
800
-        // check if 'finalize_registration' step has been completed...
801
-        $finalized = $transaction->reg_step_completed('finalize_registration');
802
-        //  if this is an IPN and the final step has not been initiated
803
-        if ($IPN && $payment_options_step_completed && $finalized === false) {
804
-            // and if it hasn't already been set as being started...
805
-            $finalized = $transaction->set_reg_step_initiated('finalize_registration');
806
-        }
807
-        $transaction->save();
808
-        // because the above will return false if the final step was not fully completed, we need to check again...
809
-        if ($IPN && $finalized !== false) {
810
-            // and if we are all good to go, then send out notifications
811
-            add_filter('FHEE__EED_Messages___maybe_registration__deliver_notifications', '__return_true');
812
-            // ok, now process the transaction according to the payment
813
-            $transaction_processor->update_transaction_and_registrations_after_checkout_or_payment(
814
-                $transaction,
815
-                $payment
816
-            );
817
-        }
818
-        // DEBUG LOG
819
-        $payment_method = $payment->payment_method();
820
-        if ($payment_method instanceof EE_Payment_Method) {
821
-            $payment_method_type_obj = $payment_method->type_obj();
822
-            if ($payment_method_type_obj instanceof EE_PMT_Base) {
823
-                $gateway = $payment_method_type_obj->get_gateway();
824
-                if ($gateway instanceof EE_Gateway) {
825
-                    $gateway->log(
826
-                        array(
827
-                            'message'               => (string) esc_html__('Post Payment Transaction Details', 'event_espresso'),
828
-                            'transaction'           => $transaction->model_field_array(),
829
-                            'finalized'             => $finalized,
830
-                            'IPN'                   => $IPN,
831
-                            'deliver_notifications' => has_filter(
832
-                                'FHEE__EED_Messages___maybe_registration__deliver_notifications'
833
-                            ),
834
-                        ),
835
-                        $payment
836
-                    );
837
-                }
838
-            }
839
-        }
840
-    }
841
-
842
-
843
-    /**
844
-     * Force posts to PayPal to use TLS v1.2. See:
845
-     * https://core.trac.wordpress.org/ticket/36320
846
-     * https://core.trac.wordpress.org/ticket/34924#comment:15
847
-     * https://www.paypal-knowledge.com/infocenter/index?page=content&widgetview=true&id=FAQ1914&viewlocale=en_US
848
-     * This will affect PayPal standard, pro, express, and Payflow.
849
-     *
850
-     * @param $handle
851
-     * @param $r
852
-     * @param $url
853
-     */
854
-    public static function _curl_requests_to_paypal_use_tls($handle, $r, $url)
855
-    {
856
-        if (strpos($url, 'https://') !== false && strpos($url, '.paypal.com') !== false) {
857
-            // Use the value of the constant CURL_SSLVERSION_TLSv1 = 1
858
-            // instead of the constant because it might not be defined
859
-            curl_setopt($handle, CURLOPT_SSLVERSION, 6);
860
-        }
861
-    }
20
+	/**
21
+	 * @var EE_Payment_Processor $_instance
22
+	 * @access    private
23
+	 */
24
+	private static $_instance;
25
+
26
+
27
+	/**
28
+	 * @singleton method used to instantiate class object
29
+	 * @access    public
30
+	 * @return EE_Payment_Processor instance
31
+	 */
32
+	public static function instance()
33
+	{
34
+		// check if class object is instantiated
35
+		if (! self::$_instance instanceof EE_Payment_Processor) {
36
+			self::$_instance = new self();
37
+		}
38
+		return self::$_instance;
39
+	}
40
+
41
+
42
+	/**
43
+	 * @return EE_Payment_Processor
44
+	 */
45
+	public static function reset()
46
+	{
47
+		self::$_instance = null;
48
+		return self::instance();
49
+	}
50
+
51
+
52
+	/**
53
+	 *private constructor to prevent direct creation
54
+	 *
55
+	 * @Constructor
56
+	 * @access private
57
+	 */
58
+	private function __construct()
59
+	{
60
+		do_action('AHEE__EE_Payment_Processor__construct');
61
+		add_action('http_api_curl', array($this, '_curl_requests_to_paypal_use_tls'), 10, 3);
62
+	}
63
+
64
+
65
+	/**
66
+	 * Using the selected gateway, processes the payment for that transaction, and updates the transaction
67
+	 * appropriately. Saves the payment that is generated
68
+	 *
69
+	 * @param EE_Payment_Method    $payment_method
70
+	 * @param EE_Transaction       $transaction
71
+	 * @param float|null                $amount       if only part of the transaction is to be paid for, how much.
72
+	 *                                           Leave null if payment is for the full amount owing
73
+	 * @param EE_Billing_Info_Form|null $billing_form (or probably null, if it's an offline or offsite payment method).
74
+	 *                                           Receive_form_submission() should have
75
+	 *                                           already been called on the billing form
76
+	 *                                           (ie, its inputs should have their normalized values set).
77
+	 * @param string|null          $return_url   string used mostly by offsite gateways to specify
78
+	 *                                           where to go AFTER the offsite gateway
79
+	 * @param string               $method       like 'CART', indicates who the client who called this was
80
+	 * @param bool                 $by_admin     TRUE if payment is being attempted from the admin
81
+	 * @param bool                 $update_txn   whether or not to call
82
+	 *                                           EE_Transaction_Processor::update_transaction_and_registrations_after_checkout_or_payment()
83
+	 * @param string               $cancel_url   URL to return to if off-site payments are cancelled
84
+	 * @return EE_Payment
85
+	 * @throws EE_Error
86
+	 * @throws InvalidArgumentException
87
+	 * @throws ReflectionException
88
+	 * @throws RuntimeException
89
+	 * @throws InvalidDataTypeException
90
+	 * @throws InvalidInterfaceException
91
+	 */
92
+	public function process_payment(
93
+		EE_Payment_Method $payment_method,
94
+		EE_Transaction $transaction,
95
+		?float $amount = null,
96
+		?EE_Billing_Info_Form $billing_form = null,
97
+		?string $return_url = null,
98
+		string $method = 'CART',
99
+		bool $by_admin = false,
100
+		bool $update_txn = true,
101
+		string $cancel_url = ''
102
+	): ?EE_Payment {
103
+		if ((float) $amount < 0) {
104
+			throw new EE_Error(
105
+				sprintf(
106
+					esc_html__(
107
+						'Attempting to make a payment for a negative amount of %1$d for transaction %2$d. That should be a refund',
108
+						'event_espresso'
109
+					),
110
+					$amount,
111
+					$transaction->ID()
112
+				)
113
+			);
114
+		}
115
+		// verify payment method
116
+		$payment_method = EEM_Payment_Method::instance()->ensure_is_obj(
117
+			$payment_method,
118
+			true
119
+		);
120
+		// verify transaction
121
+		EEM_Transaction::instance()->ensure_is_obj($transaction);
122
+		$transaction->set_payment_method_ID($payment_method->ID());
123
+		// verify payment method type
124
+		if ($payment_method->type_obj() instanceof EE_PMT_Base) {
125
+			$payment = $payment_method->type_obj()->process_payment(
126
+				$transaction,
127
+				min($amount, $transaction->remaining()), // make sure we don't overcharge
128
+				$billing_form,
129
+				$return_url,
130
+				add_query_arg(array('ee_cancel_payment' => true), $cancel_url),
131
+				$method,
132
+				$by_admin
133
+			);
134
+			// check if payment method uses an off-site gateway
135
+			if ($payment_method->type_obj()->payment_occurs() !== EE_PMT_Base::offsite) {
136
+				// don't process payments for off-site gateways yet because no payment has occurred yet
137
+				$this->update_txn_based_on_payment($transaction, $payment, $update_txn);
138
+			}
139
+			return $payment;
140
+		}
141
+		EE_Error::add_error(
142
+			sprintf(
143
+				esc_html__(
144
+					'A valid payment method could not be determined due to a technical issue.%sPlease try again or contact %s for assistance.',
145
+					'event_espresso'
146
+				),
147
+				'<br/>',
148
+				EE_Registry::instance()->CFG->organization->get_pretty('email')
149
+			),
150
+			__FILE__,
151
+			__FUNCTION__,
152
+			__LINE__
153
+		);
154
+		return null;
155
+	}
156
+
157
+
158
+	/**
159
+	 * @param EE_Transaction|int $transaction
160
+	 * @param EE_Payment_Method  $payment_method
161
+	 * @return string
162
+	 * @throws EE_Error
163
+	 * @throws InvalidArgumentException
164
+	 * @throws InvalidDataTypeException
165
+	 * @throws InvalidInterfaceException
166
+	 */
167
+	public function get_ipn_url_for_payment_method($transaction, $payment_method)
168
+	{
169
+		/** @type \EE_Transaction $transaction */
170
+		$transaction = EEM_Transaction::instance()->ensure_is_obj($transaction);
171
+		$primary_reg = $transaction->primary_registration();
172
+		if (! $primary_reg instanceof EE_Registration) {
173
+			throw new EE_Error(
174
+				sprintf(
175
+					esc_html__(
176
+						'Cannot get IPN URL for transaction with ID %d because it has no primary registration',
177
+						'event_espresso'
178
+					),
179
+					$transaction->ID()
180
+				)
181
+			);
182
+		}
183
+		$payment_method = EEM_Payment_Method::instance()->ensure_is_obj(
184
+			$payment_method,
185
+			true
186
+		);
187
+		$url = add_query_arg(
188
+			array(
189
+				'e_reg_url_link'    => $primary_reg->reg_url_link(),
190
+				'ee_payment_method' => $payment_method->slug(),
191
+			),
192
+			EE_Registry::instance()->CFG->core->txn_page_url()
193
+		);
194
+		return $url;
195
+	}
196
+
197
+
198
+	/**
199
+	 * Process the IPN. Firstly, we'll hope we put the standard args into the IPN URL so
200
+	 * we can easily find what registration the IPN is for and what payment method.
201
+	 * However, if not, we'll give all payment methods a chance to claim it and process it.
202
+	 * If a payment is found for the IPN info, it is saved.
203
+	 *
204
+	 * @param array              $_req_data            form post data
205
+	 * @param EE_Transaction|int $transaction          optional (or a transactions id)
206
+	 * @param EE_Payment_Method  $payment_method       (or a slug or id of one)
207
+	 * @param boolean            $update_txn           whether or not to call
208
+	 *                                                 EE_Transaction_Processor::update_transaction_and_registrations_after_checkout_or_payment()
209
+	 * @param bool               $separate_IPN_request whether the IPN uses a separate request (true, like PayPal)
210
+	 *                                                 or is processed manually (false, like Authorize.net)
211
+	 * @throws EE_Error
212
+	 * @throws Exception
213
+	 * @return EE_Payment
214
+	 * @throws \RuntimeException
215
+	 * @throws \ReflectionException
216
+	 * @throws \InvalidArgumentException
217
+	 * @throws InvalidInterfaceException
218
+	 * @throws InvalidDataTypeException
219
+	 */
220
+	public function process_ipn(
221
+		$_req_data,
222
+		$transaction = null,
223
+		$payment_method = null,
224
+		$update_txn = true,
225
+		$separate_IPN_request = true
226
+	) {
227
+		EE_Registry::instance()->load_model('Change_Log');
228
+		$_req_data = $this->_remove_unusable_characters_from_array((array) $_req_data);
229
+		EE_Processor_Base::set_IPN($separate_IPN_request);
230
+		$obj_for_log = null;
231
+		if ($transaction instanceof EE_Transaction) {
232
+			$obj_for_log = $transaction;
233
+			if ($payment_method instanceof EE_Payment_Method) {
234
+				$obj_for_log = EEM_Payment::instance()->get_one(
235
+					array(
236
+						array('TXN_ID' => $transaction->ID(), 'PMD_ID' => $payment_method->ID()),
237
+						'order_by' => array('PAY_timestamp' => 'desc'),
238
+					)
239
+				);
240
+			}
241
+		} elseif ($payment_method instanceof EE_Payment) {
242
+			$obj_for_log = $payment_method;
243
+		}
244
+		$log = EEM_Change_Log::instance()->log(
245
+			EEM_Change_Log::type_gateway,
246
+			array('IPN data received' => $_req_data),
247
+			$obj_for_log
248
+		);
249
+		try {
250
+			/**
251
+			 * @var EE_Payment $payment
252
+			 */
253
+			$payment = null;
254
+			if ($transaction && $payment_method) {
255
+				/** @type EE_Transaction $transaction */
256
+				$transaction = EEM_Transaction::instance()->ensure_is_obj($transaction);
257
+				/** @type EE_Payment_Method $payment_method */
258
+				$payment_method = EEM_Payment_Method::instance()->ensure_is_obj($payment_method);
259
+				if ($payment_method->type_obj() instanceof EE_PMT_Base) {
260
+					try {
261
+						$payment = $payment_method->type_obj()->handle_ipn($_req_data, $transaction);
262
+						$log->set_object($payment);
263
+					} catch (EventEspresso\core\exceptions\IpnException $e) {
264
+						EEM_Change_Log::instance()->log(
265
+							EEM_Change_Log::type_gateway,
266
+							array(
267
+								'message'     => 'IPN Exception: ' . $e->getMessage(),
268
+								'current_url' => EEH_URL::current_url(),
269
+								'payment'     => $e->getPaymentProperties(),
270
+								'IPN_data'    => $e->getIpnData(),
271
+							),
272
+							$obj_for_log
273
+						);
274
+						return $e->getPayment();
275
+					}
276
+				} else {
277
+					// not a payment
278
+					EE_Error::add_error(
279
+						sprintf(
280
+							esc_html__(
281
+								'A valid payment method could not be determined due to a technical issue.%sPlease refresh your browser and try again or contact %s for assistance.',
282
+								'event_espresso'
283
+							),
284
+							'<br/>',
285
+							EE_Registry::instance()->CFG->organization->get_pretty('email')
286
+						),
287
+						__FILE__,
288
+						__FUNCTION__,
289
+						__LINE__
290
+					);
291
+				}
292
+			} else {
293
+				// that's actually pretty ok. The IPN just wasn't able
294
+				// to identify which transaction or payment method this was for
295
+				// give all active payment methods a chance to claim it
296
+				$active_payment_methods = EEM_Payment_Method::instance()->get_all_active();
297
+				foreach ($active_payment_methods as $active_payment_method) {
298
+					try {
299
+						$payment = $active_payment_method->type_obj()->handle_unclaimed_ipn($_req_data);
300
+						$payment_method = $active_payment_method;
301
+						EEM_Change_Log::instance()->log(
302
+							EEM_Change_Log::type_gateway,
303
+							array('IPN data' => $_req_data),
304
+							$payment
305
+						);
306
+						break;
307
+					} catch (EventEspresso\core\exceptions\IpnException $e) {
308
+						EEM_Change_Log::instance()->log(
309
+							EEM_Change_Log::type_gateway,
310
+							array(
311
+								'message'     => 'IPN Exception: ' . $e->getMessage(),
312
+								'current_url' => EEH_URL::current_url(),
313
+								'payment'     => $e->getPaymentProperties(),
314
+								'IPN_data'    => $e->getIpnData(),
315
+							),
316
+							$obj_for_log
317
+						);
318
+						return $e->getPayment();
319
+					} catch (EE_Error $e) {
320
+						// that's fine- it apparently couldn't handle the IPN
321
+					}
322
+				}
323
+			}
324
+			// EEM_Payment_Log::instance()->log("got to 7",$transaction,$payment_method);
325
+			if ($payment instanceof EE_Payment) {
326
+				$payment->save();
327
+				//  update the TXN
328
+				$this->update_txn_based_on_payment(
329
+					$transaction,
330
+					$payment,
331
+					$update_txn,
332
+					$separate_IPN_request
333
+				);
334
+			} else {
335
+				// we couldn't find the payment for this IPN... let's try and log at least SOMETHING
336
+				if ($payment_method) {
337
+					EEM_Change_Log::instance()->log(
338
+						EEM_Change_Log::type_gateway,
339
+						array('IPN data' => $_req_data),
340
+						$payment_method
341
+					);
342
+				} elseif ($transaction) {
343
+					EEM_Change_Log::instance()->log(
344
+						EEM_Change_Log::type_gateway,
345
+						array('IPN data' => $_req_data),
346
+						$transaction
347
+					);
348
+				}
349
+			}
350
+			return $payment;
351
+		} catch (EE_Error $e) {
352
+			do_action(
353
+				'AHEE__log',
354
+				__FILE__,
355
+				__FUNCTION__,
356
+				sprintf(
357
+					esc_html__(
358
+						'Error occurred while receiving IPN. Transaction: %1$s, req data: %2$s. The error was "%3$s"',
359
+						'event_espresso'
360
+					),
361
+					print_r($transaction, true),
362
+					print_r($_req_data, true),
363
+					$e->getMessage()
364
+				)
365
+			);
366
+			throw $e;
367
+		}
368
+	}
369
+
370
+
371
+	/**
372
+	 * Removes any non-printable illegal characters from the input,
373
+	 * which might cause a raucous when trying to insert into the database
374
+	 *
375
+	 * @param  array $request_data
376
+	 * @return array
377
+	 */
378
+	protected function _remove_unusable_characters_from_array(array $request_data)
379
+	{
380
+		$return_data = array();
381
+		foreach ($request_data as $key => $value) {
382
+			$return_data[ $this->_remove_unusable_characters($key) ] = $this->_remove_unusable_characters(
383
+				$value
384
+			);
385
+		}
386
+		return $return_data;
387
+	}
388
+
389
+
390
+	/**
391
+	 * Removes any non-printable illegal characters from the input,
392
+	 * which might cause a raucous when trying to insert into the database
393
+	 *
394
+	 * @param string $request_data
395
+	 * @return string
396
+	 */
397
+	protected function _remove_unusable_characters($request_data)
398
+	{
399
+		return preg_replace('/[^[:print:]]/', '', $request_data);
400
+	}
401
+
402
+
403
+	/**
404
+	 * Should be called just before displaying the payment attempt results to the user,
405
+	 * when the payment attempt has finished. Some payment methods may have special
406
+	 * logic to perform here. For example, if process_payment() happens on a special request
407
+	 * and then the user is redirected to a page that displays the payment's status, this
408
+	 * should be called while loading the page that displays the payment's status. If the user is
409
+	 * sent to an offsite payment provider, this should be called upon returning from that offsite payment
410
+	 * provider.
411
+	 *
412
+	 * @param EE_Transaction|int $transaction
413
+	 * @param bool               $update_txn whether or not to call
414
+	 *                                       EE_Transaction_Processor::update_transaction_and_registrations_after_checkout_or_payment()
415
+	 * @return EE_Payment
416
+	 * @throws EE_Error
417
+	 * @throws InvalidArgumentException
418
+	 * @throws ReflectionException
419
+	 * @throws RuntimeException
420
+	 * @throws InvalidDataTypeException
421
+	 * @throws InvalidInterfaceException
422
+	 * @deprecated 4.6.24 method is no longer used. Instead it is up to client code, like SPCO,
423
+	 *                                       to call handle_ipn() for offsite gateways that don't receive separate IPNs
424
+	 */
425
+	public function finalize_payment_for($transaction, $update_txn = true)
426
+	{
427
+		/** @var $transaction EE_Transaction */
428
+		$transaction = EEM_Transaction::instance()->ensure_is_obj($transaction);
429
+		$last_payment_method = $transaction->payment_method();
430
+		if ($last_payment_method instanceof EE_Payment_Method) {
431
+			$payment = $last_payment_method->type_obj()->finalize_payment_for($transaction);
432
+			$this->update_txn_based_on_payment($transaction, $payment, $update_txn);
433
+			return $payment;
434
+		}
435
+		return null;
436
+	}
437
+
438
+
439
+	/**
440
+	 * Processes a direct refund request, saves the payment, and updates the transaction appropriately.
441
+	 *
442
+	 * @param EE_Payment_Method $payment_method
443
+	 * @param EE_Payment        $payment_to_refund
444
+	 * @param array             $refund_info
445
+	 * @return EE_Payment
446
+	 * @throws EE_Error
447
+	 * @throws InvalidArgumentException
448
+	 * @throws ReflectionException
449
+	 * @throws RuntimeException
450
+	 * @throws InvalidDataTypeException
451
+	 * @throws InvalidInterfaceException
452
+	 */
453
+	public function process_refund(
454
+		EE_Payment_Method $payment_method,
455
+		EE_Payment $payment_to_refund,
456
+		array $refund_info = array()
457
+	) {
458
+		if ($payment_method instanceof EE_Payment_Method && $payment_method->type_obj()->supports_sending_refunds()) {
459
+			$payment_method->type_obj()->process_refund($payment_to_refund, $refund_info);
460
+			$this->update_txn_based_on_payment($payment_to_refund->transaction(), $payment_to_refund);
461
+		}
462
+		return $payment_to_refund;
463
+	}
464
+
465
+
466
+	/**
467
+	 * This should be called each time there may have been an update to a
468
+	 * payment on a transaction (ie, we asked for a payment to process a
469
+	 * payment for a transaction, or we told a payment method about an IPN, or
470
+	 * we told a payment method to
471
+	 * "finalize_payment_for" (a transaction), or we told a payment method to
472
+	 * process a refund. This should handle firing the correct hooks to
473
+	 * indicate
474
+	 * what exactly happened and updating the transaction appropriately). This
475
+	 * could be integrated directly into EE_Transaction upon save, but we want
476
+	 * this logic to be separate from 'normal' plain-jane saving and updating
477
+	 * of transactions and payments, and to be tied to payment processing.
478
+	 * Note: this method DOES NOT save the payment passed into it. It is the responsibility
479
+	 * of previous code to decide whether or not to save (because the payment passed into
480
+	 * this method might be a temporary, never-to-be-saved payment from an offline gateway,
481
+	 * in which case we only want that payment object for some temporary usage during this request,
482
+	 * but we don't want it to be saved).
483
+	 *
484
+	 * @param EE_Transaction|int $transaction
485
+	 * @param EE_Payment         $payment
486
+	 * @param boolean            $update_txn
487
+	 *                        whether or not to call
488
+	 *                        EE_Transaction_Processor::
489
+	 *                        update_transaction_and_registrations_after_checkout_or_payment()
490
+	 *                        (you can save 1 DB query if you know you're going
491
+	 *                        to save it later instead)
492
+	 * @param bool               $IPN
493
+	 *                        if processing IPNs or other similar payment
494
+	 *                        related activities that occur in alternate
495
+	 *                        requests than the main one that is processing the
496
+	 *                        TXN, then set this to true to check whether the
497
+	 *                        TXN is locked before updating
498
+	 * @throws EE_Error
499
+	 * @throws InvalidArgumentException
500
+	 * @throws ReflectionException
501
+	 * @throws RuntimeException
502
+	 * @throws InvalidDataTypeException
503
+	 * @throws InvalidInterfaceException
504
+	 */
505
+	public function update_txn_based_on_payment($transaction, $payment, $update_txn = true, $IPN = false)
506
+	{
507
+		$do_action = 'AHEE__EE_Payment_Processor__update_txn_based_on_payment__not_successful';
508
+		/** @type EE_Transaction $transaction */
509
+		$transaction = EEM_Transaction::instance()->ensure_is_obj($transaction);
510
+		// can we freely update the TXN at this moment?
511
+		if ($IPN && $transaction->is_locked()) {
512
+			// don't update the transaction at this exact moment
513
+			// because the TXN is active in another request
514
+			EE_Cron_Tasks::schedule_update_transaction_with_payment(
515
+				time(),
516
+				$transaction->ID(),
517
+				$payment->ID()
518
+			);
519
+		} else {
520
+			// verify payment and that it has been saved
521
+			if ($payment instanceof EE_Payment && $payment->ID()) {
522
+				if (
523
+					$payment->payment_method() instanceof EE_Payment_Method
524
+					&& $payment->payment_method()->type_obj() instanceof EE_PMT_Base
525
+				) {
526
+					$payment->payment_method()->type_obj()->update_txn_based_on_payment($payment);
527
+					// update TXN registrations with payment info
528
+					$this->process_registration_payments($transaction, $payment);
529
+				}
530
+				$do_action = $payment->just_approved()
531
+					? 'AHEE__EE_Payment_Processor__update_txn_based_on_payment__successful'
532
+					: $do_action;
533
+			} else {
534
+				// send out notifications
535
+				add_filter('FHEE__EED_Messages___maybe_registration__deliver_notifications', '__return_true');
536
+				$do_action = 'AHEE__EE_Payment_Processor__update_txn_based_on_payment__no_payment_made';
537
+			}
538
+			if ($payment instanceof EE_Payment && $payment->status() !== EEM_Payment::status_id_failed) {
539
+				/** @type EE_Transaction_Payments $transaction_payments */
540
+				$transaction_payments = EE_Registry::instance()->load_class('Transaction_Payments');
541
+				// set new value for total paid
542
+				$transaction_payments->calculate_total_payments_and_update_status($transaction);
543
+				// call EE_Transaction_Processor::update_transaction_and_registrations_after_checkout_or_payment() ???
544
+				if ($update_txn) {
545
+					$this->_post_payment_processing($transaction, $payment, $IPN);
546
+				}
547
+			}
548
+			// granular hook for others to use.
549
+			do_action($do_action, $transaction, $payment);
550
+			do_action('AHEE_log', __CLASS__, __FUNCTION__, $do_action, '$do_action');
551
+			// global hook for others to use.
552
+			do_action('AHEE__EE_Payment_Processor__update_txn_based_on_payment', $transaction, $payment);
553
+		}
554
+	}
555
+
556
+
557
+	/**
558
+	 * update registrations REG_paid field after successful payment and link registrations with payment
559
+	 *
560
+	 * @param EE_Transaction    $transaction
561
+	 * @param EE_Payment        $payment
562
+	 * @param EE_Registration[] $registrations
563
+	 * @throws EE_Error
564
+	 * @throws InvalidArgumentException
565
+	 * @throws RuntimeException
566
+	 * @throws InvalidDataTypeException
567
+	 * @throws InvalidInterfaceException
568
+	 */
569
+	public function process_registration_payments(
570
+		EE_Transaction $transaction,
571
+		EE_Payment $payment,
572
+		array $registrations = array()
573
+	) {
574
+		// only process if payment was successful
575
+		if ($payment->status() !== EEM_Payment::status_id_approved) {
576
+			return;
577
+		}
578
+		// EEM_Registration::instance()->show_next_x_db_queries();
579
+		if (empty($registrations)) {
580
+			// find registrations with monies owing that can receive a payment
581
+			$registrations = $transaction->registrations(
582
+				array(
583
+					array(
584
+						// only these reg statuses can receive payments
585
+						'STS_ID'           => array('IN', EEM_Registration::reg_statuses_that_allow_payment()),
586
+						'REG_final_price'  => array('!=', 0),
587
+						'REG_final_price*' => array('!=', 'REG_paid', true),
588
+					),
589
+				)
590
+			);
591
+		}
592
+		// still nothing ??!??
593
+		if (empty($registrations)) {
594
+			return;
595
+		}
596
+		// todo: break out the following logic into a separate strategy class
597
+		// todo: named something like "Sequential_Reg_Payment_Strategy"
598
+		// todo: which would apply payments using the capitalist "first come first paid" approach
599
+		// todo: then have another strategy class like "Distributed_Reg_Payment_Strategy"
600
+		// todo: which would be the socialist "everybody gets a piece of pie" approach,
601
+		// todo: which would be better for deposits, where you want a bit of the payment applied to each registration
602
+		$refund = $payment->is_a_refund();
603
+		// how much is available to apply to registrations?
604
+		$available_payment_amount = abs($payment->amount());
605
+		foreach ($registrations as $registration) {
606
+			if ($registration instanceof EE_Registration) {
607
+				// nothing left?
608
+				if ($available_payment_amount <= 0) {
609
+					break;
610
+				}
611
+				if ($refund) {
612
+					$available_payment_amount = $this->process_registration_refund(
613
+						$registration,
614
+						$payment,
615
+						$available_payment_amount
616
+					);
617
+				} else {
618
+					$available_payment_amount = $this->process_registration_payment(
619
+						$registration,
620
+						$payment,
621
+						$available_payment_amount
622
+					);
623
+				}
624
+			}
625
+		}
626
+		if (
627
+			$available_payment_amount > 0
628
+			&& apply_filters(
629
+				'FHEE__EE_Payment_Processor__process_registration_payments__display_notifications',
630
+				false
631
+			)
632
+		) {
633
+			EE_Error::add_attention(
634
+				sprintf(
635
+					esc_html__(
636
+						'A remainder of %1$s exists after applying this payment to Registration(s) %2$s.%3$sPlease verify that the original payment amount of %4$s is correct. If so, you should edit this payment and select at least one additional registration in the "Registrations to Apply Payment to" section, so that the remainder of this payment can be applied to the additional registration(s).',
637
+						'event_espresso'
638
+					),
639
+					EEH_Template::format_currency($available_payment_amount),
640
+					implode(', ', array_keys($registrations)),
641
+					'<br/>',
642
+					EEH_Template::format_currency($payment->amount())
643
+				),
644
+				__FILE__,
645
+				__FUNCTION__,
646
+				__LINE__
647
+			);
648
+		}
649
+	}
650
+
651
+
652
+	/**
653
+	 * update registration REG_paid field after successful payment and link registration with payment
654
+	 *
655
+	 * @param EE_Registration $registration
656
+	 * @param EE_Payment      $payment
657
+	 * @param float           $available_payment_amount
658
+	 * @return float
659
+	 * @throws EE_Error
660
+	 * @throws InvalidArgumentException
661
+	 * @throws RuntimeException
662
+	 * @throws InvalidDataTypeException
663
+	 * @throws InvalidInterfaceException
664
+	 */
665
+	public function process_registration_payment(
666
+		EE_Registration $registration,
667
+		EE_Payment $payment,
668
+		$available_payment_amount = 0.00
669
+	) {
670
+		$owing = $registration->final_price() - $registration->paid();
671
+		if ($owing > 0) {
672
+			// don't allow payment amount to exceed the available payment amount, OR the amount owing
673
+			$payment_amount = min($available_payment_amount, $owing);
674
+			// update $available_payment_amount
675
+			$available_payment_amount -= $payment_amount;
676
+			// calculate and set new REG_paid
677
+			$registration->set_paid($registration->paid() + $payment_amount);
678
+			// now save it
679
+			$this->_apply_registration_payment($registration, $payment, $payment_amount);
680
+		}
681
+		return $available_payment_amount;
682
+	}
683
+
684
+
685
+	/**
686
+	 * update registration REG_paid field after successful payment and link registration with payment
687
+	 *
688
+	 * @param EE_Registration $registration
689
+	 * @param EE_Payment      $payment
690
+	 * @param float           $payment_amount
691
+	 * @return void
692
+	 * @throws EE_Error
693
+	 * @throws InvalidArgumentException
694
+	 * @throws InvalidDataTypeException
695
+	 * @throws InvalidInterfaceException
696
+	 */
697
+	protected function _apply_registration_payment(
698
+		EE_Registration $registration,
699
+		EE_Payment $payment,
700
+		$payment_amount = 0.00
701
+	) {
702
+		// find any existing reg payment records for this registration and payment
703
+		$existing_reg_payment = EEM_Registration_Payment::instance()->get_one(
704
+			array(array('REG_ID' => $registration->ID(), 'PAY_ID' => $payment->ID()))
705
+		);
706
+		// if existing registration payment exists
707
+		if ($existing_reg_payment instanceof EE_Registration_Payment) {
708
+			// then update that record
709
+			$existing_reg_payment->set_amount($payment_amount);
710
+			$existing_reg_payment->save();
711
+		} else {
712
+			// or add new relation between registration and payment and set amount
713
+			$registration->_add_relation_to(
714
+				$payment,
715
+				'Payment',
716
+				array('RPY_amount' => $payment_amount)
717
+			);
718
+			// make it stick
719
+			$registration->save();
720
+		}
721
+	}
722
+
723
+
724
+	/**
725
+	 * update registration REG_paid field after refund and link registration with payment
726
+	 *
727
+	 * @param EE_Registration $registration
728
+	 * @param EE_Payment      $payment
729
+	 * @param float           $available_refund_amount - IMPORTANT !!! SEND AVAILABLE REFUND AMOUNT AS A POSITIVE NUMBER
730
+	 * @return float
731
+	 * @throws EE_Error
732
+	 * @throws InvalidArgumentException
733
+	 * @throws RuntimeException
734
+	 * @throws InvalidDataTypeException
735
+	 * @throws InvalidInterfaceException
736
+	 */
737
+	public function process_registration_refund(
738
+		EE_Registration $registration,
739
+		EE_Payment $payment,
740
+		$available_refund_amount = 0.00
741
+	) {
742
+		// EEH_Debug_Tools::printr( $payment->amount(), '$payment->amount()', __FILE__, __LINE__ );
743
+		if ($registration->paid() > 0) {
744
+			// ensure $available_refund_amount is NOT negative
745
+			$available_refund_amount = (float) abs($available_refund_amount);
746
+			// don't allow refund amount to exceed the available payment amount, OR the amount paid
747
+			$refund_amount = min($available_refund_amount, (float) $registration->paid());
748
+			// update $available_payment_amount
749
+			$available_refund_amount -= $refund_amount;
750
+			// calculate and set new REG_paid
751
+			$registration->set_paid($registration->paid() - $refund_amount);
752
+			// convert payment amount back to a negative value for storage in the db
753
+			$refund_amount = (float) abs($refund_amount) * -1;
754
+			// now save it
755
+			$this->_apply_registration_payment($registration, $payment, $refund_amount);
756
+		}
757
+		return $available_refund_amount;
758
+	}
759
+
760
+
761
+	/**
762
+	 * Process payments and transaction after payment process completed.
763
+	 * ultimately this will send the TXN and payment details off so that notifications can be sent out.
764
+	 * if this request happens to be processing an IPN,
765
+	 * then we will also set the Payment Options Reg Step to completed,
766
+	 * and attempt to completely finalize the TXN if all of the other Reg Steps are completed as well.
767
+	 *
768
+	 * @param EE_Transaction $transaction
769
+	 * @param EE_Payment     $payment
770
+	 * @param bool           $IPN
771
+	 * @throws EE_Error
772
+	 * @throws InvalidArgumentException
773
+	 * @throws ReflectionException
774
+	 * @throws RuntimeException
775
+	 * @throws InvalidDataTypeException
776
+	 * @throws InvalidInterfaceException
777
+	 */
778
+	protected function _post_payment_processing(EE_Transaction $transaction, EE_Payment $payment, $IPN = false)
779
+	{
780
+		/** @type EE_Transaction_Processor $transaction_processor */
781
+		$transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
782
+		// is the Payment Options Reg Step completed ?
783
+		$payment_options_step_completed = $transaction->reg_step_completed('payment_options');
784
+		// if the Payment Options Reg Step is completed...
785
+		$revisit = $payment_options_step_completed === true;
786
+		// then this is kinda sorta a revisit with regards to payments at least
787
+		$transaction_processor->set_revisit($revisit);
788
+		// if this is an IPN, let's consider the Payment Options Reg Step completed if not already
789
+		if (
790
+			$IPN
791
+			&& $payment_options_step_completed !== true
792
+			&& ($payment->is_approved() || $payment->is_pending())
793
+		) {
794
+			$payment_options_step_completed = $transaction->set_reg_step_completed(
795
+				'payment_options'
796
+			);
797
+		}
798
+		// maybe update status, but don't save transaction just yet
799
+		$transaction->update_status_based_on_total_paid(false);
800
+		// check if 'finalize_registration' step has been completed...
801
+		$finalized = $transaction->reg_step_completed('finalize_registration');
802
+		//  if this is an IPN and the final step has not been initiated
803
+		if ($IPN && $payment_options_step_completed && $finalized === false) {
804
+			// and if it hasn't already been set as being started...
805
+			$finalized = $transaction->set_reg_step_initiated('finalize_registration');
806
+		}
807
+		$transaction->save();
808
+		// because the above will return false if the final step was not fully completed, we need to check again...
809
+		if ($IPN && $finalized !== false) {
810
+			// and if we are all good to go, then send out notifications
811
+			add_filter('FHEE__EED_Messages___maybe_registration__deliver_notifications', '__return_true');
812
+			// ok, now process the transaction according to the payment
813
+			$transaction_processor->update_transaction_and_registrations_after_checkout_or_payment(
814
+				$transaction,
815
+				$payment
816
+			);
817
+		}
818
+		// DEBUG LOG
819
+		$payment_method = $payment->payment_method();
820
+		if ($payment_method instanceof EE_Payment_Method) {
821
+			$payment_method_type_obj = $payment_method->type_obj();
822
+			if ($payment_method_type_obj instanceof EE_PMT_Base) {
823
+				$gateway = $payment_method_type_obj->get_gateway();
824
+				if ($gateway instanceof EE_Gateway) {
825
+					$gateway->log(
826
+						array(
827
+							'message'               => (string) esc_html__('Post Payment Transaction Details', 'event_espresso'),
828
+							'transaction'           => $transaction->model_field_array(),
829
+							'finalized'             => $finalized,
830
+							'IPN'                   => $IPN,
831
+							'deliver_notifications' => has_filter(
832
+								'FHEE__EED_Messages___maybe_registration__deliver_notifications'
833
+							),
834
+						),
835
+						$payment
836
+					);
837
+				}
838
+			}
839
+		}
840
+	}
841
+
842
+
843
+	/**
844
+	 * Force posts to PayPal to use TLS v1.2. See:
845
+	 * https://core.trac.wordpress.org/ticket/36320
846
+	 * https://core.trac.wordpress.org/ticket/34924#comment:15
847
+	 * https://www.paypal-knowledge.com/infocenter/index?page=content&widgetview=true&id=FAQ1914&viewlocale=en_US
848
+	 * This will affect PayPal standard, pro, express, and Payflow.
849
+	 *
850
+	 * @param $handle
851
+	 * @param $r
852
+	 * @param $url
853
+	 */
854
+	public static function _curl_requests_to_paypal_use_tls($handle, $r, $url)
855
+	{
856
+		if (strpos($url, 'https://') !== false && strpos($url, '.paypal.com') !== false) {
857
+			// Use the value of the constant CURL_SSLVERSION_TLSv1 = 1
858
+			// instead of the constant because it might not be defined
859
+			curl_setopt($handle, CURLOPT_SSLVERSION, 6);
860
+		}
861
+	}
862 862
 }
Please login to merge, or discard this patch.
core/services/container/CoffeeMaker.php 2 patches
Spacing   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -68,7 +68,7 @@  discard block
 block discarded – undo
68 68
     public static function validateType($type)
69 69
     {
70 70
         $types = CoffeeMaker::getTypes();
71
-        if (! in_array($type, $types, true)) {
71
+        if ( ! in_array($type, $types, true)) {
72 72
             throw new InvalidIdentifierException(
73 73
                 is_object($type) ? get_class($type) : gettype($type),
74 74
                 esc_html__(
@@ -151,7 +151,7 @@  discard block
 block discarded – undo
151 151
     protected function resolveClassAndFilepath(RecipeInterface $recipe)
152 152
     {
153 153
         $paths = $recipe->paths();
154
-        if (! empty($paths)) {
154
+        if ( ! empty($paths)) {
155 155
             foreach ($paths as $path) {
156 156
                 if (strpos($path, '*') === false && is_readable($path)) {
157 157
                     require_once($path);
Please login to merge, or discard this patch.
Indentation   +152 added lines, -152 removed lines patch added patch discarded remove patch
@@ -17,156 +17,156 @@
 block discarded – undo
17 17
  */
18 18
 abstract class CoffeeMaker implements CoffeeMakerInterface
19 19
 {
20
-    /**
21
-     * Indicates that CoffeeMaker should construct a NEW entity instance from the provided arguments (if given)
22
-     */
23
-    const BREW_NEW = 'new';
24
-
25
-    /**
26
-     * Indicates that CoffeeMaker should always return a SHARED instance
27
-     */
28
-    const BREW_SHARED = 'shared';
29
-
30
-    /**
31
-     * Indicates that CoffeeMaker should only load the file/class/interface but NOT instantiate
32
-     */
33
-    const BREW_LOAD_ONLY = 'load_only';
34
-
35
-
36
-    /**
37
-     * @var CoffeePotInterface $coffee_pot
38
-     */
39
-    private $coffee_pot;
40
-
41
-    /**
42
-     * @var DependencyInjector $injector
43
-     */
44
-    private $injector;
45
-
46
-
47
-    /**
48
-     * @return array
49
-     */
50
-    public static function getTypes()
51
-    {
52
-        return (array) apply_filters(
53
-            'FHEE__EventEspresso\core\services\container\CoffeeMaker__getTypes',
54
-            array(
55
-                CoffeeMaker::BREW_NEW,
56
-                CoffeeMaker::BREW_SHARED,
57
-                CoffeeMaker::BREW_LOAD_ONLY,
58
-            )
59
-        );
60
-    }
61
-
62
-
63
-    /**
64
-     * @param $type
65
-     * @throws \EventEspresso\core\exceptions\InvalidIdentifierException
66
-     */
67
-    public static function validateType($type)
68
-    {
69
-        $types = CoffeeMaker::getTypes();
70
-        if (! in_array($type, $types, true)) {
71
-            throw new InvalidIdentifierException(
72
-                is_object($type) ? get_class($type) : gettype($type),
73
-                esc_html__(
74
-                    'recipe type (one of the class constants on \EventEspresso\core\services\container\CoffeeMaker)',
75
-                    'event_espresso'
76
-                )
77
-            );
78
-        }
79
-        return $type;
80
-    }
81
-
82
-
83
-    /**
84
-     * CoffeeMaker constructor.
85
-     *
86
-     * @param CoffeePotInterface $coffee_pot
87
-     * @param InjectorInterface  $injector
88
-     */
89
-    public function __construct(CoffeePotInterface $coffee_pot, InjectorInterface $injector)
90
-    {
91
-        $this->coffee_pot = $coffee_pot;
92
-        $this->injector = $injector;
93
-    }
94
-
95
-
96
-    /**
97
-     * @return \EventEspresso\core\services\container\CoffeePotInterface
98
-     */
99
-    protected function coffeePot()
100
-    {
101
-        return $this->coffee_pot;
102
-    }
103
-
104
-
105
-    /**
106
-     * @return \EventEspresso\core\services\container\DependencyInjector
107
-     */
108
-    protected function injector()
109
-    {
110
-        return $this->injector;
111
-    }
112
-
113
-
114
-    /**
115
-     * Examines the constructor to determine which method should be used for instantiation
116
-     *
117
-     * @param \ReflectionClass $reflector
118
-     * @return mixed
119
-     * @throws InstantiationException
120
-     */
121
-    protected function resolveInstantiationMethod(\ReflectionClass $reflector)
122
-    {
123
-        if ($reflector->getConstructor() === null) {
124
-            return 'NewInstance';
125
-        }
126
-        if ($reflector->isInstantiable()) {
127
-            return 'NewInstanceArgs';
128
-        }
129
-        if (method_exists($reflector->getName(), 'instance')) {
130
-            return 'instance';
131
-        }
132
-        if (method_exists($reflector->getName(), 'new_instance')) {
133
-            return 'new_instance';
134
-        }
135
-        if (method_exists($reflector->getName(), 'new_instance_from_db')) {
136
-            return 'new_instance_from_db';
137
-        }
138
-        throw new InstantiationException($reflector->getName());
139
-    }
140
-
141
-
142
-    /**
143
-     * Ensures files for classes that are not PSR-4 compatible are loaded
144
-     * and then verifies that classes exist where applicable
145
-     *
146
-     * @param RecipeInterface $recipe
147
-     * @return bool
148
-     * @throws InvalidClassException
149
-     */
150
-    protected function resolveClassAndFilepath(RecipeInterface $recipe)
151
-    {
152
-        $paths = $recipe->paths();
153
-        if (! empty($paths)) {
154
-            foreach ($paths as $path) {
155
-                if (strpos($path, '*') === false && is_readable($path)) {
156
-                    require_once($path);
157
-                }
158
-            }
159
-        }
160
-        // re: using "false" for class_exists() second param:
161
-        // if a class name is not already known to PHP, then class_exists() will run through
162
-        // all of the registered spl_autoload functions until it either finds the class,
163
-        // or gets to the end of the registered spl_autoload functions.
164
-        // When the second parameter is true, it will also attempt to load the class file,
165
-        // but it will also trigger an error if the class can not be loaded.
166
-        // We don't want that extra error in the mix, so we have set the second param to "false"
167
-        if ($recipe->type() !== CoffeeMaker::BREW_LOAD_ONLY && ! class_exists($recipe->fqcn(), false)) {
168
-            throw new InvalidClassException($recipe->identifier());
169
-        }
170
-        return true;
171
-    }
20
+	/**
21
+	 * Indicates that CoffeeMaker should construct a NEW entity instance from the provided arguments (if given)
22
+	 */
23
+	const BREW_NEW = 'new';
24
+
25
+	/**
26
+	 * Indicates that CoffeeMaker should always return a SHARED instance
27
+	 */
28
+	const BREW_SHARED = 'shared';
29
+
30
+	/**
31
+	 * Indicates that CoffeeMaker should only load the file/class/interface but NOT instantiate
32
+	 */
33
+	const BREW_LOAD_ONLY = 'load_only';
34
+
35
+
36
+	/**
37
+	 * @var CoffeePotInterface $coffee_pot
38
+	 */
39
+	private $coffee_pot;
40
+
41
+	/**
42
+	 * @var DependencyInjector $injector
43
+	 */
44
+	private $injector;
45
+
46
+
47
+	/**
48
+	 * @return array
49
+	 */
50
+	public static function getTypes()
51
+	{
52
+		return (array) apply_filters(
53
+			'FHEE__EventEspresso\core\services\container\CoffeeMaker__getTypes',
54
+			array(
55
+				CoffeeMaker::BREW_NEW,
56
+				CoffeeMaker::BREW_SHARED,
57
+				CoffeeMaker::BREW_LOAD_ONLY,
58
+			)
59
+		);
60
+	}
61
+
62
+
63
+	/**
64
+	 * @param $type
65
+	 * @throws \EventEspresso\core\exceptions\InvalidIdentifierException
66
+	 */
67
+	public static function validateType($type)
68
+	{
69
+		$types = CoffeeMaker::getTypes();
70
+		if (! in_array($type, $types, true)) {
71
+			throw new InvalidIdentifierException(
72
+				is_object($type) ? get_class($type) : gettype($type),
73
+				esc_html__(
74
+					'recipe type (one of the class constants on \EventEspresso\core\services\container\CoffeeMaker)',
75
+					'event_espresso'
76
+				)
77
+			);
78
+		}
79
+		return $type;
80
+	}
81
+
82
+
83
+	/**
84
+	 * CoffeeMaker constructor.
85
+	 *
86
+	 * @param CoffeePotInterface $coffee_pot
87
+	 * @param InjectorInterface  $injector
88
+	 */
89
+	public function __construct(CoffeePotInterface $coffee_pot, InjectorInterface $injector)
90
+	{
91
+		$this->coffee_pot = $coffee_pot;
92
+		$this->injector = $injector;
93
+	}
94
+
95
+
96
+	/**
97
+	 * @return \EventEspresso\core\services\container\CoffeePotInterface
98
+	 */
99
+	protected function coffeePot()
100
+	{
101
+		return $this->coffee_pot;
102
+	}
103
+
104
+
105
+	/**
106
+	 * @return \EventEspresso\core\services\container\DependencyInjector
107
+	 */
108
+	protected function injector()
109
+	{
110
+		return $this->injector;
111
+	}
112
+
113
+
114
+	/**
115
+	 * Examines the constructor to determine which method should be used for instantiation
116
+	 *
117
+	 * @param \ReflectionClass $reflector
118
+	 * @return mixed
119
+	 * @throws InstantiationException
120
+	 */
121
+	protected function resolveInstantiationMethod(\ReflectionClass $reflector)
122
+	{
123
+		if ($reflector->getConstructor() === null) {
124
+			return 'NewInstance';
125
+		}
126
+		if ($reflector->isInstantiable()) {
127
+			return 'NewInstanceArgs';
128
+		}
129
+		if (method_exists($reflector->getName(), 'instance')) {
130
+			return 'instance';
131
+		}
132
+		if (method_exists($reflector->getName(), 'new_instance')) {
133
+			return 'new_instance';
134
+		}
135
+		if (method_exists($reflector->getName(), 'new_instance_from_db')) {
136
+			return 'new_instance_from_db';
137
+		}
138
+		throw new InstantiationException($reflector->getName());
139
+	}
140
+
141
+
142
+	/**
143
+	 * Ensures files for classes that are not PSR-4 compatible are loaded
144
+	 * and then verifies that classes exist where applicable
145
+	 *
146
+	 * @param RecipeInterface $recipe
147
+	 * @return bool
148
+	 * @throws InvalidClassException
149
+	 */
150
+	protected function resolveClassAndFilepath(RecipeInterface $recipe)
151
+	{
152
+		$paths = $recipe->paths();
153
+		if (! empty($paths)) {
154
+			foreach ($paths as $path) {
155
+				if (strpos($path, '*') === false && is_readable($path)) {
156
+					require_once($path);
157
+				}
158
+			}
159
+		}
160
+		// re: using "false" for class_exists() second param:
161
+		// if a class name is not already known to PHP, then class_exists() will run through
162
+		// all of the registered spl_autoload functions until it either finds the class,
163
+		// or gets to the end of the registered spl_autoload functions.
164
+		// When the second parameter is true, it will also attempt to load the class file,
165
+		// but it will also trigger an error if the class can not be loaded.
166
+		// We don't want that extra error in the mix, so we have set the second param to "false"
167
+		if ($recipe->type() !== CoffeeMaker::BREW_LOAD_ONLY && ! class_exists($recipe->fqcn(), false)) {
168
+			throw new InvalidClassException($recipe->identifier());
169
+		}
170
+		return true;
171
+	}
172 172
 }
Please login to merge, or discard this patch.
core/services/container/Recipe.php 2 patches
Spacing   +5 added lines, -5 removed lines patch added patch discarded remove patch
@@ -189,7 +189,7 @@  discard block
 block discarded – undo
189 189
      */
190 190
     public function setIdentifier($identifier)
191 191
     {
192
-        if (! is_string($identifier) || empty($identifier)) {
192
+        if ( ! is_string($identifier) || empty($identifier)) {
193 193
             throw new InvalidIdentifierException(
194 194
                 is_object($identifier) ? get_class($identifier) : gettype($identifier),
195 195
                 esc_html__('class identifier (typically a \Fully\Qualified\ClassName)', 'event_espresso')
@@ -216,7 +216,7 @@  discard block
 block discarded – undo
216 216
     public function setFqcn($fqcn)
217 217
     {
218 218
         $fqcn = ! empty($fqcn) ? $fqcn : $this->identifier;
219
-        if (! is_string($fqcn)) {
219
+        if ( ! is_string($fqcn)) {
220 220
             throw new InvalidDataTypeException(
221 221
                 '$fqcn',
222 222
                 is_object($fqcn) ? get_class($fqcn) : gettype($fqcn),
@@ -247,7 +247,7 @@  discard block
 block discarded – undo
247 247
         if (empty($ingredients)) {
248 248
             return;
249 249
         }
250
-        if (! is_array($ingredients)) {
250
+        if ( ! is_array($ingredients)) {
251 251
             throw new InvalidDataTypeException(
252 252
                 '$ingredients',
253 253
                 is_object($ingredients) ? get_class($ingredients) : gettype($ingredients),
@@ -279,7 +279,7 @@  discard block
 block discarded – undo
279 279
         if (empty($filters)) {
280 280
             return;
281 281
         }
282
-        if (! is_array($filters)) {
282
+        if ( ! is_array($filters)) {
283 283
             throw new InvalidDataTypeException(
284 284
                 '$filters',
285 285
                 is_object($filters) ? get_class($filters) : gettype($filters),
@@ -306,7 +306,7 @@  discard block
 block discarded – undo
306 306
         if (empty($paths)) {
307 307
             return;
308 308
         }
309
-        if (! (is_string($paths) || is_array($paths))) {
309
+        if ( ! (is_string($paths) || is_array($paths))) {
310 310
             throw new InvalidDataTypeException(
311 311
                 '$path',
312 312
                 is_object($paths) ? get_class($paths) : gettype($paths),
Please login to merge, or discard this patch.
Indentation   +307 added lines, -307 removed lines patch added patch discarded remove patch
@@ -18,311 +18,311 @@
 block discarded – undo
18 18
  */
19 19
 class Recipe implements RecipeInterface
20 20
 {
21
-    /**
22
-     * A default Recipe to use if none is specified for a class
23
-     */
24
-    const DEFAULT_ID = '*';
25
-
26
-    /**
27
-     * Identifier for the entity class to be constructed.
28
-     * Typically a Fully Qualified Class Name
29
-     *
30
-     * @var string $identifier
31
-     */
32
-    private $identifier;
33
-
34
-    /**
35
-     * Fully Qualified Class Name
36
-     *
37
-     * @var string $fqcn
38
-     */
39
-    private $fqcn;
40
-
41
-    /**
42
-     * a dependency class map array
43
-     * If a Recipe is for a single class (or group of classes that shares the EXACT SAME constructor arguments),
44
-     * and that class type hints for an interface, then this property allows you to configure what dependencies
45
-     * get used when instantiating the class.
46
-     * For example:
47
-     *  There's a class called Coffee, and one of its constructor arguments is BeanInterface
48
-     *  There are two implementations of BeanInterface: HonduranBean, and KenyanBean
49
-     *  We want one Coffee object to use HonduranBean for its BeanInterface,
50
-     *  and the 2nd Coffee object to use KenyanBean for its BeanInterface.
51
-     *  To do this, we need to create two Recipes:
52
-     *      one with an identifier of 'HonduranCoffee' using the following ingredients :
53
-     *          array('BeanInterface' => 'HonduranBean')
54
-     *      and the other with an identifier of 'KenyanCoffee' using the following ingredients :
55
-     *          array('BeanInterface' => 'KenyanBean')
56
-     *  Then, whenever the CoffeeShop brews an instance of HonduranCoffee,
57
-     *  an instance of HonduranBean will get injected for the BeanInterface dependency,
58
-     *  and whenever the CoffeeShop brews an instance of KenyanCoffee,
59
-     *  an instance of KenyanBean will get injected for the BeanInterface dependency
60
-     *
61
-     * @var array $ingredients
62
-     */
63
-    private $ingredients = array();
64
-
65
-    /**
66
-     * one of the class constants from CoffeeShop:
67
-     *  CoffeeMaker::BREW_NEW - creates a new instance
68
-     *  CoffeeMaker::BREW_SHARED - creates a shared instance
69
-     *  CoffeeMaker::BREW_LOAD_ONLY - loads but does not instantiate
70
-     *
71
-     * @var string $type
72
-     */
73
-    private $type;
74
-
75
-    /**
76
-     * class name aliases - typically a Fully Qualified Interface that the class implements
77
-     * identifiers passed to the CoffeeShop will be run through the filters to find the correct class name
78
-     *
79
-     * @var array $filters
80
-     */
81
-    private $filters = array();
82
-
83
-    /**
84
-     * array of full server filepaths to files that may contain the class
85
-     *
86
-     * @var array $paths
87
-     */
88
-    private $paths = array();
89
-
90
-
91
-    /**
92
-     * Recipe constructor.
93
-     *
94
-     * @param string $identifier    class identifier, can be an alias, or FQCN, or whatever
95
-     * @param string $fqcn          \Fully\Qualified\ClassName, optional if $identifier is FQCN
96
-     * @param array  $ingredients   array of dependencies that can not be resolved automatically,
97
-     *                              used for resolving concrete classes for type hinted interfaces
98
-     *                              for the dependencies of THIS class
99
-     * @param string $type          recipe type: one of the class constants on
100
-     *                              \EventEspresso\core\services\container\CoffeeMaker
101
-     * @param array  $filters       array of class aliases, or class interfaces
102
-     *                              this works somewhat opposite to the $ingredients array above,
103
-     *                              in that this array specifies interfaces or aliases
104
-     *                              that this Recipe can be used for when resolving OTHER class's dependencies
105
-     * @param array  $paths         if class can not be loaded via PSR-4 autoloading,
106
-     *                              then supply a filepath, or array of filepaths, so that it can be included
107
-     * @throws InvalidIdentifierException
108
-     * @throws RuntimeException
109
-     * @throws InvalidInterfaceException
110
-     * @throws InvalidClassException
111
-     * @throws InvalidDataTypeException
112
-     */
113
-    public function __construct(
114
-        $identifier,
115
-        $fqcn = '',
116
-        array $filters = array(),
117
-        array $ingredients = array(),
118
-        $type = CoffeeMaker::BREW_NEW,
119
-        array $paths = array()
120
-    ) {
121
-        $this->setIdentifier($identifier);
122
-        $this->setFilters($filters);
123
-        $this->setIngredients($ingredients);
124
-        $this->setType($type);
125
-        $this->setPaths($paths);
126
-        $this->setFqcn($fqcn);
127
-    }
128
-
129
-
130
-    /**
131
-     * @return string
132
-     */
133
-    public function identifier()
134
-    {
135
-        return $this->identifier;
136
-    }
137
-
138
-
139
-    /**
140
-     * @return string
141
-     */
142
-    public function fqcn()
143
-    {
144
-        return $this->fqcn;
145
-    }
146
-
147
-
148
-    /**
149
-     * @return array
150
-     */
151
-    public function filters()
152
-    {
153
-        return $this->filters;
154
-    }
155
-
156
-
157
-    /**
158
-     * @return array
159
-     */
160
-    public function ingredients()
161
-    {
162
-        return $this->ingredients;
163
-    }
164
-
165
-
166
-    /**
167
-     * @return string
168
-     */
169
-    public function type()
170
-    {
171
-        return $this->type;
172
-    }
173
-
174
-
175
-    /**
176
-     * @return array
177
-     */
178
-    public function paths()
179
-    {
180
-        return $this->paths;
181
-    }
182
-
183
-
184
-    /**
185
-     * @param  string $identifier Identifier for the entity class that the Recipe applies to
186
-     *                            Typically a Fully Qualified Class Name
187
-     * @throws InvalidIdentifierException
188
-     */
189
-    public function setIdentifier($identifier)
190
-    {
191
-        if (! is_string($identifier) || empty($identifier)) {
192
-            throw new InvalidIdentifierException(
193
-                is_object($identifier) ? get_class($identifier) : gettype($identifier),
194
-                esc_html__('class identifier (typically a \Fully\Qualified\ClassName)', 'event_espresso')
195
-            );
196
-        }
197
-        $this->identifier = $identifier;
198
-    }
199
-
200
-
201
-    /**
202
-     * Ensures incoming string is a valid Fully Qualified Class Name,
203
-     * except if this is the default wildcard Recipe ( * ),
204
-     * or it's NOT an actual FQCN because the Recipe is using filepaths
205
-     * for classes that are not PSR-4 compatible
206
-     * PLZ NOTE:
207
-     *  Recipe::setFqcn() has a check to see if Recipe::$paths is empty or not,
208
-     *  therefore you should always call Recipe::setPaths() before Recipe::setFqcn()
209
-     *
210
-     * @param string $fqcn
211
-     * @throws InvalidDataTypeException
212
-     * @throws InvalidClassException
213
-     * @throws InvalidInterfaceException
214
-     */
215
-    public function setFqcn($fqcn)
216
-    {
217
-        $fqcn = ! empty($fqcn) ? $fqcn : $this->identifier;
218
-        if (! is_string($fqcn)) {
219
-            throw new InvalidDataTypeException(
220
-                '$fqcn',
221
-                is_object($fqcn) ? get_class($fqcn) : gettype($fqcn),
222
-                esc_html__('string (Fully\Qualified\ClassName)', 'event_espresso')
223
-            );
224
-        }
225
-        $fqcn = ltrim($fqcn, '\\');
226
-        if (
227
-            $fqcn !== Recipe::DEFAULT_ID
228
-            && ! empty($fqcn)
229
-            && empty($this->paths)
230
-            && ! (class_exists($fqcn) || interface_exists($fqcn))
231
-        ) {
232
-            throw new InvalidClassException($fqcn);
233
-        }
234
-        $this->fqcn = $fqcn;
235
-    }
236
-
237
-
238
-    /**
239
-     * @param array $ingredients    an array of dependencies where keys are the aliases and values are the FQCNs
240
-     *                              example:
241
-     *                              array( 'ClassInterface' => 'Fully\Qualified\ClassName' )
242
-     * @throws InvalidDataTypeException
243
-     */
244
-    public function setIngredients(array $ingredients)
245
-    {
246
-        if (empty($ingredients)) {
247
-            return;
248
-        }
249
-        if (! is_array($ingredients)) {
250
-            throw new InvalidDataTypeException(
251
-                '$ingredients',
252
-                is_object($ingredients) ? get_class($ingredients) : gettype($ingredients),
253
-                esc_html__('array of class dependencies', 'event_espresso')
254
-            );
255
-        }
256
-        $this->ingredients = array_merge($this->ingredients, $ingredients);
257
-    }
258
-
259
-
260
-    /**
261
-     * @param string $type one of the class constants returned from CoffeeMaker::getTypes()
262
-     * @throws InvalidIdentifierException
263
-     */
264
-    public function setType($type = CoffeeMaker::BREW_NEW)
265
-    {
266
-        $this->type = CoffeeMaker::validateType($type);
267
-    }
268
-
269
-
270
-    /**
271
-     * @param array $filters    an array of filters where keys are the aliases and values are the FQCNs
272
-     *                          example:
273
-     *                          array( 'ClassInterface' => 'Fully\Qualified\ClassName' )
274
-     * @throws InvalidDataTypeException
275
-     */
276
-    public function setFilters(array $filters)
277
-    {
278
-        if (empty($filters)) {
279
-            return;
280
-        }
281
-        if (! is_array($filters)) {
282
-            throw new InvalidDataTypeException(
283
-                '$filters',
284
-                is_object($filters) ? get_class($filters) : gettype($filters),
285
-                esc_html__('array of class aliases', 'event_espresso')
286
-            );
287
-        }
288
-        $this->filters = array_merge($this->filters, $filters);
289
-    }
290
-
291
-
292
-    /**
293
-     * Ensures incoming paths is a valid filepath, or array of valid filepaths,
294
-     * and merges them in with any existing filepaths
295
-     * PLZ NOTE:
296
-     *  Recipe::setFqcn() has a check to see if Recipe::$paths is empty or not,
297
-     *  therefore you should always call Recipe::setPaths() before Recipe::setFqcn()
298
-     *
299
-     * @param string|array $paths
300
-     * @throws RuntimeException
301
-     * @throws InvalidDataTypeException
302
-     */
303
-    public function setPaths($paths = array())
304
-    {
305
-        if (empty($paths)) {
306
-            return;
307
-        }
308
-        if (! (is_string($paths) || is_array($paths))) {
309
-            throw new InvalidDataTypeException(
310
-                '$path',
311
-                is_object($paths) ? get_class($paths) : gettype($paths),
312
-                esc_html__('string or array of strings (full server filepath(s))', 'event_espresso')
313
-            );
314
-        }
315
-        $paths = (array) $paths;
316
-        foreach ($paths as $path) {
317
-            if (strpos($path, '*') === false && ! is_readable($path)) {
318
-                throw new RuntimeException(
319
-                    sprintf(
320
-                        esc_html__('The following filepath is not readable: "%1$s"', 'event_espresso'),
321
-                        $path
322
-                    )
323
-                );
324
-            }
325
-        }
326
-        $this->paths = array_merge($this->paths, $paths);
327
-    }
21
+	/**
22
+	 * A default Recipe to use if none is specified for a class
23
+	 */
24
+	const DEFAULT_ID = '*';
25
+
26
+	/**
27
+	 * Identifier for the entity class to be constructed.
28
+	 * Typically a Fully Qualified Class Name
29
+	 *
30
+	 * @var string $identifier
31
+	 */
32
+	private $identifier;
33
+
34
+	/**
35
+	 * Fully Qualified Class Name
36
+	 *
37
+	 * @var string $fqcn
38
+	 */
39
+	private $fqcn;
40
+
41
+	/**
42
+	 * a dependency class map array
43
+	 * If a Recipe is for a single class (or group of classes that shares the EXACT SAME constructor arguments),
44
+	 * and that class type hints for an interface, then this property allows you to configure what dependencies
45
+	 * get used when instantiating the class.
46
+	 * For example:
47
+	 *  There's a class called Coffee, and one of its constructor arguments is BeanInterface
48
+	 *  There are two implementations of BeanInterface: HonduranBean, and KenyanBean
49
+	 *  We want one Coffee object to use HonduranBean for its BeanInterface,
50
+	 *  and the 2nd Coffee object to use KenyanBean for its BeanInterface.
51
+	 *  To do this, we need to create two Recipes:
52
+	 *      one with an identifier of 'HonduranCoffee' using the following ingredients :
53
+	 *          array('BeanInterface' => 'HonduranBean')
54
+	 *      and the other with an identifier of 'KenyanCoffee' using the following ingredients :
55
+	 *          array('BeanInterface' => 'KenyanBean')
56
+	 *  Then, whenever the CoffeeShop brews an instance of HonduranCoffee,
57
+	 *  an instance of HonduranBean will get injected for the BeanInterface dependency,
58
+	 *  and whenever the CoffeeShop brews an instance of KenyanCoffee,
59
+	 *  an instance of KenyanBean will get injected for the BeanInterface dependency
60
+	 *
61
+	 * @var array $ingredients
62
+	 */
63
+	private $ingredients = array();
64
+
65
+	/**
66
+	 * one of the class constants from CoffeeShop:
67
+	 *  CoffeeMaker::BREW_NEW - creates a new instance
68
+	 *  CoffeeMaker::BREW_SHARED - creates a shared instance
69
+	 *  CoffeeMaker::BREW_LOAD_ONLY - loads but does not instantiate
70
+	 *
71
+	 * @var string $type
72
+	 */
73
+	private $type;
74
+
75
+	/**
76
+	 * class name aliases - typically a Fully Qualified Interface that the class implements
77
+	 * identifiers passed to the CoffeeShop will be run through the filters to find the correct class name
78
+	 *
79
+	 * @var array $filters
80
+	 */
81
+	private $filters = array();
82
+
83
+	/**
84
+	 * array of full server filepaths to files that may contain the class
85
+	 *
86
+	 * @var array $paths
87
+	 */
88
+	private $paths = array();
89
+
90
+
91
+	/**
92
+	 * Recipe constructor.
93
+	 *
94
+	 * @param string $identifier    class identifier, can be an alias, or FQCN, or whatever
95
+	 * @param string $fqcn          \Fully\Qualified\ClassName, optional if $identifier is FQCN
96
+	 * @param array  $ingredients   array of dependencies that can not be resolved automatically,
97
+	 *                              used for resolving concrete classes for type hinted interfaces
98
+	 *                              for the dependencies of THIS class
99
+	 * @param string $type          recipe type: one of the class constants on
100
+	 *                              \EventEspresso\core\services\container\CoffeeMaker
101
+	 * @param array  $filters       array of class aliases, or class interfaces
102
+	 *                              this works somewhat opposite to the $ingredients array above,
103
+	 *                              in that this array specifies interfaces or aliases
104
+	 *                              that this Recipe can be used for when resolving OTHER class's dependencies
105
+	 * @param array  $paths         if class can not be loaded via PSR-4 autoloading,
106
+	 *                              then supply a filepath, or array of filepaths, so that it can be included
107
+	 * @throws InvalidIdentifierException
108
+	 * @throws RuntimeException
109
+	 * @throws InvalidInterfaceException
110
+	 * @throws InvalidClassException
111
+	 * @throws InvalidDataTypeException
112
+	 */
113
+	public function __construct(
114
+		$identifier,
115
+		$fqcn = '',
116
+		array $filters = array(),
117
+		array $ingredients = array(),
118
+		$type = CoffeeMaker::BREW_NEW,
119
+		array $paths = array()
120
+	) {
121
+		$this->setIdentifier($identifier);
122
+		$this->setFilters($filters);
123
+		$this->setIngredients($ingredients);
124
+		$this->setType($type);
125
+		$this->setPaths($paths);
126
+		$this->setFqcn($fqcn);
127
+	}
128
+
129
+
130
+	/**
131
+	 * @return string
132
+	 */
133
+	public function identifier()
134
+	{
135
+		return $this->identifier;
136
+	}
137
+
138
+
139
+	/**
140
+	 * @return string
141
+	 */
142
+	public function fqcn()
143
+	{
144
+		return $this->fqcn;
145
+	}
146
+
147
+
148
+	/**
149
+	 * @return array
150
+	 */
151
+	public function filters()
152
+	{
153
+		return $this->filters;
154
+	}
155
+
156
+
157
+	/**
158
+	 * @return array
159
+	 */
160
+	public function ingredients()
161
+	{
162
+		return $this->ingredients;
163
+	}
164
+
165
+
166
+	/**
167
+	 * @return string
168
+	 */
169
+	public function type()
170
+	{
171
+		return $this->type;
172
+	}
173
+
174
+
175
+	/**
176
+	 * @return array
177
+	 */
178
+	public function paths()
179
+	{
180
+		return $this->paths;
181
+	}
182
+
183
+
184
+	/**
185
+	 * @param  string $identifier Identifier for the entity class that the Recipe applies to
186
+	 *                            Typically a Fully Qualified Class Name
187
+	 * @throws InvalidIdentifierException
188
+	 */
189
+	public function setIdentifier($identifier)
190
+	{
191
+		if (! is_string($identifier) || empty($identifier)) {
192
+			throw new InvalidIdentifierException(
193
+				is_object($identifier) ? get_class($identifier) : gettype($identifier),
194
+				esc_html__('class identifier (typically a \Fully\Qualified\ClassName)', 'event_espresso')
195
+			);
196
+		}
197
+		$this->identifier = $identifier;
198
+	}
199
+
200
+
201
+	/**
202
+	 * Ensures incoming string is a valid Fully Qualified Class Name,
203
+	 * except if this is the default wildcard Recipe ( * ),
204
+	 * or it's NOT an actual FQCN because the Recipe is using filepaths
205
+	 * for classes that are not PSR-4 compatible
206
+	 * PLZ NOTE:
207
+	 *  Recipe::setFqcn() has a check to see if Recipe::$paths is empty or not,
208
+	 *  therefore you should always call Recipe::setPaths() before Recipe::setFqcn()
209
+	 *
210
+	 * @param string $fqcn
211
+	 * @throws InvalidDataTypeException
212
+	 * @throws InvalidClassException
213
+	 * @throws InvalidInterfaceException
214
+	 */
215
+	public function setFqcn($fqcn)
216
+	{
217
+		$fqcn = ! empty($fqcn) ? $fqcn : $this->identifier;
218
+		if (! is_string($fqcn)) {
219
+			throw new InvalidDataTypeException(
220
+				'$fqcn',
221
+				is_object($fqcn) ? get_class($fqcn) : gettype($fqcn),
222
+				esc_html__('string (Fully\Qualified\ClassName)', 'event_espresso')
223
+			);
224
+		}
225
+		$fqcn = ltrim($fqcn, '\\');
226
+		if (
227
+			$fqcn !== Recipe::DEFAULT_ID
228
+			&& ! empty($fqcn)
229
+			&& empty($this->paths)
230
+			&& ! (class_exists($fqcn) || interface_exists($fqcn))
231
+		) {
232
+			throw new InvalidClassException($fqcn);
233
+		}
234
+		$this->fqcn = $fqcn;
235
+	}
236
+
237
+
238
+	/**
239
+	 * @param array $ingredients    an array of dependencies where keys are the aliases and values are the FQCNs
240
+	 *                              example:
241
+	 *                              array( 'ClassInterface' => 'Fully\Qualified\ClassName' )
242
+	 * @throws InvalidDataTypeException
243
+	 */
244
+	public function setIngredients(array $ingredients)
245
+	{
246
+		if (empty($ingredients)) {
247
+			return;
248
+		}
249
+		if (! is_array($ingredients)) {
250
+			throw new InvalidDataTypeException(
251
+				'$ingredients',
252
+				is_object($ingredients) ? get_class($ingredients) : gettype($ingredients),
253
+				esc_html__('array of class dependencies', 'event_espresso')
254
+			);
255
+		}
256
+		$this->ingredients = array_merge($this->ingredients, $ingredients);
257
+	}
258
+
259
+
260
+	/**
261
+	 * @param string $type one of the class constants returned from CoffeeMaker::getTypes()
262
+	 * @throws InvalidIdentifierException
263
+	 */
264
+	public function setType($type = CoffeeMaker::BREW_NEW)
265
+	{
266
+		$this->type = CoffeeMaker::validateType($type);
267
+	}
268
+
269
+
270
+	/**
271
+	 * @param array $filters    an array of filters where keys are the aliases and values are the FQCNs
272
+	 *                          example:
273
+	 *                          array( 'ClassInterface' => 'Fully\Qualified\ClassName' )
274
+	 * @throws InvalidDataTypeException
275
+	 */
276
+	public function setFilters(array $filters)
277
+	{
278
+		if (empty($filters)) {
279
+			return;
280
+		}
281
+		if (! is_array($filters)) {
282
+			throw new InvalidDataTypeException(
283
+				'$filters',
284
+				is_object($filters) ? get_class($filters) : gettype($filters),
285
+				esc_html__('array of class aliases', 'event_espresso')
286
+			);
287
+		}
288
+		$this->filters = array_merge($this->filters, $filters);
289
+	}
290
+
291
+
292
+	/**
293
+	 * Ensures incoming paths is a valid filepath, or array of valid filepaths,
294
+	 * and merges them in with any existing filepaths
295
+	 * PLZ NOTE:
296
+	 *  Recipe::setFqcn() has a check to see if Recipe::$paths is empty or not,
297
+	 *  therefore you should always call Recipe::setPaths() before Recipe::setFqcn()
298
+	 *
299
+	 * @param string|array $paths
300
+	 * @throws RuntimeException
301
+	 * @throws InvalidDataTypeException
302
+	 */
303
+	public function setPaths($paths = array())
304
+	{
305
+		if (empty($paths)) {
306
+			return;
307
+		}
308
+		if (! (is_string($paths) || is_array($paths))) {
309
+			throw new InvalidDataTypeException(
310
+				'$path',
311
+				is_object($paths) ? get_class($paths) : gettype($paths),
312
+				esc_html__('string or array of strings (full server filepath(s))', 'event_espresso')
313
+			);
314
+		}
315
+		$paths = (array) $paths;
316
+		foreach ($paths as $path) {
317
+			if (strpos($path, '*') === false && ! is_readable($path)) {
318
+				throw new RuntimeException(
319
+					sprintf(
320
+						esc_html__('The following filepath is not readable: "%1$s"', 'event_espresso'),
321
+						$path
322
+					)
323
+				);
324
+			}
325
+		}
326
+		$this->paths = array_merge($this->paths, $paths);
327
+	}
328 328
 }
Please login to merge, or discard this patch.
core/services/container/CoffeeShop.php 2 patches
Spacing   +11 added lines, -11 removed lines patch added patch discarded remove patch
@@ -159,7 +159,7 @@  discard block
 block discarded – undo
159 159
         }
160 160
         // if the reservoir doesn't have a closure already for the requested identifier,
161 161
         // then neither a shared service nor a closure for making entities has been built yet
162
-        if (! $this->reservoir->has($identifier)) {
162
+        if ( ! $this->reservoir->has($identifier)) {
163 163
             // so let's brew something up and add it to the proper collection
164 164
             $brewed = $this->makeCoffee($identifier, $arguments, $type);
165 165
         }
@@ -284,7 +284,7 @@  discard block
 block discarded – undo
284 284
      */
285 285
     public function addClosure($identifier, $closure)
286 286
     {
287
-        if (! is_callable($closure)) {
287
+        if ( ! is_callable($closure)) {
288 288
             throw new InvalidDataTypeException('$closure', $closure, 'Closure');
289 289
         }
290 290
         $identifier = $this->processIdentifier($identifier);
@@ -393,7 +393,7 @@  discard block
 block discarded – undo
393 393
             // is the wildcard recipe prefix in the identifier ?
394 394
             if (strpos($identifier, $wildcard) !== false) {
395 395
                 // track matches and use the number of wildcard characters matched for the key
396
-                $matches[ strlen($wildcard) ] = $default_recipe;
396
+                $matches[strlen($wildcard)] = $default_recipe;
397 397
             }
398 398
         }
399 399
         if (count($matches) > 0) {
@@ -433,7 +433,7 @@  discard block
 block discarded – undo
433 433
         }
434 434
         $identifier = $this->processIdentifier($identifier);
435 435
         foreach ((array) $aliases as $alias) {
436
-            $this->filters[ $this->processIdentifier($alias) ] = $identifier;
436
+            $this->filters[$this->processIdentifier($alias)] = $identifier;
437 437
         }
438 438
     }
439 439
 
@@ -475,8 +475,8 @@  discard block
 block discarded – undo
475 475
     private function filterIdentifier($identifier)
476 476
     {
477 477
         $identifier = $this->processIdentifier($identifier);
478
-        return isset($this->filters[ $identifier ]) && ! empty($this->filters[ $identifier ])
479
-            ? $this->filters[ $identifier ]
478
+        return isset($this->filters[$identifier]) && ! empty($this->filters[$identifier])
479
+            ? $this->filters[$identifier]
480 480
             : $identifier;
481 481
     }
482 482
 
@@ -491,7 +491,7 @@  discard block
 block discarded – undo
491 491
      */
492 492
     private function processIdentifier($identifier)
493 493
     {
494
-        if (! is_string($identifier)) {
494
+        if ( ! is_string($identifier)) {
495 495
             throw new InvalidIdentifierException(
496 496
                 is_object($identifier) ? get_class($identifier) : gettype($identifier),
497 497
                 '\Fully\Qualified\ClassName'
@@ -510,7 +510,7 @@  discard block
 block discarded – undo
510 510
      */
511 511
     private function getCoffeeMaker($type)
512 512
     {
513
-        if (! $this->coffee_makers->has($type)) {
513
+        if ( ! $this->coffee_makers->has($type)) {
514 514
             throw new OutOfBoundsException(
515 515
                 esc_html__('The requested coffee maker is either missing or invalid.', 'event_espresso')
516 516
             );
@@ -537,7 +537,7 @@  discard block
 block discarded – undo
537 537
             // does this recipe use a wildcard ? (but is NOT the global default)
538 538
             if ($identifier !== Recipe::DEFAULT_ID && strpos($identifier, '*') !== false) {
539 539
                 // strip the wildcard and use identifier as key
540
-                $default_recipes[ str_replace('*', '', $identifier) ] = $this->recipes->current();
540
+                $default_recipes[str_replace('*', '', $identifier)] = $this->recipes->current();
541 541
             }
542 542
             $this->recipes->next();
543 543
         }
@@ -557,7 +557,7 @@  discard block
 block discarded – undo
557 557
     private function copyDefaultRecipe(RecipeInterface $default_recipe, $identifier, $type = '')
558 558
     {
559 559
         $recipe = clone $default_recipe;
560
-        if (! empty($type)) {
560
+        if ( ! empty($type)) {
561 561
             $recipe->setType($type);
562 562
         }
563 563
         // is this the base default recipe ?
@@ -587,7 +587,7 @@  discard block
 block discarded – undo
587 587
      */
588 588
     private function validateService($identifier, $service)
589 589
     {
590
-        if (! is_object($service)) {
590
+        if ( ! is_object($service)) {
591 591
             throw new InvalidServiceException(
592 592
                 $identifier,
593 593
                 $service
Please login to merge, or discard this patch.
Indentation   +565 added lines, -565 removed lines patch added patch discarded remove patch
@@ -28,569 +28,569 @@
 block discarded – undo
28 28
  */
29 29
 class CoffeeShop implements CoffeePotInterface
30 30
 {
31
-    /**
32
-     * This was the best coffee related name I could think of to represent class name "aliases"
33
-     * So classes can be found via an alias identifier,
34
-     * that is revealed when it is run through... the filters... eh? get it?
35
-     *
36
-     * @var array $filters
37
-     */
38
-    private $filters;
39
-
40
-    /**
41
-     * These are the classes that will actually build the objects (to order of course)
42
-     *
43
-     * @var array $coffee_makers
44
-     */
45
-    private $coffee_makers;
46
-
47
-    /**
48
-     * where the instantiated "singleton" objects are stored
49
-     *
50
-     * @var CollectionInterface $carafe
51
-     */
52
-    private $carafe;
53
-
54
-    /**
55
-     * collection of Recipes that instruct us how to brew objects
56
-     *
57
-     * @var CollectionInterface $recipes
58
-     */
59
-    private $recipes;
60
-
61
-    /**
62
-     * collection of closures for brewing objects
63
-     *
64
-     * @var CollectionInterface $reservoir
65
-     */
66
-    private $reservoir;
67
-
68
-
69
-    /**
70
-     * CoffeeShop constructor
71
-     *
72
-     * @throws InvalidInterfaceException
73
-     */
74
-    public function __construct()
75
-    {
76
-        // array for storing class aliases
77
-        $this->filters = array();
78
-        // create collection for storing shared services
79
-        $this->carafe = new LooseCollection('');
80
-        // create collection for storing recipes that tell us how to build services and entities
81
-        $this->recipes = new Collection('EventEspresso\core\services\container\RecipeInterface');
82
-        // create collection for storing closures for constructing new entities
83
-        $this->reservoir = new Collection('Closure');
84
-        // create collection for storing the generators that build our services and entity closures
85
-        $this->coffee_makers = new Collection('EventEspresso\core\services\container\CoffeeMakerInterface');
86
-    }
87
-
88
-
89
-    /**
90
-     * Returns true if the container can return an entry for the given identifier.
91
-     * Returns false otherwise.
92
-     * `has($identifier)` returning true does not mean that `get($identifier)` will not throw an exception.
93
-     * It does however mean that `get($identifier)` will not throw a `ServiceNotFoundException`.
94
-     *
95
-     * @param string $identifier  Identifier of the entry to look for.
96
-     *                            Typically a Fully Qualified Class Name
97
-     * @return boolean
98
-     * @throws InvalidIdentifierException
99
-     */
100
-    public function has($identifier)
101
-    {
102
-        $identifier = $this->filterIdentifier($identifier);
103
-        return $this->carafe->has($identifier);
104
-    }
105
-
106
-
107
-    /**
108
-     * finds a previously brewed (SHARED) service and returns it
109
-     *
110
-     * @param  string $identifier Identifier for the entity class to be constructed.
111
-     *                            Typically a Fully Qualified Class Name
112
-     * @return mixed
113
-     * @throws InvalidIdentifierException
114
-     * @throws ServiceNotFoundException No service was found for this identifier.
115
-     */
116
-    public function get($identifier)
117
-    {
118
-        $identifier = $this->filterIdentifier($identifier);
119
-        if ($this->carafe->has($identifier)) {
120
-            return $this->carafe->get($identifier);
121
-        }
122
-        throw new ServiceNotFoundException($identifier);
123
-    }
124
-
125
-
126
-    /**
127
-     * returns an instance of the requested entity type using the supplied arguments.
128
-     * If a shared service is requested and an instance is already in the carafe, then it will be returned.
129
-     * If it is not already in the carafe, then the service will be constructed, added to the carafe, and returned
130
-     * If the request is for a new entity and a closure exists in the reservoir for creating it,
131
-     * then a new entity will be instantiated from the closure and returned.
132
-     * If a closure does not exist, then one will be built and added to the reservoir
133
-     * before instantiating the requested entity.
134
-     *
135
-     * @param  string $identifier Identifier for the entity class to be constructed.
136
-     *                            Typically a Fully Qualified Class Name
137
-     * @param array   $arguments  an array of arguments to be passed to the entity constructor
138
-     * @param string  $type
139
-     * @return mixed
140
-     * @throws OutOfBoundsException
141
-     * @throws InstantiationException
142
-     * @throws InvalidDataTypeException
143
-     * @throws InvalidClassException
144
-     * @throws InvalidIdentifierException
145
-     * @throws ServiceExistsException
146
-     * @throws ServiceNotFoundException No service was found for this identifier.
147
-     */
148
-    public function brew($identifier, $arguments = array(), $type = '')
149
-    {
150
-        // resolve any class aliases that may exist
151
-        $identifier = $this->filterIdentifier($identifier);
152
-        // is a shared service being requested and already exists in the carafe?
153
-        $brewed = $this->getShared($identifier, $type);
154
-        // then return whatever was found
155
-        if ($brewed !== false) {
156
-            return $brewed;
157
-        }
158
-        // if the reservoir doesn't have a closure already for the requested identifier,
159
-        // then neither a shared service nor a closure for making entities has been built yet
160
-        if (! $this->reservoir->has($identifier)) {
161
-            // so let's brew something up and add it to the proper collection
162
-            $brewed = $this->makeCoffee($identifier, $arguments, $type);
163
-        }
164
-        // did the requested class only require loading, and if so, was that successful?
165
-        if ($this->brewedLoadOnly($brewed, $identifier, $type) === true) {
166
-            return true;
167
-        }
168
-        // was the brewed item a callable factory function ?
169
-        if (is_callable($brewed)) {
170
-            // then instantiate a new entity from the cached closure
171
-            return $brewed($arguments);
172
-        }
173
-        if ($brewed) {
174
-            // requested object was a shared entity, so attempt to get it from the carafe again
175
-            // because if it wasn't there before, then it should have just been brewed and added,
176
-            // but if it still isn't there, then this time the thrown ServiceNotFoundException will not be caught
177
-            return $this->get($identifier);
178
-        }
179
-        // if identifier is for a non-shared entity,
180
-        // then either a cached closure already existed, or was just brewed
181
-        return $this->brewedClosure($identifier, $arguments);
182
-    }
183
-
184
-
185
-    /**
186
-     * @param string $identifier
187
-     * @param string $type
188
-     * @return bool|mixed
189
-     * @throws InvalidIdentifierException
190
-     */
191
-    protected function getShared($identifier, $type)
192
-    {
193
-        try {
194
-            if (empty($type) || $type === CoffeeMaker::BREW_SHARED) {
195
-                // if a shared service was requested and an instance is in the carafe, then return it
196
-                return $this->get($identifier);
197
-            }
198
-        } catch (ServiceNotFoundException $e) {
199
-            // if not then we'll just catch the ServiceNotFoundException but not do anything just yet,
200
-            // and instead, attempt to build whatever was requested
201
-        }
202
-        return false;
203
-    }
204
-
205
-
206
-    /**
207
-     * @param mixed  $brewed
208
-     * @param string $identifier
209
-     * @param string $type
210
-     * @return bool
211
-     * @throws InvalidClassException
212
-     * @throws InvalidDataTypeException
213
-     * @throws InvalidIdentifierException
214
-     * @throws OutOfBoundsException
215
-     * @throws ServiceExistsException
216
-     * @throws ServiceNotFoundException
217
-     */
218
-    protected function brewedLoadOnly($brewed, $identifier, $type)
219
-    {
220
-        if ($type === CoffeeMaker::BREW_LOAD_ONLY) {
221
-            if ($brewed !== true) {
222
-                throw new ServiceNotFoundException(
223
-                    sprintf(
224
-                        esc_html__(
225
-                            'The "%1$s" class could not be loaded.',
226
-                            'event_espresso'
227
-                        ),
228
-                        $identifier
229
-                    )
230
-                );
231
-            }
232
-            return true;
233
-        }
234
-        return false;
235
-    }
236
-
237
-
238
-    /**
239
-     * @param string $identifier
240
-     * @param array  $arguments
241
-     * @return mixed
242
-     * @throws InstantiationException
243
-     */
244
-    protected function brewedClosure($identifier, array $arguments)
245
-    {
246
-        $closure = $this->reservoir->get($identifier);
247
-        if (empty($closure)) {
248
-            throw new InstantiationException(
249
-                sprintf(
250
-                    esc_html__(
251
-                        'Could not brew an instance of "%1$s".',
252
-                        'event_espresso'
253
-                    ),
254
-                    $identifier
255
-                )
256
-            );
257
-        }
258
-        return $closure($arguments);
259
-    }
260
-
261
-
262
-    /**
263
-     * @param CoffeeMakerInterface $coffee_maker
264
-     * @param string               $type
265
-     * @return bool
266
-     * @throws InvalidIdentifierException
267
-     * @throws InvalidEntityException
268
-     */
269
-    public function addCoffeeMaker(CoffeeMakerInterface $coffee_maker, $type)
270
-    {
271
-        $type = CoffeeMaker::validateType($type);
272
-        return $this->coffee_makers->add($coffee_maker, $type);
273
-    }
274
-
275
-
276
-    /**
277
-     * @param string   $identifier
278
-     * @param callable $closure
279
-     * @return callable|null
280
-     * @throws InvalidIdentifierException
281
-     * @throws InvalidDataTypeException
282
-     */
283
-    public function addClosure($identifier, $closure)
284
-    {
285
-        if (! is_callable($closure)) {
286
-            throw new InvalidDataTypeException('$closure', $closure, 'Closure');
287
-        }
288
-        $identifier = $this->processIdentifier($identifier);
289
-        if ($this->reservoir->add($closure, $identifier)) {
290
-            return $closure;
291
-        }
292
-        return null;
293
-    }
294
-
295
-
296
-    /**
297
-     * @param string $identifier
298
-     * @return boolean
299
-     * @throws InvalidIdentifierException
300
-     */
301
-    public function removeClosure($identifier)
302
-    {
303
-        $identifier = $this->processIdentifier($identifier);
304
-        if ($this->reservoir->has($identifier)) {
305
-            return $this->reservoir->remove($this->reservoir->get($identifier));
306
-        }
307
-        return false;
308
-    }
309
-
310
-
311
-    /**
312
-     * @param  string $identifier Identifier for the entity class that the service applies to
313
-     *                            Typically a Fully Qualified Class Name
314
-     * @param mixed   $service
315
-     * @return bool
316
-     * @throws \EventEspresso\core\services\container\exceptions\InvalidServiceException
317
-     * @throws InvalidIdentifierException
318
-     */
319
-    public function addService($identifier, $service)
320
-    {
321
-        $identifier = $this->processIdentifier($identifier);
322
-        $service = $this->validateService($identifier, $service);
323
-        return $this->carafe->add($service, $identifier);
324
-    }
325
-
326
-
327
-    /**
328
-     * @param string $identifier
329
-     * @return boolean
330
-     * @throws InvalidIdentifierException
331
-     */
332
-    public function removeService($identifier)
333
-    {
334
-        $identifier = $this->processIdentifier($identifier);
335
-        if ($this->carafe->has($identifier)) {
336
-            return $this->carafe->remove($this->carafe->get($identifier));
337
-        }
338
-        return false;
339
-    }
340
-
341
-
342
-    /**
343
-     * Adds instructions on how to brew objects
344
-     *
345
-     * @param RecipeInterface $recipe
346
-     * @return mixed
347
-     * @throws InvalidIdentifierException
348
-     */
349
-    public function addRecipe(RecipeInterface $recipe)
350
-    {
351
-        $this->addAliases($recipe->identifier(), $recipe->filters());
352
-        $identifier = $this->processIdentifier($recipe->identifier());
353
-        return $this->recipes->add($recipe, $identifier);
354
-    }
355
-
356
-
357
-    /**
358
-     * @param string $identifier The Recipe's identifier
359
-     * @return boolean
360
-     * @throws InvalidIdentifierException
361
-     */
362
-    public function removeRecipe($identifier)
363
-    {
364
-        $identifier = $this->processIdentifier($identifier);
365
-        if ($this->recipes->has($identifier)) {
366
-            return $this->recipes->remove($this->recipes->get($identifier));
367
-        }
368
-        return false;
369
-    }
370
-
371
-
372
-    /**
373
-     * Get instructions on how to brew objects
374
-     *
375
-     * @param  string $identifier Identifier for the entity class that the recipe applies to
376
-     *                            Typically a Fully Qualified Class Name
377
-     * @param string  $type
378
-     * @return RecipeInterface
379
-     * @throws OutOfBoundsException
380
-     * @throws InvalidIdentifierException
381
-     */
382
-    public function getRecipe($identifier, $type = '')
383
-    {
384
-        $identifier = $this->processIdentifier($identifier);
385
-        if ($this->recipes->has($identifier)) {
386
-            return $this->recipes->get($identifier);
387
-        }
388
-        $default_recipes = $this->getDefaultRecipes();
389
-        $matches = array();
390
-        foreach ($default_recipes as $wildcard => $default_recipe) {
391
-            // is the wildcard recipe prefix in the identifier ?
392
-            if (strpos($identifier, $wildcard) !== false) {
393
-                // track matches and use the number of wildcard characters matched for the key
394
-                $matches[ strlen($wildcard) ] = $default_recipe;
395
-            }
396
-        }
397
-        if (count($matches) > 0) {
398
-            // sort our recipes by the number of wildcard characters matched
399
-            ksort($matches);
400
-            // then grab the last recipe form the list, since it had the most matching characters
401
-            $match = array_pop($matches);
402
-            // since we are using a default recipe, we need to set it's identifier and fqcn
403
-            return $this->copyDefaultRecipe($match, $identifier, $type);
404
-        }
405
-        if ($this->recipes->has(Recipe::DEFAULT_ID)) {
406
-            // since we are using a default recipe, we need to set it's identifier and fqcn
407
-            return $this->copyDefaultRecipe($this->recipes->get(Recipe::DEFAULT_ID), $identifier, $type);
408
-        }
409
-        throw new OutOfBoundsException(
410
-            sprintf(
411
-                esc_html__('Could not brew coffee because no recipes were found for class "%1$s".', 'event_espresso'),
412
-                $identifier
413
-            )
414
-        );
415
-    }
416
-
417
-
418
-    /**
419
-     * adds class name aliases to list of filters
420
-     *
421
-     * @param  string       $identifier Identifier for the entity class that the alias applies to
422
-     *                                  Typically a Fully Qualified Class Name
423
-     * @param  array|string $aliases
424
-     * @return void
425
-     * @throws InvalidIdentifierException
426
-     */
427
-    public function addAliases($identifier, $aliases)
428
-    {
429
-        if (empty($aliases)) {
430
-            return;
431
-        }
432
-        $identifier = $this->processIdentifier($identifier);
433
-        foreach ((array) $aliases as $alias) {
434
-            $this->filters[ $this->processIdentifier($alias) ] = $identifier;
435
-        }
436
-    }
437
-
438
-
439
-    /**
440
-     * Adds a service to one of the internal collections
441
-     *
442
-     * @param        $identifier
443
-     * @param array  $arguments
444
-     * @param string $type
445
-     * @return mixed
446
-     * @throws InvalidDataTypeException
447
-     * @throws InvalidClassException
448
-     * @throws OutOfBoundsException
449
-     * @throws InvalidIdentifierException
450
-     * @throws ServiceExistsException
451
-     */
452
-    private function makeCoffee($identifier, $arguments = array(), $type = '')
453
-    {
454
-        if ((empty($type) || $type === CoffeeMaker::BREW_SHARED) && $this->has($identifier)) {
455
-            throw new ServiceExistsException($identifier);
456
-        }
457
-        $identifier = $this->filterIdentifier($identifier);
458
-        $recipe = $this->getRecipe($identifier, $type);
459
-        $type = ! empty($type) ? $type : $recipe->type();
460
-        $coffee_maker = $this->getCoffeeMaker($type);
461
-        return $coffee_maker->brew($recipe, $arguments);
462
-    }
463
-
464
-
465
-    /**
466
-     * filters alias identifiers to find the real class name
467
-     *
468
-     * @param  string $identifier Identifier for the entity class that the filter applies to
469
-     *                            Typically a Fully Qualified Class Name
470
-     * @return string
471
-     * @throws InvalidIdentifierException
472
-     */
473
-    private function filterIdentifier($identifier)
474
-    {
475
-        $identifier = $this->processIdentifier($identifier);
476
-        return isset($this->filters[ $identifier ]) && ! empty($this->filters[ $identifier ])
477
-            ? $this->filters[ $identifier ]
478
-            : $identifier;
479
-    }
480
-
481
-
482
-    /**
483
-     * verifies and standardizes identifiers
484
-     *
485
-     * @param  string $identifier Identifier for the entity class
486
-     *                            Typically a Fully Qualified Class Name
487
-     * @return string
488
-     * @throws InvalidIdentifierException
489
-     */
490
-    private function processIdentifier($identifier)
491
-    {
492
-        if (! is_string($identifier)) {
493
-            throw new InvalidIdentifierException(
494
-                is_object($identifier) ? get_class($identifier) : gettype($identifier),
495
-                '\Fully\Qualified\ClassName'
496
-            );
497
-        }
498
-        return ltrim($identifier, '\\');
499
-    }
500
-
501
-
502
-    /**
503
-     * @param string $type
504
-     * @return CoffeeMakerInterface
505
-     * @throws OutOfBoundsException
506
-     * @throws InvalidDataTypeException
507
-     * @throws InvalidClassException
508
-     */
509
-    private function getCoffeeMaker($type)
510
-    {
511
-        if (! $this->coffee_makers->has($type)) {
512
-            throw new OutOfBoundsException(
513
-                esc_html__('The requested coffee maker is either missing or invalid.', 'event_espresso')
514
-            );
515
-        }
516
-        return $this->coffee_makers->get($type);
517
-    }
518
-
519
-
520
-    /**
521
-     * Retrieves all recipes that use a wildcard "*" in their identifier
522
-     * This allows recipes to be set up for handling
523
-     * legacy classes that do not support PSR-4 autoloading.
524
-     * for example:
525
-     * using "EEM_*" for a recipe identifier would target all legacy models like EEM_Attendee
526
-     *
527
-     * @return array
528
-     */
529
-    private function getDefaultRecipes()
530
-    {
531
-        $default_recipes = array();
532
-        $this->recipes->rewind();
533
-        while ($this->recipes->valid()) {
534
-            $identifier = $this->recipes->getInfo();
535
-            // does this recipe use a wildcard ? (but is NOT the global default)
536
-            if ($identifier !== Recipe::DEFAULT_ID && strpos($identifier, '*') !== false) {
537
-                // strip the wildcard and use identifier as key
538
-                $default_recipes[ str_replace('*', '', $identifier) ] = $this->recipes->current();
539
-            }
540
-            $this->recipes->next();
541
-        }
542
-        return $default_recipes;
543
-    }
544
-
545
-
546
-    /**
547
-     * clones a default recipe and then copies details
548
-     * from the incoming request to it so that it can be used
549
-     *
550
-     * @param RecipeInterface $default_recipe
551
-     * @param string          $identifier
552
-     * @param string          $type
553
-     * @return RecipeInterface
554
-     */
555
-    private function copyDefaultRecipe(RecipeInterface $default_recipe, $identifier, $type = '')
556
-    {
557
-        $recipe = clone $default_recipe;
558
-        if (! empty($type)) {
559
-            $recipe->setType($type);
560
-        }
561
-        // is this the base default recipe ?
562
-        if ($default_recipe->identifier() === Recipe::DEFAULT_ID) {
563
-            $recipe->setIdentifier($identifier);
564
-            $recipe->setFqcn($identifier);
565
-            return $recipe;
566
-        }
567
-        $recipe->setIdentifier($identifier);
568
-        foreach ($default_recipe->paths() as $path) {
569
-            $path = str_replace('*', $identifier, $path);
570
-            if (is_readable($path)) {
571
-                $recipe->setPaths($path);
572
-            }
573
-        }
574
-        $recipe->setFqcn($identifier);
575
-        return $recipe;
576
-    }
577
-
578
-
579
-    /**
580
-     * @param  string $identifier Identifier for the entity class that the service applies to
581
-     *                            Typically a Fully Qualified Class Name
582
-     * @param mixed   $service
583
-     * @return mixed
584
-     * @throws InvalidServiceException
585
-     */
586
-    private function validateService($identifier, $service)
587
-    {
588
-        if (! is_object($service)) {
589
-            throw new InvalidServiceException(
590
-                $identifier,
591
-                $service
592
-            );
593
-        }
594
-        return $service;
595
-    }
31
+	/**
32
+	 * This was the best coffee related name I could think of to represent class name "aliases"
33
+	 * So classes can be found via an alias identifier,
34
+	 * that is revealed when it is run through... the filters... eh? get it?
35
+	 *
36
+	 * @var array $filters
37
+	 */
38
+	private $filters;
39
+
40
+	/**
41
+	 * These are the classes that will actually build the objects (to order of course)
42
+	 *
43
+	 * @var array $coffee_makers
44
+	 */
45
+	private $coffee_makers;
46
+
47
+	/**
48
+	 * where the instantiated "singleton" objects are stored
49
+	 *
50
+	 * @var CollectionInterface $carafe
51
+	 */
52
+	private $carafe;
53
+
54
+	/**
55
+	 * collection of Recipes that instruct us how to brew objects
56
+	 *
57
+	 * @var CollectionInterface $recipes
58
+	 */
59
+	private $recipes;
60
+
61
+	/**
62
+	 * collection of closures for brewing objects
63
+	 *
64
+	 * @var CollectionInterface $reservoir
65
+	 */
66
+	private $reservoir;
67
+
68
+
69
+	/**
70
+	 * CoffeeShop constructor
71
+	 *
72
+	 * @throws InvalidInterfaceException
73
+	 */
74
+	public function __construct()
75
+	{
76
+		// array for storing class aliases
77
+		$this->filters = array();
78
+		// create collection for storing shared services
79
+		$this->carafe = new LooseCollection('');
80
+		// create collection for storing recipes that tell us how to build services and entities
81
+		$this->recipes = new Collection('EventEspresso\core\services\container\RecipeInterface');
82
+		// create collection for storing closures for constructing new entities
83
+		$this->reservoir = new Collection('Closure');
84
+		// create collection for storing the generators that build our services and entity closures
85
+		$this->coffee_makers = new Collection('EventEspresso\core\services\container\CoffeeMakerInterface');
86
+	}
87
+
88
+
89
+	/**
90
+	 * Returns true if the container can return an entry for the given identifier.
91
+	 * Returns false otherwise.
92
+	 * `has($identifier)` returning true does not mean that `get($identifier)` will not throw an exception.
93
+	 * It does however mean that `get($identifier)` will not throw a `ServiceNotFoundException`.
94
+	 *
95
+	 * @param string $identifier  Identifier of the entry to look for.
96
+	 *                            Typically a Fully Qualified Class Name
97
+	 * @return boolean
98
+	 * @throws InvalidIdentifierException
99
+	 */
100
+	public function has($identifier)
101
+	{
102
+		$identifier = $this->filterIdentifier($identifier);
103
+		return $this->carafe->has($identifier);
104
+	}
105
+
106
+
107
+	/**
108
+	 * finds a previously brewed (SHARED) service and returns it
109
+	 *
110
+	 * @param  string $identifier Identifier for the entity class to be constructed.
111
+	 *                            Typically a Fully Qualified Class Name
112
+	 * @return mixed
113
+	 * @throws InvalidIdentifierException
114
+	 * @throws ServiceNotFoundException No service was found for this identifier.
115
+	 */
116
+	public function get($identifier)
117
+	{
118
+		$identifier = $this->filterIdentifier($identifier);
119
+		if ($this->carafe->has($identifier)) {
120
+			return $this->carafe->get($identifier);
121
+		}
122
+		throw new ServiceNotFoundException($identifier);
123
+	}
124
+
125
+
126
+	/**
127
+	 * returns an instance of the requested entity type using the supplied arguments.
128
+	 * If a shared service is requested and an instance is already in the carafe, then it will be returned.
129
+	 * If it is not already in the carafe, then the service will be constructed, added to the carafe, and returned
130
+	 * If the request is for a new entity and a closure exists in the reservoir for creating it,
131
+	 * then a new entity will be instantiated from the closure and returned.
132
+	 * If a closure does not exist, then one will be built and added to the reservoir
133
+	 * before instantiating the requested entity.
134
+	 *
135
+	 * @param  string $identifier Identifier for the entity class to be constructed.
136
+	 *                            Typically a Fully Qualified Class Name
137
+	 * @param array   $arguments  an array of arguments to be passed to the entity constructor
138
+	 * @param string  $type
139
+	 * @return mixed
140
+	 * @throws OutOfBoundsException
141
+	 * @throws InstantiationException
142
+	 * @throws InvalidDataTypeException
143
+	 * @throws InvalidClassException
144
+	 * @throws InvalidIdentifierException
145
+	 * @throws ServiceExistsException
146
+	 * @throws ServiceNotFoundException No service was found for this identifier.
147
+	 */
148
+	public function brew($identifier, $arguments = array(), $type = '')
149
+	{
150
+		// resolve any class aliases that may exist
151
+		$identifier = $this->filterIdentifier($identifier);
152
+		// is a shared service being requested and already exists in the carafe?
153
+		$brewed = $this->getShared($identifier, $type);
154
+		// then return whatever was found
155
+		if ($brewed !== false) {
156
+			return $brewed;
157
+		}
158
+		// if the reservoir doesn't have a closure already for the requested identifier,
159
+		// then neither a shared service nor a closure for making entities has been built yet
160
+		if (! $this->reservoir->has($identifier)) {
161
+			// so let's brew something up and add it to the proper collection
162
+			$brewed = $this->makeCoffee($identifier, $arguments, $type);
163
+		}
164
+		// did the requested class only require loading, and if so, was that successful?
165
+		if ($this->brewedLoadOnly($brewed, $identifier, $type) === true) {
166
+			return true;
167
+		}
168
+		// was the brewed item a callable factory function ?
169
+		if (is_callable($brewed)) {
170
+			// then instantiate a new entity from the cached closure
171
+			return $brewed($arguments);
172
+		}
173
+		if ($brewed) {
174
+			// requested object was a shared entity, so attempt to get it from the carafe again
175
+			// because if it wasn't there before, then it should have just been brewed and added,
176
+			// but if it still isn't there, then this time the thrown ServiceNotFoundException will not be caught
177
+			return $this->get($identifier);
178
+		}
179
+		// if identifier is for a non-shared entity,
180
+		// then either a cached closure already existed, or was just brewed
181
+		return $this->brewedClosure($identifier, $arguments);
182
+	}
183
+
184
+
185
+	/**
186
+	 * @param string $identifier
187
+	 * @param string $type
188
+	 * @return bool|mixed
189
+	 * @throws InvalidIdentifierException
190
+	 */
191
+	protected function getShared($identifier, $type)
192
+	{
193
+		try {
194
+			if (empty($type) || $type === CoffeeMaker::BREW_SHARED) {
195
+				// if a shared service was requested and an instance is in the carafe, then return it
196
+				return $this->get($identifier);
197
+			}
198
+		} catch (ServiceNotFoundException $e) {
199
+			// if not then we'll just catch the ServiceNotFoundException but not do anything just yet,
200
+			// and instead, attempt to build whatever was requested
201
+		}
202
+		return false;
203
+	}
204
+
205
+
206
+	/**
207
+	 * @param mixed  $brewed
208
+	 * @param string $identifier
209
+	 * @param string $type
210
+	 * @return bool
211
+	 * @throws InvalidClassException
212
+	 * @throws InvalidDataTypeException
213
+	 * @throws InvalidIdentifierException
214
+	 * @throws OutOfBoundsException
215
+	 * @throws ServiceExistsException
216
+	 * @throws ServiceNotFoundException
217
+	 */
218
+	protected function brewedLoadOnly($brewed, $identifier, $type)
219
+	{
220
+		if ($type === CoffeeMaker::BREW_LOAD_ONLY) {
221
+			if ($brewed !== true) {
222
+				throw new ServiceNotFoundException(
223
+					sprintf(
224
+						esc_html__(
225
+							'The "%1$s" class could not be loaded.',
226
+							'event_espresso'
227
+						),
228
+						$identifier
229
+					)
230
+				);
231
+			}
232
+			return true;
233
+		}
234
+		return false;
235
+	}
236
+
237
+
238
+	/**
239
+	 * @param string $identifier
240
+	 * @param array  $arguments
241
+	 * @return mixed
242
+	 * @throws InstantiationException
243
+	 */
244
+	protected function brewedClosure($identifier, array $arguments)
245
+	{
246
+		$closure = $this->reservoir->get($identifier);
247
+		if (empty($closure)) {
248
+			throw new InstantiationException(
249
+				sprintf(
250
+					esc_html__(
251
+						'Could not brew an instance of "%1$s".',
252
+						'event_espresso'
253
+					),
254
+					$identifier
255
+				)
256
+			);
257
+		}
258
+		return $closure($arguments);
259
+	}
260
+
261
+
262
+	/**
263
+	 * @param CoffeeMakerInterface $coffee_maker
264
+	 * @param string               $type
265
+	 * @return bool
266
+	 * @throws InvalidIdentifierException
267
+	 * @throws InvalidEntityException
268
+	 */
269
+	public function addCoffeeMaker(CoffeeMakerInterface $coffee_maker, $type)
270
+	{
271
+		$type = CoffeeMaker::validateType($type);
272
+		return $this->coffee_makers->add($coffee_maker, $type);
273
+	}
274
+
275
+
276
+	/**
277
+	 * @param string   $identifier
278
+	 * @param callable $closure
279
+	 * @return callable|null
280
+	 * @throws InvalidIdentifierException
281
+	 * @throws InvalidDataTypeException
282
+	 */
283
+	public function addClosure($identifier, $closure)
284
+	{
285
+		if (! is_callable($closure)) {
286
+			throw new InvalidDataTypeException('$closure', $closure, 'Closure');
287
+		}
288
+		$identifier = $this->processIdentifier($identifier);
289
+		if ($this->reservoir->add($closure, $identifier)) {
290
+			return $closure;
291
+		}
292
+		return null;
293
+	}
294
+
295
+
296
+	/**
297
+	 * @param string $identifier
298
+	 * @return boolean
299
+	 * @throws InvalidIdentifierException
300
+	 */
301
+	public function removeClosure($identifier)
302
+	{
303
+		$identifier = $this->processIdentifier($identifier);
304
+		if ($this->reservoir->has($identifier)) {
305
+			return $this->reservoir->remove($this->reservoir->get($identifier));
306
+		}
307
+		return false;
308
+	}
309
+
310
+
311
+	/**
312
+	 * @param  string $identifier Identifier for the entity class that the service applies to
313
+	 *                            Typically a Fully Qualified Class Name
314
+	 * @param mixed   $service
315
+	 * @return bool
316
+	 * @throws \EventEspresso\core\services\container\exceptions\InvalidServiceException
317
+	 * @throws InvalidIdentifierException
318
+	 */
319
+	public function addService($identifier, $service)
320
+	{
321
+		$identifier = $this->processIdentifier($identifier);
322
+		$service = $this->validateService($identifier, $service);
323
+		return $this->carafe->add($service, $identifier);
324
+	}
325
+
326
+
327
+	/**
328
+	 * @param string $identifier
329
+	 * @return boolean
330
+	 * @throws InvalidIdentifierException
331
+	 */
332
+	public function removeService($identifier)
333
+	{
334
+		$identifier = $this->processIdentifier($identifier);
335
+		if ($this->carafe->has($identifier)) {
336
+			return $this->carafe->remove($this->carafe->get($identifier));
337
+		}
338
+		return false;
339
+	}
340
+
341
+
342
+	/**
343
+	 * Adds instructions on how to brew objects
344
+	 *
345
+	 * @param RecipeInterface $recipe
346
+	 * @return mixed
347
+	 * @throws InvalidIdentifierException
348
+	 */
349
+	public function addRecipe(RecipeInterface $recipe)
350
+	{
351
+		$this->addAliases($recipe->identifier(), $recipe->filters());
352
+		$identifier = $this->processIdentifier($recipe->identifier());
353
+		return $this->recipes->add($recipe, $identifier);
354
+	}
355
+
356
+
357
+	/**
358
+	 * @param string $identifier The Recipe's identifier
359
+	 * @return boolean
360
+	 * @throws InvalidIdentifierException
361
+	 */
362
+	public function removeRecipe($identifier)
363
+	{
364
+		$identifier = $this->processIdentifier($identifier);
365
+		if ($this->recipes->has($identifier)) {
366
+			return $this->recipes->remove($this->recipes->get($identifier));
367
+		}
368
+		return false;
369
+	}
370
+
371
+
372
+	/**
373
+	 * Get instructions on how to brew objects
374
+	 *
375
+	 * @param  string $identifier Identifier for the entity class that the recipe applies to
376
+	 *                            Typically a Fully Qualified Class Name
377
+	 * @param string  $type
378
+	 * @return RecipeInterface
379
+	 * @throws OutOfBoundsException
380
+	 * @throws InvalidIdentifierException
381
+	 */
382
+	public function getRecipe($identifier, $type = '')
383
+	{
384
+		$identifier = $this->processIdentifier($identifier);
385
+		if ($this->recipes->has($identifier)) {
386
+			return $this->recipes->get($identifier);
387
+		}
388
+		$default_recipes = $this->getDefaultRecipes();
389
+		$matches = array();
390
+		foreach ($default_recipes as $wildcard => $default_recipe) {
391
+			// is the wildcard recipe prefix in the identifier ?
392
+			if (strpos($identifier, $wildcard) !== false) {
393
+				// track matches and use the number of wildcard characters matched for the key
394
+				$matches[ strlen($wildcard) ] = $default_recipe;
395
+			}
396
+		}
397
+		if (count($matches) > 0) {
398
+			// sort our recipes by the number of wildcard characters matched
399
+			ksort($matches);
400
+			// then grab the last recipe form the list, since it had the most matching characters
401
+			$match = array_pop($matches);
402
+			// since we are using a default recipe, we need to set it's identifier and fqcn
403
+			return $this->copyDefaultRecipe($match, $identifier, $type);
404
+		}
405
+		if ($this->recipes->has(Recipe::DEFAULT_ID)) {
406
+			// since we are using a default recipe, we need to set it's identifier and fqcn
407
+			return $this->copyDefaultRecipe($this->recipes->get(Recipe::DEFAULT_ID), $identifier, $type);
408
+		}
409
+		throw new OutOfBoundsException(
410
+			sprintf(
411
+				esc_html__('Could not brew coffee because no recipes were found for class "%1$s".', 'event_espresso'),
412
+				$identifier
413
+			)
414
+		);
415
+	}
416
+
417
+
418
+	/**
419
+	 * adds class name aliases to list of filters
420
+	 *
421
+	 * @param  string       $identifier Identifier for the entity class that the alias applies to
422
+	 *                                  Typically a Fully Qualified Class Name
423
+	 * @param  array|string $aliases
424
+	 * @return void
425
+	 * @throws InvalidIdentifierException
426
+	 */
427
+	public function addAliases($identifier, $aliases)
428
+	{
429
+		if (empty($aliases)) {
430
+			return;
431
+		}
432
+		$identifier = $this->processIdentifier($identifier);
433
+		foreach ((array) $aliases as $alias) {
434
+			$this->filters[ $this->processIdentifier($alias) ] = $identifier;
435
+		}
436
+	}
437
+
438
+
439
+	/**
440
+	 * Adds a service to one of the internal collections
441
+	 *
442
+	 * @param        $identifier
443
+	 * @param array  $arguments
444
+	 * @param string $type
445
+	 * @return mixed
446
+	 * @throws InvalidDataTypeException
447
+	 * @throws InvalidClassException
448
+	 * @throws OutOfBoundsException
449
+	 * @throws InvalidIdentifierException
450
+	 * @throws ServiceExistsException
451
+	 */
452
+	private function makeCoffee($identifier, $arguments = array(), $type = '')
453
+	{
454
+		if ((empty($type) || $type === CoffeeMaker::BREW_SHARED) && $this->has($identifier)) {
455
+			throw new ServiceExistsException($identifier);
456
+		}
457
+		$identifier = $this->filterIdentifier($identifier);
458
+		$recipe = $this->getRecipe($identifier, $type);
459
+		$type = ! empty($type) ? $type : $recipe->type();
460
+		$coffee_maker = $this->getCoffeeMaker($type);
461
+		return $coffee_maker->brew($recipe, $arguments);
462
+	}
463
+
464
+
465
+	/**
466
+	 * filters alias identifiers to find the real class name
467
+	 *
468
+	 * @param  string $identifier Identifier for the entity class that the filter applies to
469
+	 *                            Typically a Fully Qualified Class Name
470
+	 * @return string
471
+	 * @throws InvalidIdentifierException
472
+	 */
473
+	private function filterIdentifier($identifier)
474
+	{
475
+		$identifier = $this->processIdentifier($identifier);
476
+		return isset($this->filters[ $identifier ]) && ! empty($this->filters[ $identifier ])
477
+			? $this->filters[ $identifier ]
478
+			: $identifier;
479
+	}
480
+
481
+
482
+	/**
483
+	 * verifies and standardizes identifiers
484
+	 *
485
+	 * @param  string $identifier Identifier for the entity class
486
+	 *                            Typically a Fully Qualified Class Name
487
+	 * @return string
488
+	 * @throws InvalidIdentifierException
489
+	 */
490
+	private function processIdentifier($identifier)
491
+	{
492
+		if (! is_string($identifier)) {
493
+			throw new InvalidIdentifierException(
494
+				is_object($identifier) ? get_class($identifier) : gettype($identifier),
495
+				'\Fully\Qualified\ClassName'
496
+			);
497
+		}
498
+		return ltrim($identifier, '\\');
499
+	}
500
+
501
+
502
+	/**
503
+	 * @param string $type
504
+	 * @return CoffeeMakerInterface
505
+	 * @throws OutOfBoundsException
506
+	 * @throws InvalidDataTypeException
507
+	 * @throws InvalidClassException
508
+	 */
509
+	private function getCoffeeMaker($type)
510
+	{
511
+		if (! $this->coffee_makers->has($type)) {
512
+			throw new OutOfBoundsException(
513
+				esc_html__('The requested coffee maker is either missing or invalid.', 'event_espresso')
514
+			);
515
+		}
516
+		return $this->coffee_makers->get($type);
517
+	}
518
+
519
+
520
+	/**
521
+	 * Retrieves all recipes that use a wildcard "*" in their identifier
522
+	 * This allows recipes to be set up for handling
523
+	 * legacy classes that do not support PSR-4 autoloading.
524
+	 * for example:
525
+	 * using "EEM_*" for a recipe identifier would target all legacy models like EEM_Attendee
526
+	 *
527
+	 * @return array
528
+	 */
529
+	private function getDefaultRecipes()
530
+	{
531
+		$default_recipes = array();
532
+		$this->recipes->rewind();
533
+		while ($this->recipes->valid()) {
534
+			$identifier = $this->recipes->getInfo();
535
+			// does this recipe use a wildcard ? (but is NOT the global default)
536
+			if ($identifier !== Recipe::DEFAULT_ID && strpos($identifier, '*') !== false) {
537
+				// strip the wildcard and use identifier as key
538
+				$default_recipes[ str_replace('*', '', $identifier) ] = $this->recipes->current();
539
+			}
540
+			$this->recipes->next();
541
+		}
542
+		return $default_recipes;
543
+	}
544
+
545
+
546
+	/**
547
+	 * clones a default recipe and then copies details
548
+	 * from the incoming request to it so that it can be used
549
+	 *
550
+	 * @param RecipeInterface $default_recipe
551
+	 * @param string          $identifier
552
+	 * @param string          $type
553
+	 * @return RecipeInterface
554
+	 */
555
+	private function copyDefaultRecipe(RecipeInterface $default_recipe, $identifier, $type = '')
556
+	{
557
+		$recipe = clone $default_recipe;
558
+		if (! empty($type)) {
559
+			$recipe->setType($type);
560
+		}
561
+		// is this the base default recipe ?
562
+		if ($default_recipe->identifier() === Recipe::DEFAULT_ID) {
563
+			$recipe->setIdentifier($identifier);
564
+			$recipe->setFqcn($identifier);
565
+			return $recipe;
566
+		}
567
+		$recipe->setIdentifier($identifier);
568
+		foreach ($default_recipe->paths() as $path) {
569
+			$path = str_replace('*', $identifier, $path);
570
+			if (is_readable($path)) {
571
+				$recipe->setPaths($path);
572
+			}
573
+		}
574
+		$recipe->setFqcn($identifier);
575
+		return $recipe;
576
+	}
577
+
578
+
579
+	/**
580
+	 * @param  string $identifier Identifier for the entity class that the service applies to
581
+	 *                            Typically a Fully Qualified Class Name
582
+	 * @param mixed   $service
583
+	 * @return mixed
584
+	 * @throws InvalidServiceException
585
+	 */
586
+	private function validateService($identifier, $service)
587
+	{
588
+		if (! is_object($service)) {
589
+			throw new InvalidServiceException(
590
+				$identifier,
591
+				$service
592
+			);
593
+		}
594
+		return $service;
595
+	}
596 596
 }
Please login to merge, or discard this patch.
core/CPTs/CptQueryModifier.php 2 patches
Indentation   +578 added lines, -578 removed lines patch added patch discarded remove patch
@@ -30,582 +30,582 @@
 block discarded – undo
30 30
  */
31 31
 class CptQueryModifier
32 32
 {
33
-    /**
34
-     * @var CurrentPage $current_page
35
-     */
36
-    protected $current_page;
37
-
38
-    /**
39
-     * @var string $post_type
40
-     */
41
-    protected $post_type = '';
42
-
43
-    /**
44
-     * CPT details from CustomPostTypeDefinitions for specific post type
45
-     *
46
-     * @var array $cpt_details
47
-     */
48
-    protected $cpt_details = array();
49
-
50
-    /**
51
-     * @var EE_Table_Base[] $model_tables
52
-     */
53
-    protected $model_tables = array();
54
-
55
-    /**
56
-     * @var array $taxonomies
57
-     */
58
-    protected $taxonomies = array();
59
-
60
-    /**
61
-     * meta table for the related CPT
62
-     *
63
-     * @var EE_Secondary_Table $meta_table
64
-     */
65
-    protected $meta_table;
66
-
67
-    /**
68
-     * EEM_CPT_Base model for the related CPT
69
-     *
70
-     * @var EEM_CPT_Base $model
71
-     */
72
-    protected $model;
73
-
74
-    /**
75
-     * @var EE_Request_Handler $request_handler
76
-     */
77
-    protected $request_handler;
78
-
79
-    /**
80
-     * @var WP_Query $wp_query
81
-     */
82
-    protected $wp_query;
83
-
84
-    /**
85
-     * @var LoaderInterface $loader
86
-     */
87
-    protected $loader;
88
-
89
-    /**
90
-     * @var RequestInterface $request
91
-     */
92
-    protected $request;
93
-
94
-
95
-    /**
96
-     * CptQueryModifier constructor
97
-     *
98
-     * @param string             $post_type
99
-     * @param array              $cpt_details
100
-     * @param WP_Query           $WP_Query
101
-     * @param CurrentPage $current_page
102
-     * @param RequestInterface   $request
103
-     * @param LoaderInterface    $loader
104
-     * @throws EE_Error
105
-     */
106
-    public function __construct(
107
-        $post_type,
108
-        array $cpt_details,
109
-        WP_Query $WP_Query,
110
-        CurrentPage $current_page,
111
-        RequestInterface $request,
112
-        LoaderInterface $loader
113
-    ) {
114
-        $this->loader = $loader;
115
-        $this->request = $request;
116
-        $this->current_page = $current_page;
117
-        $this->setWpQuery($WP_Query);
118
-        $this->setPostType($post_type);
119
-        $this->setCptDetails($cpt_details);
120
-        $this->init();
121
-    }
122
-
123
-
124
-    /**
125
-     * @return string
126
-     */
127
-    public function postType()
128
-    {
129
-        return $this->post_type;
130
-    }
131
-
132
-
133
-    /**
134
-     * @param string $post_type
135
-     */
136
-    protected function setPostType($post_type)
137
-    {
138
-        $this->post_type = $post_type;
139
-    }
140
-
141
-
142
-    /**
143
-     * @return array
144
-     */
145
-    public function cptDetails()
146
-    {
147
-        return $this->cpt_details;
148
-    }
149
-
150
-
151
-    /**
152
-     * @param array $cpt_details
153
-     */
154
-    protected function setCptDetails($cpt_details)
155
-    {
156
-        $this->cpt_details = $cpt_details;
157
-    }
158
-
159
-
160
-    /**
161
-     * @return EE_Table_Base[]
162
-     */
163
-    public function modelTables()
164
-    {
165
-        return $this->model_tables;
166
-    }
167
-
168
-
169
-    /**
170
-     * @param EE_Table_Base[] $model_tables
171
-     */
172
-    protected function setModelTables($model_tables)
173
-    {
174
-        $this->model_tables = $model_tables;
175
-    }
176
-
177
-
178
-    /**
179
-     * @return array
180
-     * @throws InvalidArgumentException
181
-     * @throws InvalidDataTypeException
182
-     * @throws InvalidInterfaceException
183
-     */
184
-    public function taxonomies()
185
-    {
186
-        if (empty($this->taxonomies)) {
187
-            $this->initializeTaxonomies();
188
-        }
189
-        return $this->taxonomies;
190
-    }
191
-
192
-
193
-    /**
194
-     * @param array $taxonomies
195
-     */
196
-    protected function setTaxonomies(array $taxonomies)
197
-    {
198
-        $this->taxonomies = $taxonomies;
199
-    }
200
-
201
-
202
-    /**
203
-     * @return EE_Secondary_Table
204
-     */
205
-    public function metaTable()
206
-    {
207
-        return $this->meta_table;
208
-    }
209
-
210
-
211
-    /**
212
-     * @param EE_Secondary_Table $meta_table
213
-     */
214
-    public function setMetaTable(EE_Secondary_Table $meta_table)
215
-    {
216
-        $this->meta_table = $meta_table;
217
-    }
218
-
219
-
220
-    /**
221
-     * @return EEM_Base
222
-     */
223
-    public function model()
224
-    {
225
-        return $this->model;
226
-    }
227
-
228
-
229
-    /**
230
-     * @param EEM_Base $CPT_model
231
-     */
232
-    protected function setModel(EEM_Base $CPT_model)
233
-    {
234
-        $this->model = $CPT_model;
235
-    }
236
-
237
-
238
-    /**
239
-     * @deprecated 4.9.63.p
240
-     * @return EE_Request_Handler
241
-     */
242
-    public function request()
243
-    {
244
-        if (! $this->request_handler instanceof EE_Request_Handler) {
245
-            $this->request_handler = LoaderFactory::getLoader()->getShared('EE_Request_Handler');
246
-        }
247
-        return $this->request_handler;
248
-    }
249
-
250
-
251
-
252
-    // phpcs:disable PSR1.Methods.CamelCapsMethodName.NotCamelCaps
253
-
254
-
255
-    /**
256
-     * @return WP_Query
257
-     */
258
-    public function WpQuery()
259
-    {
260
-        return $this->wp_query;
261
-    }
262
-    // phpcs:enable
263
-
264
-
265
-    /**
266
-     * @param WP_Query $wp_query
267
-     */
268
-    public function setWpQuery(WP_Query $wp_query)
269
-    {
270
-        $this->wp_query = $wp_query;
271
-    }
272
-
273
-
274
-    /**
275
-     * @return void
276
-     * @throws InvalidDataTypeException
277
-     * @throws InvalidInterfaceException
278
-     * @throws InvalidArgumentException
279
-     */
280
-    protected function initializeTaxonomies()
281
-    {
282
-        // check if taxonomies have already been set and that this CPT has taxonomies registered for it
283
-        if (
284
-            empty($this->taxonomies)
285
-            && isset($this->cpt_details['args']['taxonomies'])
286
-        ) {
287
-            // if so then grab them, but we want the taxonomy name as the key
288
-            $taxonomies = array_flip($this->cpt_details['args']['taxonomies']);
289
-            // then grab the list of ALL taxonomies
290
-            /** @var CustomTaxonomyDefinitions
291
-             * $taxonomy_definitions
292
-             */
293
-            $taxonomy_definitions = $this->loader->getShared(
294
-                'EventEspresso\core\domain\entities\custom_post_types\CustomTaxonomyDefinitions'
295
-            );
296
-            $all_taxonomies = $taxonomy_definitions->getCustomTaxonomyDefinitions();
297
-            foreach ($taxonomies as $taxonomy => &$details) {
298
-                // add details to our taxonomies if they exist
299
-                $details = isset($all_taxonomies[ $taxonomy ])
300
-                    ? $all_taxonomies[ $taxonomy ]
301
-                    : array();
302
-            }
303
-            // ALWAYS unset() variables that were passed by reference
304
-            unset($details);
305
-            $this->setTaxonomies($taxonomies);
306
-        }
307
-    }
308
-
309
-
310
-    /**
311
-     * @since 4.9.63.p
312
-     * @throws EE_Error
313
-     */
314
-    protected function init()
315
-    {
316
-        $this->setAdditionalCptDetails();
317
-        $this->setRequestVarsIfCpt();
318
-        // convert post_type to model name
319
-        $model_name = str_replace('EE_', '', $this->cpt_details['class_name']);
320
-        // load all tables related to CPT
321
-        $this->setupModelsAndTables($model_name);
322
-        // load and instantiate CPT_*_Strategy
323
-        $CPT_Strategy = $this->cptStrategyClass($model_name);
324
-        // !!!!!!!!!!  IMPORTANT !!!!!!!!!!!!
325
-        // here's the list of available filters in the WP_Query object
326
-        // 'posts_where_paged'
327
-        // 'posts_groupby'
328
-        // 'posts_join_paged'
329
-        // 'posts_orderby'
330
-        // 'posts_distinct'
331
-        // 'post_limits'
332
-        // 'posts_fields'
333
-        // 'posts_join'
334
-        add_filter('posts_fields', array($this, 'postsFields'));
335
-        add_filter('posts_join', array($this, 'postsJoin'));
336
-        add_filter(
337
-            'get_' . $this->post_type . '_metadata',
338
-            array($CPT_Strategy, 'get_EE_post_type_metadata'),
339
-            1,
340
-            4
341
-        );
342
-        add_filter('the_posts', array($this, 'thePosts'), 1, 1);
343
-        if ($this->wp_query->is_main_query()) {
344
-            add_filter('get_edit_post_link', array($this, 'getEditPostLink'), 10, 2);
345
-            $this->addTemplateFilters();
346
-        }
347
-    }
348
-
349
-
350
-    /**
351
-     * sets some basic query vars that pertain to the CPT
352
-     *
353
-     * @access protected
354
-     * @return void
355
-     */
356
-    protected function setAdditionalCptDetails()
357
-    {
358
-        // the post or category or term that is triggering EE
359
-        $this->cpt_details['espresso_page'] = $this->current_page->isEspressoPage();
360
-        // requested post name
361
-        $this->cpt_details['post_name'] = $this->request->getRequestParam('post_name');
362
-        // add support for viewing 'private', 'draft', or 'pending' posts
363
-        if (
364
-            isset($this->wp_query->query_vars['p'])
365
-            && $this->wp_query->query_vars['p'] !== 0
366
-            && is_user_logged_in()
367
-            && current_user_can('edit_post', $this->wp_query->query_vars['p'])
368
-        ) {
369
-            // we can just inject directly into the WP_Query object
370
-            $this->wp_query->query['post_status'] = array('publish', 'private', 'draft', 'pending');
371
-            // now set the main 'ee' request var so that the appropriate module can load the appropriate template(s)
372
-            $this->request->setRequestParam('ee', $this->cpt_details['singular_slug']);
373
-        }
374
-    }
375
-
376
-
377
-    /**
378
-     * Checks if we're on a EE-CPT archive-or-single page, and if we've never set the EE request var.
379
-     * If so, sets the 'ee' request variable
380
-     * so other parts of EE can know what CPT is getting queried.
381
-     * To Mike's knowledge, this must be called from during or after the pre_get_posts hook
382
-     * in order for is_archive() and is_single() methods to work properly.
383
-     *
384
-     * @return void
385
-     */
386
-    public function setRequestVarsIfCpt()
387
-    {
388
-        // check if ee action var has been set
389
-        if (! $this->request->requestParamIsSet('ee')) {
390
-            // check that route exists for CPT archive slug
391
-            if (is_archive() && EE_Config::get_route($this->cpt_details['plural_slug'])) {
392
-                // ie: set "ee" to "events"
393
-                $this->request->setRequestParam('ee', $this->cpt_details['plural_slug']);
394
-                // or does it match a single page CPT like /event/
395
-            } elseif (is_single() && EE_Config::get_route($this->cpt_details['singular_slug'])) {
396
-                // ie: set "ee" to "event"
397
-                $this->request->setRequestParam('ee', $this->cpt_details['singular_slug']);
398
-            }
399
-        }
400
-    }
401
-
402
-
403
-    /**
404
-     * setupModelsAndTables
405
-     *
406
-     * @access protected
407
-     * @param string $model_name
408
-     * @throws EE_Error
409
-     */
410
-    protected function setupModelsAndTables($model_name)
411
-    {
412
-        // get CPT table data via CPT Model
413
-        $full_model_name = strpos($model_name, 'EEM_') !== 0
414
-            ? 'EEM_' . $model_name
415
-            : $model_name;
416
-        $model = $this->loader->getShared($full_model_name);
417
-        if (! $model instanceof EEM_Base) {
418
-            throw new EE_Error(
419
-                sprintf(
420
-                    esc_html__(
421
-                        'The "%1$s" model could not be loaded.',
422
-                        'event_espresso'
423
-                    ),
424
-                    $full_model_name
425
-                )
426
-            );
427
-        }
428
-        $this->setModel($model);
429
-        $this->setModelTables($this->model->get_tables());
430
-        $meta_model = $model_name . '_Meta';
431
-        // is there a Meta Table for this CPT?
432
-        if (
433
-            isset($this->cpt_details['tables'][ $meta_model ])
434
-            && $this->cpt_details['tables'][ $meta_model ] instanceof EE_Secondary_Table
435
-        ) {
436
-            $this->setMetaTable($this->cpt_details['tables'][ $meta_model ]);
437
-        }
438
-    }
439
-
440
-
441
-    /**
442
-     * cptStrategyClass
443
-     *
444
-     * @access protected
445
-     * @param  string $model_name
446
-     * @return string
447
-     */
448
-    protected function cptStrategyClass($model_name)
449
-    {
450
-        // creates classname like:  CPT_Event_Strategy
451
-        $CPT_Strategy_class_name = 'EE_CPT_' . $model_name . '_Strategy';
452
-        // load and instantiate
453
-        $CPT_Strategy = $this->loader->getShared(
454
-            $CPT_Strategy_class_name,
455
-            array('WP_Query' => $this->wp_query, 'CPT' => $this->cpt_details)
456
-        );
457
-        if ($CPT_Strategy === null) {
458
-            $CPT_Strategy = $this->loader->getShared(
459
-                'EE_CPT_Default_Strategy',
460
-                array('WP_Query' => $this->wp_query, 'CPT' => $this->cpt_details)
461
-            );
462
-        }
463
-        return $CPT_Strategy;
464
-    }
465
-
466
-
467
-    /**
468
-     * postsFields
469
-     *
470
-     * @access public
471
-     * @param  $SQL
472
-     * @return string
473
-     */
474
-    public function postsFields($SQL)
475
-    {
476
-        // does this CPT have a meta table ?
477
-        if ($this->meta_table instanceof EE_Secondary_Table) {
478
-            // adds something like ", wp_esp_event_meta.* " to WP Query SELECT statement
479
-            $SQL .= ', ' . $this->meta_table->get_table_name() . '.* ';
480
-        }
481
-        remove_filter('posts_fields', array($this, 'postsFields'));
482
-        return $SQL;
483
-    }
484
-
485
-
486
-    /**
487
-     * postsJoin
488
-     *
489
-     * @access public
490
-     * @param  $SQL
491
-     * @return string
492
-     */
493
-    public function postsJoin($SQL)
494
-    {
495
-        // does this CPT have a meta table ?
496
-        if ($this->meta_table instanceof EE_Secondary_Table) {
497
-            global $wpdb;
498
-            // adds something like " LEFT JOIN wp_esp_event_meta ON ( wp_esp_event_meta.EVT_ID = wp_posts.ID ) " to WP Query JOIN statement
499
-            $SQL .= ' LEFT JOIN '
500
-                    . $this->meta_table->get_table_name()
501
-                    . ' ON ( '
502
-                    . $this->meta_table->get_table_name()
503
-                    . '.'
504
-                    . $this->meta_table->get_fk_on_table()
505
-                    . ' = '
506
-                    . $wpdb->posts
507
-                    . '.ID ) ';
508
-        }
509
-        remove_filter('posts_join', array($this, 'postsJoin'));
510
-        return $SQL;
511
-    }
512
-
513
-
514
-    /**
515
-     * thePosts
516
-     *
517
-     * @access public
518
-     * @param  WP_Post[] $posts
519
-     * @return WP_Post[]
520
-     */
521
-    public function thePosts($posts)
522
-    {
523
-        $CPT_class = $this->cpt_details['class_name'];
524
-        // loop thru posts
525
-        if (is_array($posts) && $this->model instanceof EEM_CPT_Base) {
526
-            foreach ($posts as $post) {
527
-                if ($post->post_type === $this->post_type) {
528
-                    $post->{$CPT_class} = $this->model->instantiate_class_from_post_object($post);
529
-                }
530
-            }
531
-        }
532
-        remove_filter('the_posts', array($this, 'thePosts'), 1);
533
-        return $posts;
534
-    }
535
-
536
-
537
-    /**
538
-     * @param $url
539
-     * @param $ID
540
-     * @return string
541
-     */
542
-    public function getEditPostLink($url, $ID)
543
-    {
544
-        // need to make sure we only edit links if our cpt
545
-        global $post;
546
-        // notice if the cpt is registered with `show_ee_ui` set to false, we take that to mean that the WordPress core ui
547
-        // for interacting with the CPT is desired and there is no EE UI for interacting with the CPT in the admin.
548
-        if (
549
-            ! $post instanceof WP_Post
550
-            || $post->post_type !== $this->post_type
551
-            || (
552
-                isset($this->cpt_details['args']['show_ee_ui'])
553
-                && ! $this->cpt_details['args']['show_ee_ui']
554
-            )
555
-        ) {
556
-            return $url;
557
-        }
558
-        // k made it here so all is good.
559
-        return wp_nonce_url(
560
-            add_query_arg(
561
-                array('page' => $this->post_type, 'post' => $ID, 'action' => 'edit'),
562
-                admin_url('admin.php')
563
-            ),
564
-            'edit',
565
-            'edit_nonce'
566
-        );
567
-    }
568
-
569
-
570
-    /**
571
-     * Execute any template filters.
572
-     * This method is only called if in main query.
573
-     *
574
-     * @return void
575
-     */
576
-    public function addTemplateFilters()
577
-    {
578
-        // if requested cpt supports page_templates and it's the main query
579
-        if (! empty($this->cpt_details['args']['page_templates']) && $this->wp_query->is_main_query()) {
580
-            // then let's hook into the appropriate query_template hook
581
-            add_filter('single_template', array($this, 'singleCptTemplate'));
582
-        }
583
-    }
584
-
585
-
586
-    /**
587
-     * Callback for single_template wp filter.
588
-     * This is used to load the set page_template for a single ee cpt if its set.  If "default" then we load the normal
589
-     * hierarchy.
590
-     *
591
-     * @access public
592
-     * @param string $current_template Existing default template path derived for this page call.
593
-     * @return string the path to the full template file.
594
-     */
595
-    public function singleCptTemplate($current_template)
596
-    {
597
-        $object = get_queried_object();
598
-        // does this called object HAVE a page template set that is something other than the default.
599
-        $template = get_post_meta($object->ID, '_wp_page_template', true);
600
-        // exit early if default or not set or invalid path (accounts for theme changes)
601
-        if (
602
-            $template === 'default'
603
-            || empty($template)
604
-            || ! is_readable(get_stylesheet_directory() . '/' . $template)
605
-        ) {
606
-            return $current_template;
607
-        }
608
-        // made it here so we SHOULD be able to just locate the template and then return it.
609
-        return locate_template(array($template));
610
-    }
33
+	/**
34
+	 * @var CurrentPage $current_page
35
+	 */
36
+	protected $current_page;
37
+
38
+	/**
39
+	 * @var string $post_type
40
+	 */
41
+	protected $post_type = '';
42
+
43
+	/**
44
+	 * CPT details from CustomPostTypeDefinitions for specific post type
45
+	 *
46
+	 * @var array $cpt_details
47
+	 */
48
+	protected $cpt_details = array();
49
+
50
+	/**
51
+	 * @var EE_Table_Base[] $model_tables
52
+	 */
53
+	protected $model_tables = array();
54
+
55
+	/**
56
+	 * @var array $taxonomies
57
+	 */
58
+	protected $taxonomies = array();
59
+
60
+	/**
61
+	 * meta table for the related CPT
62
+	 *
63
+	 * @var EE_Secondary_Table $meta_table
64
+	 */
65
+	protected $meta_table;
66
+
67
+	/**
68
+	 * EEM_CPT_Base model for the related CPT
69
+	 *
70
+	 * @var EEM_CPT_Base $model
71
+	 */
72
+	protected $model;
73
+
74
+	/**
75
+	 * @var EE_Request_Handler $request_handler
76
+	 */
77
+	protected $request_handler;
78
+
79
+	/**
80
+	 * @var WP_Query $wp_query
81
+	 */
82
+	protected $wp_query;
83
+
84
+	/**
85
+	 * @var LoaderInterface $loader
86
+	 */
87
+	protected $loader;
88
+
89
+	/**
90
+	 * @var RequestInterface $request
91
+	 */
92
+	protected $request;
93
+
94
+
95
+	/**
96
+	 * CptQueryModifier constructor
97
+	 *
98
+	 * @param string             $post_type
99
+	 * @param array              $cpt_details
100
+	 * @param WP_Query           $WP_Query
101
+	 * @param CurrentPage $current_page
102
+	 * @param RequestInterface   $request
103
+	 * @param LoaderInterface    $loader
104
+	 * @throws EE_Error
105
+	 */
106
+	public function __construct(
107
+		$post_type,
108
+		array $cpt_details,
109
+		WP_Query $WP_Query,
110
+		CurrentPage $current_page,
111
+		RequestInterface $request,
112
+		LoaderInterface $loader
113
+	) {
114
+		$this->loader = $loader;
115
+		$this->request = $request;
116
+		$this->current_page = $current_page;
117
+		$this->setWpQuery($WP_Query);
118
+		$this->setPostType($post_type);
119
+		$this->setCptDetails($cpt_details);
120
+		$this->init();
121
+	}
122
+
123
+
124
+	/**
125
+	 * @return string
126
+	 */
127
+	public function postType()
128
+	{
129
+		return $this->post_type;
130
+	}
131
+
132
+
133
+	/**
134
+	 * @param string $post_type
135
+	 */
136
+	protected function setPostType($post_type)
137
+	{
138
+		$this->post_type = $post_type;
139
+	}
140
+
141
+
142
+	/**
143
+	 * @return array
144
+	 */
145
+	public function cptDetails()
146
+	{
147
+		return $this->cpt_details;
148
+	}
149
+
150
+
151
+	/**
152
+	 * @param array $cpt_details
153
+	 */
154
+	protected function setCptDetails($cpt_details)
155
+	{
156
+		$this->cpt_details = $cpt_details;
157
+	}
158
+
159
+
160
+	/**
161
+	 * @return EE_Table_Base[]
162
+	 */
163
+	public function modelTables()
164
+	{
165
+		return $this->model_tables;
166
+	}
167
+
168
+
169
+	/**
170
+	 * @param EE_Table_Base[] $model_tables
171
+	 */
172
+	protected function setModelTables($model_tables)
173
+	{
174
+		$this->model_tables = $model_tables;
175
+	}
176
+
177
+
178
+	/**
179
+	 * @return array
180
+	 * @throws InvalidArgumentException
181
+	 * @throws InvalidDataTypeException
182
+	 * @throws InvalidInterfaceException
183
+	 */
184
+	public function taxonomies()
185
+	{
186
+		if (empty($this->taxonomies)) {
187
+			$this->initializeTaxonomies();
188
+		}
189
+		return $this->taxonomies;
190
+	}
191
+
192
+
193
+	/**
194
+	 * @param array $taxonomies
195
+	 */
196
+	protected function setTaxonomies(array $taxonomies)
197
+	{
198
+		$this->taxonomies = $taxonomies;
199
+	}
200
+
201
+
202
+	/**
203
+	 * @return EE_Secondary_Table
204
+	 */
205
+	public function metaTable()
206
+	{
207
+		return $this->meta_table;
208
+	}
209
+
210
+
211
+	/**
212
+	 * @param EE_Secondary_Table $meta_table
213
+	 */
214
+	public function setMetaTable(EE_Secondary_Table $meta_table)
215
+	{
216
+		$this->meta_table = $meta_table;
217
+	}
218
+
219
+
220
+	/**
221
+	 * @return EEM_Base
222
+	 */
223
+	public function model()
224
+	{
225
+		return $this->model;
226
+	}
227
+
228
+
229
+	/**
230
+	 * @param EEM_Base $CPT_model
231
+	 */
232
+	protected function setModel(EEM_Base $CPT_model)
233
+	{
234
+		$this->model = $CPT_model;
235
+	}
236
+
237
+
238
+	/**
239
+	 * @deprecated 4.9.63.p
240
+	 * @return EE_Request_Handler
241
+	 */
242
+	public function request()
243
+	{
244
+		if (! $this->request_handler instanceof EE_Request_Handler) {
245
+			$this->request_handler = LoaderFactory::getLoader()->getShared('EE_Request_Handler');
246
+		}
247
+		return $this->request_handler;
248
+	}
249
+
250
+
251
+
252
+	// phpcs:disable PSR1.Methods.CamelCapsMethodName.NotCamelCaps
253
+
254
+
255
+	/**
256
+	 * @return WP_Query
257
+	 */
258
+	public function WpQuery()
259
+	{
260
+		return $this->wp_query;
261
+	}
262
+	// phpcs:enable
263
+
264
+
265
+	/**
266
+	 * @param WP_Query $wp_query
267
+	 */
268
+	public function setWpQuery(WP_Query $wp_query)
269
+	{
270
+		$this->wp_query = $wp_query;
271
+	}
272
+
273
+
274
+	/**
275
+	 * @return void
276
+	 * @throws InvalidDataTypeException
277
+	 * @throws InvalidInterfaceException
278
+	 * @throws InvalidArgumentException
279
+	 */
280
+	protected function initializeTaxonomies()
281
+	{
282
+		// check if taxonomies have already been set and that this CPT has taxonomies registered for it
283
+		if (
284
+			empty($this->taxonomies)
285
+			&& isset($this->cpt_details['args']['taxonomies'])
286
+		) {
287
+			// if so then grab them, but we want the taxonomy name as the key
288
+			$taxonomies = array_flip($this->cpt_details['args']['taxonomies']);
289
+			// then grab the list of ALL taxonomies
290
+			/** @var CustomTaxonomyDefinitions
291
+			 * $taxonomy_definitions
292
+			 */
293
+			$taxonomy_definitions = $this->loader->getShared(
294
+				'EventEspresso\core\domain\entities\custom_post_types\CustomTaxonomyDefinitions'
295
+			);
296
+			$all_taxonomies = $taxonomy_definitions->getCustomTaxonomyDefinitions();
297
+			foreach ($taxonomies as $taxonomy => &$details) {
298
+				// add details to our taxonomies if they exist
299
+				$details = isset($all_taxonomies[ $taxonomy ])
300
+					? $all_taxonomies[ $taxonomy ]
301
+					: array();
302
+			}
303
+			// ALWAYS unset() variables that were passed by reference
304
+			unset($details);
305
+			$this->setTaxonomies($taxonomies);
306
+		}
307
+	}
308
+
309
+
310
+	/**
311
+	 * @since 4.9.63.p
312
+	 * @throws EE_Error
313
+	 */
314
+	protected function init()
315
+	{
316
+		$this->setAdditionalCptDetails();
317
+		$this->setRequestVarsIfCpt();
318
+		// convert post_type to model name
319
+		$model_name = str_replace('EE_', '', $this->cpt_details['class_name']);
320
+		// load all tables related to CPT
321
+		$this->setupModelsAndTables($model_name);
322
+		// load and instantiate CPT_*_Strategy
323
+		$CPT_Strategy = $this->cptStrategyClass($model_name);
324
+		// !!!!!!!!!!  IMPORTANT !!!!!!!!!!!!
325
+		// here's the list of available filters in the WP_Query object
326
+		// 'posts_where_paged'
327
+		// 'posts_groupby'
328
+		// 'posts_join_paged'
329
+		// 'posts_orderby'
330
+		// 'posts_distinct'
331
+		// 'post_limits'
332
+		// 'posts_fields'
333
+		// 'posts_join'
334
+		add_filter('posts_fields', array($this, 'postsFields'));
335
+		add_filter('posts_join', array($this, 'postsJoin'));
336
+		add_filter(
337
+			'get_' . $this->post_type . '_metadata',
338
+			array($CPT_Strategy, 'get_EE_post_type_metadata'),
339
+			1,
340
+			4
341
+		);
342
+		add_filter('the_posts', array($this, 'thePosts'), 1, 1);
343
+		if ($this->wp_query->is_main_query()) {
344
+			add_filter('get_edit_post_link', array($this, 'getEditPostLink'), 10, 2);
345
+			$this->addTemplateFilters();
346
+		}
347
+	}
348
+
349
+
350
+	/**
351
+	 * sets some basic query vars that pertain to the CPT
352
+	 *
353
+	 * @access protected
354
+	 * @return void
355
+	 */
356
+	protected function setAdditionalCptDetails()
357
+	{
358
+		// the post or category or term that is triggering EE
359
+		$this->cpt_details['espresso_page'] = $this->current_page->isEspressoPage();
360
+		// requested post name
361
+		$this->cpt_details['post_name'] = $this->request->getRequestParam('post_name');
362
+		// add support for viewing 'private', 'draft', or 'pending' posts
363
+		if (
364
+			isset($this->wp_query->query_vars['p'])
365
+			&& $this->wp_query->query_vars['p'] !== 0
366
+			&& is_user_logged_in()
367
+			&& current_user_can('edit_post', $this->wp_query->query_vars['p'])
368
+		) {
369
+			// we can just inject directly into the WP_Query object
370
+			$this->wp_query->query['post_status'] = array('publish', 'private', 'draft', 'pending');
371
+			// now set the main 'ee' request var so that the appropriate module can load the appropriate template(s)
372
+			$this->request->setRequestParam('ee', $this->cpt_details['singular_slug']);
373
+		}
374
+	}
375
+
376
+
377
+	/**
378
+	 * Checks if we're on a EE-CPT archive-or-single page, and if we've never set the EE request var.
379
+	 * If so, sets the 'ee' request variable
380
+	 * so other parts of EE can know what CPT is getting queried.
381
+	 * To Mike's knowledge, this must be called from during or after the pre_get_posts hook
382
+	 * in order for is_archive() and is_single() methods to work properly.
383
+	 *
384
+	 * @return void
385
+	 */
386
+	public function setRequestVarsIfCpt()
387
+	{
388
+		// check if ee action var has been set
389
+		if (! $this->request->requestParamIsSet('ee')) {
390
+			// check that route exists for CPT archive slug
391
+			if (is_archive() && EE_Config::get_route($this->cpt_details['plural_slug'])) {
392
+				// ie: set "ee" to "events"
393
+				$this->request->setRequestParam('ee', $this->cpt_details['plural_slug']);
394
+				// or does it match a single page CPT like /event/
395
+			} elseif (is_single() && EE_Config::get_route($this->cpt_details['singular_slug'])) {
396
+				// ie: set "ee" to "event"
397
+				$this->request->setRequestParam('ee', $this->cpt_details['singular_slug']);
398
+			}
399
+		}
400
+	}
401
+
402
+
403
+	/**
404
+	 * setupModelsAndTables
405
+	 *
406
+	 * @access protected
407
+	 * @param string $model_name
408
+	 * @throws EE_Error
409
+	 */
410
+	protected function setupModelsAndTables($model_name)
411
+	{
412
+		// get CPT table data via CPT Model
413
+		$full_model_name = strpos($model_name, 'EEM_') !== 0
414
+			? 'EEM_' . $model_name
415
+			: $model_name;
416
+		$model = $this->loader->getShared($full_model_name);
417
+		if (! $model instanceof EEM_Base) {
418
+			throw new EE_Error(
419
+				sprintf(
420
+					esc_html__(
421
+						'The "%1$s" model could not be loaded.',
422
+						'event_espresso'
423
+					),
424
+					$full_model_name
425
+				)
426
+			);
427
+		}
428
+		$this->setModel($model);
429
+		$this->setModelTables($this->model->get_tables());
430
+		$meta_model = $model_name . '_Meta';
431
+		// is there a Meta Table for this CPT?
432
+		if (
433
+			isset($this->cpt_details['tables'][ $meta_model ])
434
+			&& $this->cpt_details['tables'][ $meta_model ] instanceof EE_Secondary_Table
435
+		) {
436
+			$this->setMetaTable($this->cpt_details['tables'][ $meta_model ]);
437
+		}
438
+	}
439
+
440
+
441
+	/**
442
+	 * cptStrategyClass
443
+	 *
444
+	 * @access protected
445
+	 * @param  string $model_name
446
+	 * @return string
447
+	 */
448
+	protected function cptStrategyClass($model_name)
449
+	{
450
+		// creates classname like:  CPT_Event_Strategy
451
+		$CPT_Strategy_class_name = 'EE_CPT_' . $model_name . '_Strategy';
452
+		// load and instantiate
453
+		$CPT_Strategy = $this->loader->getShared(
454
+			$CPT_Strategy_class_name,
455
+			array('WP_Query' => $this->wp_query, 'CPT' => $this->cpt_details)
456
+		);
457
+		if ($CPT_Strategy === null) {
458
+			$CPT_Strategy = $this->loader->getShared(
459
+				'EE_CPT_Default_Strategy',
460
+				array('WP_Query' => $this->wp_query, 'CPT' => $this->cpt_details)
461
+			);
462
+		}
463
+		return $CPT_Strategy;
464
+	}
465
+
466
+
467
+	/**
468
+	 * postsFields
469
+	 *
470
+	 * @access public
471
+	 * @param  $SQL
472
+	 * @return string
473
+	 */
474
+	public function postsFields($SQL)
475
+	{
476
+		// does this CPT have a meta table ?
477
+		if ($this->meta_table instanceof EE_Secondary_Table) {
478
+			// adds something like ", wp_esp_event_meta.* " to WP Query SELECT statement
479
+			$SQL .= ', ' . $this->meta_table->get_table_name() . '.* ';
480
+		}
481
+		remove_filter('posts_fields', array($this, 'postsFields'));
482
+		return $SQL;
483
+	}
484
+
485
+
486
+	/**
487
+	 * postsJoin
488
+	 *
489
+	 * @access public
490
+	 * @param  $SQL
491
+	 * @return string
492
+	 */
493
+	public function postsJoin($SQL)
494
+	{
495
+		// does this CPT have a meta table ?
496
+		if ($this->meta_table instanceof EE_Secondary_Table) {
497
+			global $wpdb;
498
+			// adds something like " LEFT JOIN wp_esp_event_meta ON ( wp_esp_event_meta.EVT_ID = wp_posts.ID ) " to WP Query JOIN statement
499
+			$SQL .= ' LEFT JOIN '
500
+					. $this->meta_table->get_table_name()
501
+					. ' ON ( '
502
+					. $this->meta_table->get_table_name()
503
+					. '.'
504
+					. $this->meta_table->get_fk_on_table()
505
+					. ' = '
506
+					. $wpdb->posts
507
+					. '.ID ) ';
508
+		}
509
+		remove_filter('posts_join', array($this, 'postsJoin'));
510
+		return $SQL;
511
+	}
512
+
513
+
514
+	/**
515
+	 * thePosts
516
+	 *
517
+	 * @access public
518
+	 * @param  WP_Post[] $posts
519
+	 * @return WP_Post[]
520
+	 */
521
+	public function thePosts($posts)
522
+	{
523
+		$CPT_class = $this->cpt_details['class_name'];
524
+		// loop thru posts
525
+		if (is_array($posts) && $this->model instanceof EEM_CPT_Base) {
526
+			foreach ($posts as $post) {
527
+				if ($post->post_type === $this->post_type) {
528
+					$post->{$CPT_class} = $this->model->instantiate_class_from_post_object($post);
529
+				}
530
+			}
531
+		}
532
+		remove_filter('the_posts', array($this, 'thePosts'), 1);
533
+		return $posts;
534
+	}
535
+
536
+
537
+	/**
538
+	 * @param $url
539
+	 * @param $ID
540
+	 * @return string
541
+	 */
542
+	public function getEditPostLink($url, $ID)
543
+	{
544
+		// need to make sure we only edit links if our cpt
545
+		global $post;
546
+		// notice if the cpt is registered with `show_ee_ui` set to false, we take that to mean that the WordPress core ui
547
+		// for interacting with the CPT is desired and there is no EE UI for interacting with the CPT in the admin.
548
+		if (
549
+			! $post instanceof WP_Post
550
+			|| $post->post_type !== $this->post_type
551
+			|| (
552
+				isset($this->cpt_details['args']['show_ee_ui'])
553
+				&& ! $this->cpt_details['args']['show_ee_ui']
554
+			)
555
+		) {
556
+			return $url;
557
+		}
558
+		// k made it here so all is good.
559
+		return wp_nonce_url(
560
+			add_query_arg(
561
+				array('page' => $this->post_type, 'post' => $ID, 'action' => 'edit'),
562
+				admin_url('admin.php')
563
+			),
564
+			'edit',
565
+			'edit_nonce'
566
+		);
567
+	}
568
+
569
+
570
+	/**
571
+	 * Execute any template filters.
572
+	 * This method is only called if in main query.
573
+	 *
574
+	 * @return void
575
+	 */
576
+	public function addTemplateFilters()
577
+	{
578
+		// if requested cpt supports page_templates and it's the main query
579
+		if (! empty($this->cpt_details['args']['page_templates']) && $this->wp_query->is_main_query()) {
580
+			// then let's hook into the appropriate query_template hook
581
+			add_filter('single_template', array($this, 'singleCptTemplate'));
582
+		}
583
+	}
584
+
585
+
586
+	/**
587
+	 * Callback for single_template wp filter.
588
+	 * This is used to load the set page_template for a single ee cpt if its set.  If "default" then we load the normal
589
+	 * hierarchy.
590
+	 *
591
+	 * @access public
592
+	 * @param string $current_template Existing default template path derived for this page call.
593
+	 * @return string the path to the full template file.
594
+	 */
595
+	public function singleCptTemplate($current_template)
596
+	{
597
+		$object = get_queried_object();
598
+		// does this called object HAVE a page template set that is something other than the default.
599
+		$template = get_post_meta($object->ID, '_wp_page_template', true);
600
+		// exit early if default or not set or invalid path (accounts for theme changes)
601
+		if (
602
+			$template === 'default'
603
+			|| empty($template)
604
+			|| ! is_readable(get_stylesheet_directory() . '/' . $template)
605
+		) {
606
+			return $current_template;
607
+		}
608
+		// made it here so we SHOULD be able to just locate the template and then return it.
609
+		return locate_template(array($template));
610
+	}
611 611
 }
Please login to merge, or discard this patch.
Spacing   +15 added lines, -15 removed lines patch added patch discarded remove patch
@@ -241,7 +241,7 @@  discard block
 block discarded – undo
241 241
      */
242 242
     public function request()
243 243
     {
244
-        if (! $this->request_handler instanceof EE_Request_Handler) {
244
+        if ( ! $this->request_handler instanceof EE_Request_Handler) {
245 245
             $this->request_handler = LoaderFactory::getLoader()->getShared('EE_Request_Handler');
246 246
         }
247 247
         return $this->request_handler;
@@ -296,8 +296,8 @@  discard block
 block discarded – undo
296 296
             $all_taxonomies = $taxonomy_definitions->getCustomTaxonomyDefinitions();
297 297
             foreach ($taxonomies as $taxonomy => &$details) {
298 298
                 // add details to our taxonomies if they exist
299
-                $details = isset($all_taxonomies[ $taxonomy ])
300
-                    ? $all_taxonomies[ $taxonomy ]
299
+                $details = isset($all_taxonomies[$taxonomy])
300
+                    ? $all_taxonomies[$taxonomy]
301 301
                     : array();
302 302
             }
303 303
             // ALWAYS unset() variables that were passed by reference
@@ -334,7 +334,7 @@  discard block
 block discarded – undo
334 334
         add_filter('posts_fields', array($this, 'postsFields'));
335 335
         add_filter('posts_join', array($this, 'postsJoin'));
336 336
         add_filter(
337
-            'get_' . $this->post_type . '_metadata',
337
+            'get_'.$this->post_type.'_metadata',
338 338
             array($CPT_Strategy, 'get_EE_post_type_metadata'),
339 339
             1,
340 340
             4
@@ -386,7 +386,7 @@  discard block
 block discarded – undo
386 386
     public function setRequestVarsIfCpt()
387 387
     {
388 388
         // check if ee action var has been set
389
-        if (! $this->request->requestParamIsSet('ee')) {
389
+        if ( ! $this->request->requestParamIsSet('ee')) {
390 390
             // check that route exists for CPT archive slug
391 391
             if (is_archive() && EE_Config::get_route($this->cpt_details['plural_slug'])) {
392 392
                 // ie: set "ee" to "events"
@@ -411,10 +411,10 @@  discard block
 block discarded – undo
411 411
     {
412 412
         // get CPT table data via CPT Model
413 413
         $full_model_name = strpos($model_name, 'EEM_') !== 0
414
-            ? 'EEM_' . $model_name
414
+            ? 'EEM_'.$model_name
415 415
             : $model_name;
416 416
         $model = $this->loader->getShared($full_model_name);
417
-        if (! $model instanceof EEM_Base) {
417
+        if ( ! $model instanceof EEM_Base) {
418 418
             throw new EE_Error(
419 419
                 sprintf(
420 420
                     esc_html__(
@@ -427,13 +427,13 @@  discard block
 block discarded – undo
427 427
         }
428 428
         $this->setModel($model);
429 429
         $this->setModelTables($this->model->get_tables());
430
-        $meta_model = $model_name . '_Meta';
430
+        $meta_model = $model_name.'_Meta';
431 431
         // is there a Meta Table for this CPT?
432 432
         if (
433
-            isset($this->cpt_details['tables'][ $meta_model ])
434
-            && $this->cpt_details['tables'][ $meta_model ] instanceof EE_Secondary_Table
433
+            isset($this->cpt_details['tables'][$meta_model])
434
+            && $this->cpt_details['tables'][$meta_model] instanceof EE_Secondary_Table
435 435
         ) {
436
-            $this->setMetaTable($this->cpt_details['tables'][ $meta_model ]);
436
+            $this->setMetaTable($this->cpt_details['tables'][$meta_model]);
437 437
         }
438 438
     }
439 439
 
@@ -448,7 +448,7 @@  discard block
 block discarded – undo
448 448
     protected function cptStrategyClass($model_name)
449 449
     {
450 450
         // creates classname like:  CPT_Event_Strategy
451
-        $CPT_Strategy_class_name = 'EE_CPT_' . $model_name . '_Strategy';
451
+        $CPT_Strategy_class_name = 'EE_CPT_'.$model_name.'_Strategy';
452 452
         // load and instantiate
453 453
         $CPT_Strategy = $this->loader->getShared(
454 454
             $CPT_Strategy_class_name,
@@ -476,7 +476,7 @@  discard block
 block discarded – undo
476 476
         // does this CPT have a meta table ?
477 477
         if ($this->meta_table instanceof EE_Secondary_Table) {
478 478
             // adds something like ", wp_esp_event_meta.* " to WP Query SELECT statement
479
-            $SQL .= ', ' . $this->meta_table->get_table_name() . '.* ';
479
+            $SQL .= ', '.$this->meta_table->get_table_name().'.* ';
480 480
         }
481 481
         remove_filter('posts_fields', array($this, 'postsFields'));
482 482
         return $SQL;
@@ -576,7 +576,7 @@  discard block
 block discarded – undo
576 576
     public function addTemplateFilters()
577 577
     {
578 578
         // if requested cpt supports page_templates and it's the main query
579
-        if (! empty($this->cpt_details['args']['page_templates']) && $this->wp_query->is_main_query()) {
579
+        if ( ! empty($this->cpt_details['args']['page_templates']) && $this->wp_query->is_main_query()) {
580 580
             // then let's hook into the appropriate query_template hook
581 581
             add_filter('single_template', array($this, 'singleCptTemplate'));
582 582
         }
@@ -601,7 +601,7 @@  discard block
 block discarded – undo
601 601
         if (
602 602
             $template === 'default'
603 603
             || empty($template)
604
-            || ! is_readable(get_stylesheet_directory() . '/' . $template)
604
+            || ! is_readable(get_stylesheet_directory().'/'.$template)
605 605
         ) {
606 606
             return $current_template;
607 607
         }
Please login to merge, or discard this patch.
core/EE_Cron_Tasks.core.php 2 patches
Spacing   +14 added lines, -14 removed lines patch added patch discarded remove patch
@@ -38,7 +38,7 @@  discard block
 block discarded – undo
38 38
      */
39 39
     public static function instance()
40 40
     {
41
-        if (! self::$_instance instanceof EE_Cron_Tasks) {
41
+        if ( ! self::$_instance instanceof EE_Cron_Tasks) {
42 42
             self::$_instance = new self();
43 43
         }
44 44
         return self::$_instance;
@@ -72,7 +72,7 @@  discard block
 block discarded – undo
72 72
              */
73 73
             add_action(
74 74
                 'AHEE__EE_System__load_core_configuration__complete',
75
-                function () {
75
+                function() {
76 76
                     EE_Registry::instance()->NET_CFG->core->do_messages_on_same_request = true;
77 77
                     EE_Registry::instance()->NET_CFG->update_config(true, false);
78 78
                     add_option('ee_disabled_wp_cron_check', 1, '', false);
@@ -124,16 +124,16 @@  discard block
 block discarded – undo
124 124
             'AHEE__EE_Cron_Tasks__clean_up_junk_transactions',
125 125
         );
126 126
         $crons = (array) get_option('cron');
127
-        if (! is_array($crons)) {
127
+        if ( ! is_array($crons)) {
128 128
             return;
129 129
         }
130 130
         foreach ($crons as $timestamp => $cron) {
131 131
             /** @var array[] $cron */
132 132
             foreach ($ee_crons as $ee_cron) {
133
-                if (isset($cron[ $ee_cron ]) && is_array($cron[ $ee_cron ])) {
133
+                if (isset($cron[$ee_cron]) && is_array($cron[$ee_cron])) {
134 134
                     do_action('AHEE_log', __CLASS__, __FUNCTION__, $ee_cron, 'scheduled EE cron');
135
-                    foreach ($cron[ $ee_cron ] as $ee_cron_details) {
136
-                        if (! empty($ee_cron_details['args'])) {
135
+                    foreach ($cron[$ee_cron] as $ee_cron_details) {
136
+                        if ( ! empty($ee_cron_details['args'])) {
137 137
                             do_action(
138 138
                                 'AHEE_log',
139 139
                                 __CLASS__,
@@ -160,7 +160,7 @@  discard block
 block discarded – undo
160 160
      */
161 161
     public static function reschedule_cron_for_transactions_if_maintenance_mode($cron_task, array $TXN_IDs)
162 162
     {
163
-        if (! method_exists('EE_Cron_Tasks', $cron_task)) {
163
+        if ( ! method_exists('EE_Cron_Tasks', $cron_task)) {
164 164
             throw new DomainException(
165 165
                 sprintf(
166 166
                     esc_html__('"%1$s" is not valid method on EE_Cron_Tasks.', 'event_espresso'),
@@ -169,7 +169,7 @@  discard block
 block discarded – undo
169 169
             );
170 170
         }
171 171
         // reschedule the cron if we can't hit the db right now
172
-        if (! EE_Maintenance_Mode::instance()->models_can_query()) {
172
+        if ( ! EE_Maintenance_Mode::instance()->models_can_query()) {
173 173
             foreach ($TXN_IDs as $TXN_ID => $additional_vars) {
174 174
                 // ensure $additional_vars is an array
175 175
                 $additional_vars = is_array($additional_vars) ? $additional_vars : array($additional_vars);
@@ -250,7 +250,7 @@  discard block
 block discarded – undo
250 250
     {
251 251
         do_action('AHEE_log', __CLASS__, __FUNCTION__, $TXN_ID, '$TXN_ID');
252 252
         if (absint($TXN_ID)) {
253
-            self::$_update_transactions_with_payment[ $TXN_ID ] = $PAY_ID;
253
+            self::$_update_transactions_with_payment[$TXN_ID] = $PAY_ID;
254 254
             add_action(
255 255
                 'shutdown',
256 256
                 array('EE_Cron_Tasks', 'update_transaction_with_payment'),
@@ -297,7 +297,7 @@  discard block
 block discarded – undo
297 297
         EE_Registry::instance()->load_model('Transaction');
298 298
         foreach (self::$_update_transactions_with_payment as $TXN_ID => $PAY_ID) {
299 299
             // reschedule the cron if we can't hit the db right now
300
-            if (! EE_Maintenance_Mode::instance()->models_can_query()) {
300
+            if ( ! EE_Maintenance_Mode::instance()->models_can_query()) {
301 301
                 // reset cron job for updating the TXN
302 302
                 EE_Cron_Tasks::schedule_update_transaction_with_payment(
303 303
                     time() + EE_Cron_Tasks::reschedule_timeout,
@@ -313,7 +313,7 @@  discard block
 block discarded – undo
313 313
                 // now try to update the TXN with any payments
314 314
                 $payment_processor->update_txn_based_on_payment($transaction, $payment, true, true);
315 315
             }
316
-            unset(self::$_update_transactions_with_payment[ $TXN_ID ]);
316
+            unset(self::$_update_transactions_with_payment[$TXN_ID]);
317 317
         }
318 318
     }
319 319
 
@@ -374,7 +374,7 @@  discard block
 block discarded – undo
374 374
     public static function expired_transaction_check($TXN_ID = 0)
375 375
     {
376 376
         if (absint($TXN_ID)) {
377
-            self::$_expired_transactions[ $TXN_ID ] = $TXN_ID;
377
+            self::$_expired_transactions[$TXN_ID] = $TXN_ID;
378 378
             add_action(
379 379
                 'shutdown',
380 380
                 array('EE_Cron_Tasks', 'process_expired_transactions'),
@@ -502,7 +502,7 @@  discard block
 block discarded – undo
502 502
                         break;
503 503
                 }
504 504
             }
505
-            unset(self::$_expired_transactions[ $TXN_ID ]);
505
+            unset(self::$_expired_transactions[$TXN_ID]);
506 506
         }
507 507
     }
508 508
 
@@ -549,7 +549,7 @@  discard block
 block discarded – undo
549 549
             $reg_config = LoaderFactory::getLoader()->load('EE_Registration_Config');
550 550
             $time_diff_for_comparison = apply_filters(
551 551
                 'FHEE__EE_Cron_Tasks__clean_out_old_gateway_logs__time_diff_for_comparison',
552
-                '-' . $reg_config->gateway_log_lifespan
552
+                '-'.$reg_config->gateway_log_lifespan
553 553
             );
554 554
             EEM_Change_Log::instance()->delete_gateway_logs_older_than(new DateTime($time_diff_for_comparison));
555 555
         }
Please login to merge, or discard this patch.
Indentation   +600 added lines, -600 removed lines patch added patch discarded remove patch
@@ -14,607 +14,607 @@
 block discarded – undo
14 14
  */
15 15
 class EE_Cron_Tasks extends EE_Base
16 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 InvalidInterfaceException
36
-     * @throws 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 InvalidDataTypeException
50
-     * @throws 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
-                    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
-                    esc_html__('"%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 (
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 InvalidInterfaceException
36
+	 * @throws 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 InvalidDataTypeException
50
+	 * @throws 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
+					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
+					esc_html__('"%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 (
281 281
 // are there any TXNs that need cleaning up ?
282
-            empty(self::$_update_transactions_with_payment)
283
-            // reschedule the cron if we can't hit the db right now
284
-            || EE_Cron_Tasks::reschedule_cron_for_transactions_if_maintenance_mode(
285
-                'schedule_update_transaction_with_payment',
286
-                self::$_update_transactions_with_payment
287
-            )
288
-        ) {
289
-            return;
290
-        }
291
-        /** @type EE_Payment_Processor $payment_processor */
292
-        $payment_processor = EE_Registry::instance()->load_core('Payment_Processor');
293
-        // set revisit flag for payment processor
294
-        $payment_processor->set_revisit();
295
-        // load EEM_Transaction
296
-        EE_Registry::instance()->load_model('Transaction');
297
-        foreach (self::$_update_transactions_with_payment as $TXN_ID => $PAY_ID) {
298
-            // reschedule the cron if we can't hit the db right now
299
-            if (! EE_Maintenance_Mode::instance()->models_can_query()) {
300
-                // reset cron job for updating the TXN
301
-                EE_Cron_Tasks::schedule_update_transaction_with_payment(
302
-                    time() + EE_Cron_Tasks::reschedule_timeout,
303
-                    $TXN_ID,
304
-                    $PAY_ID
305
-                );
306
-                continue;
307
-            }
308
-            $transaction = EEM_Transaction::instance()->get_one_by_ID($TXN_ID);
309
-            $payment = EEM_Payment::instance()->get_one_by_ID($PAY_ID);
310
-            // verify transaction
311
-            if ($transaction instanceof EE_Transaction && $payment instanceof EE_Payment) {
312
-                // now try to update the TXN with any payments
313
-                $payment_processor->update_txn_based_on_payment($transaction, $payment, true, true);
314
-            }
315
-            unset(self::$_update_transactions_with_payment[ $TXN_ID ]);
316
-        }
317
-    }
318
-
319
-
320
-
321
-    /************  END OF UPDATE TRANSACTION WITH PAYMENT  ************/
322
-
323
-
324
-    /*****************  EXPIRED TRANSACTION CHECK *****************/
325
-
326
-
327
-    /**
328
-     * array of TXN IDs
329
-     *
330
-     * @var array
331
-     */
332
-    protected static $_expired_transactions = array();
333
-
334
-
335
-    /**
336
-     * schedule_expired_transaction_check
337
-     * sets a wp_schedule_single_event() for following up on TXNs after their session has expired
338
-     *
339
-     * @param int $timestamp
340
-     * @param int $TXN_ID
341
-     */
342
-    public static function schedule_expired_transaction_check(
343
-        $timestamp,
344
-        $TXN_ID
345
-    ) {
346
-        // validate $TXN_ID and $timestamp
347
-        $TXN_ID = absint($TXN_ID);
348
-        $timestamp = absint($timestamp);
349
-        if ($TXN_ID && $timestamp) {
350
-            wp_schedule_single_event(
351
-                $timestamp,
352
-                'AHEE__EE_Cron_Tasks__expired_transaction_check',
353
-                array($TXN_ID)
354
-            );
355
-        }
356
-    }
357
-
358
-
359
-    /**
360
-     * expired_transaction_check
361
-     * this is the callback for the action hook:
362
-     * 'AHEE__EE_Cron_Tasks__transaction_session_expiration_check'
363
-     * which is utilized by wp_schedule_single_event()
364
-     * in \EED_Single_Page_Checkout::_initialize_transaction().
365
-     * The passed TXN_ID gets added to an array, and then the
366
-     * process_expired_transactions() function is hooked into
367
-     * 'AHEE__EE_System__core_loaded_and_ready' which will actually handle the
368
-     * processing of any failed transactions, because doing so now would be
369
-     * too early and the required resources may not be available
370
-     *
371
-     * @param int $TXN_ID
372
-     */
373
-    public static function expired_transaction_check($TXN_ID = 0)
374
-    {
375
-        if (absint($TXN_ID)) {
376
-            self::$_expired_transactions[ $TXN_ID ] = $TXN_ID;
377
-            add_action(
378
-                'shutdown',
379
-                array('EE_Cron_Tasks', 'process_expired_transactions'),
380
-                5
381
-            );
382
-        }
383
-    }
384
-
385
-
386
-    /**
387
-     * process_expired_transactions
388
-     * loops through the self::$_expired_transactions array and processes any failed TXNs
389
-     *
390
-     * @throws EE_Error
391
-     * @throws InvalidDataTypeException
392
-     * @throws InvalidInterfaceException
393
-     * @throws InvalidArgumentException
394
-     * @throws ReflectionException
395
-     * @throws DomainException
396
-     * @throws RuntimeException
397
-     */
398
-    public static function process_expired_transactions()
399
-    {
400
-        if (
282
+			empty(self::$_update_transactions_with_payment)
283
+			// reschedule the cron if we can't hit the db right now
284
+			|| EE_Cron_Tasks::reschedule_cron_for_transactions_if_maintenance_mode(
285
+				'schedule_update_transaction_with_payment',
286
+				self::$_update_transactions_with_payment
287
+			)
288
+		) {
289
+			return;
290
+		}
291
+		/** @type EE_Payment_Processor $payment_processor */
292
+		$payment_processor = EE_Registry::instance()->load_core('Payment_Processor');
293
+		// set revisit flag for payment processor
294
+		$payment_processor->set_revisit();
295
+		// load EEM_Transaction
296
+		EE_Registry::instance()->load_model('Transaction');
297
+		foreach (self::$_update_transactions_with_payment as $TXN_ID => $PAY_ID) {
298
+			// reschedule the cron if we can't hit the db right now
299
+			if (! EE_Maintenance_Mode::instance()->models_can_query()) {
300
+				// reset cron job for updating the TXN
301
+				EE_Cron_Tasks::schedule_update_transaction_with_payment(
302
+					time() + EE_Cron_Tasks::reschedule_timeout,
303
+					$TXN_ID,
304
+					$PAY_ID
305
+				);
306
+				continue;
307
+			}
308
+			$transaction = EEM_Transaction::instance()->get_one_by_ID($TXN_ID);
309
+			$payment = EEM_Payment::instance()->get_one_by_ID($PAY_ID);
310
+			// verify transaction
311
+			if ($transaction instanceof EE_Transaction && $payment instanceof EE_Payment) {
312
+				// now try to update the TXN with any payments
313
+				$payment_processor->update_txn_based_on_payment($transaction, $payment, true, true);
314
+			}
315
+			unset(self::$_update_transactions_with_payment[ $TXN_ID ]);
316
+		}
317
+	}
318
+
319
+
320
+
321
+	/************  END OF UPDATE TRANSACTION WITH PAYMENT  ************/
322
+
323
+
324
+	/*****************  EXPIRED TRANSACTION CHECK *****************/
325
+
326
+
327
+	/**
328
+	 * array of TXN IDs
329
+	 *
330
+	 * @var array
331
+	 */
332
+	protected static $_expired_transactions = array();
333
+
334
+
335
+	/**
336
+	 * schedule_expired_transaction_check
337
+	 * sets a wp_schedule_single_event() for following up on TXNs after their session has expired
338
+	 *
339
+	 * @param int $timestamp
340
+	 * @param int $TXN_ID
341
+	 */
342
+	public static function schedule_expired_transaction_check(
343
+		$timestamp,
344
+		$TXN_ID
345
+	) {
346
+		// validate $TXN_ID and $timestamp
347
+		$TXN_ID = absint($TXN_ID);
348
+		$timestamp = absint($timestamp);
349
+		if ($TXN_ID && $timestamp) {
350
+			wp_schedule_single_event(
351
+				$timestamp,
352
+				'AHEE__EE_Cron_Tasks__expired_transaction_check',
353
+				array($TXN_ID)
354
+			);
355
+		}
356
+	}
357
+
358
+
359
+	/**
360
+	 * expired_transaction_check
361
+	 * this is the callback for the action hook:
362
+	 * 'AHEE__EE_Cron_Tasks__transaction_session_expiration_check'
363
+	 * which is utilized by wp_schedule_single_event()
364
+	 * in \EED_Single_Page_Checkout::_initialize_transaction().
365
+	 * The passed TXN_ID gets added to an array, and then the
366
+	 * process_expired_transactions() function is hooked into
367
+	 * 'AHEE__EE_System__core_loaded_and_ready' which will actually handle the
368
+	 * processing of any failed transactions, because doing so now would be
369
+	 * too early and the required resources may not be available
370
+	 *
371
+	 * @param int $TXN_ID
372
+	 */
373
+	public static function expired_transaction_check($TXN_ID = 0)
374
+	{
375
+		if (absint($TXN_ID)) {
376
+			self::$_expired_transactions[ $TXN_ID ] = $TXN_ID;
377
+			add_action(
378
+				'shutdown',
379
+				array('EE_Cron_Tasks', 'process_expired_transactions'),
380
+				5
381
+			);
382
+		}
383
+	}
384
+
385
+
386
+	/**
387
+	 * process_expired_transactions
388
+	 * loops through the self::$_expired_transactions array and processes any failed TXNs
389
+	 *
390
+	 * @throws EE_Error
391
+	 * @throws InvalidDataTypeException
392
+	 * @throws InvalidInterfaceException
393
+	 * @throws InvalidArgumentException
394
+	 * @throws ReflectionException
395
+	 * @throws DomainException
396
+	 * @throws RuntimeException
397
+	 */
398
+	public static function process_expired_transactions()
399
+	{
400
+		if (
401 401
 // are there any TXNs that need cleaning up ?
402
-            empty(self::$_expired_transactions)
403
-            // reschedule the cron if we can't hit the db right now
404
-            || EE_Cron_Tasks::reschedule_cron_for_transactions_if_maintenance_mode(
405
-                'schedule_expired_transaction_check',
406
-                self::$_expired_transactions
407
-            )
408
-        ) {
409
-            return;
410
-        }
411
-        /** @type EE_Transaction_Processor $transaction_processor */
412
-        $transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
413
-        // set revisit flag for txn processor
414
-        $transaction_processor->set_revisit();
415
-        // load EEM_Transaction
416
-        EE_Registry::instance()->load_model('Transaction');
417
-        foreach (self::$_expired_transactions as $TXN_ID) {
418
-            $transaction = EEM_Transaction::instance()->get_one_by_ID($TXN_ID);
419
-            // verify transaction and whether it is failed or not
420
-            if ($transaction instanceof EE_Transaction) {
421
-                switch ($transaction->status_ID()) {
422
-                    // Completed TXNs
423
-                    case EEM_Transaction::complete_status_code:
424
-                        // Don't update the transaction/registrations if the Primary Registration is Not Approved.
425
-                        $primary_registration = $transaction->primary_registration();
426
-                        if (
427
-                            $primary_registration instanceof EE_Registration
428
-                            && $primary_registration->status_ID() !== EEM_Registration::status_id_not_approved
429
-                        ) {
430
-                            /** @type EE_Transaction_Processor $transaction_processor */
431
-                            $transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
432
-                            $transaction_processor->update_transaction_and_registrations_after_checkout_or_payment(
433
-                                $transaction,
434
-                                $transaction->last_payment()
435
-                            );
436
-                            do_action(
437
-                                'AHEE__EE_Cron_Tasks__process_expired_transactions__completed_transaction',
438
-                                $transaction
439
-                            );
440
-                        }
441
-                        break;
442
-                    // Overpaid TXNs
443
-                    case EEM_Transaction::overpaid_status_code:
444
-                        do_action(
445
-                            'AHEE__EE_Cron_Tasks__process_expired_transactions__overpaid_transaction',
446
-                            $transaction
447
-                        );
448
-                        break;
449
-                    // Incomplete TXNs
450
-                    case EEM_Transaction::incomplete_status_code:
451
-                        do_action(
452
-                            'AHEE__EE_Cron_Tasks__process_expired_transactions__incomplete_transaction',
453
-                            $transaction
454
-                        );
455
-                        // todo : move business logic into EE_Transaction_Processor for finalizing abandoned transactions
456
-                        break;
457
-                    // Abandoned TXNs
458
-                    case EEM_Transaction::abandoned_status_code:
459
-                        // run hook before updating transaction, primarily so
460
-                        // EED_Ticket_Sales_Monitor::process_abandoned_transactions() can release reserved tickets
461
-                        do_action(
462
-                            'AHEE__EE_Cron_Tasks__process_expired_transactions__abandoned_transaction',
463
-                            $transaction
464
-                        );
465
-                        // don't finalize the TXN if it has already been completed
466
-                        if ($transaction->all_reg_steps_completed() !== true) {
467
-                            /** @type EE_Payment_Processor $payment_processor */
468
-                            $payment_processor = EE_Registry::instance()->load_core('Payment_Processor');
469
-                            // let's simulate an IPN here which will trigger any notifications that need to go out
470
-                            $payment_processor->update_txn_based_on_payment(
471
-                                $transaction,
472
-                                $transaction->last_payment(),
473
-                                true,
474
-                                true
475
-                            );
476
-                        }
477
-                        break;
478
-                    // Failed TXNs
479
-                    case EEM_Transaction::failed_status_code:
480
-                        do_action(
481
-                            'AHEE__EE_Cron_Tasks__process_expired_transactions__failed_transaction',
482
-                            $transaction
483
-                        );
484
-                        // todo :
485
-                        // perform garbage collection here and remove clean_out_junk_transactions()
486
-                        // $registrations = $transaction->registrations();
487
-                        // if (! empty($registrations)) {
488
-                        //     foreach ($registrations as $registration) {
489
-                        //         if ($registration instanceof EE_Registration) {
490
-                        //             $delete_registration = true;
491
-                        //             if ($registration->attendee() instanceof EE_Attendee) {
492
-                        //                 $delete_registration = false;
493
-                        //             }
494
-                        //             if ($delete_registration) {
495
-                        //                 $registration->delete_permanently();
496
-                        //                 $registration->delete_related_permanently();
497
-                        //             }
498
-                        //         }
499
-                        //     }
500
-                        // }
501
-                        break;
502
-                }
503
-            }
504
-            unset(self::$_expired_transactions[ $TXN_ID ]);
505
-        }
506
-    }
507
-
508
-
509
-
510
-    /*************  END OF EXPIRED TRANSACTION CHECK  *************/
511
-
512
-
513
-    /************* START CLEAN UP BOT TRANSACTIONS **********************/
514
-
515
-
516
-    /**
517
-     * callback for 'AHEE__EE_Cron_Tasks__clean_up_junk_transactions'
518
-     * which is setup during activation to run on an hourly cron
519
-     *
520
-     * @throws EE_Error
521
-     * @throws InvalidArgumentException
522
-     * @throws InvalidDataTypeException
523
-     * @throws InvalidInterfaceException
524
-     * @throws DomainException
525
-     */
526
-    public static function clean_out_junk_transactions()
527
-    {
528
-        if (EE_Maintenance_Mode::instance()->models_can_query()) {
529
-            EED_Ticket_Sales_Monitor::reset_reservation_counts();
530
-            EEM_Transaction::instance('')->delete_junk_transactions();
531
-            EEM_Registration::instance('')->delete_registrations_with_no_transaction();
532
-            EEM_Line_Item::instance('')->delete_line_items_with_no_transaction();
533
-        }
534
-    }
535
-
536
-
537
-    /**
538
-     * Deletes old gateway logs. After about a week we usually don't need them for debugging. But folks can filter that.
539
-     *
540
-     * @throws EE_Error
541
-     * @throws InvalidDataTypeException
542
-     * @throws InvalidInterfaceException
543
-     * @throws InvalidArgumentException
544
-     */
545
-    public static function clean_out_old_gateway_logs()
546
-    {
547
-        if (EE_Maintenance_Mode::instance()->models_can_query()) {
548
-            $reg_config = LoaderFactory::getLoader()->load('EE_Registration_Config');
549
-            $time_diff_for_comparison = apply_filters(
550
-                'FHEE__EE_Cron_Tasks__clean_out_old_gateway_logs__time_diff_for_comparison',
551
-                '-' . $reg_config->gateway_log_lifespan
552
-            );
553
-            EEM_Change_Log::instance()->delete_gateway_logs_older_than(new DateTime($time_diff_for_comparison));
554
-        }
555
-    }
556
-
557
-
558
-    /*****************  FINALIZE ABANDONED TRANSACTIONS *****************/
559
-
560
-
561
-    /**
562
-     * @var array
563
-     */
564
-    protected static $_abandoned_transactions = array();
565
-
566
-
567
-    /**
568
-     * @deprecated
569
-     * @param int $timestamp
570
-     * @param int $TXN_ID
571
-     */
572
-    public static function schedule_finalize_abandoned_transactions_check($timestamp, $TXN_ID)
573
-    {
574
-        EE_Cron_Tasks::schedule_expired_transaction_check($timestamp, $TXN_ID);
575
-    }
576
-
577
-
578
-    /**
579
-     * @deprecated
580
-     * @param int $TXN_ID
581
-     */
582
-    public static function check_for_abandoned_transactions($TXN_ID = 0)
583
-    {
584
-        EE_Cron_Tasks::expired_transaction_check($TXN_ID);
585
-    }
586
-
587
-
588
-    /**
589
-     * @deprecated
590
-     * @throws EE_Error
591
-     * @throws DomainException
592
-     * @throws InvalidDataTypeException
593
-     * @throws InvalidInterfaceException
594
-     * @throws InvalidArgumentException
595
-     * @throws ReflectionException
596
-     * @throws RuntimeException
597
-     */
598
-    public static function finalize_abandoned_transactions()
599
-    {
600
-        do_action('AHEE_log', __CLASS__, __FUNCTION__);
601
-        if (
402
+			empty(self::$_expired_transactions)
403
+			// reschedule the cron if we can't hit the db right now
404
+			|| EE_Cron_Tasks::reschedule_cron_for_transactions_if_maintenance_mode(
405
+				'schedule_expired_transaction_check',
406
+				self::$_expired_transactions
407
+			)
408
+		) {
409
+			return;
410
+		}
411
+		/** @type EE_Transaction_Processor $transaction_processor */
412
+		$transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
413
+		// set revisit flag for txn processor
414
+		$transaction_processor->set_revisit();
415
+		// load EEM_Transaction
416
+		EE_Registry::instance()->load_model('Transaction');
417
+		foreach (self::$_expired_transactions as $TXN_ID) {
418
+			$transaction = EEM_Transaction::instance()->get_one_by_ID($TXN_ID);
419
+			// verify transaction and whether it is failed or not
420
+			if ($transaction instanceof EE_Transaction) {
421
+				switch ($transaction->status_ID()) {
422
+					// Completed TXNs
423
+					case EEM_Transaction::complete_status_code:
424
+						// Don't update the transaction/registrations if the Primary Registration is Not Approved.
425
+						$primary_registration = $transaction->primary_registration();
426
+						if (
427
+							$primary_registration instanceof EE_Registration
428
+							&& $primary_registration->status_ID() !== EEM_Registration::status_id_not_approved
429
+						) {
430
+							/** @type EE_Transaction_Processor $transaction_processor */
431
+							$transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
432
+							$transaction_processor->update_transaction_and_registrations_after_checkout_or_payment(
433
+								$transaction,
434
+								$transaction->last_payment()
435
+							);
436
+							do_action(
437
+								'AHEE__EE_Cron_Tasks__process_expired_transactions__completed_transaction',
438
+								$transaction
439
+							);
440
+						}
441
+						break;
442
+					// Overpaid TXNs
443
+					case EEM_Transaction::overpaid_status_code:
444
+						do_action(
445
+							'AHEE__EE_Cron_Tasks__process_expired_transactions__overpaid_transaction',
446
+							$transaction
447
+						);
448
+						break;
449
+					// Incomplete TXNs
450
+					case EEM_Transaction::incomplete_status_code:
451
+						do_action(
452
+							'AHEE__EE_Cron_Tasks__process_expired_transactions__incomplete_transaction',
453
+							$transaction
454
+						);
455
+						// todo : move business logic into EE_Transaction_Processor for finalizing abandoned transactions
456
+						break;
457
+					// Abandoned TXNs
458
+					case EEM_Transaction::abandoned_status_code:
459
+						// run hook before updating transaction, primarily so
460
+						// EED_Ticket_Sales_Monitor::process_abandoned_transactions() can release reserved tickets
461
+						do_action(
462
+							'AHEE__EE_Cron_Tasks__process_expired_transactions__abandoned_transaction',
463
+							$transaction
464
+						);
465
+						// don't finalize the TXN if it has already been completed
466
+						if ($transaction->all_reg_steps_completed() !== true) {
467
+							/** @type EE_Payment_Processor $payment_processor */
468
+							$payment_processor = EE_Registry::instance()->load_core('Payment_Processor');
469
+							// let's simulate an IPN here which will trigger any notifications that need to go out
470
+							$payment_processor->update_txn_based_on_payment(
471
+								$transaction,
472
+								$transaction->last_payment(),
473
+								true,
474
+								true
475
+							);
476
+						}
477
+						break;
478
+					// Failed TXNs
479
+					case EEM_Transaction::failed_status_code:
480
+						do_action(
481
+							'AHEE__EE_Cron_Tasks__process_expired_transactions__failed_transaction',
482
+							$transaction
483
+						);
484
+						// todo :
485
+						// perform garbage collection here and remove clean_out_junk_transactions()
486
+						// $registrations = $transaction->registrations();
487
+						// if (! empty($registrations)) {
488
+						//     foreach ($registrations as $registration) {
489
+						//         if ($registration instanceof EE_Registration) {
490
+						//             $delete_registration = true;
491
+						//             if ($registration->attendee() instanceof EE_Attendee) {
492
+						//                 $delete_registration = false;
493
+						//             }
494
+						//             if ($delete_registration) {
495
+						//                 $registration->delete_permanently();
496
+						//                 $registration->delete_related_permanently();
497
+						//             }
498
+						//         }
499
+						//     }
500
+						// }
501
+						break;
502
+				}
503
+			}
504
+			unset(self::$_expired_transactions[ $TXN_ID ]);
505
+		}
506
+	}
507
+
508
+
509
+
510
+	/*************  END OF EXPIRED TRANSACTION CHECK  *************/
511
+
512
+
513
+	/************* START CLEAN UP BOT TRANSACTIONS **********************/
514
+
515
+
516
+	/**
517
+	 * callback for 'AHEE__EE_Cron_Tasks__clean_up_junk_transactions'
518
+	 * which is setup during activation to run on an hourly cron
519
+	 *
520
+	 * @throws EE_Error
521
+	 * @throws InvalidArgumentException
522
+	 * @throws InvalidDataTypeException
523
+	 * @throws InvalidInterfaceException
524
+	 * @throws DomainException
525
+	 */
526
+	public static function clean_out_junk_transactions()
527
+	{
528
+		if (EE_Maintenance_Mode::instance()->models_can_query()) {
529
+			EED_Ticket_Sales_Monitor::reset_reservation_counts();
530
+			EEM_Transaction::instance('')->delete_junk_transactions();
531
+			EEM_Registration::instance('')->delete_registrations_with_no_transaction();
532
+			EEM_Line_Item::instance('')->delete_line_items_with_no_transaction();
533
+		}
534
+	}
535
+
536
+
537
+	/**
538
+	 * Deletes old gateway logs. After about a week we usually don't need them for debugging. But folks can filter that.
539
+	 *
540
+	 * @throws EE_Error
541
+	 * @throws InvalidDataTypeException
542
+	 * @throws InvalidInterfaceException
543
+	 * @throws InvalidArgumentException
544
+	 */
545
+	public static function clean_out_old_gateway_logs()
546
+	{
547
+		if (EE_Maintenance_Mode::instance()->models_can_query()) {
548
+			$reg_config = LoaderFactory::getLoader()->load('EE_Registration_Config');
549
+			$time_diff_for_comparison = apply_filters(
550
+				'FHEE__EE_Cron_Tasks__clean_out_old_gateway_logs__time_diff_for_comparison',
551
+				'-' . $reg_config->gateway_log_lifespan
552
+			);
553
+			EEM_Change_Log::instance()->delete_gateway_logs_older_than(new DateTime($time_diff_for_comparison));
554
+		}
555
+	}
556
+
557
+
558
+	/*****************  FINALIZE ABANDONED TRANSACTIONS *****************/
559
+
560
+
561
+	/**
562
+	 * @var array
563
+	 */
564
+	protected static $_abandoned_transactions = array();
565
+
566
+
567
+	/**
568
+	 * @deprecated
569
+	 * @param int $timestamp
570
+	 * @param int $TXN_ID
571
+	 */
572
+	public static function schedule_finalize_abandoned_transactions_check($timestamp, $TXN_ID)
573
+	{
574
+		EE_Cron_Tasks::schedule_expired_transaction_check($timestamp, $TXN_ID);
575
+	}
576
+
577
+
578
+	/**
579
+	 * @deprecated
580
+	 * @param int $TXN_ID
581
+	 */
582
+	public static function check_for_abandoned_transactions($TXN_ID = 0)
583
+	{
584
+		EE_Cron_Tasks::expired_transaction_check($TXN_ID);
585
+	}
586
+
587
+
588
+	/**
589
+	 * @deprecated
590
+	 * @throws EE_Error
591
+	 * @throws DomainException
592
+	 * @throws InvalidDataTypeException
593
+	 * @throws InvalidInterfaceException
594
+	 * @throws InvalidArgumentException
595
+	 * @throws ReflectionException
596
+	 * @throws RuntimeException
597
+	 */
598
+	public static function finalize_abandoned_transactions()
599
+	{
600
+		do_action('AHEE_log', __CLASS__, __FUNCTION__);
601
+		if (
602 602
 // are there any TXNs that need cleaning up ?
603
-            empty(self::$_abandoned_transactions)
604
-            // reschedule the cron if we can't hit the db right now
605
-            || EE_Cron_Tasks::reschedule_cron_for_transactions_if_maintenance_mode(
606
-                'schedule_expired_transaction_check',
607
-                self::$_abandoned_transactions
608
-            )
609
-        ) {
610
-            return;
611
-        }
612
-        // combine our arrays of transaction IDs
613
-        self::$_expired_transactions = self::$_abandoned_transactions + self::$_expired_transactions;
614
-        // and deal with abandoned transactions here now...
615
-        EE_Cron_Tasks::process_expired_transactions();
616
-    }
617
-
618
-
619
-    /*************  END OF FINALIZE ABANDONED TRANSACTIONS  *************/
603
+			empty(self::$_abandoned_transactions)
604
+			// reschedule the cron if we can't hit the db right now
605
+			|| EE_Cron_Tasks::reschedule_cron_for_transactions_if_maintenance_mode(
606
+				'schedule_expired_transaction_check',
607
+				self::$_abandoned_transactions
608
+			)
609
+		) {
610
+			return;
611
+		}
612
+		// combine our arrays of transaction IDs
613
+		self::$_expired_transactions = self::$_abandoned_transactions + self::$_expired_transactions;
614
+		// and deal with abandoned transactions here now...
615
+		EE_Cron_Tasks::process_expired_transactions();
616
+	}
617
+
618
+
619
+	/*************  END OF FINALIZE ABANDONED TRANSACTIONS  *************/
620 620
 }
Please login to merge, or discard this patch.