Completed
Branch dev (0911a7)
by
unknown
08:01 queued 05:53
created
core/db_classes/EE_Line_Item.class.php 2 patches
Spacing   +13 added lines, -13 removed lines patch added patch discarded remove patch
@@ -96,7 +96,7 @@  discard block
 block discarded – undo
96 96
     {
97 97
         $this->calculator = LoaderFactory::getShared(LineItemCalculator::class);
98 98
         parent::__construct($fieldValues, $bydb, $timezone);
99
-        if (! $this->get('LIN_code')) {
99
+        if ( ! $this->get('LIN_code')) {
100 100
             $this->set_code($this->generate_code());
101 101
         }
102 102
     }
@@ -163,7 +163,7 @@  discard block
 block discarded – undo
163 163
     public function name()
164 164
     {
165 165
         $name = $this->get('LIN_name');
166
-        if (! $name) {
166
+        if ( ! $name) {
167 167
             $name = ucwords(str_replace('-', ' ', $this->type()));
168 168
         }
169 169
         return $name;
@@ -655,7 +655,7 @@  discard block
 block discarded – undo
655 655
             $query_params += ['order_by' => ['LIN_order' => 'ASC']];
656 656
             return $this->get_model()->get_all($query_params);
657 657
         }
658
-        if (! is_array($this->_children)) {
658
+        if ( ! is_array($this->_children)) {
659 659
             $this->_children = array();
660 660
         }
661 661
         return $this->_children;
@@ -896,7 +896,7 @@  discard block
 block discarded – undo
896 896
             }
897 897
             return $line_item->save();
898 898
         }
899
-        $this->_children[ $line_item->code() ] = $line_item;
899
+        $this->_children[$line_item->code()] = $line_item;
900 900
         if ($line_item->parent() !== $this) {
901 901
             $line_item->set_parent($this);
902 902
         }
@@ -920,7 +920,7 @@  discard block
 block discarded – undo
920 920
     public function set_parent($line_item)
921 921
     {
922 922
         if ($this->ID()) {
923
-            if (! $line_item->ID()) {
923
+            if ( ! $line_item->ID()) {
924 924
                 $line_item->save();
925 925
             }
926 926
             $this->set_parent_ID($line_item->ID());
@@ -952,8 +952,8 @@  discard block
 block discarded – undo
952 952
                 array(array('LIN_parent' => $this->ID(), 'LIN_code' => $code))
953 953
             );
954 954
         }
955
-        return isset($this->_children[ $code ])
956
-            ? $this->_children[ $code ]
955
+        return isset($this->_children[$code])
956
+            ? $this->_children[$code]
957 957
             : null;
958 958
     }
959 959
 
@@ -1013,8 +1013,8 @@  discard block
 block discarded – undo
1013 1013
             }
1014 1014
             return $items_deleted;
1015 1015
         }
1016
-        if (isset($this->_children[ $code ])) {
1017
-            unset($this->_children[ $code ]);
1016
+        if (isset($this->_children[$code])) {
1017
+            unset($this->_children[$code]);
1018 1018
             return 1;
1019 1019
         }
1020 1020
         return 0;
@@ -1055,7 +1055,7 @@  discard block
 block discarded – undo
1055 1055
     public function generate_code()
1056 1056
     {
1057 1057
         // each line item in the cart requires a unique identifier
1058
-        return md5($this->get('OBJ_type') . $this->get('OBJ_ID') . microtime());
1058
+        return md5($this->get('OBJ_type').$this->get('OBJ_ID').microtime());
1059 1059
     }
1060 1060
 
1061 1061
 
@@ -1099,7 +1099,7 @@  discard block
 block discarded – undo
1099 1099
      */
1100 1100
     public function getSubTaxes(): array
1101 1101
     {
1102
-        if (! $this->is_line_item()) {
1102
+        if ( ! $this->is_line_item()) {
1103 1103
             return [];
1104 1104
         }
1105 1105
         return EEH_Line_Item::get_descendants_of_type($this, EEM_Line_Item::type_sub_tax);
@@ -1118,7 +1118,7 @@  discard block
 block discarded – undo
1118 1118
      */
1119 1119
     public function hasSubTaxes(): bool
1120 1120
     {
1121
-        if (! $this->is_line_item()) {
1121
+        if ( ! $this->is_line_item()) {
1122 1122
             return false;
1123 1123
         }
1124 1124
         $sub_taxes = $this->getSubTaxes();
@@ -1450,7 +1450,7 @@  discard block
 block discarded – undo
1450 1450
     public function save_this_and_descendants_to_txn($txn_id = null)
1451 1451
     {
1452 1452
         $count = 0;
1453
-        if (! $txn_id) {
1453
+        if ( ! $txn_id) {
1454 1454
             $txn_id = $this->TXN_ID();
1455 1455
         }
1456 1456
         $this->set_TXN_ID($txn_id);
Please login to merge, or discard this patch.
Indentation   +1610 added lines, -1610 removed lines patch added patch discarded remove patch
@@ -16,1614 +16,1614 @@
 block discarded – undo
16 16
 class EE_Line_Item extends EE_Base_Class implements EEI_Line_Item
17 17
 {
18 18
 
19
-    /**
20
-     * for children line items (currently not a normal relation)
21
-     *
22
-     * @type EE_Line_Item[]
23
-     */
24
-    protected $_children = array();
25
-
26
-    /**
27
-     * for the parent line item
28
-     *
29
-     * @var EE_Line_Item
30
-     */
31
-    protected $_parent;
32
-
33
-    /**
34
-     * @var LineItemCalculator
35
-     */
36
-    protected $calculator;
37
-
38
-
39
-    /**
40
-     * @param array  $props_n_values          incoming values
41
-     * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
42
-     *                                        used.)
43
-     * @param array  $date_formats            incoming date_formats in an array where the first value is the
44
-     *                                        date_format and the second value is the time format
45
-     * @return EE_Line_Item
46
-     * @throws EE_Error
47
-     * @throws InvalidArgumentException
48
-     * @throws InvalidDataTypeException
49
-     * @throws InvalidInterfaceException
50
-     * @throws ReflectionException
51
-     */
52
-    public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
53
-    {
54
-        $has_object = parent::_check_for_object(
55
-            $props_n_values,
56
-            __CLASS__,
57
-            $timezone,
58
-            $date_formats
59
-        );
60
-        return $has_object
61
-            ? $has_object
62
-            : new self($props_n_values, false, $timezone);
63
-    }
64
-
65
-
66
-    /**
67
-     * @param array  $props_n_values  incoming values from the database
68
-     * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
69
-     *                                the website will be used.
70
-     * @return EE_Line_Item
71
-     * @throws EE_Error
72
-     * @throws InvalidArgumentException
73
-     * @throws InvalidDataTypeException
74
-     * @throws InvalidInterfaceException
75
-     * @throws ReflectionException
76
-     */
77
-    public static function new_instance_from_db($props_n_values = array(), $timezone = null)
78
-    {
79
-        return new self($props_n_values, true, $timezone);
80
-    }
81
-
82
-
83
-    /**
84
-     * Adds some defaults if they're not specified
85
-     *
86
-     * @param array  $fieldValues
87
-     * @param bool   $bydb
88
-     * @param string $timezone
89
-     * @throws EE_Error
90
-     * @throws InvalidArgumentException
91
-     * @throws InvalidDataTypeException
92
-     * @throws InvalidInterfaceException
93
-     * @throws ReflectionException
94
-     */
95
-    protected function __construct($fieldValues = array(), $bydb = false, $timezone = '')
96
-    {
97
-        $this->calculator = LoaderFactory::getShared(LineItemCalculator::class);
98
-        parent::__construct($fieldValues, $bydb, $timezone);
99
-        if (! $this->get('LIN_code')) {
100
-            $this->set_code($this->generate_code());
101
-        }
102
-    }
103
-
104
-
105
-    /**
106
-     * Gets ID
107
-     *
108
-     * @return int
109
-     * @throws EE_Error
110
-     * @throws InvalidArgumentException
111
-     * @throws InvalidDataTypeException
112
-     * @throws InvalidInterfaceException
113
-     * @throws ReflectionException
114
-     */
115
-    public function ID()
116
-    {
117
-        return $this->get('LIN_ID');
118
-    }
119
-
120
-
121
-    /**
122
-     * Gets TXN_ID
123
-     *
124
-     * @return int
125
-     * @throws EE_Error
126
-     * @throws InvalidArgumentException
127
-     * @throws InvalidDataTypeException
128
-     * @throws InvalidInterfaceException
129
-     * @throws ReflectionException
130
-     */
131
-    public function TXN_ID()
132
-    {
133
-        return $this->get('TXN_ID');
134
-    }
135
-
136
-
137
-    /**
138
-     * Sets TXN_ID
139
-     *
140
-     * @param int $TXN_ID
141
-     * @throws EE_Error
142
-     * @throws InvalidArgumentException
143
-     * @throws InvalidDataTypeException
144
-     * @throws InvalidInterfaceException
145
-     * @throws ReflectionException
146
-     */
147
-    public function set_TXN_ID($TXN_ID)
148
-    {
149
-        $this->set('TXN_ID', $TXN_ID);
150
-    }
151
-
152
-
153
-    /**
154
-     * Gets name
155
-     *
156
-     * @return string
157
-     * @throws EE_Error
158
-     * @throws InvalidArgumentException
159
-     * @throws InvalidDataTypeException
160
-     * @throws InvalidInterfaceException
161
-     * @throws ReflectionException
162
-     */
163
-    public function name()
164
-    {
165
-        $name = $this->get('LIN_name');
166
-        if (! $name) {
167
-            $name = ucwords(str_replace('-', ' ', $this->type()));
168
-        }
169
-        return $name;
170
-    }
171
-
172
-
173
-    /**
174
-     * Sets name
175
-     *
176
-     * @param string $name
177
-     * @throws EE_Error
178
-     * @throws InvalidArgumentException
179
-     * @throws InvalidDataTypeException
180
-     * @throws InvalidInterfaceException
181
-     * @throws ReflectionException
182
-     */
183
-    public function set_name($name)
184
-    {
185
-        $this->set('LIN_name', $name);
186
-    }
187
-
188
-
189
-    /**
190
-     * Gets desc
191
-     *
192
-     * @return string
193
-     * @throws EE_Error
194
-     * @throws InvalidArgumentException
195
-     * @throws InvalidDataTypeException
196
-     * @throws InvalidInterfaceException
197
-     * @throws ReflectionException
198
-     */
199
-    public function desc()
200
-    {
201
-        return $this->get('LIN_desc');
202
-    }
203
-
204
-
205
-    /**
206
-     * Sets desc
207
-     *
208
-     * @param string $desc
209
-     * @throws EE_Error
210
-     * @throws InvalidArgumentException
211
-     * @throws InvalidDataTypeException
212
-     * @throws InvalidInterfaceException
213
-     * @throws ReflectionException
214
-     */
215
-    public function set_desc($desc)
216
-    {
217
-        $this->set('LIN_desc', $desc);
218
-    }
219
-
220
-
221
-    /**
222
-     * Gets quantity
223
-     *
224
-     * @return int
225
-     * @throws EE_Error
226
-     * @throws InvalidArgumentException
227
-     * @throws InvalidDataTypeException
228
-     * @throws InvalidInterfaceException
229
-     * @throws ReflectionException
230
-     */
231
-    public function quantity(): int
232
-    {
233
-        return (int) $this->get('LIN_quantity');
234
-    }
235
-
236
-
237
-    /**
238
-     * Sets quantity
239
-     *
240
-     * @param int $quantity
241
-     * @throws EE_Error
242
-     * @throws InvalidArgumentException
243
-     * @throws InvalidDataTypeException
244
-     * @throws InvalidInterfaceException
245
-     * @throws ReflectionException
246
-     */
247
-    public function set_quantity($quantity)
248
-    {
249
-        $this->set('LIN_quantity', max($quantity, 0));
250
-    }
251
-
252
-
253
-    /**
254
-     * Gets item_id
255
-     *
256
-     * @return string
257
-     * @throws EE_Error
258
-     * @throws InvalidArgumentException
259
-     * @throws InvalidDataTypeException
260
-     * @throws InvalidInterfaceException
261
-     * @throws ReflectionException
262
-     */
263
-    public function OBJ_ID()
264
-    {
265
-        return $this->get('OBJ_ID');
266
-    }
267
-
268
-
269
-    /**
270
-     * Sets item_id
271
-     *
272
-     * @param string $item_id
273
-     * @throws EE_Error
274
-     * @throws InvalidArgumentException
275
-     * @throws InvalidDataTypeException
276
-     * @throws InvalidInterfaceException
277
-     * @throws ReflectionException
278
-     */
279
-    public function set_OBJ_ID($item_id)
280
-    {
281
-        $this->set('OBJ_ID', $item_id);
282
-    }
283
-
284
-
285
-    /**
286
-     * Gets item_type
287
-     *
288
-     * @return string
289
-     * @throws EE_Error
290
-     * @throws InvalidArgumentException
291
-     * @throws InvalidDataTypeException
292
-     * @throws InvalidInterfaceException
293
-     * @throws ReflectionException
294
-     */
295
-    public function OBJ_type()
296
-    {
297
-        return $this->get('OBJ_type');
298
-    }
299
-
300
-
301
-    /**
302
-     * Gets item_type
303
-     *
304
-     * @return string
305
-     * @throws EE_Error
306
-     * @throws InvalidArgumentException
307
-     * @throws InvalidDataTypeException
308
-     * @throws InvalidInterfaceException
309
-     * @throws ReflectionException
310
-     */
311
-    public function OBJ_type_i18n()
312
-    {
313
-        $obj_type = $this->OBJ_type();
314
-        switch ($obj_type) {
315
-            case EEM_Line_Item::OBJ_TYPE_EVENT:
316
-                $obj_type = esc_html__('Event', 'event_espresso');
317
-                break;
318
-            case EEM_Line_Item::OBJ_TYPE_PRICE:
319
-                $obj_type = esc_html__('Price', 'event_espresso');
320
-                break;
321
-            case EEM_Line_Item::OBJ_TYPE_PROMOTION:
322
-                $obj_type = esc_html__('Promotion', 'event_espresso');
323
-                break;
324
-            case EEM_Line_Item::OBJ_TYPE_TICKET:
325
-                $obj_type = esc_html__('Ticket', 'event_espresso');
326
-                break;
327
-            case EEM_Line_Item::OBJ_TYPE_TRANSACTION:
328
-                $obj_type = esc_html__('Transaction', 'event_espresso');
329
-                break;
330
-        }
331
-        return apply_filters('FHEE__EE_Line_Item__OBJ_type_i18n', $obj_type, $this);
332
-    }
333
-
334
-
335
-    /**
336
-     * Sets item_type
337
-     *
338
-     * @param string $OBJ_type
339
-     * @throws EE_Error
340
-     * @throws InvalidArgumentException
341
-     * @throws InvalidDataTypeException
342
-     * @throws InvalidInterfaceException
343
-     * @throws ReflectionException
344
-     */
345
-    public function set_OBJ_type($OBJ_type)
346
-    {
347
-        $this->set('OBJ_type', $OBJ_type);
348
-    }
349
-
350
-
351
-    /**
352
-     * Gets unit_price
353
-     *
354
-     * @return float
355
-     * @throws EE_Error
356
-     * @throws InvalidArgumentException
357
-     * @throws InvalidDataTypeException
358
-     * @throws InvalidInterfaceException
359
-     * @throws ReflectionException
360
-     */
361
-    public function unit_price()
362
-    {
363
-        return $this->get('LIN_unit_price');
364
-    }
365
-
366
-
367
-    /**
368
-     * Sets unit_price
369
-     *
370
-     * @param float $unit_price
371
-     * @throws EE_Error
372
-     * @throws InvalidArgumentException
373
-     * @throws InvalidDataTypeException
374
-     * @throws InvalidInterfaceException
375
-     * @throws ReflectionException
376
-     */
377
-    public function set_unit_price($unit_price)
378
-    {
379
-        $this->set('LIN_unit_price', $unit_price);
380
-    }
381
-
382
-
383
-    /**
384
-     * Checks if this item is a percentage modifier or not
385
-     *
386
-     * @return boolean
387
-     * @throws EE_Error
388
-     * @throws InvalidArgumentException
389
-     * @throws InvalidDataTypeException
390
-     * @throws InvalidInterfaceException
391
-     * @throws ReflectionException
392
-     */
393
-    public function is_percent()
394
-    {
395
-        if ($this->is_tax_sub_total()) {
396
-            // tax subtotals HAVE a percent on them, that percentage only applies
397
-            // to taxable items, so its' an exception. Treat it like a flat line item
398
-            return false;
399
-        }
400
-        $unit_price = abs($this->get('LIN_unit_price'));
401
-        $percent = abs($this->get('LIN_percent'));
402
-        if ($unit_price < .001 && $percent) {
403
-            return true;
404
-        }
405
-        if ($unit_price >= .001 && ! $percent) {
406
-            return false;
407
-        }
408
-        if ($unit_price >= .001 && $percent) {
409
-            throw new EE_Error(
410
-                sprintf(
411
-                    esc_html__(
412
-                        'A Line Item can not have a unit price of (%s) AND a percent (%s)!',
413
-                        'event_espresso'
414
-                    ),
415
-                    $unit_price,
416
-                    $percent
417
-                )
418
-            );
419
-        }
420
-        // if they're both 0, assume its not a percent item
421
-        return false;
422
-    }
423
-
424
-
425
-    /**
426
-     * Gets percent (between 100-.001)
427
-     *
428
-     * @return float
429
-     * @throws EE_Error
430
-     * @throws InvalidArgumentException
431
-     * @throws InvalidDataTypeException
432
-     * @throws InvalidInterfaceException
433
-     * @throws ReflectionException
434
-     */
435
-    public function percent()
436
-    {
437
-        return $this->get('LIN_percent');
438
-    }
439
-
440
-
441
-    /**
442
-     * Sets percent (between 100-0.01)
443
-     *
444
-     * @param float $percent
445
-     * @throws EE_Error
446
-     * @throws InvalidArgumentException
447
-     * @throws InvalidDataTypeException
448
-     * @throws InvalidInterfaceException
449
-     * @throws ReflectionException
450
-     */
451
-    public function set_percent($percent)
452
-    {
453
-        $this->set('LIN_percent', $percent);
454
-    }
455
-
456
-
457
-    /**
458
-     * Gets total
459
-     *
460
-     * @return float
461
-     * @throws EE_Error
462
-     * @throws InvalidArgumentException
463
-     * @throws InvalidDataTypeException
464
-     * @throws InvalidInterfaceException
465
-     * @throws ReflectionException
466
-     */
467
-    public function pretaxTotal(): float
468
-    {
469
-        return $this->get('LIN_pretax');
470
-    }
471
-
472
-
473
-    /**
474
-     * Sets total
475
-     *
476
-     * @param float $pretax_total
477
-     * @throws EE_Error
478
-     * @throws InvalidArgumentException
479
-     * @throws InvalidDataTypeException
480
-     * @throws InvalidInterfaceException
481
-     * @throws ReflectionException
482
-     */
483
-    public function setPretaxTotal(float $pretax_total)
484
-    {
485
-        $this->set('LIN_pretax', $pretax_total);
486
-    }
487
-
488
-
489
-    /**
490
-     * @return float
491
-     * @throws EE_Error
492
-     * @throws ReflectionException
493
-     * @since  $VID:$
494
-     */
495
-    public function totalWithTax(): float
496
-    {
497
-        return $this->get('LIN_total');
498
-    }
499
-
500
-
501
-    /**
502
-     * Sets total
503
-     *
504
-     * @param float $total
505
-     * @throws EE_Error
506
-     * @throws ReflectionException
507
-     * @since  $VID:$
508
-     */
509
-    public function setTotalWithTax(float $total)
510
-    {
511
-        $this->set('LIN_total', $total);
512
-    }
513
-
514
-
515
-    /**
516
-     * Gets total
517
-     *
518
-     * @return float
519
-     * @throws EE_Error
520
-     * @throws ReflectionException
521
-     * @deprecatd $VID:$
522
-     */
523
-    public function total(): float
524
-    {
525
-        return $this->totalWithTax();
526
-    }
527
-
528
-
529
-    /**
530
-     * Sets total
531
-     *
532
-     * @param float $total
533
-     * @throws EE_Error
534
-     * @throws ReflectionException
535
-     * @deprecatd $VID:$
536
-     */
537
-    public function set_total($total)
538
-    {
539
-        $this->setTotalWithTax($total);
540
-    }
541
-
542
-
543
-    /**
544
-     * Gets order
545
-     *
546
-     * @return int
547
-     * @throws EE_Error
548
-     * @throws InvalidArgumentException
549
-     * @throws InvalidDataTypeException
550
-     * @throws InvalidInterfaceException
551
-     * @throws ReflectionException
552
-     */
553
-    public function order()
554
-    {
555
-        return $this->get('LIN_order');
556
-    }
557
-
558
-
559
-    /**
560
-     * Sets order
561
-     *
562
-     * @param int $order
563
-     * @throws EE_Error
564
-     * @throws InvalidArgumentException
565
-     * @throws InvalidDataTypeException
566
-     * @throws InvalidInterfaceException
567
-     * @throws ReflectionException
568
-     */
569
-    public function set_order($order)
570
-    {
571
-        $this->set('LIN_order', $order);
572
-    }
573
-
574
-
575
-    /**
576
-     * Gets parent
577
-     *
578
-     * @return int
579
-     * @throws EE_Error
580
-     * @throws InvalidArgumentException
581
-     * @throws InvalidDataTypeException
582
-     * @throws InvalidInterfaceException
583
-     * @throws ReflectionException
584
-     */
585
-    public function parent_ID()
586
-    {
587
-        return $this->get('LIN_parent');
588
-    }
589
-
590
-
591
-    /**
592
-     * Sets parent
593
-     *
594
-     * @param int $parent
595
-     * @throws EE_Error
596
-     * @throws InvalidArgumentException
597
-     * @throws InvalidDataTypeException
598
-     * @throws InvalidInterfaceException
599
-     * @throws ReflectionException
600
-     */
601
-    public function set_parent_ID($parent)
602
-    {
603
-        $this->set('LIN_parent', $parent);
604
-    }
605
-
606
-
607
-    /**
608
-     * Gets type
609
-     *
610
-     * @return string
611
-     * @throws EE_Error
612
-     * @throws InvalidArgumentException
613
-     * @throws InvalidDataTypeException
614
-     * @throws InvalidInterfaceException
615
-     * @throws ReflectionException
616
-     */
617
-    public function type()
618
-    {
619
-        return $this->get('LIN_type');
620
-    }
621
-
622
-
623
-    /**
624
-     * Sets type
625
-     *
626
-     * @param string $type
627
-     * @throws EE_Error
628
-     * @throws InvalidArgumentException
629
-     * @throws InvalidDataTypeException
630
-     * @throws InvalidInterfaceException
631
-     * @throws ReflectionException
632
-     */
633
-    public function set_type($type)
634
-    {
635
-        $this->set('LIN_type', $type);
636
-    }
637
-
638
-
639
-    /**
640
-     * Gets the line item of which this item is a composite. Eg, if this is a subtotal, the parent might be a total\
641
-     * If this line item is saved to the DB, fetches the parent from the DB. However, if this line item isn't in the DB
642
-     * it uses its cached reference to its parent line item (which would have been set by `EE_Line_Item::set_parent()`
643
-     * or indirectly by `EE_Line_item::add_child_line_item()`)
644
-     *
645
-     * @return EE_Base_Class|EE_Line_Item
646
-     * @throws EE_Error
647
-     * @throws InvalidArgumentException
648
-     * @throws InvalidDataTypeException
649
-     * @throws InvalidInterfaceException
650
-     * @throws ReflectionException
651
-     */
652
-    public function parent()
653
-    {
654
-        return $this->ID()
655
-            ? $this->get_model()->get_one_by_ID($this->parent_ID())
656
-            : $this->_parent;
657
-    }
658
-
659
-
660
-    /**
661
-     * Gets ALL the children of this line item (ie, all the parts that contribute towards this total).
662
-     *
663
-     * @return EE_Line_Item[]
664
-     * @throws EE_Error
665
-     * @throws InvalidArgumentException
666
-     * @throws InvalidDataTypeException
667
-     * @throws InvalidInterfaceException
668
-     * @throws ReflectionException
669
-     */
670
-    public function children(array $query_params = []): array
671
-    {
672
-        if ($this->ID()) {
673
-            // ensure where params are an array
674
-            $query_params[0] = $query_params[0] ?? [];
675
-            // add defaults for line item parent and orderby
676
-            $query_params[0] += ['LIN_parent' => $this->ID()];
677
-            $query_params += ['order_by' => ['LIN_order' => 'ASC']];
678
-            return $this->get_model()->get_all($query_params);
679
-        }
680
-        if (! is_array($this->_children)) {
681
-            $this->_children = array();
682
-        }
683
-        return $this->_children;
684
-    }
685
-
686
-
687
-    /**
688
-     * Gets code
689
-     *
690
-     * @return string
691
-     * @throws EE_Error
692
-     * @throws InvalidArgumentException
693
-     * @throws InvalidDataTypeException
694
-     * @throws InvalidInterfaceException
695
-     * @throws ReflectionException
696
-     */
697
-    public function code()
698
-    {
699
-        return $this->get('LIN_code');
700
-    }
701
-
702
-
703
-    /**
704
-     * Sets code
705
-     *
706
-     * @param string $code
707
-     * @throws EE_Error
708
-     * @throws InvalidArgumentException
709
-     * @throws InvalidDataTypeException
710
-     * @throws InvalidInterfaceException
711
-     * @throws ReflectionException
712
-     */
713
-    public function set_code($code)
714
-    {
715
-        $this->set('LIN_code', $code);
716
-    }
717
-
718
-
719
-    /**
720
-     * Gets is_taxable
721
-     *
722
-     * @return boolean
723
-     * @throws EE_Error
724
-     * @throws InvalidArgumentException
725
-     * @throws InvalidDataTypeException
726
-     * @throws InvalidInterfaceException
727
-     * @throws ReflectionException
728
-     */
729
-    public function is_taxable()
730
-    {
731
-        return $this->get('LIN_is_taxable');
732
-    }
733
-
734
-
735
-    /**
736
-     * Sets is_taxable
737
-     *
738
-     * @param boolean $is_taxable
739
-     * @throws EE_Error
740
-     * @throws InvalidArgumentException
741
-     * @throws InvalidDataTypeException
742
-     * @throws InvalidInterfaceException
743
-     * @throws ReflectionException
744
-     */
745
-    public function set_is_taxable($is_taxable)
746
-    {
747
-        $this->set('LIN_is_taxable', $is_taxable);
748
-    }
749
-
750
-
751
-    /**
752
-     * Gets the object that this model-joins-to.
753
-     * returns one of the model objects that the field OBJ_ID can point to... see the 'OBJ_ID' field on
754
-     * EEM_Promotion_Object
755
-     *        Eg, if this line item join model object is for a ticket, this will return the EE_Ticket object
756
-     *
757
-     * @return EE_Base_Class | NULL
758
-     * @throws EE_Error
759
-     * @throws InvalidArgumentException
760
-     * @throws InvalidDataTypeException
761
-     * @throws InvalidInterfaceException
762
-     * @throws ReflectionException
763
-     */
764
-    public function get_object()
765
-    {
766
-        $model_name_of_related_obj = $this->OBJ_type();
767
-        return $this->get_model()->has_relation($model_name_of_related_obj)
768
-            ? $this->get_first_related($model_name_of_related_obj)
769
-            : null;
770
-    }
771
-
772
-
773
-    /**
774
-     * Like EE_Line_Item::get_object(), but can only ever actually return an EE_Ticket.
775
-     * (IE, if this line item is for a price or something else, will return NULL)
776
-     *
777
-     * @param array $query_params
778
-     * @return EE_Base_Class|EE_Ticket
779
-     * @throws EE_Error
780
-     * @throws InvalidArgumentException
781
-     * @throws InvalidDataTypeException
782
-     * @throws InvalidInterfaceException
783
-     * @throws ReflectionException
784
-     */
785
-    public function ticket($query_params = array())
786
-    {
787
-        // we're going to assume that when this method is called
788
-        // we always want to receive the attached ticket EVEN if that ticket is archived.
789
-        // This can be overridden via the incoming $query_params argument
790
-        $remove_defaults = array('default_where_conditions' => 'none');
791
-        $query_params = array_merge($remove_defaults, $query_params);
792
-        return $this->get_first_related(EEM_Line_Item::OBJ_TYPE_TICKET, $query_params);
793
-    }
794
-
795
-
796
-    /**
797
-     * Gets the EE_Datetime that's related to the ticket, IF this is for a ticket
798
-     *
799
-     * @return EE_Datetime | NULL
800
-     * @throws EE_Error
801
-     * @throws InvalidArgumentException
802
-     * @throws InvalidDataTypeException
803
-     * @throws InvalidInterfaceException
804
-     * @throws ReflectionException
805
-     */
806
-    public function get_ticket_datetime()
807
-    {
808
-        if ($this->OBJ_type() === EEM_Line_Item::OBJ_TYPE_TICKET) {
809
-            $ticket = $this->ticket();
810
-            if ($ticket instanceof EE_Ticket) {
811
-                $datetime = $ticket->first_datetime();
812
-                if ($datetime instanceof EE_Datetime) {
813
-                    return $datetime;
814
-                }
815
-            }
816
-        }
817
-        return null;
818
-    }
819
-
820
-
821
-    /**
822
-     * Gets the event's name that's related to the ticket, if this is for
823
-     * a ticket
824
-     *
825
-     * @return string
826
-     * @throws EE_Error
827
-     * @throws InvalidArgumentException
828
-     * @throws InvalidDataTypeException
829
-     * @throws InvalidInterfaceException
830
-     * @throws ReflectionException
831
-     */
832
-    public function ticket_event_name()
833
-    {
834
-        $event_name = esc_html__('Unknown', 'event_espresso');
835
-        $event = $this->ticket_event();
836
-        if ($event instanceof EE_Event) {
837
-            $event_name = $event->name();
838
-        }
839
-        return $event_name;
840
-    }
841
-
842
-
843
-    /**
844
-     * Gets the event that's related to the ticket, if this line item represents a ticket.
845
-     *
846
-     * @return EE_Event|null
847
-     * @throws EE_Error
848
-     * @throws InvalidArgumentException
849
-     * @throws InvalidDataTypeException
850
-     * @throws InvalidInterfaceException
851
-     * @throws ReflectionException
852
-     */
853
-    public function ticket_event()
854
-    {
855
-        $event = null;
856
-        $ticket = $this->ticket();
857
-        if ($ticket instanceof EE_Ticket) {
858
-            $datetime = $ticket->first_datetime();
859
-            if ($datetime instanceof EE_Datetime) {
860
-                $event = $datetime->event();
861
-            }
862
-        }
863
-        return $event;
864
-    }
865
-
866
-
867
-    /**
868
-     * Gets the first datetime for this lien item, assuming it's for a ticket
869
-     *
870
-     * @param string $date_format
871
-     * @param string $time_format
872
-     * @return string
873
-     * @throws EE_Error
874
-     * @throws InvalidArgumentException
875
-     * @throws InvalidDataTypeException
876
-     * @throws InvalidInterfaceException
877
-     * @throws ReflectionException
878
-     */
879
-    public function ticket_datetime_start($date_format = '', $time_format = '')
880
-    {
881
-        $first_datetime_string = esc_html__('Unknown', 'event_espresso');
882
-        $datetime = $this->get_ticket_datetime();
883
-        if ($datetime) {
884
-            $first_datetime_string = $datetime->start_date_and_time($date_format, $time_format);
885
-        }
886
-        return $first_datetime_string;
887
-    }
888
-
889
-
890
-    /**
891
-     * Adds the line item as a child to this line item. If there is another child line
892
-     * item with the same LIN_code, it is overwritten by this new one
893
-     *
894
-     * @param EE_Line_Item $line_item
895
-     * @param bool          $set_order
896
-     * @return bool success
897
-     * @throws EE_Error
898
-     * @throws InvalidArgumentException
899
-     * @throws InvalidDataTypeException
900
-     * @throws InvalidInterfaceException
901
-     * @throws ReflectionException
902
-     */
903
-    public function add_child_line_item(EE_Line_Item $line_item, $set_order = true)
904
-    {
905
-        // should we calculate the LIN_order for this line item ?
906
-        if ($set_order || $line_item->order() === null) {
907
-            $line_item->set_order(count($this->children()));
908
-        }
909
-        if ($this->ID()) {
910
-            // check for any duplicate line items (with the same code), if so, this replaces it
911
-            $line_item_with_same_code = $this->get_child_line_item($line_item->code());
912
-            if ($line_item_with_same_code instanceof EE_Line_Item && $line_item_with_same_code !== $line_item) {
913
-                $this->delete_child_line_item($line_item_with_same_code->code());
914
-            }
915
-            $line_item->set_parent_ID($this->ID());
916
-            if ($this->TXN_ID()) {
917
-                $line_item->set_TXN_ID($this->TXN_ID());
918
-            }
919
-            return $line_item->save();
920
-        }
921
-        $this->_children[ $line_item->code() ] = $line_item;
922
-        if ($line_item->parent() !== $this) {
923
-            $line_item->set_parent($this);
924
-        }
925
-        return true;
926
-    }
927
-
928
-
929
-    /**
930
-     * Similar to EE_Base_Class::_add_relation_to, except this isn't a normal relation.
931
-     * If this line item is saved to the DB, this is just a wrapper for set_parent_ID() and save()
932
-     * However, if this line item is NOT saved to the DB, this just caches the parent on
933
-     * the EE_Line_Item::_parent property.
934
-     *
935
-     * @param EE_Line_Item $line_item
936
-     * @throws EE_Error
937
-     * @throws InvalidArgumentException
938
-     * @throws InvalidDataTypeException
939
-     * @throws InvalidInterfaceException
940
-     * @throws ReflectionException
941
-     */
942
-    public function set_parent($line_item)
943
-    {
944
-        if ($this->ID()) {
945
-            if (! $line_item->ID()) {
946
-                $line_item->save();
947
-            }
948
-            $this->set_parent_ID($line_item->ID());
949
-            $this->save();
950
-        } else {
951
-            $this->_parent = $line_item;
952
-            $this->set_parent_ID($line_item->ID());
953
-        }
954
-    }
955
-
956
-
957
-    /**
958
-     * Gets the child line item as specified by its code. Because this returns an object (by reference)
959
-     * you can modify this child line item and the parent (this object) can know about them
960
-     * because it also has a reference to that line item
961
-     *
962
-     * @param string $code
963
-     * @return EE_Base_Class|EE_Line_Item|EE_Soft_Delete_Base_Class|NULL
964
-     * @throws EE_Error
965
-     * @throws InvalidArgumentException
966
-     * @throws InvalidDataTypeException
967
-     * @throws InvalidInterfaceException
968
-     * @throws ReflectionException
969
-     */
970
-    public function get_child_line_item($code)
971
-    {
972
-        if ($this->ID()) {
973
-            return $this->get_model()->get_one(
974
-                array(array('LIN_parent' => $this->ID(), 'LIN_code' => $code))
975
-            );
976
-        }
977
-        return isset($this->_children[ $code ])
978
-            ? $this->_children[ $code ]
979
-            : null;
980
-    }
981
-
982
-
983
-    /**
984
-     * Returns how many items are deleted (or, if this item has not been saved ot the DB yet, just how many it HAD
985
-     * cached on it)
986
-     *
987
-     * @return int
988
-     * @throws EE_Error
989
-     * @throws InvalidArgumentException
990
-     * @throws InvalidDataTypeException
991
-     * @throws InvalidInterfaceException
992
-     * @throws ReflectionException
993
-     */
994
-    public function delete_children_line_items()
995
-    {
996
-        if ($this->ID()) {
997
-            return $this->get_model()->delete(array(array('LIN_parent' => $this->ID())));
998
-        }
999
-        $count = count($this->_children);
1000
-        $this->_children = array();
1001
-        return $count;
1002
-    }
1003
-
1004
-
1005
-    /**
1006
-     * If this line item has been saved to the DB, deletes its child with LIN_code == $code. If this line
1007
-     * HAS NOT been saved to the DB, removes the child line item with index $code.
1008
-     * Also searches through the child's children for a matching line item. However, once a line item has been found
1009
-     * and deleted, stops searching (so if there are line items with duplicate codes, only the first one found will be
1010
-     * deleted)
1011
-     *
1012
-     * @param string $code
1013
-     * @param bool   $stop_search_once_found
1014
-     * @return int count of items deleted (or simply removed from the line item's cache, if not has not been saved to
1015
-     *             the DB yet)
1016
-     * @throws EE_Error
1017
-     * @throws InvalidArgumentException
1018
-     * @throws InvalidDataTypeException
1019
-     * @throws InvalidInterfaceException
1020
-     * @throws ReflectionException
1021
-     */
1022
-    public function delete_child_line_item($code, $stop_search_once_found = true)
1023
-    {
1024
-        if ($this->ID()) {
1025
-            $items_deleted = 0;
1026
-            if ($this->code() === $code) {
1027
-                $items_deleted += EEH_Line_Item::delete_all_child_items($this);
1028
-                $items_deleted += (int) $this->delete();
1029
-                if ($stop_search_once_found) {
1030
-                    return $items_deleted;
1031
-                }
1032
-            }
1033
-            foreach ($this->children() as $child_line_item) {
1034
-                $items_deleted += $child_line_item->delete_child_line_item($code, $stop_search_once_found);
1035
-            }
1036
-            return $items_deleted;
1037
-        }
1038
-        if (isset($this->_children[ $code ])) {
1039
-            unset($this->_children[ $code ]);
1040
-            return 1;
1041
-        }
1042
-        return 0;
1043
-    }
1044
-
1045
-
1046
-    /**
1047
-     * If this line item is in the database, is of the type subtotal, and
1048
-     * has no children, why do we have it? It should be deleted so this function
1049
-     * does that
1050
-     *
1051
-     * @return boolean
1052
-     * @throws EE_Error
1053
-     * @throws InvalidArgumentException
1054
-     * @throws InvalidDataTypeException
1055
-     * @throws InvalidInterfaceException
1056
-     * @throws ReflectionException
1057
-     */
1058
-    public function delete_if_childless_subtotal()
1059
-    {
1060
-        if ($this->ID() && $this->type() === EEM_Line_Item::type_sub_total && ! $this->children()) {
1061
-            return $this->delete();
1062
-        }
1063
-        return false;
1064
-    }
1065
-
1066
-
1067
-    /**
1068
-     * Creates a code and returns a string. doesn't assign the code to this model object
1069
-     *
1070
-     * @return string
1071
-     * @throws EE_Error
1072
-     * @throws InvalidArgumentException
1073
-     * @throws InvalidDataTypeException
1074
-     * @throws InvalidInterfaceException
1075
-     * @throws ReflectionException
1076
-     */
1077
-    public function generate_code()
1078
-    {
1079
-        // each line item in the cart requires a unique identifier
1080
-        return md5($this->get('OBJ_type') . $this->get('OBJ_ID') . microtime());
1081
-    }
1082
-
1083
-
1084
-    /**
1085
-     * @return bool
1086
-     * @throws EE_Error
1087
-     * @throws InvalidArgumentException
1088
-     * @throws InvalidDataTypeException
1089
-     * @throws InvalidInterfaceException
1090
-     * @throws ReflectionException
1091
-     */
1092
-    public function isGlobalTax(): bool
1093
-    {
1094
-        return $this->type() === EEM_Line_Item::type_tax;
1095
-    }
1096
-
1097
-
1098
-    /**
1099
-     * @return bool
1100
-     * @throws EE_Error
1101
-     * @throws InvalidArgumentException
1102
-     * @throws InvalidDataTypeException
1103
-     * @throws InvalidInterfaceException
1104
-     * @throws ReflectionException
1105
-     */
1106
-    public function isSubTax(): bool
1107
-    {
1108
-        return $this->type() === EEM_Line_Item::type_sub_tax;
1109
-    }
1110
-
1111
-
1112
-    /**
1113
-     * returns true if this is a line item with a direct descendent of the type sub-tax
1114
-     *
1115
-     * @return array
1116
-     * @throws EE_Error
1117
-     * @throws InvalidArgumentException
1118
-     * @throws InvalidDataTypeException
1119
-     * @throws InvalidInterfaceException
1120
-     * @throws ReflectionException
1121
-     */
1122
-    public function getSubTaxes(): array
1123
-    {
1124
-        if (! $this->is_line_item()) {
1125
-            return [];
1126
-        }
1127
-        return EEH_Line_Item::get_descendants_of_type($this, EEM_Line_Item::type_sub_tax);
1128
-    }
1129
-
1130
-
1131
-    /**
1132
-     * returns true if this is a line item with a direct descendent of the type sub-tax
1133
-     *
1134
-     * @return bool
1135
-     * @throws EE_Error
1136
-     * @throws InvalidArgumentException
1137
-     * @throws InvalidDataTypeException
1138
-     * @throws InvalidInterfaceException
1139
-     * @throws ReflectionException
1140
-     */
1141
-    public function hasSubTaxes(): bool
1142
-    {
1143
-        if (! $this->is_line_item()) {
1144
-            return false;
1145
-        }
1146
-        $sub_taxes = $this->getSubTaxes();
1147
-        return ! empty($sub_taxes);
1148
-    }
1149
-
1150
-
1151
-    /**
1152
-     * @return bool
1153
-     * @throws EE_Error
1154
-     * @throws ReflectionException
1155
-     * @deprecated   $VID:$
1156
-     */
1157
-    public function is_tax(): bool
1158
-    {
1159
-        return $this->isGlobalTax();
1160
-    }
1161
-
1162
-
1163
-    /**
1164
-     * @return bool
1165
-     * @throws EE_Error
1166
-     * @throws InvalidArgumentException
1167
-     * @throws InvalidDataTypeException
1168
-     * @throws InvalidInterfaceException
1169
-     * @throws ReflectionException
1170
-     */
1171
-    public function is_tax_sub_total()
1172
-    {
1173
-        return $this->type() === EEM_Line_Item::type_tax_sub_total;
1174
-    }
1175
-
1176
-
1177
-    /**
1178
-     * @return bool
1179
-     * @throws EE_Error
1180
-     * @throws InvalidArgumentException
1181
-     * @throws InvalidDataTypeException
1182
-     * @throws InvalidInterfaceException
1183
-     * @throws ReflectionException
1184
-     */
1185
-    public function is_line_item()
1186
-    {
1187
-        return $this->type() === EEM_Line_Item::type_line_item;
1188
-    }
1189
-
1190
-
1191
-    /**
1192
-     * @return bool
1193
-     * @throws EE_Error
1194
-     * @throws InvalidArgumentException
1195
-     * @throws InvalidDataTypeException
1196
-     * @throws InvalidInterfaceException
1197
-     * @throws ReflectionException
1198
-     */
1199
-    public function is_sub_line_item()
1200
-    {
1201
-        return $this->type() === EEM_Line_Item::type_sub_line_item;
1202
-    }
1203
-
1204
-
1205
-    /**
1206
-     * @return bool
1207
-     * @throws EE_Error
1208
-     * @throws InvalidArgumentException
1209
-     * @throws InvalidDataTypeException
1210
-     * @throws InvalidInterfaceException
1211
-     * @throws ReflectionException
1212
-     */
1213
-    public function is_sub_total()
1214
-    {
1215
-        return $this->type() === EEM_Line_Item::type_sub_total;
1216
-    }
1217
-
1218
-
1219
-    /**
1220
-     * Whether or not this line item is a cancellation line item
1221
-     *
1222
-     * @return boolean
1223
-     * @throws EE_Error
1224
-     * @throws InvalidArgumentException
1225
-     * @throws InvalidDataTypeException
1226
-     * @throws InvalidInterfaceException
1227
-     * @throws ReflectionException
1228
-     */
1229
-    public function is_cancellation()
1230
-    {
1231
-        return EEM_Line_Item::type_cancellation === $this->type();
1232
-    }
1233
-
1234
-
1235
-    /**
1236
-     * @return bool
1237
-     * @throws EE_Error
1238
-     * @throws InvalidArgumentException
1239
-     * @throws InvalidDataTypeException
1240
-     * @throws InvalidInterfaceException
1241
-     * @throws ReflectionException
1242
-     */
1243
-    public function is_total()
1244
-    {
1245
-        return $this->type() === EEM_Line_Item::type_total;
1246
-    }
1247
-
1248
-
1249
-    /**
1250
-     * @return bool
1251
-     * @throws EE_Error
1252
-     * @throws InvalidArgumentException
1253
-     * @throws InvalidDataTypeException
1254
-     * @throws InvalidInterfaceException
1255
-     * @throws ReflectionException
1256
-     */
1257
-    public function is_cancelled()
1258
-    {
1259
-        return $this->type() === EEM_Line_Item::type_cancellation;
1260
-    }
1261
-
1262
-
1263
-    /**
1264
-     * @return string like '2, 004.00', formatted according to the localized currency
1265
-     * @throws EE_Error
1266
-     * @throws InvalidArgumentException
1267
-     * @throws InvalidDataTypeException
1268
-     * @throws InvalidInterfaceException
1269
-     * @throws ReflectionException
1270
-     */
1271
-    public function unit_price_no_code()
1272
-    {
1273
-        return $this->get_pretty('LIN_unit_price', 'no_currency_code');
1274
-    }
1275
-
1276
-
1277
-    /**
1278
-     * @return string like '2, 004.00', formatted according to the localized currency
1279
-     * @throws EE_Error
1280
-     * @throws InvalidArgumentException
1281
-     * @throws InvalidDataTypeException
1282
-     * @throws InvalidInterfaceException
1283
-     * @throws ReflectionException
1284
-     */
1285
-    public function total_no_code()
1286
-    {
1287
-        return $this->get_pretty('LIN_total', 'no_currency_code');
1288
-    }
1289
-
1290
-
1291
-    /**
1292
-     * Gets the final total on this item, taking taxes into account.
1293
-     * Has the side-effect of setting the sub-total as it was just calculated.
1294
-     * If this is used on a grand-total line item, also updates the transaction's
1295
-     * TXN_total (provided this line item is allowed to persist, otherwise we don't
1296
-     * want to change a persistable transaction with info from a non-persistent line item)
1297
-     *
1298
-     * @param bool $update_txn_status
1299
-     * @return float
1300
-     * @throws EE_Error
1301
-     * @throws InvalidArgumentException
1302
-     * @throws InvalidDataTypeException
1303
-     * @throws InvalidInterfaceException
1304
-     * @throws ReflectionException
1305
-     * @throws RuntimeException
1306
-     */
1307
-    public function recalculate_total_including_taxes(bool $update_txn_status = false): float
1308
-    {
1309
-        $grand_total_line_item = EEH_Line_Item::find_transaction_grand_total_for_line_item($this);
1310
-        return $this->calculator->recalculateTotalIncludingTaxes($grand_total_line_item, $update_txn_status);
1311
-    }
1312
-
1313
-
1314
-    /**
1315
-     * Recursively goes through all the children and recalculates sub-totals EXCEPT for
1316
-     * tax-sub-totals (they're a an odd beast). Updates the 'total' on each line item according to either its
1317
-     * unit price * quantity or the total of all its children EXCEPT when we're only calculating the taxable total and
1318
-     * when this is called on the grand total
1319
-     *
1320
-     * @return float
1321
-     * @throws EE_Error
1322
-     * @throws InvalidArgumentException
1323
-     * @throws InvalidDataTypeException
1324
-     * @throws InvalidInterfaceException
1325
-     * @throws ReflectionException
1326
-     */
1327
-    public function recalculate_pre_tax_total(): float
1328
-    {
1329
-        $grand_total_line_item = EEH_Line_Item::find_transaction_grand_total_for_line_item($this);
1330
-        [$total] = $this->calculator->recalculateLineItemTotals($grand_total_line_item);
1331
-        return $total;
1332
-    }
1333
-
1334
-
1335
-    /**
1336
-     * Recalculates the total on each individual tax (based on a recalculation of the pre-tax total), sets
1337
-     * the totals on each tax calculated, and returns the final tax total. Re-saves tax line items
1338
-     * and tax sub-total if already in the DB
1339
-     *
1340
-     * @return float
1341
-     * @throws EE_Error
1342
-     * @throws InvalidArgumentException
1343
-     * @throws InvalidDataTypeException
1344
-     * @throws InvalidInterfaceException
1345
-     * @throws ReflectionException
1346
-     */
1347
-    public function recalculate_taxes_and_tax_total(): float
1348
-    {
1349
-        $grand_total_line_item = EEH_Line_Item::find_transaction_grand_total_for_line_item($this);
1350
-        return $this->calculator->recalculateTaxesAndTaxTotal($grand_total_line_item);
1351
-    }
1352
-
1353
-
1354
-    /**
1355
-     * Gets the total tax on this line item. Assumes taxes have already been calculated using
1356
-     * recalculate_taxes_and_total
1357
-     *
1358
-     * @return float
1359
-     * @throws EE_Error
1360
-     * @throws InvalidArgumentException
1361
-     * @throws InvalidDataTypeException
1362
-     * @throws InvalidInterfaceException
1363
-     * @throws ReflectionException
1364
-     */
1365
-    public function get_total_tax()
1366
-    {
1367
-        $grand_total_line_item = EEH_Line_Item::find_transaction_grand_total_for_line_item($this);
1368
-        return $this->calculator->recalculateTaxesAndTaxTotal($grand_total_line_item);
1369
-    }
1370
-
1371
-
1372
-    /**
1373
-     * Gets the total for all the items purchased only
1374
-     *
1375
-     * @return float
1376
-     * @throws EE_Error
1377
-     * @throws InvalidArgumentException
1378
-     * @throws InvalidDataTypeException
1379
-     * @throws InvalidInterfaceException
1380
-     * @throws ReflectionException
1381
-     */
1382
-    public function get_items_total()
1383
-    {
1384
-        // by default, let's make sure we're consistent with the existing line item
1385
-        if ($this->is_total()) {
1386
-            return $this->pretaxTotal();
1387
-        }
1388
-        $total = 0;
1389
-        foreach ($this->get_items() as $item) {
1390
-            if ($item instanceof EE_Line_Item) {
1391
-                $total += $item->pretaxTotal();
1392
-            }
1393
-        }
1394
-        return $total;
1395
-    }
1396
-
1397
-
1398
-    /**
1399
-     * Gets all the descendants (ie, children or children of children etc) that
1400
-     * are of the type 'tax'
1401
-     *
1402
-     * @return EE_Line_Item[]
1403
-     * @throws EE_Error
1404
-     */
1405
-    public function tax_descendants()
1406
-    {
1407
-        return EEH_Line_Item::get_tax_descendants($this);
1408
-    }
1409
-
1410
-
1411
-    /**
1412
-     * Gets all the real items purchased which are children of this item
1413
-     *
1414
-     * @return EE_Line_Item[]
1415
-     * @throws EE_Error
1416
-     */
1417
-    public function get_items()
1418
-    {
1419
-        return EEH_Line_Item::get_line_item_descendants($this);
1420
-    }
1421
-
1422
-
1423
-    /**
1424
-     * Returns the amount taxable among this line item's children (or if it has no children,
1425
-     * how much of it is taxable). Does not recalculate totals or subtotals.
1426
-     * If the taxable total is negative, (eg, if none of the tickets were taxable,
1427
-     * but there is a "Taxable" discount), returns 0.
1428
-     *
1429
-     * @return float
1430
-     * @throws EE_Error
1431
-     * @throws InvalidArgumentException
1432
-     * @throws InvalidDataTypeException
1433
-     * @throws InvalidInterfaceException
1434
-     * @throws ReflectionException
1435
-     */
1436
-    public function taxable_total(): float
1437
-    {
1438
-        return $this->calculator->taxableAmountForGlobalTaxes($this);
1439
-    }
1440
-
1441
-
1442
-    /**
1443
-     * Gets the transaction for this line item
1444
-     *
1445
-     * @return EE_Base_Class|EE_Transaction
1446
-     * @throws EE_Error
1447
-     * @throws InvalidArgumentException
1448
-     * @throws InvalidDataTypeException
1449
-     * @throws InvalidInterfaceException
1450
-     * @throws ReflectionException
1451
-     */
1452
-    public function transaction()
1453
-    {
1454
-        return $this->get_first_related(EEM_Line_Item::OBJ_TYPE_TRANSACTION);
1455
-    }
1456
-
1457
-
1458
-    /**
1459
-     * Saves this line item to the DB, and recursively saves its descendants.
1460
-     * Because there currently is no proper parent-child relation on the model,
1461
-     * save_this_and_cached() will NOT save the descendants.
1462
-     * Also sets the transaction on this line item and all its descendants before saving
1463
-     *
1464
-     * @param int $txn_id if none is provided, assumes $this->TXN_ID()
1465
-     * @return int count of items saved
1466
-     * @throws EE_Error
1467
-     * @throws InvalidArgumentException
1468
-     * @throws InvalidDataTypeException
1469
-     * @throws InvalidInterfaceException
1470
-     * @throws ReflectionException
1471
-     */
1472
-    public function save_this_and_descendants_to_txn($txn_id = null)
1473
-    {
1474
-        $count = 0;
1475
-        if (! $txn_id) {
1476
-            $txn_id = $this->TXN_ID();
1477
-        }
1478
-        $this->set_TXN_ID($txn_id);
1479
-        $children = $this->children();
1480
-        $count += $this->save()
1481
-            ? 1
1482
-            : 0;
1483
-        foreach ($children as $child_line_item) {
1484
-            if ($child_line_item instanceof EE_Line_Item) {
1485
-                $child_line_item->set_parent_ID($this->ID());
1486
-                $count += $child_line_item->save_this_and_descendants_to_txn($txn_id);
1487
-            }
1488
-        }
1489
-        return $count;
1490
-    }
1491
-
1492
-
1493
-    /**
1494
-     * Saves this line item to the DB, and recursively saves its descendants.
1495
-     *
1496
-     * @return int count of items saved
1497
-     * @throws EE_Error
1498
-     * @throws InvalidArgumentException
1499
-     * @throws InvalidDataTypeException
1500
-     * @throws InvalidInterfaceException
1501
-     * @throws ReflectionException
1502
-     */
1503
-    public function save_this_and_descendants()
1504
-    {
1505
-        $count = 0;
1506
-        $children = $this->children();
1507
-        $count += $this->save()
1508
-            ? 1
1509
-            : 0;
1510
-        foreach ($children as $child_line_item) {
1511
-            if ($child_line_item instanceof EE_Line_Item) {
1512
-                $child_line_item->set_parent_ID($this->ID());
1513
-                $count += $child_line_item->save_this_and_descendants();
1514
-            }
1515
-        }
1516
-        return $count;
1517
-    }
1518
-
1519
-
1520
-    /**
1521
-     * returns the cancellation line item if this item was cancelled
1522
-     *
1523
-     * @return EE_Line_Item[]
1524
-     * @throws InvalidArgumentException
1525
-     * @throws InvalidInterfaceException
1526
-     * @throws InvalidDataTypeException
1527
-     * @throws ReflectionException
1528
-     * @throws EE_Error
1529
-     */
1530
-    public function get_cancellations()
1531
-    {
1532
-        return EEH_Line_Item::get_descendants_of_type($this, EEM_Line_Item::type_cancellation);
1533
-    }
1534
-
1535
-
1536
-    /**
1537
-     * If this item has an ID, then this saves it again to update the db
1538
-     *
1539
-     * @return int count of items saved
1540
-     * @throws EE_Error
1541
-     * @throws InvalidArgumentException
1542
-     * @throws InvalidDataTypeException
1543
-     * @throws InvalidInterfaceException
1544
-     * @throws ReflectionException
1545
-     */
1546
-    public function maybe_save()
1547
-    {
1548
-        if ($this->ID()) {
1549
-            return $this->save();
1550
-        }
1551
-        return false;
1552
-    }
1553
-
1554
-
1555
-    /**
1556
-     * clears the cached children and parent from the line item
1557
-     *
1558
-     * @return void
1559
-     */
1560
-    public function clear_related_line_item_cache()
1561
-    {
1562
-        $this->_children = array();
1563
-        $this->_parent = null;
1564
-    }
1565
-
1566
-
1567
-    /**
1568
-     * @param bool $raw
1569
-     * @return int
1570
-     * @throws EE_Error
1571
-     * @throws InvalidArgumentException
1572
-     * @throws InvalidDataTypeException
1573
-     * @throws InvalidInterfaceException
1574
-     * @throws ReflectionException
1575
-     */
1576
-    public function timestamp($raw = false)
1577
-    {
1578
-        return $raw
1579
-            ? $this->get_raw('LIN_timestamp')
1580
-            : $this->get('LIN_timestamp');
1581
-    }
1582
-
1583
-
1584
-
1585
-
1586
-    /************************* DEPRECATED *************************/
1587
-    /**
1588
-     * @deprecated 4.6.0
1589
-     * @param string $type one of the constants on EEM_Line_Item
1590
-     * @return EE_Line_Item[]
1591
-     * @throws EE_Error
1592
-     */
1593
-    protected function _get_descendants_of_type($type)
1594
-    {
1595
-        EE_Error::doing_it_wrong(
1596
-            'EE_Line_Item::_get_descendants_of_type()',
1597
-            sprintf(
1598
-                esc_html__('Method replaced with %1$s', 'event_espresso'),
1599
-                'EEH_Line_Item::get_descendants_of_type()'
1600
-            ),
1601
-            '4.6.0'
1602
-        );
1603
-        return EEH_Line_Item::get_descendants_of_type($this, $type);
1604
-    }
1605
-
1606
-
1607
-    /**
1608
-     * @deprecated 4.6.0
1609
-     * @param string $type like one of the EEM_Line_Item::type_*
1610
-     * @return EE_Line_Item
1611
-     * @throws EE_Error
1612
-     * @throws InvalidArgumentException
1613
-     * @throws InvalidDataTypeException
1614
-     * @throws InvalidInterfaceException
1615
-     * @throws ReflectionException
1616
-     */
1617
-    public function get_nearest_descendant_of_type($type)
1618
-    {
1619
-        EE_Error::doing_it_wrong(
1620
-            'EE_Line_Item::get_nearest_descendant_of_type()',
1621
-            sprintf(
1622
-                esc_html__('Method replaced with %1$s', 'event_espresso'),
1623
-                'EEH_Line_Item::get_nearest_descendant_of_type()'
1624
-            ),
1625
-            '4.6.0'
1626
-        );
1627
-        return EEH_Line_Item::get_nearest_descendant_of_type($this, $type);
1628
-    }
19
+	/**
20
+	 * for children line items (currently not a normal relation)
21
+	 *
22
+	 * @type EE_Line_Item[]
23
+	 */
24
+	protected $_children = array();
25
+
26
+	/**
27
+	 * for the parent line item
28
+	 *
29
+	 * @var EE_Line_Item
30
+	 */
31
+	protected $_parent;
32
+
33
+	/**
34
+	 * @var LineItemCalculator
35
+	 */
36
+	protected $calculator;
37
+
38
+
39
+	/**
40
+	 * @param array  $props_n_values          incoming values
41
+	 * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
42
+	 *                                        used.)
43
+	 * @param array  $date_formats            incoming date_formats in an array where the first value is the
44
+	 *                                        date_format and the second value is the time format
45
+	 * @return EE_Line_Item
46
+	 * @throws EE_Error
47
+	 * @throws InvalidArgumentException
48
+	 * @throws InvalidDataTypeException
49
+	 * @throws InvalidInterfaceException
50
+	 * @throws ReflectionException
51
+	 */
52
+	public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
53
+	{
54
+		$has_object = parent::_check_for_object(
55
+			$props_n_values,
56
+			__CLASS__,
57
+			$timezone,
58
+			$date_formats
59
+		);
60
+		return $has_object
61
+			? $has_object
62
+			: new self($props_n_values, false, $timezone);
63
+	}
64
+
65
+
66
+	/**
67
+	 * @param array  $props_n_values  incoming values from the database
68
+	 * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
69
+	 *                                the website will be used.
70
+	 * @return EE_Line_Item
71
+	 * @throws EE_Error
72
+	 * @throws InvalidArgumentException
73
+	 * @throws InvalidDataTypeException
74
+	 * @throws InvalidInterfaceException
75
+	 * @throws ReflectionException
76
+	 */
77
+	public static function new_instance_from_db($props_n_values = array(), $timezone = null)
78
+	{
79
+		return new self($props_n_values, true, $timezone);
80
+	}
81
+
82
+
83
+	/**
84
+	 * Adds some defaults if they're not specified
85
+	 *
86
+	 * @param array  $fieldValues
87
+	 * @param bool   $bydb
88
+	 * @param string $timezone
89
+	 * @throws EE_Error
90
+	 * @throws InvalidArgumentException
91
+	 * @throws InvalidDataTypeException
92
+	 * @throws InvalidInterfaceException
93
+	 * @throws ReflectionException
94
+	 */
95
+	protected function __construct($fieldValues = array(), $bydb = false, $timezone = '')
96
+	{
97
+		$this->calculator = LoaderFactory::getShared(LineItemCalculator::class);
98
+		parent::__construct($fieldValues, $bydb, $timezone);
99
+		if (! $this->get('LIN_code')) {
100
+			$this->set_code($this->generate_code());
101
+		}
102
+	}
103
+
104
+
105
+	/**
106
+	 * Gets ID
107
+	 *
108
+	 * @return int
109
+	 * @throws EE_Error
110
+	 * @throws InvalidArgumentException
111
+	 * @throws InvalidDataTypeException
112
+	 * @throws InvalidInterfaceException
113
+	 * @throws ReflectionException
114
+	 */
115
+	public function ID()
116
+	{
117
+		return $this->get('LIN_ID');
118
+	}
119
+
120
+
121
+	/**
122
+	 * Gets TXN_ID
123
+	 *
124
+	 * @return int
125
+	 * @throws EE_Error
126
+	 * @throws InvalidArgumentException
127
+	 * @throws InvalidDataTypeException
128
+	 * @throws InvalidInterfaceException
129
+	 * @throws ReflectionException
130
+	 */
131
+	public function TXN_ID()
132
+	{
133
+		return $this->get('TXN_ID');
134
+	}
135
+
136
+
137
+	/**
138
+	 * Sets TXN_ID
139
+	 *
140
+	 * @param int $TXN_ID
141
+	 * @throws EE_Error
142
+	 * @throws InvalidArgumentException
143
+	 * @throws InvalidDataTypeException
144
+	 * @throws InvalidInterfaceException
145
+	 * @throws ReflectionException
146
+	 */
147
+	public function set_TXN_ID($TXN_ID)
148
+	{
149
+		$this->set('TXN_ID', $TXN_ID);
150
+	}
151
+
152
+
153
+	/**
154
+	 * Gets name
155
+	 *
156
+	 * @return string
157
+	 * @throws EE_Error
158
+	 * @throws InvalidArgumentException
159
+	 * @throws InvalidDataTypeException
160
+	 * @throws InvalidInterfaceException
161
+	 * @throws ReflectionException
162
+	 */
163
+	public function name()
164
+	{
165
+		$name = $this->get('LIN_name');
166
+		if (! $name) {
167
+			$name = ucwords(str_replace('-', ' ', $this->type()));
168
+		}
169
+		return $name;
170
+	}
171
+
172
+
173
+	/**
174
+	 * Sets name
175
+	 *
176
+	 * @param string $name
177
+	 * @throws EE_Error
178
+	 * @throws InvalidArgumentException
179
+	 * @throws InvalidDataTypeException
180
+	 * @throws InvalidInterfaceException
181
+	 * @throws ReflectionException
182
+	 */
183
+	public function set_name($name)
184
+	{
185
+		$this->set('LIN_name', $name);
186
+	}
187
+
188
+
189
+	/**
190
+	 * Gets desc
191
+	 *
192
+	 * @return string
193
+	 * @throws EE_Error
194
+	 * @throws InvalidArgumentException
195
+	 * @throws InvalidDataTypeException
196
+	 * @throws InvalidInterfaceException
197
+	 * @throws ReflectionException
198
+	 */
199
+	public function desc()
200
+	{
201
+		return $this->get('LIN_desc');
202
+	}
203
+
204
+
205
+	/**
206
+	 * Sets desc
207
+	 *
208
+	 * @param string $desc
209
+	 * @throws EE_Error
210
+	 * @throws InvalidArgumentException
211
+	 * @throws InvalidDataTypeException
212
+	 * @throws InvalidInterfaceException
213
+	 * @throws ReflectionException
214
+	 */
215
+	public function set_desc($desc)
216
+	{
217
+		$this->set('LIN_desc', $desc);
218
+	}
219
+
220
+
221
+	/**
222
+	 * Gets quantity
223
+	 *
224
+	 * @return int
225
+	 * @throws EE_Error
226
+	 * @throws InvalidArgumentException
227
+	 * @throws InvalidDataTypeException
228
+	 * @throws InvalidInterfaceException
229
+	 * @throws ReflectionException
230
+	 */
231
+	public function quantity(): int
232
+	{
233
+		return (int) $this->get('LIN_quantity');
234
+	}
235
+
236
+
237
+	/**
238
+	 * Sets quantity
239
+	 *
240
+	 * @param int $quantity
241
+	 * @throws EE_Error
242
+	 * @throws InvalidArgumentException
243
+	 * @throws InvalidDataTypeException
244
+	 * @throws InvalidInterfaceException
245
+	 * @throws ReflectionException
246
+	 */
247
+	public function set_quantity($quantity)
248
+	{
249
+		$this->set('LIN_quantity', max($quantity, 0));
250
+	}
251
+
252
+
253
+	/**
254
+	 * Gets item_id
255
+	 *
256
+	 * @return string
257
+	 * @throws EE_Error
258
+	 * @throws InvalidArgumentException
259
+	 * @throws InvalidDataTypeException
260
+	 * @throws InvalidInterfaceException
261
+	 * @throws ReflectionException
262
+	 */
263
+	public function OBJ_ID()
264
+	{
265
+		return $this->get('OBJ_ID');
266
+	}
267
+
268
+
269
+	/**
270
+	 * Sets item_id
271
+	 *
272
+	 * @param string $item_id
273
+	 * @throws EE_Error
274
+	 * @throws InvalidArgumentException
275
+	 * @throws InvalidDataTypeException
276
+	 * @throws InvalidInterfaceException
277
+	 * @throws ReflectionException
278
+	 */
279
+	public function set_OBJ_ID($item_id)
280
+	{
281
+		$this->set('OBJ_ID', $item_id);
282
+	}
283
+
284
+
285
+	/**
286
+	 * Gets item_type
287
+	 *
288
+	 * @return string
289
+	 * @throws EE_Error
290
+	 * @throws InvalidArgumentException
291
+	 * @throws InvalidDataTypeException
292
+	 * @throws InvalidInterfaceException
293
+	 * @throws ReflectionException
294
+	 */
295
+	public function OBJ_type()
296
+	{
297
+		return $this->get('OBJ_type');
298
+	}
299
+
300
+
301
+	/**
302
+	 * Gets item_type
303
+	 *
304
+	 * @return string
305
+	 * @throws EE_Error
306
+	 * @throws InvalidArgumentException
307
+	 * @throws InvalidDataTypeException
308
+	 * @throws InvalidInterfaceException
309
+	 * @throws ReflectionException
310
+	 */
311
+	public function OBJ_type_i18n()
312
+	{
313
+		$obj_type = $this->OBJ_type();
314
+		switch ($obj_type) {
315
+			case EEM_Line_Item::OBJ_TYPE_EVENT:
316
+				$obj_type = esc_html__('Event', 'event_espresso');
317
+				break;
318
+			case EEM_Line_Item::OBJ_TYPE_PRICE:
319
+				$obj_type = esc_html__('Price', 'event_espresso');
320
+				break;
321
+			case EEM_Line_Item::OBJ_TYPE_PROMOTION:
322
+				$obj_type = esc_html__('Promotion', 'event_espresso');
323
+				break;
324
+			case EEM_Line_Item::OBJ_TYPE_TICKET:
325
+				$obj_type = esc_html__('Ticket', 'event_espresso');
326
+				break;
327
+			case EEM_Line_Item::OBJ_TYPE_TRANSACTION:
328
+				$obj_type = esc_html__('Transaction', 'event_espresso');
329
+				break;
330
+		}
331
+		return apply_filters('FHEE__EE_Line_Item__OBJ_type_i18n', $obj_type, $this);
332
+	}
333
+
334
+
335
+	/**
336
+	 * Sets item_type
337
+	 *
338
+	 * @param string $OBJ_type
339
+	 * @throws EE_Error
340
+	 * @throws InvalidArgumentException
341
+	 * @throws InvalidDataTypeException
342
+	 * @throws InvalidInterfaceException
343
+	 * @throws ReflectionException
344
+	 */
345
+	public function set_OBJ_type($OBJ_type)
346
+	{
347
+		$this->set('OBJ_type', $OBJ_type);
348
+	}
349
+
350
+
351
+	/**
352
+	 * Gets unit_price
353
+	 *
354
+	 * @return float
355
+	 * @throws EE_Error
356
+	 * @throws InvalidArgumentException
357
+	 * @throws InvalidDataTypeException
358
+	 * @throws InvalidInterfaceException
359
+	 * @throws ReflectionException
360
+	 */
361
+	public function unit_price()
362
+	{
363
+		return $this->get('LIN_unit_price');
364
+	}
365
+
366
+
367
+	/**
368
+	 * Sets unit_price
369
+	 *
370
+	 * @param float $unit_price
371
+	 * @throws EE_Error
372
+	 * @throws InvalidArgumentException
373
+	 * @throws InvalidDataTypeException
374
+	 * @throws InvalidInterfaceException
375
+	 * @throws ReflectionException
376
+	 */
377
+	public function set_unit_price($unit_price)
378
+	{
379
+		$this->set('LIN_unit_price', $unit_price);
380
+	}
381
+
382
+
383
+	/**
384
+	 * Checks if this item is a percentage modifier or not
385
+	 *
386
+	 * @return boolean
387
+	 * @throws EE_Error
388
+	 * @throws InvalidArgumentException
389
+	 * @throws InvalidDataTypeException
390
+	 * @throws InvalidInterfaceException
391
+	 * @throws ReflectionException
392
+	 */
393
+	public function is_percent()
394
+	{
395
+		if ($this->is_tax_sub_total()) {
396
+			// tax subtotals HAVE a percent on them, that percentage only applies
397
+			// to taxable items, so its' an exception. Treat it like a flat line item
398
+			return false;
399
+		}
400
+		$unit_price = abs($this->get('LIN_unit_price'));
401
+		$percent = abs($this->get('LIN_percent'));
402
+		if ($unit_price < .001 && $percent) {
403
+			return true;
404
+		}
405
+		if ($unit_price >= .001 && ! $percent) {
406
+			return false;
407
+		}
408
+		if ($unit_price >= .001 && $percent) {
409
+			throw new EE_Error(
410
+				sprintf(
411
+					esc_html__(
412
+						'A Line Item can not have a unit price of (%s) AND a percent (%s)!',
413
+						'event_espresso'
414
+					),
415
+					$unit_price,
416
+					$percent
417
+				)
418
+			);
419
+		}
420
+		// if they're both 0, assume its not a percent item
421
+		return false;
422
+	}
423
+
424
+
425
+	/**
426
+	 * Gets percent (between 100-.001)
427
+	 *
428
+	 * @return float
429
+	 * @throws EE_Error
430
+	 * @throws InvalidArgumentException
431
+	 * @throws InvalidDataTypeException
432
+	 * @throws InvalidInterfaceException
433
+	 * @throws ReflectionException
434
+	 */
435
+	public function percent()
436
+	{
437
+		return $this->get('LIN_percent');
438
+	}
439
+
440
+
441
+	/**
442
+	 * Sets percent (between 100-0.01)
443
+	 *
444
+	 * @param float $percent
445
+	 * @throws EE_Error
446
+	 * @throws InvalidArgumentException
447
+	 * @throws InvalidDataTypeException
448
+	 * @throws InvalidInterfaceException
449
+	 * @throws ReflectionException
450
+	 */
451
+	public function set_percent($percent)
452
+	{
453
+		$this->set('LIN_percent', $percent);
454
+	}
455
+
456
+
457
+	/**
458
+	 * Gets total
459
+	 *
460
+	 * @return float
461
+	 * @throws EE_Error
462
+	 * @throws InvalidArgumentException
463
+	 * @throws InvalidDataTypeException
464
+	 * @throws InvalidInterfaceException
465
+	 * @throws ReflectionException
466
+	 */
467
+	public function pretaxTotal(): float
468
+	{
469
+		return $this->get('LIN_pretax');
470
+	}
471
+
472
+
473
+	/**
474
+	 * Sets total
475
+	 *
476
+	 * @param float $pretax_total
477
+	 * @throws EE_Error
478
+	 * @throws InvalidArgumentException
479
+	 * @throws InvalidDataTypeException
480
+	 * @throws InvalidInterfaceException
481
+	 * @throws ReflectionException
482
+	 */
483
+	public function setPretaxTotal(float $pretax_total)
484
+	{
485
+		$this->set('LIN_pretax', $pretax_total);
486
+	}
487
+
488
+
489
+	/**
490
+	 * @return float
491
+	 * @throws EE_Error
492
+	 * @throws ReflectionException
493
+	 * @since  $VID:$
494
+	 */
495
+	public function totalWithTax(): float
496
+	{
497
+		return $this->get('LIN_total');
498
+	}
499
+
500
+
501
+	/**
502
+	 * Sets total
503
+	 *
504
+	 * @param float $total
505
+	 * @throws EE_Error
506
+	 * @throws ReflectionException
507
+	 * @since  $VID:$
508
+	 */
509
+	public function setTotalWithTax(float $total)
510
+	{
511
+		$this->set('LIN_total', $total);
512
+	}
513
+
514
+
515
+	/**
516
+	 * Gets total
517
+	 *
518
+	 * @return float
519
+	 * @throws EE_Error
520
+	 * @throws ReflectionException
521
+	 * @deprecatd $VID:$
522
+	 */
523
+	public function total(): float
524
+	{
525
+		return $this->totalWithTax();
526
+	}
527
+
528
+
529
+	/**
530
+	 * Sets total
531
+	 *
532
+	 * @param float $total
533
+	 * @throws EE_Error
534
+	 * @throws ReflectionException
535
+	 * @deprecatd $VID:$
536
+	 */
537
+	public function set_total($total)
538
+	{
539
+		$this->setTotalWithTax($total);
540
+	}
541
+
542
+
543
+	/**
544
+	 * Gets order
545
+	 *
546
+	 * @return int
547
+	 * @throws EE_Error
548
+	 * @throws InvalidArgumentException
549
+	 * @throws InvalidDataTypeException
550
+	 * @throws InvalidInterfaceException
551
+	 * @throws ReflectionException
552
+	 */
553
+	public function order()
554
+	{
555
+		return $this->get('LIN_order');
556
+	}
557
+
558
+
559
+	/**
560
+	 * Sets order
561
+	 *
562
+	 * @param int $order
563
+	 * @throws EE_Error
564
+	 * @throws InvalidArgumentException
565
+	 * @throws InvalidDataTypeException
566
+	 * @throws InvalidInterfaceException
567
+	 * @throws ReflectionException
568
+	 */
569
+	public function set_order($order)
570
+	{
571
+		$this->set('LIN_order', $order);
572
+	}
573
+
574
+
575
+	/**
576
+	 * Gets parent
577
+	 *
578
+	 * @return int
579
+	 * @throws EE_Error
580
+	 * @throws InvalidArgumentException
581
+	 * @throws InvalidDataTypeException
582
+	 * @throws InvalidInterfaceException
583
+	 * @throws ReflectionException
584
+	 */
585
+	public function parent_ID()
586
+	{
587
+		return $this->get('LIN_parent');
588
+	}
589
+
590
+
591
+	/**
592
+	 * Sets parent
593
+	 *
594
+	 * @param int $parent
595
+	 * @throws EE_Error
596
+	 * @throws InvalidArgumentException
597
+	 * @throws InvalidDataTypeException
598
+	 * @throws InvalidInterfaceException
599
+	 * @throws ReflectionException
600
+	 */
601
+	public function set_parent_ID($parent)
602
+	{
603
+		$this->set('LIN_parent', $parent);
604
+	}
605
+
606
+
607
+	/**
608
+	 * Gets type
609
+	 *
610
+	 * @return string
611
+	 * @throws EE_Error
612
+	 * @throws InvalidArgumentException
613
+	 * @throws InvalidDataTypeException
614
+	 * @throws InvalidInterfaceException
615
+	 * @throws ReflectionException
616
+	 */
617
+	public function type()
618
+	{
619
+		return $this->get('LIN_type');
620
+	}
621
+
622
+
623
+	/**
624
+	 * Sets type
625
+	 *
626
+	 * @param string $type
627
+	 * @throws EE_Error
628
+	 * @throws InvalidArgumentException
629
+	 * @throws InvalidDataTypeException
630
+	 * @throws InvalidInterfaceException
631
+	 * @throws ReflectionException
632
+	 */
633
+	public function set_type($type)
634
+	{
635
+		$this->set('LIN_type', $type);
636
+	}
637
+
638
+
639
+	/**
640
+	 * Gets the line item of which this item is a composite. Eg, if this is a subtotal, the parent might be a total\
641
+	 * If this line item is saved to the DB, fetches the parent from the DB. However, if this line item isn't in the DB
642
+	 * it uses its cached reference to its parent line item (which would have been set by `EE_Line_Item::set_parent()`
643
+	 * or indirectly by `EE_Line_item::add_child_line_item()`)
644
+	 *
645
+	 * @return EE_Base_Class|EE_Line_Item
646
+	 * @throws EE_Error
647
+	 * @throws InvalidArgumentException
648
+	 * @throws InvalidDataTypeException
649
+	 * @throws InvalidInterfaceException
650
+	 * @throws ReflectionException
651
+	 */
652
+	public function parent()
653
+	{
654
+		return $this->ID()
655
+			? $this->get_model()->get_one_by_ID($this->parent_ID())
656
+			: $this->_parent;
657
+	}
658
+
659
+
660
+	/**
661
+	 * Gets ALL the children of this line item (ie, all the parts that contribute towards this total).
662
+	 *
663
+	 * @return EE_Line_Item[]
664
+	 * @throws EE_Error
665
+	 * @throws InvalidArgumentException
666
+	 * @throws InvalidDataTypeException
667
+	 * @throws InvalidInterfaceException
668
+	 * @throws ReflectionException
669
+	 */
670
+	public function children(array $query_params = []): array
671
+	{
672
+		if ($this->ID()) {
673
+			// ensure where params are an array
674
+			$query_params[0] = $query_params[0] ?? [];
675
+			// add defaults for line item parent and orderby
676
+			$query_params[0] += ['LIN_parent' => $this->ID()];
677
+			$query_params += ['order_by' => ['LIN_order' => 'ASC']];
678
+			return $this->get_model()->get_all($query_params);
679
+		}
680
+		if (! is_array($this->_children)) {
681
+			$this->_children = array();
682
+		}
683
+		return $this->_children;
684
+	}
685
+
686
+
687
+	/**
688
+	 * Gets code
689
+	 *
690
+	 * @return string
691
+	 * @throws EE_Error
692
+	 * @throws InvalidArgumentException
693
+	 * @throws InvalidDataTypeException
694
+	 * @throws InvalidInterfaceException
695
+	 * @throws ReflectionException
696
+	 */
697
+	public function code()
698
+	{
699
+		return $this->get('LIN_code');
700
+	}
701
+
702
+
703
+	/**
704
+	 * Sets code
705
+	 *
706
+	 * @param string $code
707
+	 * @throws EE_Error
708
+	 * @throws InvalidArgumentException
709
+	 * @throws InvalidDataTypeException
710
+	 * @throws InvalidInterfaceException
711
+	 * @throws ReflectionException
712
+	 */
713
+	public function set_code($code)
714
+	{
715
+		$this->set('LIN_code', $code);
716
+	}
717
+
718
+
719
+	/**
720
+	 * Gets is_taxable
721
+	 *
722
+	 * @return boolean
723
+	 * @throws EE_Error
724
+	 * @throws InvalidArgumentException
725
+	 * @throws InvalidDataTypeException
726
+	 * @throws InvalidInterfaceException
727
+	 * @throws ReflectionException
728
+	 */
729
+	public function is_taxable()
730
+	{
731
+		return $this->get('LIN_is_taxable');
732
+	}
733
+
734
+
735
+	/**
736
+	 * Sets is_taxable
737
+	 *
738
+	 * @param boolean $is_taxable
739
+	 * @throws EE_Error
740
+	 * @throws InvalidArgumentException
741
+	 * @throws InvalidDataTypeException
742
+	 * @throws InvalidInterfaceException
743
+	 * @throws ReflectionException
744
+	 */
745
+	public function set_is_taxable($is_taxable)
746
+	{
747
+		$this->set('LIN_is_taxable', $is_taxable);
748
+	}
749
+
750
+
751
+	/**
752
+	 * Gets the object that this model-joins-to.
753
+	 * returns one of the model objects that the field OBJ_ID can point to... see the 'OBJ_ID' field on
754
+	 * EEM_Promotion_Object
755
+	 *        Eg, if this line item join model object is for a ticket, this will return the EE_Ticket object
756
+	 *
757
+	 * @return EE_Base_Class | NULL
758
+	 * @throws EE_Error
759
+	 * @throws InvalidArgumentException
760
+	 * @throws InvalidDataTypeException
761
+	 * @throws InvalidInterfaceException
762
+	 * @throws ReflectionException
763
+	 */
764
+	public function get_object()
765
+	{
766
+		$model_name_of_related_obj = $this->OBJ_type();
767
+		return $this->get_model()->has_relation($model_name_of_related_obj)
768
+			? $this->get_first_related($model_name_of_related_obj)
769
+			: null;
770
+	}
771
+
772
+
773
+	/**
774
+	 * Like EE_Line_Item::get_object(), but can only ever actually return an EE_Ticket.
775
+	 * (IE, if this line item is for a price or something else, will return NULL)
776
+	 *
777
+	 * @param array $query_params
778
+	 * @return EE_Base_Class|EE_Ticket
779
+	 * @throws EE_Error
780
+	 * @throws InvalidArgumentException
781
+	 * @throws InvalidDataTypeException
782
+	 * @throws InvalidInterfaceException
783
+	 * @throws ReflectionException
784
+	 */
785
+	public function ticket($query_params = array())
786
+	{
787
+		// we're going to assume that when this method is called
788
+		// we always want to receive the attached ticket EVEN if that ticket is archived.
789
+		// This can be overridden via the incoming $query_params argument
790
+		$remove_defaults = array('default_where_conditions' => 'none');
791
+		$query_params = array_merge($remove_defaults, $query_params);
792
+		return $this->get_first_related(EEM_Line_Item::OBJ_TYPE_TICKET, $query_params);
793
+	}
794
+
795
+
796
+	/**
797
+	 * Gets the EE_Datetime that's related to the ticket, IF this is for a ticket
798
+	 *
799
+	 * @return EE_Datetime | NULL
800
+	 * @throws EE_Error
801
+	 * @throws InvalidArgumentException
802
+	 * @throws InvalidDataTypeException
803
+	 * @throws InvalidInterfaceException
804
+	 * @throws ReflectionException
805
+	 */
806
+	public function get_ticket_datetime()
807
+	{
808
+		if ($this->OBJ_type() === EEM_Line_Item::OBJ_TYPE_TICKET) {
809
+			$ticket = $this->ticket();
810
+			if ($ticket instanceof EE_Ticket) {
811
+				$datetime = $ticket->first_datetime();
812
+				if ($datetime instanceof EE_Datetime) {
813
+					return $datetime;
814
+				}
815
+			}
816
+		}
817
+		return null;
818
+	}
819
+
820
+
821
+	/**
822
+	 * Gets the event's name that's related to the ticket, if this is for
823
+	 * a ticket
824
+	 *
825
+	 * @return string
826
+	 * @throws EE_Error
827
+	 * @throws InvalidArgumentException
828
+	 * @throws InvalidDataTypeException
829
+	 * @throws InvalidInterfaceException
830
+	 * @throws ReflectionException
831
+	 */
832
+	public function ticket_event_name()
833
+	{
834
+		$event_name = esc_html__('Unknown', 'event_espresso');
835
+		$event = $this->ticket_event();
836
+		if ($event instanceof EE_Event) {
837
+			$event_name = $event->name();
838
+		}
839
+		return $event_name;
840
+	}
841
+
842
+
843
+	/**
844
+	 * Gets the event that's related to the ticket, if this line item represents a ticket.
845
+	 *
846
+	 * @return EE_Event|null
847
+	 * @throws EE_Error
848
+	 * @throws InvalidArgumentException
849
+	 * @throws InvalidDataTypeException
850
+	 * @throws InvalidInterfaceException
851
+	 * @throws ReflectionException
852
+	 */
853
+	public function ticket_event()
854
+	{
855
+		$event = null;
856
+		$ticket = $this->ticket();
857
+		if ($ticket instanceof EE_Ticket) {
858
+			$datetime = $ticket->first_datetime();
859
+			if ($datetime instanceof EE_Datetime) {
860
+				$event = $datetime->event();
861
+			}
862
+		}
863
+		return $event;
864
+	}
865
+
866
+
867
+	/**
868
+	 * Gets the first datetime for this lien item, assuming it's for a ticket
869
+	 *
870
+	 * @param string $date_format
871
+	 * @param string $time_format
872
+	 * @return string
873
+	 * @throws EE_Error
874
+	 * @throws InvalidArgumentException
875
+	 * @throws InvalidDataTypeException
876
+	 * @throws InvalidInterfaceException
877
+	 * @throws ReflectionException
878
+	 */
879
+	public function ticket_datetime_start($date_format = '', $time_format = '')
880
+	{
881
+		$first_datetime_string = esc_html__('Unknown', 'event_espresso');
882
+		$datetime = $this->get_ticket_datetime();
883
+		if ($datetime) {
884
+			$first_datetime_string = $datetime->start_date_and_time($date_format, $time_format);
885
+		}
886
+		return $first_datetime_string;
887
+	}
888
+
889
+
890
+	/**
891
+	 * Adds the line item as a child to this line item. If there is another child line
892
+	 * item with the same LIN_code, it is overwritten by this new one
893
+	 *
894
+	 * @param EE_Line_Item $line_item
895
+	 * @param bool          $set_order
896
+	 * @return bool success
897
+	 * @throws EE_Error
898
+	 * @throws InvalidArgumentException
899
+	 * @throws InvalidDataTypeException
900
+	 * @throws InvalidInterfaceException
901
+	 * @throws ReflectionException
902
+	 */
903
+	public function add_child_line_item(EE_Line_Item $line_item, $set_order = true)
904
+	{
905
+		// should we calculate the LIN_order for this line item ?
906
+		if ($set_order || $line_item->order() === null) {
907
+			$line_item->set_order(count($this->children()));
908
+		}
909
+		if ($this->ID()) {
910
+			// check for any duplicate line items (with the same code), if so, this replaces it
911
+			$line_item_with_same_code = $this->get_child_line_item($line_item->code());
912
+			if ($line_item_with_same_code instanceof EE_Line_Item && $line_item_with_same_code !== $line_item) {
913
+				$this->delete_child_line_item($line_item_with_same_code->code());
914
+			}
915
+			$line_item->set_parent_ID($this->ID());
916
+			if ($this->TXN_ID()) {
917
+				$line_item->set_TXN_ID($this->TXN_ID());
918
+			}
919
+			return $line_item->save();
920
+		}
921
+		$this->_children[ $line_item->code() ] = $line_item;
922
+		if ($line_item->parent() !== $this) {
923
+			$line_item->set_parent($this);
924
+		}
925
+		return true;
926
+	}
927
+
928
+
929
+	/**
930
+	 * Similar to EE_Base_Class::_add_relation_to, except this isn't a normal relation.
931
+	 * If this line item is saved to the DB, this is just a wrapper for set_parent_ID() and save()
932
+	 * However, if this line item is NOT saved to the DB, this just caches the parent on
933
+	 * the EE_Line_Item::_parent property.
934
+	 *
935
+	 * @param EE_Line_Item $line_item
936
+	 * @throws EE_Error
937
+	 * @throws InvalidArgumentException
938
+	 * @throws InvalidDataTypeException
939
+	 * @throws InvalidInterfaceException
940
+	 * @throws ReflectionException
941
+	 */
942
+	public function set_parent($line_item)
943
+	{
944
+		if ($this->ID()) {
945
+			if (! $line_item->ID()) {
946
+				$line_item->save();
947
+			}
948
+			$this->set_parent_ID($line_item->ID());
949
+			$this->save();
950
+		} else {
951
+			$this->_parent = $line_item;
952
+			$this->set_parent_ID($line_item->ID());
953
+		}
954
+	}
955
+
956
+
957
+	/**
958
+	 * Gets the child line item as specified by its code. Because this returns an object (by reference)
959
+	 * you can modify this child line item and the parent (this object) can know about them
960
+	 * because it also has a reference to that line item
961
+	 *
962
+	 * @param string $code
963
+	 * @return EE_Base_Class|EE_Line_Item|EE_Soft_Delete_Base_Class|NULL
964
+	 * @throws EE_Error
965
+	 * @throws InvalidArgumentException
966
+	 * @throws InvalidDataTypeException
967
+	 * @throws InvalidInterfaceException
968
+	 * @throws ReflectionException
969
+	 */
970
+	public function get_child_line_item($code)
971
+	{
972
+		if ($this->ID()) {
973
+			return $this->get_model()->get_one(
974
+				array(array('LIN_parent' => $this->ID(), 'LIN_code' => $code))
975
+			);
976
+		}
977
+		return isset($this->_children[ $code ])
978
+			? $this->_children[ $code ]
979
+			: null;
980
+	}
981
+
982
+
983
+	/**
984
+	 * Returns how many items are deleted (or, if this item has not been saved ot the DB yet, just how many it HAD
985
+	 * cached on it)
986
+	 *
987
+	 * @return int
988
+	 * @throws EE_Error
989
+	 * @throws InvalidArgumentException
990
+	 * @throws InvalidDataTypeException
991
+	 * @throws InvalidInterfaceException
992
+	 * @throws ReflectionException
993
+	 */
994
+	public function delete_children_line_items()
995
+	{
996
+		if ($this->ID()) {
997
+			return $this->get_model()->delete(array(array('LIN_parent' => $this->ID())));
998
+		}
999
+		$count = count($this->_children);
1000
+		$this->_children = array();
1001
+		return $count;
1002
+	}
1003
+
1004
+
1005
+	/**
1006
+	 * If this line item has been saved to the DB, deletes its child with LIN_code == $code. If this line
1007
+	 * HAS NOT been saved to the DB, removes the child line item with index $code.
1008
+	 * Also searches through the child's children for a matching line item. However, once a line item has been found
1009
+	 * and deleted, stops searching (so if there are line items with duplicate codes, only the first one found will be
1010
+	 * deleted)
1011
+	 *
1012
+	 * @param string $code
1013
+	 * @param bool   $stop_search_once_found
1014
+	 * @return int count of items deleted (or simply removed from the line item's cache, if not has not been saved to
1015
+	 *             the DB yet)
1016
+	 * @throws EE_Error
1017
+	 * @throws InvalidArgumentException
1018
+	 * @throws InvalidDataTypeException
1019
+	 * @throws InvalidInterfaceException
1020
+	 * @throws ReflectionException
1021
+	 */
1022
+	public function delete_child_line_item($code, $stop_search_once_found = true)
1023
+	{
1024
+		if ($this->ID()) {
1025
+			$items_deleted = 0;
1026
+			if ($this->code() === $code) {
1027
+				$items_deleted += EEH_Line_Item::delete_all_child_items($this);
1028
+				$items_deleted += (int) $this->delete();
1029
+				if ($stop_search_once_found) {
1030
+					return $items_deleted;
1031
+				}
1032
+			}
1033
+			foreach ($this->children() as $child_line_item) {
1034
+				$items_deleted += $child_line_item->delete_child_line_item($code, $stop_search_once_found);
1035
+			}
1036
+			return $items_deleted;
1037
+		}
1038
+		if (isset($this->_children[ $code ])) {
1039
+			unset($this->_children[ $code ]);
1040
+			return 1;
1041
+		}
1042
+		return 0;
1043
+	}
1044
+
1045
+
1046
+	/**
1047
+	 * If this line item is in the database, is of the type subtotal, and
1048
+	 * has no children, why do we have it? It should be deleted so this function
1049
+	 * does that
1050
+	 *
1051
+	 * @return boolean
1052
+	 * @throws EE_Error
1053
+	 * @throws InvalidArgumentException
1054
+	 * @throws InvalidDataTypeException
1055
+	 * @throws InvalidInterfaceException
1056
+	 * @throws ReflectionException
1057
+	 */
1058
+	public function delete_if_childless_subtotal()
1059
+	{
1060
+		if ($this->ID() && $this->type() === EEM_Line_Item::type_sub_total && ! $this->children()) {
1061
+			return $this->delete();
1062
+		}
1063
+		return false;
1064
+	}
1065
+
1066
+
1067
+	/**
1068
+	 * Creates a code and returns a string. doesn't assign the code to this model object
1069
+	 *
1070
+	 * @return string
1071
+	 * @throws EE_Error
1072
+	 * @throws InvalidArgumentException
1073
+	 * @throws InvalidDataTypeException
1074
+	 * @throws InvalidInterfaceException
1075
+	 * @throws ReflectionException
1076
+	 */
1077
+	public function generate_code()
1078
+	{
1079
+		// each line item in the cart requires a unique identifier
1080
+		return md5($this->get('OBJ_type') . $this->get('OBJ_ID') . microtime());
1081
+	}
1082
+
1083
+
1084
+	/**
1085
+	 * @return bool
1086
+	 * @throws EE_Error
1087
+	 * @throws InvalidArgumentException
1088
+	 * @throws InvalidDataTypeException
1089
+	 * @throws InvalidInterfaceException
1090
+	 * @throws ReflectionException
1091
+	 */
1092
+	public function isGlobalTax(): bool
1093
+	{
1094
+		return $this->type() === EEM_Line_Item::type_tax;
1095
+	}
1096
+
1097
+
1098
+	/**
1099
+	 * @return bool
1100
+	 * @throws EE_Error
1101
+	 * @throws InvalidArgumentException
1102
+	 * @throws InvalidDataTypeException
1103
+	 * @throws InvalidInterfaceException
1104
+	 * @throws ReflectionException
1105
+	 */
1106
+	public function isSubTax(): bool
1107
+	{
1108
+		return $this->type() === EEM_Line_Item::type_sub_tax;
1109
+	}
1110
+
1111
+
1112
+	/**
1113
+	 * returns true if this is a line item with a direct descendent of the type sub-tax
1114
+	 *
1115
+	 * @return array
1116
+	 * @throws EE_Error
1117
+	 * @throws InvalidArgumentException
1118
+	 * @throws InvalidDataTypeException
1119
+	 * @throws InvalidInterfaceException
1120
+	 * @throws ReflectionException
1121
+	 */
1122
+	public function getSubTaxes(): array
1123
+	{
1124
+		if (! $this->is_line_item()) {
1125
+			return [];
1126
+		}
1127
+		return EEH_Line_Item::get_descendants_of_type($this, EEM_Line_Item::type_sub_tax);
1128
+	}
1129
+
1130
+
1131
+	/**
1132
+	 * returns true if this is a line item with a direct descendent of the type sub-tax
1133
+	 *
1134
+	 * @return bool
1135
+	 * @throws EE_Error
1136
+	 * @throws InvalidArgumentException
1137
+	 * @throws InvalidDataTypeException
1138
+	 * @throws InvalidInterfaceException
1139
+	 * @throws ReflectionException
1140
+	 */
1141
+	public function hasSubTaxes(): bool
1142
+	{
1143
+		if (! $this->is_line_item()) {
1144
+			return false;
1145
+		}
1146
+		$sub_taxes = $this->getSubTaxes();
1147
+		return ! empty($sub_taxes);
1148
+	}
1149
+
1150
+
1151
+	/**
1152
+	 * @return bool
1153
+	 * @throws EE_Error
1154
+	 * @throws ReflectionException
1155
+	 * @deprecated   $VID:$
1156
+	 */
1157
+	public function is_tax(): bool
1158
+	{
1159
+		return $this->isGlobalTax();
1160
+	}
1161
+
1162
+
1163
+	/**
1164
+	 * @return bool
1165
+	 * @throws EE_Error
1166
+	 * @throws InvalidArgumentException
1167
+	 * @throws InvalidDataTypeException
1168
+	 * @throws InvalidInterfaceException
1169
+	 * @throws ReflectionException
1170
+	 */
1171
+	public function is_tax_sub_total()
1172
+	{
1173
+		return $this->type() === EEM_Line_Item::type_tax_sub_total;
1174
+	}
1175
+
1176
+
1177
+	/**
1178
+	 * @return bool
1179
+	 * @throws EE_Error
1180
+	 * @throws InvalidArgumentException
1181
+	 * @throws InvalidDataTypeException
1182
+	 * @throws InvalidInterfaceException
1183
+	 * @throws ReflectionException
1184
+	 */
1185
+	public function is_line_item()
1186
+	{
1187
+		return $this->type() === EEM_Line_Item::type_line_item;
1188
+	}
1189
+
1190
+
1191
+	/**
1192
+	 * @return bool
1193
+	 * @throws EE_Error
1194
+	 * @throws InvalidArgumentException
1195
+	 * @throws InvalidDataTypeException
1196
+	 * @throws InvalidInterfaceException
1197
+	 * @throws ReflectionException
1198
+	 */
1199
+	public function is_sub_line_item()
1200
+	{
1201
+		return $this->type() === EEM_Line_Item::type_sub_line_item;
1202
+	}
1203
+
1204
+
1205
+	/**
1206
+	 * @return bool
1207
+	 * @throws EE_Error
1208
+	 * @throws InvalidArgumentException
1209
+	 * @throws InvalidDataTypeException
1210
+	 * @throws InvalidInterfaceException
1211
+	 * @throws ReflectionException
1212
+	 */
1213
+	public function is_sub_total()
1214
+	{
1215
+		return $this->type() === EEM_Line_Item::type_sub_total;
1216
+	}
1217
+
1218
+
1219
+	/**
1220
+	 * Whether or not this line item is a cancellation line item
1221
+	 *
1222
+	 * @return boolean
1223
+	 * @throws EE_Error
1224
+	 * @throws InvalidArgumentException
1225
+	 * @throws InvalidDataTypeException
1226
+	 * @throws InvalidInterfaceException
1227
+	 * @throws ReflectionException
1228
+	 */
1229
+	public function is_cancellation()
1230
+	{
1231
+		return EEM_Line_Item::type_cancellation === $this->type();
1232
+	}
1233
+
1234
+
1235
+	/**
1236
+	 * @return bool
1237
+	 * @throws EE_Error
1238
+	 * @throws InvalidArgumentException
1239
+	 * @throws InvalidDataTypeException
1240
+	 * @throws InvalidInterfaceException
1241
+	 * @throws ReflectionException
1242
+	 */
1243
+	public function is_total()
1244
+	{
1245
+		return $this->type() === EEM_Line_Item::type_total;
1246
+	}
1247
+
1248
+
1249
+	/**
1250
+	 * @return bool
1251
+	 * @throws EE_Error
1252
+	 * @throws InvalidArgumentException
1253
+	 * @throws InvalidDataTypeException
1254
+	 * @throws InvalidInterfaceException
1255
+	 * @throws ReflectionException
1256
+	 */
1257
+	public function is_cancelled()
1258
+	{
1259
+		return $this->type() === EEM_Line_Item::type_cancellation;
1260
+	}
1261
+
1262
+
1263
+	/**
1264
+	 * @return string like '2, 004.00', formatted according to the localized currency
1265
+	 * @throws EE_Error
1266
+	 * @throws InvalidArgumentException
1267
+	 * @throws InvalidDataTypeException
1268
+	 * @throws InvalidInterfaceException
1269
+	 * @throws ReflectionException
1270
+	 */
1271
+	public function unit_price_no_code()
1272
+	{
1273
+		return $this->get_pretty('LIN_unit_price', 'no_currency_code');
1274
+	}
1275
+
1276
+
1277
+	/**
1278
+	 * @return string like '2, 004.00', formatted according to the localized currency
1279
+	 * @throws EE_Error
1280
+	 * @throws InvalidArgumentException
1281
+	 * @throws InvalidDataTypeException
1282
+	 * @throws InvalidInterfaceException
1283
+	 * @throws ReflectionException
1284
+	 */
1285
+	public function total_no_code()
1286
+	{
1287
+		return $this->get_pretty('LIN_total', 'no_currency_code');
1288
+	}
1289
+
1290
+
1291
+	/**
1292
+	 * Gets the final total on this item, taking taxes into account.
1293
+	 * Has the side-effect of setting the sub-total as it was just calculated.
1294
+	 * If this is used on a grand-total line item, also updates the transaction's
1295
+	 * TXN_total (provided this line item is allowed to persist, otherwise we don't
1296
+	 * want to change a persistable transaction with info from a non-persistent line item)
1297
+	 *
1298
+	 * @param bool $update_txn_status
1299
+	 * @return float
1300
+	 * @throws EE_Error
1301
+	 * @throws InvalidArgumentException
1302
+	 * @throws InvalidDataTypeException
1303
+	 * @throws InvalidInterfaceException
1304
+	 * @throws ReflectionException
1305
+	 * @throws RuntimeException
1306
+	 */
1307
+	public function recalculate_total_including_taxes(bool $update_txn_status = false): float
1308
+	{
1309
+		$grand_total_line_item = EEH_Line_Item::find_transaction_grand_total_for_line_item($this);
1310
+		return $this->calculator->recalculateTotalIncludingTaxes($grand_total_line_item, $update_txn_status);
1311
+	}
1312
+
1313
+
1314
+	/**
1315
+	 * Recursively goes through all the children and recalculates sub-totals EXCEPT for
1316
+	 * tax-sub-totals (they're a an odd beast). Updates the 'total' on each line item according to either its
1317
+	 * unit price * quantity or the total of all its children EXCEPT when we're only calculating the taxable total and
1318
+	 * when this is called on the grand total
1319
+	 *
1320
+	 * @return float
1321
+	 * @throws EE_Error
1322
+	 * @throws InvalidArgumentException
1323
+	 * @throws InvalidDataTypeException
1324
+	 * @throws InvalidInterfaceException
1325
+	 * @throws ReflectionException
1326
+	 */
1327
+	public function recalculate_pre_tax_total(): float
1328
+	{
1329
+		$grand_total_line_item = EEH_Line_Item::find_transaction_grand_total_for_line_item($this);
1330
+		[$total] = $this->calculator->recalculateLineItemTotals($grand_total_line_item);
1331
+		return $total;
1332
+	}
1333
+
1334
+
1335
+	/**
1336
+	 * Recalculates the total on each individual tax (based on a recalculation of the pre-tax total), sets
1337
+	 * the totals on each tax calculated, and returns the final tax total. Re-saves tax line items
1338
+	 * and tax sub-total if already in the DB
1339
+	 *
1340
+	 * @return float
1341
+	 * @throws EE_Error
1342
+	 * @throws InvalidArgumentException
1343
+	 * @throws InvalidDataTypeException
1344
+	 * @throws InvalidInterfaceException
1345
+	 * @throws ReflectionException
1346
+	 */
1347
+	public function recalculate_taxes_and_tax_total(): float
1348
+	{
1349
+		$grand_total_line_item = EEH_Line_Item::find_transaction_grand_total_for_line_item($this);
1350
+		return $this->calculator->recalculateTaxesAndTaxTotal($grand_total_line_item);
1351
+	}
1352
+
1353
+
1354
+	/**
1355
+	 * Gets the total tax on this line item. Assumes taxes have already been calculated using
1356
+	 * recalculate_taxes_and_total
1357
+	 *
1358
+	 * @return float
1359
+	 * @throws EE_Error
1360
+	 * @throws InvalidArgumentException
1361
+	 * @throws InvalidDataTypeException
1362
+	 * @throws InvalidInterfaceException
1363
+	 * @throws ReflectionException
1364
+	 */
1365
+	public function get_total_tax()
1366
+	{
1367
+		$grand_total_line_item = EEH_Line_Item::find_transaction_grand_total_for_line_item($this);
1368
+		return $this->calculator->recalculateTaxesAndTaxTotal($grand_total_line_item);
1369
+	}
1370
+
1371
+
1372
+	/**
1373
+	 * Gets the total for all the items purchased only
1374
+	 *
1375
+	 * @return float
1376
+	 * @throws EE_Error
1377
+	 * @throws InvalidArgumentException
1378
+	 * @throws InvalidDataTypeException
1379
+	 * @throws InvalidInterfaceException
1380
+	 * @throws ReflectionException
1381
+	 */
1382
+	public function get_items_total()
1383
+	{
1384
+		// by default, let's make sure we're consistent with the existing line item
1385
+		if ($this->is_total()) {
1386
+			return $this->pretaxTotal();
1387
+		}
1388
+		$total = 0;
1389
+		foreach ($this->get_items() as $item) {
1390
+			if ($item instanceof EE_Line_Item) {
1391
+				$total += $item->pretaxTotal();
1392
+			}
1393
+		}
1394
+		return $total;
1395
+	}
1396
+
1397
+
1398
+	/**
1399
+	 * Gets all the descendants (ie, children or children of children etc) that
1400
+	 * are of the type 'tax'
1401
+	 *
1402
+	 * @return EE_Line_Item[]
1403
+	 * @throws EE_Error
1404
+	 */
1405
+	public function tax_descendants()
1406
+	{
1407
+		return EEH_Line_Item::get_tax_descendants($this);
1408
+	}
1409
+
1410
+
1411
+	/**
1412
+	 * Gets all the real items purchased which are children of this item
1413
+	 *
1414
+	 * @return EE_Line_Item[]
1415
+	 * @throws EE_Error
1416
+	 */
1417
+	public function get_items()
1418
+	{
1419
+		return EEH_Line_Item::get_line_item_descendants($this);
1420
+	}
1421
+
1422
+
1423
+	/**
1424
+	 * Returns the amount taxable among this line item's children (or if it has no children,
1425
+	 * how much of it is taxable). Does not recalculate totals or subtotals.
1426
+	 * If the taxable total is negative, (eg, if none of the tickets were taxable,
1427
+	 * but there is a "Taxable" discount), returns 0.
1428
+	 *
1429
+	 * @return float
1430
+	 * @throws EE_Error
1431
+	 * @throws InvalidArgumentException
1432
+	 * @throws InvalidDataTypeException
1433
+	 * @throws InvalidInterfaceException
1434
+	 * @throws ReflectionException
1435
+	 */
1436
+	public function taxable_total(): float
1437
+	{
1438
+		return $this->calculator->taxableAmountForGlobalTaxes($this);
1439
+	}
1440
+
1441
+
1442
+	/**
1443
+	 * Gets the transaction for this line item
1444
+	 *
1445
+	 * @return EE_Base_Class|EE_Transaction
1446
+	 * @throws EE_Error
1447
+	 * @throws InvalidArgumentException
1448
+	 * @throws InvalidDataTypeException
1449
+	 * @throws InvalidInterfaceException
1450
+	 * @throws ReflectionException
1451
+	 */
1452
+	public function transaction()
1453
+	{
1454
+		return $this->get_first_related(EEM_Line_Item::OBJ_TYPE_TRANSACTION);
1455
+	}
1456
+
1457
+
1458
+	/**
1459
+	 * Saves this line item to the DB, and recursively saves its descendants.
1460
+	 * Because there currently is no proper parent-child relation on the model,
1461
+	 * save_this_and_cached() will NOT save the descendants.
1462
+	 * Also sets the transaction on this line item and all its descendants before saving
1463
+	 *
1464
+	 * @param int $txn_id if none is provided, assumes $this->TXN_ID()
1465
+	 * @return int count of items saved
1466
+	 * @throws EE_Error
1467
+	 * @throws InvalidArgumentException
1468
+	 * @throws InvalidDataTypeException
1469
+	 * @throws InvalidInterfaceException
1470
+	 * @throws ReflectionException
1471
+	 */
1472
+	public function save_this_and_descendants_to_txn($txn_id = null)
1473
+	{
1474
+		$count = 0;
1475
+		if (! $txn_id) {
1476
+			$txn_id = $this->TXN_ID();
1477
+		}
1478
+		$this->set_TXN_ID($txn_id);
1479
+		$children = $this->children();
1480
+		$count += $this->save()
1481
+			? 1
1482
+			: 0;
1483
+		foreach ($children as $child_line_item) {
1484
+			if ($child_line_item instanceof EE_Line_Item) {
1485
+				$child_line_item->set_parent_ID($this->ID());
1486
+				$count += $child_line_item->save_this_and_descendants_to_txn($txn_id);
1487
+			}
1488
+		}
1489
+		return $count;
1490
+	}
1491
+
1492
+
1493
+	/**
1494
+	 * Saves this line item to the DB, and recursively saves its descendants.
1495
+	 *
1496
+	 * @return int count of items saved
1497
+	 * @throws EE_Error
1498
+	 * @throws InvalidArgumentException
1499
+	 * @throws InvalidDataTypeException
1500
+	 * @throws InvalidInterfaceException
1501
+	 * @throws ReflectionException
1502
+	 */
1503
+	public function save_this_and_descendants()
1504
+	{
1505
+		$count = 0;
1506
+		$children = $this->children();
1507
+		$count += $this->save()
1508
+			? 1
1509
+			: 0;
1510
+		foreach ($children as $child_line_item) {
1511
+			if ($child_line_item instanceof EE_Line_Item) {
1512
+				$child_line_item->set_parent_ID($this->ID());
1513
+				$count += $child_line_item->save_this_and_descendants();
1514
+			}
1515
+		}
1516
+		return $count;
1517
+	}
1518
+
1519
+
1520
+	/**
1521
+	 * returns the cancellation line item if this item was cancelled
1522
+	 *
1523
+	 * @return EE_Line_Item[]
1524
+	 * @throws InvalidArgumentException
1525
+	 * @throws InvalidInterfaceException
1526
+	 * @throws InvalidDataTypeException
1527
+	 * @throws ReflectionException
1528
+	 * @throws EE_Error
1529
+	 */
1530
+	public function get_cancellations()
1531
+	{
1532
+		return EEH_Line_Item::get_descendants_of_type($this, EEM_Line_Item::type_cancellation);
1533
+	}
1534
+
1535
+
1536
+	/**
1537
+	 * If this item has an ID, then this saves it again to update the db
1538
+	 *
1539
+	 * @return int count of items saved
1540
+	 * @throws EE_Error
1541
+	 * @throws InvalidArgumentException
1542
+	 * @throws InvalidDataTypeException
1543
+	 * @throws InvalidInterfaceException
1544
+	 * @throws ReflectionException
1545
+	 */
1546
+	public function maybe_save()
1547
+	{
1548
+		if ($this->ID()) {
1549
+			return $this->save();
1550
+		}
1551
+		return false;
1552
+	}
1553
+
1554
+
1555
+	/**
1556
+	 * clears the cached children and parent from the line item
1557
+	 *
1558
+	 * @return void
1559
+	 */
1560
+	public function clear_related_line_item_cache()
1561
+	{
1562
+		$this->_children = array();
1563
+		$this->_parent = null;
1564
+	}
1565
+
1566
+
1567
+	/**
1568
+	 * @param bool $raw
1569
+	 * @return int
1570
+	 * @throws EE_Error
1571
+	 * @throws InvalidArgumentException
1572
+	 * @throws InvalidDataTypeException
1573
+	 * @throws InvalidInterfaceException
1574
+	 * @throws ReflectionException
1575
+	 */
1576
+	public function timestamp($raw = false)
1577
+	{
1578
+		return $raw
1579
+			? $this->get_raw('LIN_timestamp')
1580
+			: $this->get('LIN_timestamp');
1581
+	}
1582
+
1583
+
1584
+
1585
+
1586
+	/************************* DEPRECATED *************************/
1587
+	/**
1588
+	 * @deprecated 4.6.0
1589
+	 * @param string $type one of the constants on EEM_Line_Item
1590
+	 * @return EE_Line_Item[]
1591
+	 * @throws EE_Error
1592
+	 */
1593
+	protected function _get_descendants_of_type($type)
1594
+	{
1595
+		EE_Error::doing_it_wrong(
1596
+			'EE_Line_Item::_get_descendants_of_type()',
1597
+			sprintf(
1598
+				esc_html__('Method replaced with %1$s', 'event_espresso'),
1599
+				'EEH_Line_Item::get_descendants_of_type()'
1600
+			),
1601
+			'4.6.0'
1602
+		);
1603
+		return EEH_Line_Item::get_descendants_of_type($this, $type);
1604
+	}
1605
+
1606
+
1607
+	/**
1608
+	 * @deprecated 4.6.0
1609
+	 * @param string $type like one of the EEM_Line_Item::type_*
1610
+	 * @return EE_Line_Item
1611
+	 * @throws EE_Error
1612
+	 * @throws InvalidArgumentException
1613
+	 * @throws InvalidDataTypeException
1614
+	 * @throws InvalidInterfaceException
1615
+	 * @throws ReflectionException
1616
+	 */
1617
+	public function get_nearest_descendant_of_type($type)
1618
+	{
1619
+		EE_Error::doing_it_wrong(
1620
+			'EE_Line_Item::get_nearest_descendant_of_type()',
1621
+			sprintf(
1622
+				esc_html__('Method replaced with %1$s', 'event_espresso'),
1623
+				'EEH_Line_Item::get_nearest_descendant_of_type()'
1624
+			),
1625
+			'4.6.0'
1626
+		);
1627
+		return EEH_Line_Item::get_nearest_descendant_of_type($this, $type);
1628
+	}
1629 1629
 }
Please login to merge, or discard this patch.
core/EE_Dependency_Map.core.php 1 patch
Indentation   +1063 added lines, -1063 removed lines patch added patch discarded remove patch
@@ -22,1067 +22,1067 @@
 block discarded – undo
22 22
 class EE_Dependency_Map
23 23
 {
24 24
 
25
-    /**
26
-     * This means that the requested class dependency is not present in the dependency map
27
-     */
28
-    const not_registered = 0;
29
-
30
-    /**
31
-     * This instructs class loaders to ALWAYS return a newly instantiated object for the requested class.
32
-     */
33
-    const load_new_object = 1;
34
-
35
-    /**
36
-     * This instructs class loaders to return a previously instantiated and cached object for the requested class.
37
-     * IF a previously instantiated object does not exist, a new one will be created and added to the cache.
38
-     */
39
-    const load_from_cache = 2;
40
-
41
-    /**
42
-     * When registering a dependency,
43
-     * this indicates to keep any existing dependencies that already exist,
44
-     * and simply discard any new dependencies declared in the incoming data
45
-     */
46
-    const KEEP_EXISTING_DEPENDENCIES = 0;
47
-
48
-    /**
49
-     * When registering a dependency,
50
-     * this indicates to overwrite any existing dependencies that already exist using the incoming data
51
-     */
52
-    const OVERWRITE_DEPENDENCIES = 1;
53
-
54
-    /**
55
-     * @type EE_Dependency_Map $_instance
56
-     */
57
-    protected static $_instance;
58
-
59
-    /**
60
-     * @var ClassInterfaceCache $class_cache
61
-     */
62
-    private $class_cache;
63
-
64
-    /**
65
-     * @type RequestInterface $request
66
-     */
67
-    protected $request;
68
-
69
-    /**
70
-     * @type LegacyRequestInterface $legacy_request
71
-     */
72
-    protected $legacy_request;
73
-
74
-    /**
75
-     * @type ResponseInterface $response
76
-     */
77
-    protected $response;
78
-
79
-    /**
80
-     * @type LoaderInterface $loader
81
-     */
82
-    protected $loader;
83
-
84
-    /**
85
-     * @type array $_dependency_map
86
-     */
87
-    protected $_dependency_map = [];
88
-
89
-    /**
90
-     * @type array $_class_loaders
91
-     */
92
-    protected $_class_loaders = [];
93
-
94
-
95
-    /**
96
-     * EE_Dependency_Map constructor.
97
-     *
98
-     * @param ClassInterfaceCache $class_cache
99
-     */
100
-    protected function __construct(ClassInterfaceCache $class_cache)
101
-    {
102
-        $this->class_cache = $class_cache;
103
-        do_action('EE_Dependency_Map____construct', $this);
104
-    }
105
-
106
-
107
-    /**
108
-     * @return void
109
-     * @throws InvalidAliasException
110
-     */
111
-    public function initialize()
112
-    {
113
-        $this->_register_core_dependencies();
114
-        $this->_register_core_class_loaders();
115
-        $this->_register_core_aliases();
116
-    }
117
-
118
-
119
-    /**
120
-     * @singleton method used to instantiate class object
121
-     * @param ClassInterfaceCache|null $class_cache
122
-     * @return EE_Dependency_Map
123
-     */
124
-    public static function instance(ClassInterfaceCache $class_cache = null)
125
-    {
126
-        // check if class object is instantiated, and instantiated properly
127
-        if (
128
-            ! EE_Dependency_Map::$_instance instanceof EE_Dependency_Map
129
-            && $class_cache instanceof ClassInterfaceCache
130
-        ) {
131
-            EE_Dependency_Map::$_instance = new EE_Dependency_Map($class_cache);
132
-        }
133
-        return EE_Dependency_Map::$_instance;
134
-    }
135
-
136
-
137
-    /**
138
-     * @param RequestInterface $request
139
-     */
140
-    public function setRequest(RequestInterface $request)
141
-    {
142
-        $this->request = $request;
143
-    }
144
-
145
-
146
-    /**
147
-     * @param LegacyRequestInterface $legacy_request
148
-     */
149
-    public function setLegacyRequest(LegacyRequestInterface $legacy_request)
150
-    {
151
-        $this->legacy_request = $legacy_request;
152
-    }
153
-
154
-
155
-    /**
156
-     * @param ResponseInterface $response
157
-     */
158
-    public function setResponse(ResponseInterface $response)
159
-    {
160
-        $this->response = $response;
161
-    }
162
-
163
-
164
-    /**
165
-     * @param LoaderInterface $loader
166
-     */
167
-    public function setLoader(LoaderInterface $loader)
168
-    {
169
-        $this->loader = $loader;
170
-    }
171
-
172
-
173
-    /**
174
-     * @param string $class
175
-     * @param array  $dependencies
176
-     * @param int    $overwrite
177
-     * @return bool
178
-     */
179
-    public static function register_dependencies(
180
-        $class,
181
-        array $dependencies,
182
-        $overwrite = EE_Dependency_Map::KEEP_EXISTING_DEPENDENCIES
183
-    ) {
184
-        return EE_Dependency_Map::$_instance->registerDependencies($class, $dependencies, $overwrite);
185
-    }
186
-
187
-
188
-    /**
189
-     * Assigns an array of class names and corresponding load sources (new or cached)
190
-     * to the class specified by the first parameter.
191
-     * IMPORTANT !!!
192
-     * The order of elements in the incoming $dependencies array MUST match
193
-     * the order of the constructor parameters for the class in question.
194
-     * This is especially important when overriding any existing dependencies that are registered.
195
-     * the third parameter controls whether any duplicate dependencies are overwritten or not.
196
-     *
197
-     * @param string $class
198
-     * @param array  $dependencies
199
-     * @param int    $overwrite
200
-     * @return bool
201
-     */
202
-    public function registerDependencies(
203
-        $class,
204
-        array $dependencies,
205
-        $overwrite = EE_Dependency_Map::KEEP_EXISTING_DEPENDENCIES
206
-    ) {
207
-        $class      = trim($class, '\\');
208
-        $registered = false;
209
-        if (empty(EE_Dependency_Map::$_instance->_dependency_map[ $class ])) {
210
-            EE_Dependency_Map::$_instance->_dependency_map[ $class ] = [];
211
-        }
212
-        // we need to make sure that any aliases used when registering a dependency
213
-        // get resolved to the correct class name
214
-        foreach ($dependencies as $dependency => $load_source) {
215
-            $alias = EE_Dependency_Map::$_instance->getFqnForAlias($dependency);
216
-            if (
217
-                $overwrite === EE_Dependency_Map::OVERWRITE_DEPENDENCIES
218
-                || ! isset(EE_Dependency_Map::$_instance->_dependency_map[ $class ][ $alias ])
219
-            ) {
220
-                unset($dependencies[ $dependency ]);
221
-                $dependencies[ $alias ] = $load_source;
222
-                $registered             = true;
223
-            }
224
-        }
225
-        // now add our two lists of dependencies together.
226
-        // using Union (+=) favours the arrays in precedence from left to right,
227
-        // so $dependencies is NOT overwritten because it is listed first
228
-        // ie: with A = B + C, entries in B take precedence over duplicate entries in C
229
-        // Union is way faster than array_merge() but should be used with caution...
230
-        // especially with numerically indexed arrays
231
-        $dependencies += EE_Dependency_Map::$_instance->_dependency_map[ $class ];
232
-        // now we need to ensure that the resulting dependencies
233
-        // array only has the entries that are required for the class
234
-        // so first count how many dependencies were originally registered for the class
235
-        $dependency_count = count(EE_Dependency_Map::$_instance->_dependency_map[ $class ]);
236
-        // if that count is non-zero (meaning dependencies were already registered)
237
-        EE_Dependency_Map::$_instance->_dependency_map[ $class ] = $dependency_count
238
-            // then truncate the  final array to match that count
239
-            ? array_slice($dependencies, 0, $dependency_count)
240
-            // otherwise just take the incoming array because nothing previously existed
241
-            : $dependencies;
242
-        return $registered;
243
-    }
244
-
245
-
246
-    /**
247
-     * @param string $class_name
248
-     * @param string $loader
249
-     * @return bool
250
-     * @throws DomainException
251
-     */
252
-    public static function register_class_loader($class_name, $loader = 'load_core')
253
-    {
254
-        return EE_Dependency_Map::$_instance->registerClassLoader($class_name, $loader);
255
-    }
256
-
257
-
258
-    /**
259
-     * @param string $class_name
260
-     * @param string $loader
261
-     * @return bool
262
-     * @throws DomainException
263
-     */
264
-    public function registerClassLoader($class_name, $loader = 'load_core')
265
-    {
266
-        if (! $loader instanceof Closure && strpos($class_name, '\\') !== false) {
267
-            throw new DomainException(
268
-                esc_html__('Don\'t use class loaders for FQCNs.', 'event_espresso')
269
-            );
270
-        }
271
-        // check that loader is callable or method starts with "load_" and exists in EE_Registry
272
-        if (
273
-            ! is_callable($loader)
274
-            && (
275
-                strpos($loader, 'load_') !== 0
276
-                || ! method_exists('EE_Registry', $loader)
277
-            )
278
-        ) {
279
-            throw new DomainException(
280
-                sprintf(
281
-                    esc_html__(
282
-                        '"%1$s" is not a valid loader method on EE_Registry.',
283
-                        'event_espresso'
284
-                    ),
285
-                    $loader
286
-                )
287
-            );
288
-        }
289
-        $class_name = EE_Dependency_Map::$_instance->getFqnForAlias($class_name);
290
-        if (! isset(EE_Dependency_Map::$_instance->_class_loaders[ $class_name ])) {
291
-            EE_Dependency_Map::$_instance->_class_loaders[ $class_name ] = $loader;
292
-            return true;
293
-        }
294
-        return false;
295
-    }
296
-
297
-
298
-    /**
299
-     * @return array
300
-     */
301
-    public function dependency_map()
302
-    {
303
-        return $this->_dependency_map;
304
-    }
305
-
306
-
307
-    /**
308
-     * returns TRUE if dependency map contains a listing for the provided class name
309
-     *
310
-     * @param string $class_name
311
-     * @return boolean
312
-     */
313
-    public function has($class_name = '')
314
-    {
315
-        // all legacy models have the same dependencies
316
-        if (strpos($class_name, 'EEM_') === 0) {
317
-            $class_name = 'LEGACY_MODELS';
318
-        }
319
-        return isset($this->_dependency_map[ $class_name ]);
320
-    }
321
-
322
-
323
-    /**
324
-     * returns TRUE if dependency map contains a listing for the provided class name AND dependency
325
-     *
326
-     * @param string $class_name
327
-     * @param string $dependency
328
-     * @return bool
329
-     */
330
-    public function has_dependency_for_class($class_name = '', $dependency = '')
331
-    {
332
-        // all legacy models have the same dependencies
333
-        if (strpos($class_name, 'EEM_') === 0) {
334
-            $class_name = 'LEGACY_MODELS';
335
-        }
336
-        $dependency = $this->getFqnForAlias($dependency, $class_name);
337
-        return isset($this->_dependency_map[ $class_name ][ $dependency ]);
338
-    }
339
-
340
-
341
-    /**
342
-     * returns loading strategy for whether a previously cached dependency should be loaded or a new instance returned
343
-     *
344
-     * @param string $class_name
345
-     * @param string $dependency
346
-     * @return int
347
-     */
348
-    public function loading_strategy_for_class_dependency($class_name = '', $dependency = '')
349
-    {
350
-        // all legacy models have the same dependencies
351
-        if (strpos($class_name, 'EEM_') === 0) {
352
-            $class_name = 'LEGACY_MODELS';
353
-        }
354
-        $dependency = $this->getFqnForAlias($dependency);
355
-        return $this->has_dependency_for_class($class_name, $dependency)
356
-            ? $this->_dependency_map[ $class_name ][ $dependency ]
357
-            : EE_Dependency_Map::not_registered;
358
-    }
359
-
360
-
361
-    /**
362
-     * @param string $class_name
363
-     * @return string | Closure
364
-     */
365
-    public function class_loader($class_name)
366
-    {
367
-        // all legacy models use load_model()
368
-        if (strpos($class_name, 'EEM_') === 0) {
369
-            return 'load_model';
370
-        }
371
-        // EE_CPT_*_Strategy classes like EE_CPT_Event_Strategy, EE_CPT_Venue_Strategy, etc
372
-        // perform strpos() first to avoid loading regex every time we load a class
373
-        if (
374
-            strpos($class_name, 'EE_CPT_') === 0
375
-            && preg_match('/^EE_CPT_([a-zA-Z]+)_Strategy$/', $class_name)
376
-        ) {
377
-            return 'load_core';
378
-        }
379
-        $class_name = $this->getFqnForAlias($class_name);
380
-        return isset($this->_class_loaders[ $class_name ]) ? $this->_class_loaders[ $class_name ] : '';
381
-    }
382
-
383
-
384
-    /**
385
-     * @return array
386
-     */
387
-    public function class_loaders()
388
-    {
389
-        return $this->_class_loaders;
390
-    }
391
-
392
-
393
-    /**
394
-     * adds an alias for a classname
395
-     *
396
-     * @param string $fqcn      the class name that should be used (concrete class to replace interface)
397
-     * @param string $alias     the class name that would be type hinted for (abstract parent or interface)
398
-     * @param string $for_class the class that has the dependency (is type hinting for the interface)
399
-     * @throws InvalidAliasException
400
-     */
401
-    public function add_alias($fqcn, $alias, $for_class = '')
402
-    {
403
-        $this->class_cache->addAlias($fqcn, $alias, $for_class);
404
-    }
405
-
406
-
407
-    /**
408
-     * Returns TRUE if the provided fully qualified name IS an alias
409
-     * WHY?
410
-     * Because if a class is type hinting for a concretion,
411
-     * then why would we need to find another class to supply it?
412
-     * ie: if a class asks for `Fully/Qualified/Namespace/SpecificClassName`,
413
-     * then give it an instance of `Fully/Qualified/Namespace/SpecificClassName`.
414
-     * Don't go looking for some substitute.
415
-     * Whereas if a class is type hinting for an interface...
416
-     * then we need to find an actual class to use.
417
-     * So the interface IS the alias for some other FQN,
418
-     * and we need to find out if `Fully/Qualified/Namespace/SomeInterface`
419
-     * represents some other class.
420
-     *
421
-     * @param string $fqn
422
-     * @param string $for_class
423
-     * @return bool
424
-     */
425
-    public function isAlias($fqn = '', $for_class = '')
426
-    {
427
-        return $this->class_cache->isAlias($fqn, $for_class);
428
-    }
429
-
430
-
431
-    /**
432
-     * Returns a FQN for provided alias if one exists, otherwise returns the original $alias
433
-     * functions recursively, so that multiple aliases can be used to drill down to a FQN
434
-     *  for example:
435
-     *      if the following two entries were added to the _aliases array:
436
-     *          array(
437
-     *              'interface_alias'           => 'some\namespace\interface'
438
-     *              'some\namespace\interface'  => 'some\namespace\classname'
439
-     *          )
440
-     *      then one could use EE_Registry::instance()->create( 'interface_alias' )
441
-     *      to load an instance of 'some\namespace\classname'
442
-     *
443
-     * @param string $alias
444
-     * @param string $for_class
445
-     * @return string
446
-     */
447
-    public function getFqnForAlias($alias = '', $for_class = '')
448
-    {
449
-        return (string) $this->class_cache->getFqnForAlias($alias, $for_class);
450
-    }
451
-
452
-
453
-    /**
454
-     * Registers the core dependencies and whether a previously instantiated object should be loaded from the cache,
455
-     * if one exists, or whether a new object should be generated every time the requested class is loaded.
456
-     * This is done by using the following class constants:
457
-     *        EE_Dependency_Map::load_from_cache - loads previously instantiated object
458
-     *        EE_Dependency_Map::load_new_object - generates a new object every time
459
-     */
460
-    protected function _register_core_dependencies()
461
-    {
462
-        $this->_dependency_map = [
463
-            'EE_Request_Handler'                                                                                          => [
464
-                'EE_Request' => EE_Dependency_Map::load_from_cache,
465
-            ],
466
-            'EE_System'                                                                                                   => [
467
-                'EventEspresso\core\services\loaders\Loader'  => EE_Dependency_Map::load_from_cache,
468
-                'EE_Maintenance_Mode'                         => EE_Dependency_Map::load_from_cache,
469
-                'EE_Registry'                                 => EE_Dependency_Map::load_from_cache,
470
-                'EventEspresso\core\services\request\Request' => EE_Dependency_Map::load_from_cache,
471
-                'EventEspresso\core\services\routing\Router'  => EE_Dependency_Map::load_from_cache,
472
-            ],
473
-            'EE_Admin'                                                                                                    => [
474
-                'EventEspresso\core\services\loaders\Loader'  => EE_Dependency_Map::load_from_cache,
475
-                'EventEspresso\core\services\request\Request' => EE_Dependency_Map::load_from_cache,
476
-            ],
477
-            'EE_Cart'                                                                                                     => [
478
-                'EE_Session' => EE_Dependency_Map::load_from_cache,
479
-            ],
480
-            'EE_Messenger_Collection_Loader'                                                                              => [
481
-                'EE_Messenger_Collection' => EE_Dependency_Map::load_new_object,
482
-            ],
483
-            'EE_Message_Type_Collection_Loader'                                                                           => [
484
-                'EE_Message_Type_Collection' => EE_Dependency_Map::load_new_object,
485
-            ],
486
-            'EE_Message_Resource_Manager'                                                                                 => [
487
-                'EE_Messenger_Collection_Loader'    => EE_Dependency_Map::load_new_object,
488
-                'EE_Message_Type_Collection_Loader' => EE_Dependency_Map::load_new_object,
489
-                'EEM_Message_Template_Group'        => EE_Dependency_Map::load_from_cache,
490
-            ],
491
-            'EE_Message_Factory'                                                                                          => [
492
-                'EE_Message_Resource_Manager' => EE_Dependency_Map::load_from_cache,
493
-            ],
494
-            'EE_messages'                                                                                                 => [
495
-                'EE_Message_Resource_Manager' => EE_Dependency_Map::load_from_cache,
496
-            ],
497
-            'EE_Messages_Generator'                                                                                       => [
498
-                'EE_Messages_Queue'                    => EE_Dependency_Map::load_new_object,
499
-                'EE_Messages_Data_Handler_Collection'  => EE_Dependency_Map::load_new_object,
500
-                'EE_Message_Template_Group_Collection' => EE_Dependency_Map::load_new_object,
501
-                'EEH_Parse_Shortcodes'                 => EE_Dependency_Map::load_from_cache,
502
-            ],
503
-            'EE_Messages_Processor'                                                                                       => [
504
-                'EE_Message_Resource_Manager' => EE_Dependency_Map::load_from_cache,
505
-            ],
506
-            'EE_Messages_Queue'                                                                                           => [
507
-                'EE_Message_Repository' => EE_Dependency_Map::load_new_object,
508
-            ],
509
-            'EE_Messages_Template_Defaults'                                                                               => [
510
-                'EEM_Message_Template_Group' => EE_Dependency_Map::load_from_cache,
511
-                'EEM_Message_Template'       => EE_Dependency_Map::load_from_cache,
512
-            ],
513
-            'EE_Message_To_Generate_From_Request'                                                                         => [
514
-                'EE_Message_Resource_Manager' => EE_Dependency_Map::load_from_cache,
515
-                'EE_Request_Handler'          => EE_Dependency_Map::load_from_cache,
516
-            ],
517
-            'EventEspresso\core\services\commands\CommandBus'                                                             => [
518
-                'EventEspresso\core\services\commands\CommandHandlerManager' => EE_Dependency_Map::load_from_cache,
519
-            ],
520
-            'EventEspresso\services\commands\CommandHandler'                                                              => [
521
-                'EE_Registry'         => EE_Dependency_Map::load_from_cache,
522
-                'CommandBusInterface' => EE_Dependency_Map::load_from_cache,
523
-            ],
524
-            'EventEspresso\core\services\commands\CommandHandlerManager'                                                  => [
525
-                'EventEspresso\core\services\loaders\Loader' => EE_Dependency_Map::load_from_cache,
526
-            ],
527
-            'EventEspresso\core\services\commands\CompositeCommandHandler'                                                => [
528
-                'EventEspresso\core\services\commands\CommandBus'     => EE_Dependency_Map::load_from_cache,
529
-                'EventEspresso\core\services\commands\CommandFactory' => EE_Dependency_Map::load_from_cache,
530
-            ],
531
-            'EventEspresso\core\services\commands\CommandFactory'                                                         => [
532
-                'EventEspresso\core\services\loaders\Loader' => EE_Dependency_Map::load_from_cache,
533
-            ],
534
-            'EventEspresso\core\services\commands\middleware\CapChecker'                                                  => [
535
-                'EventEspresso\core\domain\services\capabilities\CapabilitiesChecker' => EE_Dependency_Map::load_from_cache,
536
-            ],
537
-            'EventEspresso\core\domain\services\capabilities\CapabilitiesChecker'                                         => [
538
-                'EE_Capabilities' => EE_Dependency_Map::load_from_cache,
539
-            ],
540
-            'EventEspresso\core\domain\services\capabilities\RegistrationsCapChecker'                                     => [
541
-                'EE_Capabilities' => EE_Dependency_Map::load_from_cache,
542
-            ],
543
-            'EventEspresso\core\services\commands\registration\CreateRegistrationCommandHandler'                          => [
544
-                'EventEspresso\core\domain\services\registration\CreateRegistrationService' => EE_Dependency_Map::load_from_cache,
545
-            ],
546
-            'EventEspresso\core\services\commands\registration\CopyRegistrationDetailsCommandHandler'                     => [
547
-                'EventEspresso\core\domain\services\registration\CopyRegistrationService' => EE_Dependency_Map::load_from_cache,
548
-            ],
549
-            'EventEspresso\core\services\commands\registration\CopyRegistrationPaymentsCommandHandler'                    => [
550
-                'EventEspresso\core\domain\services\registration\CopyRegistrationService' => EE_Dependency_Map::load_from_cache,
551
-            ],
552
-            'EventEspresso\core\services\commands\registration\CancelRegistrationAndTicketLineItemCommandHandler'         => [
553
-                'EventEspresso\core\domain\services\registration\CancelTicketLineItemService' => EE_Dependency_Map::load_from_cache,
554
-            ],
555
-            'EventEspresso\core\services\commands\registration\UpdateRegistrationAndTransactionAfterChangeCommandHandler' => [
556
-                'EventEspresso\core\domain\services\registration\UpdateRegistrationService' => EE_Dependency_Map::load_from_cache,
557
-            ],
558
-            'EventEspresso\core\services\commands\ticket\CreateTicketLineItemCommandHandler'                              => [
559
-                'EventEspresso\core\domain\services\ticket\CreateTicketLineItemService' => EE_Dependency_Map::load_from_cache,
560
-            ],
561
-            'EventEspresso\core\services\commands\ticket\CancelTicketLineItemCommandHandler'                              => [
562
-                'EventEspresso\core\domain\services\ticket\CancelTicketLineItemService' => EE_Dependency_Map::load_from_cache,
563
-            ],
564
-            'EventEspresso\core\domain\services\registration\CancelRegistrationService'                                   => [
565
-                'EventEspresso\core\domain\services\ticket\CancelTicketLineItemService' => EE_Dependency_Map::load_from_cache,
566
-            ],
567
-            'EventEspresso\core\services\commands\attendee\CreateAttendeeCommandHandler'                                  => [
568
-                'EEM_Attendee' => EE_Dependency_Map::load_from_cache,
569
-            ],
570
-            'EventEspresso\core\services\database\TableManager'                                                           => [
571
-                'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
572
-            ],
573
-            'EE_Data_Migration_Class_Base'                                                                                => [
574
-                'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
575
-                'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
576
-            ],
577
-            'EE_DMS_Core_4_1_0'                                                                                           => [
578
-                'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
579
-                'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
580
-            ],
581
-            'EE_DMS_Core_4_2_0'                                                                                           => [
582
-                'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
583
-                'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
584
-            ],
585
-            'EE_DMS_Core_4_3_0'                                                                                           => [
586
-                'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
587
-                'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
588
-            ],
589
-            'EE_DMS_Core_4_4_0'                                                                                           => [
590
-                'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
591
-                'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
592
-            ],
593
-            'EE_DMS_Core_4_5_0'                                                                                           => [
594
-                'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
595
-                'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
596
-            ],
597
-            'EE_DMS_Core_4_6_0'                                                                                           => [
598
-                'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
599
-                'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
600
-            ],
601
-            'EE_DMS_Core_4_7_0'                                                                                           => [
602
-                'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
603
-                'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
604
-            ],
605
-            'EE_DMS_Core_4_8_0'                                                                                           => [
606
-                'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
607
-                'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
608
-            ],
609
-            'EE_DMS_Core_4_9_0'                                                                                           => [
610
-                'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
611
-                'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
612
-            ],
613
-            'EE_DMS_Core_4_10_0'                                                                                          => [
614
-                'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
615
-                'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
616
-                'EE_DMS_Core_4_9_0'                                  => EE_Dependency_Map::load_from_cache,
617
-            ],
618
-            'EE_DMS_Core_4_11_0'                                                                                          => [
619
-                'EE_DMS_Core_4_10_0'                                 => EE_Dependency_Map::load_from_cache,
620
-                'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
621
-                'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
622
-            ],
623
-            'EE_DMS_Core_4_12_0' => [
624
-                'EE_DMS_Core_4_11_0'                                 => EE_Dependency_Map::load_from_cache,
625
-                'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
626
-                'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
627
-            ],
628
-            'EventEspresso\core\services\assets\Registry'                                                                 => [
629
-                'EventEspresso\core\services\assets\AssetCollection' => EE_Dependency_Map::load_new_object,
630
-                'EventEspresso\core\services\assets\AssetManifest'   => EE_Dependency_Map::load_from_cache,
631
-            ],
632
-            'EventEspresso\core\services\cache\BasicCacheManager'                                                         => [
633
-                'EventEspresso\core\services\cache\TransientCacheStorage' => EE_Dependency_Map::load_from_cache,
634
-            ],
635
-            'EventEspresso\core\services\cache\PostRelatedCacheManager'                                                   => [
636
-                'EventEspresso\core\services\cache\TransientCacheStorage' => EE_Dependency_Map::load_from_cache,
637
-            ],
638
-            'EventEspresso\core\domain\services\validation\email\EmailValidationService'                                  => [
639
-                'EE_Registration_Config'                     => EE_Dependency_Map::load_from_cache,
640
-                'EventEspresso\core\services\loaders\Loader' => EE_Dependency_Map::load_from_cache,
641
-            ],
642
-            'EventEspresso\core\domain\values\EmailAddress'                                                               => [
643
-                null,
644
-                'EventEspresso\core\domain\services\validation\email\EmailValidationService' => EE_Dependency_Map::load_from_cache,
645
-            ],
646
-            'EventEspresso\core\services\orm\ModelFieldFactory'                                                           => [
647
-                'EventEspresso\core\services\loaders\Loader' => EE_Dependency_Map::load_from_cache,
648
-            ],
649
-            'LEGACY_MODELS'                                                                                               => [
650
-                null,
651
-                'EventEspresso\core\services\database\ModelFieldFactory' => EE_Dependency_Map::load_from_cache,
652
-            ],
653
-            'EE_Module_Request_Router'                                                                                    => [
654
-                'EE_Request' => EE_Dependency_Map::load_from_cache,
655
-            ],
656
-            'EE_Registration_Processor'                                                                                   => [
657
-                'EE_Request' => EE_Dependency_Map::load_from_cache,
658
-            ],
659
-            'EventEspresso\core\services\notifications\PersistentAdminNoticeManager'                                      => [
660
-                null,
661
-                'EventEspresso\core\domain\services\capabilities\CapabilitiesChecker' => EE_Dependency_Map::load_from_cache,
662
-                'EventEspresso\core\services\request\Request'                         => EE_Dependency_Map::load_from_cache,
663
-            ],
664
-            'EventEspresso\caffeinated\modules\recaptcha_invisible\InvisibleRecaptcha'                                    => [
665
-                'EE_Registration_Config' => EE_Dependency_Map::load_from_cache,
666
-                'EE_Session'             => EE_Dependency_Map::load_from_cache,
667
-            ],
668
-            'EventEspresso\modules\ticket_selector\DisplayTicketSelector'                                                 => [
669
-                'EventEspresso\core\domain\entities\users\CurrentUser' => EE_Dependency_Map::load_from_cache,
670
-            ],
671
-            'EventEspresso\modules\ticket_selector\ProcessTicketSelector'                                                 => [
672
-                'EE_Core_Config'                                                          => EE_Dependency_Map::load_from_cache,
673
-                'EventEspresso\core\services\request\Request'                             => EE_Dependency_Map::load_from_cache,
674
-                'EE_Session'                                                              => EE_Dependency_Map::load_from_cache,
675
-                'EEM_Ticket'                                                              => EE_Dependency_Map::load_from_cache,
676
-                'EventEspresso\modules\ticket_selector\TicketDatetimeAvailabilityTracker' => EE_Dependency_Map::load_from_cache,
677
-            ],
678
-            'EventEspresso\modules\ticket_selector\TicketDatetimeAvailabilityTracker'                                     => [
679
-                'EEM_Datetime' => EE_Dependency_Map::load_from_cache,
680
-            ],
681
-            'EventEspresso\core\domain\entities\custom_post_types\CustomPostTypeDefinitions'                              => [
682
-                'EE_Core_Config'                             => EE_Dependency_Map::load_from_cache,
683
-                'EventEspresso\core\services\loaders\Loader' => EE_Dependency_Map::load_from_cache,
684
-            ],
685
-            'EventEspresso\core\domain\services\custom_post_types\RegisterCustomPostTypes'                                => [
686
-                'EventEspresso\core\domain\entities\custom_post_types\CustomPostTypeDefinitions' => EE_Dependency_Map::load_from_cache,
687
-            ],
688
-            'EventEspresso\core\domain\services\custom_post_types\RegisterCustomTaxonomies'                               => [
689
-                'EventEspresso\core\domain\entities\custom_post_types\CustomTaxonomyDefinitions' => EE_Dependency_Map::load_from_cache,
690
-            ],
691
-            'EE_CPT_Strategy'                                                                                             => [
692
-                'EventEspresso\core\domain\entities\custom_post_types\CustomPostTypeDefinitions' => EE_Dependency_Map::load_from_cache,
693
-                'EventEspresso\core\domain\entities\custom_post_types\CustomTaxonomyDefinitions' => EE_Dependency_Map::load_from_cache,
694
-            ],
695
-            'EventEspresso\core\services\loaders\ObjectIdentifier'                                                        => [
696
-                'EventEspresso\core\services\loaders\ClassInterfaceCache' => EE_Dependency_Map::load_from_cache,
697
-            ],
698
-            'EventEspresso\core\CPTs\CptQueryModifier'                                                                    => [
699
-                null,
700
-                null,
701
-                null,
702
-                'EE_Request_Handler'                          => EE_Dependency_Map::load_from_cache,
703
-                'EventEspresso\core\services\request\Request' => EE_Dependency_Map::load_from_cache,
704
-                'EventEspresso\core\services\loaders\Loader'  => EE_Dependency_Map::load_from_cache,
705
-            ],
706
-            'EventEspresso\core\services\dependencies\DependencyResolver'                                                 => [
707
-                'EventEspresso\core\services\container\Mirror'            => EE_Dependency_Map::load_from_cache,
708
-                'EventEspresso\core\services\loaders\ClassInterfaceCache' => EE_Dependency_Map::load_from_cache,
709
-                'EE_Dependency_Map'                                       => EE_Dependency_Map::load_from_cache,
710
-            ],
711
-            'EventEspresso\core\services\routing\RouteMatchSpecificationDependencyResolver'                               => [
712
-                'EventEspresso\core\services\container\Mirror'            => EE_Dependency_Map::load_from_cache,
713
-                'EventEspresso\core\services\loaders\ClassInterfaceCache' => EE_Dependency_Map::load_from_cache,
714
-                'EE_Dependency_Map'                                       => EE_Dependency_Map::load_from_cache,
715
-            ],
716
-            'EventEspresso\core\services\routing\RouteMatchSpecificationFactory'                                          => [
717
-                'EventEspresso\core\services\routing\RouteMatchSpecificationDependencyResolver' => EE_Dependency_Map::load_from_cache,
718
-                'EventEspresso\core\services\loaders\Loader'                                    => EE_Dependency_Map::load_from_cache,
719
-            ],
720
-            'EventEspresso\core\services\routing\RouteMatchSpecificationManager'                                          => [
721
-                'EventEspresso\core\services\routing\RouteMatchSpecificationCollection' => EE_Dependency_Map::load_from_cache,
722
-                'EventEspresso\core\services\routing\RouteMatchSpecificationFactory'    => EE_Dependency_Map::load_from_cache,
723
-            ],
724
-            'EE_URL_Validation_Strategy'                                                                                  => [
725
-                null,
726
-                null,
727
-                'EventEspresso\core\services\validators\URLValidator' => EE_Dependency_Map::load_from_cache,
728
-            ],
729
-            'EventEspresso\core\services\request\files\FilesDataHandler'                                                  => [
730
-                'EventEspresso\core\services\request\Request' => EE_Dependency_Map::load_from_cache,
731
-            ],
732
-            'EventEspressoBatchRequest\BatchRequestProcessor'                                                             => [
733
-                'EventEspresso\core\services\loaders\Loader' => EE_Dependency_Map::load_from_cache,
734
-            ],
735
-            'EventEspresso\core\domain\services\converters\RestApiSpoofer'                                                => [
736
-                'WP_REST_Server'                                               => EE_Dependency_Map::load_from_cache,
737
-                'EED_Core_Rest_Api'                                            => EE_Dependency_Map::load_from_cache,
738
-                'EventEspresso\core\libraries\rest_api\controllers\model\Read' => EE_Dependency_Map::load_from_cache,
739
-                null,
740
-            ],
741
-            'EventEspresso\core\services\routing\RouteHandler'                                                            => [
742
-                'EventEspresso\core\services\json\JsonDataNodeHandler' => EE_Dependency_Map::load_from_cache,
743
-                'EventEspresso\core\services\loaders\Loader'           => EE_Dependency_Map::load_from_cache,
744
-                'EventEspresso\core\services\request\Request'          => EE_Dependency_Map::load_from_cache,
745
-                'EventEspresso\core\services\routing\RouteCollection'  => EE_Dependency_Map::load_from_cache,
746
-            ],
747
-            'EventEspresso\core\services\json\JsonDataNodeHandler'                                                        => [
748
-                'EventEspresso\core\services\json\JsonDataNodeValidator' => EE_Dependency_Map::load_from_cache,
749
-            ],
750
-            'EventEspresso\core\services\routing\Router'                                                                  => [
751
-                'EE_Dependency_Map'                                => EE_Dependency_Map::load_from_cache,
752
-                'EventEspresso\core\services\loaders\Loader'       => EE_Dependency_Map::load_from_cache,
753
-                'EventEspresso\core\services\routing\RouteHandler' => EE_Dependency_Map::load_from_cache,
754
-            ],
755
-            'EventEspresso\core\services\assets\AssetManifest'                                                            => [
756
-                'EventEspresso\core\domain\Domain' => EE_Dependency_Map::load_from_cache,
757
-            ],
758
-            'EventEspresso\core\services\assets\AssetManifestFactory'                                                     => [
759
-                'EventEspresso\core\services\loaders\Loader' => EE_Dependency_Map::load_from_cache,
760
-            ],
761
-            'EventEspresso\core\services\assets\BaristaFactory'                                                           => [
762
-                'EventEspresso\core\services\assets\AssetManifestFactory' => EE_Dependency_Map::load_from_cache,
763
-                'EventEspresso\core\services\loaders\Loader'              => EE_Dependency_Map::load_from_cache,
764
-            ],
765
-            'EventEspresso\core\domain\services\capabilities\FeatureFlags'                                                => [
766
-                'EventEspresso\core\domain\services\capabilities\CapabilitiesChecker' => EE_Dependency_Map::load_from_cache,
767
-            ],
768
-            'EventEspresso\core\services\addon\AddonManager' => [
769
-                'EventEspresso\core\services\addon\AddonCollection'              => EE_Dependency_Map::load_from_cache,
770
-                'EventEspresso\core\Psr4Autoloader'                              => EE_Dependency_Map::load_from_cache,
771
-                'EventEspresso\core\services\addon\api\v1\RegisterAddon'         => EE_Dependency_Map::load_from_cache,
772
-                'EventEspresso\core\services\addon\api\IncompatibleAddonHandler' => EE_Dependency_Map::load_from_cache,
773
-                'EventEspresso\core\services\addon\api\ThirdPartyPluginHandler'  => EE_Dependency_Map::load_from_cache,
774
-            ],
775
-            'EventEspresso\core\services\addon\api\ThirdPartyPluginHandler' => [
776
-                'EventEspresso\core\services\request\Request'  => EE_Dependency_Map::load_from_cache,
777
-            ],
778
-            'EventEspressoBatchRequest\JobHandlers\ExecuteBatchDeletion' => [
779
-                'EventEspresso\core\services\orm\tree_traversal\NodeGroupDao' => EE_Dependency_Map::load_from_cache
780
-            ],
781
-            'EventEspressoBatchRequest\JobHandlers\PreviewEventDeletion' => [
782
-                'EventEspresso\core\services\orm\tree_traversal\NodeGroupDao' => EE_Dependency_Map::load_from_cache
783
-            ],
784
-            'EventEspresso\core\domain\services\admin\events\data\PreviewDeletion' => [
785
-                'EventEspresso\core\services\orm\tree_traversal\NodeGroupDao' => EE_Dependency_Map::load_from_cache,
786
-                'EEM_Event' => EE_Dependency_Map::load_from_cache,
787
-                'EEM_Datetime' => EE_Dependency_Map::load_from_cache,
788
-                'EEM_Registration' => EE_Dependency_Map::load_from_cache
789
-            ],
790
-            'EventEspresso\core\domain\services\admin\events\data\ConfirmDeletion' => [
791
-                'EventEspresso\core\services\orm\tree_traversal\NodeGroupDao' => EE_Dependency_Map::load_from_cache,
792
-            ],
793
-            'EventEspresso\core\domain\entities\users\CurrentUser' => [
794
-                'EventEspresso\core\domain\entities\users\EventManagers' => EE_Dependency_Map::load_from_cache,
795
-            ],
796
-            'EventEspresso\core\services\form\meta\InputTypes' => [
797
-                'EventEspresso\core\services\form\meta\inputs\Block'   => EE_Dependency_Map::load_from_cache,
798
-                'EventEspresso\core\services\form\meta\inputs\Button'   => EE_Dependency_Map::load_from_cache,
799
-                'EventEspresso\core\services\form\meta\inputs\DateTime' => EE_Dependency_Map::load_from_cache,
800
-                'EventEspresso\core\services\form\meta\inputs\Input'    => EE_Dependency_Map::load_from_cache,
801
-                'EventEspresso\core\services\form\meta\inputs\Number'   => EE_Dependency_Map::load_from_cache,
802
-                'EventEspresso\core\services\form\meta\inputs\Phone'    => EE_Dependency_Map::load_from_cache,
803
-                'EventEspresso\core\services\form\meta\inputs\Select'   => EE_Dependency_Map::load_from_cache,
804
-                'EventEspresso\core\services\form\meta\inputs\Text'     => EE_Dependency_Map::load_from_cache,
805
-            ],
806
-            'EventEspresso\core\domain\services\registration\form\v1\RegFormDependencyHandler' => [
807
-                'EE_Dependency_Map' => EE_Dependency_Map::load_from_cache,
808
-            ],
809
-            'EventEspresso\core\services\calculators\LineItemCalculator' => [
810
-                'EventEspresso\core\services\helpers\DecimalValues' => EE_Dependency_Map::load_from_cache,
811
-            ],
812
-            'EventEspresso\core\services\helpers\DecimalValues'          => [
813
-                'EE_Currency_Config' => EE_Dependency_Map::load_from_cache,
814
-            ],
815
-        ];
816
-    }
817
-
818
-
819
-    /**
820
-     * Registers how core classes are loaded.
821
-     * This can either be done by simply providing the name of one of the EE_Registry loader methods such as:
822
-     *        'EE_Request_Handler' => 'load_core'
823
-     *        'EE_Messages_Queue'  => 'load_lib'
824
-     *        'EEH_Debug_Tools'    => 'load_helper'
825
-     * or, if greater control is required, by providing a custom closure. For example:
826
-     *        'Some_Class' => function () {
827
-     *            return new Some_Class();
828
-     *        },
829
-     * This is required for instantiating dependencies
830
-     * where an interface has been type hinted in a class constructor. For example:
831
-     *        'Required_Interface' => function () {
832
-     *            return new A_Class_That_Implements_Required_Interface();
833
-     *        },
834
-     */
835
-    protected function _register_core_class_loaders()
836
-    {
837
-        $this->_class_loaders = [
838
-            // load_core
839
-            'EE_Dependency_Map'                            => function () {
840
-                return $this;
841
-            },
842
-            'EE_Capabilities'                              => 'load_core',
843
-            'EE_Encryption'                                => 'load_core',
844
-            'EE_Front_Controller'                          => 'load_core',
845
-            'EE_Module_Request_Router'                     => 'load_core',
846
-            'EE_Registry'                                  => 'load_core',
847
-            'EE_Request'                                   => function () {
848
-                return $this->legacy_request;
849
-            },
850
-            'EventEspresso\core\services\request\Request'  => function () {
851
-                return $this->request;
852
-            },
853
-            'EventEspresso\core\services\request\Response' => function () {
854
-                return $this->response;
855
-            },
856
-            'EE_Base'                                      => 'load_core',
857
-            'EE_Request_Handler'                           => 'load_core',
858
-            'EE_Session'                                   => 'load_core',
859
-            'EE_Cron_Tasks'                                => 'load_core',
860
-            'EE_System'                                    => 'load_core',
861
-            'EE_Maintenance_Mode'                          => 'load_core',
862
-            'EE_Register_CPTs'                             => 'load_core',
863
-            'EE_Admin'                                     => 'load_core',
864
-            'EE_CPT_Strategy'                              => 'load_core',
865
-            // load_class
866
-            'EE_Registration_Processor'                    => 'load_class',
867
-            // load_lib
868
-            'EE_Message_Resource_Manager'                  => 'load_lib',
869
-            'EE_Message_Type_Collection'                   => 'load_lib',
870
-            'EE_Message_Type_Collection_Loader'            => 'load_lib',
871
-            'EE_Messenger_Collection'                      => 'load_lib',
872
-            'EE_Messenger_Collection_Loader'               => 'load_lib',
873
-            'EE_Messages_Processor'                        => 'load_lib',
874
-            'EE_Message_Repository'                        => 'load_lib',
875
-            'EE_Messages_Queue'                            => 'load_lib',
876
-            'EE_Messages_Data_Handler_Collection'          => 'load_lib',
877
-            'EE_Message_Template_Group_Collection'         => 'load_lib',
878
-            'EE_Payment_Method_Manager'                    => 'load_lib',
879
-            'EE_DMS_Core_4_1_0'                            => 'load_dms',
880
-            'EE_DMS_Core_4_2_0'                            => 'load_dms',
881
-            'EE_DMS_Core_4_3_0'                            => 'load_dms',
882
-            'EE_DMS_Core_4_5_0'                            => 'load_dms',
883
-            'EE_DMS_Core_4_6_0'                            => 'load_dms',
884
-            'EE_DMS_Core_4_7_0'                            => 'load_dms',
885
-            'EE_DMS_Core_4_8_0'                            => 'load_dms',
886
-            'EE_DMS_Core_4_9_0'                            => 'load_dms',
887
-            'EE_DMS_Core_4_10_0'                           => 'load_dms',
888
-            'EE_DMS_Core_4_11_0'                           => 'load_dms',
889
-            'EE_DMS_Core_4_12_0'                           => 'load_dms',
890
-            'EE_Messages_Generator'                        => static function () {
891
-                return EE_Registry::instance()->load_lib(
892
-                    'Messages_Generator',
893
-                    [],
894
-                    false,
895
-                    false
896
-                );
897
-            },
898
-            'EE_Messages_Template_Defaults'                => static function ($arguments = []) {
899
-                return EE_Registry::instance()->load_lib(
900
-                    'Messages_Template_Defaults',
901
-                    $arguments,
902
-                    false,
903
-                    false
904
-                );
905
-            },
906
-            // load_helper
907
-            'EEH_Parse_Shortcodes'                         => static function () {
908
-                if (EE_Registry::instance()->load_helper('Parse_Shortcodes')) {
909
-                    return new EEH_Parse_Shortcodes();
910
-                }
911
-                return null;
912
-            },
913
-            'EE_Template_Config'                           => static function () {
914
-                return EE_Config::instance()->template_settings;
915
-            },
916
-            'EE_Currency_Config'                           => static function () {
917
-                return EE_Config::instance()->currency;
918
-            },
919
-            'EE_Registration_Config'                       => static function () {
920
-                return EE_Config::instance()->registration;
921
-            },
922
-            'EE_Core_Config'                               => static function () {
923
-                return EE_Config::instance()->core;
924
-            },
925
-            'EventEspresso\core\services\loaders\Loader'   => static function () {
926
-                return LoaderFactory::getLoader();
927
-            },
928
-            'EE_Network_Config'                            => static function () {
929
-                return EE_Network_Config::instance();
930
-            },
931
-            'EE_Config'                                    => static function () {
932
-                return EE_Config::instance();
933
-            },
934
-            'EventEspresso\core\domain\Domain'             => static function () {
935
-                return DomainFactory::getEventEspressoCoreDomain();
936
-            },
937
-            'EE_Admin_Config'                              => static function () {
938
-                return EE_Config::instance()->admin;
939
-            },
940
-            'EE_Organization_Config'                       => static function () {
941
-                return EE_Config::instance()->organization;
942
-            },
943
-            'EE_Network_Core_Config'                       => static function () {
944
-                return EE_Network_Config::instance()->core;
945
-            },
946
-            'EE_Environment_Config'                        => static function () {
947
-                return EE_Config::instance()->environment;
948
-            },
949
-            'EED_Core_Rest_Api'                            => static function () {
950
-                return EED_Core_Rest_Api::instance();
951
-            },
952
-            'WP_REST_Server'                               => static function () {
953
-                return rest_get_server();
954
-            },
955
-            'EventEspresso\core\Psr4Autoloader'            => static function () {
956
-                return EE_Psr4AutoloaderInit::psr4_loader();
957
-            },
958
-        ];
959
-    }
960
-
961
-
962
-    /**
963
-     * can be used for supplying alternate names for classes,
964
-     * or for connecting interface names to instantiable classes
965
-     *
966
-     * @throws InvalidAliasException
967
-     */
968
-    protected function _register_core_aliases()
969
-    {
970
-        $aliases = [
971
-            'CommandBusInterface'                                                          => 'EventEspresso\core\services\commands\CommandBusInterface',
972
-            'EventEspresso\core\services\commands\CommandBusInterface'                     => 'EventEspresso\core\services\commands\CommandBus',
973
-            'CommandHandlerManagerInterface'                                               => 'EventEspresso\core\services\commands\CommandHandlerManagerInterface',
974
-            'EventEspresso\core\services\commands\CommandHandlerManagerInterface'          => 'EventEspresso\core\services\commands\CommandHandlerManager',
975
-            'CapChecker'                                                                   => 'EventEspresso\core\services\commands\middleware\CapChecker',
976
-            'AddActionHook'                                                                => 'EventEspresso\core\services\commands\middleware\AddActionHook',
977
-            'CapabilitiesChecker'                                                          => 'EventEspresso\core\domain\services\capabilities\CapabilitiesChecker',
978
-            'CapabilitiesCheckerInterface'                                                 => 'EventEspresso\core\domain\services\capabilities\CapabilitiesCheckerInterface',
979
-            'EventEspresso\core\domain\services\capabilities\CapabilitiesCheckerInterface' => 'EventEspresso\core\domain\services\capabilities\CapabilitiesChecker',
980
-            'CreateRegistrationService'                                                    => 'EventEspresso\core\domain\services\registration\CreateRegistrationService',
981
-            'CreateRegistrationCommandHandler'                                             => 'EventEspresso\core\services\commands\registration\CreateRegistrationCommand',
982
-            'CopyRegistrationDetailsCommandHandler'                                        => 'EventEspresso\core\services\commands\registration\CopyRegistrationDetailsCommand',
983
-            'CopyRegistrationPaymentsCommandHandler'                                       => 'EventEspresso\core\services\commands\registration\CopyRegistrationPaymentsCommand',
984
-            'CancelRegistrationAndTicketLineItemCommandHandler'                            => 'EventEspresso\core\services\commands\registration\CancelRegistrationAndTicketLineItemCommandHandler',
985
-            'UpdateRegistrationAndTransactionAfterChangeCommandHandler'                    => 'EventEspresso\core\services\commands\registration\UpdateRegistrationAndTransactionAfterChangeCommandHandler',
986
-            'CreateTicketLineItemCommandHandler'                                           => 'EventEspresso\core\services\commands\ticket\CreateTicketLineItemCommand',
987
-            'CreateTransactionCommandHandler'                                              => 'EventEspresso\core\services\commands\transaction\CreateTransactionCommandHandler',
988
-            'CreateAttendeeCommandHandler'                                                 => 'EventEspresso\core\services\commands\attendee\CreateAttendeeCommandHandler',
989
-            'TableManager'                                                                 => 'EventEspresso\core\services\database\TableManager',
990
-            'TableAnalysis'                                                                => 'EventEspresso\core\services\database\TableAnalysis',
991
-            'EspressoShortcode'                                                            => 'EventEspresso\core\services\shortcodes\EspressoShortcode',
992
-            'ShortcodeInterface'                                                           => 'EventEspresso\core\services\shortcodes\ShortcodeInterface',
993
-            'EventEspresso\core\services\shortcodes\ShortcodeInterface'                    => 'EventEspresso\core\services\shortcodes\EspressoShortcode',
994
-            'EventEspresso\core\services\cache\CacheStorageInterface'                      => 'EventEspresso\core\services\cache\TransientCacheStorage',
995
-            'LoaderInterface'                                                              => 'EventEspresso\core\services\loaders\LoaderInterface',
996
-            'EventEspresso\core\services\loaders\LoaderInterface'                          => 'EventEspresso\core\services\loaders\Loader',
997
-            'CommandFactoryInterface'                                                      => 'EventEspresso\core\services\commands\CommandFactoryInterface',
998
-            'EventEspresso\core\services\commands\CommandFactoryInterface'                 => 'EventEspresso\core\services\commands\CommandFactory',
999
-            'EmailValidatorInterface'                                                      => 'EventEspresso\core\domain\services\validation\email\EmailValidatorInterface',
1000
-            'EventEspresso\core\domain\services\validation\email\EmailValidatorInterface'  => 'EventEspresso\core\domain\services\validation\email\EmailValidationService',
1001
-            'NoticeConverterInterface'                                                     => 'EventEspresso\core\services\notices\NoticeConverterInterface',
1002
-            'EventEspresso\core\services\notices\NoticeConverterInterface'                 => 'EventEspresso\core\services\notices\ConvertNoticesToEeErrors',
1003
-            'NoticesContainerInterface'                                                    => 'EventEspresso\core\services\notices\NoticesContainerInterface',
1004
-            'EventEspresso\core\services\notices\NoticesContainerInterface'                => 'EventEspresso\core\services\notices\NoticesContainer',
1005
-            'EventEspresso\core\services\request\RequestInterface'                         => 'EventEspresso\core\services\request\Request',
1006
-            'EventEspresso\core\services\request\ResponseInterface'                        => 'EventEspresso\core\services\request\Response',
1007
-            'EventEspresso\core\domain\DomainInterface'                                    => 'EventEspresso\core\domain\Domain',
1008
-            'Registration_Processor'                                                       => 'EE_Registration_Processor',
1009
-            'EventEspresso\core\services\assets\AssetManifestInterface'                    => 'EventEspresso\core\services\assets\AssetManifest',
1010
-        ];
1011
-        foreach ($aliases as $alias => $fqn) {
1012
-            if (is_array($fqn)) {
1013
-                foreach ($fqn as $class => $for_class) {
1014
-                    $this->class_cache->addAlias($class, $alias, $for_class);
1015
-                }
1016
-                continue;
1017
-            }
1018
-            $this->class_cache->addAlias($fqn, $alias);
1019
-        }
1020
-        if (! (defined('DOING_AJAX') && DOING_AJAX) && is_admin()) {
1021
-            $this->class_cache->addAlias(
1022
-                'EventEspresso\core\services\notices\ConvertNoticesToAdminNotices',
1023
-                'EventEspresso\core\services\notices\NoticeConverterInterface'
1024
-            );
1025
-        }
1026
-    }
1027
-
1028
-
1029
-    /**
1030
-     * This is used to reset the internal map and class_loaders to their original default state at the beginning of the
1031
-     * request Primarily used by unit tests.
1032
-     */
1033
-    public function reset()
1034
-    {
1035
-        $this->_register_core_class_loaders();
1036
-        $this->_register_core_dependencies();
1037
-    }
1038
-
1039
-
1040
-    /**
1041
-     * PLZ NOTE: a better name for this method would be is_alias()
1042
-     * because it returns TRUE if the provided fully qualified name IS an alias
1043
-     * WHY?
1044
-     * Because if a class is type hinting for a concretion,
1045
-     * then why would we need to find another class to supply it?
1046
-     * ie: if a class asks for `Fully/Qualified/Namespace/SpecificClassName`,
1047
-     * then give it an instance of `Fully/Qualified/Namespace/SpecificClassName`.
1048
-     * Don't go looking for some substitute.
1049
-     * Whereas if a class is type hinting for an interface...
1050
-     * then we need to find an actual class to use.
1051
-     * So the interface IS the alias for some other FQN,
1052
-     * and we need to find out if `Fully/Qualified/Namespace/SomeInterface`
1053
-     * represents some other class.
1054
-     *
1055
-     * @param string $fqn
1056
-     * @param string $for_class
1057
-     * @return bool
1058
-     * @deprecated 4.9.62.p
1059
-     */
1060
-    public function has_alias($fqn = '', $for_class = '')
1061
-    {
1062
-        return $this->isAlias($fqn, $for_class);
1063
-    }
1064
-
1065
-
1066
-    /**
1067
-     * PLZ NOTE: a better name for this method would be get_fqn_for_alias()
1068
-     * because it returns a FQN for provided alias if one exists, otherwise returns the original $alias
1069
-     * functions recursively, so that multiple aliases can be used to drill down to a FQN
1070
-     *  for example:
1071
-     *      if the following two entries were added to the _aliases array:
1072
-     *          array(
1073
-     *              'interface_alias'           => 'some\namespace\interface'
1074
-     *              'some\namespace\interface'  => 'some\namespace\classname'
1075
-     *          )
1076
-     *      then one could use EE_Registry::instance()->create( 'interface_alias' )
1077
-     *      to load an instance of 'some\namespace\classname'
1078
-     *
1079
-     * @param string $alias
1080
-     * @param string $for_class
1081
-     * @return string
1082
-     * @deprecated 4.9.62.p
1083
-     */
1084
-    public function get_alias($alias = '', $for_class = '')
1085
-    {
1086
-        return $this->getFqnForAlias($alias, $for_class);
1087
-    }
25
+	/**
26
+	 * This means that the requested class dependency is not present in the dependency map
27
+	 */
28
+	const not_registered = 0;
29
+
30
+	/**
31
+	 * This instructs class loaders to ALWAYS return a newly instantiated object for the requested class.
32
+	 */
33
+	const load_new_object = 1;
34
+
35
+	/**
36
+	 * This instructs class loaders to return a previously instantiated and cached object for the requested class.
37
+	 * IF a previously instantiated object does not exist, a new one will be created and added to the cache.
38
+	 */
39
+	const load_from_cache = 2;
40
+
41
+	/**
42
+	 * When registering a dependency,
43
+	 * this indicates to keep any existing dependencies that already exist,
44
+	 * and simply discard any new dependencies declared in the incoming data
45
+	 */
46
+	const KEEP_EXISTING_DEPENDENCIES = 0;
47
+
48
+	/**
49
+	 * When registering a dependency,
50
+	 * this indicates to overwrite any existing dependencies that already exist using the incoming data
51
+	 */
52
+	const OVERWRITE_DEPENDENCIES = 1;
53
+
54
+	/**
55
+	 * @type EE_Dependency_Map $_instance
56
+	 */
57
+	protected static $_instance;
58
+
59
+	/**
60
+	 * @var ClassInterfaceCache $class_cache
61
+	 */
62
+	private $class_cache;
63
+
64
+	/**
65
+	 * @type RequestInterface $request
66
+	 */
67
+	protected $request;
68
+
69
+	/**
70
+	 * @type LegacyRequestInterface $legacy_request
71
+	 */
72
+	protected $legacy_request;
73
+
74
+	/**
75
+	 * @type ResponseInterface $response
76
+	 */
77
+	protected $response;
78
+
79
+	/**
80
+	 * @type LoaderInterface $loader
81
+	 */
82
+	protected $loader;
83
+
84
+	/**
85
+	 * @type array $_dependency_map
86
+	 */
87
+	protected $_dependency_map = [];
88
+
89
+	/**
90
+	 * @type array $_class_loaders
91
+	 */
92
+	protected $_class_loaders = [];
93
+
94
+
95
+	/**
96
+	 * EE_Dependency_Map constructor.
97
+	 *
98
+	 * @param ClassInterfaceCache $class_cache
99
+	 */
100
+	protected function __construct(ClassInterfaceCache $class_cache)
101
+	{
102
+		$this->class_cache = $class_cache;
103
+		do_action('EE_Dependency_Map____construct', $this);
104
+	}
105
+
106
+
107
+	/**
108
+	 * @return void
109
+	 * @throws InvalidAliasException
110
+	 */
111
+	public function initialize()
112
+	{
113
+		$this->_register_core_dependencies();
114
+		$this->_register_core_class_loaders();
115
+		$this->_register_core_aliases();
116
+	}
117
+
118
+
119
+	/**
120
+	 * @singleton method used to instantiate class object
121
+	 * @param ClassInterfaceCache|null $class_cache
122
+	 * @return EE_Dependency_Map
123
+	 */
124
+	public static function instance(ClassInterfaceCache $class_cache = null)
125
+	{
126
+		// check if class object is instantiated, and instantiated properly
127
+		if (
128
+			! EE_Dependency_Map::$_instance instanceof EE_Dependency_Map
129
+			&& $class_cache instanceof ClassInterfaceCache
130
+		) {
131
+			EE_Dependency_Map::$_instance = new EE_Dependency_Map($class_cache);
132
+		}
133
+		return EE_Dependency_Map::$_instance;
134
+	}
135
+
136
+
137
+	/**
138
+	 * @param RequestInterface $request
139
+	 */
140
+	public function setRequest(RequestInterface $request)
141
+	{
142
+		$this->request = $request;
143
+	}
144
+
145
+
146
+	/**
147
+	 * @param LegacyRequestInterface $legacy_request
148
+	 */
149
+	public function setLegacyRequest(LegacyRequestInterface $legacy_request)
150
+	{
151
+		$this->legacy_request = $legacy_request;
152
+	}
153
+
154
+
155
+	/**
156
+	 * @param ResponseInterface $response
157
+	 */
158
+	public function setResponse(ResponseInterface $response)
159
+	{
160
+		$this->response = $response;
161
+	}
162
+
163
+
164
+	/**
165
+	 * @param LoaderInterface $loader
166
+	 */
167
+	public function setLoader(LoaderInterface $loader)
168
+	{
169
+		$this->loader = $loader;
170
+	}
171
+
172
+
173
+	/**
174
+	 * @param string $class
175
+	 * @param array  $dependencies
176
+	 * @param int    $overwrite
177
+	 * @return bool
178
+	 */
179
+	public static function register_dependencies(
180
+		$class,
181
+		array $dependencies,
182
+		$overwrite = EE_Dependency_Map::KEEP_EXISTING_DEPENDENCIES
183
+	) {
184
+		return EE_Dependency_Map::$_instance->registerDependencies($class, $dependencies, $overwrite);
185
+	}
186
+
187
+
188
+	/**
189
+	 * Assigns an array of class names and corresponding load sources (new or cached)
190
+	 * to the class specified by the first parameter.
191
+	 * IMPORTANT !!!
192
+	 * The order of elements in the incoming $dependencies array MUST match
193
+	 * the order of the constructor parameters for the class in question.
194
+	 * This is especially important when overriding any existing dependencies that are registered.
195
+	 * the third parameter controls whether any duplicate dependencies are overwritten or not.
196
+	 *
197
+	 * @param string $class
198
+	 * @param array  $dependencies
199
+	 * @param int    $overwrite
200
+	 * @return bool
201
+	 */
202
+	public function registerDependencies(
203
+		$class,
204
+		array $dependencies,
205
+		$overwrite = EE_Dependency_Map::KEEP_EXISTING_DEPENDENCIES
206
+	) {
207
+		$class      = trim($class, '\\');
208
+		$registered = false;
209
+		if (empty(EE_Dependency_Map::$_instance->_dependency_map[ $class ])) {
210
+			EE_Dependency_Map::$_instance->_dependency_map[ $class ] = [];
211
+		}
212
+		// we need to make sure that any aliases used when registering a dependency
213
+		// get resolved to the correct class name
214
+		foreach ($dependencies as $dependency => $load_source) {
215
+			$alias = EE_Dependency_Map::$_instance->getFqnForAlias($dependency);
216
+			if (
217
+				$overwrite === EE_Dependency_Map::OVERWRITE_DEPENDENCIES
218
+				|| ! isset(EE_Dependency_Map::$_instance->_dependency_map[ $class ][ $alias ])
219
+			) {
220
+				unset($dependencies[ $dependency ]);
221
+				$dependencies[ $alias ] = $load_source;
222
+				$registered             = true;
223
+			}
224
+		}
225
+		// now add our two lists of dependencies together.
226
+		// using Union (+=) favours the arrays in precedence from left to right,
227
+		// so $dependencies is NOT overwritten because it is listed first
228
+		// ie: with A = B + C, entries in B take precedence over duplicate entries in C
229
+		// Union is way faster than array_merge() but should be used with caution...
230
+		// especially with numerically indexed arrays
231
+		$dependencies += EE_Dependency_Map::$_instance->_dependency_map[ $class ];
232
+		// now we need to ensure that the resulting dependencies
233
+		// array only has the entries that are required for the class
234
+		// so first count how many dependencies were originally registered for the class
235
+		$dependency_count = count(EE_Dependency_Map::$_instance->_dependency_map[ $class ]);
236
+		// if that count is non-zero (meaning dependencies were already registered)
237
+		EE_Dependency_Map::$_instance->_dependency_map[ $class ] = $dependency_count
238
+			// then truncate the  final array to match that count
239
+			? array_slice($dependencies, 0, $dependency_count)
240
+			// otherwise just take the incoming array because nothing previously existed
241
+			: $dependencies;
242
+		return $registered;
243
+	}
244
+
245
+
246
+	/**
247
+	 * @param string $class_name
248
+	 * @param string $loader
249
+	 * @return bool
250
+	 * @throws DomainException
251
+	 */
252
+	public static function register_class_loader($class_name, $loader = 'load_core')
253
+	{
254
+		return EE_Dependency_Map::$_instance->registerClassLoader($class_name, $loader);
255
+	}
256
+
257
+
258
+	/**
259
+	 * @param string $class_name
260
+	 * @param string $loader
261
+	 * @return bool
262
+	 * @throws DomainException
263
+	 */
264
+	public function registerClassLoader($class_name, $loader = 'load_core')
265
+	{
266
+		if (! $loader instanceof Closure && strpos($class_name, '\\') !== false) {
267
+			throw new DomainException(
268
+				esc_html__('Don\'t use class loaders for FQCNs.', 'event_espresso')
269
+			);
270
+		}
271
+		// check that loader is callable or method starts with "load_" and exists in EE_Registry
272
+		if (
273
+			! is_callable($loader)
274
+			&& (
275
+				strpos($loader, 'load_') !== 0
276
+				|| ! method_exists('EE_Registry', $loader)
277
+			)
278
+		) {
279
+			throw new DomainException(
280
+				sprintf(
281
+					esc_html__(
282
+						'"%1$s" is not a valid loader method on EE_Registry.',
283
+						'event_espresso'
284
+					),
285
+					$loader
286
+				)
287
+			);
288
+		}
289
+		$class_name = EE_Dependency_Map::$_instance->getFqnForAlias($class_name);
290
+		if (! isset(EE_Dependency_Map::$_instance->_class_loaders[ $class_name ])) {
291
+			EE_Dependency_Map::$_instance->_class_loaders[ $class_name ] = $loader;
292
+			return true;
293
+		}
294
+		return false;
295
+	}
296
+
297
+
298
+	/**
299
+	 * @return array
300
+	 */
301
+	public function dependency_map()
302
+	{
303
+		return $this->_dependency_map;
304
+	}
305
+
306
+
307
+	/**
308
+	 * returns TRUE if dependency map contains a listing for the provided class name
309
+	 *
310
+	 * @param string $class_name
311
+	 * @return boolean
312
+	 */
313
+	public function has($class_name = '')
314
+	{
315
+		// all legacy models have the same dependencies
316
+		if (strpos($class_name, 'EEM_') === 0) {
317
+			$class_name = 'LEGACY_MODELS';
318
+		}
319
+		return isset($this->_dependency_map[ $class_name ]);
320
+	}
321
+
322
+
323
+	/**
324
+	 * returns TRUE if dependency map contains a listing for the provided class name AND dependency
325
+	 *
326
+	 * @param string $class_name
327
+	 * @param string $dependency
328
+	 * @return bool
329
+	 */
330
+	public function has_dependency_for_class($class_name = '', $dependency = '')
331
+	{
332
+		// all legacy models have the same dependencies
333
+		if (strpos($class_name, 'EEM_') === 0) {
334
+			$class_name = 'LEGACY_MODELS';
335
+		}
336
+		$dependency = $this->getFqnForAlias($dependency, $class_name);
337
+		return isset($this->_dependency_map[ $class_name ][ $dependency ]);
338
+	}
339
+
340
+
341
+	/**
342
+	 * returns loading strategy for whether a previously cached dependency should be loaded or a new instance returned
343
+	 *
344
+	 * @param string $class_name
345
+	 * @param string $dependency
346
+	 * @return int
347
+	 */
348
+	public function loading_strategy_for_class_dependency($class_name = '', $dependency = '')
349
+	{
350
+		// all legacy models have the same dependencies
351
+		if (strpos($class_name, 'EEM_') === 0) {
352
+			$class_name = 'LEGACY_MODELS';
353
+		}
354
+		$dependency = $this->getFqnForAlias($dependency);
355
+		return $this->has_dependency_for_class($class_name, $dependency)
356
+			? $this->_dependency_map[ $class_name ][ $dependency ]
357
+			: EE_Dependency_Map::not_registered;
358
+	}
359
+
360
+
361
+	/**
362
+	 * @param string $class_name
363
+	 * @return string | Closure
364
+	 */
365
+	public function class_loader($class_name)
366
+	{
367
+		// all legacy models use load_model()
368
+		if (strpos($class_name, 'EEM_') === 0) {
369
+			return 'load_model';
370
+		}
371
+		// EE_CPT_*_Strategy classes like EE_CPT_Event_Strategy, EE_CPT_Venue_Strategy, etc
372
+		// perform strpos() first to avoid loading regex every time we load a class
373
+		if (
374
+			strpos($class_name, 'EE_CPT_') === 0
375
+			&& preg_match('/^EE_CPT_([a-zA-Z]+)_Strategy$/', $class_name)
376
+		) {
377
+			return 'load_core';
378
+		}
379
+		$class_name = $this->getFqnForAlias($class_name);
380
+		return isset($this->_class_loaders[ $class_name ]) ? $this->_class_loaders[ $class_name ] : '';
381
+	}
382
+
383
+
384
+	/**
385
+	 * @return array
386
+	 */
387
+	public function class_loaders()
388
+	{
389
+		return $this->_class_loaders;
390
+	}
391
+
392
+
393
+	/**
394
+	 * adds an alias for a classname
395
+	 *
396
+	 * @param string $fqcn      the class name that should be used (concrete class to replace interface)
397
+	 * @param string $alias     the class name that would be type hinted for (abstract parent or interface)
398
+	 * @param string $for_class the class that has the dependency (is type hinting for the interface)
399
+	 * @throws InvalidAliasException
400
+	 */
401
+	public function add_alias($fqcn, $alias, $for_class = '')
402
+	{
403
+		$this->class_cache->addAlias($fqcn, $alias, $for_class);
404
+	}
405
+
406
+
407
+	/**
408
+	 * Returns TRUE if the provided fully qualified name IS an alias
409
+	 * WHY?
410
+	 * Because if a class is type hinting for a concretion,
411
+	 * then why would we need to find another class to supply it?
412
+	 * ie: if a class asks for `Fully/Qualified/Namespace/SpecificClassName`,
413
+	 * then give it an instance of `Fully/Qualified/Namespace/SpecificClassName`.
414
+	 * Don't go looking for some substitute.
415
+	 * Whereas if a class is type hinting for an interface...
416
+	 * then we need to find an actual class to use.
417
+	 * So the interface IS the alias for some other FQN,
418
+	 * and we need to find out if `Fully/Qualified/Namespace/SomeInterface`
419
+	 * represents some other class.
420
+	 *
421
+	 * @param string $fqn
422
+	 * @param string $for_class
423
+	 * @return bool
424
+	 */
425
+	public function isAlias($fqn = '', $for_class = '')
426
+	{
427
+		return $this->class_cache->isAlias($fqn, $for_class);
428
+	}
429
+
430
+
431
+	/**
432
+	 * Returns a FQN for provided alias if one exists, otherwise returns the original $alias
433
+	 * functions recursively, so that multiple aliases can be used to drill down to a FQN
434
+	 *  for example:
435
+	 *      if the following two entries were added to the _aliases array:
436
+	 *          array(
437
+	 *              'interface_alias'           => 'some\namespace\interface'
438
+	 *              'some\namespace\interface'  => 'some\namespace\classname'
439
+	 *          )
440
+	 *      then one could use EE_Registry::instance()->create( 'interface_alias' )
441
+	 *      to load an instance of 'some\namespace\classname'
442
+	 *
443
+	 * @param string $alias
444
+	 * @param string $for_class
445
+	 * @return string
446
+	 */
447
+	public function getFqnForAlias($alias = '', $for_class = '')
448
+	{
449
+		return (string) $this->class_cache->getFqnForAlias($alias, $for_class);
450
+	}
451
+
452
+
453
+	/**
454
+	 * Registers the core dependencies and whether a previously instantiated object should be loaded from the cache,
455
+	 * if one exists, or whether a new object should be generated every time the requested class is loaded.
456
+	 * This is done by using the following class constants:
457
+	 *        EE_Dependency_Map::load_from_cache - loads previously instantiated object
458
+	 *        EE_Dependency_Map::load_new_object - generates a new object every time
459
+	 */
460
+	protected function _register_core_dependencies()
461
+	{
462
+		$this->_dependency_map = [
463
+			'EE_Request_Handler'                                                                                          => [
464
+				'EE_Request' => EE_Dependency_Map::load_from_cache,
465
+			],
466
+			'EE_System'                                                                                                   => [
467
+				'EventEspresso\core\services\loaders\Loader'  => EE_Dependency_Map::load_from_cache,
468
+				'EE_Maintenance_Mode'                         => EE_Dependency_Map::load_from_cache,
469
+				'EE_Registry'                                 => EE_Dependency_Map::load_from_cache,
470
+				'EventEspresso\core\services\request\Request' => EE_Dependency_Map::load_from_cache,
471
+				'EventEspresso\core\services\routing\Router'  => EE_Dependency_Map::load_from_cache,
472
+			],
473
+			'EE_Admin'                                                                                                    => [
474
+				'EventEspresso\core\services\loaders\Loader'  => EE_Dependency_Map::load_from_cache,
475
+				'EventEspresso\core\services\request\Request' => EE_Dependency_Map::load_from_cache,
476
+			],
477
+			'EE_Cart'                                                                                                     => [
478
+				'EE_Session' => EE_Dependency_Map::load_from_cache,
479
+			],
480
+			'EE_Messenger_Collection_Loader'                                                                              => [
481
+				'EE_Messenger_Collection' => EE_Dependency_Map::load_new_object,
482
+			],
483
+			'EE_Message_Type_Collection_Loader'                                                                           => [
484
+				'EE_Message_Type_Collection' => EE_Dependency_Map::load_new_object,
485
+			],
486
+			'EE_Message_Resource_Manager'                                                                                 => [
487
+				'EE_Messenger_Collection_Loader'    => EE_Dependency_Map::load_new_object,
488
+				'EE_Message_Type_Collection_Loader' => EE_Dependency_Map::load_new_object,
489
+				'EEM_Message_Template_Group'        => EE_Dependency_Map::load_from_cache,
490
+			],
491
+			'EE_Message_Factory'                                                                                          => [
492
+				'EE_Message_Resource_Manager' => EE_Dependency_Map::load_from_cache,
493
+			],
494
+			'EE_messages'                                                                                                 => [
495
+				'EE_Message_Resource_Manager' => EE_Dependency_Map::load_from_cache,
496
+			],
497
+			'EE_Messages_Generator'                                                                                       => [
498
+				'EE_Messages_Queue'                    => EE_Dependency_Map::load_new_object,
499
+				'EE_Messages_Data_Handler_Collection'  => EE_Dependency_Map::load_new_object,
500
+				'EE_Message_Template_Group_Collection' => EE_Dependency_Map::load_new_object,
501
+				'EEH_Parse_Shortcodes'                 => EE_Dependency_Map::load_from_cache,
502
+			],
503
+			'EE_Messages_Processor'                                                                                       => [
504
+				'EE_Message_Resource_Manager' => EE_Dependency_Map::load_from_cache,
505
+			],
506
+			'EE_Messages_Queue'                                                                                           => [
507
+				'EE_Message_Repository' => EE_Dependency_Map::load_new_object,
508
+			],
509
+			'EE_Messages_Template_Defaults'                                                                               => [
510
+				'EEM_Message_Template_Group' => EE_Dependency_Map::load_from_cache,
511
+				'EEM_Message_Template'       => EE_Dependency_Map::load_from_cache,
512
+			],
513
+			'EE_Message_To_Generate_From_Request'                                                                         => [
514
+				'EE_Message_Resource_Manager' => EE_Dependency_Map::load_from_cache,
515
+				'EE_Request_Handler'          => EE_Dependency_Map::load_from_cache,
516
+			],
517
+			'EventEspresso\core\services\commands\CommandBus'                                                             => [
518
+				'EventEspresso\core\services\commands\CommandHandlerManager' => EE_Dependency_Map::load_from_cache,
519
+			],
520
+			'EventEspresso\services\commands\CommandHandler'                                                              => [
521
+				'EE_Registry'         => EE_Dependency_Map::load_from_cache,
522
+				'CommandBusInterface' => EE_Dependency_Map::load_from_cache,
523
+			],
524
+			'EventEspresso\core\services\commands\CommandHandlerManager'                                                  => [
525
+				'EventEspresso\core\services\loaders\Loader' => EE_Dependency_Map::load_from_cache,
526
+			],
527
+			'EventEspresso\core\services\commands\CompositeCommandHandler'                                                => [
528
+				'EventEspresso\core\services\commands\CommandBus'     => EE_Dependency_Map::load_from_cache,
529
+				'EventEspresso\core\services\commands\CommandFactory' => EE_Dependency_Map::load_from_cache,
530
+			],
531
+			'EventEspresso\core\services\commands\CommandFactory'                                                         => [
532
+				'EventEspresso\core\services\loaders\Loader' => EE_Dependency_Map::load_from_cache,
533
+			],
534
+			'EventEspresso\core\services\commands\middleware\CapChecker'                                                  => [
535
+				'EventEspresso\core\domain\services\capabilities\CapabilitiesChecker' => EE_Dependency_Map::load_from_cache,
536
+			],
537
+			'EventEspresso\core\domain\services\capabilities\CapabilitiesChecker'                                         => [
538
+				'EE_Capabilities' => EE_Dependency_Map::load_from_cache,
539
+			],
540
+			'EventEspresso\core\domain\services\capabilities\RegistrationsCapChecker'                                     => [
541
+				'EE_Capabilities' => EE_Dependency_Map::load_from_cache,
542
+			],
543
+			'EventEspresso\core\services\commands\registration\CreateRegistrationCommandHandler'                          => [
544
+				'EventEspresso\core\domain\services\registration\CreateRegistrationService' => EE_Dependency_Map::load_from_cache,
545
+			],
546
+			'EventEspresso\core\services\commands\registration\CopyRegistrationDetailsCommandHandler'                     => [
547
+				'EventEspresso\core\domain\services\registration\CopyRegistrationService' => EE_Dependency_Map::load_from_cache,
548
+			],
549
+			'EventEspresso\core\services\commands\registration\CopyRegistrationPaymentsCommandHandler'                    => [
550
+				'EventEspresso\core\domain\services\registration\CopyRegistrationService' => EE_Dependency_Map::load_from_cache,
551
+			],
552
+			'EventEspresso\core\services\commands\registration\CancelRegistrationAndTicketLineItemCommandHandler'         => [
553
+				'EventEspresso\core\domain\services\registration\CancelTicketLineItemService' => EE_Dependency_Map::load_from_cache,
554
+			],
555
+			'EventEspresso\core\services\commands\registration\UpdateRegistrationAndTransactionAfterChangeCommandHandler' => [
556
+				'EventEspresso\core\domain\services\registration\UpdateRegistrationService' => EE_Dependency_Map::load_from_cache,
557
+			],
558
+			'EventEspresso\core\services\commands\ticket\CreateTicketLineItemCommandHandler'                              => [
559
+				'EventEspresso\core\domain\services\ticket\CreateTicketLineItemService' => EE_Dependency_Map::load_from_cache,
560
+			],
561
+			'EventEspresso\core\services\commands\ticket\CancelTicketLineItemCommandHandler'                              => [
562
+				'EventEspresso\core\domain\services\ticket\CancelTicketLineItemService' => EE_Dependency_Map::load_from_cache,
563
+			],
564
+			'EventEspresso\core\domain\services\registration\CancelRegistrationService'                                   => [
565
+				'EventEspresso\core\domain\services\ticket\CancelTicketLineItemService' => EE_Dependency_Map::load_from_cache,
566
+			],
567
+			'EventEspresso\core\services\commands\attendee\CreateAttendeeCommandHandler'                                  => [
568
+				'EEM_Attendee' => EE_Dependency_Map::load_from_cache,
569
+			],
570
+			'EventEspresso\core\services\database\TableManager'                                                           => [
571
+				'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
572
+			],
573
+			'EE_Data_Migration_Class_Base'                                                                                => [
574
+				'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
575
+				'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
576
+			],
577
+			'EE_DMS_Core_4_1_0'                                                                                           => [
578
+				'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
579
+				'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
580
+			],
581
+			'EE_DMS_Core_4_2_0'                                                                                           => [
582
+				'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
583
+				'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
584
+			],
585
+			'EE_DMS_Core_4_3_0'                                                                                           => [
586
+				'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
587
+				'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
588
+			],
589
+			'EE_DMS_Core_4_4_0'                                                                                           => [
590
+				'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
591
+				'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
592
+			],
593
+			'EE_DMS_Core_4_5_0'                                                                                           => [
594
+				'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
595
+				'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
596
+			],
597
+			'EE_DMS_Core_4_6_0'                                                                                           => [
598
+				'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
599
+				'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
600
+			],
601
+			'EE_DMS_Core_4_7_0'                                                                                           => [
602
+				'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
603
+				'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
604
+			],
605
+			'EE_DMS_Core_4_8_0'                                                                                           => [
606
+				'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
607
+				'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
608
+			],
609
+			'EE_DMS_Core_4_9_0'                                                                                           => [
610
+				'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
611
+				'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
612
+			],
613
+			'EE_DMS_Core_4_10_0'                                                                                          => [
614
+				'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
615
+				'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
616
+				'EE_DMS_Core_4_9_0'                                  => EE_Dependency_Map::load_from_cache,
617
+			],
618
+			'EE_DMS_Core_4_11_0'                                                                                          => [
619
+				'EE_DMS_Core_4_10_0'                                 => EE_Dependency_Map::load_from_cache,
620
+				'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
621
+				'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
622
+			],
623
+			'EE_DMS_Core_4_12_0' => [
624
+				'EE_DMS_Core_4_11_0'                                 => EE_Dependency_Map::load_from_cache,
625
+				'EventEspresso\core\services\database\TableAnalysis' => EE_Dependency_Map::load_from_cache,
626
+				'EventEspresso\core\services\database\TableManager'  => EE_Dependency_Map::load_from_cache,
627
+			],
628
+			'EventEspresso\core\services\assets\Registry'                                                                 => [
629
+				'EventEspresso\core\services\assets\AssetCollection' => EE_Dependency_Map::load_new_object,
630
+				'EventEspresso\core\services\assets\AssetManifest'   => EE_Dependency_Map::load_from_cache,
631
+			],
632
+			'EventEspresso\core\services\cache\BasicCacheManager'                                                         => [
633
+				'EventEspresso\core\services\cache\TransientCacheStorage' => EE_Dependency_Map::load_from_cache,
634
+			],
635
+			'EventEspresso\core\services\cache\PostRelatedCacheManager'                                                   => [
636
+				'EventEspresso\core\services\cache\TransientCacheStorage' => EE_Dependency_Map::load_from_cache,
637
+			],
638
+			'EventEspresso\core\domain\services\validation\email\EmailValidationService'                                  => [
639
+				'EE_Registration_Config'                     => EE_Dependency_Map::load_from_cache,
640
+				'EventEspresso\core\services\loaders\Loader' => EE_Dependency_Map::load_from_cache,
641
+			],
642
+			'EventEspresso\core\domain\values\EmailAddress'                                                               => [
643
+				null,
644
+				'EventEspresso\core\domain\services\validation\email\EmailValidationService' => EE_Dependency_Map::load_from_cache,
645
+			],
646
+			'EventEspresso\core\services\orm\ModelFieldFactory'                                                           => [
647
+				'EventEspresso\core\services\loaders\Loader' => EE_Dependency_Map::load_from_cache,
648
+			],
649
+			'LEGACY_MODELS'                                                                                               => [
650
+				null,
651
+				'EventEspresso\core\services\database\ModelFieldFactory' => EE_Dependency_Map::load_from_cache,
652
+			],
653
+			'EE_Module_Request_Router'                                                                                    => [
654
+				'EE_Request' => EE_Dependency_Map::load_from_cache,
655
+			],
656
+			'EE_Registration_Processor'                                                                                   => [
657
+				'EE_Request' => EE_Dependency_Map::load_from_cache,
658
+			],
659
+			'EventEspresso\core\services\notifications\PersistentAdminNoticeManager'                                      => [
660
+				null,
661
+				'EventEspresso\core\domain\services\capabilities\CapabilitiesChecker' => EE_Dependency_Map::load_from_cache,
662
+				'EventEspresso\core\services\request\Request'                         => EE_Dependency_Map::load_from_cache,
663
+			],
664
+			'EventEspresso\caffeinated\modules\recaptcha_invisible\InvisibleRecaptcha'                                    => [
665
+				'EE_Registration_Config' => EE_Dependency_Map::load_from_cache,
666
+				'EE_Session'             => EE_Dependency_Map::load_from_cache,
667
+			],
668
+			'EventEspresso\modules\ticket_selector\DisplayTicketSelector'                                                 => [
669
+				'EventEspresso\core\domain\entities\users\CurrentUser' => EE_Dependency_Map::load_from_cache,
670
+			],
671
+			'EventEspresso\modules\ticket_selector\ProcessTicketSelector'                                                 => [
672
+				'EE_Core_Config'                                                          => EE_Dependency_Map::load_from_cache,
673
+				'EventEspresso\core\services\request\Request'                             => EE_Dependency_Map::load_from_cache,
674
+				'EE_Session'                                                              => EE_Dependency_Map::load_from_cache,
675
+				'EEM_Ticket'                                                              => EE_Dependency_Map::load_from_cache,
676
+				'EventEspresso\modules\ticket_selector\TicketDatetimeAvailabilityTracker' => EE_Dependency_Map::load_from_cache,
677
+			],
678
+			'EventEspresso\modules\ticket_selector\TicketDatetimeAvailabilityTracker'                                     => [
679
+				'EEM_Datetime' => EE_Dependency_Map::load_from_cache,
680
+			],
681
+			'EventEspresso\core\domain\entities\custom_post_types\CustomPostTypeDefinitions'                              => [
682
+				'EE_Core_Config'                             => EE_Dependency_Map::load_from_cache,
683
+				'EventEspresso\core\services\loaders\Loader' => EE_Dependency_Map::load_from_cache,
684
+			],
685
+			'EventEspresso\core\domain\services\custom_post_types\RegisterCustomPostTypes'                                => [
686
+				'EventEspresso\core\domain\entities\custom_post_types\CustomPostTypeDefinitions' => EE_Dependency_Map::load_from_cache,
687
+			],
688
+			'EventEspresso\core\domain\services\custom_post_types\RegisterCustomTaxonomies'                               => [
689
+				'EventEspresso\core\domain\entities\custom_post_types\CustomTaxonomyDefinitions' => EE_Dependency_Map::load_from_cache,
690
+			],
691
+			'EE_CPT_Strategy'                                                                                             => [
692
+				'EventEspresso\core\domain\entities\custom_post_types\CustomPostTypeDefinitions' => EE_Dependency_Map::load_from_cache,
693
+				'EventEspresso\core\domain\entities\custom_post_types\CustomTaxonomyDefinitions' => EE_Dependency_Map::load_from_cache,
694
+			],
695
+			'EventEspresso\core\services\loaders\ObjectIdentifier'                                                        => [
696
+				'EventEspresso\core\services\loaders\ClassInterfaceCache' => EE_Dependency_Map::load_from_cache,
697
+			],
698
+			'EventEspresso\core\CPTs\CptQueryModifier'                                                                    => [
699
+				null,
700
+				null,
701
+				null,
702
+				'EE_Request_Handler'                          => EE_Dependency_Map::load_from_cache,
703
+				'EventEspresso\core\services\request\Request' => EE_Dependency_Map::load_from_cache,
704
+				'EventEspresso\core\services\loaders\Loader'  => EE_Dependency_Map::load_from_cache,
705
+			],
706
+			'EventEspresso\core\services\dependencies\DependencyResolver'                                                 => [
707
+				'EventEspresso\core\services\container\Mirror'            => EE_Dependency_Map::load_from_cache,
708
+				'EventEspresso\core\services\loaders\ClassInterfaceCache' => EE_Dependency_Map::load_from_cache,
709
+				'EE_Dependency_Map'                                       => EE_Dependency_Map::load_from_cache,
710
+			],
711
+			'EventEspresso\core\services\routing\RouteMatchSpecificationDependencyResolver'                               => [
712
+				'EventEspresso\core\services\container\Mirror'            => EE_Dependency_Map::load_from_cache,
713
+				'EventEspresso\core\services\loaders\ClassInterfaceCache' => EE_Dependency_Map::load_from_cache,
714
+				'EE_Dependency_Map'                                       => EE_Dependency_Map::load_from_cache,
715
+			],
716
+			'EventEspresso\core\services\routing\RouteMatchSpecificationFactory'                                          => [
717
+				'EventEspresso\core\services\routing\RouteMatchSpecificationDependencyResolver' => EE_Dependency_Map::load_from_cache,
718
+				'EventEspresso\core\services\loaders\Loader'                                    => EE_Dependency_Map::load_from_cache,
719
+			],
720
+			'EventEspresso\core\services\routing\RouteMatchSpecificationManager'                                          => [
721
+				'EventEspresso\core\services\routing\RouteMatchSpecificationCollection' => EE_Dependency_Map::load_from_cache,
722
+				'EventEspresso\core\services\routing\RouteMatchSpecificationFactory'    => EE_Dependency_Map::load_from_cache,
723
+			],
724
+			'EE_URL_Validation_Strategy'                                                                                  => [
725
+				null,
726
+				null,
727
+				'EventEspresso\core\services\validators\URLValidator' => EE_Dependency_Map::load_from_cache,
728
+			],
729
+			'EventEspresso\core\services\request\files\FilesDataHandler'                                                  => [
730
+				'EventEspresso\core\services\request\Request' => EE_Dependency_Map::load_from_cache,
731
+			],
732
+			'EventEspressoBatchRequest\BatchRequestProcessor'                                                             => [
733
+				'EventEspresso\core\services\loaders\Loader' => EE_Dependency_Map::load_from_cache,
734
+			],
735
+			'EventEspresso\core\domain\services\converters\RestApiSpoofer'                                                => [
736
+				'WP_REST_Server'                                               => EE_Dependency_Map::load_from_cache,
737
+				'EED_Core_Rest_Api'                                            => EE_Dependency_Map::load_from_cache,
738
+				'EventEspresso\core\libraries\rest_api\controllers\model\Read' => EE_Dependency_Map::load_from_cache,
739
+				null,
740
+			],
741
+			'EventEspresso\core\services\routing\RouteHandler'                                                            => [
742
+				'EventEspresso\core\services\json\JsonDataNodeHandler' => EE_Dependency_Map::load_from_cache,
743
+				'EventEspresso\core\services\loaders\Loader'           => EE_Dependency_Map::load_from_cache,
744
+				'EventEspresso\core\services\request\Request'          => EE_Dependency_Map::load_from_cache,
745
+				'EventEspresso\core\services\routing\RouteCollection'  => EE_Dependency_Map::load_from_cache,
746
+			],
747
+			'EventEspresso\core\services\json\JsonDataNodeHandler'                                                        => [
748
+				'EventEspresso\core\services\json\JsonDataNodeValidator' => EE_Dependency_Map::load_from_cache,
749
+			],
750
+			'EventEspresso\core\services\routing\Router'                                                                  => [
751
+				'EE_Dependency_Map'                                => EE_Dependency_Map::load_from_cache,
752
+				'EventEspresso\core\services\loaders\Loader'       => EE_Dependency_Map::load_from_cache,
753
+				'EventEspresso\core\services\routing\RouteHandler' => EE_Dependency_Map::load_from_cache,
754
+			],
755
+			'EventEspresso\core\services\assets\AssetManifest'                                                            => [
756
+				'EventEspresso\core\domain\Domain' => EE_Dependency_Map::load_from_cache,
757
+			],
758
+			'EventEspresso\core\services\assets\AssetManifestFactory'                                                     => [
759
+				'EventEspresso\core\services\loaders\Loader' => EE_Dependency_Map::load_from_cache,
760
+			],
761
+			'EventEspresso\core\services\assets\BaristaFactory'                                                           => [
762
+				'EventEspresso\core\services\assets\AssetManifestFactory' => EE_Dependency_Map::load_from_cache,
763
+				'EventEspresso\core\services\loaders\Loader'              => EE_Dependency_Map::load_from_cache,
764
+			],
765
+			'EventEspresso\core\domain\services\capabilities\FeatureFlags'                                                => [
766
+				'EventEspresso\core\domain\services\capabilities\CapabilitiesChecker' => EE_Dependency_Map::load_from_cache,
767
+			],
768
+			'EventEspresso\core\services\addon\AddonManager' => [
769
+				'EventEspresso\core\services\addon\AddonCollection'              => EE_Dependency_Map::load_from_cache,
770
+				'EventEspresso\core\Psr4Autoloader'                              => EE_Dependency_Map::load_from_cache,
771
+				'EventEspresso\core\services\addon\api\v1\RegisterAddon'         => EE_Dependency_Map::load_from_cache,
772
+				'EventEspresso\core\services\addon\api\IncompatibleAddonHandler' => EE_Dependency_Map::load_from_cache,
773
+				'EventEspresso\core\services\addon\api\ThirdPartyPluginHandler'  => EE_Dependency_Map::load_from_cache,
774
+			],
775
+			'EventEspresso\core\services\addon\api\ThirdPartyPluginHandler' => [
776
+				'EventEspresso\core\services\request\Request'  => EE_Dependency_Map::load_from_cache,
777
+			],
778
+			'EventEspressoBatchRequest\JobHandlers\ExecuteBatchDeletion' => [
779
+				'EventEspresso\core\services\orm\tree_traversal\NodeGroupDao' => EE_Dependency_Map::load_from_cache
780
+			],
781
+			'EventEspressoBatchRequest\JobHandlers\PreviewEventDeletion' => [
782
+				'EventEspresso\core\services\orm\tree_traversal\NodeGroupDao' => EE_Dependency_Map::load_from_cache
783
+			],
784
+			'EventEspresso\core\domain\services\admin\events\data\PreviewDeletion' => [
785
+				'EventEspresso\core\services\orm\tree_traversal\NodeGroupDao' => EE_Dependency_Map::load_from_cache,
786
+				'EEM_Event' => EE_Dependency_Map::load_from_cache,
787
+				'EEM_Datetime' => EE_Dependency_Map::load_from_cache,
788
+				'EEM_Registration' => EE_Dependency_Map::load_from_cache
789
+			],
790
+			'EventEspresso\core\domain\services\admin\events\data\ConfirmDeletion' => [
791
+				'EventEspresso\core\services\orm\tree_traversal\NodeGroupDao' => EE_Dependency_Map::load_from_cache,
792
+			],
793
+			'EventEspresso\core\domain\entities\users\CurrentUser' => [
794
+				'EventEspresso\core\domain\entities\users\EventManagers' => EE_Dependency_Map::load_from_cache,
795
+			],
796
+			'EventEspresso\core\services\form\meta\InputTypes' => [
797
+				'EventEspresso\core\services\form\meta\inputs\Block'   => EE_Dependency_Map::load_from_cache,
798
+				'EventEspresso\core\services\form\meta\inputs\Button'   => EE_Dependency_Map::load_from_cache,
799
+				'EventEspresso\core\services\form\meta\inputs\DateTime' => EE_Dependency_Map::load_from_cache,
800
+				'EventEspresso\core\services\form\meta\inputs\Input'    => EE_Dependency_Map::load_from_cache,
801
+				'EventEspresso\core\services\form\meta\inputs\Number'   => EE_Dependency_Map::load_from_cache,
802
+				'EventEspresso\core\services\form\meta\inputs\Phone'    => EE_Dependency_Map::load_from_cache,
803
+				'EventEspresso\core\services\form\meta\inputs\Select'   => EE_Dependency_Map::load_from_cache,
804
+				'EventEspresso\core\services\form\meta\inputs\Text'     => EE_Dependency_Map::load_from_cache,
805
+			],
806
+			'EventEspresso\core\domain\services\registration\form\v1\RegFormDependencyHandler' => [
807
+				'EE_Dependency_Map' => EE_Dependency_Map::load_from_cache,
808
+			],
809
+			'EventEspresso\core\services\calculators\LineItemCalculator' => [
810
+				'EventEspresso\core\services\helpers\DecimalValues' => EE_Dependency_Map::load_from_cache,
811
+			],
812
+			'EventEspresso\core\services\helpers\DecimalValues'          => [
813
+				'EE_Currency_Config' => EE_Dependency_Map::load_from_cache,
814
+			],
815
+		];
816
+	}
817
+
818
+
819
+	/**
820
+	 * Registers how core classes are loaded.
821
+	 * This can either be done by simply providing the name of one of the EE_Registry loader methods such as:
822
+	 *        'EE_Request_Handler' => 'load_core'
823
+	 *        'EE_Messages_Queue'  => 'load_lib'
824
+	 *        'EEH_Debug_Tools'    => 'load_helper'
825
+	 * or, if greater control is required, by providing a custom closure. For example:
826
+	 *        'Some_Class' => function () {
827
+	 *            return new Some_Class();
828
+	 *        },
829
+	 * This is required for instantiating dependencies
830
+	 * where an interface has been type hinted in a class constructor. For example:
831
+	 *        'Required_Interface' => function () {
832
+	 *            return new A_Class_That_Implements_Required_Interface();
833
+	 *        },
834
+	 */
835
+	protected function _register_core_class_loaders()
836
+	{
837
+		$this->_class_loaders = [
838
+			// load_core
839
+			'EE_Dependency_Map'                            => function () {
840
+				return $this;
841
+			},
842
+			'EE_Capabilities'                              => 'load_core',
843
+			'EE_Encryption'                                => 'load_core',
844
+			'EE_Front_Controller'                          => 'load_core',
845
+			'EE_Module_Request_Router'                     => 'load_core',
846
+			'EE_Registry'                                  => 'load_core',
847
+			'EE_Request'                                   => function () {
848
+				return $this->legacy_request;
849
+			},
850
+			'EventEspresso\core\services\request\Request'  => function () {
851
+				return $this->request;
852
+			},
853
+			'EventEspresso\core\services\request\Response' => function () {
854
+				return $this->response;
855
+			},
856
+			'EE_Base'                                      => 'load_core',
857
+			'EE_Request_Handler'                           => 'load_core',
858
+			'EE_Session'                                   => 'load_core',
859
+			'EE_Cron_Tasks'                                => 'load_core',
860
+			'EE_System'                                    => 'load_core',
861
+			'EE_Maintenance_Mode'                          => 'load_core',
862
+			'EE_Register_CPTs'                             => 'load_core',
863
+			'EE_Admin'                                     => 'load_core',
864
+			'EE_CPT_Strategy'                              => 'load_core',
865
+			// load_class
866
+			'EE_Registration_Processor'                    => 'load_class',
867
+			// load_lib
868
+			'EE_Message_Resource_Manager'                  => 'load_lib',
869
+			'EE_Message_Type_Collection'                   => 'load_lib',
870
+			'EE_Message_Type_Collection_Loader'            => 'load_lib',
871
+			'EE_Messenger_Collection'                      => 'load_lib',
872
+			'EE_Messenger_Collection_Loader'               => 'load_lib',
873
+			'EE_Messages_Processor'                        => 'load_lib',
874
+			'EE_Message_Repository'                        => 'load_lib',
875
+			'EE_Messages_Queue'                            => 'load_lib',
876
+			'EE_Messages_Data_Handler_Collection'          => 'load_lib',
877
+			'EE_Message_Template_Group_Collection'         => 'load_lib',
878
+			'EE_Payment_Method_Manager'                    => 'load_lib',
879
+			'EE_DMS_Core_4_1_0'                            => 'load_dms',
880
+			'EE_DMS_Core_4_2_0'                            => 'load_dms',
881
+			'EE_DMS_Core_4_3_0'                            => 'load_dms',
882
+			'EE_DMS_Core_4_5_0'                            => 'load_dms',
883
+			'EE_DMS_Core_4_6_0'                            => 'load_dms',
884
+			'EE_DMS_Core_4_7_0'                            => 'load_dms',
885
+			'EE_DMS_Core_4_8_0'                            => 'load_dms',
886
+			'EE_DMS_Core_4_9_0'                            => 'load_dms',
887
+			'EE_DMS_Core_4_10_0'                           => 'load_dms',
888
+			'EE_DMS_Core_4_11_0'                           => 'load_dms',
889
+			'EE_DMS_Core_4_12_0'                           => 'load_dms',
890
+			'EE_Messages_Generator'                        => static function () {
891
+				return EE_Registry::instance()->load_lib(
892
+					'Messages_Generator',
893
+					[],
894
+					false,
895
+					false
896
+				);
897
+			},
898
+			'EE_Messages_Template_Defaults'                => static function ($arguments = []) {
899
+				return EE_Registry::instance()->load_lib(
900
+					'Messages_Template_Defaults',
901
+					$arguments,
902
+					false,
903
+					false
904
+				);
905
+			},
906
+			// load_helper
907
+			'EEH_Parse_Shortcodes'                         => static function () {
908
+				if (EE_Registry::instance()->load_helper('Parse_Shortcodes')) {
909
+					return new EEH_Parse_Shortcodes();
910
+				}
911
+				return null;
912
+			},
913
+			'EE_Template_Config'                           => static function () {
914
+				return EE_Config::instance()->template_settings;
915
+			},
916
+			'EE_Currency_Config'                           => static function () {
917
+				return EE_Config::instance()->currency;
918
+			},
919
+			'EE_Registration_Config'                       => static function () {
920
+				return EE_Config::instance()->registration;
921
+			},
922
+			'EE_Core_Config'                               => static function () {
923
+				return EE_Config::instance()->core;
924
+			},
925
+			'EventEspresso\core\services\loaders\Loader'   => static function () {
926
+				return LoaderFactory::getLoader();
927
+			},
928
+			'EE_Network_Config'                            => static function () {
929
+				return EE_Network_Config::instance();
930
+			},
931
+			'EE_Config'                                    => static function () {
932
+				return EE_Config::instance();
933
+			},
934
+			'EventEspresso\core\domain\Domain'             => static function () {
935
+				return DomainFactory::getEventEspressoCoreDomain();
936
+			},
937
+			'EE_Admin_Config'                              => static function () {
938
+				return EE_Config::instance()->admin;
939
+			},
940
+			'EE_Organization_Config'                       => static function () {
941
+				return EE_Config::instance()->organization;
942
+			},
943
+			'EE_Network_Core_Config'                       => static function () {
944
+				return EE_Network_Config::instance()->core;
945
+			},
946
+			'EE_Environment_Config'                        => static function () {
947
+				return EE_Config::instance()->environment;
948
+			},
949
+			'EED_Core_Rest_Api'                            => static function () {
950
+				return EED_Core_Rest_Api::instance();
951
+			},
952
+			'WP_REST_Server'                               => static function () {
953
+				return rest_get_server();
954
+			},
955
+			'EventEspresso\core\Psr4Autoloader'            => static function () {
956
+				return EE_Psr4AutoloaderInit::psr4_loader();
957
+			},
958
+		];
959
+	}
960
+
961
+
962
+	/**
963
+	 * can be used for supplying alternate names for classes,
964
+	 * or for connecting interface names to instantiable classes
965
+	 *
966
+	 * @throws InvalidAliasException
967
+	 */
968
+	protected function _register_core_aliases()
969
+	{
970
+		$aliases = [
971
+			'CommandBusInterface'                                                          => 'EventEspresso\core\services\commands\CommandBusInterface',
972
+			'EventEspresso\core\services\commands\CommandBusInterface'                     => 'EventEspresso\core\services\commands\CommandBus',
973
+			'CommandHandlerManagerInterface'                                               => 'EventEspresso\core\services\commands\CommandHandlerManagerInterface',
974
+			'EventEspresso\core\services\commands\CommandHandlerManagerInterface'          => 'EventEspresso\core\services\commands\CommandHandlerManager',
975
+			'CapChecker'                                                                   => 'EventEspresso\core\services\commands\middleware\CapChecker',
976
+			'AddActionHook'                                                                => 'EventEspresso\core\services\commands\middleware\AddActionHook',
977
+			'CapabilitiesChecker'                                                          => 'EventEspresso\core\domain\services\capabilities\CapabilitiesChecker',
978
+			'CapabilitiesCheckerInterface'                                                 => 'EventEspresso\core\domain\services\capabilities\CapabilitiesCheckerInterface',
979
+			'EventEspresso\core\domain\services\capabilities\CapabilitiesCheckerInterface' => 'EventEspresso\core\domain\services\capabilities\CapabilitiesChecker',
980
+			'CreateRegistrationService'                                                    => 'EventEspresso\core\domain\services\registration\CreateRegistrationService',
981
+			'CreateRegistrationCommandHandler'                                             => 'EventEspresso\core\services\commands\registration\CreateRegistrationCommand',
982
+			'CopyRegistrationDetailsCommandHandler'                                        => 'EventEspresso\core\services\commands\registration\CopyRegistrationDetailsCommand',
983
+			'CopyRegistrationPaymentsCommandHandler'                                       => 'EventEspresso\core\services\commands\registration\CopyRegistrationPaymentsCommand',
984
+			'CancelRegistrationAndTicketLineItemCommandHandler'                            => 'EventEspresso\core\services\commands\registration\CancelRegistrationAndTicketLineItemCommandHandler',
985
+			'UpdateRegistrationAndTransactionAfterChangeCommandHandler'                    => 'EventEspresso\core\services\commands\registration\UpdateRegistrationAndTransactionAfterChangeCommandHandler',
986
+			'CreateTicketLineItemCommandHandler'                                           => 'EventEspresso\core\services\commands\ticket\CreateTicketLineItemCommand',
987
+			'CreateTransactionCommandHandler'                                              => 'EventEspresso\core\services\commands\transaction\CreateTransactionCommandHandler',
988
+			'CreateAttendeeCommandHandler'                                                 => 'EventEspresso\core\services\commands\attendee\CreateAttendeeCommandHandler',
989
+			'TableManager'                                                                 => 'EventEspresso\core\services\database\TableManager',
990
+			'TableAnalysis'                                                                => 'EventEspresso\core\services\database\TableAnalysis',
991
+			'EspressoShortcode'                                                            => 'EventEspresso\core\services\shortcodes\EspressoShortcode',
992
+			'ShortcodeInterface'                                                           => 'EventEspresso\core\services\shortcodes\ShortcodeInterface',
993
+			'EventEspresso\core\services\shortcodes\ShortcodeInterface'                    => 'EventEspresso\core\services\shortcodes\EspressoShortcode',
994
+			'EventEspresso\core\services\cache\CacheStorageInterface'                      => 'EventEspresso\core\services\cache\TransientCacheStorage',
995
+			'LoaderInterface'                                                              => 'EventEspresso\core\services\loaders\LoaderInterface',
996
+			'EventEspresso\core\services\loaders\LoaderInterface'                          => 'EventEspresso\core\services\loaders\Loader',
997
+			'CommandFactoryInterface'                                                      => 'EventEspresso\core\services\commands\CommandFactoryInterface',
998
+			'EventEspresso\core\services\commands\CommandFactoryInterface'                 => 'EventEspresso\core\services\commands\CommandFactory',
999
+			'EmailValidatorInterface'                                                      => 'EventEspresso\core\domain\services\validation\email\EmailValidatorInterface',
1000
+			'EventEspresso\core\domain\services\validation\email\EmailValidatorInterface'  => 'EventEspresso\core\domain\services\validation\email\EmailValidationService',
1001
+			'NoticeConverterInterface'                                                     => 'EventEspresso\core\services\notices\NoticeConverterInterface',
1002
+			'EventEspresso\core\services\notices\NoticeConverterInterface'                 => 'EventEspresso\core\services\notices\ConvertNoticesToEeErrors',
1003
+			'NoticesContainerInterface'                                                    => 'EventEspresso\core\services\notices\NoticesContainerInterface',
1004
+			'EventEspresso\core\services\notices\NoticesContainerInterface'                => 'EventEspresso\core\services\notices\NoticesContainer',
1005
+			'EventEspresso\core\services\request\RequestInterface'                         => 'EventEspresso\core\services\request\Request',
1006
+			'EventEspresso\core\services\request\ResponseInterface'                        => 'EventEspresso\core\services\request\Response',
1007
+			'EventEspresso\core\domain\DomainInterface'                                    => 'EventEspresso\core\domain\Domain',
1008
+			'Registration_Processor'                                                       => 'EE_Registration_Processor',
1009
+			'EventEspresso\core\services\assets\AssetManifestInterface'                    => 'EventEspresso\core\services\assets\AssetManifest',
1010
+		];
1011
+		foreach ($aliases as $alias => $fqn) {
1012
+			if (is_array($fqn)) {
1013
+				foreach ($fqn as $class => $for_class) {
1014
+					$this->class_cache->addAlias($class, $alias, $for_class);
1015
+				}
1016
+				continue;
1017
+			}
1018
+			$this->class_cache->addAlias($fqn, $alias);
1019
+		}
1020
+		if (! (defined('DOING_AJAX') && DOING_AJAX) && is_admin()) {
1021
+			$this->class_cache->addAlias(
1022
+				'EventEspresso\core\services\notices\ConvertNoticesToAdminNotices',
1023
+				'EventEspresso\core\services\notices\NoticeConverterInterface'
1024
+			);
1025
+		}
1026
+	}
1027
+
1028
+
1029
+	/**
1030
+	 * This is used to reset the internal map and class_loaders to their original default state at the beginning of the
1031
+	 * request Primarily used by unit tests.
1032
+	 */
1033
+	public function reset()
1034
+	{
1035
+		$this->_register_core_class_loaders();
1036
+		$this->_register_core_dependencies();
1037
+	}
1038
+
1039
+
1040
+	/**
1041
+	 * PLZ NOTE: a better name for this method would be is_alias()
1042
+	 * because it returns TRUE if the provided fully qualified name IS an alias
1043
+	 * WHY?
1044
+	 * Because if a class is type hinting for a concretion,
1045
+	 * then why would we need to find another class to supply it?
1046
+	 * ie: if a class asks for `Fully/Qualified/Namespace/SpecificClassName`,
1047
+	 * then give it an instance of `Fully/Qualified/Namespace/SpecificClassName`.
1048
+	 * Don't go looking for some substitute.
1049
+	 * Whereas if a class is type hinting for an interface...
1050
+	 * then we need to find an actual class to use.
1051
+	 * So the interface IS the alias for some other FQN,
1052
+	 * and we need to find out if `Fully/Qualified/Namespace/SomeInterface`
1053
+	 * represents some other class.
1054
+	 *
1055
+	 * @param string $fqn
1056
+	 * @param string $for_class
1057
+	 * @return bool
1058
+	 * @deprecated 4.9.62.p
1059
+	 */
1060
+	public function has_alias($fqn = '', $for_class = '')
1061
+	{
1062
+		return $this->isAlias($fqn, $for_class);
1063
+	}
1064
+
1065
+
1066
+	/**
1067
+	 * PLZ NOTE: a better name for this method would be get_fqn_for_alias()
1068
+	 * because it returns a FQN for provided alias if one exists, otherwise returns the original $alias
1069
+	 * functions recursively, so that multiple aliases can be used to drill down to a FQN
1070
+	 *  for example:
1071
+	 *      if the following two entries were added to the _aliases array:
1072
+	 *          array(
1073
+	 *              'interface_alias'           => 'some\namespace\interface'
1074
+	 *              'some\namespace\interface'  => 'some\namespace\classname'
1075
+	 *          )
1076
+	 *      then one could use EE_Registry::instance()->create( 'interface_alias' )
1077
+	 *      to load an instance of 'some\namespace\classname'
1078
+	 *
1079
+	 * @param string $alias
1080
+	 * @param string $for_class
1081
+	 * @return string
1082
+	 * @deprecated 4.9.62.p
1083
+	 */
1084
+	public function get_alias($alias = '', $for_class = '')
1085
+	{
1086
+		return $this->getFqnForAlias($alias, $for_class);
1087
+	}
1088 1088
 }
Please login to merge, or discard this patch.
core/helpers/EEH_Line_Item.helper.php 2 patches
Indentation   +2128 added lines, -2128 removed lines patch added patch discarded remove patch
@@ -21,2132 +21,2132 @@
 block discarded – undo
21 21
  */
22 22
 class EEH_Line_Item
23 23
 {
24
-    /**
25
-     * @var EE_Line_Item[]
26
-    */
27
-    private static $global_taxes;
28
-
29
-
30
-    /**
31
-     * Adds a simple item (unrelated to any other model object) to the provided PARENT line item.
32
-     * Does NOT automatically re-calculate the line item totals or update the related transaction.
33
-     * You should call recalculate_total_including_taxes() on the grant total line item after this
34
-     * to update the subtotals, and EE_Registration_Processor::calculate_reg_final_prices_per_line_item()
35
-     * to keep the registration final prices in-sync with the transaction's total.
36
-     *
37
-     * @param EE_Line_Item $parent_line_item
38
-     * @param string       $name
39
-     * @param float        $unit_price
40
-     * @param string       $description
41
-     * @param int          $quantity
42
-     * @param boolean      $taxable
43
-     * @param string|null  $code if set to a value, ensures there is only one line item with that code
44
-     * @param bool         $return_item
45
-     * @param bool         $recalculate_totals
46
-     * @return boolean|EE_Line_Item success
47
-     * @throws EE_Error
48
-     * @throws ReflectionException
49
-     */
50
-    public static function add_unrelated_item(
51
-        EE_Line_Item $parent_line_item,
52
-        string $name,
53
-        float $unit_price,
54
-        string $description = '',
55
-        int $quantity = 1,
56
-        bool $taxable = false,
57
-        ?string $code = null,
58
-        bool $return_item = false,
59
-        bool $recalculate_totals = true
60
-    ) {
61
-        $items_subtotal = self::get_pre_tax_subtotal($parent_line_item);
62
-        $line_item      = EE_Line_Item::new_instance(
63
-            [
64
-                'LIN_name'       => $name,
65
-                'LIN_desc'       => $description,
66
-                'LIN_unit_price' => $unit_price,
67
-                'LIN_quantity'   => $quantity,
68
-                'LIN_percent'    => null,
69
-                'LIN_is_taxable' => $taxable,
70
-                'LIN_order'      => $items_subtotal instanceof EE_Line_Item
71
-                    ? count($items_subtotal->children())
72
-                    : 0,
73
-                'LIN_total'      => (float) $unit_price * (int) $quantity,
74
-                'LIN_type'       => EEM_Line_Item::type_line_item,
75
-                'LIN_code'       => $code,
76
-            ]
77
-        );
78
-        $line_item      = apply_filters(
79
-            'FHEE__EEH_Line_Item__add_unrelated_item__line_item',
80
-            $line_item,
81
-            $parent_line_item
82
-        );
83
-        $added          = self::add_item($parent_line_item, $line_item, $recalculate_totals);
84
-        return $return_item ? $line_item : $added;
85
-    }
86
-
87
-
88
-    /**
89
-     * Adds a simple item ( unrelated to any other model object) to the total line item,
90
-     * in the correct spot in the line item tree. Does not automatically
91
-     * re-calculate the line item totals, nor update the related transaction, nor upgrade the transaction's
92
-     * registrations' final prices (which should probably change because of this).
93
-     * You should call recalculate_total_including_taxes() on the grand total line item, then
94
-     * update the transaction's total, and EE_Registration_Processor::update_registration_final_prices()
95
-     * after using this, to keep the registration final prices in-sync with the transaction's total.
96
-     *
97
-     * @param EE_Line_Item $parent_line_item
98
-     * @param string       $name
99
-     * @param float        $percentage_amount
100
-     * @param string       $description
101
-     * @param boolean      $taxable
102
-     * @param string|null  $code
103
-     * @param bool         $return_item
104
-     * @return boolean|EE_Line_Item success
105
-     * @throws EE_Error
106
-     * @throws ReflectionException
107
-     */
108
-    public static function add_percentage_based_item(
109
-        EE_Line_Item $parent_line_item,
110
-        string $name,
111
-        float $percentage_amount,
112
-        string $description = '',
113
-        bool $taxable = false,
114
-        ?string $code = null,
115
-        bool $return_item = false
116
-    ) {
117
-        $total = $percentage_amount * $parent_line_item->total() / 100;
118
-        $line_item = EE_Line_Item::new_instance(
119
-            [
120
-                'LIN_name'       => $name,
121
-                'LIN_desc'       => $description,
122
-                'LIN_unit_price' => 0,
123
-                'LIN_percent'    => $percentage_amount,
124
-                'LIN_quantity'   => 1,
125
-                'LIN_is_taxable' => $taxable,
126
-                'LIN_total'      => (float) $total,
127
-                'LIN_type'       => EEM_Line_Item::type_line_item,
128
-                'LIN_parent'     => $parent_line_item->ID(),
129
-                'LIN_code'       => $code,
130
-            ]
131
-        );
132
-        $line_item = apply_filters(
133
-            'FHEE__EEH_Line_Item__add_percentage_based_item__line_item',
134
-            $line_item
135
-        );
136
-        $added     = $parent_line_item->add_child_line_item($line_item, false);
137
-        return $return_item ? $line_item : $added;
138
-    }
139
-
140
-
141
-    /**
142
-     * Returns the new line item created by adding a purchase of the ticket
143
-     * ensures that ticket line item is saved, and that cart total has been recalculated.
144
-     * If this ticket has already been purchased, just increments its count.
145
-     * Automatically re-calculates the line item totals and updates the related transaction. But
146
-     * DOES NOT automatically upgrade the transaction's registrations' final prices (which
147
-     * should probably change because of this).
148
-     * You should call EE_Registration_Processor::calculate_reg_final_prices_per_line_item()
149
-     * after using this, to keep the registration final prices in-sync with the transaction's total.
150
-     *
151
-     * @param EE_Line_Item $total_line_item grand total line item of type EEM_Line_Item::type_total
152
-     * @param EE_Ticket    $ticket
153
-     * @param int          $qty
154
-     * @return EE_Line_Item
155
-     * @throws EE_Error
156
-     * @throws InvalidArgumentException
157
-     * @throws InvalidDataTypeException
158
-     * @throws InvalidInterfaceException
159
-     * @throws ReflectionException
160
-     */
161
-    public static function add_ticket_purchase(EE_Line_Item $total_line_item, EE_Ticket $ticket, $qty = 1)
162
-    {
163
-        if (! $total_line_item instanceof EE_Line_Item || ! $total_line_item->is_total()) {
164
-            throw new EE_Error(
165
-                sprintf(
166
-                    esc_html__(
167
-                        'A valid line item total is required in order to add tickets. A line item of type "%s" was passed.',
168
-                        'event_espresso'
169
-                    ),
170
-                    $ticket->ID(),
171
-                    $total_line_item->ID()
172
-                )
173
-            );
174
-        }
175
-        // either increment the qty for an existing ticket
176
-        $line_item = self::increment_ticket_qty_if_already_in_cart($total_line_item, $ticket, $qty);
177
-        // or add a new one
178
-        if (! $line_item instanceof EE_Line_Item) {
179
-            $line_item = self::create_ticket_line_item($total_line_item, $ticket, $qty);
180
-        }
181
-        $total_line_item->recalculate_total_including_taxes();
182
-        return $line_item;
183
-    }
184
-
185
-
186
-    /**
187
-     * Returns the new line item created by adding a purchase of the ticket
188
-     *
189
-     * @param EE_Line_Item $total_line_item
190
-     * @param EE_Ticket    $ticket
191
-     * @param int          $qty
192
-     * @return EE_Line_Item
193
-     * @throws EE_Error
194
-     * @throws InvalidArgumentException
195
-     * @throws InvalidDataTypeException
196
-     * @throws InvalidInterfaceException
197
-     * @throws ReflectionException
198
-     */
199
-    public static function increment_ticket_qty_if_already_in_cart(
200
-        EE_Line_Item $total_line_item,
201
-        EE_Ticket $ticket,
202
-        $qty = 1
203
-    ) {
204
-        $line_item = null;
205
-        if ($total_line_item instanceof EE_Line_Item && $total_line_item->is_total()) {
206
-            $ticket_line_items = EEH_Line_Item::get_ticket_line_items($total_line_item);
207
-            foreach ((array) $ticket_line_items as $ticket_line_item) {
208
-                if (
209
-                    $ticket_line_item instanceof EE_Line_Item
210
-                    && (int) $ticket_line_item->OBJ_ID() === (int) $ticket->ID()
211
-                ) {
212
-                    $line_item = $ticket_line_item;
213
-                    break;
214
-                }
215
-            }
216
-        }
217
-        if ($line_item instanceof EE_Line_Item) {
218
-            EEH_Line_Item::increment_quantity($line_item, $qty);
219
-            return $line_item;
220
-        }
221
-        return null;
222
-    }
223
-
224
-
225
-    /**
226
-     * Increments the line item and all its children's quantity by $qty (but percent line items are unaffected).
227
-     * Does NOT save or recalculate other line items totals
228
-     *
229
-     * @param EE_Line_Item $line_item
230
-     * @param int          $qty
231
-     * @return void
232
-     * @throws EE_Error
233
-     * @throws InvalidArgumentException
234
-     * @throws InvalidDataTypeException
235
-     * @throws InvalidInterfaceException
236
-     * @throws ReflectionException
237
-     */
238
-    public static function increment_quantity(EE_Line_Item $line_item, $qty = 1)
239
-    {
240
-        if (! $line_item->is_percent()) {
241
-            $qty += $line_item->quantity();
242
-            $line_item->set_quantity($qty);
243
-            $line_item->set_total($line_item->unit_price() * $qty);
244
-            $line_item->save();
245
-        }
246
-        foreach ($line_item->children() as $child) {
247
-            if ($child->is_sub_line_item()) {
248
-                EEH_Line_Item::update_quantity($child, $qty);
249
-            }
250
-        }
251
-    }
252
-
253
-
254
-    /**
255
-     * Decrements the line item and all its children's quantity by $qty (but percent line items are unaffected).
256
-     * Does NOT save or recalculate other line items totals
257
-     *
258
-     * @param EE_Line_Item $line_item
259
-     * @param int          $qty
260
-     * @return void
261
-     * @throws EE_Error
262
-     * @throws InvalidArgumentException
263
-     * @throws InvalidDataTypeException
264
-     * @throws InvalidInterfaceException
265
-     * @throws ReflectionException
266
-     */
267
-    public static function decrement_quantity(EE_Line_Item $line_item, $qty = 1)
268
-    {
269
-        if (! $line_item->is_percent()) {
270
-            $qty = $line_item->quantity() - $qty;
271
-            $qty = max($qty, 0);
272
-            $line_item->set_quantity($qty);
273
-            $line_item->set_total($line_item->unit_price() * $qty);
274
-            $line_item->save();
275
-        }
276
-        foreach ($line_item->children() as $child) {
277
-            if ($child->is_sub_line_item()) {
278
-                EEH_Line_Item::update_quantity($child, $qty);
279
-            }
280
-        }
281
-    }
282
-
283
-
284
-    /**
285
-     * Updates the line item and its children's quantities to the specified number.
286
-     * Does NOT save them or recalculate totals.
287
-     *
288
-     * @param EE_Line_Item $line_item
289
-     * @param int          $new_quantity
290
-     * @throws EE_Error
291
-     * @throws InvalidArgumentException
292
-     * @throws InvalidDataTypeException
293
-     * @throws InvalidInterfaceException
294
-     * @throws ReflectionException
295
-     */
296
-    public static function update_quantity(EE_Line_Item $line_item, $new_quantity)
297
-    {
298
-        if (! $line_item->is_percent()) {
299
-            $line_item->set_quantity($new_quantity);
300
-            $line_item->set_total($line_item->unit_price() * $new_quantity);
301
-            $line_item->save();
302
-        }
303
-        foreach ($line_item->children() as $child) {
304
-            if ($child->is_sub_line_item()) {
305
-                EEH_Line_Item::update_quantity($child, $new_quantity);
306
-            }
307
-        }
308
-    }
309
-
310
-
311
-    /**
312
-     * Returns the new line item created by adding a purchase of the ticket
313
-     *
314
-     * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
315
-     * @param EE_Ticket    $ticket
316
-     * @param int          $qty
317
-     * @return EE_Line_Item
318
-     * @throws EE_Error
319
-     * @throws InvalidArgumentException
320
-     * @throws InvalidDataTypeException
321
-     * @throws InvalidInterfaceException
322
-     * @throws ReflectionException
323
-     */
324
-    public static function create_ticket_line_item(EE_Line_Item $total_line_item, EE_Ticket $ticket, $qty = 1)
325
-    {
326
-        $datetimes = $ticket->datetimes();
327
-        $first_datetime = reset($datetimes);
328
-        $first_datetime_name = esc_html__('Event', 'event_espresso');
329
-        if ($first_datetime instanceof EE_Datetime && $first_datetime->event() instanceof EE_Event) {
330
-            $first_datetime_name = $first_datetime->event()->name();
331
-        }
332
-        $event = sprintf(_x('(For %1$s)', '(For Event Name)', 'event_espresso'), $first_datetime_name);
333
-        // get event subtotal line
334
-        $events_sub_total = self::get_event_line_item_for_ticket($total_line_item, $ticket);
335
-        $taxes = $ticket->tax_price_modifiers();
336
-        // add $ticket to cart
337
-        $line_item = EE_Line_Item::new_instance(array(
338
-            'LIN_name'       => $ticket->name(),
339
-            'LIN_desc'       => $ticket->description() !== '' ? $ticket->description() . ' ' . $event : $event,
340
-            'LIN_unit_price' => $ticket->price(),
341
-            'LIN_quantity'   => $qty,
342
-            'LIN_is_taxable' => empty($taxes) && $ticket->taxable(),
343
-            'LIN_order'      => count($events_sub_total->children()),
344
-            'LIN_total'      => $ticket->price() * $qty,
345
-            'LIN_type'       => EEM_Line_Item::type_line_item,
346
-            'OBJ_ID'         => $ticket->ID(),
347
-            'OBJ_type'       => EEM_Line_Item::OBJ_TYPE_TICKET,
348
-        ));
349
-        $line_item = apply_filters(
350
-            'FHEE__EEH_Line_Item__create_ticket_line_item__line_item',
351
-            $line_item
352
-        );
353
-        if (!$line_item instanceof EE_Line_Item) {
354
-            throw new DomainException(
355
-                esc_html__('Invalid EE_Line_Item received.', 'event_espresso')
356
-            );
357
-        }
358
-        $events_sub_total->add_child_line_item($line_item);
359
-        // now add the sub-line items
360
-        $running_total = 0;
361
-        $running_pre_tax_total = 0;
362
-        foreach ($ticket->prices() as $price) {
363
-            $sign = $price->is_discount() ? -1 : 1;
364
-            $price_total = $price->is_percent()
365
-                ? $running_pre_tax_total * $price->amount() / 100
366
-                : $price->amount() * $qty;
367
-            if ($price->is_percent()) {
368
-                $percent = $sign * $price->amount();
369
-                $unit_price = 0;
370
-            } else {
371
-                $percent    = 0;
372
-                $unit_price = $sign * $price->amount();
373
-            }
374
-            $sub_line_item = EE_Line_Item::new_instance(array(
375
-                'LIN_name'       => $price->name(),
376
-                'LIN_desc'       => $price->desc(),
377
-                'LIN_quantity'   => $price->is_percent() ? null : $qty,
378
-                'LIN_is_taxable' => false,
379
-                'LIN_order'      => $price->order(),
380
-                'LIN_total'      => $price_total,
381
-                'LIN_pretax'     => 0,
382
-                'LIN_unit_price' => $unit_price,
383
-                'LIN_percent'    => $percent,
384
-                'LIN_type'       => $price->is_tax() ? EEM_Line_Item::type_sub_tax : EEM_Line_Item::type_sub_line_item,
385
-                'OBJ_ID'         => $price->ID(),
386
-                'OBJ_type'       => EEM_Line_Item::OBJ_TYPE_PRICE,
387
-            ));
388
-            $sub_line_item = apply_filters(
389
-                'FHEE__EEH_Line_Item__create_ticket_line_item__sub_line_item',
390
-                $sub_line_item
391
-            );
392
-            $running_total += $sign * $price_total;
393
-            $running_pre_tax_total += ! $price->is_tax() ? $sign * $price_total : 0;
394
-            $line_item->add_child_line_item($sub_line_item);
395
-        }
396
-        $line_item->setPretaxTotal($running_pre_tax_total);
397
-        return $line_item;
398
-    }
399
-
400
-
401
-    /**
402
-     * Adds the specified item under the pre-tax-sub-total line item. Automatically
403
-     * re-calculates the line item totals and updates the related transaction. But
404
-     * DOES NOT automatically upgrade the transaction's registrations' final prices (which
405
-     * should probably change because of this).
406
-     * You should call EE_Registration_Processor::calculate_reg_final_prices_per_line_item()
407
-     * after using this, to keep the registration final prices in-sync with the transaction's total.
408
-     *
409
-     * @param EE_Line_Item $total_line_item
410
-     * @param EE_Line_Item $item to be added
411
-     * @return boolean
412
-     * @throws EE_Error
413
-     * @throws InvalidArgumentException
414
-     * @throws InvalidDataTypeException
415
-     * @throws InvalidInterfaceException
416
-     * @throws ReflectionException
417
-     */
418
-    public static function add_item(EE_Line_Item $total_line_item, EE_Line_Item $item, $recalculate_totals = true)
419
-    {
420
-        $pre_tax_subtotal = self::get_pre_tax_subtotal($total_line_item);
421
-        if ($pre_tax_subtotal instanceof EE_Line_Item) {
422
-            $success = $pre_tax_subtotal->add_child_line_item($item);
423
-        } else {
424
-            return false;
425
-        }
426
-        if ($recalculate_totals) {
427
-            $total_line_item->recalculate_total_including_taxes();
428
-        }
429
-        return $success;
430
-    }
431
-
432
-
433
-    /**
434
-     * cancels an existing ticket line item,
435
-     * by decrementing it's quantity by 1 and adding a new "type_cancellation" sub-line-item.
436
-     * ALL totals and subtotals will NEED TO BE UPDATED after performing this action
437
-     *
438
-     * @param EE_Line_Item $ticket_line_item
439
-     * @param int          $qty
440
-     * @return bool success
441
-     * @throws EE_Error
442
-     * @throws InvalidArgumentException
443
-     * @throws InvalidDataTypeException
444
-     * @throws InvalidInterfaceException
445
-     * @throws ReflectionException
446
-     */
447
-    public static function cancel_ticket_line_item(EE_Line_Item $ticket_line_item, $qty = 1)
448
-    {
449
-        // validate incoming line_item
450
-        if ($ticket_line_item->OBJ_type() !== EEM_Line_Item::OBJ_TYPE_TICKET) {
451
-            throw new EE_Error(
452
-                sprintf(
453
-                    esc_html__(
454
-                        'The supplied line item must have an Object Type of "Ticket", not %1$s.',
455
-                        'event_espresso'
456
-                    ),
457
-                    $ticket_line_item->type()
458
-                )
459
-            );
460
-        }
461
-        if ($ticket_line_item->quantity() < $qty) {
462
-            throw new EE_Error(
463
-                sprintf(
464
-                    esc_html__(
465
-                        'Can not cancel %1$d ticket(s) because the supplied line item has a quantity of %2$d.',
466
-                        'event_espresso'
467
-                    ),
468
-                    $qty,
469
-                    $ticket_line_item->quantity()
470
-                )
471
-            );
472
-        }
473
-        // decrement ticket quantity; don't rely on auto-fixing when recalculating totals to do this
474
-        $ticket_line_item->set_quantity($ticket_line_item->quantity() - $qty);
475
-        foreach ($ticket_line_item->children() as $child_line_item) {
476
-            if (
477
-                $child_line_item->is_sub_line_item()
478
-                && ! $child_line_item->is_percent()
479
-                && ! $child_line_item->is_cancellation()
480
-            ) {
481
-                $child_line_item->set_quantity($child_line_item->quantity() - $qty);
482
-            }
483
-        }
484
-        // get cancellation sub line item
485
-        $cancellation_line_item = EEH_Line_Item::get_descendants_of_type(
486
-            $ticket_line_item,
487
-            EEM_Line_Item::type_cancellation
488
-        );
489
-        $cancellation_line_item = reset($cancellation_line_item);
490
-        // verify that this ticket was indeed previously cancelled
491
-        if ($cancellation_line_item instanceof EE_Line_Item) {
492
-            // increment cancelled quantity
493
-            $cancellation_line_item->set_quantity($cancellation_line_item->quantity() + $qty);
494
-        } else {
495
-            // create cancellation sub line item
496
-            $cancellation_line_item = EE_Line_Item::new_instance(array(
497
-                'LIN_name'       => esc_html__('Cancellation', 'event_espresso'),
498
-                'LIN_desc'       => sprintf(
499
-                    esc_html_x(
500
-                        'Cancelled %1$s : %2$s',
501
-                        'Cancelled Ticket Name : 2015-01-01 11:11',
502
-                        'event_espresso'
503
-                    ),
504
-                    $ticket_line_item->name(),
505
-                    current_time(get_option('date_format') . ' ' . get_option('time_format'))
506
-                ),
507
-                'LIN_unit_price' => 0, // $ticket_line_item->unit_price()
508
-                'LIN_quantity'   => $qty,
509
-                'LIN_is_taxable' => $ticket_line_item->is_taxable(),
510
-                'LIN_order'      => count($ticket_line_item->children()),
511
-                'LIN_total'      => 0, // $ticket_line_item->unit_price()
512
-                'LIN_type'       => EEM_Line_Item::type_cancellation,
513
-            ));
514
-            $ticket_line_item->add_child_line_item($cancellation_line_item);
515
-        }
516
-        if ($ticket_line_item->save_this_and_descendants() > 0) {
517
-            // decrement parent line item quantity
518
-            $event_line_item = $ticket_line_item->parent();
519
-            if (
520
-                $event_line_item instanceof EE_Line_Item
521
-                && $event_line_item->OBJ_type() === EEM_Line_Item::OBJ_TYPE_EVENT
522
-            ) {
523
-                $event_line_item->set_quantity($event_line_item->quantity() - $qty);
524
-                $event_line_item->save();
525
-            }
526
-            EEH_Line_Item::get_grand_total_and_recalculate_everything($ticket_line_item);
527
-            return true;
528
-        }
529
-        return false;
530
-    }
531
-
532
-
533
-    /**
534
-     * reinstates (un-cancels?) a previously canceled ticket line item,
535
-     * by incrementing it's quantity by 1, and decrementing it's "type_cancellation" sub-line-item.
536
-     * ALL totals and subtotals will NEED TO BE UPDATED after performing this action
537
-     *
538
-     * @param EE_Line_Item $ticket_line_item
539
-     * @param int          $qty
540
-     * @return bool success
541
-     * @throws EE_Error
542
-     * @throws InvalidArgumentException
543
-     * @throws InvalidDataTypeException
544
-     * @throws InvalidInterfaceException
545
-     * @throws ReflectionException
546
-     */
547
-    public static function reinstate_canceled_ticket_line_item(EE_Line_Item $ticket_line_item, $qty = 1)
548
-    {
549
-        // validate incoming line_item
550
-        if ($ticket_line_item->OBJ_type() !== EEM_Line_Item::OBJ_TYPE_TICKET) {
551
-            throw new EE_Error(
552
-                sprintf(
553
-                    esc_html__(
554
-                        'The supplied line item must have an Object Type of "Ticket", not %1$s.',
555
-                        'event_espresso'
556
-                    ),
557
-                    $ticket_line_item->type()
558
-                )
559
-            );
560
-        }
561
-        // get cancellation sub line item
562
-        $cancellation_line_item = EEH_Line_Item::get_descendants_of_type(
563
-            $ticket_line_item,
564
-            EEM_Line_Item::type_cancellation
565
-        );
566
-        $cancellation_line_item = reset($cancellation_line_item);
567
-        // verify that this ticket was indeed previously cancelled
568
-        if (! $cancellation_line_item instanceof EE_Line_Item) {
569
-            return false;
570
-        }
571
-        if ($cancellation_line_item->quantity() > $qty) {
572
-            // decrement cancelled quantity
573
-            $cancellation_line_item->set_quantity($cancellation_line_item->quantity() - $qty);
574
-        } elseif ($cancellation_line_item->quantity() === $qty) {
575
-            // decrement cancelled quantity in case anyone still has the object kicking around
576
-            $cancellation_line_item->set_quantity($cancellation_line_item->quantity() - $qty);
577
-            // delete because quantity will end up as 0
578
-            $cancellation_line_item->delete();
579
-            // and attempt to destroy the object,
580
-            // even though PHP won't actually destroy it until it needs the memory
581
-            unset($cancellation_line_item);
582
-        } else {
583
-            // what ?!?! negative quantity ?!?!
584
-            throw new EE_Error(
585
-                sprintf(
586
-                    esc_html__(
587
-                        'Can not reinstate %1$d cancelled ticket(s) because the cancelled ticket quantity is only %2$d.',
588
-                        'event_espresso'
589
-                    ),
590
-                    $qty,
591
-                    $cancellation_line_item->quantity()
592
-                )
593
-            );
594
-        }
595
-        // increment ticket quantity
596
-        $ticket_line_item->set_quantity($ticket_line_item->quantity() + $qty);
597
-        if ($ticket_line_item->save_this_and_descendants() > 0) {
598
-            // increment parent line item quantity
599
-            $event_line_item = $ticket_line_item->parent();
600
-            if (
601
-                $event_line_item instanceof EE_Line_Item
602
-                && $event_line_item->OBJ_type() === EEM_Line_Item::OBJ_TYPE_EVENT
603
-            ) {
604
-                $event_line_item->set_quantity($event_line_item->quantity() + $qty);
605
-            }
606
-            EEH_Line_Item::get_grand_total_and_recalculate_everything($ticket_line_item);
607
-            return true;
608
-        }
609
-        return false;
610
-    }
611
-
612
-
613
-    /**
614
-     * calls EEH_Line_Item::find_transaction_grand_total_for_line_item()
615
-     * then EE_Line_Item::recalculate_total_including_taxes() on the result
616
-     *
617
-     * @param EE_Line_Item $line_item
618
-     * @return float
619
-     * @throws EE_Error
620
-     * @throws InvalidArgumentException
621
-     * @throws InvalidDataTypeException
622
-     * @throws InvalidInterfaceException
623
-     * @throws ReflectionException
624
-     */
625
-    public static function get_grand_total_and_recalculate_everything(EE_Line_Item $line_item)
626
-    {
627
-        $grand_total_line_item = EEH_Line_Item::find_transaction_grand_total_for_line_item($line_item);
628
-        return $grand_total_line_item->recalculate_total_including_taxes();
629
-    }
630
-
631
-
632
-    /**
633
-     * Gets the line item which contains the subtotal of all the items
634
-     *
635
-     * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
636
-     * @return EE_Line_Item
637
-     * @throws EE_Error
638
-     * @throws InvalidArgumentException
639
-     * @throws InvalidDataTypeException
640
-     * @throws InvalidInterfaceException
641
-     * @throws ReflectionException
642
-     */
643
-    public static function get_pre_tax_subtotal(EE_Line_Item $total_line_item)
644
-    {
645
-        $pre_tax_subtotal = $total_line_item->get_child_line_item('pre-tax-subtotal');
646
-        return $pre_tax_subtotal instanceof EE_Line_Item
647
-            ? $pre_tax_subtotal
648
-            : self::create_pre_tax_subtotal($total_line_item);
649
-    }
650
-
651
-
652
-    /**
653
-     * Gets the line item for the taxes subtotal
654
-     *
655
-     * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
656
-     * @return EE_Line_Item
657
-     * @throws EE_Error
658
-     * @throws InvalidArgumentException
659
-     * @throws InvalidDataTypeException
660
-     * @throws InvalidInterfaceException
661
-     * @throws ReflectionException
662
-     */
663
-    public static function get_taxes_subtotal(EE_Line_Item $total_line_item)
664
-    {
665
-        $taxes = $total_line_item->get_child_line_item('taxes');
666
-        return $taxes ? $taxes : self::create_taxes_subtotal($total_line_item);
667
-    }
668
-
669
-
670
-    /**
671
-     * sets the TXN ID on an EE_Line_Item if passed a valid EE_Transaction object
672
-     *
673
-     * @param EE_Line_Item   $line_item
674
-     * @param EE_Transaction $transaction
675
-     * @return void
676
-     * @throws EE_Error
677
-     * @throws InvalidArgumentException
678
-     * @throws InvalidDataTypeException
679
-     * @throws InvalidInterfaceException
680
-     * @throws ReflectionException
681
-     */
682
-    public static function set_TXN_ID(EE_Line_Item $line_item, $transaction = null)
683
-    {
684
-        if ($transaction) {
685
-            /** @type EEM_Transaction $EEM_Transaction */
686
-            $EEM_Transaction = EE_Registry::instance()->load_model('Transaction');
687
-            $TXN_ID = $EEM_Transaction->ensure_is_ID($transaction);
688
-            $line_item->set_TXN_ID($TXN_ID);
689
-        }
690
-    }
691
-
692
-
693
-    /**
694
-     * Creates a new default total line item for the transaction,
695
-     * and its tickets subtotal and taxes subtotal line items (and adds the
696
-     * existing taxes as children of the taxes subtotal line item)
697
-     *
698
-     * @param EE_Transaction $transaction
699
-     * @return EE_Line_Item of type total
700
-     * @throws EE_Error
701
-     * @throws InvalidArgumentException
702
-     * @throws InvalidDataTypeException
703
-     * @throws InvalidInterfaceException
704
-     * @throws ReflectionException
705
-     */
706
-    public static function create_total_line_item($transaction = null)
707
-    {
708
-        $total_line_item = EE_Line_Item::new_instance(array(
709
-            'LIN_code' => 'total',
710
-            'LIN_name' => esc_html__('Grand Total', 'event_espresso'),
711
-            'LIN_type' => EEM_Line_Item::type_total,
712
-            'OBJ_type' => EEM_Line_Item::OBJ_TYPE_TRANSACTION,
713
-        ));
714
-        $total_line_item = apply_filters(
715
-            'FHEE__EEH_Line_Item__create_total_line_item__total_line_item',
716
-            $total_line_item
717
-        );
718
-        self::set_TXN_ID($total_line_item, $transaction);
719
-        self::create_pre_tax_subtotal($total_line_item, $transaction);
720
-        self::create_taxes_subtotal($total_line_item, $transaction);
721
-        return $total_line_item;
722
-    }
723
-
724
-
725
-    /**
726
-     * Creates a default items subtotal line item
727
-     *
728
-     * @param EE_Line_Item   $total_line_item
729
-     * @param EE_Transaction $transaction
730
-     * @return EE_Line_Item
731
-     * @throws EE_Error
732
-     * @throws InvalidArgumentException
733
-     * @throws InvalidDataTypeException
734
-     * @throws InvalidInterfaceException
735
-     * @throws ReflectionException
736
-     */
737
-    protected static function create_pre_tax_subtotal(EE_Line_Item $total_line_item, $transaction = null)
738
-    {
739
-        $pre_tax_line_item = EE_Line_Item::new_instance(array(
740
-            'LIN_code' => 'pre-tax-subtotal',
741
-            'LIN_name' => esc_html__('Pre-Tax Subtotal', 'event_espresso'),
742
-            'LIN_type' => EEM_Line_Item::type_sub_total,
743
-        ));
744
-        $pre_tax_line_item = apply_filters(
745
-            'FHEE__EEH_Line_Item__create_pre_tax_subtotal__pre_tax_line_item',
746
-            $pre_tax_line_item
747
-        );
748
-        self::set_TXN_ID($pre_tax_line_item, $transaction);
749
-        $total_line_item->add_child_line_item($pre_tax_line_item);
750
-        self::create_event_subtotal($pre_tax_line_item, $transaction);
751
-        return $pre_tax_line_item;
752
-    }
753
-
754
-
755
-    /**
756
-     * Creates a line item for the taxes subtotal and finds all the tax prices
757
-     * and applies taxes to it
758
-     *
759
-     * @param EE_Line_Item   $total_line_item of type EEM_Line_Item::type_total
760
-     * @param EE_Transaction $transaction
761
-     * @return EE_Line_Item
762
-     * @throws EE_Error
763
-     * @throws InvalidArgumentException
764
-     * @throws InvalidDataTypeException
765
-     * @throws InvalidInterfaceException
766
-     * @throws ReflectionException
767
-     */
768
-    protected static function create_taxes_subtotal(EE_Line_Item $total_line_item, $transaction = null)
769
-    {
770
-        $tax_line_item = EE_Line_Item::new_instance(array(
771
-            'LIN_code'  => 'taxes',
772
-            'LIN_name'  => esc_html__('Taxes', 'event_espresso'),
773
-            'LIN_type'  => EEM_Line_Item::type_tax_sub_total,
774
-            'LIN_order' => 1000,// this should always come last
775
-        ));
776
-        $tax_line_item = apply_filters(
777
-            'FHEE__EEH_Line_Item__create_taxes_subtotal__tax_line_item',
778
-            $tax_line_item
779
-        );
780
-        self::set_TXN_ID($tax_line_item, $transaction);
781
-        $total_line_item->add_child_line_item($tax_line_item);
782
-        // and lastly, add the actual taxes
783
-        self::apply_taxes($total_line_item);
784
-        return $tax_line_item;
785
-    }
786
-
787
-
788
-    /**
789
-     * Creates a default items subtotal line item
790
-     *
791
-     * @param EE_Line_Item   $pre_tax_line_item
792
-     * @param EE_Transaction $transaction
793
-     * @param EE_Event       $event
794
-     * @return EE_Line_Item
795
-     * @throws EE_Error
796
-     * @throws InvalidArgumentException
797
-     * @throws InvalidDataTypeException
798
-     * @throws InvalidInterfaceException
799
-     * @throws ReflectionException
800
-     */
801
-    public static function create_event_subtotal(EE_Line_Item $pre_tax_line_item, $transaction = null, $event = null)
802
-    {
803
-        $event_line_item = EE_Line_Item::new_instance(array(
804
-            'LIN_code' => self::get_event_code($event),
805
-            'LIN_name' => self::get_event_name($event),
806
-            'LIN_desc' => self::get_event_desc($event),
807
-            'LIN_type' => EEM_Line_Item::type_sub_total,
808
-            'OBJ_type' => EEM_Line_Item::OBJ_TYPE_EVENT,
809
-            'OBJ_ID'   => $event instanceof EE_Event ? $event->ID() : 0,
810
-        ));
811
-        $event_line_item = apply_filters(
812
-            'FHEE__EEH_Line_Item__create_event_subtotal__event_line_item',
813
-            $event_line_item
814
-        );
815
-        self::set_TXN_ID($event_line_item, $transaction);
816
-        $pre_tax_line_item->add_child_line_item($event_line_item);
817
-        return $event_line_item;
818
-    }
819
-
820
-
821
-    /**
822
-     * Gets what the event ticket's code SHOULD be
823
-     *
824
-     * @param EE_Event $event
825
-     * @return string
826
-     * @throws EE_Error
827
-     */
828
-    public static function get_event_code($event)
829
-    {
830
-        return 'event-' . ($event instanceof EE_Event ? $event->ID() : '0');
831
-    }
832
-
833
-
834
-    /**
835
-     * Gets the event name
836
-     *
837
-     * @param EE_Event $event
838
-     * @return string
839
-     * @throws EE_Error
840
-     */
841
-    public static function get_event_name($event)
842
-    {
843
-        return $event instanceof EE_Event
844
-            ? mb_substr($event->name(), 0, 245)
845
-            : esc_html__('Event', 'event_espresso');
846
-    }
847
-
848
-
849
-    /**
850
-     * Gets the event excerpt
851
-     *
852
-     * @param EE_Event $event
853
-     * @return string
854
-     * @throws EE_Error
855
-     */
856
-    public static function get_event_desc($event)
857
-    {
858
-        return $event instanceof EE_Event ? $event->short_description() : '';
859
-    }
860
-
861
-
862
-    /**
863
-     * Given the grand total line item and a ticket, finds the event sub-total
864
-     * line item the ticket's purchase should be added onto
865
-     *
866
-     * @access public
867
-     * @param EE_Line_Item $grand_total the grand total line item
868
-     * @param EE_Ticket    $ticket
869
-     * @return EE_Line_Item
870
-     * @throws EE_Error
871
-     * @throws InvalidArgumentException
872
-     * @throws InvalidDataTypeException
873
-     * @throws InvalidInterfaceException
874
-     * @throws ReflectionException
875
-     */
876
-    public static function get_event_line_item_for_ticket(EE_Line_Item $grand_total, EE_Ticket $ticket)
877
-    {
878
-        $first_datetime = $ticket->first_datetime();
879
-        if (! $first_datetime instanceof EE_Datetime) {
880
-            throw new EE_Error(
881
-                sprintf(
882
-                    __('The supplied ticket (ID %d) has no datetimes', 'event_espresso'),
883
-                    $ticket->ID()
884
-                )
885
-            );
886
-        }
887
-        $event = $first_datetime->event();
888
-        if (! $event instanceof EE_Event) {
889
-            throw new EE_Error(
890
-                sprintf(
891
-                    esc_html__(
892
-                        'The supplied ticket (ID %d) has no event data associated with it.',
893
-                        'event_espresso'
894
-                    ),
895
-                    $ticket->ID()
896
-                )
897
-            );
898
-        }
899
-        $events_sub_total = EEH_Line_Item::get_event_line_item($grand_total, $event);
900
-        if (! $events_sub_total instanceof EE_Line_Item) {
901
-            throw new EE_Error(
902
-                sprintf(
903
-                    esc_html__(
904
-                        'There is no events sub-total for ticket %s on total line item %d',
905
-                        'event_espresso'
906
-                    ),
907
-                    $ticket->ID(),
908
-                    $grand_total->ID()
909
-                )
910
-            );
911
-        }
912
-        return $events_sub_total;
913
-    }
914
-
915
-
916
-    /**
917
-     * Gets the event line item
918
-     *
919
-     * @param EE_Line_Item $grand_total
920
-     * @param EE_Event     $event
921
-     * @return EE_Line_Item for the event subtotal which is a child of $grand_total
922
-     * @throws EE_Error
923
-     * @throws InvalidArgumentException
924
-     * @throws InvalidDataTypeException
925
-     * @throws InvalidInterfaceException
926
-     * @throws ReflectionException
927
-     */
928
-    public static function get_event_line_item(EE_Line_Item $grand_total, $event)
929
-    {
930
-        /** @type EE_Event $event */
931
-        $event = EEM_Event::instance()->ensure_is_obj($event, true);
932
-        $event_line_item = null;
933
-        $found = false;
934
-        foreach (EEH_Line_Item::get_event_subtotals($grand_total) as $event_line_item) {
935
-            // default event subtotal, we should only ever find this the first time this method is called
936
-            if (! $event_line_item->OBJ_ID()) {
937
-                // let's use this! but first... set the event details
938
-                EEH_Line_Item::set_event_subtotal_details($event_line_item, $event);
939
-                $found = true;
940
-                break;
941
-            }
942
-            if ($event_line_item->OBJ_ID() === $event->ID()) {
943
-                // found existing line item for this event in the cart, so break out of loop and use this one
944
-                $found = true;
945
-                break;
946
-            }
947
-        }
948
-        if (! $found) {
949
-            // there is no event sub-total yet, so add it
950
-            $pre_tax_subtotal = EEH_Line_Item::get_pre_tax_subtotal($grand_total);
951
-            // create a new "event" subtotal below that
952
-            $event_line_item = EEH_Line_Item::create_event_subtotal($pre_tax_subtotal, null, $event);
953
-            // and set the event details
954
-            EEH_Line_Item::set_event_subtotal_details($event_line_item, $event);
955
-        }
956
-        return $event_line_item;
957
-    }
958
-
959
-
960
-    /**
961
-     * Creates a default items subtotal line item
962
-     *
963
-     * @param EE_Line_Item   $event_line_item
964
-     * @param EE_Event       $event
965
-     * @param EE_Transaction $transaction
966
-     * @return void
967
-     * @throws EE_Error
968
-     * @throws InvalidArgumentException
969
-     * @throws InvalidDataTypeException
970
-     * @throws InvalidInterfaceException
971
-     * @throws ReflectionException
972
-     */
973
-    public static function set_event_subtotal_details(
974
-        EE_Line_Item $event_line_item,
975
-        EE_Event $event,
976
-        $transaction = null
977
-    ) {
978
-        if ($event instanceof EE_Event) {
979
-            $event_line_item->set_code(self::get_event_code($event));
980
-            $event_line_item->set_name(self::get_event_name($event));
981
-            $event_line_item->set_desc(self::get_event_desc($event));
982
-            $event_line_item->set_OBJ_ID($event->ID());
983
-        }
984
-        self::set_TXN_ID($event_line_item, $transaction);
985
-    }
986
-
987
-
988
-    /**
989
-     * Finds what taxes should apply, adds them as tax line items under the taxes sub-total,
990
-     * and recalculates the taxes sub-total and the grand total. Resets the taxes, so
991
-     * any old taxes are removed
992
-     *
993
-     * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
994
-     * @param bool         $update_txn_status
995
-     * @return bool
996
-     * @throws EE_Error
997
-     * @throws InvalidArgumentException
998
-     * @throws InvalidDataTypeException
999
-     * @throws InvalidInterfaceException
1000
-     * @throws ReflectionException
1001
-     * @throws RuntimeException
1002
-     */
1003
-    public static function apply_taxes(EE_Line_Item $total_line_item, $update_txn_status = false)
1004
-    {
1005
-        $total_line_item = EEH_Line_Item::find_transaction_grand_total_for_line_item($total_line_item);
1006
-        $taxes_line_item = self::get_taxes_subtotal($total_line_item);
1007
-        $existing_global_taxes = $taxes_line_item->tax_descendants();
1008
-        $updates = false;
1009
-        // loop thru taxes
1010
-        $global_taxes = EEH_Line_Item::getGlobalTaxes();
1011
-        foreach ($global_taxes as $order => $taxes) {
1012
-            foreach ($taxes as $tax) {
1013
-                if ($tax instanceof EE_Price) {
1014
-                    $found = false;
1015
-                    // check if this is already an existing tax
1016
-                    foreach ($existing_global_taxes as $existing_global_tax) {
1017
-                        if ($tax->ID() === $existing_global_tax->OBJ_ID()) {
1018
-                            // maybe update the tax rate in case it has changed
1019
-                            if ($existing_global_tax->percent() !== $tax->amount()) {
1020
-                                $existing_global_tax->set_percent($tax->amount());
1021
-                                $existing_global_tax->save();
1022
-                                $updates = true;
1023
-                            }
1024
-                            $found = true;
1025
-                            break;
1026
-                        }
1027
-                    }
1028
-                    if (! $found) {
1029
-                        // add a new line item for this global tax
1030
-                        $tax_line_item = apply_filters(
1031
-                            'FHEE__EEH_Line_Item__apply_taxes__tax_line_item',
1032
-                            EE_Line_Item::new_instance(
1033
-                                [
1034
-                                    'LIN_name'       => $tax->name(),
1035
-                                    'LIN_desc'       => $tax->desc(),
1036
-                                    'LIN_percent'    => $tax->amount(),
1037
-                                    'LIN_is_taxable' => false,
1038
-                                    'LIN_order'      => $order,
1039
-                                    'LIN_total'      => 0,
1040
-                                    'LIN_type'       => EEM_Line_Item::type_tax,
1041
-                                    'OBJ_type'       => EEM_Line_Item::OBJ_TYPE_PRICE,
1042
-                                    'OBJ_ID'         => $tax->ID(),
1043
-                                ]
1044
-                            )
1045
-                        );
1046
-                        $updates = $taxes_line_item->add_child_line_item($tax_line_item) ? true : $updates;
1047
-                    }
1048
-                }
1049
-            }
1050
-        }
1051
-        // only recalculate totals if something changed
1052
-        if ($updates) {
1053
-            $total_line_item->recalculate_total_including_taxes($update_txn_status);
1054
-            return true;
1055
-        }
1056
-        return false;
1057
-    }
1058
-
1059
-
1060
-    /**
1061
-     * Ensures that taxes have been applied to the order, if not applies them.
1062
-     * Returns the total amount of tax
1063
-     *
1064
-     * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
1065
-     * @return float
1066
-     * @throws EE_Error
1067
-     * @throws InvalidArgumentException
1068
-     * @throws InvalidDataTypeException
1069
-     * @throws InvalidInterfaceException
1070
-     * @throws ReflectionException
1071
-     */
1072
-    public static function ensure_taxes_applied($total_line_item)
1073
-    {
1074
-        $taxes_subtotal = self::get_taxes_subtotal($total_line_item);
1075
-        if (! $taxes_subtotal->children()) {
1076
-            self::apply_taxes($total_line_item);
1077
-        }
1078
-        return $taxes_subtotal->total();
1079
-    }
1080
-
1081
-
1082
-    /**
1083
-     * Deletes ALL children of the passed line item
1084
-     *
1085
-     * @param EE_Line_Item $parent_line_item
1086
-     * @return bool
1087
-     * @throws EE_Error
1088
-     * @throws InvalidArgumentException
1089
-     * @throws InvalidDataTypeException
1090
-     * @throws InvalidInterfaceException
1091
-     * @throws ReflectionException
1092
-     */
1093
-    public static function delete_all_child_items(EE_Line_Item $parent_line_item)
1094
-    {
1095
-        $deleted = 0;
1096
-        foreach ($parent_line_item->children() as $child_line_item) {
1097
-            if ($child_line_item instanceof EE_Line_Item) {
1098
-                $deleted += EEH_Line_Item::delete_all_child_items($child_line_item);
1099
-                if ($child_line_item->ID()) {
1100
-                    $child_line_item->delete();
1101
-                    unset($child_line_item);
1102
-                } else {
1103
-                    $parent_line_item->delete_child_line_item($child_line_item->code());
1104
-                }
1105
-                $deleted++;
1106
-            }
1107
-        }
1108
-        return $deleted;
1109
-    }
1110
-
1111
-
1112
-    /**
1113
-     * Deletes the line items as indicated by the line item code(s) provided,
1114
-     * regardless of where they're found in the line item tree. Automatically
1115
-     * re-calculates the line item totals and updates the related transaction. But
1116
-     * DOES NOT automatically upgrade the transaction's registrations' final prices (which
1117
-     * should probably change because of this).
1118
-     * You should call EE_Registration_Processor::calculate_reg_final_prices_per_line_item()
1119
-     * after using this, to keep the registration final prices in-sync with the transaction's total.
1120
-     *
1121
-     * @param EE_Line_Item      $total_line_item of type EEM_Line_Item::type_total
1122
-     * @param array|bool|string $line_item_codes
1123
-     * @return int number of items successfully removed
1124
-     * @throws EE_Error
1125
-     */
1126
-    public static function delete_items(EE_Line_Item $total_line_item, $line_item_codes = false)
1127
-    {
1128
-
1129
-        if ($total_line_item->type() !== EEM_Line_Item::type_total) {
1130
-            EE_Error::doing_it_wrong(
1131
-                'EEH_Line_Item::delete_items',
1132
-                esc_html__(
1133
-                    'This static method should only be called with a TOTAL line item, otherwise we won\'t recalculate the totals correctly',
1134
-                    'event_espresso'
1135
-                ),
1136
-                '4.6.18'
1137
-            );
1138
-        }
1139
-        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
1140
-
1141
-        // check if only a single line_item_id was passed
1142
-        if (! empty($line_item_codes) && ! is_array($line_item_codes)) {
1143
-            // place single line_item_id in an array to appear as multiple line_item_ids
1144
-            $line_item_codes = array($line_item_codes);
1145
-        }
1146
-        $removals = 0;
1147
-        // cycle thru line_item_ids
1148
-        foreach ($line_item_codes as $line_item_id) {
1149
-            $removals += $total_line_item->delete_child_line_item($line_item_id);
1150
-        }
1151
-
1152
-        if ($removals > 0) {
1153
-            $total_line_item->recalculate_taxes_and_tax_total();
1154
-            return $removals;
1155
-        } else {
1156
-            return false;
1157
-        }
1158
-    }
1159
-
1160
-
1161
-    /**
1162
-     * Overwrites the previous tax by clearing out the old taxes, and creates a new
1163
-     * tax and updates the total line item accordingly
1164
-     *
1165
-     * @param EE_Line_Item $total_line_item
1166
-     * @param float        $amount
1167
-     * @param string       $name
1168
-     * @param string       $description
1169
-     * @param string       $code
1170
-     * @param boolean      $add_to_existing_line_item
1171
-     *                          if true, and a duplicate line item with the same code is found,
1172
-     *                          $amount will be added onto it; otherwise will simply set the taxes to match $amount
1173
-     * @return EE_Line_Item the new tax line item created
1174
-     * @throws EE_Error
1175
-     * @throws InvalidArgumentException
1176
-     * @throws InvalidDataTypeException
1177
-     * @throws InvalidInterfaceException
1178
-     * @throws ReflectionException
1179
-     */
1180
-    public static function set_total_tax_to(
1181
-        EE_Line_Item $total_line_item,
1182
-        $amount,
1183
-        $name = null,
1184
-        $description = null,
1185
-        $code = null,
1186
-        $add_to_existing_line_item = false
1187
-    ) {
1188
-        $tax_subtotal = self::get_taxes_subtotal($total_line_item);
1189
-        $taxable_total = $total_line_item->taxable_total();
1190
-
1191
-        if ($add_to_existing_line_item) {
1192
-            $new_tax = $tax_subtotal->get_child_line_item($code);
1193
-            EEM_Line_Item::instance()->delete(
1194
-                array(array('LIN_code' => array('!=', $code), 'LIN_parent' => $tax_subtotal->ID()))
1195
-            );
1196
-        } else {
1197
-            $new_tax = null;
1198
-            $tax_subtotal->delete_children_line_items();
1199
-        }
1200
-        if ($new_tax) {
1201
-            $new_tax->set_total($new_tax->total() + $amount);
1202
-            $new_tax->set_percent($taxable_total ? $new_tax->total() / $taxable_total * 100 : 0);
1203
-        } else {
1204
-            // no existing tax item. Create it
1205
-            $new_tax = EE_Line_Item::new_instance(array(
1206
-                'TXN_ID'      => $total_line_item->TXN_ID(),
1207
-                'LIN_name'    => $name ?: esc_html__('Tax', 'event_espresso'),
1208
-                'LIN_desc'    => $description ?: '',
1209
-                'LIN_percent' => $taxable_total ? ($amount / $taxable_total * 100) : 0,
1210
-                'LIN_total'   => $amount,
1211
-                'LIN_parent'  => $tax_subtotal->ID(),
1212
-                'LIN_type'    => EEM_Line_Item::type_tax,
1213
-                'LIN_code'    => $code,
1214
-            ));
1215
-        }
1216
-
1217
-        $new_tax = apply_filters(
1218
-            'FHEE__EEH_Line_Item__set_total_tax_to__new_tax_subtotal',
1219
-            $new_tax,
1220
-            $total_line_item
1221
-        );
1222
-        $new_tax->save();
1223
-        $tax_subtotal->set_total($new_tax->total());
1224
-        $tax_subtotal->save();
1225
-        $total_line_item->recalculate_total_including_taxes();
1226
-        return $new_tax;
1227
-    }
1228
-
1229
-
1230
-    /**
1231
-     * Makes all the line items which are children of $line_item taxable (or not).
1232
-     * Does NOT save the line items
1233
-     *
1234
-     * @param EE_Line_Item $line_item
1235
-     * @param boolean      $taxable
1236
-     * @param string       $code_substring_for_whitelist if this string is part of the line item's code
1237
-     *                                                   it will be whitelisted (ie, except from becoming taxable)
1238
-     * @throws EE_Error
1239
-     */
1240
-    public static function set_line_items_taxable(
1241
-        EE_Line_Item $line_item,
1242
-        $taxable = true,
1243
-        $code_substring_for_whitelist = null
1244
-    ) {
1245
-        $whitelisted = false;
1246
-        if ($code_substring_for_whitelist !== null) {
1247
-            $whitelisted = strpos($line_item->code(), $code_substring_for_whitelist) !== false;
1248
-        }
1249
-        if (! $whitelisted && $line_item->is_line_item()) {
1250
-            $line_item->set_is_taxable($taxable);
1251
-        }
1252
-        foreach ($line_item->children() as $child_line_item) {
1253
-            EEH_Line_Item::set_line_items_taxable(
1254
-                $child_line_item,
1255
-                $taxable,
1256
-                $code_substring_for_whitelist
1257
-            );
1258
-        }
1259
-    }
1260
-
1261
-
1262
-    /**
1263
-     * Gets all descendants that are event subtotals
1264
-     *
1265
-     * @uses  EEH_Line_Item::get_subtotals_of_object_type()
1266
-     * @param EE_Line_Item $parent_line_item - the line item to find descendants of
1267
-     * @return EE_Line_Item[]
1268
-     * @throws EE_Error
1269
-     */
1270
-    public static function get_event_subtotals(EE_Line_Item $parent_line_item)
1271
-    {
1272
-        return self::get_subtotals_of_object_type($parent_line_item, EEM_Line_Item::OBJ_TYPE_EVENT);
1273
-    }
1274
-
1275
-
1276
-    /**
1277
-     * Gets all descendants subtotals that match the supplied object type
1278
-     *
1279
-     * @uses  EEH_Line_Item::_get_descendants_by_type_and_object_type()
1280
-     * @param EE_Line_Item $parent_line_item - the line item to find descendants of
1281
-     * @param string       $obj_type
1282
-     * @return EE_Line_Item[]
1283
-     * @throws EE_Error
1284
-     */
1285
-    public static function get_subtotals_of_object_type(EE_Line_Item $parent_line_item, $obj_type = '')
1286
-    {
1287
-        return self::_get_descendants_by_type_and_object_type(
1288
-            $parent_line_item,
1289
-            EEM_Line_Item::type_sub_total,
1290
-            $obj_type
1291
-        );
1292
-    }
1293
-
1294
-
1295
-    /**
1296
-     * Gets all descendants that are tickets
1297
-     *
1298
-     * @uses  EEH_Line_Item::get_line_items_of_object_type()
1299
-     * @param EE_Line_Item $parent_line_item - the line item to find descendants of
1300
-     * @return EE_Line_Item[]
1301
-     * @throws EE_Error
1302
-     */
1303
-    public static function get_ticket_line_items(EE_Line_Item $parent_line_item)
1304
-    {
1305
-        return self::get_line_items_of_object_type(
1306
-            $parent_line_item,
1307
-            EEM_Line_Item::OBJ_TYPE_TICKET
1308
-        );
1309
-    }
1310
-
1311
-
1312
-    /**
1313
-     * Gets all descendants subtotals that match the supplied object type
1314
-     *
1315
-     * @uses  EEH_Line_Item::_get_descendants_by_type_and_object_type()
1316
-     * @param EE_Line_Item $parent_line_item - the line item to find descendants of
1317
-     * @param string       $obj_type
1318
-     * @return EE_Line_Item[]
1319
-     * @throws EE_Error
1320
-     */
1321
-    public static function get_line_items_of_object_type(EE_Line_Item $parent_line_item, $obj_type = '')
1322
-    {
1323
-        return self::_get_descendants_by_type_and_object_type(
1324
-            $parent_line_item,
1325
-            EEM_Line_Item::type_line_item,
1326
-            $obj_type
1327
-        );
1328
-    }
1329
-
1330
-
1331
-    /**
1332
-     * Gets all the descendants (ie, children or children of children etc) that are of the type 'tax'
1333
-     *
1334
-     * @uses  EEH_Line_Item::get_descendants_of_type()
1335
-     * @param EE_Line_Item $parent_line_item - the line item to find descendants of
1336
-     * @return EE_Line_Item[]
1337
-     * @throws EE_Error
1338
-     */
1339
-    public static function get_tax_descendants(EE_Line_Item $parent_line_item)
1340
-    {
1341
-        return EEH_Line_Item::get_descendants_of_type(
1342
-            $parent_line_item,
1343
-            EEM_Line_Item::type_tax
1344
-        );
1345
-    }
1346
-
1347
-
1348
-    /**
1349
-     * Gets all the real items purchased which are children of this item
1350
-     *
1351
-     * @uses  EEH_Line_Item::get_descendants_of_type()
1352
-     * @param EE_Line_Item $parent_line_item - the line item to find descendants of
1353
-     * @return EE_Line_Item[]
1354
-     * @throws EE_Error
1355
-     */
1356
-    public static function get_line_item_descendants(EE_Line_Item $parent_line_item)
1357
-    {
1358
-        return EEH_Line_Item::get_descendants_of_type(
1359
-            $parent_line_item,
1360
-            EEM_Line_Item::type_line_item
1361
-        );
1362
-    }
1363
-
1364
-
1365
-    /**
1366
-     * Gets all descendants of supplied line item that match the supplied line item type
1367
-     *
1368
-     * @uses  EEH_Line_Item::_get_descendants_by_type_and_object_type()
1369
-     * @param EE_Line_Item $parent_line_item - the line item to find descendants of
1370
-     * @param string       $line_item_type   one of the EEM_Line_Item constants
1371
-     * @return EE_Line_Item[]
1372
-     * @throws EE_Error
1373
-     */
1374
-    public static function get_descendants_of_type(EE_Line_Item $parent_line_item, $line_item_type)
1375
-    {
1376
-        return self::_get_descendants_by_type_and_object_type(
1377
-            $parent_line_item,
1378
-            $line_item_type,
1379
-            null
1380
-        );
1381
-    }
1382
-
1383
-
1384
-    /**
1385
-     * Gets all descendants of supplied line item that match the supplied line item type and possibly the object type
1386
-     * as well
1387
-     *
1388
-     * @param EE_Line_Item  $parent_line_item - the line item to find descendants of
1389
-     * @param string        $line_item_type   one of the EEM_Line_Item constants
1390
-     * @param string | NULL $obj_type         object model class name (minus prefix) or NULL to ignore object type when
1391
-     *                                        searching
1392
-     * @return EE_Line_Item[]
1393
-     * @throws EE_Error
1394
-     */
1395
-    protected static function _get_descendants_by_type_and_object_type(
1396
-        EE_Line_Item $parent_line_item,
1397
-        $line_item_type,
1398
-        $obj_type = null
1399
-    ) {
1400
-        $objects = array();
1401
-        foreach ($parent_line_item->children() as $child_line_item) {
1402
-            if ($child_line_item instanceof EE_Line_Item) {
1403
-                if (
1404
-                    $child_line_item->type() === $line_item_type
1405
-                    && (
1406
-                        $child_line_item->OBJ_type() === $obj_type || $obj_type === null
1407
-                    )
1408
-                ) {
1409
-                    $objects[] = $child_line_item;
1410
-                } else {
1411
-                    // go-through-all-its children looking for more matches
1412
-                    $objects = array_merge(
1413
-                        $objects,
1414
-                        self::_get_descendants_by_type_and_object_type(
1415
-                            $child_line_item,
1416
-                            $line_item_type,
1417
-                            $obj_type
1418
-                        )
1419
-                    );
1420
-                }
1421
-            }
1422
-        }
1423
-        return $objects;
1424
-    }
1425
-
1426
-
1427
-    /**
1428
-     * Gets all descendants subtotals that match the supplied object type
1429
-     *
1430
-     * @uses  EEH_Line_Item::_get_descendants_by_type_and_object_type()
1431
-     * @param EE_Line_Item $parent_line_item - the line item to find descendants of
1432
-     * @param string       $OBJ_type         object type (like Event)
1433
-     * @param array        $OBJ_IDs          array of OBJ_IDs
1434
-     * @return EE_Line_Item[]
1435
-     * @throws EE_Error
1436
-     */
1437
-    public static function get_line_items_by_object_type_and_IDs(
1438
-        EE_Line_Item $parent_line_item,
1439
-        $OBJ_type = '',
1440
-        $OBJ_IDs = array()
1441
-    ) {
1442
-        return self::_get_descendants_by_object_type_and_object_ID(
1443
-            $parent_line_item,
1444
-            $OBJ_type,
1445
-            $OBJ_IDs
1446
-        );
1447
-    }
1448
-
1449
-
1450
-    /**
1451
-     * Gets all descendants of supplied line item that match the supplied line item type and possibly the object type
1452
-     * as well
1453
-     *
1454
-     * @param EE_Line_Item $parent_line_item - the line item to find descendants of
1455
-     * @param string       $OBJ_type         object type (like Event)
1456
-     * @param array        $OBJ_IDs          array of OBJ_IDs
1457
-     * @return EE_Line_Item[]
1458
-     * @throws EE_Error
1459
-     */
1460
-    protected static function _get_descendants_by_object_type_and_object_ID(
1461
-        EE_Line_Item $parent_line_item,
1462
-        $OBJ_type,
1463
-        $OBJ_IDs
1464
-    ) {
1465
-        $objects = array();
1466
-        foreach ($parent_line_item->children() as $child_line_item) {
1467
-            if ($child_line_item instanceof EE_Line_Item) {
1468
-                if (
1469
-                    $child_line_item->OBJ_type() === $OBJ_type
1470
-                    && is_array($OBJ_IDs)
1471
-                    && in_array($child_line_item->OBJ_ID(), $OBJ_IDs)
1472
-                ) {
1473
-                    $objects[] = $child_line_item;
1474
-                } else {
1475
-                    // go-through-all-its children looking for more matches
1476
-                    $objects = array_merge(
1477
-                        $objects,
1478
-                        self::_get_descendants_by_object_type_and_object_ID(
1479
-                            $child_line_item,
1480
-                            $OBJ_type,
1481
-                            $OBJ_IDs
1482
-                        )
1483
-                    );
1484
-                }
1485
-            }
1486
-        }
1487
-        return $objects;
1488
-    }
1489
-
1490
-
1491
-    /**
1492
-     * Uses a breadth-first-search in order to find the nearest descendant of
1493
-     * the specified type and returns it, else NULL
1494
-     *
1495
-     * @uses  EEH_Line_Item::_get_nearest_descendant()
1496
-     * @param EE_Line_Item $parent_line_item - the line item to find descendants of
1497
-     * @param string       $type             like one of the EEM_Line_Item::type_*
1498
-     * @return EE_Line_Item
1499
-     * @throws EE_Error
1500
-     * @throws InvalidArgumentException
1501
-     * @throws InvalidDataTypeException
1502
-     * @throws InvalidInterfaceException
1503
-     * @throws ReflectionException
1504
-     */
1505
-    public static function get_nearest_descendant_of_type(EE_Line_Item $parent_line_item, $type)
1506
-    {
1507
-        return self::_get_nearest_descendant($parent_line_item, 'LIN_type', $type);
1508
-    }
1509
-
1510
-
1511
-    /**
1512
-     * Uses a breadth-first-search in order to find the nearest descendant
1513
-     * having the specified LIN_code and returns it, else NULL
1514
-     *
1515
-     * @uses  EEH_Line_Item::_get_nearest_descendant()
1516
-     * @param EE_Line_Item $parent_line_item - the line item to find descendants of
1517
-     * @param string       $code             any value used for LIN_code
1518
-     * @return EE_Line_Item
1519
-     * @throws EE_Error
1520
-     * @throws InvalidArgumentException
1521
-     * @throws InvalidDataTypeException
1522
-     * @throws InvalidInterfaceException
1523
-     * @throws ReflectionException
1524
-     */
1525
-    public static function get_nearest_descendant_having_code(EE_Line_Item $parent_line_item, $code)
1526
-    {
1527
-        return self::_get_nearest_descendant($parent_line_item, 'LIN_code', $code);
1528
-    }
1529
-
1530
-
1531
-    /**
1532
-     * Uses a breadth-first-search in order to find the nearest descendant
1533
-     * having the specified LIN_code and returns it, else NULL
1534
-     *
1535
-     * @param EE_Line_Item $parent_line_item - the line item to find descendants of
1536
-     * @param string       $search_field     name of EE_Line_Item property
1537
-     * @param string       $value            any value stored in $search_field
1538
-     * @return EE_Line_Item
1539
-     * @throws EE_Error
1540
-     * @throws InvalidArgumentException
1541
-     * @throws InvalidDataTypeException
1542
-     * @throws InvalidInterfaceException
1543
-     * @throws ReflectionException
1544
-     */
1545
-    protected static function _get_nearest_descendant(EE_Line_Item $parent_line_item, $search_field, $value)
1546
-    {
1547
-        foreach ($parent_line_item->children() as $child) {
1548
-            if ($child->get($search_field) == $value) {
1549
-                return $child;
1550
-            }
1551
-        }
1552
-        foreach ($parent_line_item->children() as $child) {
1553
-            $descendant_found = self::_get_nearest_descendant(
1554
-                $child,
1555
-                $search_field,
1556
-                $value
1557
-            );
1558
-            if ($descendant_found) {
1559
-                return $descendant_found;
1560
-            }
1561
-        }
1562
-        return null;
1563
-    }
1564
-
1565
-
1566
-    /**
1567
-     * if passed line item has a TXN ID, uses that to jump directly to the grand total line item for the transaction,
1568
-     * else recursively walks up the line item tree until a parent of type total is found,
1569
-     *
1570
-     * @param EE_Line_Item $line_item
1571
-     * @return EE_Line_Item
1572
-     * @throws EE_Error
1573
-     * @throws ReflectionException
1574
-     */
1575
-    public static function find_transaction_grand_total_for_line_item(EE_Line_Item $line_item): EE_Line_Item
1576
-    {
1577
-        if ($line_item->is_total()) {
1578
-            return $line_item;
1579
-        }
1580
-        if ($line_item->TXN_ID()) {
1581
-            $total_line_item = $line_item->transaction()->total_line_item(false);
1582
-            if ($total_line_item instanceof EE_Line_Item) {
1583
-                return $total_line_item;
1584
-            }
1585
-        } else {
1586
-            $line_item_parent = $line_item->parent();
1587
-            if ($line_item_parent instanceof EE_Line_Item) {
1588
-                if ($line_item_parent->is_total()) {
1589
-                    return $line_item_parent;
1590
-                }
1591
-                return EEH_Line_Item::find_transaction_grand_total_for_line_item($line_item_parent);
1592
-            }
1593
-        }
1594
-        throw new EE_Error(
1595
-            sprintf(
1596
-                esc_html__(
1597
-                    'A valid grand total for line item %1$d was not found.',
1598
-                    'event_espresso'
1599
-                ),
1600
-                $line_item->ID()
1601
-            )
1602
-        );
1603
-    }
1604
-
1605
-
1606
-    /**
1607
-     * Prints out a representation of the line item tree
1608
-     *
1609
-     * @param EE_Line_Item $line_item
1610
-     * @param int          $indentation
1611
-     * @return void
1612
-     * @throws EE_Error
1613
-     */
1614
-    public static function visualize(EE_Line_Item $line_item, $indentation = 0)
1615
-    {
1616
-        $new_line = defined('EE_TESTS_DIR') ? "\n" : '<br />';
1617
-        echo $new_line;
1618
-        if (! $indentation) {
1619
-            echo $new_line;
1620
-        }
1621
-        echo str_repeat('. ', $indentation);
1622
-        $breakdown = '';
1623
-        if ($line_item->is_line_item() || $line_item->is_sub_line_item() || $line_item->isSubTax()) {
1624
-            if ($line_item->is_percent()) {
1625
-                $breakdown = "{$line_item->percent()}%";
1626
-            } else {
1627
-                $breakdown = "\${$line_item->unit_price()} x {$line_item->quantity()}";
1628
-            }
1629
-        }
1630
-        echo $line_item->name();
1631
-        echo " [ ID:{$line_item->ID()} | qty:{$line_item->quantity()} ] {$line_item->type()} : ";
1632
-        echo "\${$line_item->total()}";
1633
-        if ($breakdown) {
1634
-            echo " ( {$breakdown} )";
1635
-        }
1636
-        if ($line_item->is_taxable()) {
1637
-            echo '  * taxable';
1638
-        }
1639
-        if ($line_item->children()) {
1640
-            foreach ($line_item->children() as $child) {
1641
-                self::visualize($child, $indentation + 1);
1642
-            }
1643
-        }
1644
-        if (! $indentation) {
1645
-            echo $new_line . $new_line;
1646
-        }
1647
-    }
1648
-
1649
-
1650
-    /**
1651
-     * Calculates the registration's final price, taking into account that they
1652
-     * need to not only help pay for their OWN ticket, but also any transaction-wide surcharges and taxes,
1653
-     * and receive a portion of any transaction-wide discounts.
1654
-     * eg1, if I buy a $1 ticket and brent buys a $9 ticket, and we receive a $5 discount
1655
-     * then I'll get 1/10 of that $5 discount, which is $0.50, and brent will get
1656
-     * 9/10ths of that $5 discount, which is $4.50. So my final price should be $0.50
1657
-     * and brent's final price should be $5.50.
1658
-     * In order to do this, we basically need to traverse the line item tree calculating
1659
-     * the running totals (just as if we were recalculating the total), but when we identify
1660
-     * regular line items, we need to keep track of their share of the grand total.
1661
-     * Also, we need to keep track of the TAXABLE total for each ticket purchase, so
1662
-     * we can know how to apply taxes to it. (Note: "taxable total" does not equal the "pretax total"
1663
-     * when there are non-taxable items; otherwise they would be the same)
1664
-     *
1665
-     * @param EE_Line_Item $line_item
1666
-     * @param array        $billable_ticket_quantities  array of EE_Ticket IDs and their corresponding quantity that
1667
-     *                                                  can be included in price calculations at this moment
1668
-     * @return array        keys are line items for tickets IDs and values are their share of the running total,
1669
-     *                                                  plus the key 'total', and 'taxable' which also has keys of all
1670
-     *                                                  the ticket IDs.
1671
-     *                                                  Eg array(
1672
-     *                                                      12 => 4.3
1673
-     *                                                      23 => 8.0
1674
-     *                                                      'total' => 16.6,
1675
-     *                                                      'taxable' => array(
1676
-     *                                                          12 => 10,
1677
-     *                                                          23 => 4
1678
-     *                                                      ).
1679
-     *                                                  So to find which registrations have which final price, we need
1680
-     *                                                  to find which line item is theirs, which can be done with
1681
-     *                                                  `EEM_Line_Item::instance()->get_line_item_for_registration(
1682
-     *                                                  $registration );`
1683
-     * @throws EE_Error
1684
-     * @throws InvalidArgumentException
1685
-     * @throws InvalidDataTypeException
1686
-     * @throws InvalidInterfaceException
1687
-     * @throws ReflectionException
1688
-     */
1689
-    public static function calculate_reg_final_prices_per_line_item(
1690
-        EE_Line_Item $line_item,
1691
-        $billable_ticket_quantities = array()
1692
-    ) {
1693
-        $running_totals = [
1694
-            'total'   => 0,
1695
-            'taxable' => ['total' => 0]
1696
-        ];
1697
-        foreach ($line_item->children() as $child_line_item) {
1698
-            switch ($child_line_item->type()) {
1699
-                case EEM_Line_Item::type_sub_total:
1700
-                    $running_totals_from_subtotal = EEH_Line_Item::calculate_reg_final_prices_per_line_item(
1701
-                        $child_line_item,
1702
-                        $billable_ticket_quantities
1703
-                    );
1704
-                    // combine arrays but preserve numeric keys
1705
-                    $running_totals = array_replace_recursive($running_totals_from_subtotal, $running_totals);
1706
-                    $running_totals['total'] += $running_totals_from_subtotal['total'];
1707
-                    $running_totals['taxable']['total'] += $running_totals_from_subtotal['taxable']['total'];
1708
-                    break;
1709
-
1710
-                case EEM_Line_Item::type_tax_sub_total:
1711
-                    // find how much the taxes percentage is
1712
-                    if ($child_line_item->percent() !== 0) {
1713
-                        $tax_percent_decimal = $child_line_item->percent() / 100;
1714
-                    } else {
1715
-                        $tax_percent_decimal = EE_Taxes::get_total_taxes_percentage() / 100;
1716
-                    }
1717
-                    // and apply to all the taxable totals, and add to the pretax totals
1718
-                    foreach ($running_totals as $line_item_id => $this_running_total) {
1719
-                        // "total" and "taxable" array key is an exception
1720
-                        if ($line_item_id === 'taxable') {
1721
-                            continue;
1722
-                        }
1723
-                        $taxable_total = $running_totals['taxable'][ $line_item_id ];
1724
-                        $running_totals[ $line_item_id ] += ($taxable_total * $tax_percent_decimal);
1725
-                    }
1726
-                    break;
1727
-
1728
-                case EEM_Line_Item::type_line_item:
1729
-                    // ticket line items or ????
1730
-                    if ($child_line_item->OBJ_type() === EEM_Line_Item::OBJ_TYPE_TICKET) {
1731
-                        // kk it's a ticket
1732
-                        if (isset($running_totals[ $child_line_item->ID() ])) {
1733
-                            // huh? that shouldn't happen.
1734
-                            $running_totals['total'] += $child_line_item->total();
1735
-                        } else {
1736
-                            // its not in our running totals yet. great.
1737
-                            if ($child_line_item->is_taxable()) {
1738
-                                $taxable_amount = $child_line_item->unit_price();
1739
-                            } else {
1740
-                                $taxable_amount = 0;
1741
-                            }
1742
-                            // are we only calculating totals for some tickets?
1743
-                            if (isset($billable_ticket_quantities[ $child_line_item->OBJ_ID() ])) {
1744
-                                $quantity = $billable_ticket_quantities[ $child_line_item->OBJ_ID() ];
1745
-                                $running_totals[ $child_line_item->ID() ] = $quantity
1746
-                                    ? $child_line_item->unit_price()
1747
-                                    : 0;
1748
-                                $running_totals['taxable'][ $child_line_item->ID() ] = $quantity
1749
-                                    ? $taxable_amount
1750
-                                    : 0;
1751
-                            } else {
1752
-                                $quantity = $child_line_item->quantity();
1753
-                                $running_totals[ $child_line_item->ID() ] = $child_line_item->unit_price();
1754
-                                $running_totals['taxable'][ $child_line_item->ID() ] = $taxable_amount;
1755
-                            }
1756
-                            $running_totals['taxable']['total'] += $taxable_amount * $quantity;
1757
-                            $running_totals['total'] += $child_line_item->unit_price() * $quantity;
1758
-                        }
1759
-                    } else {
1760
-                        // it's some other type of item added to the cart
1761
-                        // it should affect the running totals
1762
-                        // basically we want to convert it into a PERCENT modifier. Because
1763
-                        // more clearly affect all registration's final price equally
1764
-                        $line_items_percent_of_running_total = $running_totals['total'] > 0
1765
-                            ? ($child_line_item->total() / $running_totals['total']) + 1
1766
-                            : 1;
1767
-                        foreach ($running_totals as $line_item_id => $this_running_total) {
1768
-                            // the "taxable" array key is an exception
1769
-                            if ($line_item_id === 'taxable') {
1770
-                                continue;
1771
-                            }
1772
-                            // update the running totals
1773
-                            // yes this actually even works for the running grand total!
1774
-                            $running_totals[ $line_item_id ] =
1775
-                                $line_items_percent_of_running_total * $this_running_total;
1776
-
1777
-                            if ($child_line_item->is_taxable()) {
1778
-                                $running_totals['taxable'][ $line_item_id ] =
1779
-                                    $line_items_percent_of_running_total * $running_totals['taxable'][ $line_item_id ];
1780
-                            }
1781
-                        }
1782
-                    }
1783
-                    break;
1784
-            }
1785
-        }
1786
-        return $running_totals;
1787
-    }
1788
-
1789
-
1790
-    /**
1791
-     * @param EE_Line_Item $total_line_item
1792
-     * @param EE_Line_Item $ticket_line_item
1793
-     * @return float | null
1794
-     * @throws EE_Error
1795
-     * @throws InvalidArgumentException
1796
-     * @throws InvalidDataTypeException
1797
-     * @throws InvalidInterfaceException
1798
-     * @throws OutOfRangeException
1799
-     * @throws ReflectionException
1800
-     */
1801
-    public static function calculate_final_price_for_ticket_line_item(
1802
-        EE_Line_Item $total_line_item,
1803
-        EE_Line_Item $ticket_line_item
1804
-    ) {
1805
-        static $final_prices_per_ticket_line_item = array();
1806
-        if (empty($final_prices_per_ticket_line_item) || empty($final_prices_per_ticket_line_item[ $total_line_item->ID() ])) {
1807
-            $final_prices_per_ticket_line_item[ $total_line_item->ID() ] = EEH_Line_Item::calculate_reg_final_prices_per_line_item(
1808
-                $total_line_item
1809
-            );
1810
-        }
1811
-        // ok now find this new registration's final price
1812
-        if (isset($final_prices_per_ticket_line_item[ $total_line_item->ID() ][ $ticket_line_item->ID() ])) {
1813
-            return $final_prices_per_ticket_line_item[ $total_line_item->ID() ][ $ticket_line_item->ID() ];
1814
-        }
1815
-        $message = sprintf(
1816
-            esc_html__(
1817
-                'The final price for the ticket line item (ID:%1$d) on the total line item (ID:%2$d) could not be calculated.',
1818
-                'event_espresso'
1819
-            ),
1820
-            $ticket_line_item->ID(),
1821
-            $total_line_item->ID()
1822
-        );
1823
-        if (WP_DEBUG) {
1824
-            $message .= '<br>' . print_r($final_prices_per_ticket_line_item, true);
1825
-            throw new OutOfRangeException($message);
1826
-        }
1827
-        EE_Log::instance()->log(__CLASS__, __FUNCTION__, $message);
1828
-        return null;
1829
-    }
1830
-
1831
-
1832
-    /**
1833
-     * Creates a duplicate of the line item tree, except only includes billable items
1834
-     * and the portion of line items attributed to billable things
1835
-     *
1836
-     * @param EE_Line_Item      $line_item
1837
-     * @param EE_Registration[] $registrations
1838
-     * @return EE_Line_Item
1839
-     * @throws EE_Error
1840
-     * @throws InvalidArgumentException
1841
-     * @throws InvalidDataTypeException
1842
-     * @throws InvalidInterfaceException
1843
-     * @throws ReflectionException
1844
-     */
1845
-    public static function billable_line_item_tree(EE_Line_Item $line_item, $registrations)
1846
-    {
1847
-        $copy_li = EEH_Line_Item::billable_line_item($line_item, $registrations);
1848
-        foreach ($line_item->children() as $child_li) {
1849
-            $copy_li->add_child_line_item(
1850
-                EEH_Line_Item::billable_line_item_tree($child_li, $registrations)
1851
-            );
1852
-        }
1853
-        // if this is the grand total line item, make sure the totals all add up
1854
-        // (we could have duplicated this logic AS we copied the line items, but
1855
-        // it seems DRYer this way)
1856
-        if ($copy_li->type() === EEM_Line_Item::type_total) {
1857
-            $copy_li->recalculate_total_including_taxes();
1858
-        }
1859
-        return $copy_li;
1860
-    }
1861
-
1862
-
1863
-    /**
1864
-     * Creates a new, unsaved line item from $line_item that factors in the
1865
-     * number of billable registrations on $registrations.
1866
-     *
1867
-     * @param EE_Line_Item      $line_item
1868
-     * @param EE_Registration[] $registrations
1869
-     * @return EE_Line_Item
1870
-     * @throws EE_Error
1871
-     * @throws InvalidArgumentException
1872
-     * @throws InvalidDataTypeException
1873
-     * @throws InvalidInterfaceException
1874
-     * @throws ReflectionException
1875
-     */
1876
-    public static function billable_line_item(EE_Line_Item $line_item, $registrations)
1877
-    {
1878
-        $new_li_fields = $line_item->model_field_array();
1879
-        if (
1880
-            $line_item->type() === EEM_Line_Item::type_line_item &&
1881
-            $line_item->OBJ_type() === EEM_Line_Item::OBJ_TYPE_TICKET
1882
-        ) {
1883
-            $count = 0;
1884
-            foreach ($registrations as $registration) {
1885
-                if (
1886
-                    $line_item->OBJ_ID() === $registration->ticket_ID() &&
1887
-                    in_array(
1888
-                        $registration->status_ID(),
1889
-                        EEM_Registration::reg_statuses_that_allow_payment(),
1890
-                        true
1891
-                    )
1892
-                ) {
1893
-                    $count++;
1894
-                }
1895
-            }
1896
-            $new_li_fields['LIN_quantity'] = $count;
1897
-        }
1898
-        // don't set the total. We'll leave that up to the code that calculates it
1899
-        unset($new_li_fields['LIN_ID'], $new_li_fields['LIN_parent'], $new_li_fields['LIN_total']);
1900
-        return EE_Line_Item::new_instance($new_li_fields);
1901
-    }
1902
-
1903
-
1904
-    /**
1905
-     * Returns a modified line item tree where all the subtotals which have a total of 0
1906
-     * are removed, and line items with a quantity of 0
1907
-     *
1908
-     * @param EE_Line_Item $line_item |null
1909
-     * @return EE_Line_Item|null
1910
-     * @throws EE_Error
1911
-     * @throws InvalidArgumentException
1912
-     * @throws InvalidDataTypeException
1913
-     * @throws InvalidInterfaceException
1914
-     * @throws ReflectionException
1915
-     */
1916
-    public static function non_empty_line_items(EE_Line_Item $line_item)
1917
-    {
1918
-        $copied_li = EEH_Line_Item::non_empty_line_item($line_item);
1919
-        if ($copied_li === null) {
1920
-            return null;
1921
-        }
1922
-        // if this is an event subtotal, we want to only include it if it
1923
-        // has a non-zero total and at least one ticket line item child
1924
-        $ticket_children = 0;
1925
-        foreach ($line_item->children() as $child_li) {
1926
-            $child_li_copy = EEH_Line_Item::non_empty_line_items($child_li);
1927
-            if ($child_li_copy !== null) {
1928
-                $copied_li->add_child_line_item($child_li_copy);
1929
-                if (
1930
-                    $child_li_copy->type() === EEM_Line_Item::type_line_item &&
1931
-                    $child_li_copy->OBJ_type() === EEM_Line_Item::OBJ_TYPE_TICKET
1932
-                ) {
1933
-                    $ticket_children++;
1934
-                }
1935
-            }
1936
-        }
1937
-        // if this is an event subtotal with NO ticket children
1938
-        // we basically want to ignore it
1939
-        if (
1940
-            $ticket_children === 0
1941
-            && $line_item->type() === EEM_Line_Item::type_sub_total
1942
-            && $line_item->OBJ_type() === EEM_Line_Item::OBJ_TYPE_EVENT
1943
-            && $line_item->total() === 0
1944
-        ) {
1945
-            return null;
1946
-        }
1947
-        return $copied_li;
1948
-    }
1949
-
1950
-
1951
-    /**
1952
-     * Creates a new, unsaved line item, but if it's a ticket line item
1953
-     * with a total of 0, or a subtotal of 0, returns null instead
1954
-     *
1955
-     * @param EE_Line_Item $line_item
1956
-     * @return EE_Line_Item
1957
-     * @throws EE_Error
1958
-     * @throws InvalidArgumentException
1959
-     * @throws InvalidDataTypeException
1960
-     * @throws InvalidInterfaceException
1961
-     * @throws ReflectionException
1962
-     */
1963
-    public static function non_empty_line_item(EE_Line_Item $line_item)
1964
-    {
1965
-        if (
1966
-            $line_item->type() === EEM_Line_Item::type_line_item
1967
-            && $line_item->OBJ_type() === EEM_Line_Item::OBJ_TYPE_TICKET
1968
-            && $line_item->quantity() === 0
1969
-        ) {
1970
-            return null;
1971
-        }
1972
-        $new_li_fields = $line_item->model_field_array();
1973
-        // don't set the total. We'll leave that up to the code that calculates it
1974
-        unset($new_li_fields['LIN_ID'], $new_li_fields['LIN_parent']);
1975
-        return EE_Line_Item::new_instance($new_li_fields);
1976
-    }
1977
-
1978
-
1979
-    /**
1980
-     * Cycles through all of the ticket line items for the supplied total line item
1981
-     * and ensures that the line item's "is_taxable" field matches that of its corresponding ticket
1982
-     *
1983
-     * @param EE_Line_Item $total_line_item
1984
-     * @since 4.9.79.p
1985
-     * @throws EE_Error
1986
-     * @throws InvalidArgumentException
1987
-     * @throws InvalidDataTypeException
1988
-     * @throws InvalidInterfaceException
1989
-     * @throws ReflectionException
1990
-     */
1991
-    public static function resetIsTaxableForTickets(EE_Line_Item $total_line_item)
1992
-    {
1993
-        $ticket_line_items = self::get_ticket_line_items($total_line_item);
1994
-        foreach ($ticket_line_items as $ticket_line_item) {
1995
-            if (
1996
-                $ticket_line_item instanceof EE_Line_Item
1997
-                && $ticket_line_item->OBJ_type() === EEM_Line_Item::OBJ_TYPE_TICKET
1998
-            ) {
1999
-                $ticket = $ticket_line_item->ticket();
2000
-                if ($ticket instanceof EE_Ticket && $ticket->taxable() !== $ticket_line_item->is_taxable()) {
2001
-                    $ticket_line_item->set_is_taxable($ticket->taxable());
2002
-                    $ticket_line_item->save();
2003
-                }
2004
-            }
2005
-        }
2006
-    }
2007
-
2008
-
2009
-    /**
2010
-     * @return EE_Line_Item[]
2011
-     * @throws EE_Error
2012
-     * @throws ReflectionException
2013
-     * @since   $VID:$
2014
-     */
2015
-    private static function getGlobalTaxes(): array
2016
-    {
2017
-        if (EEH_Line_Item::$global_taxes === null) {
2018
-
2019
-            /** @type EEM_Price $EEM_Price */
2020
-            $EEM_Price = EE_Registry::instance()->load_model('Price');
2021
-            // get array of taxes via Price Model
2022
-            EEH_Line_Item::$global_taxes = $EEM_Price->get_all_prices_that_are_taxes();
2023
-            ksort(EEH_Line_Item::$global_taxes);
2024
-        }
2025
-        return EEH_Line_Item::$global_taxes;
2026
-    }
2027
-
2028
-
2029
-
2030
-    /**************************************** @DEPRECATED METHODS *************************************** */
2031
-    /**
2032
-     * @deprecated
2033
-     * @param EE_Line_Item $total_line_item
2034
-     * @return EE_Line_Item
2035
-     * @throws EE_Error
2036
-     * @throws InvalidArgumentException
2037
-     * @throws InvalidDataTypeException
2038
-     * @throws InvalidInterfaceException
2039
-     * @throws ReflectionException
2040
-     */
2041
-    public static function get_items_subtotal(EE_Line_Item $total_line_item)
2042
-    {
2043
-        EE_Error::doing_it_wrong(
2044
-            'EEH_Line_Item::get_items_subtotal()',
2045
-            sprintf(
2046
-                esc_html__('Method replaced with %1$s', 'event_espresso'),
2047
-                'EEH_Line_Item::get_pre_tax_subtotal()'
2048
-            ),
2049
-            '4.6.0'
2050
-        );
2051
-        return self::get_pre_tax_subtotal($total_line_item);
2052
-    }
2053
-
2054
-
2055
-    /**
2056
-     * @deprecated
2057
-     * @param EE_Transaction $transaction
2058
-     * @return EE_Line_Item
2059
-     * @throws EE_Error
2060
-     * @throws InvalidArgumentException
2061
-     * @throws InvalidDataTypeException
2062
-     * @throws InvalidInterfaceException
2063
-     * @throws ReflectionException
2064
-     */
2065
-    public static function create_default_total_line_item($transaction = null)
2066
-    {
2067
-        EE_Error::doing_it_wrong(
2068
-            'EEH_Line_Item::create_default_total_line_item()',
2069
-            sprintf(
2070
-                esc_html__('Method replaced with %1$s', 'event_espresso'),
2071
-                'EEH_Line_Item::create_total_line_item()'
2072
-            ),
2073
-            '4.6.0'
2074
-        );
2075
-        return self::create_total_line_item($transaction);
2076
-    }
2077
-
2078
-
2079
-    /**
2080
-     * @deprecated
2081
-     * @param EE_Line_Item   $total_line_item
2082
-     * @param EE_Transaction $transaction
2083
-     * @return EE_Line_Item
2084
-     * @throws EE_Error
2085
-     * @throws InvalidArgumentException
2086
-     * @throws InvalidDataTypeException
2087
-     * @throws InvalidInterfaceException
2088
-     * @throws ReflectionException
2089
-     */
2090
-    public static function create_default_tickets_subtotal(EE_Line_Item $total_line_item, $transaction = null)
2091
-    {
2092
-        EE_Error::doing_it_wrong(
2093
-            'EEH_Line_Item::create_default_tickets_subtotal()',
2094
-            sprintf(
2095
-                esc_html__('Method replaced with %1$s', 'event_espresso'),
2096
-                'EEH_Line_Item::create_pre_tax_subtotal()'
2097
-            ),
2098
-            '4.6.0'
2099
-        );
2100
-        return self::create_pre_tax_subtotal($total_line_item, $transaction);
2101
-    }
2102
-
2103
-
2104
-    /**
2105
-     * @deprecated
2106
-     * @param EE_Line_Item   $total_line_item
2107
-     * @param EE_Transaction $transaction
2108
-     * @return EE_Line_Item
2109
-     * @throws EE_Error
2110
-     * @throws InvalidArgumentException
2111
-     * @throws InvalidDataTypeException
2112
-     * @throws InvalidInterfaceException
2113
-     * @throws ReflectionException
2114
-     */
2115
-    public static function create_default_taxes_subtotal(EE_Line_Item $total_line_item, $transaction = null)
2116
-    {
2117
-        EE_Error::doing_it_wrong(
2118
-            'EEH_Line_Item::create_default_taxes_subtotal()',
2119
-            sprintf(
2120
-                esc_html__('Method replaced with %1$s', 'event_espresso'),
2121
-                'EEH_Line_Item::create_taxes_subtotal()'
2122
-            ),
2123
-            '4.6.0'
2124
-        );
2125
-        return self::create_taxes_subtotal($total_line_item, $transaction);
2126
-    }
2127
-
2128
-
2129
-    /**
2130
-     * @deprecated
2131
-     * @param EE_Line_Item   $total_line_item
2132
-     * @param EE_Transaction $transaction
2133
-     * @return EE_Line_Item
2134
-     * @throws EE_Error
2135
-     * @throws InvalidArgumentException
2136
-     * @throws InvalidDataTypeException
2137
-     * @throws InvalidInterfaceException
2138
-     * @throws ReflectionException
2139
-     */
2140
-    public static function create_default_event_subtotal(EE_Line_Item $total_line_item, $transaction = null)
2141
-    {
2142
-        EE_Error::doing_it_wrong(
2143
-            'EEH_Line_Item::create_default_event_subtotal()',
2144
-            sprintf(
2145
-                esc_html__('Method replaced with %1$s', 'event_espresso'),
2146
-                'EEH_Line_Item::create_event_subtotal()'
2147
-            ),
2148
-            '4.6.0'
2149
-        );
2150
-        return self::create_event_subtotal($total_line_item, $transaction);
2151
-    }
24
+	/**
25
+	 * @var EE_Line_Item[]
26
+	 */
27
+	private static $global_taxes;
28
+
29
+
30
+	/**
31
+	 * Adds a simple item (unrelated to any other model object) to the provided PARENT line item.
32
+	 * Does NOT automatically re-calculate the line item totals or update the related transaction.
33
+	 * You should call recalculate_total_including_taxes() on the grant total line item after this
34
+	 * to update the subtotals, and EE_Registration_Processor::calculate_reg_final_prices_per_line_item()
35
+	 * to keep the registration final prices in-sync with the transaction's total.
36
+	 *
37
+	 * @param EE_Line_Item $parent_line_item
38
+	 * @param string       $name
39
+	 * @param float        $unit_price
40
+	 * @param string       $description
41
+	 * @param int          $quantity
42
+	 * @param boolean      $taxable
43
+	 * @param string|null  $code if set to a value, ensures there is only one line item with that code
44
+	 * @param bool         $return_item
45
+	 * @param bool         $recalculate_totals
46
+	 * @return boolean|EE_Line_Item success
47
+	 * @throws EE_Error
48
+	 * @throws ReflectionException
49
+	 */
50
+	public static function add_unrelated_item(
51
+		EE_Line_Item $parent_line_item,
52
+		string $name,
53
+		float $unit_price,
54
+		string $description = '',
55
+		int $quantity = 1,
56
+		bool $taxable = false,
57
+		?string $code = null,
58
+		bool $return_item = false,
59
+		bool $recalculate_totals = true
60
+	) {
61
+		$items_subtotal = self::get_pre_tax_subtotal($parent_line_item);
62
+		$line_item      = EE_Line_Item::new_instance(
63
+			[
64
+				'LIN_name'       => $name,
65
+				'LIN_desc'       => $description,
66
+				'LIN_unit_price' => $unit_price,
67
+				'LIN_quantity'   => $quantity,
68
+				'LIN_percent'    => null,
69
+				'LIN_is_taxable' => $taxable,
70
+				'LIN_order'      => $items_subtotal instanceof EE_Line_Item
71
+					? count($items_subtotal->children())
72
+					: 0,
73
+				'LIN_total'      => (float) $unit_price * (int) $quantity,
74
+				'LIN_type'       => EEM_Line_Item::type_line_item,
75
+				'LIN_code'       => $code,
76
+			]
77
+		);
78
+		$line_item      = apply_filters(
79
+			'FHEE__EEH_Line_Item__add_unrelated_item__line_item',
80
+			$line_item,
81
+			$parent_line_item
82
+		);
83
+		$added          = self::add_item($parent_line_item, $line_item, $recalculate_totals);
84
+		return $return_item ? $line_item : $added;
85
+	}
86
+
87
+
88
+	/**
89
+	 * Adds a simple item ( unrelated to any other model object) to the total line item,
90
+	 * in the correct spot in the line item tree. Does not automatically
91
+	 * re-calculate the line item totals, nor update the related transaction, nor upgrade the transaction's
92
+	 * registrations' final prices (which should probably change because of this).
93
+	 * You should call recalculate_total_including_taxes() on the grand total line item, then
94
+	 * update the transaction's total, and EE_Registration_Processor::update_registration_final_prices()
95
+	 * after using this, to keep the registration final prices in-sync with the transaction's total.
96
+	 *
97
+	 * @param EE_Line_Item $parent_line_item
98
+	 * @param string       $name
99
+	 * @param float        $percentage_amount
100
+	 * @param string       $description
101
+	 * @param boolean      $taxable
102
+	 * @param string|null  $code
103
+	 * @param bool         $return_item
104
+	 * @return boolean|EE_Line_Item success
105
+	 * @throws EE_Error
106
+	 * @throws ReflectionException
107
+	 */
108
+	public static function add_percentage_based_item(
109
+		EE_Line_Item $parent_line_item,
110
+		string $name,
111
+		float $percentage_amount,
112
+		string $description = '',
113
+		bool $taxable = false,
114
+		?string $code = null,
115
+		bool $return_item = false
116
+	) {
117
+		$total = $percentage_amount * $parent_line_item->total() / 100;
118
+		$line_item = EE_Line_Item::new_instance(
119
+			[
120
+				'LIN_name'       => $name,
121
+				'LIN_desc'       => $description,
122
+				'LIN_unit_price' => 0,
123
+				'LIN_percent'    => $percentage_amount,
124
+				'LIN_quantity'   => 1,
125
+				'LIN_is_taxable' => $taxable,
126
+				'LIN_total'      => (float) $total,
127
+				'LIN_type'       => EEM_Line_Item::type_line_item,
128
+				'LIN_parent'     => $parent_line_item->ID(),
129
+				'LIN_code'       => $code,
130
+			]
131
+		);
132
+		$line_item = apply_filters(
133
+			'FHEE__EEH_Line_Item__add_percentage_based_item__line_item',
134
+			$line_item
135
+		);
136
+		$added     = $parent_line_item->add_child_line_item($line_item, false);
137
+		return $return_item ? $line_item : $added;
138
+	}
139
+
140
+
141
+	/**
142
+	 * Returns the new line item created by adding a purchase of the ticket
143
+	 * ensures that ticket line item is saved, and that cart total has been recalculated.
144
+	 * If this ticket has already been purchased, just increments its count.
145
+	 * Automatically re-calculates the line item totals and updates the related transaction. But
146
+	 * DOES NOT automatically upgrade the transaction's registrations' final prices (which
147
+	 * should probably change because of this).
148
+	 * You should call EE_Registration_Processor::calculate_reg_final_prices_per_line_item()
149
+	 * after using this, to keep the registration final prices in-sync with the transaction's total.
150
+	 *
151
+	 * @param EE_Line_Item $total_line_item grand total line item of type EEM_Line_Item::type_total
152
+	 * @param EE_Ticket    $ticket
153
+	 * @param int          $qty
154
+	 * @return EE_Line_Item
155
+	 * @throws EE_Error
156
+	 * @throws InvalidArgumentException
157
+	 * @throws InvalidDataTypeException
158
+	 * @throws InvalidInterfaceException
159
+	 * @throws ReflectionException
160
+	 */
161
+	public static function add_ticket_purchase(EE_Line_Item $total_line_item, EE_Ticket $ticket, $qty = 1)
162
+	{
163
+		if (! $total_line_item instanceof EE_Line_Item || ! $total_line_item->is_total()) {
164
+			throw new EE_Error(
165
+				sprintf(
166
+					esc_html__(
167
+						'A valid line item total is required in order to add tickets. A line item of type "%s" was passed.',
168
+						'event_espresso'
169
+					),
170
+					$ticket->ID(),
171
+					$total_line_item->ID()
172
+				)
173
+			);
174
+		}
175
+		// either increment the qty for an existing ticket
176
+		$line_item = self::increment_ticket_qty_if_already_in_cart($total_line_item, $ticket, $qty);
177
+		// or add a new one
178
+		if (! $line_item instanceof EE_Line_Item) {
179
+			$line_item = self::create_ticket_line_item($total_line_item, $ticket, $qty);
180
+		}
181
+		$total_line_item->recalculate_total_including_taxes();
182
+		return $line_item;
183
+	}
184
+
185
+
186
+	/**
187
+	 * Returns the new line item created by adding a purchase of the ticket
188
+	 *
189
+	 * @param EE_Line_Item $total_line_item
190
+	 * @param EE_Ticket    $ticket
191
+	 * @param int          $qty
192
+	 * @return EE_Line_Item
193
+	 * @throws EE_Error
194
+	 * @throws InvalidArgumentException
195
+	 * @throws InvalidDataTypeException
196
+	 * @throws InvalidInterfaceException
197
+	 * @throws ReflectionException
198
+	 */
199
+	public static function increment_ticket_qty_if_already_in_cart(
200
+		EE_Line_Item $total_line_item,
201
+		EE_Ticket $ticket,
202
+		$qty = 1
203
+	) {
204
+		$line_item = null;
205
+		if ($total_line_item instanceof EE_Line_Item && $total_line_item->is_total()) {
206
+			$ticket_line_items = EEH_Line_Item::get_ticket_line_items($total_line_item);
207
+			foreach ((array) $ticket_line_items as $ticket_line_item) {
208
+				if (
209
+					$ticket_line_item instanceof EE_Line_Item
210
+					&& (int) $ticket_line_item->OBJ_ID() === (int) $ticket->ID()
211
+				) {
212
+					$line_item = $ticket_line_item;
213
+					break;
214
+				}
215
+			}
216
+		}
217
+		if ($line_item instanceof EE_Line_Item) {
218
+			EEH_Line_Item::increment_quantity($line_item, $qty);
219
+			return $line_item;
220
+		}
221
+		return null;
222
+	}
223
+
224
+
225
+	/**
226
+	 * Increments the line item and all its children's quantity by $qty (but percent line items are unaffected).
227
+	 * Does NOT save or recalculate other line items totals
228
+	 *
229
+	 * @param EE_Line_Item $line_item
230
+	 * @param int          $qty
231
+	 * @return void
232
+	 * @throws EE_Error
233
+	 * @throws InvalidArgumentException
234
+	 * @throws InvalidDataTypeException
235
+	 * @throws InvalidInterfaceException
236
+	 * @throws ReflectionException
237
+	 */
238
+	public static function increment_quantity(EE_Line_Item $line_item, $qty = 1)
239
+	{
240
+		if (! $line_item->is_percent()) {
241
+			$qty += $line_item->quantity();
242
+			$line_item->set_quantity($qty);
243
+			$line_item->set_total($line_item->unit_price() * $qty);
244
+			$line_item->save();
245
+		}
246
+		foreach ($line_item->children() as $child) {
247
+			if ($child->is_sub_line_item()) {
248
+				EEH_Line_Item::update_quantity($child, $qty);
249
+			}
250
+		}
251
+	}
252
+
253
+
254
+	/**
255
+	 * Decrements the line item and all its children's quantity by $qty (but percent line items are unaffected).
256
+	 * Does NOT save or recalculate other line items totals
257
+	 *
258
+	 * @param EE_Line_Item $line_item
259
+	 * @param int          $qty
260
+	 * @return void
261
+	 * @throws EE_Error
262
+	 * @throws InvalidArgumentException
263
+	 * @throws InvalidDataTypeException
264
+	 * @throws InvalidInterfaceException
265
+	 * @throws ReflectionException
266
+	 */
267
+	public static function decrement_quantity(EE_Line_Item $line_item, $qty = 1)
268
+	{
269
+		if (! $line_item->is_percent()) {
270
+			$qty = $line_item->quantity() - $qty;
271
+			$qty = max($qty, 0);
272
+			$line_item->set_quantity($qty);
273
+			$line_item->set_total($line_item->unit_price() * $qty);
274
+			$line_item->save();
275
+		}
276
+		foreach ($line_item->children() as $child) {
277
+			if ($child->is_sub_line_item()) {
278
+				EEH_Line_Item::update_quantity($child, $qty);
279
+			}
280
+		}
281
+	}
282
+
283
+
284
+	/**
285
+	 * Updates the line item and its children's quantities to the specified number.
286
+	 * Does NOT save them or recalculate totals.
287
+	 *
288
+	 * @param EE_Line_Item $line_item
289
+	 * @param int          $new_quantity
290
+	 * @throws EE_Error
291
+	 * @throws InvalidArgumentException
292
+	 * @throws InvalidDataTypeException
293
+	 * @throws InvalidInterfaceException
294
+	 * @throws ReflectionException
295
+	 */
296
+	public static function update_quantity(EE_Line_Item $line_item, $new_quantity)
297
+	{
298
+		if (! $line_item->is_percent()) {
299
+			$line_item->set_quantity($new_quantity);
300
+			$line_item->set_total($line_item->unit_price() * $new_quantity);
301
+			$line_item->save();
302
+		}
303
+		foreach ($line_item->children() as $child) {
304
+			if ($child->is_sub_line_item()) {
305
+				EEH_Line_Item::update_quantity($child, $new_quantity);
306
+			}
307
+		}
308
+	}
309
+
310
+
311
+	/**
312
+	 * Returns the new line item created by adding a purchase of the ticket
313
+	 *
314
+	 * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
315
+	 * @param EE_Ticket    $ticket
316
+	 * @param int          $qty
317
+	 * @return EE_Line_Item
318
+	 * @throws EE_Error
319
+	 * @throws InvalidArgumentException
320
+	 * @throws InvalidDataTypeException
321
+	 * @throws InvalidInterfaceException
322
+	 * @throws ReflectionException
323
+	 */
324
+	public static function create_ticket_line_item(EE_Line_Item $total_line_item, EE_Ticket $ticket, $qty = 1)
325
+	{
326
+		$datetimes = $ticket->datetimes();
327
+		$first_datetime = reset($datetimes);
328
+		$first_datetime_name = esc_html__('Event', 'event_espresso');
329
+		if ($first_datetime instanceof EE_Datetime && $first_datetime->event() instanceof EE_Event) {
330
+			$first_datetime_name = $first_datetime->event()->name();
331
+		}
332
+		$event = sprintf(_x('(For %1$s)', '(For Event Name)', 'event_espresso'), $first_datetime_name);
333
+		// get event subtotal line
334
+		$events_sub_total = self::get_event_line_item_for_ticket($total_line_item, $ticket);
335
+		$taxes = $ticket->tax_price_modifiers();
336
+		// add $ticket to cart
337
+		$line_item = EE_Line_Item::new_instance(array(
338
+			'LIN_name'       => $ticket->name(),
339
+			'LIN_desc'       => $ticket->description() !== '' ? $ticket->description() . ' ' . $event : $event,
340
+			'LIN_unit_price' => $ticket->price(),
341
+			'LIN_quantity'   => $qty,
342
+			'LIN_is_taxable' => empty($taxes) && $ticket->taxable(),
343
+			'LIN_order'      => count($events_sub_total->children()),
344
+			'LIN_total'      => $ticket->price() * $qty,
345
+			'LIN_type'       => EEM_Line_Item::type_line_item,
346
+			'OBJ_ID'         => $ticket->ID(),
347
+			'OBJ_type'       => EEM_Line_Item::OBJ_TYPE_TICKET,
348
+		));
349
+		$line_item = apply_filters(
350
+			'FHEE__EEH_Line_Item__create_ticket_line_item__line_item',
351
+			$line_item
352
+		);
353
+		if (!$line_item instanceof EE_Line_Item) {
354
+			throw new DomainException(
355
+				esc_html__('Invalid EE_Line_Item received.', 'event_espresso')
356
+			);
357
+		}
358
+		$events_sub_total->add_child_line_item($line_item);
359
+		// now add the sub-line items
360
+		$running_total = 0;
361
+		$running_pre_tax_total = 0;
362
+		foreach ($ticket->prices() as $price) {
363
+			$sign = $price->is_discount() ? -1 : 1;
364
+			$price_total = $price->is_percent()
365
+				? $running_pre_tax_total * $price->amount() / 100
366
+				: $price->amount() * $qty;
367
+			if ($price->is_percent()) {
368
+				$percent = $sign * $price->amount();
369
+				$unit_price = 0;
370
+			} else {
371
+				$percent    = 0;
372
+				$unit_price = $sign * $price->amount();
373
+			}
374
+			$sub_line_item = EE_Line_Item::new_instance(array(
375
+				'LIN_name'       => $price->name(),
376
+				'LIN_desc'       => $price->desc(),
377
+				'LIN_quantity'   => $price->is_percent() ? null : $qty,
378
+				'LIN_is_taxable' => false,
379
+				'LIN_order'      => $price->order(),
380
+				'LIN_total'      => $price_total,
381
+				'LIN_pretax'     => 0,
382
+				'LIN_unit_price' => $unit_price,
383
+				'LIN_percent'    => $percent,
384
+				'LIN_type'       => $price->is_tax() ? EEM_Line_Item::type_sub_tax : EEM_Line_Item::type_sub_line_item,
385
+				'OBJ_ID'         => $price->ID(),
386
+				'OBJ_type'       => EEM_Line_Item::OBJ_TYPE_PRICE,
387
+			));
388
+			$sub_line_item = apply_filters(
389
+				'FHEE__EEH_Line_Item__create_ticket_line_item__sub_line_item',
390
+				$sub_line_item
391
+			);
392
+			$running_total += $sign * $price_total;
393
+			$running_pre_tax_total += ! $price->is_tax() ? $sign * $price_total : 0;
394
+			$line_item->add_child_line_item($sub_line_item);
395
+		}
396
+		$line_item->setPretaxTotal($running_pre_tax_total);
397
+		return $line_item;
398
+	}
399
+
400
+
401
+	/**
402
+	 * Adds the specified item under the pre-tax-sub-total line item. Automatically
403
+	 * re-calculates the line item totals and updates the related transaction. But
404
+	 * DOES NOT automatically upgrade the transaction's registrations' final prices (which
405
+	 * should probably change because of this).
406
+	 * You should call EE_Registration_Processor::calculate_reg_final_prices_per_line_item()
407
+	 * after using this, to keep the registration final prices in-sync with the transaction's total.
408
+	 *
409
+	 * @param EE_Line_Item $total_line_item
410
+	 * @param EE_Line_Item $item to be added
411
+	 * @return boolean
412
+	 * @throws EE_Error
413
+	 * @throws InvalidArgumentException
414
+	 * @throws InvalidDataTypeException
415
+	 * @throws InvalidInterfaceException
416
+	 * @throws ReflectionException
417
+	 */
418
+	public static function add_item(EE_Line_Item $total_line_item, EE_Line_Item $item, $recalculate_totals = true)
419
+	{
420
+		$pre_tax_subtotal = self::get_pre_tax_subtotal($total_line_item);
421
+		if ($pre_tax_subtotal instanceof EE_Line_Item) {
422
+			$success = $pre_tax_subtotal->add_child_line_item($item);
423
+		} else {
424
+			return false;
425
+		}
426
+		if ($recalculate_totals) {
427
+			$total_line_item->recalculate_total_including_taxes();
428
+		}
429
+		return $success;
430
+	}
431
+
432
+
433
+	/**
434
+	 * cancels an existing ticket line item,
435
+	 * by decrementing it's quantity by 1 and adding a new "type_cancellation" sub-line-item.
436
+	 * ALL totals and subtotals will NEED TO BE UPDATED after performing this action
437
+	 *
438
+	 * @param EE_Line_Item $ticket_line_item
439
+	 * @param int          $qty
440
+	 * @return bool success
441
+	 * @throws EE_Error
442
+	 * @throws InvalidArgumentException
443
+	 * @throws InvalidDataTypeException
444
+	 * @throws InvalidInterfaceException
445
+	 * @throws ReflectionException
446
+	 */
447
+	public static function cancel_ticket_line_item(EE_Line_Item $ticket_line_item, $qty = 1)
448
+	{
449
+		// validate incoming line_item
450
+		if ($ticket_line_item->OBJ_type() !== EEM_Line_Item::OBJ_TYPE_TICKET) {
451
+			throw new EE_Error(
452
+				sprintf(
453
+					esc_html__(
454
+						'The supplied line item must have an Object Type of "Ticket", not %1$s.',
455
+						'event_espresso'
456
+					),
457
+					$ticket_line_item->type()
458
+				)
459
+			);
460
+		}
461
+		if ($ticket_line_item->quantity() < $qty) {
462
+			throw new EE_Error(
463
+				sprintf(
464
+					esc_html__(
465
+						'Can not cancel %1$d ticket(s) because the supplied line item has a quantity of %2$d.',
466
+						'event_espresso'
467
+					),
468
+					$qty,
469
+					$ticket_line_item->quantity()
470
+				)
471
+			);
472
+		}
473
+		// decrement ticket quantity; don't rely on auto-fixing when recalculating totals to do this
474
+		$ticket_line_item->set_quantity($ticket_line_item->quantity() - $qty);
475
+		foreach ($ticket_line_item->children() as $child_line_item) {
476
+			if (
477
+				$child_line_item->is_sub_line_item()
478
+				&& ! $child_line_item->is_percent()
479
+				&& ! $child_line_item->is_cancellation()
480
+			) {
481
+				$child_line_item->set_quantity($child_line_item->quantity() - $qty);
482
+			}
483
+		}
484
+		// get cancellation sub line item
485
+		$cancellation_line_item = EEH_Line_Item::get_descendants_of_type(
486
+			$ticket_line_item,
487
+			EEM_Line_Item::type_cancellation
488
+		);
489
+		$cancellation_line_item = reset($cancellation_line_item);
490
+		// verify that this ticket was indeed previously cancelled
491
+		if ($cancellation_line_item instanceof EE_Line_Item) {
492
+			// increment cancelled quantity
493
+			$cancellation_line_item->set_quantity($cancellation_line_item->quantity() + $qty);
494
+		} else {
495
+			// create cancellation sub line item
496
+			$cancellation_line_item = EE_Line_Item::new_instance(array(
497
+				'LIN_name'       => esc_html__('Cancellation', 'event_espresso'),
498
+				'LIN_desc'       => sprintf(
499
+					esc_html_x(
500
+						'Cancelled %1$s : %2$s',
501
+						'Cancelled Ticket Name : 2015-01-01 11:11',
502
+						'event_espresso'
503
+					),
504
+					$ticket_line_item->name(),
505
+					current_time(get_option('date_format') . ' ' . get_option('time_format'))
506
+				),
507
+				'LIN_unit_price' => 0, // $ticket_line_item->unit_price()
508
+				'LIN_quantity'   => $qty,
509
+				'LIN_is_taxable' => $ticket_line_item->is_taxable(),
510
+				'LIN_order'      => count($ticket_line_item->children()),
511
+				'LIN_total'      => 0, // $ticket_line_item->unit_price()
512
+				'LIN_type'       => EEM_Line_Item::type_cancellation,
513
+			));
514
+			$ticket_line_item->add_child_line_item($cancellation_line_item);
515
+		}
516
+		if ($ticket_line_item->save_this_and_descendants() > 0) {
517
+			// decrement parent line item quantity
518
+			$event_line_item = $ticket_line_item->parent();
519
+			if (
520
+				$event_line_item instanceof EE_Line_Item
521
+				&& $event_line_item->OBJ_type() === EEM_Line_Item::OBJ_TYPE_EVENT
522
+			) {
523
+				$event_line_item->set_quantity($event_line_item->quantity() - $qty);
524
+				$event_line_item->save();
525
+			}
526
+			EEH_Line_Item::get_grand_total_and_recalculate_everything($ticket_line_item);
527
+			return true;
528
+		}
529
+		return false;
530
+	}
531
+
532
+
533
+	/**
534
+	 * reinstates (un-cancels?) a previously canceled ticket line item,
535
+	 * by incrementing it's quantity by 1, and decrementing it's "type_cancellation" sub-line-item.
536
+	 * ALL totals and subtotals will NEED TO BE UPDATED after performing this action
537
+	 *
538
+	 * @param EE_Line_Item $ticket_line_item
539
+	 * @param int          $qty
540
+	 * @return bool success
541
+	 * @throws EE_Error
542
+	 * @throws InvalidArgumentException
543
+	 * @throws InvalidDataTypeException
544
+	 * @throws InvalidInterfaceException
545
+	 * @throws ReflectionException
546
+	 */
547
+	public static function reinstate_canceled_ticket_line_item(EE_Line_Item $ticket_line_item, $qty = 1)
548
+	{
549
+		// validate incoming line_item
550
+		if ($ticket_line_item->OBJ_type() !== EEM_Line_Item::OBJ_TYPE_TICKET) {
551
+			throw new EE_Error(
552
+				sprintf(
553
+					esc_html__(
554
+						'The supplied line item must have an Object Type of "Ticket", not %1$s.',
555
+						'event_espresso'
556
+					),
557
+					$ticket_line_item->type()
558
+				)
559
+			);
560
+		}
561
+		// get cancellation sub line item
562
+		$cancellation_line_item = EEH_Line_Item::get_descendants_of_type(
563
+			$ticket_line_item,
564
+			EEM_Line_Item::type_cancellation
565
+		);
566
+		$cancellation_line_item = reset($cancellation_line_item);
567
+		// verify that this ticket was indeed previously cancelled
568
+		if (! $cancellation_line_item instanceof EE_Line_Item) {
569
+			return false;
570
+		}
571
+		if ($cancellation_line_item->quantity() > $qty) {
572
+			// decrement cancelled quantity
573
+			$cancellation_line_item->set_quantity($cancellation_line_item->quantity() - $qty);
574
+		} elseif ($cancellation_line_item->quantity() === $qty) {
575
+			// decrement cancelled quantity in case anyone still has the object kicking around
576
+			$cancellation_line_item->set_quantity($cancellation_line_item->quantity() - $qty);
577
+			// delete because quantity will end up as 0
578
+			$cancellation_line_item->delete();
579
+			// and attempt to destroy the object,
580
+			// even though PHP won't actually destroy it until it needs the memory
581
+			unset($cancellation_line_item);
582
+		} else {
583
+			// what ?!?! negative quantity ?!?!
584
+			throw new EE_Error(
585
+				sprintf(
586
+					esc_html__(
587
+						'Can not reinstate %1$d cancelled ticket(s) because the cancelled ticket quantity is only %2$d.',
588
+						'event_espresso'
589
+					),
590
+					$qty,
591
+					$cancellation_line_item->quantity()
592
+				)
593
+			);
594
+		}
595
+		// increment ticket quantity
596
+		$ticket_line_item->set_quantity($ticket_line_item->quantity() + $qty);
597
+		if ($ticket_line_item->save_this_and_descendants() > 0) {
598
+			// increment parent line item quantity
599
+			$event_line_item = $ticket_line_item->parent();
600
+			if (
601
+				$event_line_item instanceof EE_Line_Item
602
+				&& $event_line_item->OBJ_type() === EEM_Line_Item::OBJ_TYPE_EVENT
603
+			) {
604
+				$event_line_item->set_quantity($event_line_item->quantity() + $qty);
605
+			}
606
+			EEH_Line_Item::get_grand_total_and_recalculate_everything($ticket_line_item);
607
+			return true;
608
+		}
609
+		return false;
610
+	}
611
+
612
+
613
+	/**
614
+	 * calls EEH_Line_Item::find_transaction_grand_total_for_line_item()
615
+	 * then EE_Line_Item::recalculate_total_including_taxes() on the result
616
+	 *
617
+	 * @param EE_Line_Item $line_item
618
+	 * @return float
619
+	 * @throws EE_Error
620
+	 * @throws InvalidArgumentException
621
+	 * @throws InvalidDataTypeException
622
+	 * @throws InvalidInterfaceException
623
+	 * @throws ReflectionException
624
+	 */
625
+	public static function get_grand_total_and_recalculate_everything(EE_Line_Item $line_item)
626
+	{
627
+		$grand_total_line_item = EEH_Line_Item::find_transaction_grand_total_for_line_item($line_item);
628
+		return $grand_total_line_item->recalculate_total_including_taxes();
629
+	}
630
+
631
+
632
+	/**
633
+	 * Gets the line item which contains the subtotal of all the items
634
+	 *
635
+	 * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
636
+	 * @return EE_Line_Item
637
+	 * @throws EE_Error
638
+	 * @throws InvalidArgumentException
639
+	 * @throws InvalidDataTypeException
640
+	 * @throws InvalidInterfaceException
641
+	 * @throws ReflectionException
642
+	 */
643
+	public static function get_pre_tax_subtotal(EE_Line_Item $total_line_item)
644
+	{
645
+		$pre_tax_subtotal = $total_line_item->get_child_line_item('pre-tax-subtotal');
646
+		return $pre_tax_subtotal instanceof EE_Line_Item
647
+			? $pre_tax_subtotal
648
+			: self::create_pre_tax_subtotal($total_line_item);
649
+	}
650
+
651
+
652
+	/**
653
+	 * Gets the line item for the taxes subtotal
654
+	 *
655
+	 * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
656
+	 * @return EE_Line_Item
657
+	 * @throws EE_Error
658
+	 * @throws InvalidArgumentException
659
+	 * @throws InvalidDataTypeException
660
+	 * @throws InvalidInterfaceException
661
+	 * @throws ReflectionException
662
+	 */
663
+	public static function get_taxes_subtotal(EE_Line_Item $total_line_item)
664
+	{
665
+		$taxes = $total_line_item->get_child_line_item('taxes');
666
+		return $taxes ? $taxes : self::create_taxes_subtotal($total_line_item);
667
+	}
668
+
669
+
670
+	/**
671
+	 * sets the TXN ID on an EE_Line_Item if passed a valid EE_Transaction object
672
+	 *
673
+	 * @param EE_Line_Item   $line_item
674
+	 * @param EE_Transaction $transaction
675
+	 * @return void
676
+	 * @throws EE_Error
677
+	 * @throws InvalidArgumentException
678
+	 * @throws InvalidDataTypeException
679
+	 * @throws InvalidInterfaceException
680
+	 * @throws ReflectionException
681
+	 */
682
+	public static function set_TXN_ID(EE_Line_Item $line_item, $transaction = null)
683
+	{
684
+		if ($transaction) {
685
+			/** @type EEM_Transaction $EEM_Transaction */
686
+			$EEM_Transaction = EE_Registry::instance()->load_model('Transaction');
687
+			$TXN_ID = $EEM_Transaction->ensure_is_ID($transaction);
688
+			$line_item->set_TXN_ID($TXN_ID);
689
+		}
690
+	}
691
+
692
+
693
+	/**
694
+	 * Creates a new default total line item for the transaction,
695
+	 * and its tickets subtotal and taxes subtotal line items (and adds the
696
+	 * existing taxes as children of the taxes subtotal line item)
697
+	 *
698
+	 * @param EE_Transaction $transaction
699
+	 * @return EE_Line_Item of type total
700
+	 * @throws EE_Error
701
+	 * @throws InvalidArgumentException
702
+	 * @throws InvalidDataTypeException
703
+	 * @throws InvalidInterfaceException
704
+	 * @throws ReflectionException
705
+	 */
706
+	public static function create_total_line_item($transaction = null)
707
+	{
708
+		$total_line_item = EE_Line_Item::new_instance(array(
709
+			'LIN_code' => 'total',
710
+			'LIN_name' => esc_html__('Grand Total', 'event_espresso'),
711
+			'LIN_type' => EEM_Line_Item::type_total,
712
+			'OBJ_type' => EEM_Line_Item::OBJ_TYPE_TRANSACTION,
713
+		));
714
+		$total_line_item = apply_filters(
715
+			'FHEE__EEH_Line_Item__create_total_line_item__total_line_item',
716
+			$total_line_item
717
+		);
718
+		self::set_TXN_ID($total_line_item, $transaction);
719
+		self::create_pre_tax_subtotal($total_line_item, $transaction);
720
+		self::create_taxes_subtotal($total_line_item, $transaction);
721
+		return $total_line_item;
722
+	}
723
+
724
+
725
+	/**
726
+	 * Creates a default items subtotal line item
727
+	 *
728
+	 * @param EE_Line_Item   $total_line_item
729
+	 * @param EE_Transaction $transaction
730
+	 * @return EE_Line_Item
731
+	 * @throws EE_Error
732
+	 * @throws InvalidArgumentException
733
+	 * @throws InvalidDataTypeException
734
+	 * @throws InvalidInterfaceException
735
+	 * @throws ReflectionException
736
+	 */
737
+	protected static function create_pre_tax_subtotal(EE_Line_Item $total_line_item, $transaction = null)
738
+	{
739
+		$pre_tax_line_item = EE_Line_Item::new_instance(array(
740
+			'LIN_code' => 'pre-tax-subtotal',
741
+			'LIN_name' => esc_html__('Pre-Tax Subtotal', 'event_espresso'),
742
+			'LIN_type' => EEM_Line_Item::type_sub_total,
743
+		));
744
+		$pre_tax_line_item = apply_filters(
745
+			'FHEE__EEH_Line_Item__create_pre_tax_subtotal__pre_tax_line_item',
746
+			$pre_tax_line_item
747
+		);
748
+		self::set_TXN_ID($pre_tax_line_item, $transaction);
749
+		$total_line_item->add_child_line_item($pre_tax_line_item);
750
+		self::create_event_subtotal($pre_tax_line_item, $transaction);
751
+		return $pre_tax_line_item;
752
+	}
753
+
754
+
755
+	/**
756
+	 * Creates a line item for the taxes subtotal and finds all the tax prices
757
+	 * and applies taxes to it
758
+	 *
759
+	 * @param EE_Line_Item   $total_line_item of type EEM_Line_Item::type_total
760
+	 * @param EE_Transaction $transaction
761
+	 * @return EE_Line_Item
762
+	 * @throws EE_Error
763
+	 * @throws InvalidArgumentException
764
+	 * @throws InvalidDataTypeException
765
+	 * @throws InvalidInterfaceException
766
+	 * @throws ReflectionException
767
+	 */
768
+	protected static function create_taxes_subtotal(EE_Line_Item $total_line_item, $transaction = null)
769
+	{
770
+		$tax_line_item = EE_Line_Item::new_instance(array(
771
+			'LIN_code'  => 'taxes',
772
+			'LIN_name'  => esc_html__('Taxes', 'event_espresso'),
773
+			'LIN_type'  => EEM_Line_Item::type_tax_sub_total,
774
+			'LIN_order' => 1000,// this should always come last
775
+		));
776
+		$tax_line_item = apply_filters(
777
+			'FHEE__EEH_Line_Item__create_taxes_subtotal__tax_line_item',
778
+			$tax_line_item
779
+		);
780
+		self::set_TXN_ID($tax_line_item, $transaction);
781
+		$total_line_item->add_child_line_item($tax_line_item);
782
+		// and lastly, add the actual taxes
783
+		self::apply_taxes($total_line_item);
784
+		return $tax_line_item;
785
+	}
786
+
787
+
788
+	/**
789
+	 * Creates a default items subtotal line item
790
+	 *
791
+	 * @param EE_Line_Item   $pre_tax_line_item
792
+	 * @param EE_Transaction $transaction
793
+	 * @param EE_Event       $event
794
+	 * @return EE_Line_Item
795
+	 * @throws EE_Error
796
+	 * @throws InvalidArgumentException
797
+	 * @throws InvalidDataTypeException
798
+	 * @throws InvalidInterfaceException
799
+	 * @throws ReflectionException
800
+	 */
801
+	public static function create_event_subtotal(EE_Line_Item $pre_tax_line_item, $transaction = null, $event = null)
802
+	{
803
+		$event_line_item = EE_Line_Item::new_instance(array(
804
+			'LIN_code' => self::get_event_code($event),
805
+			'LIN_name' => self::get_event_name($event),
806
+			'LIN_desc' => self::get_event_desc($event),
807
+			'LIN_type' => EEM_Line_Item::type_sub_total,
808
+			'OBJ_type' => EEM_Line_Item::OBJ_TYPE_EVENT,
809
+			'OBJ_ID'   => $event instanceof EE_Event ? $event->ID() : 0,
810
+		));
811
+		$event_line_item = apply_filters(
812
+			'FHEE__EEH_Line_Item__create_event_subtotal__event_line_item',
813
+			$event_line_item
814
+		);
815
+		self::set_TXN_ID($event_line_item, $transaction);
816
+		$pre_tax_line_item->add_child_line_item($event_line_item);
817
+		return $event_line_item;
818
+	}
819
+
820
+
821
+	/**
822
+	 * Gets what the event ticket's code SHOULD be
823
+	 *
824
+	 * @param EE_Event $event
825
+	 * @return string
826
+	 * @throws EE_Error
827
+	 */
828
+	public static function get_event_code($event)
829
+	{
830
+		return 'event-' . ($event instanceof EE_Event ? $event->ID() : '0');
831
+	}
832
+
833
+
834
+	/**
835
+	 * Gets the event name
836
+	 *
837
+	 * @param EE_Event $event
838
+	 * @return string
839
+	 * @throws EE_Error
840
+	 */
841
+	public static function get_event_name($event)
842
+	{
843
+		return $event instanceof EE_Event
844
+			? mb_substr($event->name(), 0, 245)
845
+			: esc_html__('Event', 'event_espresso');
846
+	}
847
+
848
+
849
+	/**
850
+	 * Gets the event excerpt
851
+	 *
852
+	 * @param EE_Event $event
853
+	 * @return string
854
+	 * @throws EE_Error
855
+	 */
856
+	public static function get_event_desc($event)
857
+	{
858
+		return $event instanceof EE_Event ? $event->short_description() : '';
859
+	}
860
+
861
+
862
+	/**
863
+	 * Given the grand total line item and a ticket, finds the event sub-total
864
+	 * line item the ticket's purchase should be added onto
865
+	 *
866
+	 * @access public
867
+	 * @param EE_Line_Item $grand_total the grand total line item
868
+	 * @param EE_Ticket    $ticket
869
+	 * @return EE_Line_Item
870
+	 * @throws EE_Error
871
+	 * @throws InvalidArgumentException
872
+	 * @throws InvalidDataTypeException
873
+	 * @throws InvalidInterfaceException
874
+	 * @throws ReflectionException
875
+	 */
876
+	public static function get_event_line_item_for_ticket(EE_Line_Item $grand_total, EE_Ticket $ticket)
877
+	{
878
+		$first_datetime = $ticket->first_datetime();
879
+		if (! $first_datetime instanceof EE_Datetime) {
880
+			throw new EE_Error(
881
+				sprintf(
882
+					__('The supplied ticket (ID %d) has no datetimes', 'event_espresso'),
883
+					$ticket->ID()
884
+				)
885
+			);
886
+		}
887
+		$event = $first_datetime->event();
888
+		if (! $event instanceof EE_Event) {
889
+			throw new EE_Error(
890
+				sprintf(
891
+					esc_html__(
892
+						'The supplied ticket (ID %d) has no event data associated with it.',
893
+						'event_espresso'
894
+					),
895
+					$ticket->ID()
896
+				)
897
+			);
898
+		}
899
+		$events_sub_total = EEH_Line_Item::get_event_line_item($grand_total, $event);
900
+		if (! $events_sub_total instanceof EE_Line_Item) {
901
+			throw new EE_Error(
902
+				sprintf(
903
+					esc_html__(
904
+						'There is no events sub-total for ticket %s on total line item %d',
905
+						'event_espresso'
906
+					),
907
+					$ticket->ID(),
908
+					$grand_total->ID()
909
+				)
910
+			);
911
+		}
912
+		return $events_sub_total;
913
+	}
914
+
915
+
916
+	/**
917
+	 * Gets the event line item
918
+	 *
919
+	 * @param EE_Line_Item $grand_total
920
+	 * @param EE_Event     $event
921
+	 * @return EE_Line_Item for the event subtotal which is a child of $grand_total
922
+	 * @throws EE_Error
923
+	 * @throws InvalidArgumentException
924
+	 * @throws InvalidDataTypeException
925
+	 * @throws InvalidInterfaceException
926
+	 * @throws ReflectionException
927
+	 */
928
+	public static function get_event_line_item(EE_Line_Item $grand_total, $event)
929
+	{
930
+		/** @type EE_Event $event */
931
+		$event = EEM_Event::instance()->ensure_is_obj($event, true);
932
+		$event_line_item = null;
933
+		$found = false;
934
+		foreach (EEH_Line_Item::get_event_subtotals($grand_total) as $event_line_item) {
935
+			// default event subtotal, we should only ever find this the first time this method is called
936
+			if (! $event_line_item->OBJ_ID()) {
937
+				// let's use this! but first... set the event details
938
+				EEH_Line_Item::set_event_subtotal_details($event_line_item, $event);
939
+				$found = true;
940
+				break;
941
+			}
942
+			if ($event_line_item->OBJ_ID() === $event->ID()) {
943
+				// found existing line item for this event in the cart, so break out of loop and use this one
944
+				$found = true;
945
+				break;
946
+			}
947
+		}
948
+		if (! $found) {
949
+			// there is no event sub-total yet, so add it
950
+			$pre_tax_subtotal = EEH_Line_Item::get_pre_tax_subtotal($grand_total);
951
+			// create a new "event" subtotal below that
952
+			$event_line_item = EEH_Line_Item::create_event_subtotal($pre_tax_subtotal, null, $event);
953
+			// and set the event details
954
+			EEH_Line_Item::set_event_subtotal_details($event_line_item, $event);
955
+		}
956
+		return $event_line_item;
957
+	}
958
+
959
+
960
+	/**
961
+	 * Creates a default items subtotal line item
962
+	 *
963
+	 * @param EE_Line_Item   $event_line_item
964
+	 * @param EE_Event       $event
965
+	 * @param EE_Transaction $transaction
966
+	 * @return void
967
+	 * @throws EE_Error
968
+	 * @throws InvalidArgumentException
969
+	 * @throws InvalidDataTypeException
970
+	 * @throws InvalidInterfaceException
971
+	 * @throws ReflectionException
972
+	 */
973
+	public static function set_event_subtotal_details(
974
+		EE_Line_Item $event_line_item,
975
+		EE_Event $event,
976
+		$transaction = null
977
+	) {
978
+		if ($event instanceof EE_Event) {
979
+			$event_line_item->set_code(self::get_event_code($event));
980
+			$event_line_item->set_name(self::get_event_name($event));
981
+			$event_line_item->set_desc(self::get_event_desc($event));
982
+			$event_line_item->set_OBJ_ID($event->ID());
983
+		}
984
+		self::set_TXN_ID($event_line_item, $transaction);
985
+	}
986
+
987
+
988
+	/**
989
+	 * Finds what taxes should apply, adds them as tax line items under the taxes sub-total,
990
+	 * and recalculates the taxes sub-total and the grand total. Resets the taxes, so
991
+	 * any old taxes are removed
992
+	 *
993
+	 * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
994
+	 * @param bool         $update_txn_status
995
+	 * @return bool
996
+	 * @throws EE_Error
997
+	 * @throws InvalidArgumentException
998
+	 * @throws InvalidDataTypeException
999
+	 * @throws InvalidInterfaceException
1000
+	 * @throws ReflectionException
1001
+	 * @throws RuntimeException
1002
+	 */
1003
+	public static function apply_taxes(EE_Line_Item $total_line_item, $update_txn_status = false)
1004
+	{
1005
+		$total_line_item = EEH_Line_Item::find_transaction_grand_total_for_line_item($total_line_item);
1006
+		$taxes_line_item = self::get_taxes_subtotal($total_line_item);
1007
+		$existing_global_taxes = $taxes_line_item->tax_descendants();
1008
+		$updates = false;
1009
+		// loop thru taxes
1010
+		$global_taxes = EEH_Line_Item::getGlobalTaxes();
1011
+		foreach ($global_taxes as $order => $taxes) {
1012
+			foreach ($taxes as $tax) {
1013
+				if ($tax instanceof EE_Price) {
1014
+					$found = false;
1015
+					// check if this is already an existing tax
1016
+					foreach ($existing_global_taxes as $existing_global_tax) {
1017
+						if ($tax->ID() === $existing_global_tax->OBJ_ID()) {
1018
+							// maybe update the tax rate in case it has changed
1019
+							if ($existing_global_tax->percent() !== $tax->amount()) {
1020
+								$existing_global_tax->set_percent($tax->amount());
1021
+								$existing_global_tax->save();
1022
+								$updates = true;
1023
+							}
1024
+							$found = true;
1025
+							break;
1026
+						}
1027
+					}
1028
+					if (! $found) {
1029
+						// add a new line item for this global tax
1030
+						$tax_line_item = apply_filters(
1031
+							'FHEE__EEH_Line_Item__apply_taxes__tax_line_item',
1032
+							EE_Line_Item::new_instance(
1033
+								[
1034
+									'LIN_name'       => $tax->name(),
1035
+									'LIN_desc'       => $tax->desc(),
1036
+									'LIN_percent'    => $tax->amount(),
1037
+									'LIN_is_taxable' => false,
1038
+									'LIN_order'      => $order,
1039
+									'LIN_total'      => 0,
1040
+									'LIN_type'       => EEM_Line_Item::type_tax,
1041
+									'OBJ_type'       => EEM_Line_Item::OBJ_TYPE_PRICE,
1042
+									'OBJ_ID'         => $tax->ID(),
1043
+								]
1044
+							)
1045
+						);
1046
+						$updates = $taxes_line_item->add_child_line_item($tax_line_item) ? true : $updates;
1047
+					}
1048
+				}
1049
+			}
1050
+		}
1051
+		// only recalculate totals if something changed
1052
+		if ($updates) {
1053
+			$total_line_item->recalculate_total_including_taxes($update_txn_status);
1054
+			return true;
1055
+		}
1056
+		return false;
1057
+	}
1058
+
1059
+
1060
+	/**
1061
+	 * Ensures that taxes have been applied to the order, if not applies them.
1062
+	 * Returns the total amount of tax
1063
+	 *
1064
+	 * @param EE_Line_Item $total_line_item of type EEM_Line_Item::type_total
1065
+	 * @return float
1066
+	 * @throws EE_Error
1067
+	 * @throws InvalidArgumentException
1068
+	 * @throws InvalidDataTypeException
1069
+	 * @throws InvalidInterfaceException
1070
+	 * @throws ReflectionException
1071
+	 */
1072
+	public static function ensure_taxes_applied($total_line_item)
1073
+	{
1074
+		$taxes_subtotal = self::get_taxes_subtotal($total_line_item);
1075
+		if (! $taxes_subtotal->children()) {
1076
+			self::apply_taxes($total_line_item);
1077
+		}
1078
+		return $taxes_subtotal->total();
1079
+	}
1080
+
1081
+
1082
+	/**
1083
+	 * Deletes ALL children of the passed line item
1084
+	 *
1085
+	 * @param EE_Line_Item $parent_line_item
1086
+	 * @return bool
1087
+	 * @throws EE_Error
1088
+	 * @throws InvalidArgumentException
1089
+	 * @throws InvalidDataTypeException
1090
+	 * @throws InvalidInterfaceException
1091
+	 * @throws ReflectionException
1092
+	 */
1093
+	public static function delete_all_child_items(EE_Line_Item $parent_line_item)
1094
+	{
1095
+		$deleted = 0;
1096
+		foreach ($parent_line_item->children() as $child_line_item) {
1097
+			if ($child_line_item instanceof EE_Line_Item) {
1098
+				$deleted += EEH_Line_Item::delete_all_child_items($child_line_item);
1099
+				if ($child_line_item->ID()) {
1100
+					$child_line_item->delete();
1101
+					unset($child_line_item);
1102
+				} else {
1103
+					$parent_line_item->delete_child_line_item($child_line_item->code());
1104
+				}
1105
+				$deleted++;
1106
+			}
1107
+		}
1108
+		return $deleted;
1109
+	}
1110
+
1111
+
1112
+	/**
1113
+	 * Deletes the line items as indicated by the line item code(s) provided,
1114
+	 * regardless of where they're found in the line item tree. Automatically
1115
+	 * re-calculates the line item totals and updates the related transaction. But
1116
+	 * DOES NOT automatically upgrade the transaction's registrations' final prices (which
1117
+	 * should probably change because of this).
1118
+	 * You should call EE_Registration_Processor::calculate_reg_final_prices_per_line_item()
1119
+	 * after using this, to keep the registration final prices in-sync with the transaction's total.
1120
+	 *
1121
+	 * @param EE_Line_Item      $total_line_item of type EEM_Line_Item::type_total
1122
+	 * @param array|bool|string $line_item_codes
1123
+	 * @return int number of items successfully removed
1124
+	 * @throws EE_Error
1125
+	 */
1126
+	public static function delete_items(EE_Line_Item $total_line_item, $line_item_codes = false)
1127
+	{
1128
+
1129
+		if ($total_line_item->type() !== EEM_Line_Item::type_total) {
1130
+			EE_Error::doing_it_wrong(
1131
+				'EEH_Line_Item::delete_items',
1132
+				esc_html__(
1133
+					'This static method should only be called with a TOTAL line item, otherwise we won\'t recalculate the totals correctly',
1134
+					'event_espresso'
1135
+				),
1136
+				'4.6.18'
1137
+			);
1138
+		}
1139
+		do_action('AHEE_log', __FILE__, __FUNCTION__, '');
1140
+
1141
+		// check if only a single line_item_id was passed
1142
+		if (! empty($line_item_codes) && ! is_array($line_item_codes)) {
1143
+			// place single line_item_id in an array to appear as multiple line_item_ids
1144
+			$line_item_codes = array($line_item_codes);
1145
+		}
1146
+		$removals = 0;
1147
+		// cycle thru line_item_ids
1148
+		foreach ($line_item_codes as $line_item_id) {
1149
+			$removals += $total_line_item->delete_child_line_item($line_item_id);
1150
+		}
1151
+
1152
+		if ($removals > 0) {
1153
+			$total_line_item->recalculate_taxes_and_tax_total();
1154
+			return $removals;
1155
+		} else {
1156
+			return false;
1157
+		}
1158
+	}
1159
+
1160
+
1161
+	/**
1162
+	 * Overwrites the previous tax by clearing out the old taxes, and creates a new
1163
+	 * tax and updates the total line item accordingly
1164
+	 *
1165
+	 * @param EE_Line_Item $total_line_item
1166
+	 * @param float        $amount
1167
+	 * @param string       $name
1168
+	 * @param string       $description
1169
+	 * @param string       $code
1170
+	 * @param boolean      $add_to_existing_line_item
1171
+	 *                          if true, and a duplicate line item with the same code is found,
1172
+	 *                          $amount will be added onto it; otherwise will simply set the taxes to match $amount
1173
+	 * @return EE_Line_Item the new tax line item created
1174
+	 * @throws EE_Error
1175
+	 * @throws InvalidArgumentException
1176
+	 * @throws InvalidDataTypeException
1177
+	 * @throws InvalidInterfaceException
1178
+	 * @throws ReflectionException
1179
+	 */
1180
+	public static function set_total_tax_to(
1181
+		EE_Line_Item $total_line_item,
1182
+		$amount,
1183
+		$name = null,
1184
+		$description = null,
1185
+		$code = null,
1186
+		$add_to_existing_line_item = false
1187
+	) {
1188
+		$tax_subtotal = self::get_taxes_subtotal($total_line_item);
1189
+		$taxable_total = $total_line_item->taxable_total();
1190
+
1191
+		if ($add_to_existing_line_item) {
1192
+			$new_tax = $tax_subtotal->get_child_line_item($code);
1193
+			EEM_Line_Item::instance()->delete(
1194
+				array(array('LIN_code' => array('!=', $code), 'LIN_parent' => $tax_subtotal->ID()))
1195
+			);
1196
+		} else {
1197
+			$new_tax = null;
1198
+			$tax_subtotal->delete_children_line_items();
1199
+		}
1200
+		if ($new_tax) {
1201
+			$new_tax->set_total($new_tax->total() + $amount);
1202
+			$new_tax->set_percent($taxable_total ? $new_tax->total() / $taxable_total * 100 : 0);
1203
+		} else {
1204
+			// no existing tax item. Create it
1205
+			$new_tax = EE_Line_Item::new_instance(array(
1206
+				'TXN_ID'      => $total_line_item->TXN_ID(),
1207
+				'LIN_name'    => $name ?: esc_html__('Tax', 'event_espresso'),
1208
+				'LIN_desc'    => $description ?: '',
1209
+				'LIN_percent' => $taxable_total ? ($amount / $taxable_total * 100) : 0,
1210
+				'LIN_total'   => $amount,
1211
+				'LIN_parent'  => $tax_subtotal->ID(),
1212
+				'LIN_type'    => EEM_Line_Item::type_tax,
1213
+				'LIN_code'    => $code,
1214
+			));
1215
+		}
1216
+
1217
+		$new_tax = apply_filters(
1218
+			'FHEE__EEH_Line_Item__set_total_tax_to__new_tax_subtotal',
1219
+			$new_tax,
1220
+			$total_line_item
1221
+		);
1222
+		$new_tax->save();
1223
+		$tax_subtotal->set_total($new_tax->total());
1224
+		$tax_subtotal->save();
1225
+		$total_line_item->recalculate_total_including_taxes();
1226
+		return $new_tax;
1227
+	}
1228
+
1229
+
1230
+	/**
1231
+	 * Makes all the line items which are children of $line_item taxable (or not).
1232
+	 * Does NOT save the line items
1233
+	 *
1234
+	 * @param EE_Line_Item $line_item
1235
+	 * @param boolean      $taxable
1236
+	 * @param string       $code_substring_for_whitelist if this string is part of the line item's code
1237
+	 *                                                   it will be whitelisted (ie, except from becoming taxable)
1238
+	 * @throws EE_Error
1239
+	 */
1240
+	public static function set_line_items_taxable(
1241
+		EE_Line_Item $line_item,
1242
+		$taxable = true,
1243
+		$code_substring_for_whitelist = null
1244
+	) {
1245
+		$whitelisted = false;
1246
+		if ($code_substring_for_whitelist !== null) {
1247
+			$whitelisted = strpos($line_item->code(), $code_substring_for_whitelist) !== false;
1248
+		}
1249
+		if (! $whitelisted && $line_item->is_line_item()) {
1250
+			$line_item->set_is_taxable($taxable);
1251
+		}
1252
+		foreach ($line_item->children() as $child_line_item) {
1253
+			EEH_Line_Item::set_line_items_taxable(
1254
+				$child_line_item,
1255
+				$taxable,
1256
+				$code_substring_for_whitelist
1257
+			);
1258
+		}
1259
+	}
1260
+
1261
+
1262
+	/**
1263
+	 * Gets all descendants that are event subtotals
1264
+	 *
1265
+	 * @uses  EEH_Line_Item::get_subtotals_of_object_type()
1266
+	 * @param EE_Line_Item $parent_line_item - the line item to find descendants of
1267
+	 * @return EE_Line_Item[]
1268
+	 * @throws EE_Error
1269
+	 */
1270
+	public static function get_event_subtotals(EE_Line_Item $parent_line_item)
1271
+	{
1272
+		return self::get_subtotals_of_object_type($parent_line_item, EEM_Line_Item::OBJ_TYPE_EVENT);
1273
+	}
1274
+
1275
+
1276
+	/**
1277
+	 * Gets all descendants subtotals that match the supplied object type
1278
+	 *
1279
+	 * @uses  EEH_Line_Item::_get_descendants_by_type_and_object_type()
1280
+	 * @param EE_Line_Item $parent_line_item - the line item to find descendants of
1281
+	 * @param string       $obj_type
1282
+	 * @return EE_Line_Item[]
1283
+	 * @throws EE_Error
1284
+	 */
1285
+	public static function get_subtotals_of_object_type(EE_Line_Item $parent_line_item, $obj_type = '')
1286
+	{
1287
+		return self::_get_descendants_by_type_and_object_type(
1288
+			$parent_line_item,
1289
+			EEM_Line_Item::type_sub_total,
1290
+			$obj_type
1291
+		);
1292
+	}
1293
+
1294
+
1295
+	/**
1296
+	 * Gets all descendants that are tickets
1297
+	 *
1298
+	 * @uses  EEH_Line_Item::get_line_items_of_object_type()
1299
+	 * @param EE_Line_Item $parent_line_item - the line item to find descendants of
1300
+	 * @return EE_Line_Item[]
1301
+	 * @throws EE_Error
1302
+	 */
1303
+	public static function get_ticket_line_items(EE_Line_Item $parent_line_item)
1304
+	{
1305
+		return self::get_line_items_of_object_type(
1306
+			$parent_line_item,
1307
+			EEM_Line_Item::OBJ_TYPE_TICKET
1308
+		);
1309
+	}
1310
+
1311
+
1312
+	/**
1313
+	 * Gets all descendants subtotals that match the supplied object type
1314
+	 *
1315
+	 * @uses  EEH_Line_Item::_get_descendants_by_type_and_object_type()
1316
+	 * @param EE_Line_Item $parent_line_item - the line item to find descendants of
1317
+	 * @param string       $obj_type
1318
+	 * @return EE_Line_Item[]
1319
+	 * @throws EE_Error
1320
+	 */
1321
+	public static function get_line_items_of_object_type(EE_Line_Item $parent_line_item, $obj_type = '')
1322
+	{
1323
+		return self::_get_descendants_by_type_and_object_type(
1324
+			$parent_line_item,
1325
+			EEM_Line_Item::type_line_item,
1326
+			$obj_type
1327
+		);
1328
+	}
1329
+
1330
+
1331
+	/**
1332
+	 * Gets all the descendants (ie, children or children of children etc) that are of the type 'tax'
1333
+	 *
1334
+	 * @uses  EEH_Line_Item::get_descendants_of_type()
1335
+	 * @param EE_Line_Item $parent_line_item - the line item to find descendants of
1336
+	 * @return EE_Line_Item[]
1337
+	 * @throws EE_Error
1338
+	 */
1339
+	public static function get_tax_descendants(EE_Line_Item $parent_line_item)
1340
+	{
1341
+		return EEH_Line_Item::get_descendants_of_type(
1342
+			$parent_line_item,
1343
+			EEM_Line_Item::type_tax
1344
+		);
1345
+	}
1346
+
1347
+
1348
+	/**
1349
+	 * Gets all the real items purchased which are children of this item
1350
+	 *
1351
+	 * @uses  EEH_Line_Item::get_descendants_of_type()
1352
+	 * @param EE_Line_Item $parent_line_item - the line item to find descendants of
1353
+	 * @return EE_Line_Item[]
1354
+	 * @throws EE_Error
1355
+	 */
1356
+	public static function get_line_item_descendants(EE_Line_Item $parent_line_item)
1357
+	{
1358
+		return EEH_Line_Item::get_descendants_of_type(
1359
+			$parent_line_item,
1360
+			EEM_Line_Item::type_line_item
1361
+		);
1362
+	}
1363
+
1364
+
1365
+	/**
1366
+	 * Gets all descendants of supplied line item that match the supplied line item type
1367
+	 *
1368
+	 * @uses  EEH_Line_Item::_get_descendants_by_type_and_object_type()
1369
+	 * @param EE_Line_Item $parent_line_item - the line item to find descendants of
1370
+	 * @param string       $line_item_type   one of the EEM_Line_Item constants
1371
+	 * @return EE_Line_Item[]
1372
+	 * @throws EE_Error
1373
+	 */
1374
+	public static function get_descendants_of_type(EE_Line_Item $parent_line_item, $line_item_type)
1375
+	{
1376
+		return self::_get_descendants_by_type_and_object_type(
1377
+			$parent_line_item,
1378
+			$line_item_type,
1379
+			null
1380
+		);
1381
+	}
1382
+
1383
+
1384
+	/**
1385
+	 * Gets all descendants of supplied line item that match the supplied line item type and possibly the object type
1386
+	 * as well
1387
+	 *
1388
+	 * @param EE_Line_Item  $parent_line_item - the line item to find descendants of
1389
+	 * @param string        $line_item_type   one of the EEM_Line_Item constants
1390
+	 * @param string | NULL $obj_type         object model class name (minus prefix) or NULL to ignore object type when
1391
+	 *                                        searching
1392
+	 * @return EE_Line_Item[]
1393
+	 * @throws EE_Error
1394
+	 */
1395
+	protected static function _get_descendants_by_type_and_object_type(
1396
+		EE_Line_Item $parent_line_item,
1397
+		$line_item_type,
1398
+		$obj_type = null
1399
+	) {
1400
+		$objects = array();
1401
+		foreach ($parent_line_item->children() as $child_line_item) {
1402
+			if ($child_line_item instanceof EE_Line_Item) {
1403
+				if (
1404
+					$child_line_item->type() === $line_item_type
1405
+					&& (
1406
+						$child_line_item->OBJ_type() === $obj_type || $obj_type === null
1407
+					)
1408
+				) {
1409
+					$objects[] = $child_line_item;
1410
+				} else {
1411
+					// go-through-all-its children looking for more matches
1412
+					$objects = array_merge(
1413
+						$objects,
1414
+						self::_get_descendants_by_type_and_object_type(
1415
+							$child_line_item,
1416
+							$line_item_type,
1417
+							$obj_type
1418
+						)
1419
+					);
1420
+				}
1421
+			}
1422
+		}
1423
+		return $objects;
1424
+	}
1425
+
1426
+
1427
+	/**
1428
+	 * Gets all descendants subtotals that match the supplied object type
1429
+	 *
1430
+	 * @uses  EEH_Line_Item::_get_descendants_by_type_and_object_type()
1431
+	 * @param EE_Line_Item $parent_line_item - the line item to find descendants of
1432
+	 * @param string       $OBJ_type         object type (like Event)
1433
+	 * @param array        $OBJ_IDs          array of OBJ_IDs
1434
+	 * @return EE_Line_Item[]
1435
+	 * @throws EE_Error
1436
+	 */
1437
+	public static function get_line_items_by_object_type_and_IDs(
1438
+		EE_Line_Item $parent_line_item,
1439
+		$OBJ_type = '',
1440
+		$OBJ_IDs = array()
1441
+	) {
1442
+		return self::_get_descendants_by_object_type_and_object_ID(
1443
+			$parent_line_item,
1444
+			$OBJ_type,
1445
+			$OBJ_IDs
1446
+		);
1447
+	}
1448
+
1449
+
1450
+	/**
1451
+	 * Gets all descendants of supplied line item that match the supplied line item type and possibly the object type
1452
+	 * as well
1453
+	 *
1454
+	 * @param EE_Line_Item $parent_line_item - the line item to find descendants of
1455
+	 * @param string       $OBJ_type         object type (like Event)
1456
+	 * @param array        $OBJ_IDs          array of OBJ_IDs
1457
+	 * @return EE_Line_Item[]
1458
+	 * @throws EE_Error
1459
+	 */
1460
+	protected static function _get_descendants_by_object_type_and_object_ID(
1461
+		EE_Line_Item $parent_line_item,
1462
+		$OBJ_type,
1463
+		$OBJ_IDs
1464
+	) {
1465
+		$objects = array();
1466
+		foreach ($parent_line_item->children() as $child_line_item) {
1467
+			if ($child_line_item instanceof EE_Line_Item) {
1468
+				if (
1469
+					$child_line_item->OBJ_type() === $OBJ_type
1470
+					&& is_array($OBJ_IDs)
1471
+					&& in_array($child_line_item->OBJ_ID(), $OBJ_IDs)
1472
+				) {
1473
+					$objects[] = $child_line_item;
1474
+				} else {
1475
+					// go-through-all-its children looking for more matches
1476
+					$objects = array_merge(
1477
+						$objects,
1478
+						self::_get_descendants_by_object_type_and_object_ID(
1479
+							$child_line_item,
1480
+							$OBJ_type,
1481
+							$OBJ_IDs
1482
+						)
1483
+					);
1484
+				}
1485
+			}
1486
+		}
1487
+		return $objects;
1488
+	}
1489
+
1490
+
1491
+	/**
1492
+	 * Uses a breadth-first-search in order to find the nearest descendant of
1493
+	 * the specified type and returns it, else NULL
1494
+	 *
1495
+	 * @uses  EEH_Line_Item::_get_nearest_descendant()
1496
+	 * @param EE_Line_Item $parent_line_item - the line item to find descendants of
1497
+	 * @param string       $type             like one of the EEM_Line_Item::type_*
1498
+	 * @return EE_Line_Item
1499
+	 * @throws EE_Error
1500
+	 * @throws InvalidArgumentException
1501
+	 * @throws InvalidDataTypeException
1502
+	 * @throws InvalidInterfaceException
1503
+	 * @throws ReflectionException
1504
+	 */
1505
+	public static function get_nearest_descendant_of_type(EE_Line_Item $parent_line_item, $type)
1506
+	{
1507
+		return self::_get_nearest_descendant($parent_line_item, 'LIN_type', $type);
1508
+	}
1509
+
1510
+
1511
+	/**
1512
+	 * Uses a breadth-first-search in order to find the nearest descendant
1513
+	 * having the specified LIN_code and returns it, else NULL
1514
+	 *
1515
+	 * @uses  EEH_Line_Item::_get_nearest_descendant()
1516
+	 * @param EE_Line_Item $parent_line_item - the line item to find descendants of
1517
+	 * @param string       $code             any value used for LIN_code
1518
+	 * @return EE_Line_Item
1519
+	 * @throws EE_Error
1520
+	 * @throws InvalidArgumentException
1521
+	 * @throws InvalidDataTypeException
1522
+	 * @throws InvalidInterfaceException
1523
+	 * @throws ReflectionException
1524
+	 */
1525
+	public static function get_nearest_descendant_having_code(EE_Line_Item $parent_line_item, $code)
1526
+	{
1527
+		return self::_get_nearest_descendant($parent_line_item, 'LIN_code', $code);
1528
+	}
1529
+
1530
+
1531
+	/**
1532
+	 * Uses a breadth-first-search in order to find the nearest descendant
1533
+	 * having the specified LIN_code and returns it, else NULL
1534
+	 *
1535
+	 * @param EE_Line_Item $parent_line_item - the line item to find descendants of
1536
+	 * @param string       $search_field     name of EE_Line_Item property
1537
+	 * @param string       $value            any value stored in $search_field
1538
+	 * @return EE_Line_Item
1539
+	 * @throws EE_Error
1540
+	 * @throws InvalidArgumentException
1541
+	 * @throws InvalidDataTypeException
1542
+	 * @throws InvalidInterfaceException
1543
+	 * @throws ReflectionException
1544
+	 */
1545
+	protected static function _get_nearest_descendant(EE_Line_Item $parent_line_item, $search_field, $value)
1546
+	{
1547
+		foreach ($parent_line_item->children() as $child) {
1548
+			if ($child->get($search_field) == $value) {
1549
+				return $child;
1550
+			}
1551
+		}
1552
+		foreach ($parent_line_item->children() as $child) {
1553
+			$descendant_found = self::_get_nearest_descendant(
1554
+				$child,
1555
+				$search_field,
1556
+				$value
1557
+			);
1558
+			if ($descendant_found) {
1559
+				return $descendant_found;
1560
+			}
1561
+		}
1562
+		return null;
1563
+	}
1564
+
1565
+
1566
+	/**
1567
+	 * if passed line item has a TXN ID, uses that to jump directly to the grand total line item for the transaction,
1568
+	 * else recursively walks up the line item tree until a parent of type total is found,
1569
+	 *
1570
+	 * @param EE_Line_Item $line_item
1571
+	 * @return EE_Line_Item
1572
+	 * @throws EE_Error
1573
+	 * @throws ReflectionException
1574
+	 */
1575
+	public static function find_transaction_grand_total_for_line_item(EE_Line_Item $line_item): EE_Line_Item
1576
+	{
1577
+		if ($line_item->is_total()) {
1578
+			return $line_item;
1579
+		}
1580
+		if ($line_item->TXN_ID()) {
1581
+			$total_line_item = $line_item->transaction()->total_line_item(false);
1582
+			if ($total_line_item instanceof EE_Line_Item) {
1583
+				return $total_line_item;
1584
+			}
1585
+		} else {
1586
+			$line_item_parent = $line_item->parent();
1587
+			if ($line_item_parent instanceof EE_Line_Item) {
1588
+				if ($line_item_parent->is_total()) {
1589
+					return $line_item_parent;
1590
+				}
1591
+				return EEH_Line_Item::find_transaction_grand_total_for_line_item($line_item_parent);
1592
+			}
1593
+		}
1594
+		throw new EE_Error(
1595
+			sprintf(
1596
+				esc_html__(
1597
+					'A valid grand total for line item %1$d was not found.',
1598
+					'event_espresso'
1599
+				),
1600
+				$line_item->ID()
1601
+			)
1602
+		);
1603
+	}
1604
+
1605
+
1606
+	/**
1607
+	 * Prints out a representation of the line item tree
1608
+	 *
1609
+	 * @param EE_Line_Item $line_item
1610
+	 * @param int          $indentation
1611
+	 * @return void
1612
+	 * @throws EE_Error
1613
+	 */
1614
+	public static function visualize(EE_Line_Item $line_item, $indentation = 0)
1615
+	{
1616
+		$new_line = defined('EE_TESTS_DIR') ? "\n" : '<br />';
1617
+		echo $new_line;
1618
+		if (! $indentation) {
1619
+			echo $new_line;
1620
+		}
1621
+		echo str_repeat('. ', $indentation);
1622
+		$breakdown = '';
1623
+		if ($line_item->is_line_item() || $line_item->is_sub_line_item() || $line_item->isSubTax()) {
1624
+			if ($line_item->is_percent()) {
1625
+				$breakdown = "{$line_item->percent()}%";
1626
+			} else {
1627
+				$breakdown = "\${$line_item->unit_price()} x {$line_item->quantity()}";
1628
+			}
1629
+		}
1630
+		echo $line_item->name();
1631
+		echo " [ ID:{$line_item->ID()} | qty:{$line_item->quantity()} ] {$line_item->type()} : ";
1632
+		echo "\${$line_item->total()}";
1633
+		if ($breakdown) {
1634
+			echo " ( {$breakdown} )";
1635
+		}
1636
+		if ($line_item->is_taxable()) {
1637
+			echo '  * taxable';
1638
+		}
1639
+		if ($line_item->children()) {
1640
+			foreach ($line_item->children() as $child) {
1641
+				self::visualize($child, $indentation + 1);
1642
+			}
1643
+		}
1644
+		if (! $indentation) {
1645
+			echo $new_line . $new_line;
1646
+		}
1647
+	}
1648
+
1649
+
1650
+	/**
1651
+	 * Calculates the registration's final price, taking into account that they
1652
+	 * need to not only help pay for their OWN ticket, but also any transaction-wide surcharges and taxes,
1653
+	 * and receive a portion of any transaction-wide discounts.
1654
+	 * eg1, if I buy a $1 ticket and brent buys a $9 ticket, and we receive a $5 discount
1655
+	 * then I'll get 1/10 of that $5 discount, which is $0.50, and brent will get
1656
+	 * 9/10ths of that $5 discount, which is $4.50. So my final price should be $0.50
1657
+	 * and brent's final price should be $5.50.
1658
+	 * In order to do this, we basically need to traverse the line item tree calculating
1659
+	 * the running totals (just as if we were recalculating the total), but when we identify
1660
+	 * regular line items, we need to keep track of their share of the grand total.
1661
+	 * Also, we need to keep track of the TAXABLE total for each ticket purchase, so
1662
+	 * we can know how to apply taxes to it. (Note: "taxable total" does not equal the "pretax total"
1663
+	 * when there are non-taxable items; otherwise they would be the same)
1664
+	 *
1665
+	 * @param EE_Line_Item $line_item
1666
+	 * @param array        $billable_ticket_quantities  array of EE_Ticket IDs and their corresponding quantity that
1667
+	 *                                                  can be included in price calculations at this moment
1668
+	 * @return array        keys are line items for tickets IDs and values are their share of the running total,
1669
+	 *                                                  plus the key 'total', and 'taxable' which also has keys of all
1670
+	 *                                                  the ticket IDs.
1671
+	 *                                                  Eg array(
1672
+	 *                                                      12 => 4.3
1673
+	 *                                                      23 => 8.0
1674
+	 *                                                      'total' => 16.6,
1675
+	 *                                                      'taxable' => array(
1676
+	 *                                                          12 => 10,
1677
+	 *                                                          23 => 4
1678
+	 *                                                      ).
1679
+	 *                                                  So to find which registrations have which final price, we need
1680
+	 *                                                  to find which line item is theirs, which can be done with
1681
+	 *                                                  `EEM_Line_Item::instance()->get_line_item_for_registration(
1682
+	 *                                                  $registration );`
1683
+	 * @throws EE_Error
1684
+	 * @throws InvalidArgumentException
1685
+	 * @throws InvalidDataTypeException
1686
+	 * @throws InvalidInterfaceException
1687
+	 * @throws ReflectionException
1688
+	 */
1689
+	public static function calculate_reg_final_prices_per_line_item(
1690
+		EE_Line_Item $line_item,
1691
+		$billable_ticket_quantities = array()
1692
+	) {
1693
+		$running_totals = [
1694
+			'total'   => 0,
1695
+			'taxable' => ['total' => 0]
1696
+		];
1697
+		foreach ($line_item->children() as $child_line_item) {
1698
+			switch ($child_line_item->type()) {
1699
+				case EEM_Line_Item::type_sub_total:
1700
+					$running_totals_from_subtotal = EEH_Line_Item::calculate_reg_final_prices_per_line_item(
1701
+						$child_line_item,
1702
+						$billable_ticket_quantities
1703
+					);
1704
+					// combine arrays but preserve numeric keys
1705
+					$running_totals = array_replace_recursive($running_totals_from_subtotal, $running_totals);
1706
+					$running_totals['total'] += $running_totals_from_subtotal['total'];
1707
+					$running_totals['taxable']['total'] += $running_totals_from_subtotal['taxable']['total'];
1708
+					break;
1709
+
1710
+				case EEM_Line_Item::type_tax_sub_total:
1711
+					// find how much the taxes percentage is
1712
+					if ($child_line_item->percent() !== 0) {
1713
+						$tax_percent_decimal = $child_line_item->percent() / 100;
1714
+					} else {
1715
+						$tax_percent_decimal = EE_Taxes::get_total_taxes_percentage() / 100;
1716
+					}
1717
+					// and apply to all the taxable totals, and add to the pretax totals
1718
+					foreach ($running_totals as $line_item_id => $this_running_total) {
1719
+						// "total" and "taxable" array key is an exception
1720
+						if ($line_item_id === 'taxable') {
1721
+							continue;
1722
+						}
1723
+						$taxable_total = $running_totals['taxable'][ $line_item_id ];
1724
+						$running_totals[ $line_item_id ] += ($taxable_total * $tax_percent_decimal);
1725
+					}
1726
+					break;
1727
+
1728
+				case EEM_Line_Item::type_line_item:
1729
+					// ticket line items or ????
1730
+					if ($child_line_item->OBJ_type() === EEM_Line_Item::OBJ_TYPE_TICKET) {
1731
+						// kk it's a ticket
1732
+						if (isset($running_totals[ $child_line_item->ID() ])) {
1733
+							// huh? that shouldn't happen.
1734
+							$running_totals['total'] += $child_line_item->total();
1735
+						} else {
1736
+							// its not in our running totals yet. great.
1737
+							if ($child_line_item->is_taxable()) {
1738
+								$taxable_amount = $child_line_item->unit_price();
1739
+							} else {
1740
+								$taxable_amount = 0;
1741
+							}
1742
+							// are we only calculating totals for some tickets?
1743
+							if (isset($billable_ticket_quantities[ $child_line_item->OBJ_ID() ])) {
1744
+								$quantity = $billable_ticket_quantities[ $child_line_item->OBJ_ID() ];
1745
+								$running_totals[ $child_line_item->ID() ] = $quantity
1746
+									? $child_line_item->unit_price()
1747
+									: 0;
1748
+								$running_totals['taxable'][ $child_line_item->ID() ] = $quantity
1749
+									? $taxable_amount
1750
+									: 0;
1751
+							} else {
1752
+								$quantity = $child_line_item->quantity();
1753
+								$running_totals[ $child_line_item->ID() ] = $child_line_item->unit_price();
1754
+								$running_totals['taxable'][ $child_line_item->ID() ] = $taxable_amount;
1755
+							}
1756
+							$running_totals['taxable']['total'] += $taxable_amount * $quantity;
1757
+							$running_totals['total'] += $child_line_item->unit_price() * $quantity;
1758
+						}
1759
+					} else {
1760
+						// it's some other type of item added to the cart
1761
+						// it should affect the running totals
1762
+						// basically we want to convert it into a PERCENT modifier. Because
1763
+						// more clearly affect all registration's final price equally
1764
+						$line_items_percent_of_running_total = $running_totals['total'] > 0
1765
+							? ($child_line_item->total() / $running_totals['total']) + 1
1766
+							: 1;
1767
+						foreach ($running_totals as $line_item_id => $this_running_total) {
1768
+							// the "taxable" array key is an exception
1769
+							if ($line_item_id === 'taxable') {
1770
+								continue;
1771
+							}
1772
+							// update the running totals
1773
+							// yes this actually even works for the running grand total!
1774
+							$running_totals[ $line_item_id ] =
1775
+								$line_items_percent_of_running_total * $this_running_total;
1776
+
1777
+							if ($child_line_item->is_taxable()) {
1778
+								$running_totals['taxable'][ $line_item_id ] =
1779
+									$line_items_percent_of_running_total * $running_totals['taxable'][ $line_item_id ];
1780
+							}
1781
+						}
1782
+					}
1783
+					break;
1784
+			}
1785
+		}
1786
+		return $running_totals;
1787
+	}
1788
+
1789
+
1790
+	/**
1791
+	 * @param EE_Line_Item $total_line_item
1792
+	 * @param EE_Line_Item $ticket_line_item
1793
+	 * @return float | null
1794
+	 * @throws EE_Error
1795
+	 * @throws InvalidArgumentException
1796
+	 * @throws InvalidDataTypeException
1797
+	 * @throws InvalidInterfaceException
1798
+	 * @throws OutOfRangeException
1799
+	 * @throws ReflectionException
1800
+	 */
1801
+	public static function calculate_final_price_for_ticket_line_item(
1802
+		EE_Line_Item $total_line_item,
1803
+		EE_Line_Item $ticket_line_item
1804
+	) {
1805
+		static $final_prices_per_ticket_line_item = array();
1806
+		if (empty($final_prices_per_ticket_line_item) || empty($final_prices_per_ticket_line_item[ $total_line_item->ID() ])) {
1807
+			$final_prices_per_ticket_line_item[ $total_line_item->ID() ] = EEH_Line_Item::calculate_reg_final_prices_per_line_item(
1808
+				$total_line_item
1809
+			);
1810
+		}
1811
+		// ok now find this new registration's final price
1812
+		if (isset($final_prices_per_ticket_line_item[ $total_line_item->ID() ][ $ticket_line_item->ID() ])) {
1813
+			return $final_prices_per_ticket_line_item[ $total_line_item->ID() ][ $ticket_line_item->ID() ];
1814
+		}
1815
+		$message = sprintf(
1816
+			esc_html__(
1817
+				'The final price for the ticket line item (ID:%1$d) on the total line item (ID:%2$d) could not be calculated.',
1818
+				'event_espresso'
1819
+			),
1820
+			$ticket_line_item->ID(),
1821
+			$total_line_item->ID()
1822
+		);
1823
+		if (WP_DEBUG) {
1824
+			$message .= '<br>' . print_r($final_prices_per_ticket_line_item, true);
1825
+			throw new OutOfRangeException($message);
1826
+		}
1827
+		EE_Log::instance()->log(__CLASS__, __FUNCTION__, $message);
1828
+		return null;
1829
+	}
1830
+
1831
+
1832
+	/**
1833
+	 * Creates a duplicate of the line item tree, except only includes billable items
1834
+	 * and the portion of line items attributed to billable things
1835
+	 *
1836
+	 * @param EE_Line_Item      $line_item
1837
+	 * @param EE_Registration[] $registrations
1838
+	 * @return EE_Line_Item
1839
+	 * @throws EE_Error
1840
+	 * @throws InvalidArgumentException
1841
+	 * @throws InvalidDataTypeException
1842
+	 * @throws InvalidInterfaceException
1843
+	 * @throws ReflectionException
1844
+	 */
1845
+	public static function billable_line_item_tree(EE_Line_Item $line_item, $registrations)
1846
+	{
1847
+		$copy_li = EEH_Line_Item::billable_line_item($line_item, $registrations);
1848
+		foreach ($line_item->children() as $child_li) {
1849
+			$copy_li->add_child_line_item(
1850
+				EEH_Line_Item::billable_line_item_tree($child_li, $registrations)
1851
+			);
1852
+		}
1853
+		// if this is the grand total line item, make sure the totals all add up
1854
+		// (we could have duplicated this logic AS we copied the line items, but
1855
+		// it seems DRYer this way)
1856
+		if ($copy_li->type() === EEM_Line_Item::type_total) {
1857
+			$copy_li->recalculate_total_including_taxes();
1858
+		}
1859
+		return $copy_li;
1860
+	}
1861
+
1862
+
1863
+	/**
1864
+	 * Creates a new, unsaved line item from $line_item that factors in the
1865
+	 * number of billable registrations on $registrations.
1866
+	 *
1867
+	 * @param EE_Line_Item      $line_item
1868
+	 * @param EE_Registration[] $registrations
1869
+	 * @return EE_Line_Item
1870
+	 * @throws EE_Error
1871
+	 * @throws InvalidArgumentException
1872
+	 * @throws InvalidDataTypeException
1873
+	 * @throws InvalidInterfaceException
1874
+	 * @throws ReflectionException
1875
+	 */
1876
+	public static function billable_line_item(EE_Line_Item $line_item, $registrations)
1877
+	{
1878
+		$new_li_fields = $line_item->model_field_array();
1879
+		if (
1880
+			$line_item->type() === EEM_Line_Item::type_line_item &&
1881
+			$line_item->OBJ_type() === EEM_Line_Item::OBJ_TYPE_TICKET
1882
+		) {
1883
+			$count = 0;
1884
+			foreach ($registrations as $registration) {
1885
+				if (
1886
+					$line_item->OBJ_ID() === $registration->ticket_ID() &&
1887
+					in_array(
1888
+						$registration->status_ID(),
1889
+						EEM_Registration::reg_statuses_that_allow_payment(),
1890
+						true
1891
+					)
1892
+				) {
1893
+					$count++;
1894
+				}
1895
+			}
1896
+			$new_li_fields['LIN_quantity'] = $count;
1897
+		}
1898
+		// don't set the total. We'll leave that up to the code that calculates it
1899
+		unset($new_li_fields['LIN_ID'], $new_li_fields['LIN_parent'], $new_li_fields['LIN_total']);
1900
+		return EE_Line_Item::new_instance($new_li_fields);
1901
+	}
1902
+
1903
+
1904
+	/**
1905
+	 * Returns a modified line item tree where all the subtotals which have a total of 0
1906
+	 * are removed, and line items with a quantity of 0
1907
+	 *
1908
+	 * @param EE_Line_Item $line_item |null
1909
+	 * @return EE_Line_Item|null
1910
+	 * @throws EE_Error
1911
+	 * @throws InvalidArgumentException
1912
+	 * @throws InvalidDataTypeException
1913
+	 * @throws InvalidInterfaceException
1914
+	 * @throws ReflectionException
1915
+	 */
1916
+	public static function non_empty_line_items(EE_Line_Item $line_item)
1917
+	{
1918
+		$copied_li = EEH_Line_Item::non_empty_line_item($line_item);
1919
+		if ($copied_li === null) {
1920
+			return null;
1921
+		}
1922
+		// if this is an event subtotal, we want to only include it if it
1923
+		// has a non-zero total and at least one ticket line item child
1924
+		$ticket_children = 0;
1925
+		foreach ($line_item->children() as $child_li) {
1926
+			$child_li_copy = EEH_Line_Item::non_empty_line_items($child_li);
1927
+			if ($child_li_copy !== null) {
1928
+				$copied_li->add_child_line_item($child_li_copy);
1929
+				if (
1930
+					$child_li_copy->type() === EEM_Line_Item::type_line_item &&
1931
+					$child_li_copy->OBJ_type() === EEM_Line_Item::OBJ_TYPE_TICKET
1932
+				) {
1933
+					$ticket_children++;
1934
+				}
1935
+			}
1936
+		}
1937
+		// if this is an event subtotal with NO ticket children
1938
+		// we basically want to ignore it
1939
+		if (
1940
+			$ticket_children === 0
1941
+			&& $line_item->type() === EEM_Line_Item::type_sub_total
1942
+			&& $line_item->OBJ_type() === EEM_Line_Item::OBJ_TYPE_EVENT
1943
+			&& $line_item->total() === 0
1944
+		) {
1945
+			return null;
1946
+		}
1947
+		return $copied_li;
1948
+	}
1949
+
1950
+
1951
+	/**
1952
+	 * Creates a new, unsaved line item, but if it's a ticket line item
1953
+	 * with a total of 0, or a subtotal of 0, returns null instead
1954
+	 *
1955
+	 * @param EE_Line_Item $line_item
1956
+	 * @return EE_Line_Item
1957
+	 * @throws EE_Error
1958
+	 * @throws InvalidArgumentException
1959
+	 * @throws InvalidDataTypeException
1960
+	 * @throws InvalidInterfaceException
1961
+	 * @throws ReflectionException
1962
+	 */
1963
+	public static function non_empty_line_item(EE_Line_Item $line_item)
1964
+	{
1965
+		if (
1966
+			$line_item->type() === EEM_Line_Item::type_line_item
1967
+			&& $line_item->OBJ_type() === EEM_Line_Item::OBJ_TYPE_TICKET
1968
+			&& $line_item->quantity() === 0
1969
+		) {
1970
+			return null;
1971
+		}
1972
+		$new_li_fields = $line_item->model_field_array();
1973
+		// don't set the total. We'll leave that up to the code that calculates it
1974
+		unset($new_li_fields['LIN_ID'], $new_li_fields['LIN_parent']);
1975
+		return EE_Line_Item::new_instance($new_li_fields);
1976
+	}
1977
+
1978
+
1979
+	/**
1980
+	 * Cycles through all of the ticket line items for the supplied total line item
1981
+	 * and ensures that the line item's "is_taxable" field matches that of its corresponding ticket
1982
+	 *
1983
+	 * @param EE_Line_Item $total_line_item
1984
+	 * @since 4.9.79.p
1985
+	 * @throws EE_Error
1986
+	 * @throws InvalidArgumentException
1987
+	 * @throws InvalidDataTypeException
1988
+	 * @throws InvalidInterfaceException
1989
+	 * @throws ReflectionException
1990
+	 */
1991
+	public static function resetIsTaxableForTickets(EE_Line_Item $total_line_item)
1992
+	{
1993
+		$ticket_line_items = self::get_ticket_line_items($total_line_item);
1994
+		foreach ($ticket_line_items as $ticket_line_item) {
1995
+			if (
1996
+				$ticket_line_item instanceof EE_Line_Item
1997
+				&& $ticket_line_item->OBJ_type() === EEM_Line_Item::OBJ_TYPE_TICKET
1998
+			) {
1999
+				$ticket = $ticket_line_item->ticket();
2000
+				if ($ticket instanceof EE_Ticket && $ticket->taxable() !== $ticket_line_item->is_taxable()) {
2001
+					$ticket_line_item->set_is_taxable($ticket->taxable());
2002
+					$ticket_line_item->save();
2003
+				}
2004
+			}
2005
+		}
2006
+	}
2007
+
2008
+
2009
+	/**
2010
+	 * @return EE_Line_Item[]
2011
+	 * @throws EE_Error
2012
+	 * @throws ReflectionException
2013
+	 * @since   $VID:$
2014
+	 */
2015
+	private static function getGlobalTaxes(): array
2016
+	{
2017
+		if (EEH_Line_Item::$global_taxes === null) {
2018
+
2019
+			/** @type EEM_Price $EEM_Price */
2020
+			$EEM_Price = EE_Registry::instance()->load_model('Price');
2021
+			// get array of taxes via Price Model
2022
+			EEH_Line_Item::$global_taxes = $EEM_Price->get_all_prices_that_are_taxes();
2023
+			ksort(EEH_Line_Item::$global_taxes);
2024
+		}
2025
+		return EEH_Line_Item::$global_taxes;
2026
+	}
2027
+
2028
+
2029
+
2030
+	/**************************************** @DEPRECATED METHODS *************************************** */
2031
+	/**
2032
+	 * @deprecated
2033
+	 * @param EE_Line_Item $total_line_item
2034
+	 * @return EE_Line_Item
2035
+	 * @throws EE_Error
2036
+	 * @throws InvalidArgumentException
2037
+	 * @throws InvalidDataTypeException
2038
+	 * @throws InvalidInterfaceException
2039
+	 * @throws ReflectionException
2040
+	 */
2041
+	public static function get_items_subtotal(EE_Line_Item $total_line_item)
2042
+	{
2043
+		EE_Error::doing_it_wrong(
2044
+			'EEH_Line_Item::get_items_subtotal()',
2045
+			sprintf(
2046
+				esc_html__('Method replaced with %1$s', 'event_espresso'),
2047
+				'EEH_Line_Item::get_pre_tax_subtotal()'
2048
+			),
2049
+			'4.6.0'
2050
+		);
2051
+		return self::get_pre_tax_subtotal($total_line_item);
2052
+	}
2053
+
2054
+
2055
+	/**
2056
+	 * @deprecated
2057
+	 * @param EE_Transaction $transaction
2058
+	 * @return EE_Line_Item
2059
+	 * @throws EE_Error
2060
+	 * @throws InvalidArgumentException
2061
+	 * @throws InvalidDataTypeException
2062
+	 * @throws InvalidInterfaceException
2063
+	 * @throws ReflectionException
2064
+	 */
2065
+	public static function create_default_total_line_item($transaction = null)
2066
+	{
2067
+		EE_Error::doing_it_wrong(
2068
+			'EEH_Line_Item::create_default_total_line_item()',
2069
+			sprintf(
2070
+				esc_html__('Method replaced with %1$s', 'event_espresso'),
2071
+				'EEH_Line_Item::create_total_line_item()'
2072
+			),
2073
+			'4.6.0'
2074
+		);
2075
+		return self::create_total_line_item($transaction);
2076
+	}
2077
+
2078
+
2079
+	/**
2080
+	 * @deprecated
2081
+	 * @param EE_Line_Item   $total_line_item
2082
+	 * @param EE_Transaction $transaction
2083
+	 * @return EE_Line_Item
2084
+	 * @throws EE_Error
2085
+	 * @throws InvalidArgumentException
2086
+	 * @throws InvalidDataTypeException
2087
+	 * @throws InvalidInterfaceException
2088
+	 * @throws ReflectionException
2089
+	 */
2090
+	public static function create_default_tickets_subtotal(EE_Line_Item $total_line_item, $transaction = null)
2091
+	{
2092
+		EE_Error::doing_it_wrong(
2093
+			'EEH_Line_Item::create_default_tickets_subtotal()',
2094
+			sprintf(
2095
+				esc_html__('Method replaced with %1$s', 'event_espresso'),
2096
+				'EEH_Line_Item::create_pre_tax_subtotal()'
2097
+			),
2098
+			'4.6.0'
2099
+		);
2100
+		return self::create_pre_tax_subtotal($total_line_item, $transaction);
2101
+	}
2102
+
2103
+
2104
+	/**
2105
+	 * @deprecated
2106
+	 * @param EE_Line_Item   $total_line_item
2107
+	 * @param EE_Transaction $transaction
2108
+	 * @return EE_Line_Item
2109
+	 * @throws EE_Error
2110
+	 * @throws InvalidArgumentException
2111
+	 * @throws InvalidDataTypeException
2112
+	 * @throws InvalidInterfaceException
2113
+	 * @throws ReflectionException
2114
+	 */
2115
+	public static function create_default_taxes_subtotal(EE_Line_Item $total_line_item, $transaction = null)
2116
+	{
2117
+		EE_Error::doing_it_wrong(
2118
+			'EEH_Line_Item::create_default_taxes_subtotal()',
2119
+			sprintf(
2120
+				esc_html__('Method replaced with %1$s', 'event_espresso'),
2121
+				'EEH_Line_Item::create_taxes_subtotal()'
2122
+			),
2123
+			'4.6.0'
2124
+		);
2125
+		return self::create_taxes_subtotal($total_line_item, $transaction);
2126
+	}
2127
+
2128
+
2129
+	/**
2130
+	 * @deprecated
2131
+	 * @param EE_Line_Item   $total_line_item
2132
+	 * @param EE_Transaction $transaction
2133
+	 * @return EE_Line_Item
2134
+	 * @throws EE_Error
2135
+	 * @throws InvalidArgumentException
2136
+	 * @throws InvalidDataTypeException
2137
+	 * @throws InvalidInterfaceException
2138
+	 * @throws ReflectionException
2139
+	 */
2140
+	public static function create_default_event_subtotal(EE_Line_Item $total_line_item, $transaction = null)
2141
+	{
2142
+		EE_Error::doing_it_wrong(
2143
+			'EEH_Line_Item::create_default_event_subtotal()',
2144
+			sprintf(
2145
+				esc_html__('Method replaced with %1$s', 'event_espresso'),
2146
+				'EEH_Line_Item::create_event_subtotal()'
2147
+			),
2148
+			'4.6.0'
2149
+		);
2150
+		return self::create_event_subtotal($total_line_item, $transaction);
2151
+	}
2152 2152
 }
Please login to merge, or discard this patch.
Spacing   +43 added lines, -43 removed lines patch added patch discarded remove patch
@@ -75,12 +75,12 @@  discard block
 block discarded – undo
75 75
                 'LIN_code'       => $code,
76 76
             ]
77 77
         );
78
-        $line_item      = apply_filters(
78
+        $line_item = apply_filters(
79 79
             'FHEE__EEH_Line_Item__add_unrelated_item__line_item',
80 80
             $line_item,
81 81
             $parent_line_item
82 82
         );
83
-        $added          = self::add_item($parent_line_item, $line_item, $recalculate_totals);
83
+        $added = self::add_item($parent_line_item, $line_item, $recalculate_totals);
84 84
         return $return_item ? $line_item : $added;
85 85
     }
86 86
 
@@ -133,7 +133,7 @@  discard block
 block discarded – undo
133 133
             'FHEE__EEH_Line_Item__add_percentage_based_item__line_item',
134 134
             $line_item
135 135
         );
136
-        $added     = $parent_line_item->add_child_line_item($line_item, false);
136
+        $added = $parent_line_item->add_child_line_item($line_item, false);
137 137
         return $return_item ? $line_item : $added;
138 138
     }
139 139
 
@@ -160,7 +160,7 @@  discard block
 block discarded – undo
160 160
      */
161 161
     public static function add_ticket_purchase(EE_Line_Item $total_line_item, EE_Ticket $ticket, $qty = 1)
162 162
     {
163
-        if (! $total_line_item instanceof EE_Line_Item || ! $total_line_item->is_total()) {
163
+        if ( ! $total_line_item instanceof EE_Line_Item || ! $total_line_item->is_total()) {
164 164
             throw new EE_Error(
165 165
                 sprintf(
166 166
                     esc_html__(
@@ -175,7 +175,7 @@  discard block
 block discarded – undo
175 175
         // either increment the qty for an existing ticket
176 176
         $line_item = self::increment_ticket_qty_if_already_in_cart($total_line_item, $ticket, $qty);
177 177
         // or add a new one
178
-        if (! $line_item instanceof EE_Line_Item) {
178
+        if ( ! $line_item instanceof EE_Line_Item) {
179 179
             $line_item = self::create_ticket_line_item($total_line_item, $ticket, $qty);
180 180
         }
181 181
         $total_line_item->recalculate_total_including_taxes();
@@ -237,7 +237,7 @@  discard block
 block discarded – undo
237 237
      */
238 238
     public static function increment_quantity(EE_Line_Item $line_item, $qty = 1)
239 239
     {
240
-        if (! $line_item->is_percent()) {
240
+        if ( ! $line_item->is_percent()) {
241 241
             $qty += $line_item->quantity();
242 242
             $line_item->set_quantity($qty);
243 243
             $line_item->set_total($line_item->unit_price() * $qty);
@@ -266,7 +266,7 @@  discard block
 block discarded – undo
266 266
      */
267 267
     public static function decrement_quantity(EE_Line_Item $line_item, $qty = 1)
268 268
     {
269
-        if (! $line_item->is_percent()) {
269
+        if ( ! $line_item->is_percent()) {
270 270
             $qty = $line_item->quantity() - $qty;
271 271
             $qty = max($qty, 0);
272 272
             $line_item->set_quantity($qty);
@@ -295,7 +295,7 @@  discard block
 block discarded – undo
295 295
      */
296 296
     public static function update_quantity(EE_Line_Item $line_item, $new_quantity)
297 297
     {
298
-        if (! $line_item->is_percent()) {
298
+        if ( ! $line_item->is_percent()) {
299 299
             $line_item->set_quantity($new_quantity);
300 300
             $line_item->set_total($line_item->unit_price() * $new_quantity);
301 301
             $line_item->save();
@@ -336,7 +336,7 @@  discard block
 block discarded – undo
336 336
         // add $ticket to cart
337 337
         $line_item = EE_Line_Item::new_instance(array(
338 338
             'LIN_name'       => $ticket->name(),
339
-            'LIN_desc'       => $ticket->description() !== '' ? $ticket->description() . ' ' . $event : $event,
339
+            'LIN_desc'       => $ticket->description() !== '' ? $ticket->description().' '.$event : $event,
340 340
             'LIN_unit_price' => $ticket->price(),
341 341
             'LIN_quantity'   => $qty,
342 342
             'LIN_is_taxable' => empty($taxes) && $ticket->taxable(),
@@ -350,7 +350,7 @@  discard block
 block discarded – undo
350 350
             'FHEE__EEH_Line_Item__create_ticket_line_item__line_item',
351 351
             $line_item
352 352
         );
353
-        if (!$line_item instanceof EE_Line_Item) {
353
+        if ( ! $line_item instanceof EE_Line_Item) {
354 354
             throw new DomainException(
355 355
                 esc_html__('Invalid EE_Line_Item received.', 'event_espresso')
356 356
             );
@@ -502,7 +502,7 @@  discard block
 block discarded – undo
502 502
                         'event_espresso'
503 503
                     ),
504 504
                     $ticket_line_item->name(),
505
-                    current_time(get_option('date_format') . ' ' . get_option('time_format'))
505
+                    current_time(get_option('date_format').' '.get_option('time_format'))
506 506
                 ),
507 507
                 'LIN_unit_price' => 0, // $ticket_line_item->unit_price()
508 508
                 'LIN_quantity'   => $qty,
@@ -565,7 +565,7 @@  discard block
 block discarded – undo
565 565
         );
566 566
         $cancellation_line_item = reset($cancellation_line_item);
567 567
         // verify that this ticket was indeed previously cancelled
568
-        if (! $cancellation_line_item instanceof EE_Line_Item) {
568
+        if ( ! $cancellation_line_item instanceof EE_Line_Item) {
569 569
             return false;
570 570
         }
571 571
         if ($cancellation_line_item->quantity() > $qty) {
@@ -771,7 +771,7 @@  discard block
 block discarded – undo
771 771
             'LIN_code'  => 'taxes',
772 772
             'LIN_name'  => esc_html__('Taxes', 'event_espresso'),
773 773
             'LIN_type'  => EEM_Line_Item::type_tax_sub_total,
774
-            'LIN_order' => 1000,// this should always come last
774
+            'LIN_order' => 1000, // this should always come last
775 775
         ));
776 776
         $tax_line_item = apply_filters(
777 777
             'FHEE__EEH_Line_Item__create_taxes_subtotal__tax_line_item',
@@ -827,7 +827,7 @@  discard block
 block discarded – undo
827 827
      */
828 828
     public static function get_event_code($event)
829 829
     {
830
-        return 'event-' . ($event instanceof EE_Event ? $event->ID() : '0');
830
+        return 'event-'.($event instanceof EE_Event ? $event->ID() : '0');
831 831
     }
832 832
 
833 833
 
@@ -876,7 +876,7 @@  discard block
 block discarded – undo
876 876
     public static function get_event_line_item_for_ticket(EE_Line_Item $grand_total, EE_Ticket $ticket)
877 877
     {
878 878
         $first_datetime = $ticket->first_datetime();
879
-        if (! $first_datetime instanceof EE_Datetime) {
879
+        if ( ! $first_datetime instanceof EE_Datetime) {
880 880
             throw new EE_Error(
881 881
                 sprintf(
882 882
                     __('The supplied ticket (ID %d) has no datetimes', 'event_espresso'),
@@ -885,7 +885,7 @@  discard block
 block discarded – undo
885 885
             );
886 886
         }
887 887
         $event = $first_datetime->event();
888
-        if (! $event instanceof EE_Event) {
888
+        if ( ! $event instanceof EE_Event) {
889 889
             throw new EE_Error(
890 890
                 sprintf(
891 891
                     esc_html__(
@@ -897,7 +897,7 @@  discard block
 block discarded – undo
897 897
             );
898 898
         }
899 899
         $events_sub_total = EEH_Line_Item::get_event_line_item($grand_total, $event);
900
-        if (! $events_sub_total instanceof EE_Line_Item) {
900
+        if ( ! $events_sub_total instanceof EE_Line_Item) {
901 901
             throw new EE_Error(
902 902
                 sprintf(
903 903
                     esc_html__(
@@ -933,7 +933,7 @@  discard block
 block discarded – undo
933 933
         $found = false;
934 934
         foreach (EEH_Line_Item::get_event_subtotals($grand_total) as $event_line_item) {
935 935
             // default event subtotal, we should only ever find this the first time this method is called
936
-            if (! $event_line_item->OBJ_ID()) {
936
+            if ( ! $event_line_item->OBJ_ID()) {
937 937
                 // let's use this! but first... set the event details
938 938
                 EEH_Line_Item::set_event_subtotal_details($event_line_item, $event);
939 939
                 $found = true;
@@ -945,7 +945,7 @@  discard block
 block discarded – undo
945 945
                 break;
946 946
             }
947 947
         }
948
-        if (! $found) {
948
+        if ( ! $found) {
949 949
             // there is no event sub-total yet, so add it
950 950
             $pre_tax_subtotal = EEH_Line_Item::get_pre_tax_subtotal($grand_total);
951 951
             // create a new "event" subtotal below that
@@ -1025,7 +1025,7 @@  discard block
 block discarded – undo
1025 1025
                             break;
1026 1026
                         }
1027 1027
                     }
1028
-                    if (! $found) {
1028
+                    if ( ! $found) {
1029 1029
                         // add a new line item for this global tax
1030 1030
                         $tax_line_item = apply_filters(
1031 1031
                             'FHEE__EEH_Line_Item__apply_taxes__tax_line_item',
@@ -1072,7 +1072,7 @@  discard block
 block discarded – undo
1072 1072
     public static function ensure_taxes_applied($total_line_item)
1073 1073
     {
1074 1074
         $taxes_subtotal = self::get_taxes_subtotal($total_line_item);
1075
-        if (! $taxes_subtotal->children()) {
1075
+        if ( ! $taxes_subtotal->children()) {
1076 1076
             self::apply_taxes($total_line_item);
1077 1077
         }
1078 1078
         return $taxes_subtotal->total();
@@ -1139,7 +1139,7 @@  discard block
 block discarded – undo
1139 1139
         do_action('AHEE_log', __FILE__, __FUNCTION__, '');
1140 1140
 
1141 1141
         // check if only a single line_item_id was passed
1142
-        if (! empty($line_item_codes) && ! is_array($line_item_codes)) {
1142
+        if ( ! empty($line_item_codes) && ! is_array($line_item_codes)) {
1143 1143
             // place single line_item_id in an array to appear as multiple line_item_ids
1144 1144
             $line_item_codes = array($line_item_codes);
1145 1145
         }
@@ -1246,7 +1246,7 @@  discard block
 block discarded – undo
1246 1246
         if ($code_substring_for_whitelist !== null) {
1247 1247
             $whitelisted = strpos($line_item->code(), $code_substring_for_whitelist) !== false;
1248 1248
         }
1249
-        if (! $whitelisted && $line_item->is_line_item()) {
1249
+        if ( ! $whitelisted && $line_item->is_line_item()) {
1250 1250
             $line_item->set_is_taxable($taxable);
1251 1251
         }
1252 1252
         foreach ($line_item->children() as $child_line_item) {
@@ -1615,7 +1615,7 @@  discard block
 block discarded – undo
1615 1615
     {
1616 1616
         $new_line = defined('EE_TESTS_DIR') ? "\n" : '<br />';
1617 1617
         echo $new_line;
1618
-        if (! $indentation) {
1618
+        if ( ! $indentation) {
1619 1619
             echo $new_line;
1620 1620
         }
1621 1621
         echo str_repeat('. ', $indentation);
@@ -1641,8 +1641,8 @@  discard block
 block discarded – undo
1641 1641
                 self::visualize($child, $indentation + 1);
1642 1642
             }
1643 1643
         }
1644
-        if (! $indentation) {
1645
-            echo $new_line . $new_line;
1644
+        if ( ! $indentation) {
1645
+            echo $new_line.$new_line;
1646 1646
         }
1647 1647
     }
1648 1648
 
@@ -1720,8 +1720,8 @@  discard block
 block discarded – undo
1720 1720
                         if ($line_item_id === 'taxable') {
1721 1721
                             continue;
1722 1722
                         }
1723
-                        $taxable_total = $running_totals['taxable'][ $line_item_id ];
1724
-                        $running_totals[ $line_item_id ] += ($taxable_total * $tax_percent_decimal);
1723
+                        $taxable_total = $running_totals['taxable'][$line_item_id];
1724
+                        $running_totals[$line_item_id] += ($taxable_total * $tax_percent_decimal);
1725 1725
                     }
1726 1726
                     break;
1727 1727
 
@@ -1729,7 +1729,7 @@  discard block
 block discarded – undo
1729 1729
                     // ticket line items or ????
1730 1730
                     if ($child_line_item->OBJ_type() === EEM_Line_Item::OBJ_TYPE_TICKET) {
1731 1731
                         // kk it's a ticket
1732
-                        if (isset($running_totals[ $child_line_item->ID() ])) {
1732
+                        if (isset($running_totals[$child_line_item->ID()])) {
1733 1733
                             // huh? that shouldn't happen.
1734 1734
                             $running_totals['total'] += $child_line_item->total();
1735 1735
                         } else {
@@ -1740,18 +1740,18 @@  discard block
 block discarded – undo
1740 1740
                                 $taxable_amount = 0;
1741 1741
                             }
1742 1742
                             // are we only calculating totals for some tickets?
1743
-                            if (isset($billable_ticket_quantities[ $child_line_item->OBJ_ID() ])) {
1744
-                                $quantity = $billable_ticket_quantities[ $child_line_item->OBJ_ID() ];
1745
-                                $running_totals[ $child_line_item->ID() ] = $quantity
1743
+                            if (isset($billable_ticket_quantities[$child_line_item->OBJ_ID()])) {
1744
+                                $quantity = $billable_ticket_quantities[$child_line_item->OBJ_ID()];
1745
+                                $running_totals[$child_line_item->ID()] = $quantity
1746 1746
                                     ? $child_line_item->unit_price()
1747 1747
                                     : 0;
1748
-                                $running_totals['taxable'][ $child_line_item->ID() ] = $quantity
1748
+                                $running_totals['taxable'][$child_line_item->ID()] = $quantity
1749 1749
                                     ? $taxable_amount
1750 1750
                                     : 0;
1751 1751
                             } else {
1752 1752
                                 $quantity = $child_line_item->quantity();
1753
-                                $running_totals[ $child_line_item->ID() ] = $child_line_item->unit_price();
1754
-                                $running_totals['taxable'][ $child_line_item->ID() ] = $taxable_amount;
1753
+                                $running_totals[$child_line_item->ID()] = $child_line_item->unit_price();
1754
+                                $running_totals['taxable'][$child_line_item->ID()] = $taxable_amount;
1755 1755
                             }
1756 1756
                             $running_totals['taxable']['total'] += $taxable_amount * $quantity;
1757 1757
                             $running_totals['total'] += $child_line_item->unit_price() * $quantity;
@@ -1771,12 +1771,12 @@  discard block
 block discarded – undo
1771 1771
                             }
1772 1772
                             // update the running totals
1773 1773
                             // yes this actually even works for the running grand total!
1774
-                            $running_totals[ $line_item_id ] =
1774
+                            $running_totals[$line_item_id] =
1775 1775
                                 $line_items_percent_of_running_total * $this_running_total;
1776 1776
 
1777 1777
                             if ($child_line_item->is_taxable()) {
1778
-                                $running_totals['taxable'][ $line_item_id ] =
1779
-                                    $line_items_percent_of_running_total * $running_totals['taxable'][ $line_item_id ];
1778
+                                $running_totals['taxable'][$line_item_id] =
1779
+                                    $line_items_percent_of_running_total * $running_totals['taxable'][$line_item_id];
1780 1780
                             }
1781 1781
                         }
1782 1782
                     }
@@ -1803,14 +1803,14 @@  discard block
 block discarded – undo
1803 1803
         EE_Line_Item $ticket_line_item
1804 1804
     ) {
1805 1805
         static $final_prices_per_ticket_line_item = array();
1806
-        if (empty($final_prices_per_ticket_line_item) || empty($final_prices_per_ticket_line_item[ $total_line_item->ID() ])) {
1807
-            $final_prices_per_ticket_line_item[ $total_line_item->ID() ] = EEH_Line_Item::calculate_reg_final_prices_per_line_item(
1806
+        if (empty($final_prices_per_ticket_line_item) || empty($final_prices_per_ticket_line_item[$total_line_item->ID()])) {
1807
+            $final_prices_per_ticket_line_item[$total_line_item->ID()] = EEH_Line_Item::calculate_reg_final_prices_per_line_item(
1808 1808
                 $total_line_item
1809 1809
             );
1810 1810
         }
1811 1811
         // ok now find this new registration's final price
1812
-        if (isset($final_prices_per_ticket_line_item[ $total_line_item->ID() ][ $ticket_line_item->ID() ])) {
1813
-            return $final_prices_per_ticket_line_item[ $total_line_item->ID() ][ $ticket_line_item->ID() ];
1812
+        if (isset($final_prices_per_ticket_line_item[$total_line_item->ID()][$ticket_line_item->ID()])) {
1813
+            return $final_prices_per_ticket_line_item[$total_line_item->ID()][$ticket_line_item->ID()];
1814 1814
         }
1815 1815
         $message = sprintf(
1816 1816
             esc_html__(
@@ -1821,7 +1821,7 @@  discard block
 block discarded – undo
1821 1821
             $total_line_item->ID()
1822 1822
         );
1823 1823
         if (WP_DEBUG) {
1824
-            $message .= '<br>' . print_r($final_prices_per_ticket_line_item, true);
1824
+            $message .= '<br>'.print_r($final_prices_per_ticket_line_item, true);
1825 1825
             throw new OutOfRangeException($message);
1826 1826
         }
1827 1827
         EE_Log::instance()->log(__CLASS__, __FUNCTION__, $message);
Please login to merge, or discard this patch.
core/db_models/fields/EE_Money_Field.php 1 patch
Indentation   +75 added lines, -75 removed lines patch added patch discarded remove patch
@@ -9,86 +9,86 @@
 block discarded – undo
9 9
  */
10 10
 class EE_Money_Field extends EE_Float_Field
11 11
 {
12
-    /**
13
-     * @var DecimalValues
14
-     */
15
-    protected $decimal_values;
12
+	/**
13
+	 * @var DecimalValues
14
+	 */
15
+	protected $decimal_values;
16 16
 
17
-    /**
18
-     * @param string $table_column
19
-     * @param string $nicename
20
-     * @param bool   $nullable
21
-     * @param null   $default_value
22
-     */
23
-    public function __construct($table_column, $nicename, $nullable, $default_value = null)
24
-    {
25
-        parent::__construct($table_column, $nicename, $nullable, $default_value);
26
-        $this->setSchemaType('object');
27
-        $this->decimal_values = LoaderFactory::getShared(DecimalValues::class);
28
-    }
17
+	/**
18
+	 * @param string $table_column
19
+	 * @param string $nicename
20
+	 * @param bool   $nullable
21
+	 * @param null   $default_value
22
+	 */
23
+	public function __construct($table_column, $nicename, $nullable, $default_value = null)
24
+	{
25
+		parent::__construct($table_column, $nicename, $nullable, $default_value);
26
+		$this->setSchemaType('object');
27
+		$this->decimal_values = LoaderFactory::getShared(DecimalValues::class);
28
+	}
29 29
 
30 30
 
31
-    /**
32
-     * Schemas:
33
-     *    'localized_float': "3,023.00"
34
-     *    'no_currency_code': "$3,023.00"
35
-     *    null: "$3,023.00<span>USD</span>"
36
-     *
37
-     * @param string $value_on_field_to_be_outputted
38
-     * @param string $schema
39
-     * @return string
40
-     * @throws EE_Error
41
-     */
42
-    public function prepare_for_pretty_echoing($value_on_field_to_be_outputted, $schema = null): string
43
-    {
44
-        if ($schema == 'localized_float') {
45
-            return parent::prepare_for_pretty_echoing($value_on_field_to_be_outputted);
46
-        }
47
-        $display_code = $schema !== 'no_currency_code';
48
-        // we don't use the $pretty_float because format_currency will take care of it.
49
-        return EEH_Template::format_currency($value_on_field_to_be_outputted, false, $display_code);
50
-    }
31
+	/**
32
+	 * Schemas:
33
+	 *    'localized_float': "3,023.00"
34
+	 *    'no_currency_code': "$3,023.00"
35
+	 *    null: "$3,023.00<span>USD</span>"
36
+	 *
37
+	 * @param string $value_on_field_to_be_outputted
38
+	 * @param string $schema
39
+	 * @return string
40
+	 * @throws EE_Error
41
+	 */
42
+	public function prepare_for_pretty_echoing($value_on_field_to_be_outputted, $schema = null): string
43
+	{
44
+		if ($schema == 'localized_float') {
45
+			return parent::prepare_for_pretty_echoing($value_on_field_to_be_outputted);
46
+		}
47
+		$display_code = $schema !== 'no_currency_code';
48
+		// we don't use the $pretty_float because format_currency will take care of it.
49
+		return EEH_Template::format_currency($value_on_field_to_be_outputted, false, $display_code);
50
+	}
51 51
 
52 52
 
53
-    /**
54
-     * If provided with a string, strips out money-related formatting to turn it into a proper float.
55
-     * Rounds the float to the correct number of decimal places for this country's currency.
56
-     * Also, interprets periods and commas according to the country's currency settings.
57
-     * So if you want to pass in a string that NEEDS to interpret periods as decimal marks, call floatval() on it first.
58
-     *
59
-     * @param string $value_inputted_for_field_on_model_object
60
-     * @return float
61
-     */
62
-    public function prepare_for_set($value_inputted_for_field_on_model_object): float
63
-    {
64
-        // now it's a float-style string or number
65
-        $float_val = parent::prepare_for_set($value_inputted_for_field_on_model_object);
66
-        // round to the correctly number of decimal places for this  currency
67
-        return $this->decimal_values->roundDecimalValue($float_val);
68
-    }
53
+	/**
54
+	 * If provided with a string, strips out money-related formatting to turn it into a proper float.
55
+	 * Rounds the float to the correct number of decimal places for this country's currency.
56
+	 * Also, interprets periods and commas according to the country's currency settings.
57
+	 * So if you want to pass in a string that NEEDS to interpret periods as decimal marks, call floatval() on it first.
58
+	 *
59
+	 * @param string $value_inputted_for_field_on_model_object
60
+	 * @return float
61
+	 */
62
+	public function prepare_for_set($value_inputted_for_field_on_model_object): float
63
+	{
64
+		// now it's a float-style string or number
65
+		$float_val = parent::prepare_for_set($value_inputted_for_field_on_model_object);
66
+		// round to the correctly number of decimal places for this  currency
67
+		return $this->decimal_values->roundDecimalValue($float_val);
68
+	}
69 69
 
70 70
 
71
-    /**
72
-     * @return array[]
73
-     */
74
-    public function getSchemaProperties(): array
75
-    {
76
-        return [
77
-            'raw'    => [
78
-                'description' => sprintf(
79
-                    __('%s - the raw value as it exists in the database as a simple float.', 'event_espresso'),
80
-                    $this->get_nicename()
81
-                ),
82
-                'type'        => 'number',
83
-            ],
84
-            'pretty' => [
85
-                'description' => sprintf(
86
-                    __('%s - formatted for display in the set currency and decimal places.', 'event_espresso'),
87
-                    $this->get_nicename()
88
-                ),
89
-                'type'        => 'string',
90
-                'format'      => 'money',
91
-            ],
92
-        ];
93
-    }
71
+	/**
72
+	 * @return array[]
73
+	 */
74
+	public function getSchemaProperties(): array
75
+	{
76
+		return [
77
+			'raw'    => [
78
+				'description' => sprintf(
79
+					__('%s - the raw value as it exists in the database as a simple float.', 'event_espresso'),
80
+					$this->get_nicename()
81
+				),
82
+				'type'        => 'number',
83
+			],
84
+			'pretty' => [
85
+				'description' => sprintf(
86
+					__('%s - formatted for display in the set currency and decimal places.', 'event_espresso'),
87
+					$this->get_nicename()
88
+				),
89
+				'type'        => 'string',
90
+				'format'      => 'money',
91
+			],
92
+		];
93
+	}
94 94
 }
Please login to merge, or discard this patch.
core/EE_Object_Collection.core.php 1 patch
Indentation   +156 added lines, -156 removed lines patch added patch discarded remove patch
@@ -16,160 +16,160 @@
 block discarded – undo
16 16
 abstract class EE_Object_Collection extends SplObjectStorage implements EEI_Collection
17 17
 {
18 18
 
19
-    /**
20
-     * an interface (or class) name to be used for restricting the type of objects added to the storage
21
-     * this should be set from within the child class constructor
22
-     *
23
-     * @type string $interface
24
-     */
25
-    protected $interface;
26
-
27
-
28
-    /**
29
-     * add
30
-     *
31
-     * attaches an object to the Collection
32
-     * and sets any supplied data associated with the current iterator entry
33
-     * by calling EE_Object_Collection::set_info()
34
-     *
35
-     * @access public
36
-     * @param object $object
37
-     * @param mixed  $info
38
-     * @return bool
39
-     */
40
-    public function add($object, $info = null)
41
-    {
42
-        $class = $this->interface;
43
-        if (! $object instanceof $class) {
44
-            return false;
45
-        }
46
-        $this->attach($object);
47
-        $this->set_info($object, $info);
48
-        return $this->contains($object);
49
-    }
50
-
51
-
52
-    /**
53
-     * set_info
54
-     *
55
-     * Sets the data associated with an object in the Collection
56
-     * if no $info is supplied, then the spl_object_hash() is used
57
-     *
58
-     * @access public
59
-     * @param object $object
60
-     * @param mixed  $info
61
-     * @return bool
62
-     */
63
-    public function set_info($object, $info = null)
64
-    {
65
-        $info = ! empty($info) ? $info : spl_object_hash($object);
66
-        $this->rewind();
67
-        while ($this->valid()) {
68
-            if ($object === $this->current()) {
69
-                $this->setInfo($info);
70
-                $this->rewind();
71
-                return true;
72
-            }
73
-            $this->next();
74
-        }
75
-        return false;
76
-    }
77
-
78
-
79
-    /**
80
-     * get_by_info
81
-     *
82
-     * finds and returns an object in the Collection based on the info that was set using addObject()
83
-     * PLZ NOTE: the pointer is reset to the beginning of the collection before returning
84
-     *
85
-     * @access public
86
-     * @param mixed
87
-     * @return null | object
88
-     */
89
-    public function get_by_info($info)
90
-    {
91
-        $this->rewind();
92
-        while ($this->valid()) {
93
-            if ($info === $this->getInfo()) {
94
-                $object = $this->current();
95
-                $this->rewind();
96
-                return $object;
97
-            }
98
-            $this->next();
99
-        }
100
-        return null;
101
-    }
102
-
103
-
104
-    /**
105
-     * has
106
-     *
107
-     * returns TRUE or FALSE depending on whether the supplied object is within the Collection
108
-     *
109
-     * @access public
110
-     * @param object $object
111
-     * @return bool
112
-     */
113
-    public function has($object)
114
-    {
115
-        return $this->contains($object);
116
-    }
117
-
118
-
119
-    /**
120
-     * remove
121
-     *
122
-     * detaches an object from the Collection
123
-     *
124
-     * @access public
125
-     * @param $object
126
-     * @return bool
127
-     */
128
-    public function remove($object)
129
-    {
130
-        $this->detach($object);
131
-        return true;
132
-    }
133
-
134
-
135
-    /**
136
-     * set_current
137
-     *
138
-     * advances pointer to the provided object
139
-     *
140
-     * @access public
141
-     * @param $object
142
-     * @return void
143
-     */
144
-    public function set_current($object)
145
-    {
146
-        $this->rewind();
147
-        while ($this->valid()) {
148
-            if ($this->current() === $object) {
149
-                break;
150
-            }
151
-            $this->next();
152
-        }
153
-    }
154
-
155
-
156
-    /**
157
-     * set_current_by_info
158
-     *
159
-     * advances pointer to the object whose info matches that which was provided
160
-     *
161
-     * @access public
162
-     * @param $info
163
-     * @return void
164
-     */
165
-    public function set_current_by_info($info)
166
-    {
167
-        $this->rewind();
168
-        while ($this->valid()) {
169
-            if ($info === $this->getInfo()) {
170
-                break;
171
-            }
172
-            $this->next();
173
-        }
174
-    }
19
+	/**
20
+	 * an interface (or class) name to be used for restricting the type of objects added to the storage
21
+	 * this should be set from within the child class constructor
22
+	 *
23
+	 * @type string $interface
24
+	 */
25
+	protected $interface;
26
+
27
+
28
+	/**
29
+	 * add
30
+	 *
31
+	 * attaches an object to the Collection
32
+	 * and sets any supplied data associated with the current iterator entry
33
+	 * by calling EE_Object_Collection::set_info()
34
+	 *
35
+	 * @access public
36
+	 * @param object $object
37
+	 * @param mixed  $info
38
+	 * @return bool
39
+	 */
40
+	public function add($object, $info = null)
41
+	{
42
+		$class = $this->interface;
43
+		if (! $object instanceof $class) {
44
+			return false;
45
+		}
46
+		$this->attach($object);
47
+		$this->set_info($object, $info);
48
+		return $this->contains($object);
49
+	}
50
+
51
+
52
+	/**
53
+	 * set_info
54
+	 *
55
+	 * Sets the data associated with an object in the Collection
56
+	 * if no $info is supplied, then the spl_object_hash() is used
57
+	 *
58
+	 * @access public
59
+	 * @param object $object
60
+	 * @param mixed  $info
61
+	 * @return bool
62
+	 */
63
+	public function set_info($object, $info = null)
64
+	{
65
+		$info = ! empty($info) ? $info : spl_object_hash($object);
66
+		$this->rewind();
67
+		while ($this->valid()) {
68
+			if ($object === $this->current()) {
69
+				$this->setInfo($info);
70
+				$this->rewind();
71
+				return true;
72
+			}
73
+			$this->next();
74
+		}
75
+		return false;
76
+	}
77
+
78
+
79
+	/**
80
+	 * get_by_info
81
+	 *
82
+	 * finds and returns an object in the Collection based on the info that was set using addObject()
83
+	 * PLZ NOTE: the pointer is reset to the beginning of the collection before returning
84
+	 *
85
+	 * @access public
86
+	 * @param mixed
87
+	 * @return null | object
88
+	 */
89
+	public function get_by_info($info)
90
+	{
91
+		$this->rewind();
92
+		while ($this->valid()) {
93
+			if ($info === $this->getInfo()) {
94
+				$object = $this->current();
95
+				$this->rewind();
96
+				return $object;
97
+			}
98
+			$this->next();
99
+		}
100
+		return null;
101
+	}
102
+
103
+
104
+	/**
105
+	 * has
106
+	 *
107
+	 * returns TRUE or FALSE depending on whether the supplied object is within the Collection
108
+	 *
109
+	 * @access public
110
+	 * @param object $object
111
+	 * @return bool
112
+	 */
113
+	public function has($object)
114
+	{
115
+		return $this->contains($object);
116
+	}
117
+
118
+
119
+	/**
120
+	 * remove
121
+	 *
122
+	 * detaches an object from the Collection
123
+	 *
124
+	 * @access public
125
+	 * @param $object
126
+	 * @return bool
127
+	 */
128
+	public function remove($object)
129
+	{
130
+		$this->detach($object);
131
+		return true;
132
+	}
133
+
134
+
135
+	/**
136
+	 * set_current
137
+	 *
138
+	 * advances pointer to the provided object
139
+	 *
140
+	 * @access public
141
+	 * @param $object
142
+	 * @return void
143
+	 */
144
+	public function set_current($object)
145
+	{
146
+		$this->rewind();
147
+		while ($this->valid()) {
148
+			if ($this->current() === $object) {
149
+				break;
150
+			}
151
+			$this->next();
152
+		}
153
+	}
154
+
155
+
156
+	/**
157
+	 * set_current_by_info
158
+	 *
159
+	 * advances pointer to the object whose info matches that which was provided
160
+	 *
161
+	 * @access public
162
+	 * @param $info
163
+	 * @return void
164
+	 */
165
+	public function set_current_by_info($info)
166
+	{
167
+		$this->rewind();
168
+		while ($this->valid()) {
169
+			if ($info === $this->getInfo()) {
170
+				break;
171
+			}
172
+			$this->next();
173
+		}
174
+	}
175 175
 }
Please login to merge, or discard this patch.
core/helpers/EEH_Debug_Tools.helper.php 2 patches
Indentation   +705 added lines, -705 removed lines patch added patch discarded remove patch
@@ -13,699 +13,699 @@  discard block
 block discarded – undo
13 13
 class EEH_Debug_Tools
14 14
 {
15 15
 
16
-    /**
17
-     *    instance of the EEH_Autoloader object
18
-     *
19
-     * @var    $_instance
20
-     * @access    private
21
-     */
22
-    private static $_instance;
23
-
24
-    /**
25
-     * @var array
26
-     */
27
-    protected $_memory_usage_points = array();
28
-
29
-
30
-
31
-    /**
32
-     * @singleton method used to instantiate class object
33
-     * @access    public
34
-     * @return EEH_Debug_Tools
35
-     */
36
-    public static function instance()
37
-    {
38
-        // check if class object is instantiated, and instantiated properly
39
-        if (! self::$_instance instanceof EEH_Debug_Tools) {
40
-            self::$_instance = new self();
41
-        }
42
-        return self::$_instance;
43
-    }
44
-
45
-
46
-
47
-    /**
48
-     * private class constructor
49
-     */
50
-    private function __construct()
51
-    {
52
-        // load Kint PHP debugging library
53
-        if (! class_exists('Kint') && file_exists(EE_PLUGIN_DIR_PATH . 'tests/kint/Kint.class.php')) {
54
-            // despite EE4 having a check for an existing copy of the Kint debugging class,
55
-            // if another plugin was loaded AFTER EE4 and they did NOT perform a similar check,
56
-            // then hilarity would ensue as PHP throws a "Cannot redeclare class Kint" error
57
-            // so we've moved it to our test folder so that it is not included with production releases
58
-            // plz use https://wordpress.org/plugins/kint-debugger/  if testing production versions of EE
59
-            require_once(EE_PLUGIN_DIR_PATH . 'tests/kint/Kint.class.php');
60
-        }
61
-        // if ( ! defined('DOING_AJAX') || $_REQUEST['noheader'] !== 'true' || ! isset( $_REQUEST['noheader'], $_REQUEST['TB_iframe'] ) ) {
62
-        // add_action( 'shutdown', array($this,'espresso_session_footer_dump') );
63
-        // }
64
-        $plugin = basename(EE_PLUGIN_DIR_PATH);
65
-        add_action("activate_{$plugin}", array('EEH_Debug_Tools', 'ee_plugin_activation_errors'));
66
-        add_action('activated_plugin', array('EEH_Debug_Tools', 'ee_plugin_activation_errors'));
67
-        add_action('shutdown', array('EEH_Debug_Tools', 'show_db_name'));
68
-    }
69
-
70
-
71
-
72
-    /**
73
-     *    show_db_name
74
-     *
75
-     * @return void
76
-     */
77
-    public static function show_db_name()
78
-    {
79
-        if (! defined('DOING_AJAX') && (defined('EE_ERROR_EMAILS') && EE_ERROR_EMAILS)) {
80
-            echo '<p style="font-size:10px;font-weight:normal;color:#E76700;margin: 1em 2em; text-align: right;">DB_NAME: '
81
-                 . DB_NAME
82
-                 . '</p>';
83
-        }
84
-        if (EE_DEBUG) {
85
-            Benchmark::displayResults();
86
-        }
87
-    }
88
-
89
-
90
-
91
-    /**
92
-     *    dump EE_Session object at bottom of page after everything else has happened
93
-     *
94
-     * @return void
95
-     */
96
-    public function espresso_session_footer_dump()
97
-    {
98
-        if (
99
-            (defined('WP_DEBUG') && WP_DEBUG)
100
-            && ! defined('DOING_AJAX')
101
-            && class_exists('Kint')
102
-            && function_exists('wp_get_current_user')
103
-            && current_user_can('update_core')
104
-            && class_exists('EE_Registry')
105
-        ) {
106
-            Kint::dump(EE_Registry::instance()->SSN->id());
107
-            Kint::dump(EE_Registry::instance()->SSN);
108
-            //          Kint::dump( EE_Registry::instance()->SSN->get_session_data('cart')->get_tickets() );
109
-            $this->espresso_list_hooked_functions();
110
-            Benchmark::displayResults();
111
-        }
112
-    }
113
-
114
-
115
-
116
-    /**
117
-     *    List All Hooked Functions
118
-     *    to list all functions for a specific hook, add ee_list_hooks={hook-name} to URL
119
-     *    http://wp.smashingmagazine.com/2009/08/18/10-useful-wordpress-hook-hacks/
120
-     *
121
-     * @param string $tag
122
-     * @return void
123
-     */
124
-    public function espresso_list_hooked_functions($tag = '')
125
-    {
126
-        global $wp_filter;
127
-        echo '<br/><br/><br/><h3>Hooked Functions</h3>';
128
-        if ($tag) {
129
-            $hook[ $tag ] = $wp_filter[ $tag ];
130
-            if (! is_array($hook[ $tag ])) {
131
-                trigger_error("Nothing found for '$tag' hook", E_USER_WARNING);
132
-                return;
133
-            }
134
-            echo '<h5>For Tag: ' . $tag . '</h5>';
135
-        } else {
136
-            $hook = is_array($wp_filter) ? $wp_filter : array($wp_filter);
137
-            ksort($hook);
138
-        }
139
-        foreach ($hook as $tag_name => $priorities) {
140
-            echo "<br />&gt;&gt;&gt;&gt;&gt;\t<strong>$tag_name</strong><br />";
141
-            ksort($priorities);
142
-            foreach ($priorities as $priority => $function) {
143
-                echo $priority;
144
-                foreach ($function as $name => $properties) {
145
-                    echo "\t$name<br />";
146
-                }
147
-            }
148
-        }
149
-    }
150
-
151
-
152
-
153
-    /**
154
-     *    registered_filter_callbacks
155
-     *
156
-     * @param string $hook_name
157
-     * @return array
158
-     */
159
-    public static function registered_filter_callbacks($hook_name = '')
160
-    {
161
-        $filters = array();
162
-        global $wp_filter;
163
-        if (isset($wp_filter[ $hook_name ])) {
164
-            $filters[ $hook_name ] = array();
165
-            foreach ($wp_filter[ $hook_name ] as $priority => $callbacks) {
166
-                $filters[ $hook_name ][ $priority ] = array();
167
-                foreach ($callbacks as $callback) {
168
-                    $filters[ $hook_name ][ $priority ][] = $callback['function'];
169
-                }
170
-            }
171
-        }
172
-        return $filters;
173
-    }
174
-
175
-
176
-
177
-    /**
178
-     *    captures plugin activation errors for debugging
179
-     *
180
-     * @return void
181
-     * @throws EE_Error
182
-     */
183
-    public static function ee_plugin_activation_errors()
184
-    {
185
-        if (WP_DEBUG) {
186
-            $activation_errors = ob_get_contents();
187
-            if (empty($activation_errors)) {
188
-                return;
189
-            }
190
-            $activation_errors = date('Y-m-d H:i:s') . "\n" . $activation_errors;
191
-            espresso_load_required('EEH_File', EE_HELPERS . 'EEH_File.helper.php');
192
-            if (class_exists('EEH_File')) {
193
-                try {
194
-                    EEH_File::ensure_file_exists_and_is_writable(
195
-                        EVENT_ESPRESSO_UPLOAD_DIR . 'logs/espresso_plugin_activation_errors.html'
196
-                    );
197
-                    EEH_File::write_to_file(
198
-                        EVENT_ESPRESSO_UPLOAD_DIR . 'logs/espresso_plugin_activation_errors.html',
199
-                        $activation_errors
200
-                    );
201
-                } catch (EE_Error $e) {
202
-                    EE_Error::add_error(
203
-                        sprintf(
204
-                            __(
205
-                                'The Event Espresso activation errors file could not be setup because: %s',
206
-                                'event_espresso'
207
-                            ),
208
-                            $e->getMessage()
209
-                        ),
210
-                        __FILE__,
211
-                        __FUNCTION__,
212
-                        __LINE__
213
-                    );
214
-                }
215
-            } else {
216
-                // old school attempt
217
-                file_put_contents(
218
-                    EVENT_ESPRESSO_UPLOAD_DIR . 'logs/espresso_plugin_activation_errors.html',
219
-                    $activation_errors
220
-                );
221
-            }
222
-            $activation_errors = get_option('ee_plugin_activation_errors', '') . $activation_errors;
223
-            update_option('ee_plugin_activation_errors', $activation_errors);
224
-        }
225
-    }
226
-
227
-
228
-
229
-    /**
230
-     * This basically mimics the WordPress _doing_it_wrong() function except adds our own messaging etc.
231
-     * Very useful for providing helpful messages to developers when the method of doing something has been deprecated,
232
-     * or we want to make sure they use something the right way.
233
-     *
234
-     * @access public
235
-     * @param string $function      The function that was called
236
-     * @param string $message       A message explaining what has been done incorrectly
237
-     * @param string $version       The version of Event Espresso where the error was added
238
-     * @param string $applies_when  a version string for when you want the doing_it_wrong notice to begin appearing
239
-     *                              for a deprecated function. This allows deprecation to occur during one version,
240
-     *                              but not have any notices appear until a later version. This allows developers
241
-     *                              extra time to update their code before notices appear.
242
-     * @param int    $error_type
243
-     * @uses   trigger_error()
244
-     */
245
-    public function doing_it_wrong(
246
-        $function,
247
-        $message,
248
-        $version,
249
-        $applies_when = '',
250
-        $error_type = null
251
-    ) {
252
-        $applies_when = ! empty($applies_when) ? $applies_when : espresso_version();
253
-        $error_type = $error_type !== null ? $error_type : E_USER_NOTICE;
254
-        // because we swapped the parameter order around for the last two params,
255
-        // let's verify that some third party isn't still passing an error type value for the third param
256
-        if (is_int($applies_when)) {
257
-            $error_type = $applies_when;
258
-            $applies_when = espresso_version();
259
-        }
260
-        // if not displaying notices yet, then just leave
261
-        if (version_compare(espresso_version(), $applies_when, '<')) {
262
-            return;
263
-        }
264
-        do_action('AHEE__EEH_Debug_Tools__doing_it_wrong_run', $function, $message, $version);
265
-        $version = $version === null
266
-            ? ''
267
-            : sprintf(
268
-                __('(This message was added in version %s of Event Espresso)', 'event_espresso'),
269
-                $version
270
-            );
271
-        $error_message = sprintf(
272
-            esc_html__('%1$s was called %2$sincorrectly%3$s. %4$s %5$s', 'event_espresso'),
273
-            $function,
274
-            '<strong>',
275
-            '</strong>',
276
-            $message,
277
-            $version
278
-        );
279
-        // don't trigger error if doing ajax,
280
-        // instead we'll add a transient EE_Error notice that in theory should show on the next request.
281
-        if (defined('DOING_AJAX') && DOING_AJAX) {
282
-            $error_message .= ' ' . esc_html__(
283
-                'This is a doing_it_wrong message that was triggered during an ajax request.  The request params on this request were: ',
284
-                'event_espresso'
285
-            );
286
-            $error_message .= '<ul><li>';
287
-            $error_message .= implode('</li><li>', EE_Registry::instance()->REQ->params());
288
-            $error_message .= '</ul>';
289
-            EE_Error::add_error($error_message, 'debug::doing_it_wrong', $function, '42');
290
-            // now we set this on the transient so it shows up on the next request.
291
-            EE_Error::get_notices(false, true);
292
-        } else {
293
-            trigger_error($error_message, $error_type);
294
-        }
295
-    }
296
-
297
-
298
-
299
-
300
-    /**
301
-     * Logger helpers
302
-     */
303
-    /**
304
-     * debug
305
-     *
306
-     * @param string $class
307
-     * @param string $func
308
-     * @param string $line
309
-     * @param array  $info
310
-     * @param bool   $display_request
311
-     * @param string $debug_index
312
-     * @param string $debug_key
313
-     * @throws EE_Error
314
-     * @throws \EventEspresso\core\exceptions\InvalidSessionDataException
315
-     */
316
-    public static function log(
317
-        $class = '',
318
-        $func = '',
319
-        $line = '',
320
-        $info = array(),
321
-        $display_request = false,
322
-        $debug_index = '',
323
-        $debug_key = 'EE_DEBUG_SPCO'
324
-    ) {
325
-        if (WP_DEBUG) {
326
-            $debug_key = $debug_key . '_' . EE_Session::instance()->id();
327
-            $debug_data = get_option($debug_key, array());
328
-            $default_data = array(
329
-                $class => $func . '() : ' . $line,
330
-                'REQ'  => $display_request ? $_REQUEST : '',
331
-            );
332
-            // don't serialize objects
333
-            $info = self::strip_objects($info);
334
-            $index = ! empty($debug_index) ? $debug_index : 0;
335
-            if (! isset($debug_data[ $index ])) {
336
-                $debug_data[ $index ] = array();
337
-            }
338
-            $debug_data[ $index ][ microtime() ] = array_merge($default_data, $info);
339
-            update_option($debug_key, $debug_data);
340
-        }
341
-    }
342
-
343
-
344
-
345
-    /**
346
-     * strip_objects
347
-     *
348
-     * @param array $info
349
-     * @return array
350
-     */
351
-    public static function strip_objects($info = array())
352
-    {
353
-        foreach ($info as $key => $value) {
354
-            if (is_array($value)) {
355
-                $info[ $key ] = self::strip_objects($value);
356
-            } elseif (is_object($value)) {
357
-                $object_class = get_class($value);
358
-                $info[ $object_class ] = array();
359
-                $info[ $object_class ]['ID'] = method_exists($value, 'ID') ? $value->ID() : spl_object_hash($value);
360
-                if (method_exists($value, 'ID')) {
361
-                    $info[ $object_class ]['ID'] = $value->ID();
362
-                }
363
-                if (method_exists($value, 'status')) {
364
-                    $info[ $object_class ]['status'] = $value->status();
365
-                } elseif (method_exists($value, 'status_ID')) {
366
-                    $info[ $object_class ]['status'] = $value->status_ID();
367
-                }
368
-                unset($info[ $key ]);
369
-            }
370
-        }
371
-        return (array) $info;
372
-    }
373
-
374
-
375
-
376
-    /**
377
-     * @param mixed      $var
378
-     * @param string     $var_name
379
-     * @param string     $file
380
-     * @param int|string $line
381
-     * @param int|string $heading_tag
382
-     * @param bool       $die
383
-     * @param string     $margin
384
-     */
385
-    public static function printv(
386
-        $var,
387
-        $var_name = '',
388
-        $file = '',
389
-        $line = '',
390
-        $heading_tag = 5,
391
-        $die = false,
392
-        $margin = ''
393
-    ) {
394
-        $var_name = ! $var_name ? 'string' : $var_name;
395
-        $var_name = ucwords(str_replace('$', '', $var_name));
396
-        $is_method = method_exists($var_name, $var);
397
-        $var_name = ucwords(str_replace('_', ' ', $var_name));
398
-        $heading_tag = EEH_Debug_Tools::headingTag($heading_tag);
399
-        // $result = EEH_Debug_Tools::headingSpacer($heading_tag);
400
-        $result = EEH_Debug_Tools::heading($var_name, $heading_tag, $margin, $line);
401
-        $result .= $is_method
402
-            ? EEH_Debug_Tools::grey_span('::') . EEH_Debug_Tools::orange_span($var . '()')
403
-            : EEH_Debug_Tools::grey_span(' : ') . EEH_Debug_Tools::orange_span($var);
404
-        $result .= EEH_Debug_Tools::file_and_line($file, $line, $heading_tag);
405
-        $result .= EEH_Debug_Tools::headingX($heading_tag);
406
-        if ($die) {
407
-            die($result);
408
-        }
409
-        echo $result;
410
-    }
411
-
412
-
413
-    protected static function headingTag($heading_tag)
414
-    {
415
-        $heading_tag = absint($heading_tag);
416
-        return $heading_tag > 0 && $heading_tag < 7 ? "h{$heading_tag}" : 'h5';
417
-    }
418
-
419
-
420
-    protected static function headingSpacer($heading_tag)
421
-    {
422
-        return EEH_Debug_Tools::plainOutput() && ($heading_tag === 'h1' || $heading_tag === 'h2')
423
-            ? "\n"
424
-            : '';
425
-    }
426
-
427
-
428
-    protected static function plainOutput()
429
-    {
430
-        return defined('EE_TESTS_DIR')
431
-               || (defined('DOING_AJAX') && DOING_AJAX && ! isset($_REQUEST['pretty_output']))
432
-               || (
433
-                   isset($_SERVER['REQUEST_URI'])
434
-                   && strpos(parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH), 'wp-json') !== false
435
-               );
436
-    }
437
-
438
-
439
-    /**
440
-     * @param string $var_name
441
-     * @param string $heading_tag
442
-     * @param string $margin
443
-     * @param int    $line
444
-     * @return string
445
-     */
446
-    protected static function heading($var_name = '', $heading_tag = 'h5', $margin = '', $line = 0)
447
-    {
448
-        if (EEH_Debug_Tools::plainOutput()) {
449
-            switch ($heading_tag) {
450
-                case 'h1':
451
-                    $line_breaks = EEH_Debug_Tools::lineBreak(3);
452
-                    break;
453
-                case 'h2':
454
-                    $line_breaks = EEH_Debug_Tools::lineBreak(2);
455
-                    break;
456
-                default:
457
-                    $line_breaks = EEH_Debug_Tools::lineBreak();
458
-                    break;
459
-            }
460
-            return "{$line_breaks}{$line}) {$var_name}";
461
-        }
462
-        $margin = "25px 0 0 {$margin}";
463
-        return '<' . $heading_tag . ' style="color:#2EA2CC; margin:' . $margin . ';"><b>' . $var_name . '</b>';
464
-    }
465
-
466
-
467
-
468
-    /**
469
-     * @param string $heading_tag
470
-     * @return string
471
-     */
472
-    protected static function headingX($heading_tag = 'h5')
473
-    {
474
-        if (EEH_Debug_Tools::plainOutput()) {
475
-            return '';
476
-        }
477
-        return '</' . $heading_tag . '>';
478
-    }
479
-
480
-
481
-
482
-    /**
483
-     * @param string $content
484
-     * @return string
485
-     */
486
-    protected static function grey_span($content = '')
487
-    {
488
-        if (EEH_Debug_Tools::plainOutput()) {
489
-            return $content;
490
-        }
491
-        return '<span style="color:#999">' . $content . '</span>';
492
-    }
493
-
494
-
495
-
496
-    /**
497
-     * @param string $file
498
-     * @param int    $line
499
-     * @return string
500
-     */
501
-    protected static function file_and_line($file, $line, $heading_tag)
502
-    {
503
-        if ($file === '' || $line === '') {
504
-            return '';
505
-        }
506
-        $file = str_replace(EE_PLUGIN_DIR_PATH, '/', $file);
507
-        if (EEH_Debug_Tools::plainOutput()) {
508
-            if ($heading_tag === 'h1' || $heading_tag === 'h2') {
509
-                return " ({$file})" . EEH_Debug_Tools::lineBreak();
510
-            }
511
-            return '';
512
-        }
513
-        return EEH_Debug_Tools::lineBreak()
514
-               . '<span style="font-size:9px;font-weight:normal;color:#666;line-height: 12px;">'
515
-               . $file
516
-               . EEH_Debug_Tools::lineBreak()
517
-               . 'line no: '
518
-               . $line
519
-               . '</span>';
520
-    }
521
-
522
-
523
-
524
-    /**
525
-     * @param string $content
526
-     * @return string
527
-     */
528
-    protected static function orange_span($content = '')
529
-    {
530
-        if (EEH_Debug_Tools::plainOutput()) {
531
-            return $content;
532
-        }
533
-        return '<span style="color:#E76700">' . $content . '</span>';
534
-    }
535
-
536
-
537
-
538
-    /**
539
-     * @param mixed $var
540
-     * @return string
541
-     */
542
-    protected static function pre_span($var)
543
-    {
544
-        ob_start();
545
-        var_dump($var);
546
-        $var = ob_get_clean();
547
-        if (EEH_Debug_Tools::plainOutput()) {
548
-            return $var;
549
-        }
550
-        return '<pre style="color: #9C3; display: inline-block; padding:.4em .6em; background: #334">' . $var . '</pre>';
551
-    }
552
-
553
-
554
-
555
-    /**
556
-     * @param mixed      $var
557
-     * @param string     $var_name
558
-     * @param string     $file
559
-     * @param int|string $line
560
-     * @param int|string $heading_tag
561
-     * @param bool       $die
562
-     */
563
-    public static function printr(
564
-        $var,
565
-        $var_name = '',
566
-        $file = '',
567
-        $line = '',
568
-        $heading_tag = 5,
569
-        $die = false
570
-    ) {
571
-        // return;
572
-        $file = str_replace(rtrim(ABSPATH, '\\/'), '', $file);
573
-        if (empty($var) && empty($var_name)) {
574
-            $var = $file;
575
-            $var_name = "line $line";
576
-            $file = '';
577
-            $line = '';
578
-        }
579
-        $margin = is_admin() ? ' 180px' : '0';
580
-        // $print_r = false;
581
-        if (is_string($var)) {
582
-            EEH_Debug_Tools::printv($var, $var_name, $file, $line, $heading_tag, $die, $margin);
583
-            return;
584
-        }
585
-        if (is_object($var)) {
586
-            $var_name = ! $var_name ? 'object' : $var_name;
587
-            // $print_r = true;
588
-        } elseif (is_array($var)) {
589
-            $var_name = ! $var_name ? 'array' : $var_name;
590
-            // $print_r = true;
591
-        } elseif (is_numeric($var)) {
592
-            $var_name = ! $var_name ? 'numeric' : $var_name;
593
-        } elseif ($var === null) {
594
-            $var_name = ! $var_name ? 'null' : $var_name;
595
-        }
596
-        $var_name = ucwords(str_replace(array('$', '_'), array('', ' '), $var_name));
597
-        $heading_tag = EEH_Debug_Tools::headingTag($heading_tag);
598
-        // $result = EEH_Debug_Tools::headingSpacer($heading_tag);
599
-        $result = EEH_Debug_Tools::heading($var_name, $heading_tag, $margin, $line);
600
-        $result .= EEH_Debug_Tools::grey_span(' : ') . EEH_Debug_Tools::orange_span(
601
-            EEH_Debug_Tools::pre_span($var)
602
-        );
603
-        $result .= EEH_Debug_Tools::file_and_line($file, $line, $heading_tag);
604
-        $result .= EEH_Debug_Tools::headingX($heading_tag);
605
-        if ($die) {
606
-            die($result);
607
-        }
608
-        echo $result;
609
-    }
610
-
611
-
612
-    private static function lineBreak($lines = 1): string
613
-    {
614
-        $linebreak = defined('DOING_AJAX') && DOING_AJAX ? '<br />' : PHP_EOL;
615
-        return str_repeat($linebreak, $lines);
616
-    }
617
-
618
-
619
-    public static function shortClassName(string $fqcn): string
620
-    {
621
-        return substr(strrchr($fqcn, '\\'), 1);
622
-    }
623
-
624
-
625
-
626
-    /******************** deprecated ********************/
627
-
628
-
629
-
630
-    /**
631
-     * @deprecated 4.9.39.rc.034
632
-     */
633
-    public function reset_times()
634
-    {
635
-        Benchmark::resetTimes();
636
-    }
637
-
638
-
639
-
640
-    /**
641
-     * @deprecated 4.9.39.rc.034
642
-     * @param null $timer_name
643
-     */
644
-    public function start_timer($timer_name = null)
645
-    {
646
-        Benchmark::startTimer($timer_name);
647
-    }
648
-
649
-
650
-
651
-    /**
652
-     * @deprecated 4.9.39.rc.034
653
-     * @param string $timer_name
654
-     */
655
-    public function stop_timer($timer_name = '')
656
-    {
657
-        Benchmark::stopTimer($timer_name);
658
-    }
659
-
660
-
661
-
662
-    /**
663
-     * @deprecated 4.9.39.rc.034
664
-     * @param string  $label      The label to show for this time eg "Start of calling Some_Class::some_function"
665
-     * @param boolean $output_now whether to echo now, or wait until EEH_Debug_Tools::show_times() is called
666
-     * @return void
667
-     */
668
-    public function measure_memory($label, $output_now = false)
669
-    {
670
-        Benchmark::measureMemory($label, $output_now);
671
-    }
672
-
673
-
674
-
675
-    /**
676
-     * @deprecated 4.9.39.rc.034
677
-     * @param int $size
678
-     * @return string
679
-     */
680
-    public function convert($size)
681
-    {
682
-        return Benchmark::convert($size);
683
-    }
684
-
685
-
686
-
687
-    /**
688
-     * @deprecated 4.9.39.rc.034
689
-     * @param bool $output_now
690
-     * @return string
691
-     */
692
-    public function show_times($output_now = true)
693
-    {
694
-        return Benchmark::displayResults($output_now);
695
-    }
16
+	/**
17
+	 *    instance of the EEH_Autoloader object
18
+	 *
19
+	 * @var    $_instance
20
+	 * @access    private
21
+	 */
22
+	private static $_instance;
23
+
24
+	/**
25
+	 * @var array
26
+	 */
27
+	protected $_memory_usage_points = array();
28
+
29
+
30
+
31
+	/**
32
+	 * @singleton method used to instantiate class object
33
+	 * @access    public
34
+	 * @return EEH_Debug_Tools
35
+	 */
36
+	public static function instance()
37
+	{
38
+		// check if class object is instantiated, and instantiated properly
39
+		if (! self::$_instance instanceof EEH_Debug_Tools) {
40
+			self::$_instance = new self();
41
+		}
42
+		return self::$_instance;
43
+	}
44
+
45
+
46
+
47
+	/**
48
+	 * private class constructor
49
+	 */
50
+	private function __construct()
51
+	{
52
+		// load Kint PHP debugging library
53
+		if (! class_exists('Kint') && file_exists(EE_PLUGIN_DIR_PATH . 'tests/kint/Kint.class.php')) {
54
+			// despite EE4 having a check for an existing copy of the Kint debugging class,
55
+			// if another plugin was loaded AFTER EE4 and they did NOT perform a similar check,
56
+			// then hilarity would ensue as PHP throws a "Cannot redeclare class Kint" error
57
+			// so we've moved it to our test folder so that it is not included with production releases
58
+			// plz use https://wordpress.org/plugins/kint-debugger/  if testing production versions of EE
59
+			require_once(EE_PLUGIN_DIR_PATH . 'tests/kint/Kint.class.php');
60
+		}
61
+		// if ( ! defined('DOING_AJAX') || $_REQUEST['noheader'] !== 'true' || ! isset( $_REQUEST['noheader'], $_REQUEST['TB_iframe'] ) ) {
62
+		// add_action( 'shutdown', array($this,'espresso_session_footer_dump') );
63
+		// }
64
+		$plugin = basename(EE_PLUGIN_DIR_PATH);
65
+		add_action("activate_{$plugin}", array('EEH_Debug_Tools', 'ee_plugin_activation_errors'));
66
+		add_action('activated_plugin', array('EEH_Debug_Tools', 'ee_plugin_activation_errors'));
67
+		add_action('shutdown', array('EEH_Debug_Tools', 'show_db_name'));
68
+	}
69
+
70
+
71
+
72
+	/**
73
+	 *    show_db_name
74
+	 *
75
+	 * @return void
76
+	 */
77
+	public static function show_db_name()
78
+	{
79
+		if (! defined('DOING_AJAX') && (defined('EE_ERROR_EMAILS') && EE_ERROR_EMAILS)) {
80
+			echo '<p style="font-size:10px;font-weight:normal;color:#E76700;margin: 1em 2em; text-align: right;">DB_NAME: '
81
+				 . DB_NAME
82
+				 . '</p>';
83
+		}
84
+		if (EE_DEBUG) {
85
+			Benchmark::displayResults();
86
+		}
87
+	}
88
+
89
+
90
+
91
+	/**
92
+	 *    dump EE_Session object at bottom of page after everything else has happened
93
+	 *
94
+	 * @return void
95
+	 */
96
+	public function espresso_session_footer_dump()
97
+	{
98
+		if (
99
+			(defined('WP_DEBUG') && WP_DEBUG)
100
+			&& ! defined('DOING_AJAX')
101
+			&& class_exists('Kint')
102
+			&& function_exists('wp_get_current_user')
103
+			&& current_user_can('update_core')
104
+			&& class_exists('EE_Registry')
105
+		) {
106
+			Kint::dump(EE_Registry::instance()->SSN->id());
107
+			Kint::dump(EE_Registry::instance()->SSN);
108
+			//          Kint::dump( EE_Registry::instance()->SSN->get_session_data('cart')->get_tickets() );
109
+			$this->espresso_list_hooked_functions();
110
+			Benchmark::displayResults();
111
+		}
112
+	}
113
+
114
+
115
+
116
+	/**
117
+	 *    List All Hooked Functions
118
+	 *    to list all functions for a specific hook, add ee_list_hooks={hook-name} to URL
119
+	 *    http://wp.smashingmagazine.com/2009/08/18/10-useful-wordpress-hook-hacks/
120
+	 *
121
+	 * @param string $tag
122
+	 * @return void
123
+	 */
124
+	public function espresso_list_hooked_functions($tag = '')
125
+	{
126
+		global $wp_filter;
127
+		echo '<br/><br/><br/><h3>Hooked Functions</h3>';
128
+		if ($tag) {
129
+			$hook[ $tag ] = $wp_filter[ $tag ];
130
+			if (! is_array($hook[ $tag ])) {
131
+				trigger_error("Nothing found for '$tag' hook", E_USER_WARNING);
132
+				return;
133
+			}
134
+			echo '<h5>For Tag: ' . $tag . '</h5>';
135
+		} else {
136
+			$hook = is_array($wp_filter) ? $wp_filter : array($wp_filter);
137
+			ksort($hook);
138
+		}
139
+		foreach ($hook as $tag_name => $priorities) {
140
+			echo "<br />&gt;&gt;&gt;&gt;&gt;\t<strong>$tag_name</strong><br />";
141
+			ksort($priorities);
142
+			foreach ($priorities as $priority => $function) {
143
+				echo $priority;
144
+				foreach ($function as $name => $properties) {
145
+					echo "\t$name<br />";
146
+				}
147
+			}
148
+		}
149
+	}
150
+
151
+
152
+
153
+	/**
154
+	 *    registered_filter_callbacks
155
+	 *
156
+	 * @param string $hook_name
157
+	 * @return array
158
+	 */
159
+	public static function registered_filter_callbacks($hook_name = '')
160
+	{
161
+		$filters = array();
162
+		global $wp_filter;
163
+		if (isset($wp_filter[ $hook_name ])) {
164
+			$filters[ $hook_name ] = array();
165
+			foreach ($wp_filter[ $hook_name ] as $priority => $callbacks) {
166
+				$filters[ $hook_name ][ $priority ] = array();
167
+				foreach ($callbacks as $callback) {
168
+					$filters[ $hook_name ][ $priority ][] = $callback['function'];
169
+				}
170
+			}
171
+		}
172
+		return $filters;
173
+	}
174
+
175
+
176
+
177
+	/**
178
+	 *    captures plugin activation errors for debugging
179
+	 *
180
+	 * @return void
181
+	 * @throws EE_Error
182
+	 */
183
+	public static function ee_plugin_activation_errors()
184
+	{
185
+		if (WP_DEBUG) {
186
+			$activation_errors = ob_get_contents();
187
+			if (empty($activation_errors)) {
188
+				return;
189
+			}
190
+			$activation_errors = date('Y-m-d H:i:s') . "\n" . $activation_errors;
191
+			espresso_load_required('EEH_File', EE_HELPERS . 'EEH_File.helper.php');
192
+			if (class_exists('EEH_File')) {
193
+				try {
194
+					EEH_File::ensure_file_exists_and_is_writable(
195
+						EVENT_ESPRESSO_UPLOAD_DIR . 'logs/espresso_plugin_activation_errors.html'
196
+					);
197
+					EEH_File::write_to_file(
198
+						EVENT_ESPRESSO_UPLOAD_DIR . 'logs/espresso_plugin_activation_errors.html',
199
+						$activation_errors
200
+					);
201
+				} catch (EE_Error $e) {
202
+					EE_Error::add_error(
203
+						sprintf(
204
+							__(
205
+								'The Event Espresso activation errors file could not be setup because: %s',
206
+								'event_espresso'
207
+							),
208
+							$e->getMessage()
209
+						),
210
+						__FILE__,
211
+						__FUNCTION__,
212
+						__LINE__
213
+					);
214
+				}
215
+			} else {
216
+				// old school attempt
217
+				file_put_contents(
218
+					EVENT_ESPRESSO_UPLOAD_DIR . 'logs/espresso_plugin_activation_errors.html',
219
+					$activation_errors
220
+				);
221
+			}
222
+			$activation_errors = get_option('ee_plugin_activation_errors', '') . $activation_errors;
223
+			update_option('ee_plugin_activation_errors', $activation_errors);
224
+		}
225
+	}
226
+
227
+
228
+
229
+	/**
230
+	 * This basically mimics the WordPress _doing_it_wrong() function except adds our own messaging etc.
231
+	 * Very useful for providing helpful messages to developers when the method of doing something has been deprecated,
232
+	 * or we want to make sure they use something the right way.
233
+	 *
234
+	 * @access public
235
+	 * @param string $function      The function that was called
236
+	 * @param string $message       A message explaining what has been done incorrectly
237
+	 * @param string $version       The version of Event Espresso where the error was added
238
+	 * @param string $applies_when  a version string for when you want the doing_it_wrong notice to begin appearing
239
+	 *                              for a deprecated function. This allows deprecation to occur during one version,
240
+	 *                              but not have any notices appear until a later version. This allows developers
241
+	 *                              extra time to update their code before notices appear.
242
+	 * @param int    $error_type
243
+	 * @uses   trigger_error()
244
+	 */
245
+	public function doing_it_wrong(
246
+		$function,
247
+		$message,
248
+		$version,
249
+		$applies_when = '',
250
+		$error_type = null
251
+	) {
252
+		$applies_when = ! empty($applies_when) ? $applies_when : espresso_version();
253
+		$error_type = $error_type !== null ? $error_type : E_USER_NOTICE;
254
+		// because we swapped the parameter order around for the last two params,
255
+		// let's verify that some third party isn't still passing an error type value for the third param
256
+		if (is_int($applies_when)) {
257
+			$error_type = $applies_when;
258
+			$applies_when = espresso_version();
259
+		}
260
+		// if not displaying notices yet, then just leave
261
+		if (version_compare(espresso_version(), $applies_when, '<')) {
262
+			return;
263
+		}
264
+		do_action('AHEE__EEH_Debug_Tools__doing_it_wrong_run', $function, $message, $version);
265
+		$version = $version === null
266
+			? ''
267
+			: sprintf(
268
+				__('(This message was added in version %s of Event Espresso)', 'event_espresso'),
269
+				$version
270
+			);
271
+		$error_message = sprintf(
272
+			esc_html__('%1$s was called %2$sincorrectly%3$s. %4$s %5$s', 'event_espresso'),
273
+			$function,
274
+			'<strong>',
275
+			'</strong>',
276
+			$message,
277
+			$version
278
+		);
279
+		// don't trigger error if doing ajax,
280
+		// instead we'll add a transient EE_Error notice that in theory should show on the next request.
281
+		if (defined('DOING_AJAX') && DOING_AJAX) {
282
+			$error_message .= ' ' . esc_html__(
283
+				'This is a doing_it_wrong message that was triggered during an ajax request.  The request params on this request were: ',
284
+				'event_espresso'
285
+			);
286
+			$error_message .= '<ul><li>';
287
+			$error_message .= implode('</li><li>', EE_Registry::instance()->REQ->params());
288
+			$error_message .= '</ul>';
289
+			EE_Error::add_error($error_message, 'debug::doing_it_wrong', $function, '42');
290
+			// now we set this on the transient so it shows up on the next request.
291
+			EE_Error::get_notices(false, true);
292
+		} else {
293
+			trigger_error($error_message, $error_type);
294
+		}
295
+	}
296
+
297
+
298
+
299
+
300
+	/**
301
+	 * Logger helpers
302
+	 */
303
+	/**
304
+	 * debug
305
+	 *
306
+	 * @param string $class
307
+	 * @param string $func
308
+	 * @param string $line
309
+	 * @param array  $info
310
+	 * @param bool   $display_request
311
+	 * @param string $debug_index
312
+	 * @param string $debug_key
313
+	 * @throws EE_Error
314
+	 * @throws \EventEspresso\core\exceptions\InvalidSessionDataException
315
+	 */
316
+	public static function log(
317
+		$class = '',
318
+		$func = '',
319
+		$line = '',
320
+		$info = array(),
321
+		$display_request = false,
322
+		$debug_index = '',
323
+		$debug_key = 'EE_DEBUG_SPCO'
324
+	) {
325
+		if (WP_DEBUG) {
326
+			$debug_key = $debug_key . '_' . EE_Session::instance()->id();
327
+			$debug_data = get_option($debug_key, array());
328
+			$default_data = array(
329
+				$class => $func . '() : ' . $line,
330
+				'REQ'  => $display_request ? $_REQUEST : '',
331
+			);
332
+			// don't serialize objects
333
+			$info = self::strip_objects($info);
334
+			$index = ! empty($debug_index) ? $debug_index : 0;
335
+			if (! isset($debug_data[ $index ])) {
336
+				$debug_data[ $index ] = array();
337
+			}
338
+			$debug_data[ $index ][ microtime() ] = array_merge($default_data, $info);
339
+			update_option($debug_key, $debug_data);
340
+		}
341
+	}
342
+
343
+
344
+
345
+	/**
346
+	 * strip_objects
347
+	 *
348
+	 * @param array $info
349
+	 * @return array
350
+	 */
351
+	public static function strip_objects($info = array())
352
+	{
353
+		foreach ($info as $key => $value) {
354
+			if (is_array($value)) {
355
+				$info[ $key ] = self::strip_objects($value);
356
+			} elseif (is_object($value)) {
357
+				$object_class = get_class($value);
358
+				$info[ $object_class ] = array();
359
+				$info[ $object_class ]['ID'] = method_exists($value, 'ID') ? $value->ID() : spl_object_hash($value);
360
+				if (method_exists($value, 'ID')) {
361
+					$info[ $object_class ]['ID'] = $value->ID();
362
+				}
363
+				if (method_exists($value, 'status')) {
364
+					$info[ $object_class ]['status'] = $value->status();
365
+				} elseif (method_exists($value, 'status_ID')) {
366
+					$info[ $object_class ]['status'] = $value->status_ID();
367
+				}
368
+				unset($info[ $key ]);
369
+			}
370
+		}
371
+		return (array) $info;
372
+	}
373
+
374
+
375
+
376
+	/**
377
+	 * @param mixed      $var
378
+	 * @param string     $var_name
379
+	 * @param string     $file
380
+	 * @param int|string $line
381
+	 * @param int|string $heading_tag
382
+	 * @param bool       $die
383
+	 * @param string     $margin
384
+	 */
385
+	public static function printv(
386
+		$var,
387
+		$var_name = '',
388
+		$file = '',
389
+		$line = '',
390
+		$heading_tag = 5,
391
+		$die = false,
392
+		$margin = ''
393
+	) {
394
+		$var_name = ! $var_name ? 'string' : $var_name;
395
+		$var_name = ucwords(str_replace('$', '', $var_name));
396
+		$is_method = method_exists($var_name, $var);
397
+		$var_name = ucwords(str_replace('_', ' ', $var_name));
398
+		$heading_tag = EEH_Debug_Tools::headingTag($heading_tag);
399
+		// $result = EEH_Debug_Tools::headingSpacer($heading_tag);
400
+		$result = EEH_Debug_Tools::heading($var_name, $heading_tag, $margin, $line);
401
+		$result .= $is_method
402
+			? EEH_Debug_Tools::grey_span('::') . EEH_Debug_Tools::orange_span($var . '()')
403
+			: EEH_Debug_Tools::grey_span(' : ') . EEH_Debug_Tools::orange_span($var);
404
+		$result .= EEH_Debug_Tools::file_and_line($file, $line, $heading_tag);
405
+		$result .= EEH_Debug_Tools::headingX($heading_tag);
406
+		if ($die) {
407
+			die($result);
408
+		}
409
+		echo $result;
410
+	}
411
+
412
+
413
+	protected static function headingTag($heading_tag)
414
+	{
415
+		$heading_tag = absint($heading_tag);
416
+		return $heading_tag > 0 && $heading_tag < 7 ? "h{$heading_tag}" : 'h5';
417
+	}
418
+
419
+
420
+	protected static function headingSpacer($heading_tag)
421
+	{
422
+		return EEH_Debug_Tools::plainOutput() && ($heading_tag === 'h1' || $heading_tag === 'h2')
423
+			? "\n"
424
+			: '';
425
+	}
426
+
427
+
428
+	protected static function plainOutput()
429
+	{
430
+		return defined('EE_TESTS_DIR')
431
+			   || (defined('DOING_AJAX') && DOING_AJAX && ! isset($_REQUEST['pretty_output']))
432
+			   || (
433
+				   isset($_SERVER['REQUEST_URI'])
434
+				   && strpos(parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH), 'wp-json') !== false
435
+			   );
436
+	}
437
+
438
+
439
+	/**
440
+	 * @param string $var_name
441
+	 * @param string $heading_tag
442
+	 * @param string $margin
443
+	 * @param int    $line
444
+	 * @return string
445
+	 */
446
+	protected static function heading($var_name = '', $heading_tag = 'h5', $margin = '', $line = 0)
447
+	{
448
+		if (EEH_Debug_Tools::plainOutput()) {
449
+			switch ($heading_tag) {
450
+				case 'h1':
451
+					$line_breaks = EEH_Debug_Tools::lineBreak(3);
452
+					break;
453
+				case 'h2':
454
+					$line_breaks = EEH_Debug_Tools::lineBreak(2);
455
+					break;
456
+				default:
457
+					$line_breaks = EEH_Debug_Tools::lineBreak();
458
+					break;
459
+			}
460
+			return "{$line_breaks}{$line}) {$var_name}";
461
+		}
462
+		$margin = "25px 0 0 {$margin}";
463
+		return '<' . $heading_tag . ' style="color:#2EA2CC; margin:' . $margin . ';"><b>' . $var_name . '</b>';
464
+	}
465
+
466
+
467
+
468
+	/**
469
+	 * @param string $heading_tag
470
+	 * @return string
471
+	 */
472
+	protected static function headingX($heading_tag = 'h5')
473
+	{
474
+		if (EEH_Debug_Tools::plainOutput()) {
475
+			return '';
476
+		}
477
+		return '</' . $heading_tag . '>';
478
+	}
479
+
480
+
481
+
482
+	/**
483
+	 * @param string $content
484
+	 * @return string
485
+	 */
486
+	protected static function grey_span($content = '')
487
+	{
488
+		if (EEH_Debug_Tools::plainOutput()) {
489
+			return $content;
490
+		}
491
+		return '<span style="color:#999">' . $content . '</span>';
492
+	}
493
+
494
+
495
+
496
+	/**
497
+	 * @param string $file
498
+	 * @param int    $line
499
+	 * @return string
500
+	 */
501
+	protected static function file_and_line($file, $line, $heading_tag)
502
+	{
503
+		if ($file === '' || $line === '') {
504
+			return '';
505
+		}
506
+		$file = str_replace(EE_PLUGIN_DIR_PATH, '/', $file);
507
+		if (EEH_Debug_Tools::plainOutput()) {
508
+			if ($heading_tag === 'h1' || $heading_tag === 'h2') {
509
+				return " ({$file})" . EEH_Debug_Tools::lineBreak();
510
+			}
511
+			return '';
512
+		}
513
+		return EEH_Debug_Tools::lineBreak()
514
+			   . '<span style="font-size:9px;font-weight:normal;color:#666;line-height: 12px;">'
515
+			   . $file
516
+			   . EEH_Debug_Tools::lineBreak()
517
+			   . 'line no: '
518
+			   . $line
519
+			   . '</span>';
520
+	}
521
+
522
+
523
+
524
+	/**
525
+	 * @param string $content
526
+	 * @return string
527
+	 */
528
+	protected static function orange_span($content = '')
529
+	{
530
+		if (EEH_Debug_Tools::plainOutput()) {
531
+			return $content;
532
+		}
533
+		return '<span style="color:#E76700">' . $content . '</span>';
534
+	}
535
+
536
+
537
+
538
+	/**
539
+	 * @param mixed $var
540
+	 * @return string
541
+	 */
542
+	protected static function pre_span($var)
543
+	{
544
+		ob_start();
545
+		var_dump($var);
546
+		$var = ob_get_clean();
547
+		if (EEH_Debug_Tools::plainOutput()) {
548
+			return $var;
549
+		}
550
+		return '<pre style="color: #9C3; display: inline-block; padding:.4em .6em; background: #334">' . $var . '</pre>';
551
+	}
552
+
553
+
554
+
555
+	/**
556
+	 * @param mixed      $var
557
+	 * @param string     $var_name
558
+	 * @param string     $file
559
+	 * @param int|string $line
560
+	 * @param int|string $heading_tag
561
+	 * @param bool       $die
562
+	 */
563
+	public static function printr(
564
+		$var,
565
+		$var_name = '',
566
+		$file = '',
567
+		$line = '',
568
+		$heading_tag = 5,
569
+		$die = false
570
+	) {
571
+		// return;
572
+		$file = str_replace(rtrim(ABSPATH, '\\/'), '', $file);
573
+		if (empty($var) && empty($var_name)) {
574
+			$var = $file;
575
+			$var_name = "line $line";
576
+			$file = '';
577
+			$line = '';
578
+		}
579
+		$margin = is_admin() ? ' 180px' : '0';
580
+		// $print_r = false;
581
+		if (is_string($var)) {
582
+			EEH_Debug_Tools::printv($var, $var_name, $file, $line, $heading_tag, $die, $margin);
583
+			return;
584
+		}
585
+		if (is_object($var)) {
586
+			$var_name = ! $var_name ? 'object' : $var_name;
587
+			// $print_r = true;
588
+		} elseif (is_array($var)) {
589
+			$var_name = ! $var_name ? 'array' : $var_name;
590
+			// $print_r = true;
591
+		} elseif (is_numeric($var)) {
592
+			$var_name = ! $var_name ? 'numeric' : $var_name;
593
+		} elseif ($var === null) {
594
+			$var_name = ! $var_name ? 'null' : $var_name;
595
+		}
596
+		$var_name = ucwords(str_replace(array('$', '_'), array('', ' '), $var_name));
597
+		$heading_tag = EEH_Debug_Tools::headingTag($heading_tag);
598
+		// $result = EEH_Debug_Tools::headingSpacer($heading_tag);
599
+		$result = EEH_Debug_Tools::heading($var_name, $heading_tag, $margin, $line);
600
+		$result .= EEH_Debug_Tools::grey_span(' : ') . EEH_Debug_Tools::orange_span(
601
+			EEH_Debug_Tools::pre_span($var)
602
+		);
603
+		$result .= EEH_Debug_Tools::file_and_line($file, $line, $heading_tag);
604
+		$result .= EEH_Debug_Tools::headingX($heading_tag);
605
+		if ($die) {
606
+			die($result);
607
+		}
608
+		echo $result;
609
+	}
610
+
611
+
612
+	private static function lineBreak($lines = 1): string
613
+	{
614
+		$linebreak = defined('DOING_AJAX') && DOING_AJAX ? '<br />' : PHP_EOL;
615
+		return str_repeat($linebreak, $lines);
616
+	}
617
+
618
+
619
+	public static function shortClassName(string $fqcn): string
620
+	{
621
+		return substr(strrchr($fqcn, '\\'), 1);
622
+	}
623
+
624
+
625
+
626
+	/******************** deprecated ********************/
627
+
628
+
629
+
630
+	/**
631
+	 * @deprecated 4.9.39.rc.034
632
+	 */
633
+	public function reset_times()
634
+	{
635
+		Benchmark::resetTimes();
636
+	}
637
+
638
+
639
+
640
+	/**
641
+	 * @deprecated 4.9.39.rc.034
642
+	 * @param null $timer_name
643
+	 */
644
+	public function start_timer($timer_name = null)
645
+	{
646
+		Benchmark::startTimer($timer_name);
647
+	}
648
+
649
+
650
+
651
+	/**
652
+	 * @deprecated 4.9.39.rc.034
653
+	 * @param string $timer_name
654
+	 */
655
+	public function stop_timer($timer_name = '')
656
+	{
657
+		Benchmark::stopTimer($timer_name);
658
+	}
659
+
660
+
661
+
662
+	/**
663
+	 * @deprecated 4.9.39.rc.034
664
+	 * @param string  $label      The label to show for this time eg "Start of calling Some_Class::some_function"
665
+	 * @param boolean $output_now whether to echo now, or wait until EEH_Debug_Tools::show_times() is called
666
+	 * @return void
667
+	 */
668
+	public function measure_memory($label, $output_now = false)
669
+	{
670
+		Benchmark::measureMemory($label, $output_now);
671
+	}
672
+
673
+
674
+
675
+	/**
676
+	 * @deprecated 4.9.39.rc.034
677
+	 * @param int $size
678
+	 * @return string
679
+	 */
680
+	public function convert($size)
681
+	{
682
+		return Benchmark::convert($size);
683
+	}
684
+
685
+
686
+
687
+	/**
688
+	 * @deprecated 4.9.39.rc.034
689
+	 * @param bool $output_now
690
+	 * @return string
691
+	 */
692
+	public function show_times($output_now = true)
693
+	{
694
+		return Benchmark::displayResults($output_now);
695
+	}
696 696
 
697 697
 
698 698
 
699
-    /**
700
-     * @deprecated 4.9.39.rc.034
701
-     * @param string $timer_name
702
-     * @param float  $total_time
703
-     * @return string
704
-     */
705
-    public function format_time($timer_name, $total_time)
706
-    {
707
-        return Benchmark::formatTime($timer_name, $total_time);
708
-    }
699
+	/**
700
+	 * @deprecated 4.9.39.rc.034
701
+	 * @param string $timer_name
702
+	 * @param float  $total_time
703
+	 * @return string
704
+	 */
705
+	public function format_time($timer_name, $total_time)
706
+	{
707
+		return Benchmark::formatTime($timer_name, $total_time);
708
+	}
709 709
 }
710 710
 
711 711
 
@@ -715,31 +715,31 @@  discard block
 block discarded – undo
715 715
  * Plugin URI: http://upthemes.com/plugins/kint-debugger/
716 716
  */
717 717
 if (class_exists('Kint') && ! function_exists('dump_wp_query')) {
718
-    function dump_wp_query()
719
-    {
720
-        global $wp_query;
721
-        d($wp_query);
722
-    }
718
+	function dump_wp_query()
719
+	{
720
+		global $wp_query;
721
+		d($wp_query);
722
+	}
723 723
 }
724 724
 /**
725 725
  * borrowed from Kint Debugger
726 726
  * Plugin URI: http://upthemes.com/plugins/kint-debugger/
727 727
  */
728 728
 if (class_exists('Kint') && ! function_exists('dump_wp')) {
729
-    function dump_wp()
730
-    {
731
-        global $wp;
732
-        d($wp);
733
-    }
729
+	function dump_wp()
730
+	{
731
+		global $wp;
732
+		d($wp);
733
+	}
734 734
 }
735 735
 /**
736 736
  * borrowed from Kint Debugger
737 737
  * Plugin URI: http://upthemes.com/plugins/kint-debugger/
738 738
  */
739 739
 if (class_exists('Kint') && ! function_exists('dump_post')) {
740
-    function dump_post()
741
-    {
742
-        global $post;
743
-        d($post);
744
-    }
740
+	function dump_post()
741
+	{
742
+		global $post;
743
+		d($post);
744
+	}
745 745
 }
Please login to merge, or discard this patch.
Spacing   +40 added lines, -40 removed lines patch added patch discarded remove patch
@@ -36,7 +36,7 @@  discard block
 block discarded – undo
36 36
     public static function instance()
37 37
     {
38 38
         // check if class object is instantiated, and instantiated properly
39
-        if (! self::$_instance instanceof EEH_Debug_Tools) {
39
+        if ( ! self::$_instance instanceof EEH_Debug_Tools) {
40 40
             self::$_instance = new self();
41 41
         }
42 42
         return self::$_instance;
@@ -50,13 +50,13 @@  discard block
 block discarded – undo
50 50
     private function __construct()
51 51
     {
52 52
         // load Kint PHP debugging library
53
-        if (! class_exists('Kint') && file_exists(EE_PLUGIN_DIR_PATH . 'tests/kint/Kint.class.php')) {
53
+        if ( ! class_exists('Kint') && file_exists(EE_PLUGIN_DIR_PATH.'tests/kint/Kint.class.php')) {
54 54
             // despite EE4 having a check for an existing copy of the Kint debugging class,
55 55
             // if another plugin was loaded AFTER EE4 and they did NOT perform a similar check,
56 56
             // then hilarity would ensue as PHP throws a "Cannot redeclare class Kint" error
57 57
             // so we've moved it to our test folder so that it is not included with production releases
58 58
             // plz use https://wordpress.org/plugins/kint-debugger/  if testing production versions of EE
59
-            require_once(EE_PLUGIN_DIR_PATH . 'tests/kint/Kint.class.php');
59
+            require_once(EE_PLUGIN_DIR_PATH.'tests/kint/Kint.class.php');
60 60
         }
61 61
         // if ( ! defined('DOING_AJAX') || $_REQUEST['noheader'] !== 'true' || ! isset( $_REQUEST['noheader'], $_REQUEST['TB_iframe'] ) ) {
62 62
         // add_action( 'shutdown', array($this,'espresso_session_footer_dump') );
@@ -76,7 +76,7 @@  discard block
 block discarded – undo
76 76
      */
77 77
     public static function show_db_name()
78 78
     {
79
-        if (! defined('DOING_AJAX') && (defined('EE_ERROR_EMAILS') && EE_ERROR_EMAILS)) {
79
+        if ( ! defined('DOING_AJAX') && (defined('EE_ERROR_EMAILS') && EE_ERROR_EMAILS)) {
80 80
             echo '<p style="font-size:10px;font-weight:normal;color:#E76700;margin: 1em 2em; text-align: right;">DB_NAME: '
81 81
                  . DB_NAME
82 82
                  . '</p>';
@@ -126,12 +126,12 @@  discard block
 block discarded – undo
126 126
         global $wp_filter;
127 127
         echo '<br/><br/><br/><h3>Hooked Functions</h3>';
128 128
         if ($tag) {
129
-            $hook[ $tag ] = $wp_filter[ $tag ];
130
-            if (! is_array($hook[ $tag ])) {
129
+            $hook[$tag] = $wp_filter[$tag];
130
+            if ( ! is_array($hook[$tag])) {
131 131
                 trigger_error("Nothing found for '$tag' hook", E_USER_WARNING);
132 132
                 return;
133 133
             }
134
-            echo '<h5>For Tag: ' . $tag . '</h5>';
134
+            echo '<h5>For Tag: '.$tag.'</h5>';
135 135
         } else {
136 136
             $hook = is_array($wp_filter) ? $wp_filter : array($wp_filter);
137 137
             ksort($hook);
@@ -160,12 +160,12 @@  discard block
 block discarded – undo
160 160
     {
161 161
         $filters = array();
162 162
         global $wp_filter;
163
-        if (isset($wp_filter[ $hook_name ])) {
164
-            $filters[ $hook_name ] = array();
165
-            foreach ($wp_filter[ $hook_name ] as $priority => $callbacks) {
166
-                $filters[ $hook_name ][ $priority ] = array();
163
+        if (isset($wp_filter[$hook_name])) {
164
+            $filters[$hook_name] = array();
165
+            foreach ($wp_filter[$hook_name] as $priority => $callbacks) {
166
+                $filters[$hook_name][$priority] = array();
167 167
                 foreach ($callbacks as $callback) {
168
-                    $filters[ $hook_name ][ $priority ][] = $callback['function'];
168
+                    $filters[$hook_name][$priority][] = $callback['function'];
169 169
                 }
170 170
             }
171 171
         }
@@ -187,15 +187,15 @@  discard block
 block discarded – undo
187 187
             if (empty($activation_errors)) {
188 188
                 return;
189 189
             }
190
-            $activation_errors = date('Y-m-d H:i:s') . "\n" . $activation_errors;
191
-            espresso_load_required('EEH_File', EE_HELPERS . 'EEH_File.helper.php');
190
+            $activation_errors = date('Y-m-d H:i:s')."\n".$activation_errors;
191
+            espresso_load_required('EEH_File', EE_HELPERS.'EEH_File.helper.php');
192 192
             if (class_exists('EEH_File')) {
193 193
                 try {
194 194
                     EEH_File::ensure_file_exists_and_is_writable(
195
-                        EVENT_ESPRESSO_UPLOAD_DIR . 'logs/espresso_plugin_activation_errors.html'
195
+                        EVENT_ESPRESSO_UPLOAD_DIR.'logs/espresso_plugin_activation_errors.html'
196 196
                     );
197 197
                     EEH_File::write_to_file(
198
-                        EVENT_ESPRESSO_UPLOAD_DIR . 'logs/espresso_plugin_activation_errors.html',
198
+                        EVENT_ESPRESSO_UPLOAD_DIR.'logs/espresso_plugin_activation_errors.html',
199 199
                         $activation_errors
200 200
                     );
201 201
                 } catch (EE_Error $e) {
@@ -215,11 +215,11 @@  discard block
 block discarded – undo
215 215
             } else {
216 216
                 // old school attempt
217 217
                 file_put_contents(
218
-                    EVENT_ESPRESSO_UPLOAD_DIR . 'logs/espresso_plugin_activation_errors.html',
218
+                    EVENT_ESPRESSO_UPLOAD_DIR.'logs/espresso_plugin_activation_errors.html',
219 219
                     $activation_errors
220 220
                 );
221 221
             }
222
-            $activation_errors = get_option('ee_plugin_activation_errors', '') . $activation_errors;
222
+            $activation_errors = get_option('ee_plugin_activation_errors', '').$activation_errors;
223 223
             update_option('ee_plugin_activation_errors', $activation_errors);
224 224
         }
225 225
     }
@@ -279,7 +279,7 @@  discard block
 block discarded – undo
279 279
         // don't trigger error if doing ajax,
280 280
         // instead we'll add a transient EE_Error notice that in theory should show on the next request.
281 281
         if (defined('DOING_AJAX') && DOING_AJAX) {
282
-            $error_message .= ' ' . esc_html__(
282
+            $error_message .= ' '.esc_html__(
283 283
                 'This is a doing_it_wrong message that was triggered during an ajax request.  The request params on this request were: ',
284 284
                 'event_espresso'
285 285
             );
@@ -323,19 +323,19 @@  discard block
 block discarded – undo
323 323
         $debug_key = 'EE_DEBUG_SPCO'
324 324
     ) {
325 325
         if (WP_DEBUG) {
326
-            $debug_key = $debug_key . '_' . EE_Session::instance()->id();
326
+            $debug_key = $debug_key.'_'.EE_Session::instance()->id();
327 327
             $debug_data = get_option($debug_key, array());
328 328
             $default_data = array(
329
-                $class => $func . '() : ' . $line,
329
+                $class => $func.'() : '.$line,
330 330
                 'REQ'  => $display_request ? $_REQUEST : '',
331 331
             );
332 332
             // don't serialize objects
333 333
             $info = self::strip_objects($info);
334 334
             $index = ! empty($debug_index) ? $debug_index : 0;
335
-            if (! isset($debug_data[ $index ])) {
336
-                $debug_data[ $index ] = array();
335
+            if ( ! isset($debug_data[$index])) {
336
+                $debug_data[$index] = array();
337 337
             }
338
-            $debug_data[ $index ][ microtime() ] = array_merge($default_data, $info);
338
+            $debug_data[$index][microtime()] = array_merge($default_data, $info);
339 339
             update_option($debug_key, $debug_data);
340 340
         }
341 341
     }
@@ -352,20 +352,20 @@  discard block
 block discarded – undo
352 352
     {
353 353
         foreach ($info as $key => $value) {
354 354
             if (is_array($value)) {
355
-                $info[ $key ] = self::strip_objects($value);
355
+                $info[$key] = self::strip_objects($value);
356 356
             } elseif (is_object($value)) {
357 357
                 $object_class = get_class($value);
358
-                $info[ $object_class ] = array();
359
-                $info[ $object_class ]['ID'] = method_exists($value, 'ID') ? $value->ID() : spl_object_hash($value);
358
+                $info[$object_class] = array();
359
+                $info[$object_class]['ID'] = method_exists($value, 'ID') ? $value->ID() : spl_object_hash($value);
360 360
                 if (method_exists($value, 'ID')) {
361
-                    $info[ $object_class ]['ID'] = $value->ID();
361
+                    $info[$object_class]['ID'] = $value->ID();
362 362
                 }
363 363
                 if (method_exists($value, 'status')) {
364
-                    $info[ $object_class ]['status'] = $value->status();
364
+                    $info[$object_class]['status'] = $value->status();
365 365
                 } elseif (method_exists($value, 'status_ID')) {
366
-                    $info[ $object_class ]['status'] = $value->status_ID();
366
+                    $info[$object_class]['status'] = $value->status_ID();
367 367
                 }
368
-                unset($info[ $key ]);
368
+                unset($info[$key]);
369 369
             }
370 370
         }
371 371
         return (array) $info;
@@ -399,8 +399,8 @@  discard block
 block discarded – undo
399 399
         // $result = EEH_Debug_Tools::headingSpacer($heading_tag);
400 400
         $result = EEH_Debug_Tools::heading($var_name, $heading_tag, $margin, $line);
401 401
         $result .= $is_method
402
-            ? EEH_Debug_Tools::grey_span('::') . EEH_Debug_Tools::orange_span($var . '()')
403
-            : EEH_Debug_Tools::grey_span(' : ') . EEH_Debug_Tools::orange_span($var);
402
+            ? EEH_Debug_Tools::grey_span('::').EEH_Debug_Tools::orange_span($var.'()')
403
+            : EEH_Debug_Tools::grey_span(' : ').EEH_Debug_Tools::orange_span($var);
404 404
         $result .= EEH_Debug_Tools::file_and_line($file, $line, $heading_tag);
405 405
         $result .= EEH_Debug_Tools::headingX($heading_tag);
406 406
         if ($die) {
@@ -460,7 +460,7 @@  discard block
 block discarded – undo
460 460
             return "{$line_breaks}{$line}) {$var_name}";
461 461
         }
462 462
         $margin = "25px 0 0 {$margin}";
463
-        return '<' . $heading_tag . ' style="color:#2EA2CC; margin:' . $margin . ';"><b>' . $var_name . '</b>';
463
+        return '<'.$heading_tag.' style="color:#2EA2CC; margin:'.$margin.';"><b>'.$var_name.'</b>';
464 464
     }
465 465
 
466 466
 
@@ -474,7 +474,7 @@  discard block
 block discarded – undo
474 474
         if (EEH_Debug_Tools::plainOutput()) {
475 475
             return '';
476 476
         }
477
-        return '</' . $heading_tag . '>';
477
+        return '</'.$heading_tag.'>';
478 478
     }
479 479
 
480 480
 
@@ -488,7 +488,7 @@  discard block
 block discarded – undo
488 488
         if (EEH_Debug_Tools::plainOutput()) {
489 489
             return $content;
490 490
         }
491
-        return '<span style="color:#999">' . $content . '</span>';
491
+        return '<span style="color:#999">'.$content.'</span>';
492 492
     }
493 493
 
494 494
 
@@ -506,7 +506,7 @@  discard block
 block discarded – undo
506 506
         $file = str_replace(EE_PLUGIN_DIR_PATH, '/', $file);
507 507
         if (EEH_Debug_Tools::plainOutput()) {
508 508
             if ($heading_tag === 'h1' || $heading_tag === 'h2') {
509
-                return " ({$file})" . EEH_Debug_Tools::lineBreak();
509
+                return " ({$file})".EEH_Debug_Tools::lineBreak();
510 510
             }
511 511
             return '';
512 512
         }
@@ -530,7 +530,7 @@  discard block
 block discarded – undo
530 530
         if (EEH_Debug_Tools::plainOutput()) {
531 531
             return $content;
532 532
         }
533
-        return '<span style="color:#E76700">' . $content . '</span>';
533
+        return '<span style="color:#E76700">'.$content.'</span>';
534 534
     }
535 535
 
536 536
 
@@ -547,7 +547,7 @@  discard block
 block discarded – undo
547 547
         if (EEH_Debug_Tools::plainOutput()) {
548 548
             return $var;
549 549
         }
550
-        return '<pre style="color: #9C3; display: inline-block; padding:.4em .6em; background: #334">' . $var . '</pre>';
550
+        return '<pre style="color: #9C3; display: inline-block; padding:.4em .6em; background: #334">'.$var.'</pre>';
551 551
     }
552 552
 
553 553
 
@@ -597,7 +597,7 @@  discard block
 block discarded – undo
597 597
         $heading_tag = EEH_Debug_Tools::headingTag($heading_tag);
598 598
         // $result = EEH_Debug_Tools::headingSpacer($heading_tag);
599 599
         $result = EEH_Debug_Tools::heading($var_name, $heading_tag, $margin, $line);
600
-        $result .= EEH_Debug_Tools::grey_span(' : ') . EEH_Debug_Tools::orange_span(
600
+        $result .= EEH_Debug_Tools::grey_span(' : ').EEH_Debug_Tools::orange_span(
601 601
             EEH_Debug_Tools::pre_span($var)
602 602
         );
603 603
         $result .= EEH_Debug_Tools::file_and_line($file, $line, $heading_tag);
Please login to merge, or discard this patch.
core/services/payment_methods/gateways/GatewayDataFormatterInterface.php 1 patch
Indentation   +45 added lines, -45 removed lines patch added patch discarded remove patch
@@ -16,64 +16,64 @@
 block discarded – undo
16 16
 interface GatewayDataFormatterInterface
17 17
 {
18 18
 
19
-    /**
20
-     * Gets the text to use for a gateway's line item name when this is a partial payment
21
-     *
22
-     * @param EEI_Payment $payment
23
-     * @return string
24
-     */
25
-    public function formatPartialPaymentLineItemName(EEI_Payment $payment): string;
19
+	/**
20
+	 * Gets the text to use for a gateway's line item name when this is a partial payment
21
+	 *
22
+	 * @param EEI_Payment $payment
23
+	 * @return string
24
+	 */
25
+	public function formatPartialPaymentLineItemName(EEI_Payment $payment): string;
26 26
 
27 27
 
28 28
 
29
-    /**
30
-     * Gets the text to use for a gateway's line item description when this is a partial payment
31
-     *
32
-     * @param EEI_Payment $payment
33
-     * @return string
34
-     */
35
-    public function formatPartialPaymentLineItemDesc(EEI_Payment $payment): string;
29
+	/**
30
+	 * Gets the text to use for a gateway's line item description when this is a partial payment
31
+	 *
32
+	 * @param EEI_Payment $payment
33
+	 * @return string
34
+	 */
35
+	public function formatPartialPaymentLineItemDesc(EEI_Payment $payment): string;
36 36
 
37 37
 
38 38
 
39
-    /**
40
-     * Gets the name to use for a line item when sending line items to the gateway
41
-     *
42
-     * @param EE_Line_Item $line_item
43
-     * @param EEI_Payment   $payment
44
-     * @return string
45
-     */
46
-    public function formatLineItemName(EE_Line_Item $line_item, EEI_Payment $payment): string;
39
+	/**
40
+	 * Gets the name to use for a line item when sending line items to the gateway
41
+	 *
42
+	 * @param EE_Line_Item $line_item
43
+	 * @param EEI_Payment   $payment
44
+	 * @return string
45
+	 */
46
+	public function formatLineItemName(EE_Line_Item $line_item, EEI_Payment $payment): string;
47 47
 
48 48
 
49 49
 
50
-    /**
51
-     * Gets the description to use for a line item when sending line items to the gateway
52
-     *
53
-     * @param EE_Line_Item $line_item
54
-     * @param EEI_Payment   $payment
55
-     * @return string
56
-     */
57
-    public function formatLineItemDesc(EE_Line_Item $line_item, EEI_Payment $payment): string;
50
+	/**
51
+	 * Gets the description to use for a line item when sending line items to the gateway
52
+	 *
53
+	 * @param EE_Line_Item $line_item
54
+	 * @param EEI_Payment   $payment
55
+	 * @return string
56
+	 */
57
+	public function formatLineItemDesc(EE_Line_Item $line_item, EEI_Payment $payment): string;
58 58
 
59 59
 
60 60
 
61
-    /**
62
-     * Gets the order description that should generally be sent to gateways
63
-     *
64
-     * @param EEI_Payment $payment
65
-     * @return string
66
-     */
67
-    public function formatOrderDescription(EEI_Payment $payment): string;
61
+	/**
62
+	 * Gets the order description that should generally be sent to gateways
63
+	 *
64
+	 * @param EEI_Payment $payment
65
+	 * @return string
66
+	 */
67
+	public function formatOrderDescription(EEI_Payment $payment): string;
68 68
 
69 69
 
70 70
 
71
-    /**
72
-     * Formats the amount so it can generally be sent to gateways
73
-     *
74
-     * @param float $amount
75
-     * @param int   $precision
76
-     * @return string
77
-     */
78
-    public function formatCurrency(float $amount, int $precision = 2): string;
71
+	/**
72
+	 * Formats the amount so it can generally be sent to gateways
73
+	 *
74
+	 * @param float $amount
75
+	 * @param int   $precision
76
+	 * @return string
77
+	 */
78
+	public function formatCurrency(float $amount, int $precision = 2): string;
79 79
 }
Please login to merge, or discard this patch.
core/services/payment_methods/gateways/GatewayDataFormatter.php 1 patch
Indentation   +107 added lines, -107 removed lines patch added patch discarded remove patch
@@ -19,121 +19,121 @@
 block discarded – undo
19 19
 class GatewayDataFormatter implements GatewayDataFormatterInterface
20 20
 {
21 21
 
22
-    /**
23
-     * Gets the text to use for a gateway's line item name when this is a partial payment
24
-     *
25
-     * @param EEI_Payment $payment
26
-     * @return string
27
-     */
28
-    public function formatPartialPaymentLineItemName(EEI_Payment $payment): string
29
-    {
30
-        return apply_filters(
31
-            'EEG_Paypal_Pro__do_direct_payment__partial_amount_line_item_name',
32
-            $payment->get_first_event_name(),
33
-            $this,
34
-            $payment
35
-        );
36
-    }
22
+	/**
23
+	 * Gets the text to use for a gateway's line item name when this is a partial payment
24
+	 *
25
+	 * @param EEI_Payment $payment
26
+	 * @return string
27
+	 */
28
+	public function formatPartialPaymentLineItemName(EEI_Payment $payment): string
29
+	{
30
+		return apply_filters(
31
+			'EEG_Paypal_Pro__do_direct_payment__partial_amount_line_item_name',
32
+			$payment->get_first_event_name(),
33
+			$this,
34
+			$payment
35
+		);
36
+	}
37 37
 
38 38
 
39
-    /**
40
-     * Gets the text to use for a gateway's line item description when this is a partial payment
41
-     *
42
-     * @param EEI_Payment $payment
43
-     * @return string
44
-     * @throws EE_Error
45
-     */
46
-    public function formatPartialPaymentLineItemDesc(EEI_Payment $payment): string
47
-    {
48
-        return apply_filters(
49
-            'FHEE__EE_Gateway___partial_payment_desc',
50
-            sprintf(
51
-                __('Payment of %1$s for %2$s', "event_espresso"),
52
-                $payment->get_pretty('PAY_amount', 'no_currency_code'),
53
-                $payment->get_first_event_name()
54
-            ),
55
-            $this,
56
-            $payment
57
-        );
58
-    }
39
+	/**
40
+	 * Gets the text to use for a gateway's line item description when this is a partial payment
41
+	 *
42
+	 * @param EEI_Payment $payment
43
+	 * @return string
44
+	 * @throws EE_Error
45
+	 */
46
+	public function formatPartialPaymentLineItemDesc(EEI_Payment $payment): string
47
+	{
48
+		return apply_filters(
49
+			'FHEE__EE_Gateway___partial_payment_desc',
50
+			sprintf(
51
+				__('Payment of %1$s for %2$s', "event_espresso"),
52
+				$payment->get_pretty('PAY_amount', 'no_currency_code'),
53
+				$payment->get_first_event_name()
54
+			),
55
+			$this,
56
+			$payment
57
+		);
58
+	}
59 59
 
60 60
 
61
-    /**
62
-     * Gets the name to use for a line item when sending line items to the gateway
63
-     *
64
-     * @param EE_Line_Item $line_item
65
-     * @param EEI_Payment  $payment
66
-     * @return string
67
-     * @throws EE_Error
68
-     * @throws ReflectionException
69
-     */
70
-    public function formatLineItemName(EE_Line_Item $line_item, EEI_Payment $payment): string
71
-    {
72
-        return apply_filters(
73
-            'FHEE__EE_gateway___line_item_name',
74
-            sprintf(
75
-                _x('%1$s for %2$s', 'Ticket for Event', 'event_espresso'),
76
-                $line_item->name(),
77
-                $line_item->ticket_event_name()
78
-            ),
79
-            $this,
80
-            $line_item,
81
-            $payment
82
-        );
83
-    }
61
+	/**
62
+	 * Gets the name to use for a line item when sending line items to the gateway
63
+	 *
64
+	 * @param EE_Line_Item $line_item
65
+	 * @param EEI_Payment  $payment
66
+	 * @return string
67
+	 * @throws EE_Error
68
+	 * @throws ReflectionException
69
+	 */
70
+	public function formatLineItemName(EE_Line_Item $line_item, EEI_Payment $payment): string
71
+	{
72
+		return apply_filters(
73
+			'FHEE__EE_gateway___line_item_name',
74
+			sprintf(
75
+				_x('%1$s for %2$s', 'Ticket for Event', 'event_espresso'),
76
+				$line_item->name(),
77
+				$line_item->ticket_event_name()
78
+			),
79
+			$this,
80
+			$line_item,
81
+			$payment
82
+		);
83
+	}
84 84
 
85 85
 
86
-    /**
87
-     * Gets the description to use for a line item when sending line items to the gateway
88
-     *
89
-     * @param EE_Line_Item $line_item
90
-     * @param EEI_Payment  $payment
91
-     * @return string
92
-     * @throws EE_Error
93
-     * @throws ReflectionException
94
-     */
95
-    public function formatLineItemDesc(EE_Line_Item $line_item, EEI_Payment $payment): string
96
-    {
97
-        return apply_filters(
98
-            'FHEE__EE_Gateway___line_item_desc',
99
-            $line_item->desc(),
100
-            $this,
101
-            $line_item,
102
-            $payment
103
-        );
104
-    }
86
+	/**
87
+	 * Gets the description to use for a line item when sending line items to the gateway
88
+	 *
89
+	 * @param EE_Line_Item $line_item
90
+	 * @param EEI_Payment  $payment
91
+	 * @return string
92
+	 * @throws EE_Error
93
+	 * @throws ReflectionException
94
+	 */
95
+	public function formatLineItemDesc(EE_Line_Item $line_item, EEI_Payment $payment): string
96
+	{
97
+		return apply_filters(
98
+			'FHEE__EE_Gateway___line_item_desc',
99
+			$line_item->desc(),
100
+			$this,
101
+			$line_item,
102
+			$payment
103
+		);
104
+	}
105 105
 
106 106
 
107
-    /**
108
-     * Gets the order description that should generally be sent to gateways
109
-     *
110
-     * @param EEI_Payment $payment
111
-     * @return string
112
-     */
113
-    public function formatOrderDescription(EEI_Payment $payment): string
114
-    {
115
-        return apply_filters(
116
-            'FHEE__EE_Gateway___order_description',
117
-            sprintf(
118
-                __('Event Registrations from %1$s for %2$s', "event_espresso"),
119
-                wp_specialchars_decode(get_bloginfo(), ENT_QUOTES),
120
-                $payment->get_first_event_name()
121
-            ),
122
-            $this,
123
-            $payment
124
-        );
125
-    }
107
+	/**
108
+	 * Gets the order description that should generally be sent to gateways
109
+	 *
110
+	 * @param EEI_Payment $payment
111
+	 * @return string
112
+	 */
113
+	public function formatOrderDescription(EEI_Payment $payment): string
114
+	{
115
+		return apply_filters(
116
+			'FHEE__EE_Gateway___order_description',
117
+			sprintf(
118
+				__('Event Registrations from %1$s for %2$s', "event_espresso"),
119
+				wp_specialchars_decode(get_bloginfo(), ENT_QUOTES),
120
+				$payment->get_first_event_name()
121
+			),
122
+			$this,
123
+			$payment
124
+		);
125
+	}
126 126
 
127 127
 
128
-    /**
129
-     * Formats the amount so it can generally be sent to gateways
130
-     *
131
-     * @param float $amount
132
-     * @param int $precision
133
-     * @return string
134
-     */
135
-    public function formatCurrency(float $amount, int $precision = 2): string
136
-    {
137
-        return number_format($amount, $precision, '.', '');
138
-    }
128
+	/**
129
+	 * Formats the amount so it can generally be sent to gateways
130
+	 *
131
+	 * @param float $amount
132
+	 * @param int $precision
133
+	 * @return string
134
+	 */
135
+	public function formatCurrency(float $amount, int $precision = 2): string
136
+	{
137
+		return number_format($amount, $precision, '.', '');
138
+	}
139 139
 }
Please login to merge, or discard this patch.
core/services/calculators/LineItemCalculator.php 2 patches
Indentation   +719 added lines, -719 removed lines patch added patch discarded remove patch
@@ -21,723 +21,723 @@
 block discarded – undo
21 21
 class LineItemCalculator
22 22
 {
23 23
 
24
-    /**
25
-     * @var DecimalValues
26
-     */
27
-    protected $decimal_values;
28
-
29
-    /**
30
-     * @var array
31
-     */
32
-    protected $default_query_params = [
33
-        ['LIN_type' => ['!=', EEM_Line_Item::type_cancellation]]
34
-    ];
35
-
36
-
37
-    /**
38
-     * @param DecimalValues $decimal_values
39
-     */
40
-    public function __construct(DecimalValues $decimal_values)
41
-    {
42
-        $this->decimal_values = $decimal_values;
43
-    }
44
-
45
-
46
-    /**
47
-     * Gets the final total on this item, taking taxes into account.
48
-     * Has the side-effect of setting the sub-total as it was just calculated.
49
-     * If this is used on a grand-total line item, also updates the transaction's
50
-     * TXN_total (provided this line item is allowed to persist, otherwise we don't
51
-     * want to change a persistable transaction with info from a non-persistent line item)
52
-     *
53
-     * @param EE_Line_Item $line_item
54
-     * @param bool         $update_txn_status
55
-     * @return float
56
-     * @throws EE_Error
57
-     * @throws ReflectionException
58
-     */
59
-    public function recalculateTotalIncludingTaxes(EE_Line_Item $line_item, bool $update_txn_status = false): float
60
-    {
61
-        $this->validateLineItemAndType($line_item, EEM_Line_Item::type_total);
62
-        $ticket_line_items = EEH_Line_Item::get_ticket_line_items($line_item);
63
-        if (empty($ticket_line_items)) {
64
-            return 0;
65
-        }
66
-        [, $pretax_total] = $this->recalculateLineItemTotals($line_item);
67
-        // EEH_Line_Item::visualize($line_item);
68
-        $total_tax = $this->recalculateTaxesAndTaxTotal($line_item);
69
-        // no negative totals plz
70
-        $grand_total  = max($pretax_total + $total_tax, 0);
71
-        $this->updatePreTaxTotal($line_item, $pretax_total, true);
72
-        $grand_total  = $this->updateTotal($line_item, $grand_total, true);
73
-        $this->updateTransaction($line_item, $grand_total, $update_txn_status);
74
-        return $grand_total;
75
-    }
76
-
77
-
78
-    /**
79
-     * Recursively goes through all the children and recalculates sub-totals EXCEPT for
80
-     * tax-sub-totals (they're a an odd beast). Updates the 'total' on each line item according to either its
81
-     * unit price * quantity or the total of all its children EXCEPT when we're only calculating the taxable total and
82
-     * when this is called on the grand total
83
-     *
84
-     * @param EE_Line_Item $line_item
85
-     * @param float        $total
86
-     * @param float        $pretax_total
87
-     * @return array
88
-     * @throws EE_Error
89
-     * @throws ReflectionException
90
-     */
91
-    public function recalculateLineItemTotals(
92
-        EE_Line_Item $line_item,
93
-        float $total = 0,
94
-        float $pretax_total = 0
95
-    ): array {
96
-        switch ($line_item->type()) {
97
-            case EEM_Line_Item::type_total:
98
-            case EEM_Line_Item::type_sub_total:
99
-                [$total, $pretax_total] = $this->recalculateSubTotal($line_item);
100
-                break;
101
-
102
-            case EEM_Line_Item::type_line_item:
103
-                [$total, $pretax_total] = $this->recalculateLineItem($line_item, $total, $pretax_total);
104
-                break;
105
-
106
-            case EEM_Line_Item::type_sub_line_item:
107
-                // sub line items operate on the total and update both the total AND the pre-tax total
108
-                [$total, $pretax_total] = $this->recalculateSubLineItem($line_item, $total, $pretax_total);
109
-                break;
110
-
111
-            case EEM_Line_Item::type_sub_tax:
112
-                // sub line item taxes ONLY operate on the pre-tax total and ONLY update the total
113
-                [$total, $pretax_total] = $this->recalculateSubTax($line_item, $pretax_total);
114
-                break;
115
-
116
-            case EEM_Line_Item::type_tax_sub_total:
117
-            case EEM_Line_Item::type_tax:
118
-            case EEM_Line_Item::type_cancellation:
119
-                // completely ignore tax totals, tax sub-totals, and cancelled line items
120
-                // when calculating the pre-tax-total
121
-                $total = $pretax_total = 0;
122
-                break;
123
-        }
124
-        return [$total, $pretax_total];
125
-    }
126
-
127
-
128
-    /**
129
-     * @param EE_Line_Item $line_item
130
-     * @return array
131
-     * @throws EE_Error
132
-     * @throws ReflectionException
133
-     */
134
-    private function recalculateSubTotal(EE_Line_Item $line_item): array
135
-    {
136
-        // reset the total and pretax total to zero since we are recalculating them
137
-        $total = $pretax_total = 0;
138
-        if ($line_item->is_total()) {
139
-            // if this is the grand total line item
140
-            // then first update ALL of the line item quantities (if need be)
141
-            $this->updateLineItemQuantities($line_item);
142
-        }
143
-        // recursively loop through children and recalculate their totals
144
-        $children = $line_item->children($this->default_query_params);
145
-        if (empty($children)) {
146
-            return [$total, $pretax_total];
147
-        }
148
-        foreach ($children as $child_line_item) {
149
-            [$child_total, $child_pretax_total] = $this->recalculateLineItemTotals(
150
-                $child_line_item,
151
-                $total,
152
-                $pretax_total
153
-            );
154
-            $total += $child_total;
155
-            $pretax_total += $child_pretax_total;
156
-        }
157
-        // update the unit price and pretax total
158
-        $this->updateUnitPrice($line_item, $pretax_total);
159
-        $pretax_total = $this->updatePreTaxTotal($line_item, $pretax_total, true);
160
-        // for the actual pre-tax sub total line item, we want to save the pretax value for everything
161
-        if ($line_item->is_sub_total() && $line_item->name() === esc_html__('Pre-Tax Subtotal', 'event_espresso')) {
162
-            $this->updateTotal($line_item, $pretax_total, true);
163
-        } elseif (! $line_item->is_total()) {
164
-            // we don't update the total for the total line item, because that will need to include taxes
165
-            $total = $this->updateTotal($line_item, $total, true);
166
-        }
167
-        return [$total, $pretax_total];
168
-    }
169
-
170
-
171
-    /**
172
-     * @param EE_Line_Item $line_item
173
-     * @param float        $total
174
-     * @param float        $pretax_total
175
-     * @return array
176
-     * @throws EE_Error
177
-     * @throws ReflectionException
178
-     */
179
-    private function recalculateLineItem(
180
-        EE_Line_Item $line_item,
181
-        float $total = 0,
182
-        float $pretax_total = 0
183
-    ): array {
184
-        if ($line_item->is_percent()) {
185
-            $total = $this->calculatePercentage($total, $line_item->percent());
186
-            $pretax_total = $this->calculatePercentage($pretax_total, $line_item->percent());
187
-        } else {
188
-            // recursively loop through children and recalculate their totals
189
-            $children = $line_item->children($this->default_query_params);
190
-            if (! empty($children)) {
191
-                // reset the total and pretax total to zero since we are recalculating them
192
-                $total = $pretax_total = 0;
193
-                foreach ($children as $child_line_item) {
194
-                    [$child_total, $child_pretax_total] = $this->recalculateLineItemTotals(
195
-                        $child_line_item,
196
-                        $total,
197
-                        $pretax_total
198
-                    );
199
-                    $total        += $child_total;
200
-                    $pretax_total += $child_pretax_total;
201
-                }
202
-            } else {
203
-                // no child line items, so recalculate the total from the unit price and quantity
204
-                // and set the pretax total to match since their are obviously no sub-taxes
205
-                $pretax_total = $total = $this->calculateTotalForQuantity($line_item);
206
-            }
207
-        }
208
-        $total  = $this->updateTotal($line_item, $total, true);
209
-        $pretax_total = $this->updatePreTaxTotal($line_item, $pretax_total, true);
210
-
211
-        // need to also adjust unit price too if the pretax total or quantity has been updated
212
-        $this->updateUnitPrice($line_item, $pretax_total);
213
-        return [$total, $pretax_total];
214
-    }
215
-
216
-
217
-    /**
218
-     * @param EE_Line_Item $sub_line_item
219
-     * @param float|int    $total
220
-     * @param float|int    $pretax_total
221
-     * @return float[]
222
-     * @throws EE_Error
223
-     * @throws ReflectionException
224
-     */
225
-    private function recalculateSubLineItem(EE_Line_Item $sub_line_item, float $total = 0, float $pretax_total = 0): array
226
-    {
227
-        if ($sub_line_item->is_percent()) {
228
-            $new_total = $this->calculatePercentage($total, $sub_line_item->percent());
229
-            $new_pretax_total = $this->calculatePercentage($pretax_total, $sub_line_item->percent());
230
-        } else {
231
-            $new_total = $new_pretax_total = $this->calculateTotalForQuantity($sub_line_item);
232
-        }
233
-        $total = $this->updateTotal($sub_line_item, $new_total);
234
-        $pretax_total = $this->updatePreTaxTotal($sub_line_item, $new_pretax_total);
235
-        // need to also adjust unit price too if the pretax total or quantity has been updated
236
-        $this->updateUnitPrice($sub_line_item, $pretax_total);
237
-        return [$total, $pretax_total];
238
-    }
239
-
240
-
241
-    /**
242
-     * @param EE_Line_Item $sub_line_item
243
-     * @param float|int    $pretax_total
244
-     * @return float[]
245
-     * @throws EE_Error
246
-     * @throws ReflectionException
247
-     */
248
-    private function recalculateSubTax(EE_Line_Item $sub_line_item, float $pretax_total = 0): array
249
-    {
250
-        $total_tax = $this->calculatePercentage($pretax_total, $sub_line_item->percent());
251
-        $total_tax = $this->updateTotal($sub_line_item, $total_tax);
252
-        return [$total_tax, 0];
253
-    }
254
-
255
-
256
-    /**
257
-     * recursively loops through the entire line item tree updating line item quantities accordingly.
258
-     * this needs to be done prior to running any other calculations for reasons that are hopefully obvious :p
259
-     *
260
-     * @param EE_Line_Item $line_item
261
-     * @param int          $quantity
262
-     * @return int
263
-     * @throws EE_Error
264
-     * @throws ReflectionException
265
-     */
266
-    private function updateLineItemQuantities(EE_Line_Item $line_item, int $quantity = 1): int
267
-    {
268
-        switch ($line_item->type()) {
269
-            case EEM_Line_Item::type_total:
270
-            case EEM_Line_Item::type_sub_total:
271
-            case EEM_Line_Item::type_tax_sub_total:
272
-                // first, loop through children and set their quantities
273
-                $count = 0;
274
-                $children = $line_item->children($this->default_query_params);
275
-                foreach ($children as $child_line_item) {
276
-                    $count += $this->updateLineItemQuantities($child_line_item);
277
-                }
278
-                // totals and subtotals should have a quantity of 1
279
-                // unless their children have all been removed, in which case we can set them to 0
280
-                $quantity = $count > 0 ? 1 : 0;
281
-                $this->updateQuantity($line_item, $quantity);
282
-                return $quantity;
283
-
284
-            case EEM_Line_Item::type_line_item:
285
-                // line items should ALREADY have accurate quantities set, if not, then somebody done goofed!
286
-                // but if this is a percentage based line item, then ensure its quantity is 1
287
-                if ($line_item->is_percent()) {
288
-                    $this->updateQuantity($line_item, 1);
289
-                }
290
-                // and we also need to loop through all of the sub items and ensure those quantities match this parent.
291
-                $children = $line_item->children($this->default_query_params);
292
-                $quantity = $line_item->quantity();
293
-                foreach ($children as $child_line_item) {
294
-                    $this->updateLineItemQuantities($child_line_item, $quantity);
295
-                }
296
-                // percentage line items should not increment their parent's count, so they return 0
297
-                return ! $line_item->is_percent() ? $quantity : 0;
298
-
299
-            case EEM_Line_Item::type_sub_line_item:
300
-                // percentage based items need their quantity set to 1,
301
-                // all others use the incoming value from the parent line item
302
-                $quantity = $line_item->is_percent() ? 1 : $quantity;
303
-                $this->updateQuantity($line_item, $quantity);
304
-                // percentage line items should not increment their parent's count, so they return 0
305
-                return ! $line_item->is_percent() ? $quantity : 0;
306
-
307
-            case EEM_Line_Item::type_tax:
308
-            case EEM_Line_Item::type_sub_tax:
309
-                // taxes should have a quantity of 1
310
-                $this->updateQuantity($line_item, 1);
311
-                return 1;
312
-
313
-            case EEM_Line_Item::type_cancellation:
314
-                // cancellations will be ignored for all calculations
315
-                // because their parent quantities should have already been adjusted when they were added
316
-                // so assume that things are already set correctly
317
-                return 0;
318
-        }
319
-        return 0;
320
-    }
321
-
322
-
323
-    /**
324
-     * @param float $total
325
-     * @param float $percent
326
-     * @param bool  $round
327
-     * @return float
328
-     */
329
-    private function calculatePercentage(float $total, float $percent, bool $round = false): float
330
-    {
331
-        $amount = $total * $percent / 100;
332
-        return $this->decimal_values->roundDecimalValue($amount, $round);
333
-    }
334
-
335
-
336
-    /**
337
-     * @param EE_Line_Item $line_item
338
-     * @return float
339
-     * @throws EE_Error
340
-     * @throws ReflectionException
341
-     */
342
-    private function calculateTotalForQuantity(EE_Line_Item $line_item): float
343
-    {
344
-        $total = $line_item->unit_price() * $line_item->quantity();
345
-        return $this->decimal_values->roundDecimalValue($total);
346
-    }
347
-
348
-
349
-    /**
350
-     * @param EE_Line_Item $line_item
351
-     * @param float        $percent
352
-     * @throws EE_Error
353
-     * @throws ReflectionException
354
-     */
355
-    private function updatePercent(EE_Line_Item $line_item, float $percent)
356
-    {
357
-        // update and save new percent only if incoming value does not match existing value
358
-        if ($line_item->percent() !== $percent) {
359
-            $line_item->set_percent($percent);
360
-            $line_item->maybe_save();
361
-        }
362
-    }
363
-
364
-
365
-    /**
366
-     * @param EE_Line_Item $line_item
367
-     * @param float        $pretax_total
368
-     * @param bool         $round
369
-     * @return float
370
-     * @throws EE_Error
371
-     * @throws ReflectionException
372
-     */
373
-    private function updatePreTaxTotal(EE_Line_Item $line_item, float $pretax_total, bool $round = false): float
374
-    {
375
-        $pretax_total = $this->decimal_values->roundDecimalValue($pretax_total, $round);
376
-        // update and save new total only if incoming value does not match existing value
377
-        if ($line_item->preTaxTotal() !== $pretax_total) {
378
-            $line_item->setPreTaxTotal($pretax_total);
379
-            $line_item->maybe_save();
380
-        }
381
-        return $pretax_total;
382
-    }
383
-
384
-
385
-    /**
386
-     * @param EE_Line_Item $line_item
387
-     * @param int          $quantity
388
-     * @throws EE_Error
389
-     * @throws ReflectionException
390
-     */
391
-    private function updateQuantity(EE_Line_Item $line_item, int $quantity)
392
-    {
393
-        // update and save new quantity only if incoming value does not match existing value
394
-        if ($line_item->quantity() !== $quantity) {
395
-            $line_item->set_quantity($quantity);
396
-            $line_item->maybe_save();
397
-        }
398
-    }
399
-
400
-
401
-    /**
402
-     * @param EE_Line_Item $line_item
403
-     * @param float        $total
404
-     * @param bool         $round
405
-     * @return float
406
-     * @throws EE_Error
407
-     * @throws ReflectionException
408
-     */
409
-    private function updateTotal(EE_Line_Item $line_item, float $total, bool $round = false): float
410
-    {
411
-        $total = $this->decimal_values->roundDecimalValue($total, $round);
412
-        // update and save new total only if incoming value does not match existing value
413
-        if ($line_item->total() !== $total) {
414
-            $line_item->set_total($total);
415
-            $line_item->maybe_save();
416
-        }
417
-        return $total;
418
-    }
419
-
420
-
421
-    /**
422
-     * @param EE_Line_Item $line_item
423
-     * @param float        $total
424
-     * @param bool         $update_status
425
-     * @return void
426
-     * @throws EE_Error
427
-     * @throws ReflectionException
428
-     */
429
-    private function updateTransaction(EE_Line_Item $line_item, float $total, bool $update_status)
430
-    {
431
-        // only update the related transaction's total
432
-        // if we intend to save this line item and its a grand total
433
-        if ($line_item->allow_persist()) {
434
-            $transaction = $line_item->transaction();
435
-            if ($transaction instanceof EE_Transaction) {
436
-                $transaction->set_total($total);
437
-                if ($update_status) {
438
-                    // don't save the TXN because that will be done below
439
-                    // and the following method only saves if the status changes
440
-                    $transaction->update_status_based_on_total_paid(false);
441
-                }
442
-                if ($transaction->ID()) {
443
-                    $transaction->save();
444
-                }
445
-            }
446
-        }
447
-    }
448
-
449
-
450
-    /**
451
-     * @param EE_Line_Item $line_item
452
-     * @param float        $pretax_total
453
-     * @return void
454
-     * @throws EE_Error
455
-     * @throws ReflectionException
456
-     */
457
-    private function updateUnitPrice(EE_Line_Item $line_item, float $pretax_total)
458
-    {
459
-        $quantity = $line_item->quantity();
460
-        // don't divide by zero else you'll create a singularity and implode the interweb
461
-        // we also don't set unit prices for percentage based line items
462
-        if ($quantity === 0 || $line_item->is_percent()) {
463
-            return;
464
-        }
465
-        $new_unit_price = $pretax_total / $quantity;
466
-        $new_unit_price = $this->decimal_values->roundDecimalValue($new_unit_price);
467
-        // update and save new total only if incoming value does not match existing value
468
-        if ($line_item->unit_price() !== $new_unit_price) {
469
-            $line_item->set_unit_price($new_unit_price);
470
-            $line_item->maybe_save();
471
-        }
472
-    }
473
-
474
-
475
-    /**
476
-     * Recalculates the total on each individual tax (based on a recalculation of the pre-tax total), sets
477
-     * the totals on each tax calculated, and returns the final tax total. Re-saves tax line items
478
-     * and tax sub-total if already in the DB
479
-     *
480
-     * @param EE_Line_Item $total_line_item
481
-     * @return float
482
-     * @throws EE_Error
483
-     * @throws ReflectionException
484
-     */
485
-    public function recalculateTaxesAndTaxTotal(EE_Line_Item $total_line_item): float
486
-    {
487
-        $this->validateLineItemAndType($total_line_item, EEM_Line_Item::type_total);
488
-        // calculate the total taxable amount for globally applied taxes
489
-        $taxable_total = $this->taxableAmountForGlobalTaxes($total_line_item);
490
-        $global_taxes     = $this->applyGlobalTaxes($total_line_item, $taxable_total);
491
-        $non_global_taxes = $this->calculateNonGlobalTaxes($total_line_item);
492
-        $all_tax_total        = $this->applyNonGlobalTaxes($total_line_item, $global_taxes, $non_global_taxes);
493
-        $this->recalculateTaxSubTotal($total_line_item);
494
-        return $all_tax_total;
495
-    }
496
-
497
-
498
-    /**
499
-     * @param EE_Line_Item $total_line_item
500
-     * @param float        $taxable_total
501
-     * @return float
502
-     * @throws EE_Error
503
-     * @throws ReflectionException
504
-     */
505
-    private function applyGlobalTaxes(EE_Line_Item $total_line_item, float $taxable_total): float
506
-    {
507
-        $this->validateLineItemAndType($total_line_item, EEM_Line_Item::type_total);
508
-        $total_tax = 0;
509
-        // loop through all global taxes all taxes
510
-        $global_taxes = $total_line_item->tax_descendants();
511
-        foreach ($global_taxes as $tax) {
512
-            $tax_total = $this->calculatePercentage($taxable_total, $tax->percent());
513
-            $tax_total = $this->updateTotal($tax, $tax_total, true);
514
-            $total_tax += $tax_total;
515
-        }
516
-        return $this->decimal_values->roundDecimalValue($total_tax, true);
517
-    }
518
-
519
-
520
-    /**
521
-     * Simply forces all the tax-sub-totals to recalculate. Assumes the taxes have been calculated
522
-     *
523
-     * @param EE_Line_Item $line_item
524
-     * @return void
525
-     * @throws EE_Error
526
-     * @throws ReflectionException
527
-     */
528
-    private function recalculateTaxSubTotal(EE_Line_Item $line_item)
529
-    {
530
-        $this->validateLineItemAndType($line_item, EEM_Line_Item::type_total);
531
-        foreach ($line_item->children() as $maybe_tax_subtotal) {
532
-            if (
533
-                $this->validateLineItemAndType($maybe_tax_subtotal)
534
-                && $maybe_tax_subtotal->is_tax_sub_total()
535
-            ) {
536
-                $total         = 0;
537
-                $total_percent = 0;
538
-                // simply loop through all its children (which should be taxes) and sum their total
539
-                foreach ($maybe_tax_subtotal->children() as $child_tax) {
540
-                    if ($this->validateLineItemAndType($child_tax) && $child_tax->isGlobalTax()) {
541
-                        $total         += $child_tax->total();
542
-                        $total_percent += $child_tax->percent();
543
-                    }
544
-                }
545
-                $this->updateTotal($maybe_tax_subtotal, $total, true);
546
-                $this->updatePercent($maybe_tax_subtotal, $total_percent);
547
-            }
548
-        }
549
-    }
550
-
551
-
552
-    /**
553
-     * returns an array of tax details like:
554
-     *  [
555
-     *      'GST_7' => [
556
-     *          'name'  => 'GST',
557
-     *          'rate'  => float(7),
558
-     *          'total' => float(4.9),
559
-     *      ]
560
-     *  ]
561
-     *
562
-     * @param EE_Line_Item $total_line_item
563
-     * @param array        $non_global_taxes
564
-     * @param float        $line_item_total
565
-     * @return array
566
-     * @throws EE_Error
567
-     * @throws ReflectionException
568
-     */
569
-    private function calculateNonGlobalTaxes(
570
-        EE_Line_Item $total_line_item,
571
-        array $non_global_taxes = [],
572
-        float $line_item_total = 0
573
-    ): array {
574
-        foreach ($total_line_item->children() as $line_item) {
575
-            if ($this->validateLineItemAndType($line_item)) {
576
-                if ($line_item->is_sub_total()) {
577
-                    $non_global_taxes = $this->calculateNonGlobalTaxes($line_item, $non_global_taxes);
578
-                } elseif ($line_item->is_line_item()) {
579
-                    $non_global_taxes = $this->calculateNonGlobalTaxes(
580
-                        $line_item,
581
-                        $non_global_taxes,
582
-                        $line_item->pretaxTotal()
583
-                    );
584
-                } elseif ($line_item->isSubTax()) {
585
-                    $tax_ID = $line_item->name() . '_' . $line_item->percent();
586
-                    if (! isset($non_global_taxes[ $tax_ID ])) {
587
-                        $non_global_taxes[ $tax_ID ] = [
588
-                            'name'  => $line_item->name(),
589
-                            'rate'  => $line_item->percent(),
590
-                            'total' => 0,
591
-                            'obj'   => $line_item->OBJ_type(),
592
-                            'objID' => $line_item->OBJ_ID(),
593
-                        ];
594
-                    }
595
-                    $tax = $this->calculatePercentage($line_item_total, $line_item->percent());
596
-                    $non_global_taxes[ $tax_ID ]['total'] += $tax;
597
-                }
598
-            }
599
-        }
600
-        return $non_global_taxes;
601
-    }
602
-
603
-
604
-    /**
605
-     * @param EE_Line_Item $total_line_item
606
-     * @param float        $tax_total
607
-     * @param array        $non_global_taxes array of tax details generated by calculateNonGlobalTaxes()
608
-     * @return float
609
-     * @throws EE_Error
610
-     * @throws ReflectionException
611
-     */
612
-    private function applyNonGlobalTaxes(
613
-        EE_Line_Item $total_line_item,
614
-        float $tax_total,
615
-        array $non_global_taxes
616
-    ): float {
617
-        $global_taxes   = $total_line_item->tax_descendants();
618
-        $taxes_subtotal = EEH_Line_Item::get_taxes_subtotal($total_line_item);
619
-        foreach ($non_global_taxes as $non_global_tax) {
620
-            $found = false;
621
-            foreach ($global_taxes as $global_tax) {
622
-                if (
623
-                    $this->validateLineItemAndType($global_tax)
624
-                    && $non_global_tax['obj'] === $global_tax->OBJ_type()
625
-                    && $non_global_tax['objID'] === $global_tax->OBJ_ID()
626
-                ) {
627
-                    $found = true;
628
-                    $new_total = $global_tax->total() + $non_global_tax['total'];
629
-                    // add non global tax to matching global tax AND the tax total
630
-                    $global_tax->set_total($new_total);
631
-                    $global_tax->maybe_save();
632
-                    $tax_total += $non_global_tax['total'];
633
-                }
634
-            }
635
-            if (! $found) {
636
-                // add a new line item for this non global tax
637
-                $taxes_subtotal->add_child_line_item(
638
-                    EE_Line_Item::new_instance(
639
-                        [
640
-                            'LIN_name'       => $non_global_tax['name'],
641
-                            'LIN_percent'    => $non_global_tax['rate'],
642
-                            'LIN_is_taxable' => false,
643
-                            'LIN_total'      => $non_global_tax['total'],
644
-                            'LIN_type'       => EEM_Line_Item::type_tax,
645
-                            'OBJ_type'       => $non_global_tax['obj'],
646
-                            'OBJ_ID'         => $non_global_tax['objID'],
647
-                        ]
648
-                    )
649
-                );
650
-                $tax_total += $non_global_tax['total'];
651
-            }
652
-        }
653
-        return $this->decimal_values->roundDecimalValue($tax_total, true);
654
-    }
655
-
656
-
657
-    /**
658
-     * Gets the total tax on this line item. Assumes taxes have already been calculated using
659
-     * recalculate_taxes_and_total
660
-     *
661
-     * @param EE_Line_Item $line_item
662
-     * @return float
663
-     * @throws EE_Error
664
-     * @throws ReflectionException
665
-     */
666
-    public function getTotalTax(EE_Line_Item $line_item): float
667
-    {
668
-        $this->validateLineItemAndType($line_item, EEM_Line_Item::type_total);
669
-        $this->recalculateTaxSubTotal($line_item);
670
-        $total = 0;
671
-        foreach ($line_item->tax_descendants() as $tax_line_item) {
672
-            if ($this->validateLineItemAndType($tax_line_item)) {
673
-                $total += $tax_line_item->total();
674
-            }
675
-        }
676
-        return $this->decimal_values->roundDecimalValue($total, true);
677
-    }
678
-
679
-
680
-    /**
681
-     * Returns the amount taxable among this line item's children (or if it has no children,
682
-     * how much of it is taxable). Does not recalculate totals or subtotals.
683
-     * If the taxable total is negative, (eg, if none of the tickets were taxable,
684
-     * but there is a "Taxable" discount), returns 0.
685
-     *
686
-     * @param EE_Line_Item|null $line_item
687
-     * @return float
688
-     * @throws EE_Error
689
-     * @throws ReflectionException
690
-     */
691
-    public function taxableAmountForGlobalTaxes(?EE_Line_Item $line_item): float
692
-    {
693
-        $total      = 0;
694
-        $child_line_items = $line_item->children($this->default_query_params);
695
-        foreach ($child_line_items as $child_line_item) {
696
-            $this->validateLineItemAndType($child_line_item);
697
-            if ($child_line_item->is_sub_total()) {
698
-                $total += $this->taxableAmountForGlobalTaxes($child_line_item);
699
-            } elseif ($child_line_item->is_line_item() && $child_line_item->is_taxable()) {
700
-                // if it's a percent item, only take into account
701
-                // the percentage that's taxable (the taxable total so far)
702
-                if ($child_line_item->is_percent()) {
703
-                    $total += $this->calculatePercentage($total, $child_line_item->percent(), true);
704
-                } else {
705
-                    // pretax total will be equal to the total for line items with globally applied taxes
706
-                    $pretax_total = $this->calculateTotalForQuantity($child_line_item);
707
-                    $total += $this->updatePreTaxTotal($child_line_item, $pretax_total);
708
-                }
709
-            }
710
-        }
711
-        return max($total, 0);
712
-    }
713
-
714
-
715
-    /**
716
-     * @param EE_Line_Item|null $line_item
717
-     * @param string|null       $type
718
-     * @return bool
719
-     * @throws EE_Error
720
-     * @throws ReflectionException
721
-     */
722
-    private function validateLineItemAndType(?EE_Line_Item $line_item, ?string $type = null): bool
723
-    {
724
-        if (! $line_item instanceof EE_Line_Item) {
725
-            throw new DomainException(
726
-                esc_html__('Invalid or missing Line Item supplied .', 'event_espresso')
727
-            );
728
-        }
729
-        if ($type && $line_item->type() !== $type) {
730
-            throw new DomainException(
731
-                sprintf(
732
-                    esc_html__(
733
-                        'Invalid Line Item type supplied. Received "%1$s" but expected "%2$s".',
734
-                        'event_espresso'
735
-                    ),
736
-                    $line_item->type(),
737
-                    $type
738
-                )
739
-            );
740
-        }
741
-        return true;
742
-    }
24
+	/**
25
+	 * @var DecimalValues
26
+	 */
27
+	protected $decimal_values;
28
+
29
+	/**
30
+	 * @var array
31
+	 */
32
+	protected $default_query_params = [
33
+		['LIN_type' => ['!=', EEM_Line_Item::type_cancellation]]
34
+	];
35
+
36
+
37
+	/**
38
+	 * @param DecimalValues $decimal_values
39
+	 */
40
+	public function __construct(DecimalValues $decimal_values)
41
+	{
42
+		$this->decimal_values = $decimal_values;
43
+	}
44
+
45
+
46
+	/**
47
+	 * Gets the final total on this item, taking taxes into account.
48
+	 * Has the side-effect of setting the sub-total as it was just calculated.
49
+	 * If this is used on a grand-total line item, also updates the transaction's
50
+	 * TXN_total (provided this line item is allowed to persist, otherwise we don't
51
+	 * want to change a persistable transaction with info from a non-persistent line item)
52
+	 *
53
+	 * @param EE_Line_Item $line_item
54
+	 * @param bool         $update_txn_status
55
+	 * @return float
56
+	 * @throws EE_Error
57
+	 * @throws ReflectionException
58
+	 */
59
+	public function recalculateTotalIncludingTaxes(EE_Line_Item $line_item, bool $update_txn_status = false): float
60
+	{
61
+		$this->validateLineItemAndType($line_item, EEM_Line_Item::type_total);
62
+		$ticket_line_items = EEH_Line_Item::get_ticket_line_items($line_item);
63
+		if (empty($ticket_line_items)) {
64
+			return 0;
65
+		}
66
+		[, $pretax_total] = $this->recalculateLineItemTotals($line_item);
67
+		// EEH_Line_Item::visualize($line_item);
68
+		$total_tax = $this->recalculateTaxesAndTaxTotal($line_item);
69
+		// no negative totals plz
70
+		$grand_total  = max($pretax_total + $total_tax, 0);
71
+		$this->updatePreTaxTotal($line_item, $pretax_total, true);
72
+		$grand_total  = $this->updateTotal($line_item, $grand_total, true);
73
+		$this->updateTransaction($line_item, $grand_total, $update_txn_status);
74
+		return $grand_total;
75
+	}
76
+
77
+
78
+	/**
79
+	 * Recursively goes through all the children and recalculates sub-totals EXCEPT for
80
+	 * tax-sub-totals (they're a an odd beast). Updates the 'total' on each line item according to either its
81
+	 * unit price * quantity or the total of all its children EXCEPT when we're only calculating the taxable total and
82
+	 * when this is called on the grand total
83
+	 *
84
+	 * @param EE_Line_Item $line_item
85
+	 * @param float        $total
86
+	 * @param float        $pretax_total
87
+	 * @return array
88
+	 * @throws EE_Error
89
+	 * @throws ReflectionException
90
+	 */
91
+	public function recalculateLineItemTotals(
92
+		EE_Line_Item $line_item,
93
+		float $total = 0,
94
+		float $pretax_total = 0
95
+	): array {
96
+		switch ($line_item->type()) {
97
+			case EEM_Line_Item::type_total:
98
+			case EEM_Line_Item::type_sub_total:
99
+				[$total, $pretax_total] = $this->recalculateSubTotal($line_item);
100
+				break;
101
+
102
+			case EEM_Line_Item::type_line_item:
103
+				[$total, $pretax_total] = $this->recalculateLineItem($line_item, $total, $pretax_total);
104
+				break;
105
+
106
+			case EEM_Line_Item::type_sub_line_item:
107
+				// sub line items operate on the total and update both the total AND the pre-tax total
108
+				[$total, $pretax_total] = $this->recalculateSubLineItem($line_item, $total, $pretax_total);
109
+				break;
110
+
111
+			case EEM_Line_Item::type_sub_tax:
112
+				// sub line item taxes ONLY operate on the pre-tax total and ONLY update the total
113
+				[$total, $pretax_total] = $this->recalculateSubTax($line_item, $pretax_total);
114
+				break;
115
+
116
+			case EEM_Line_Item::type_tax_sub_total:
117
+			case EEM_Line_Item::type_tax:
118
+			case EEM_Line_Item::type_cancellation:
119
+				// completely ignore tax totals, tax sub-totals, and cancelled line items
120
+				// when calculating the pre-tax-total
121
+				$total = $pretax_total = 0;
122
+				break;
123
+		}
124
+		return [$total, $pretax_total];
125
+	}
126
+
127
+
128
+	/**
129
+	 * @param EE_Line_Item $line_item
130
+	 * @return array
131
+	 * @throws EE_Error
132
+	 * @throws ReflectionException
133
+	 */
134
+	private function recalculateSubTotal(EE_Line_Item $line_item): array
135
+	{
136
+		// reset the total and pretax total to zero since we are recalculating them
137
+		$total = $pretax_total = 0;
138
+		if ($line_item->is_total()) {
139
+			// if this is the grand total line item
140
+			// then first update ALL of the line item quantities (if need be)
141
+			$this->updateLineItemQuantities($line_item);
142
+		}
143
+		// recursively loop through children and recalculate their totals
144
+		$children = $line_item->children($this->default_query_params);
145
+		if (empty($children)) {
146
+			return [$total, $pretax_total];
147
+		}
148
+		foreach ($children as $child_line_item) {
149
+			[$child_total, $child_pretax_total] = $this->recalculateLineItemTotals(
150
+				$child_line_item,
151
+				$total,
152
+				$pretax_total
153
+			);
154
+			$total += $child_total;
155
+			$pretax_total += $child_pretax_total;
156
+		}
157
+		// update the unit price and pretax total
158
+		$this->updateUnitPrice($line_item, $pretax_total);
159
+		$pretax_total = $this->updatePreTaxTotal($line_item, $pretax_total, true);
160
+		// for the actual pre-tax sub total line item, we want to save the pretax value for everything
161
+		if ($line_item->is_sub_total() && $line_item->name() === esc_html__('Pre-Tax Subtotal', 'event_espresso')) {
162
+			$this->updateTotal($line_item, $pretax_total, true);
163
+		} elseif (! $line_item->is_total()) {
164
+			// we don't update the total for the total line item, because that will need to include taxes
165
+			$total = $this->updateTotal($line_item, $total, true);
166
+		}
167
+		return [$total, $pretax_total];
168
+	}
169
+
170
+
171
+	/**
172
+	 * @param EE_Line_Item $line_item
173
+	 * @param float        $total
174
+	 * @param float        $pretax_total
175
+	 * @return array
176
+	 * @throws EE_Error
177
+	 * @throws ReflectionException
178
+	 */
179
+	private function recalculateLineItem(
180
+		EE_Line_Item $line_item,
181
+		float $total = 0,
182
+		float $pretax_total = 0
183
+	): array {
184
+		if ($line_item->is_percent()) {
185
+			$total = $this->calculatePercentage($total, $line_item->percent());
186
+			$pretax_total = $this->calculatePercentage($pretax_total, $line_item->percent());
187
+		} else {
188
+			// recursively loop through children and recalculate their totals
189
+			$children = $line_item->children($this->default_query_params);
190
+			if (! empty($children)) {
191
+				// reset the total and pretax total to zero since we are recalculating them
192
+				$total = $pretax_total = 0;
193
+				foreach ($children as $child_line_item) {
194
+					[$child_total, $child_pretax_total] = $this->recalculateLineItemTotals(
195
+						$child_line_item,
196
+						$total,
197
+						$pretax_total
198
+					);
199
+					$total        += $child_total;
200
+					$pretax_total += $child_pretax_total;
201
+				}
202
+			} else {
203
+				// no child line items, so recalculate the total from the unit price and quantity
204
+				// and set the pretax total to match since their are obviously no sub-taxes
205
+				$pretax_total = $total = $this->calculateTotalForQuantity($line_item);
206
+			}
207
+		}
208
+		$total  = $this->updateTotal($line_item, $total, true);
209
+		$pretax_total = $this->updatePreTaxTotal($line_item, $pretax_total, true);
210
+
211
+		// need to also adjust unit price too if the pretax total or quantity has been updated
212
+		$this->updateUnitPrice($line_item, $pretax_total);
213
+		return [$total, $pretax_total];
214
+	}
215
+
216
+
217
+	/**
218
+	 * @param EE_Line_Item $sub_line_item
219
+	 * @param float|int    $total
220
+	 * @param float|int    $pretax_total
221
+	 * @return float[]
222
+	 * @throws EE_Error
223
+	 * @throws ReflectionException
224
+	 */
225
+	private function recalculateSubLineItem(EE_Line_Item $sub_line_item, float $total = 0, float $pretax_total = 0): array
226
+	{
227
+		if ($sub_line_item->is_percent()) {
228
+			$new_total = $this->calculatePercentage($total, $sub_line_item->percent());
229
+			$new_pretax_total = $this->calculatePercentage($pretax_total, $sub_line_item->percent());
230
+		} else {
231
+			$new_total = $new_pretax_total = $this->calculateTotalForQuantity($sub_line_item);
232
+		}
233
+		$total = $this->updateTotal($sub_line_item, $new_total);
234
+		$pretax_total = $this->updatePreTaxTotal($sub_line_item, $new_pretax_total);
235
+		// need to also adjust unit price too if the pretax total or quantity has been updated
236
+		$this->updateUnitPrice($sub_line_item, $pretax_total);
237
+		return [$total, $pretax_total];
238
+	}
239
+
240
+
241
+	/**
242
+	 * @param EE_Line_Item $sub_line_item
243
+	 * @param float|int    $pretax_total
244
+	 * @return float[]
245
+	 * @throws EE_Error
246
+	 * @throws ReflectionException
247
+	 */
248
+	private function recalculateSubTax(EE_Line_Item $sub_line_item, float $pretax_total = 0): array
249
+	{
250
+		$total_tax = $this->calculatePercentage($pretax_total, $sub_line_item->percent());
251
+		$total_tax = $this->updateTotal($sub_line_item, $total_tax);
252
+		return [$total_tax, 0];
253
+	}
254
+
255
+
256
+	/**
257
+	 * recursively loops through the entire line item tree updating line item quantities accordingly.
258
+	 * this needs to be done prior to running any other calculations for reasons that are hopefully obvious :p
259
+	 *
260
+	 * @param EE_Line_Item $line_item
261
+	 * @param int          $quantity
262
+	 * @return int
263
+	 * @throws EE_Error
264
+	 * @throws ReflectionException
265
+	 */
266
+	private function updateLineItemQuantities(EE_Line_Item $line_item, int $quantity = 1): int
267
+	{
268
+		switch ($line_item->type()) {
269
+			case EEM_Line_Item::type_total:
270
+			case EEM_Line_Item::type_sub_total:
271
+			case EEM_Line_Item::type_tax_sub_total:
272
+				// first, loop through children and set their quantities
273
+				$count = 0;
274
+				$children = $line_item->children($this->default_query_params);
275
+				foreach ($children as $child_line_item) {
276
+					$count += $this->updateLineItemQuantities($child_line_item);
277
+				}
278
+				// totals and subtotals should have a quantity of 1
279
+				// unless their children have all been removed, in which case we can set them to 0
280
+				$quantity = $count > 0 ? 1 : 0;
281
+				$this->updateQuantity($line_item, $quantity);
282
+				return $quantity;
283
+
284
+			case EEM_Line_Item::type_line_item:
285
+				// line items should ALREADY have accurate quantities set, if not, then somebody done goofed!
286
+				// but if this is a percentage based line item, then ensure its quantity is 1
287
+				if ($line_item->is_percent()) {
288
+					$this->updateQuantity($line_item, 1);
289
+				}
290
+				// and we also need to loop through all of the sub items and ensure those quantities match this parent.
291
+				$children = $line_item->children($this->default_query_params);
292
+				$quantity = $line_item->quantity();
293
+				foreach ($children as $child_line_item) {
294
+					$this->updateLineItemQuantities($child_line_item, $quantity);
295
+				}
296
+				// percentage line items should not increment their parent's count, so they return 0
297
+				return ! $line_item->is_percent() ? $quantity : 0;
298
+
299
+			case EEM_Line_Item::type_sub_line_item:
300
+				// percentage based items need their quantity set to 1,
301
+				// all others use the incoming value from the parent line item
302
+				$quantity = $line_item->is_percent() ? 1 : $quantity;
303
+				$this->updateQuantity($line_item, $quantity);
304
+				// percentage line items should not increment their parent's count, so they return 0
305
+				return ! $line_item->is_percent() ? $quantity : 0;
306
+
307
+			case EEM_Line_Item::type_tax:
308
+			case EEM_Line_Item::type_sub_tax:
309
+				// taxes should have a quantity of 1
310
+				$this->updateQuantity($line_item, 1);
311
+				return 1;
312
+
313
+			case EEM_Line_Item::type_cancellation:
314
+				// cancellations will be ignored for all calculations
315
+				// because their parent quantities should have already been adjusted when they were added
316
+				// so assume that things are already set correctly
317
+				return 0;
318
+		}
319
+		return 0;
320
+	}
321
+
322
+
323
+	/**
324
+	 * @param float $total
325
+	 * @param float $percent
326
+	 * @param bool  $round
327
+	 * @return float
328
+	 */
329
+	private function calculatePercentage(float $total, float $percent, bool $round = false): float
330
+	{
331
+		$amount = $total * $percent / 100;
332
+		return $this->decimal_values->roundDecimalValue($amount, $round);
333
+	}
334
+
335
+
336
+	/**
337
+	 * @param EE_Line_Item $line_item
338
+	 * @return float
339
+	 * @throws EE_Error
340
+	 * @throws ReflectionException
341
+	 */
342
+	private function calculateTotalForQuantity(EE_Line_Item $line_item): float
343
+	{
344
+		$total = $line_item->unit_price() * $line_item->quantity();
345
+		return $this->decimal_values->roundDecimalValue($total);
346
+	}
347
+
348
+
349
+	/**
350
+	 * @param EE_Line_Item $line_item
351
+	 * @param float        $percent
352
+	 * @throws EE_Error
353
+	 * @throws ReflectionException
354
+	 */
355
+	private function updatePercent(EE_Line_Item $line_item, float $percent)
356
+	{
357
+		// update and save new percent only if incoming value does not match existing value
358
+		if ($line_item->percent() !== $percent) {
359
+			$line_item->set_percent($percent);
360
+			$line_item->maybe_save();
361
+		}
362
+	}
363
+
364
+
365
+	/**
366
+	 * @param EE_Line_Item $line_item
367
+	 * @param float        $pretax_total
368
+	 * @param bool         $round
369
+	 * @return float
370
+	 * @throws EE_Error
371
+	 * @throws ReflectionException
372
+	 */
373
+	private function updatePreTaxTotal(EE_Line_Item $line_item, float $pretax_total, bool $round = false): float
374
+	{
375
+		$pretax_total = $this->decimal_values->roundDecimalValue($pretax_total, $round);
376
+		// update and save new total only if incoming value does not match existing value
377
+		if ($line_item->preTaxTotal() !== $pretax_total) {
378
+			$line_item->setPreTaxTotal($pretax_total);
379
+			$line_item->maybe_save();
380
+		}
381
+		return $pretax_total;
382
+	}
383
+
384
+
385
+	/**
386
+	 * @param EE_Line_Item $line_item
387
+	 * @param int          $quantity
388
+	 * @throws EE_Error
389
+	 * @throws ReflectionException
390
+	 */
391
+	private function updateQuantity(EE_Line_Item $line_item, int $quantity)
392
+	{
393
+		// update and save new quantity only if incoming value does not match existing value
394
+		if ($line_item->quantity() !== $quantity) {
395
+			$line_item->set_quantity($quantity);
396
+			$line_item->maybe_save();
397
+		}
398
+	}
399
+
400
+
401
+	/**
402
+	 * @param EE_Line_Item $line_item
403
+	 * @param float        $total
404
+	 * @param bool         $round
405
+	 * @return float
406
+	 * @throws EE_Error
407
+	 * @throws ReflectionException
408
+	 */
409
+	private function updateTotal(EE_Line_Item $line_item, float $total, bool $round = false): float
410
+	{
411
+		$total = $this->decimal_values->roundDecimalValue($total, $round);
412
+		// update and save new total only if incoming value does not match existing value
413
+		if ($line_item->total() !== $total) {
414
+			$line_item->set_total($total);
415
+			$line_item->maybe_save();
416
+		}
417
+		return $total;
418
+	}
419
+
420
+
421
+	/**
422
+	 * @param EE_Line_Item $line_item
423
+	 * @param float        $total
424
+	 * @param bool         $update_status
425
+	 * @return void
426
+	 * @throws EE_Error
427
+	 * @throws ReflectionException
428
+	 */
429
+	private function updateTransaction(EE_Line_Item $line_item, float $total, bool $update_status)
430
+	{
431
+		// only update the related transaction's total
432
+		// if we intend to save this line item and its a grand total
433
+		if ($line_item->allow_persist()) {
434
+			$transaction = $line_item->transaction();
435
+			if ($transaction instanceof EE_Transaction) {
436
+				$transaction->set_total($total);
437
+				if ($update_status) {
438
+					// don't save the TXN because that will be done below
439
+					// and the following method only saves if the status changes
440
+					$transaction->update_status_based_on_total_paid(false);
441
+				}
442
+				if ($transaction->ID()) {
443
+					$transaction->save();
444
+				}
445
+			}
446
+		}
447
+	}
448
+
449
+
450
+	/**
451
+	 * @param EE_Line_Item $line_item
452
+	 * @param float        $pretax_total
453
+	 * @return void
454
+	 * @throws EE_Error
455
+	 * @throws ReflectionException
456
+	 */
457
+	private function updateUnitPrice(EE_Line_Item $line_item, float $pretax_total)
458
+	{
459
+		$quantity = $line_item->quantity();
460
+		// don't divide by zero else you'll create a singularity and implode the interweb
461
+		// we also don't set unit prices for percentage based line items
462
+		if ($quantity === 0 || $line_item->is_percent()) {
463
+			return;
464
+		}
465
+		$new_unit_price = $pretax_total / $quantity;
466
+		$new_unit_price = $this->decimal_values->roundDecimalValue($new_unit_price);
467
+		// update and save new total only if incoming value does not match existing value
468
+		if ($line_item->unit_price() !== $new_unit_price) {
469
+			$line_item->set_unit_price($new_unit_price);
470
+			$line_item->maybe_save();
471
+		}
472
+	}
473
+
474
+
475
+	/**
476
+	 * Recalculates the total on each individual tax (based on a recalculation of the pre-tax total), sets
477
+	 * the totals on each tax calculated, and returns the final tax total. Re-saves tax line items
478
+	 * and tax sub-total if already in the DB
479
+	 *
480
+	 * @param EE_Line_Item $total_line_item
481
+	 * @return float
482
+	 * @throws EE_Error
483
+	 * @throws ReflectionException
484
+	 */
485
+	public function recalculateTaxesAndTaxTotal(EE_Line_Item $total_line_item): float
486
+	{
487
+		$this->validateLineItemAndType($total_line_item, EEM_Line_Item::type_total);
488
+		// calculate the total taxable amount for globally applied taxes
489
+		$taxable_total = $this->taxableAmountForGlobalTaxes($total_line_item);
490
+		$global_taxes     = $this->applyGlobalTaxes($total_line_item, $taxable_total);
491
+		$non_global_taxes = $this->calculateNonGlobalTaxes($total_line_item);
492
+		$all_tax_total        = $this->applyNonGlobalTaxes($total_line_item, $global_taxes, $non_global_taxes);
493
+		$this->recalculateTaxSubTotal($total_line_item);
494
+		return $all_tax_total;
495
+	}
496
+
497
+
498
+	/**
499
+	 * @param EE_Line_Item $total_line_item
500
+	 * @param float        $taxable_total
501
+	 * @return float
502
+	 * @throws EE_Error
503
+	 * @throws ReflectionException
504
+	 */
505
+	private function applyGlobalTaxes(EE_Line_Item $total_line_item, float $taxable_total): float
506
+	{
507
+		$this->validateLineItemAndType($total_line_item, EEM_Line_Item::type_total);
508
+		$total_tax = 0;
509
+		// loop through all global taxes all taxes
510
+		$global_taxes = $total_line_item->tax_descendants();
511
+		foreach ($global_taxes as $tax) {
512
+			$tax_total = $this->calculatePercentage($taxable_total, $tax->percent());
513
+			$tax_total = $this->updateTotal($tax, $tax_total, true);
514
+			$total_tax += $tax_total;
515
+		}
516
+		return $this->decimal_values->roundDecimalValue($total_tax, true);
517
+	}
518
+
519
+
520
+	/**
521
+	 * Simply forces all the tax-sub-totals to recalculate. Assumes the taxes have been calculated
522
+	 *
523
+	 * @param EE_Line_Item $line_item
524
+	 * @return void
525
+	 * @throws EE_Error
526
+	 * @throws ReflectionException
527
+	 */
528
+	private function recalculateTaxSubTotal(EE_Line_Item $line_item)
529
+	{
530
+		$this->validateLineItemAndType($line_item, EEM_Line_Item::type_total);
531
+		foreach ($line_item->children() as $maybe_tax_subtotal) {
532
+			if (
533
+				$this->validateLineItemAndType($maybe_tax_subtotal)
534
+				&& $maybe_tax_subtotal->is_tax_sub_total()
535
+			) {
536
+				$total         = 0;
537
+				$total_percent = 0;
538
+				// simply loop through all its children (which should be taxes) and sum their total
539
+				foreach ($maybe_tax_subtotal->children() as $child_tax) {
540
+					if ($this->validateLineItemAndType($child_tax) && $child_tax->isGlobalTax()) {
541
+						$total         += $child_tax->total();
542
+						$total_percent += $child_tax->percent();
543
+					}
544
+				}
545
+				$this->updateTotal($maybe_tax_subtotal, $total, true);
546
+				$this->updatePercent($maybe_tax_subtotal, $total_percent);
547
+			}
548
+		}
549
+	}
550
+
551
+
552
+	/**
553
+	 * returns an array of tax details like:
554
+	 *  [
555
+	 *      'GST_7' => [
556
+	 *          'name'  => 'GST',
557
+	 *          'rate'  => float(7),
558
+	 *          'total' => float(4.9),
559
+	 *      ]
560
+	 *  ]
561
+	 *
562
+	 * @param EE_Line_Item $total_line_item
563
+	 * @param array        $non_global_taxes
564
+	 * @param float        $line_item_total
565
+	 * @return array
566
+	 * @throws EE_Error
567
+	 * @throws ReflectionException
568
+	 */
569
+	private function calculateNonGlobalTaxes(
570
+		EE_Line_Item $total_line_item,
571
+		array $non_global_taxes = [],
572
+		float $line_item_total = 0
573
+	): array {
574
+		foreach ($total_line_item->children() as $line_item) {
575
+			if ($this->validateLineItemAndType($line_item)) {
576
+				if ($line_item->is_sub_total()) {
577
+					$non_global_taxes = $this->calculateNonGlobalTaxes($line_item, $non_global_taxes);
578
+				} elseif ($line_item->is_line_item()) {
579
+					$non_global_taxes = $this->calculateNonGlobalTaxes(
580
+						$line_item,
581
+						$non_global_taxes,
582
+						$line_item->pretaxTotal()
583
+					);
584
+				} elseif ($line_item->isSubTax()) {
585
+					$tax_ID = $line_item->name() . '_' . $line_item->percent();
586
+					if (! isset($non_global_taxes[ $tax_ID ])) {
587
+						$non_global_taxes[ $tax_ID ] = [
588
+							'name'  => $line_item->name(),
589
+							'rate'  => $line_item->percent(),
590
+							'total' => 0,
591
+							'obj'   => $line_item->OBJ_type(),
592
+							'objID' => $line_item->OBJ_ID(),
593
+						];
594
+					}
595
+					$tax = $this->calculatePercentage($line_item_total, $line_item->percent());
596
+					$non_global_taxes[ $tax_ID ]['total'] += $tax;
597
+				}
598
+			}
599
+		}
600
+		return $non_global_taxes;
601
+	}
602
+
603
+
604
+	/**
605
+	 * @param EE_Line_Item $total_line_item
606
+	 * @param float        $tax_total
607
+	 * @param array        $non_global_taxes array of tax details generated by calculateNonGlobalTaxes()
608
+	 * @return float
609
+	 * @throws EE_Error
610
+	 * @throws ReflectionException
611
+	 */
612
+	private function applyNonGlobalTaxes(
613
+		EE_Line_Item $total_line_item,
614
+		float $tax_total,
615
+		array $non_global_taxes
616
+	): float {
617
+		$global_taxes   = $total_line_item->tax_descendants();
618
+		$taxes_subtotal = EEH_Line_Item::get_taxes_subtotal($total_line_item);
619
+		foreach ($non_global_taxes as $non_global_tax) {
620
+			$found = false;
621
+			foreach ($global_taxes as $global_tax) {
622
+				if (
623
+					$this->validateLineItemAndType($global_tax)
624
+					&& $non_global_tax['obj'] === $global_tax->OBJ_type()
625
+					&& $non_global_tax['objID'] === $global_tax->OBJ_ID()
626
+				) {
627
+					$found = true;
628
+					$new_total = $global_tax->total() + $non_global_tax['total'];
629
+					// add non global tax to matching global tax AND the tax total
630
+					$global_tax->set_total($new_total);
631
+					$global_tax->maybe_save();
632
+					$tax_total += $non_global_tax['total'];
633
+				}
634
+			}
635
+			if (! $found) {
636
+				// add a new line item for this non global tax
637
+				$taxes_subtotal->add_child_line_item(
638
+					EE_Line_Item::new_instance(
639
+						[
640
+							'LIN_name'       => $non_global_tax['name'],
641
+							'LIN_percent'    => $non_global_tax['rate'],
642
+							'LIN_is_taxable' => false,
643
+							'LIN_total'      => $non_global_tax['total'],
644
+							'LIN_type'       => EEM_Line_Item::type_tax,
645
+							'OBJ_type'       => $non_global_tax['obj'],
646
+							'OBJ_ID'         => $non_global_tax['objID'],
647
+						]
648
+					)
649
+				);
650
+				$tax_total += $non_global_tax['total'];
651
+			}
652
+		}
653
+		return $this->decimal_values->roundDecimalValue($tax_total, true);
654
+	}
655
+
656
+
657
+	/**
658
+	 * Gets the total tax on this line item. Assumes taxes have already been calculated using
659
+	 * recalculate_taxes_and_total
660
+	 *
661
+	 * @param EE_Line_Item $line_item
662
+	 * @return float
663
+	 * @throws EE_Error
664
+	 * @throws ReflectionException
665
+	 */
666
+	public function getTotalTax(EE_Line_Item $line_item): float
667
+	{
668
+		$this->validateLineItemAndType($line_item, EEM_Line_Item::type_total);
669
+		$this->recalculateTaxSubTotal($line_item);
670
+		$total = 0;
671
+		foreach ($line_item->tax_descendants() as $tax_line_item) {
672
+			if ($this->validateLineItemAndType($tax_line_item)) {
673
+				$total += $tax_line_item->total();
674
+			}
675
+		}
676
+		return $this->decimal_values->roundDecimalValue($total, true);
677
+	}
678
+
679
+
680
+	/**
681
+	 * Returns the amount taxable among this line item's children (or if it has no children,
682
+	 * how much of it is taxable). Does not recalculate totals or subtotals.
683
+	 * If the taxable total is negative, (eg, if none of the tickets were taxable,
684
+	 * but there is a "Taxable" discount), returns 0.
685
+	 *
686
+	 * @param EE_Line_Item|null $line_item
687
+	 * @return float
688
+	 * @throws EE_Error
689
+	 * @throws ReflectionException
690
+	 */
691
+	public function taxableAmountForGlobalTaxes(?EE_Line_Item $line_item): float
692
+	{
693
+		$total      = 0;
694
+		$child_line_items = $line_item->children($this->default_query_params);
695
+		foreach ($child_line_items as $child_line_item) {
696
+			$this->validateLineItemAndType($child_line_item);
697
+			if ($child_line_item->is_sub_total()) {
698
+				$total += $this->taxableAmountForGlobalTaxes($child_line_item);
699
+			} elseif ($child_line_item->is_line_item() && $child_line_item->is_taxable()) {
700
+				// if it's a percent item, only take into account
701
+				// the percentage that's taxable (the taxable total so far)
702
+				if ($child_line_item->is_percent()) {
703
+					$total += $this->calculatePercentage($total, $child_line_item->percent(), true);
704
+				} else {
705
+					// pretax total will be equal to the total for line items with globally applied taxes
706
+					$pretax_total = $this->calculateTotalForQuantity($child_line_item);
707
+					$total += $this->updatePreTaxTotal($child_line_item, $pretax_total);
708
+				}
709
+			}
710
+		}
711
+		return max($total, 0);
712
+	}
713
+
714
+
715
+	/**
716
+	 * @param EE_Line_Item|null $line_item
717
+	 * @param string|null       $type
718
+	 * @return bool
719
+	 * @throws EE_Error
720
+	 * @throws ReflectionException
721
+	 */
722
+	private function validateLineItemAndType(?EE_Line_Item $line_item, ?string $type = null): bool
723
+	{
724
+		if (! $line_item instanceof EE_Line_Item) {
725
+			throw new DomainException(
726
+				esc_html__('Invalid or missing Line Item supplied .', 'event_espresso')
727
+			);
728
+		}
729
+		if ($type && $line_item->type() !== $type) {
730
+			throw new DomainException(
731
+				sprintf(
732
+					esc_html__(
733
+						'Invalid Line Item type supplied. Received "%1$s" but expected "%2$s".',
734
+						'event_espresso'
735
+					),
736
+					$line_item->type(),
737
+					$type
738
+				)
739
+			);
740
+		}
741
+		return true;
742
+	}
743 743
 }
Please login to merge, or discard this patch.
Spacing   +11 added lines, -11 removed lines patch added patch discarded remove patch
@@ -160,7 +160,7 @@  discard block
 block discarded – undo
160 160
         // for the actual pre-tax sub total line item, we want to save the pretax value for everything
161 161
         if ($line_item->is_sub_total() && $line_item->name() === esc_html__('Pre-Tax Subtotal', 'event_espresso')) {
162 162
             $this->updateTotal($line_item, $pretax_total, true);
163
-        } elseif (! $line_item->is_total()) {
163
+        } elseif ( ! $line_item->is_total()) {
164 164
             // we don't update the total for the total line item, because that will need to include taxes
165 165
             $total = $this->updateTotal($line_item, $total, true);
166 166
         }
@@ -187,7 +187,7 @@  discard block
 block discarded – undo
187 187
         } else {
188 188
             // recursively loop through children and recalculate their totals
189 189
             $children = $line_item->children($this->default_query_params);
190
-            if (! empty($children)) {
190
+            if ( ! empty($children)) {
191 191
                 // reset the total and pretax total to zero since we are recalculating them
192 192
                 $total = $pretax_total = 0;
193 193
                 foreach ($children as $child_line_item) {
@@ -205,7 +205,7 @@  discard block
 block discarded – undo
205 205
                 $pretax_total = $total = $this->calculateTotalForQuantity($line_item);
206 206
             }
207 207
         }
208
-        $total  = $this->updateTotal($line_item, $total, true);
208
+        $total = $this->updateTotal($line_item, $total, true);
209 209
         $pretax_total = $this->updatePreTaxTotal($line_item, $pretax_total, true);
210 210
 
211 211
         // need to also adjust unit price too if the pretax total or quantity has been updated
@@ -489,7 +489,7 @@  discard block
 block discarded – undo
489 489
         $taxable_total = $this->taxableAmountForGlobalTaxes($total_line_item);
490 490
         $global_taxes     = $this->applyGlobalTaxes($total_line_item, $taxable_total);
491 491
         $non_global_taxes = $this->calculateNonGlobalTaxes($total_line_item);
492
-        $all_tax_total        = $this->applyNonGlobalTaxes($total_line_item, $global_taxes, $non_global_taxes);
492
+        $all_tax_total = $this->applyNonGlobalTaxes($total_line_item, $global_taxes, $non_global_taxes);
493 493
         $this->recalculateTaxSubTotal($total_line_item);
494 494
         return $all_tax_total;
495 495
     }
@@ -582,9 +582,9 @@  discard block
 block discarded – undo
582 582
                         $line_item->pretaxTotal()
583 583
                     );
584 584
                 } elseif ($line_item->isSubTax()) {
585
-                    $tax_ID = $line_item->name() . '_' . $line_item->percent();
586
-                    if (! isset($non_global_taxes[ $tax_ID ])) {
587
-                        $non_global_taxes[ $tax_ID ] = [
585
+                    $tax_ID = $line_item->name().'_'.$line_item->percent();
586
+                    if ( ! isset($non_global_taxes[$tax_ID])) {
587
+                        $non_global_taxes[$tax_ID] = [
588 588
                             'name'  => $line_item->name(),
589 589
                             'rate'  => $line_item->percent(),
590 590
                             'total' => 0,
@@ -593,7 +593,7 @@  discard block
 block discarded – undo
593 593
                         ];
594 594
                     }
595 595
                     $tax = $this->calculatePercentage($line_item_total, $line_item->percent());
596
-                    $non_global_taxes[ $tax_ID ]['total'] += $tax;
596
+                    $non_global_taxes[$tax_ID]['total'] += $tax;
597 597
                 }
598 598
             }
599 599
         }
@@ -632,7 +632,7 @@  discard block
 block discarded – undo
632 632
                     $tax_total += $non_global_tax['total'];
633 633
                 }
634 634
             }
635
-            if (! $found) {
635
+            if ( ! $found) {
636 636
                 // add a new line item for this non global tax
637 637
                 $taxes_subtotal->add_child_line_item(
638 638
                     EE_Line_Item::new_instance(
@@ -690,7 +690,7 @@  discard block
 block discarded – undo
690 690
      */
691 691
     public function taxableAmountForGlobalTaxes(?EE_Line_Item $line_item): float
692 692
     {
693
-        $total      = 0;
693
+        $total = 0;
694 694
         $child_line_items = $line_item->children($this->default_query_params);
695 695
         foreach ($child_line_items as $child_line_item) {
696 696
             $this->validateLineItemAndType($child_line_item);
@@ -721,7 +721,7 @@  discard block
 block discarded – undo
721 721
      */
722 722
     private function validateLineItemAndType(?EE_Line_Item $line_item, ?string $type = null): bool
723 723
     {
724
-        if (! $line_item instanceof EE_Line_Item) {
724
+        if ( ! $line_item instanceof EE_Line_Item) {
725 725
             throw new DomainException(
726 726
                 esc_html__('Invalid or missing Line Item supplied .', 'event_espresso')
727 727
             );
Please login to merge, or discard this patch.