Completed
Pull Request — master (#10259)
by Mike
20:49
created

WC_Abstract_Order::set_billing_email()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 3

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 4
rs 10
cc 2
eloc 3
nc 2
nop 1
1
<?php
1 ignored issue
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 19 and the first side effect is on line 3.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
if ( ! defined( 'ABSPATH' ) ) {
3
	exit;
4
}
5
6
/**
7
 * Abstract Order
8
 *
9
 * Handles order data and database interaction.
10
 *
11
 * @class       WC_Abstract_Order
12
 * @version     2.6.0
13
 * @package     WooCommerce/Classes
14
 * @category    Class
15
 * @author      WooThemes
16
 *
17
 * @todo check date formats are bw compat and consistant
18
 */
19
abstract class WC_Abstract_Order implements WC_Data {
20
21
    /**
22
     * Data array, with defaults.
23
     *
24
     * @todo when migrating to custom tables, these will be columns
25
     * @since 2.6.0
26
     * @var array
27
     */
28
    protected $_data = array(
29
		'order_id'             => 0,
30
        'parent_id'            => 0,
31
		'status'               => '',
32
        /**
33
         * @todo confusion. Was 'simple'. But this was not the same as post_type, which is shop_order. Other post types in core are shop_order_refund
34
         * The order type for shop_order_refund is refund.
35
         * Why do we need two separate variables? This should be unified, especially once this is in a custom table and post_type is redundent.
36
         * Switching to 'shop_order', and then using this value in the order factory instead of post_type. @thenbrent might have feedback on this.
37
         */
38
		'order_type'           => 'shop_order',
39
		'order_key'            => '',
40
		'order_currency'       => '',
41
		'date_created'         => '',
42
		'date_modified'        => '',
43
		'customer_id'          => 0,
44
		'billing_first_name'   => '',
45
		'billing_last_name'    => '',
46
		'billing_company'      => '',
47
		'billing_address_1'    => '',
48
		'billing_address_2'    => '',
49
		'billing_city'         => '',
50
		'billing_state'        => '',
51
		'billing_postcode'     => '',
52
		'billing_country'      => '',
53
		'billing_email'        => '',
54
		'billing_phone'        => '',
55
		'shipping_first_name'  => '',
56
		'shipping_last_name'   => '',
57
		'shipping_company'     => '',
58
		'shipping_address_1'   => '',
59
		'shipping_address_2'   => '',
60
		'shipping_city'        => '',
61
		'shipping_state'       => '',
62
		'shipping_postcode'    => '',
63
		'shipping_country'     => '',
64
		'discount_total'       => 0,
65
		'discount_tax'         => 0,
66
		'shipping_total'       => 0,
67
		'shipping_tax'         => 0,
68
		'cart_tax'             => 0, // cart_tax is the new name for the legacy 'order_tax' which is the tax for items only, not shipping.
69
		'order_total'          => 0,
70
		'order_tax'            => 0, // Sum of all taxes.
71
    );
72
73
    /**
74
     * Stores meta data.
75
     * @var array
76
     */
77
    protected $_meta = array(
78
        'payment_method'       => '',
79
		'payment_method_title' => '',
80
		'transaction_id'       => '',
81
		'customer_ip_address'  => '',
82
		'customer_user_agent'  => '',
83
		'created_via'          => '',
84
		'order_version'        => '',
85
		'prices_include_tax'   => false,
86
		'customer_note'        => '',
87
		'date_completed'       => '',
88
		'date_paid'            => '',
89
    );
90
91
    /**
92
     * Stores data about status changes so relevant hooks can be fired.
93
     * @var bool|array
94
     */
95
    protected $_status_transition = false;
96
97
    /**
98
     * Get the order if ID is passed, otherwise the order is new and empty.
99
     * This class should NOT be instantiated, but the get_order function or new WC_Order_Factory.
100
     * should be used. It is possible, but the aforementioned are preferred and are the only.
101
     * methods that will be maintained going forward.
102
     *
103
     * @param  int|object|WC_Order $order Order to init.
104
     */
105 View Code Duplication
    public function __construct( $order = 0 ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
106
		if ( is_numeric( $order ) ) {
107
            $this->read( $order );
108
        } elseif ( $order instanceof WC_Order ) {
109
            $this->read( absint( $order->get_id() ) );
110
        } elseif ( ! empty( $order->ID ) ) {
111
            $this->read( absint( $order->ID ) );
112
        }
113
    }
114
115
    /**
116
     * Change data to JSON format.
117
     * @return string Data in JSON format.
118
     */
119
    public function __toString() {
120
        return json_encode( $this->get_data() );
121
    }
122
123
    public function add_meta_data(){} // @todo
124
    public function get_meta_data(){} // @todo
125
    public function get_order_meta( $key ){} // @todo
0 ignored issues
show
Unused Code introduced by
The parameter $key is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
126
127
    /*
128
    |--------------------------------------------------------------------------
129
    | Getters
130
    |--------------------------------------------------------------------------
131
    |
132
    | Methods for getting data from the order object.
133
    |
134
    */
135
136
    /**
137
     * Get all class data in array format.
138
     * @since 2.6.0
139
     * @return array
140
     */
141
    public function get_data() {
142
        return array_merge(
143
            $this->_data,
144
            $this->_meta,
145
            array(
146
                'line_items'     => $this->get_items( 'line_item' ),
147
                'tax_lines'      => $this->get_items( 'tax' ),
148
                'shipping_lines' => $this->get_items( 'shipping' ),
149
                'fee_lines'      => $this->get_items( 'fee' ),
150
                'coupon_lines'   => $this->get_items( 'coupon' ),
151
            )
152
        );
153
    }
154
155
    /**
156
     * Get order ID.
157
     * @since 2.6.0
158
     * @return integer
159
     */
160
    public function get_id() {
161
        return $this->get_order_id();
162
    }
163
164
    /**
165
     * Get order ID.
166
     * @since 2.6.0
167
     * @return integer
168
     */
169
    public function get_order_id() {
170
        return absint( $this->_data['order_id'] );
171
    }
172
173
    /**
174
     * Get parent order ID.
175
     * @since 2.6.0
176
     * @return integer
177
     */
178
    public function get_parent_id() {
179
        return absint( $this->_data['parent_id'] );
180
    }
181
182
    /**
183
     * get_order_number function.
184
     *
185
     * Gets the order number for display (by default, order ID).
186
     *
187
     * @return string
188
     */
189
    public function get_order_number() {
190
        return apply_filters( 'woocommerce_order_number', $this->get_id(), $this );
191
    }
192
193
    /**
194
     * Get order key.
195
     * @since 2.6.0
196
     * @return string
197
     */
198
    public function get_order_key() {
199
        return $this->_data['order_key'];
200
    }
201
202
    /**
203
     * Gets order currency.
204
     * @return string
205
     */
206
    public function get_order_currency() {
207
        return apply_filters( 'woocommerce_get_order_currency', $this->_data['order_currency'], $this );
208
    }
209
210
    /**
211
     * Get Order Type
212
     * @return string
213
     */
214
    public function get_order_type() {
215
        return $this->_data['order_type'];
216
    }
217
218
    /**
219
     * Get date_created
220
     * @return string
221
     */
222
    public function get_date_created() {
223
        return $this->_data['date_created'];
224
    }
225
226
    /**
227
     * Get date_modified
228
     * @return string
229
     */
230
    public function get_date_modified() {
231
        return $this->_data['date_modified'];
232
    }
233
234
    /**
235
     * Get date_completed
236
     * @return string
237
     */
238
    public function get_date_completed() {
239
        return $this->_data['date_completed'];
240
    }
241
242
	/**
243
     * Get date_paid
244
     * @return string
245
     */
246
    public function get_date_paid() {
247
        return $this->_data['date_paid'];
248
    }
249
250
    /**
251
     * Get customer_id
252
     * @return int
253
     */
254
    public function get_customer_id() {
255
        return absint( $this->_data['customer_id'] );
256
    }
257
258
    /**
259
     * Get billing_first_name
260
     * @return string
261
     */
262
    public function get_billing_first_name() {
263
        return $this->_data['billing_first_name'];
264
    }
265
266
    /**
267
     * Get billing_last_name
268
     * @return string
269
     */
270
    public function get_billing_last_name() {
271
        return $this->_data['billing_last_name'];
272
    }
273
274
    /**
275
     * Get billing_company
276
     * @return string
277
     */
278
    public function get_billing_company() {
279
        return $this->_data['billing_company'];
280
    }
281
282
    /**
283
     * Get billing_address_1
284
     * @return string
285
     */
286
    public function get_billing_address_1() {
287
        return $this->_data['billing_address_1'];
288
    }
289
290
    /**
291
     * Get billing_address_2
292
     * @return string $value
293
     */
294
    public function get_billing_address_2() {
295
        return $this->_data['billing_address_2'];
296
    }
297
298
    /**
299
     * Get billing_city
300
     * @return string $value
301
     */
302
    public function get_billing_city() {
303
        return $this->_data['billing_city'];
304
    }
305
306
    /**
307
     * Get billing_state
308
     * @return string
309
     */
310
    public function get_billing_state() {
311
        return $this->_data['billing_state'];
312
    }
313
314
    /**
315
     * Get billing_postcode
316
     * @return string
317
     */
318
    public function get_billing_postcode() {
319
        return $this->_data['billing_postcode'];
320
    }
321
322
    /**
323
     * Get billing_country
324
     * @return string
325
     */
326
    public function get_billing_country() {
327
        return $this->_data['billing_country'];
328
    }
329
330
    /**
331
     * Get billing_email
332
     * @return string
333
     */
334
    public function get_billing_email() {
335
        return sanitize_email( $this->_data['billing_email'] );
336
    }
337
338
    /**
339
     * Get billing_phone
340
     * @return string
341
     */
342
    public function get_billing_phone() {
343
        return $this->_data['billing_phone'];
344
    }
345
346
    /**
347
     * Get shipping_first_name
348
     * @return string
349
     */
350
    public function get_shipping_first_name() {
351
        return $this->_data['shipping_first_name'];
352
    }
353
354
    /**
355
     * Get shipping_last_name
356
     * @return string
357
     */
358
    public function get_shipping_last_name() {
359
         return $this->_data['shipping_last_name'];
360
    }
361
362
    /**
363
     * Get shipping_company
364
     * @return string
365
     */
366
    public function get_shipping_company() {
367
        return $this->_data['shipping_company'];
368
    }
369
370
    /**
371
     * Get shipping_address_1
372
     * @return string
373
     */
374
    public function get_shipping_address_1() {
375
        return $this->_data['shipping_address_1'];
376
    }
377
378
    /**
379
     * Get shipping_address_2
380
     * @return string
381
     */
382
    public function get_shipping_address_2() {
383
        return $this->_data['shipping_address_2'];
384
    }
385
386
    /**
387
     * Get shipping_city
388
     * @return string
389
     */
390
    public function get_shipping_city() {
391
        return $this->_data['shipping_city'];
392
    }
393
394
    /**
395
     * Get shipping_state
396
     * @return string
397
     */
398
    public function get_shipping_state() {
399
        return $this->_data['shipping_state'];
400
    }
401
402
    /**
403
     * Get shipping_postcode
404
     * @return string
405
     */
406
    public function get_shipping_postcode() {
407
        return $this->_data['shipping_postcode'];
408
    }
409
410
    /**
411
     * Get shipping_country
412
     * @return string
413
     */
414
    public function get_shipping_country() {
415
        return $this->_data['shipping_country'];
416
    }
417
418
    /**
419
     * Get the payment method.
420
     * @return string
421
     */
422
    public function get_payment_method() {
423
        return $this->_data['payment_method'];
424
    }
425
426
    /**
427
     * Get payment_method_title
428
     * @return string
429
     */
430
    public function get_payment_method_title() {
431
        return $this->_data['payment_method_title'];
432
    }
433
434
    /**
435
     * Get transaction_id
436
     * @return string
437
     */
438
    public function get_transaction_id() {
439
        return $this->_data['transaction_id'];
440
    }
441
442
    /**
443
     * Get customer_ip_address
444
     * @return string
445
     */
446
    public function get_customer_ip_address() {
447
        return $this->_data['customer_ip_address'];
448
    }
449
450
    /**
451
     * Get customer_user_agent
452
     * @return string
453
     */
454
    public function get_customer_user_agent() {
455
        return $this->_data['customer_user_agent'];
456
    }
457
458
    /**
459
     * Get created_via
460
     * @return string
461
     */
462
    public function get_created_via() {
463
        return $this->_data['created_via'];
464
    }
465
466
    /**
467
     * Get order_version
468
     * @return string
469
     */
470
    public function get_order_version() {
471
        return $this->_data['order_version'];
472
    }
473
474
    /**
475
     * Get prices_include_tax
476
     * @return bool
477
     */
478
    public function get_prices_include_tax() {
479
        return (bool) $this->_data['prices_include_tax'];
480
    }
481
482
    /**
483
     * Get customer_note
484
     * @return string
485
     */
486
    public function get_customer_note() {
487
        return $this->_data['customer_note'];
488
    }
489
490
    /**
491
     * Returns the requested address in raw, non-formatted way.
492
     * @since  2.4.0
493
     * @param  string $type Billing or shipping. Anything else besides 'billing' will return shipping address.
494
     * @return array The stored address after filter.
495
     */
496
    public function get_address( $type = 'billing' ) {
497
        if ( 'billing' === $type ) {
498
            $address = array(
499
                'first_name' => $this->get_billing_first_name(),
500
                'last_name'  => $this->get_billing_last_name(),
501
                'company'    => $this->get_billing_company(),
502
                'address_1'  => $this->get_billing_address_1(),
503
                'address_2'  => $this->get_billing_address_2(),
504
                'city'       => $this->get_billing_city(),
505
                'state'      => $this->get_billing_state(),
506
                'postcode'   => $this->get_billing_postcode(),
507
                'country'    => $this->get_billing_country(),
508
                'email'      => $this->get_billing_email(),
509
                'phone'      => $this->get_billing_phone()
510
            );
511
        } else {
512
            $address = array(
513
                'first_name' => $this->get_shipping_first_name(),
514
                'last_name'  => $this->get_shipping_last_name(),
515
                'company'    => $this->get_shipping_company(),
516
                'address_1'  => $this->get_shipping_address_1(),
517
                'address_2'  => $this->get_shipping_address_2(),
518
                'city'       => $this->get_shipping_city(),
519
                'state'      => $this->get_shipping_state(),
520
                'postcode'   => $this->get_shipping_postcode(),
521
                'country'    => $this->get_shipping_country()
522
            );
523
        }
524
        return apply_filters( 'woocommerce_get_order_address', $address, $type, $this );
525
    }
526
527
    /**
528
     * Return the order statuses without wc- internal prefix.
529
     * @return string
530
     */
531
    public function get_status() {
532
        return apply_filters( 'woocommerce_order_get_status', 'wc-' === substr( $this->_data['status'], 0, 3 ) ? substr( $this->_data['status'], 3 ) : $this->_data['status'], $this );
533
    }
534
535
    /**
536
     * Alias for get_customer_id().
537
     * @since  2.2
538
     * @return int
539
     */
540
    public function get_user_id() {
541
        return $this->get_customer_id();
542
    }
543
544
    /**
545
     * Get the user associated with the order. False for guests.
546
     *
547
     * @since  2.2
548
     * @return WP_User|false
549
     */
550
    public function get_user() {
551
        return $this->get_user_id() ? get_user_by( 'id', $this->get_user_id() ) : false;
552
    }
553
554
    /**
555
     * Get a formatted billing address for the order.
556
     * @return string
557
     */
558
    public function get_formatted_billing_address() {
559
        return WC()->countries->get_formatted_address( apply_filters( 'woocommerce_order_formatted_billing_address', $this->get_address( 'billing' ), $this ) );
560
    }
561
562
    /**
563
     * Get a formatted shipping address for the order.
564
     * @return string
565
     */
566
    public function get_formatted_shipping_address() {
567
        if ( $this->get_shipping_address_1() || $this->get_shipping_address_2() ) {
568
            return WC()->countries->get_formatted_address( apply_filters( 'woocommerce_order_formatted_shipping_address', $this->get_address( 'shipping' ), $this ) );
569
        } else {
570
            return '';
571
        }
572
    }
573
574
    /**
575
     * Get a formatted shipping address for the order.
576
     *
577
     * @return string
578
     */
579
    public function get_shipping_address_map_url() {
580
        $address = apply_filters( 'woocommerce_shipping_address_map_url_parts', array(
581
            'address_1' => $this->get_shipping_address_1(),
582
            'address_2' => $this->get_shipping_address_2(),
583
            'city'      => $this->get_shipping_city(),
584
            'state'     => $this->get_shipping_state(),
585
            'postcode'  => $this->get_shipping_postcode(),
586
            'country'   => $this->get_shipping_country()
587
        ), $this );
588
        return apply_filters( 'woocommerce_shipping_address_map_url', 'http://maps.google.com/maps?&q=' . urlencode( implode( ', ', $address ) ) . '&z=16', $this );
589
    }
590
591
    /**
592
     * Get a formatted billing full name.
593
     *
594
     * @since 2.4.0
595
     *
596
     * @return string
597
     */
598
    public function get_formatted_billing_full_name() {
599
        return sprintf( _x( '%1$s %2$s', 'full name', 'woocommerce' ),  $this->get_billing_first_name(), $this->get_billing_last_name() );
600
    }
601
602
    /**
603
     * Get a formatted shipping full name.
604
     *
605
     * @since 2.4.0
606
     *
607
     * @return string
608
     */
609
    public function get_formatted_shipping_full_name() {
610
        return sprintf( _x( '%1$s %2$s', 'full name', 'woocommerce' ),  $this->get_shipping_first_name(), $this->get_shipping_last_name() );
611
    }
612
613
    /**
614
     * Get discount_total
615
     * @return string
616
     */
617
    public function get_discount_total() {
618
        $discount_total = wc_format_decimal( $this->_data['discount_total'] );
619
620
        // Backwards compatible total calculation - totals were not stored consistently in old versions.
621
        if ( ( ! $this->get_order_version() || version_compare( $this->get_order_version(), '2.3.7', '<' ) ) && $this->get_prices_include_tax() ) {
622
            $discount_total = $discount_total - $this->get_discount_tax();
623
        }
624
625
        return $discount_total;
626
    }
627
628
    /**
629
     * Get discount_tax
630
     * @return string
631
     */
632
    public function get_discount_tax() {
633
        return wc_format_decimal( $this->_data['discount_tax'] );
634
    }
635
636
    /**
637
     * Get shipping_total
638
     * woocommerce_order_amount_total_shipping filter has been removed to avoid
639
     * these values being modified and then saved back to the DB. There are
640
     * other, later hooks available to change totals on display. e.g.
641
     * woocommerce_get_order_item_totals.
642
     * @return string
643
     */
644
    public function get_shipping_total() {
645
        return wc_format_decimal( $this->_data['shipping_total'] );
646
    }
647
648
    /**
649
     * Gets cart tax amount.
650
     *
651
     * @since 2.6.0 woocommerce_order_amount_cart_tax filter has been removed to avoid
652
     * these values being modified and then saved back to the DB or used in
653
     * calculations. There are other, later hooks available to change totals on
654
     * display. e.g. woocommerce_get_order_item_totals.
655
     * @return float
656
     */
657
    public function get_cart_tax() {
658
        return wc_format_decimal( $this->_data['cart_tax'] );
659
    }
660
661
    /**
662
     * Get shipping_tax.
663
     *
664
     * @since 2.6.0 woocommerce_order_amount_shipping_tax filter has been removed to avoid
665
     * these values being modified and then saved back to the DB or used in
666
     * calculations. There are other, later hooks available to change totals on
667
     * display. e.g. woocommerce_get_order_item_totals.
668
     * @return string
669
     */
670
    public function get_shipping_tax() {
671
        return wc_format_decimal( $this->_data['shipping_tax'] );
672
    }
673
674
    /**
675
     * Order tax is the sum of all taxes.
676
     * @return string
677
     */
678
    public function get_order_tax() {
679
        return wc_round_tax_total( $this->_data['order_tax'] );
680
    }
681
682
    /**
683
     * Get the stored order total. Includes taxes and everything else.
684
     * @return string
685
     */
686
    public function get_order_total() {
687
        return wc_format_decimal( $this->_data['order_total'], wc_get_price_decimals() );
688
    }
689
690
    /**
691
     * Gets the total discount amount.
692
     * @param  bool $ex_tax Show discount excl any tax.
693
     * @return float
694
     */
695
    public function get_total_discount( $ex_tax = true ) {
696
        if ( $ex_tax ) {
697
            $total_discount = $this->get_discount_total();
698
        } else {
699
            $total_discount = $this->get_discount_total() + $this->get_discount_tax();
700
        }
701
        return apply_filters( 'woocommerce_order_amount_total_discount', round( $total_discount, WC_ROUNDING_PRECISION ), $this );
702
    }
703
704
    /**
705
     * Get total tax amount. Alias for get_order_tax().
706
     *
707
     * @since 2.6.0 woocommerce_order_amount_total_tax filter has been removed to avoid
708
     * these values being modified and then saved back to the DB. There are
709
     * other, later hooks available to change totals on display. e.g.
710
     * woocommerce_get_order_item_totals.
711
     * @return float
712
     */
713
    public function get_total_tax() {
714
        return $this->get_order_tax();
715
    }
716
717
    /**
718
     * Gets shipping total. Alias of WC_Order::get_shipping_total().
719
     *
720
     * @since 2.6.0 woocommerce_order_amount_total_shipping filter has been removed to avoid
721
     * these values being modified and then saved back to the DB or used in
722
     * calculations. There are other, later hooks available to change totals on
723
     * display. e.g. woocommerce_get_order_item_totals.
724
     * @return float
725
     */
726
    public function get_total_shipping() {
727
        return $this->get_shipping_total();
728
    }
729
730
    /**
731
     * Gets order grand total. incl. taxes. Used in gateways. Filtered.
732
     * @return float
733
     */
734
    public function get_total() {
735
        return apply_filters( 'woocommerce_order_amount_total', $this->get_order_total(), $this );
736
    }
737
738
    /**
739
     * Gets order subtotal.
740
     * @return float
741
     */
742
    public function get_subtotal() {
743
        $subtotal = 0;
744
745
        foreach ( $this->get_items() as $item ) {
746
            $subtotal += isset( $item['line_subtotal'] ) ? $item['line_subtotal'] : 0;
747
        }
748
749
        return apply_filters( 'woocommerce_order_amount_subtotal', (double) $subtotal, $this );
750
    }
751
752
    /**
753
     * Get taxes, merged by code, formatted ready for output.
754
     *
755
     * @return array
756
     */
757
    public function get_tax_totals() {
758
        $tax_totals = array();
759
760
        foreach ( $this->get_items( 'tax' ) as $key => $tax ) {
761
            $code = $tax[ 'name' ];
762
763 View Code Duplication
            if ( ! isset( $tax_totals[ $code ] ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
764
                $tax_totals[ $code ] = new stdClass();
765
                $tax_totals[ $code ]->amount = 0;
766
            }
767
768
            $tax_totals[ $code ]->id                = $key;
769
            $tax_totals[ $code ]->rate_id           = $tax['rate_id'];
770
            $tax_totals[ $code ]->is_compound       = $tax[ 'compound' ];
771
            $tax_totals[ $code ]->label             = isset( $tax[ 'label' ] ) ? $tax[ 'label' ] : $tax[ 'name' ];
772
            $tax_totals[ $code ]->amount           += $tax[ 'tax_amount' ] + $tax[ 'shipping_tax_amount' ];
773
            $tax_totals[ $code ]->formatted_amount  = wc_price( wc_round_tax_total( $tax_totals[ $code ]->amount ), array('currency' => $this->get_order_currency()) );
774
        }
775
776
        return apply_filters( 'woocommerce_order_tax_totals', $tax_totals, $this );
777
    }
778
779
    /**
780
     * Gets formatted shipping method title.
781
     * @return string
782
     */
783
    public function get_shipping_method() {
784
        $names = array();
785
        foreach ( $this->get_shipping_methods() as $shipping_method ) {
786
            $names[] = $shipping_method->get_name();
787
        }
788
        return apply_filters( 'woocommerce_order_shipping_method', implode( ', ', $names ), $this );
789
    }
790
791
    /*
792
    |--------------------------------------------------------------------------
793
    | Setters
794
    |--------------------------------------------------------------------------
795
    |
796
    | Functions for setting order data. These should not update anything in the
797
    | database itself and should only change what is stored in the class
798
    | object. However, for backwards compatibility pre 2.6.0 some of these
799
    | setters may handle both.
800
    |
801
    */
802
803
    /**
804
     * Set order ID.
805
     * @since 2.6.0
806
     * @param int $value
807
     */
808
    public function set_order_id( $value ) {
809
        $this->_data['order_id'] = absint( $value );
810
    }
811
812
    /**
813
     * Set parent order ID.
814
     * @since 2.6.0
815
     * @param int $value
816
     */
817
    public function set_parent_id( $value ) {
818
        $this->_data['parent_id'] = absint( $value );
819
    }
820
821
    /**
822
     * Set order status.
823
     * @since 2.6.0
824
     * @param string $new_status Status to change the order to. No internal wc- prefix is required.
825
     * @param string $note (default: '') Optional note to add.
826
     * @param bool $manual is this a manual order status change?
0 ignored issues
show
Documentation introduced by
There is no parameter named $manual. Did you maybe mean $manual_update?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
827
     */
828
    public function set_status( $new_status, $note = '', $manual_update = false ) {
829
        // Remove prefixes and standardize
830
        $current_status = $this->get_status();
831
        $new_status     = 'wc-' === substr( $new_status, 0, 3 ) ? substr( $new_status, 3 ) : $new_status;
832
833
        if ( in_array( 'wc-' . $new_status, array_keys( wc_get_order_statuses() ) ) && $new_status !== $current_status ) {
834
            if ( ! empty( $current_status ) ) {
835
                $this->_status_transition = array(
836
                    'original' => ! empty( $this->_status_transition['original'] ) ? $this->_status_transition['original'] : $current_status,
837
                    'note'     => $note ? $note : '',
838
                    'manual'   => (bool) $manual_update
839
                );
840
				if ( 'completed' === $new_status ) {
841
					$this->set_date_completed( current_time( 'timestamp' ) );
842
				}
843
            }
844
            $this->_data['status'] = 'wc-' . $new_status;
845
        }
846
    }
847
848
	/**
849
     * Updates status of order immediately.
850
     * @uses WC_Order::set_status()
851
     */
852
    public function update_status( $new_status, $note = '', $manual = false ) {
853
        if ( ! $this->get_id() ) {
854
            return false;
855
        }
856
		$this->set_status( $new_status, $note, $manual );
857
		$this->save();
858
        return true;
859
    }
860
861
    /**
862
     * Set Order Type
863
     * @param string $value
864
     */
865
    public function set_order_type( $value ) {
866
        $this->_data['order_type'] = $value;
867
    }
868
869
    /**
870
     * Set order_key
871
     * @param string $value
872
     */
873
    public function set_order_key( $value ) {
874
        $this->_data['order_key'] = $value;
875
    }
876
877
    /**
878
     * Set order_currency
879
     * @param string $value
880
     */
881
    public function set_order_currency( $value ) {
882
        $this->_data['order_currency'] = $value;
883
    }
884
885
    /**
886
     * Set date_created
887
     * @param string $timestamp Timestamp
888
     */
889
    public function set_date_created( $timestamp ) {
890
        $this->_data['date_created'] = is_numeric( $timestamp ) ? $timestamp : strtotime( $timestamp );
891
    }
892
893
    /**
894
     * Set date_modified
895
     * @param string $timestamp
896
     */
897
    public function set_date_modified( $timestamp ) {
898
        $this->_data['date_modified'] = is_numeric( $timestamp ) ? $timestamp : strtotime( $timestamp );
899
    }
900
901
    /**
902
     * Set date_completed
903
     * @param string $timestamp
904
     */
905
    public function set_date_completed( $timestamp ) {
906
        $this->_data['date_completed'] = is_numeric( $timestamp ) ? $timestamp : strtotime( $timestamp );
907
    }
908
909
	/**
910
     * Set date_paid
911
     * @param string $timestamp
912
     */
913
    public function set_date_paid( $timestamp ) {
914
        $this->_data['date_paid'] = is_numeric( $timestamp ) ? $timestamp : strtotime( $timestamp );
915
    }
916
917
    /**
918
     * Set customer_id
919
     * @param int $value
920
     */
921
    public function set_customer_id( $value ) {
922
        $this->_data['customer_id'] = absint( $value );
923
    }
924
925
    /**
926
     * Set billing_first_name
927
     * @param string $value
928
     */
929
    public function set_billing_first_name( $value ) {
930
        $this->_data['billing_first_name'] = $value;
931
    }
932
933
    /**
934
     * Set billing_last_name
935
     * @param string $value
936
     */
937
    public function set_billing_last_name( $value ) {
938
        $this->_data['billing_last_name'] = $value;
939
    }
940
941
    /**
942
     * Set billing_company
943
     * @param string $value
944
     */
945
    public function set_billing_company( $value ) {
946
        $this->_data['billing_company'] = $value;
947
    }
948
949
    /**
950
     * Set billing_address_1
951
     * @param string $value
952
     */
953
    public function set_billing_address_1( $value ) {
954
        $this->_data['billing_address_1'] = $value;
955
    }
956
957
    /**
958
     * Set billing_address_2
959
     * @param string $value
960
     */
961
    public function set_billing_address_2( $value ) {
962
        $this->_data['billing_address_2'] = $value;
963
    }
964
965
    /**
966
     * Set billing_city
967
     * @param string $value
968
     */
969
    public function set_billing_city( $value ) {
970
        $this->_data['billing_city'] = $value;
971
    }
972
973
    /**
974
     * Set billing_state
975
     * @param string $value
976
     */
977
    public function set_billing_state( $value ) {
978
        $this->_data['billing_state'] = $value;
979
    }
980
981
    /**
982
     * Set billing_postcode
983
     * @param string $value
984
     */
985
    public function set_billing_postcode( $value ) {
986
        $this->_data['billing_postcode'] = $value;
987
    }
988
989
    /**
990
     * Set billing_country
991
     * @param string $value
992
     */
993
    public function set_billing_country( $value ) {
994
        $this->_data['billing_country'] = $value;
995
    }
996
997
    /**
998
     * Set billing_email
999
     * @param string $value
1000
     */
1001
    public function set_billing_email( $value ) {
1002
		$value = sanitize_email( $value );
1003
        $this->_data['billing_email'] = is_email( $value ) ? $value : '';
1004
    }
1005
1006
    /**
1007
     * Set billing_phone
1008
     * @param string $value
1009
     */
1010
    public function set_billing_phone( $value ) {
1011
        $this->_data['billing_phone'] = $value;
1012
    }
1013
1014
    /**
1015
     * Set shipping_first_name
1016
     * @param string $value
1017
     */
1018
    public function set_shipping_first_name( $value ) {
1019
        $this->_data['shipping_first_name'] = $value;
1020
    }
1021
1022
    /**
1023
     * Set shipping_last_name
1024
     * @param string $value
1025
     */
1026
    public function set_shipping_last_name( $value ) {
1027
        $this->_data['shipping_last_name'] = $value;
1028
    }
1029
1030
    /**
1031
     * Set shipping_company
1032
     * @param string $value
1033
     */
1034
    public function set_shipping_company( $value ) {
1035
        $this->_data['shipping_company'] = $value;
1036
    }
1037
1038
    /**
1039
     * Set shipping_address_1
1040
     * @param string $value
1041
     */
1042
    public function set_shipping_address_1( $value ) {
1043
        $this->_data['shipping_address_1'] = $value;
1044
    }
1045
1046
    /**
1047
     * Set shipping_address_2
1048
     * @param string $value
1049
     */
1050
    public function set_shipping_address_2( $value ) {
1051
        $this->_data['shipping_address_2'] = $value;
1052
    }
1053
1054
    /**
1055
     * Set shipping_city
1056
     * @param string $value
1057
     */
1058
    public function set_shipping_city( $value ) {
1059
        $this->_data['shipping_city'] = $value;
1060
    }
1061
1062
    /**
1063
     * Set shipping_state
1064
     * @param string $value
1065
     */
1066
    public function set_shipping_state( $value ) {
1067
        $this->_data['shipping_state'] = $value;
1068
    }
1069
1070
    /**
1071
     * Set shipping_postcode
1072
     * @param string $value
1073
     */
1074
    public function set_shipping_postcode( $value ) {
1075
        $this->_data['shipping_postcode'] = $value;
1076
    }
1077
1078
    /**
1079
     * Set shipping_country
1080
     * @param string $value
1081
     */
1082
    public function set_shipping_country( $value ) {
1083
        $this->_data['shipping_country'] = $value;
1084
    }
1085
1086
    /**
1087
     * Set discount_total
1088
     * @param string $value
1089
     */
1090
    public function set_discount_total( $value ) {
1091
        $this->_data['discount_total'] = wc_format_decimal( $value );
1092
    }
1093
1094
    /**
1095
     * Set discount_tax
1096
     * @param string $value
1097
     */
1098
    public function set_discount_tax( $value ) {
1099
        $this->_data['discount_tax'] = wc_format_decimal( $value );
1100
    }
1101
1102
    /**
1103
     * Set shipping_total
1104
     * @param string $value
1105
     */
1106
    public function set_shipping_total( $value ) {
1107
        $this->_data['shipping_total'] = wc_format_decimal( $value );
1108
    }
1109
1110
    /**
1111
     * Set shipping_tax
1112
     * @param string $value
1113
     */
1114
    public function set_shipping_tax( $value ) {
1115
        $this->_data['shipping_tax'] = wc_format_decimal( $value );
1116
        $this->set_order_tax( $this->get_cart_tax() + $this->get_shipping_tax() );
1117
    }
1118
1119
    /**
1120
     * Set cart tax
1121
     * @param string $value
1122
     */
1123
    public function set_cart_tax( $value ) {
1124
        $this->_data['cart_tax'] = wc_format_decimal( $value );
1125
        $this->set_order_tax( $this->get_cart_tax() + $this->get_shipping_tax() );
1126
    }
1127
1128
    /**
1129
     * Sets order tax (sum of cart and shipping tax). Used internaly only.
1130
     * @access protected
1131
     * @param string $value
1132
     */
1133
    protected function set_order_tax( $value ) {
1134
        $this->_data['order_tax'] = wc_format_decimal( $value );
1135
    }
1136
1137
    /**
1138
     * Set order_total
1139
     * @param string $value
1140
     */
1141
    public function set_order_total( $value ) {
1142
        $this->_data['order_total'] = wc_format_decimal( $value, wc_get_price_decimals() );
1143
    }
1144
1145
    /**
1146
     * Set the payment method ID.
1147
     * @since 2.2.0
1148
     * @param string $value Supports WC_Payment_Gateway for bw compatibility with < 2.6
1149
     */
1150
    public function set_payment_method( $value ) {
1151
        if ( is_object( $value ) ) {
1152
            update_post_meta( $this->get_id(), '_payment_method', $value->id );
1153
            update_post_meta( $this->get_id(), '_payment_method_title', $value->get_title() );
1154
            $this->set_payment_method( $value->id );
1155
            $this->set_payment_method_title( $value->get_title() );
1156
        } else {
1157
            $this->_data['payment_method'] = $value;
1158
        }
1159
    }
1160
1161
    /**
1162
     * Set payment_method_title
1163
     * @param string $value
1164
     */
1165
    public function set_payment_method_title( $value ) {
1166
        $this->_data['payment_method_title'] = $value;
1167
    }
1168
1169
    /**
1170
     * Set transaction_id
1171
     * @param string $value
1172
     */
1173
    public function set_transaction_id( $value ) {
1174
        $this->_data['transaction_id'] = $value;
1175
    }
1176
1177
    /**
1178
     * Set customer_ip_address
1179
     * @param string $value
1180
     */
1181
    public function set_customer_ip_address( $value ) {
1182
        $this->_data['customer_ip_address'] = $value;
1183
    }
1184
1185
    /**
1186
     * Set customer_user_agent
1187
     * @param string $value
1188
     */
1189
    public function set_customer_user_agent( $value ) {
1190
        $this->_data['customer_user_agent'] = $value;
1191
    }
1192
1193
    /**
1194
     * Set created_via
1195
     * @param string $value
1196
     */
1197
    public function set_created_via( $value ) {
1198
        $this->_data['created_via'] = $value;
1199
    }
1200
1201
    /**
1202
     * Set order_version
1203
     * @param string $value
1204
     */
1205
    public function set_order_version( $value ) {
1206
        $this->_data['order_version'] = $value;
1207
    }
1208
1209
    /**
1210
     * Set prices_include_tax
1211
     * @param bool $value
1212
     */
1213
    public function set_prices_include_tax( $value ) {
1214
        $this->_data['prices_include_tax'] = (bool) $value;
1215
    }
1216
1217
    /**
1218
     * Set customer_note
1219
     * @param string $value
1220
     */
1221
    public function set_customer_note( $value ) {
1222
        $this->_data['customer_note'] = $value;
1223
    }
1224
1225
    /**
1226
     * Set the customer address.
1227
     * @since 2.2.0
1228
     * @param array $address Address data.
1229
     * @param string $type billing or shipping.
1230
     */
1231
    public function set_address( $address, $type = 'billing' ) {
1232
        foreach ( $address as $key => $value ) {
1233
            update_post_meta( $this->get_id(), "_{$type}_" . $key, $value );
1234
            if ( method_exists( $this, "set_{$type}_{$key}" ) ) {
1235
                $this->{"set_{$type}_{$key}"}( $value );
1236
            }
1237
        }
1238
    }
1239
1240
    /**
1241
     * Set an order total.
1242
     * @since 2.2.0
1243
     * @param float $amount
1244
     * @param string $total_type
1245
     * @return bool
1246
     */
1247
    public function set_total( $amount, $total_type = 'total' ) {
1248
        if ( ! in_array( $total_type, array( 'shipping', 'tax', 'shipping_tax', 'total', 'cart_discount', 'cart_discount_tax' ) ) ) {
1249
            return false;
1250
        }
1251
1252
        switch ( $total_type ) {
1253
            case 'total' :
1254
                $amount = wc_format_decimal( $amount, wc_get_price_decimals() );
1255
                $this->set_order_total( $amount );
1 ignored issue
show
Bug introduced by
It seems like $amount defined by wc_format_decimal($amoun...c_get_price_decimals()) on line 1254 can also be of type array; however, WC_Abstract_Order::set_order_total() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
1256
                update_post_meta( $this->get_id(), '_order_total', $amount );
1257
                break;
1258
            case 'cart_discount' :
1259
                $amount = wc_format_decimal( $amount );
1260
                $this->set_discount_total( $amount );
1 ignored issue
show
Bug introduced by
It seems like $amount defined by wc_format_decimal($amount) on line 1259 can also be of type array; however, WC_Abstract_Order::set_discount_total() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
1261
                update_post_meta( $this->get_id(), '_cart_discount', $amount );
1262
                break;
1263
            case 'cart_discount_tax' :
1264
                $amount = wc_format_decimal( $amount );
1265
                $this->set_discount_tax( $amount );
1 ignored issue
show
Bug introduced by
It seems like $amount defined by wc_format_decimal($amount) on line 1264 can also be of type array; however, WC_Abstract_Order::set_discount_tax() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
1266
                update_post_meta( $this->get_id(), '_cart_discount_tax', $amount );
1267
                break;
1268
            case 'shipping' :
1269
                $amount = wc_format_decimal( $amount );
1270
                $this->set_shipping_total( $amount );
1 ignored issue
show
Bug introduced by
It seems like $amount defined by wc_format_decimal($amount) on line 1269 can also be of type array; however, WC_Abstract_Order::set_shipping_total() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
1271
                update_post_meta( $this->get_id(), '_order_shipping', $amount );
1272
                break;
1273
            case 'shipping_tax' :
1274
                $amount = wc_format_decimal( $amount );
1275
                $this->set_shipping_tax( $amount );
1 ignored issue
show
Bug introduced by
It seems like $amount defined by wc_format_decimal($amount) on line 1274 can also be of type array; however, WC_Abstract_Order::set_shipping_tax() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
1276
                update_post_meta( $this->get_id(), '_order_shipping_tax', $amount );
1277
                break;
1278
            case 'tax' :
1279
                $amount = wc_format_decimal( $amount );
1280
                $this->set_cart_tax( $amount );
1 ignored issue
show
Bug introduced by
It seems like $amount defined by wc_format_decimal($amount) on line 1279 can also be of type array; however, WC_Abstract_Order::set_cart_tax() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
1281
                update_post_meta( $this->get_id(), '_order_tax', $amount );
1282
                break;
1283
        }
1284
1285
        return true;
1286
    }
1287
1288
    /*
1289
    |--------------------------------------------------------------------------
1290
    | CRUD methods
1291
    |--------------------------------------------------------------------------
1292
    |
1293
    | Methods which create, read, update and delete orders from the database.
1294
    | Written in abstract fashion so that the way orders are stored can be
1295
    | changed more easily in the future.
1296
    |
1297
    | A save method is included for convenience (chooses update or create based
1298
    | on if the order exists yet).
1299
    |
1300
    */
1301
1302
    /**
1303
     * Insert data into the database.
1304
     * @since 2.6.0
1305
     * @access protected
1306
     */
1307
    public function create() {
1308
        // Set random key
1309
        $this->set_order_key( uniqid( 'order_' ) );
1310
1311
        $order_id = wp_insert_post( apply_filters( 'woocommerce_new_order_data', array(
1312
            'post_type'     => $this->get_order_type(),
1313
            'post_status'   => 'wc-' . ( $this->get_status() ? $this->get_status() : apply_filters( 'woocommerce_default_order_status', 'pending' ) ),
1314
            'ping_status'   => 'closed',
1315
            'post_author'   => 1,
1316
            'post_title'    => sprintf( __( 'Order &ndash; %s', 'woocommerce' ), strftime( _x( '%b %d, %Y @ %I:%M %p', 'Order date parsed by strftime', 'woocommerce' ) ) ),
1317
            'post_password' => $this->get_order_key(),
1318
            'post_parent'   => $this->get_parent_id()
1319
        ) ), true );
1320
1321
        if ( $order_id ) {
1322
            $this->set_order_id( $order_id );
1323
1324
            // Set meta data
1325
            update_post_meta( $order_id, '_billing_first_name', $this->get_billing_first_name() );
1326
            update_post_meta( $order_id, '_billing_last_name', $this->get_billing_last_name() );
1327
            update_post_meta( $order_id, '_billing_company', $this->get_billing_company() );
1328
            update_post_meta( $order_id, '_billing_address_1', $this->get_billing_address_1() );
1329
            update_post_meta( $order_id, '_billing_address_2', $this->get_billing_address_2() );
1330
            update_post_meta( $order_id, '_billing_city', $this->get_billing_city() );
1331
            update_post_meta( $order_id, '_billing_state', $this->get_billing_state() );
1332
            update_post_meta( $order_id, '_billing_postcode', $this->get_billing_postcode() );
1333
            update_post_meta( $order_id, '_billing_country', $this->get_billing_country() );
1334
            update_post_meta( $order_id, '_billing_email', $this->get_billing_email() );
1335
            update_post_meta( $order_id, '_billing_phone', $this->get_billing_phone() );
1336
            update_post_meta( $order_id, '_shipping_first_name', $this->get_shipping_first_name() );
1337
            update_post_meta( $order_id, '_shipping_last_name', $this->get_shipping_last_name() );
1338
            update_post_meta( $order_id, '_shipping_company', $this->get_shipping_company() );
1339
            update_post_meta( $order_id, '_shipping_address_1', $this->get_shipping_address_1() );
1340
            update_post_meta( $order_id, '_shipping_address_2', $this->get_shipping_address_2() );
1341
            update_post_meta( $order_id, '_shipping_city', $this->get_shipping_city() );
1342
            update_post_meta( $order_id, '_shipping_state', $this->get_shipping_state() );
1343
            update_post_meta( $order_id, '_shipping_postcode', $this->get_shipping_postcode() );
1344
            update_post_meta( $order_id, '_shipping_country', $this->get_shipping_country() );
1345
            update_post_meta( $order_id, '_payment_method', $this->get_payment_method() );
1346
            update_post_meta( $order_id, '_payment_method_title', $this->get_payment_method_title() );
1347
            update_post_meta( $order_id, '_transaction_id', $this->get_transaction_id() );
1348
            update_post_meta( $order_id, '_customer_user', $this->get_customer_id() );
1349
            update_post_meta( $order_id, '_customer_ip_address', $this->get_customer_ip_address() );
1350
            update_post_meta( $order_id, '_customer_user_agent', $this->get_customer_user_agent() );
1351
            update_post_meta( $order_id, '_created_via', $this->get_created_via() );
1352
            update_post_meta( $order_id, '_order_version', $this->get_order_version() );
1353
            update_post_meta( $order_id, '_prices_include_tax', $this->get_prices_include_tax() );
1354
            update_post_meta( $order_id, '_completed_date', $this->get_date_completed() );
1355
			update_post_meta( $order_id, '_paid_date', $this->get_date_paid() );
1356
            update_post_meta( $order_id, '_order_currency', $this->get_order_currency() );
1357
            update_post_meta( $order_id, '_order_key', $this->get_order_key() );
1358
            update_post_meta( $order_id, '_cart_discount', $this->get_discount_total() );
1359
            update_post_meta( $order_id, '_cart_discount_tax', $this->get_discount_tax() );
1360
            update_post_meta( $order_id, '_order_shipping', $this->get_shipping_total() );
1361
            update_post_meta( $order_id, '_order_shipping_tax', $this->get_shipping_tax() );
1362
            update_post_meta( $order_id, '_order_tax', $this->get_cart_tax() );
1363
            update_post_meta( $order_id, '_order_total', $this->get_order_total() );
1364
        }
1365
    }
1366
1367
    /**
1368
     * Read from the database.
1369
     * @since 2.6.0
1370
     * @access protected
1371
     * @param int $id ID of object to read.
1372
     */
1373
    public function read( $id ) {
1374
        $post_object = get_post( $id );
1375
        $order_id    = absint( $post_object->ID );
1376
1377
        // Map standard post data
1378
        $this->set_order_id( $order_id );
1379
        $this->set_date_created( $post_object->post_date );
1380
        $this->set_date_modified( $post_object->post_modified );
1381
        $this->set_status( $post_object->post_status );
1382
        $this->set_customer_note( $post_object->post_excerpt );
1383
1384
        // Map meta data
1385
        $this->set_customer_id( get_post_meta( $order_id, '_customer_user', true ) );
1386
        $this->set_order_key( get_post_meta( $order_id, '_order_key', true ) );
1387
        $this->set_order_currency( get_post_meta( $order_id, '_order_currency', true ) );
1388
        $this->set_billing_first_name( get_post_meta( $order_id, '_billing_first_name', true ) );
1389
        $this->set_billing_last_name( get_post_meta( $order_id, '_billing_last_name', true ) );
1390
        $this->set_billing_company( get_post_meta( $order_id, '_billing_company', true ) );
1391
        $this->set_billing_address_1( get_post_meta( $order_id, '_billing_address_1', true ) );
1392
        $this->set_billing_address_2( get_post_meta( $order_id, '_billing_address_2', true ) );
1393
        $this->set_billing_city( get_post_meta( $order_id, '_billing_city', true ) );
1394
        $this->set_billing_state( get_post_meta( $order_id, '_billing_state', true ) );
1395
        $this->set_billing_postcode( get_post_meta( $order_id, '_billing_postcode', true ) );
1396
        $this->set_billing_country( get_post_meta( $order_id, '_billing_country', true ) );
1397
        $this->set_billing_email( get_post_meta( $order_id, 'billing_email', true ) );
1398
        $this->set_billing_phone( get_post_meta( $order_id, '_billing_phone', true ) );
1399
        $this->set_shipping_first_name( get_post_meta( $order_id, '_shipping_first_name', true ) );
1400
        $this->set_shipping_last_name( get_post_meta( $order_id, '_shipping_last_name', true ) );
1401
        $this->set_shipping_company( get_post_meta( $order_id, '_shipping_company', true ) );
1402
        $this->set_shipping_address_1( get_post_meta( $order_id, '_shipping_address_1', true ) );
1403
        $this->set_shipping_address_2( get_post_meta( $order_id, '_shipping_address_2', true ) );
1404
        $this->set_shipping_city( get_post_meta( $order_id, '_shipping_city', true ) );
1405
        $this->set_shipping_state( get_post_meta( $order_id, '_shipping_state', true ) );
1406
        $this->set_shipping_postcode( get_post_meta( $order_id, '_shipping_postcode', true ) );
1407
        $this->set_shipping_country( get_post_meta( $order_id, '_shipping_country', true ) );
1408
        $this->set_payment_method( get_post_meta( $order_id, '_payment_method', true ) );
1409
        $this->set_payment_method_title( get_post_meta( $order_id, '_payment_method_title', true ) );
1410
        $this->set_transaction_id( get_post_meta( $order_id, '_transaction_id', true ) );
1411
        $this->set_customer_ip_address( get_post_meta( $order_id, '_customer_ip_address', true ) );
1412
        $this->set_customer_user_agent(get_post_meta( $order_id, '_customer_user_agent', true ) );
1413
        $this->set_created_via( get_post_meta( $order_id, '_created_via', true ) );
1414
        $this->set_order_version( get_post_meta( $order_id, '_order_version', true ) );
1415
        $this->set_prices_include_tax( get_post_meta( $order_id, '_prices_include_tax', true ) );
1416
        $this->set_date_completed( get_post_meta( $order_id, '_completed_date', true ) );
1417
		$this->set_date_paid( get_post_meta( $order_id, '_paid_date', true ) );
1418
1419
        // Map totals
1420
        $this->set_discount_total( get_post_meta( $order_id, '_cart_discount', true ) );
1421
        $this->set_discount_tax( get_post_meta( $order_id, '_cart_discount_tax', true ) );
1422
        $this->set_shipping_total( get_post_meta( $order_id, '_order_shipping', true ) );
1423
        $this->set_shipping_tax( get_post_meta( $order_id, '_order_shipping_tax', true ) );
1424
        $this->set_cart_tax( get_post_meta( $order_id, '_order_tax', true ) );
1425
        $this->set_order_total( get_post_meta( $order_id, '_order_total', true ) );
1426
1427
        // Map user data
1428
        if ( empty( $this->get_billing_email() ) && ( $user = $this->get_user() ) ) {
1429
            $this->set_billing_email( $user->user_email );
1430
        }
1431
1432
        // Orders store the state of prices including tax when created.
1433
        $this->prices_include_tax = metadata_exists( 'post', $order_id, '_prices_include_tax' ) ? 'yes' === get_post_meta( $order_id, '_prices_include_tax', true ) : 'yes' === get_option( 'woocommerce_prices_include_tax' );
1434
    }
1435
1436
    /**
1437
     * Update data in the database.
1438
     * @since 2.6.0
1439
     * @access protected
1440
     */
1441
    public function update() {
1442
        global $wpdb;
1443
1444
        $order_id = $this->get_id();
1445
1446
        $wpdb->update(
1447
            $wpdb->posts,
1448
            array(
1449
                'post_date'     => date( 'Y-m-d H:i:s', $this->get_date_created() ),
1450
                'post_date_gmt' => get_gmt_from_date( date( 'Y-m-d H:i:s', $this->get_date_created() ) ),
1451
                'post_status'   => 'wc-' . ( $this->get_status() ? $this->get_status() : apply_filters( 'woocommerce_default_order_status', 'pending' ) ),
1452
                'post_parent'   => $this->get_parent_id()
1453
            ),
1454
            array(
1455
                'ID' => $order_id
1456
            )
1457
        );
1458
1459
        // Update meta data
1460
        update_post_meta( $order_id, '_billing_first_name', $this->get_billing_first_name() );
1461
        update_post_meta( $order_id, '_billing_last_name', $this->get_billing_last_name() );
1462
        update_post_meta( $order_id, '_billing_company', $this->get_billing_company() );
1463
        update_post_meta( $order_id, '_billing_address_1', $this->get_billing_address_1() );
1464
        update_post_meta( $order_id, '_billing_address_2', $this->get_billing_address_2() );
1465
        update_post_meta( $order_id, '_billing_city', $this->get_billing_city() );
1466
        update_post_meta( $order_id, '_billing_state', $this->get_billing_state() );
1467
        update_post_meta( $order_id, '_billing_postcode', $this->get_billing_postcode() );
1468
        update_post_meta( $order_id, '_billing_country', $this->get_billing_country() );
1469
        update_post_meta( $order_id, '_billing_email', $this->get_billing_email() );
1470
        update_post_meta( $order_id, '_billing_phone', $this->get_billing_phone() );
1471
        update_post_meta( $order_id, '_shipping_first_name', $this->get_shipping_first_name() );
1472
        update_post_meta( $order_id, '_shipping_last_name', $this->get_shipping_last_name() );
1473
        update_post_meta( $order_id, '_shipping_company', $this->get_shipping_company() );
1474
        update_post_meta( $order_id, '_shipping_address_1', $this->get_shipping_address_1() );
1475
        update_post_meta( $order_id, '_shipping_address_2', $this->get_shipping_address_2() );
1476
        update_post_meta( $order_id, '_shipping_city', $this->get_shipping_city() );
1477
        update_post_meta( $order_id, '_shipping_state', $this->get_shipping_state() );
1478
        update_post_meta( $order_id, '_shipping_postcode', $this->get_shipping_postcode() );
1479
        update_post_meta( $order_id, '_shipping_country', $this->get_shipping_country() );
1480
        update_post_meta( $order_id, '_payment_method', $this->get_payment_method() );
1481
        update_post_meta( $order_id, '_payment_method_title', $this->get_payment_method_title() );
1482
        update_post_meta( $order_id, '_transaction_id', $this->get_transaction_id() );
1483
        update_post_meta( $order_id, '_customer_user', $this->get_customer_id() );
1484
        update_post_meta( $order_id, '_customer_ip_address', $this->get_customer_ip_address() );
1485
        update_post_meta( $order_id, '_customer_user_agent', $this->get_customer_user_agent() );
1486
        update_post_meta( $order_id, '_created_via', $this->get_created_via() );
1487
        update_post_meta( $order_id, '_order_version', $this->get_order_version() );
1488
        update_post_meta( $order_id, '_prices_include_tax', $this->get_prices_include_tax() );
1489
        update_post_meta( $order_id, '_order_currency', $this->get_order_currency() );
1490
        update_post_meta( $order_id, '_order_key', $this->get_order_key() );
1491
        update_post_meta( $order_id, '_cart_discount', $this->get_discount_total() );
1492
        update_post_meta( $order_id, '_cart_discount_tax', $this->get_discount_tax() );
1493
        update_post_meta( $order_id, '_order_shipping', $this->get_shipping_total() );
1494
        update_post_meta( $order_id, '_order_shipping_tax', $this->get_shipping_tax() );
1495
        update_post_meta( $order_id, '_order_tax', $this->get_cart_tax() );
1496
        update_post_meta( $order_id, '_order_total', $this->get_order_total() );
1497
1498
        if ( $this->_status_transition ) {
1499
            if ( ! empty( $this->_status_transition['original'] ) ) {
1500
                $transition_note = sprintf( __( 'Order status changed from %s to %s.', 'woocommerce' ), wc_get_order_status_name( $this->_status_transition['original'] ), wc_get_order_status_name( $this->get_status() ) );
1501
1502
                do_action( 'woocommerce_order_status_' . $this->_status_transition['original'] . '_to_' . $this->get_status(), $this->get_id() );
1503
                do_action( 'woocommerce_order_status_changed', $this->get_id(), $this->_status_transition['original'], $this->get_status() );
1504
            } else {
1505
                $transition_note = sprintf( __( 'Order status set to %s.', 'woocommerce' ), wc_get_order_status_name( $this->get_status() ) );
1506
            }
1507
1508
            do_action( 'woocommerce_order_status_' . $this->get_status(), $this->get_id() );
1509
1510
            // Note the transition occured
1511
            $this->add_order_note( trim( $this->_status_transition['note'] . ' ' . $transition_note ), 0, $this->_status_transition['manual'] );
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class WC_Abstract_Order as the method add_order_note() does only exist in the following sub-classes of WC_Abstract_Order: WC_Order. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
1512
1513
            // This has ran, so reset status transition variable
1514
            $this->_status_transition = false;
1515
        }
1516
    }
1517
1518
    /**
1519
     * Delete data from the database.
1520
     * @since 2.6.0
1521
     * @access protected
1522
     */
1523
    public function delete() {
1524
        wp_delete_post( $this->get_id() );
1525
    }
1526
1527
    /**
1528
     * Save data to the database.
1529
     * @since 2.6.0
1530
     * @access protected
1531
     */
1532
    public function save() {
1533
        if ( ! $this->get_id() ) {
1534
            $this->create();
1535
        } else {
1536
            $this->update();
1537
        }
1538
        wc_delete_shop_order_transients( $this->get_id() );
1539
    }
1540
1541
    /*
1542
    |--------------------------------------------------------------------------
1543
    | Order Item Handling
1544
    |--------------------------------------------------------------------------
1545
    |
1546
    | Order items are used for products, taxes, shipping, and fees within
1547
    | each order.
1548
    |
1549
    */
1550
1551
    /**
1552
     * Return an array of items/products within this order.
1553
     *
1554
     * @param string|array $type Types of line items to get (array or string).
1555
     * @return Array of WC_Order_item
1556
     */
1557
    public function get_items( $type = 'line_item' ) {
1558
        global $wpdb;
1559
1560
        $type            = ! is_array( $type ) ? array( $type ) : $type;
1561
        $items           = array();
1562
        $get_items_sql   = $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}woocommerce_order_items WHERE order_id = %d ", $this->get_id() );
1563
        $get_items_sql  .= "AND order_item_type IN ( '" . implode( "','", array_map( 'esc_sql', $type ) ) . "' ) ORDER BY order_item_id;";
1564
        $raw_items       = $wpdb->get_results( $get_items_sql );
1565
1566
        foreach ( $raw_items as $item ) {
1567
            $item                                = $this->get_item( $item );
1568
            $items[ $item->get_order_item_id() ] = $item;
1569
        }
1570
1571
        return apply_filters( 'woocommerce_order_get_items', $items, $this );
1572
    }
1573
1574
    /**
1575
     * Get an order item object, based on it's type.
1576
     * @param  int $item_id
1577
     * @return WC_Order_Item
1578
     */
1579
    public function get_item( $item_id ) {
1580
        return WC_Order_Factory::get_order_item( $item_id );
1581
    }
1582
1583
    /**
1584
     * Display meta data belonging to an item. @todo
1585
     * @param  array $item
1586
     */
1587
    public function display_item_meta( $item ) {
1588
        $product   = $this->get_product_from_item( $item );
0 ignored issues
show
Documentation introduced by
$item is of type array, but the function expects a object.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1589
        $item_meta = new WC_Order_Item_Meta( $item, $product );
0 ignored issues
show
Bug introduced by
It seems like $product defined by $this->get_product_from_item($item) on line 1588 can also be of type boolean; however, WC_Order_Item_Meta::__construct() does only seem to accept object<WC_Product>|null, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
1590
        $item_meta->display();
1591
    }
1592
1593
    /**
1594
     * Return an array of fees within this order.
1595
     *
1596
     * @return array
1597
     */
1598
    public function get_fees() {
1599
        return $this->get_items( 'fee' );
1600
    }
1601
1602
    /**
1603
     * Return an array of taxes within this order.
1604
     *
1605
     * @return array
1606
     */
1607
    public function get_taxes() {
1608
        return $this->get_items( 'tax' );
1609
    }
1610
1611
    /**
1612
     * Return an array of shipping costs within this order.
1613
     *
1614
     * @return array
1615
     */
1616
    public function get_shipping_methods() {
1617
        return $this->get_items( 'shipping' );
1618
    }
1619
1620
    /**
1621
     * Get coupon codes only.
1622
     *
1623
     * @return array
1624
     */
1625
    public function get_used_coupons() {
1626
        return array_map( 'trim', wp_list_pluck( $this->get_items( 'coupon' ), 'name' ) );
1627
    }
1628
1629
    /**
1630
     * Gets the count of order items of a certain type.
1631
     *
1632
     * @param string $item_type
1633
     * @return string
1634
     */
1635
    public function get_item_count( $item_type = '' ) {
1636
        if ( empty( $item_type ) ) {
1637
            $item_type = array( 'line_item' );
1638
        }
1639
        if ( ! is_array( $item_type ) ) {
1640
            $item_type = array( $item_type );
1641
        }
1642
1643
        $items = $this->get_items( $item_type );
1644
        $count = 0;
1645
1646
        foreach ( $items as $item ) {
1647
            $count += $item->get_qty();
1648
        }
1649
1650
        return apply_filters( 'woocommerce_get_item_count', $count, $item_type, $this );
1651
    }
1652
1653
    /**
1654
     * Remove all line items (products, coupons, shipping, taxes) from the order.
1655
     *
1656
     * @param string $type Order item type. Default null.
1657
     */
1658
    public function remove_order_items( $type = null ) {
1659
        global $wpdb;
1660
1661
        if ( ! empty( $type ) ) {
1662
            $wpdb->query( $wpdb->prepare( "DELETE FROM itemmeta USING {$wpdb->prefix}woocommerce_order_itemmeta itemmeta INNER JOIN {$wpdb->prefix}woocommerce_order_items items WHERE itemmeta.order_item_id = items.order_item_id AND items.order_id = %d AND items.order_item_type = %s", $this->get_id(), $type ) );
1663
            $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}woocommerce_order_items WHERE order_id = %d AND order_item_type = %s", $this->get_id(), $type ) );
1664
        } else {
1665
            $wpdb->query( $wpdb->prepare( "DELETE FROM itemmeta USING {$wpdb->prefix}woocommerce_order_itemmeta itemmeta INNER JOIN {$wpdb->prefix}woocommerce_order_items items WHERE itemmeta.order_item_id = items.order_item_id and items.order_id = %d", $this->get_id() ) );
1666
            $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}woocommerce_order_items WHERE order_id = %d", $this->get_id() ) );
1667
        }
1668
    }
1669
1670
    /**
1671
     * Add a product line item to the order.
1672
     * Order must be saved prior to adding items.
1673
     *
1674
     * @since 2.2
1675
     * @param \WC_Product $product
1676
     * @param int $qty Line item quantity.
1677
     * @param array $args
1678
     * @return int updated order item ID
1679
     */
1680
    public function add_product( $product, $qty = 1, $args = array() ) {
1681
        $args = wp_parse_args( $args, array(
1682
            'name'         => $product->get_title(),
1683
            'qty'          => absint( $qty ),
1684
            'tax_class'    => $product->get_tax_class(),
1685
            'product_id'   => $product->id,
1686
            'variation_id' => isset( $product->variation_id ) ? $product->variation_id : 0,
1687
            'variation'    => array(),
1688
            'subtotal'     => $product->get_price_excluding_tax( $qty ),
1689
            'subtotal_tax' => 0,
1690
            'total'        => $product->get_price_excluding_tax( $qty ),
1691
            'total_tax'    => 0,
1692
            'taxes'        => array(
1693
                'subtotal' => array(),
1694
                'total'    => array()
1695
            )
1696
        ) );
1697
        $item = new WC_Order_Item_Product();
1698
        $item_id = $this->update_product( $item, $product, $args );
1699
1700
		do_action( 'woocommerce_order_add_product', $this->get_id(), $item->get_order_item_id(), $product, $qty, $args );
1701
1702
		return $item_id;
1703
    }
1704
1705
    /**
1706
     * Update a line item for the order.
1707
     *
1708
     * Note this does not update order totals.
1709
     *
1710
     * @since 2.2
1711
     * @param object|int $item order item ID or item object.
1712
     * @param WC_Product $product
1713
     * @param array $args data to update.
1714
     * @return int updated order item ID
1715
     */
1716
    public function update_product( $item, $product, $args ) {
1717
        if ( is_numeric( $item ) ) {
1718
            $item = $this->get_item( $item );
1719
        }
1720
1721
        if ( ! is_object( $product ) || ! $item->is_type( 'line_item' ) ) {
1722
            return false;
1723
        }
1724
1725
        if ( ! $this->get_id() ) {
1726
            $this->save();
1727
        }
1728
1729
		$item->set_order_id( $this->get_id() );
1730
1731
        if ( ! $item->get_order_item_id() ) {
1732
            $inserting = true;
1733
        } else {
1734
            $inserting = false;
1735
        }
1736
1737
        if ( isset( $args['name'] ) ) {
1738
            $item->set_name( $args['name'] );
1739
        }
1740
1741
        if ( isset( $args['qty'] ) ) {
1742
            $item->set_qty( $args['qty'] );
1743
1744
            if ( $product->backorders_require_notification() && $product->is_on_backorder( $args['qty'] ) ) {
1745
                $item->add_meta_data( apply_filters( 'woocommerce_backordered_item_meta_name', __( 'Backordered', 'woocommerce' ) ), $args['qty'] - max( 0, $product->get_total_stock() ), true );
1746
            }
1747
1748
            $item->set_line_subtotal( $product->get_price_excluding_tax( $args['qty'] ) );
1749
            $item->set_line_total( $product->get_price_excluding_tax( $args['qty'] ) );
1750
        }
1751
1752
        if ( isset( $args['tax_class'] ) ) {
1753
            $item->set_tax_class( $args['tax_class'] );
1754
        }
1755
1756
        if ( isset( $args['product_id'] ) ) {
1757
            $item->set_product_id( $args['product_id'] );
1758
        }
1759
1760
        if ( isset( $args['variation_id'] ) ) {
1761
            $item->set_variation_id( $args['variation_id'] );
1762
        }
1763
1764
        if ( isset( $args['variation'] ) && is_array( $args['variation'] ) ) {
1765
            foreach ( $args['variation'] as $key => $value ) {
1766
                $item->add_meta_data( str_replace( 'attribute_', '', $key ), $value, true );
1767
            }
1768
        }
1769
1770
        if ( isset( $args['totals'] ) ) {
1771
            // BW compatibility with old args
1772
            if ( isset( $args['totals']['subtotal'] ) ) {
1773
                $args['subtotal'] = $args['totals']['subtotal'];
1774
            }
1775
            if ( isset( $args['totals']['total'] ) ) {
1776
                $args['total'] = $args['totals']['total'];
1777
            }
1778
            if ( isset( $args['totals']['subtotal_tax'] ) ) {
1779
                $args['subtotal_tax'] = $args['totals']['subtotal_tax'];
1780
            }
1781
            if ( isset( $args['totals']['tax'] ) ) {
1782
                $args['total_tax'] = $args['totals']['tax'];
1783
            }
1784
            if ( isset( $args['totals']['tax_data'] ) ) {
1785
                $args['taxes'] = $args['totals']['tax_data'];
1786
            }
1787
        }
1788
1789
        if ( isset( $args['subtotal'] ) ) {
1790
            $item->set_subtotal( $args['subtotal'] );
1791
        }
1792
        if ( isset( $args['total'] ) ) {
1793
            $item->set_total( $args['total'] );
1794
        }
1795
        if ( isset( $args['subtotal_tax'] ) ) {
1796
            $item->set_line_subtotal_tax( $args['subtotal_tax'] );
1797
        }
1798
        if ( isset( $args['total_tax'] ) ) {
1799
            $item->set_total_tax( $args['total_tax'] );
1800
        }
1801
        if ( isset( $args['taxes'] ) ) {
1802
            $item->set_taxes( $args['taxes'] );
1803
        }
1804
1805
        $item->save();
1806
1807
        if ( ! $inserting ) {
1808
            do_action( 'woocommerce_order_edit_product', $this->get_id(), $item->get_order_item_id(), $args, $product );
1809
        }
1810
1811
        return $item->get_order_item_id();
1812
    }
1813
1814
    /**
1815
     * Add coupon code to the order.
1816
     * Order must be saved prior to adding items.
1817
     *
1818
     * @param string $code
1819
     * @param int $discount_amount
1820
     * @param int $discount_amount_tax "Discounted" tax - used for tax inclusive prices.
1821
     * @return int updated order item ID
1822
     */
1823
    public function add_coupon( $code, $discount_amount = 0, $discount_amount_tax = 0 ) {
1824
        $args = wp_parse_args( $args, array(
1825
            'code'                => $code,
1826
            'discount_amount'     => $discount_amount,
1827
            'discount_amount_tax' => $discount_amount_tax
1828
        ) );
1829
        $item = new WC_Order_Item_Coupon();
1830
        $item_id = $this->update_coupon( $item, $args );
1831
1832
		do_action( 'woocommerce_order_add_coupon', $this->get_id(), $item->get_order_item_id(), $code, $discount_amount, $discount_amount_tax );
1833
1834
		return $item_id;
1835
    }
1836
1837
    /**
1838
     * Update coupon for order. Note this does not update order totals.
1839
     * @since 2.2
1840
     * @param object|int $item
1841
     * @param array $args
1842
     * @return int updated order item ID
1843
     */
1844
    public function update_coupon( $item, $args ) {
1845
        if ( is_numeric( $item ) ) {
1846
            $item = $this->get_item( $item );
1847
        }
1848
1849
        if ( ! is_object( $product ) || ! $item->is_type( 'coupon' ) ) {
1850
            return false;
1851
        }
1852
1853
        if ( ! $this->get_id() ) {
1854
            $this->save();
1855
        }
1856
1857
		$item->set_order_id( $this->get_id() );
1858
1859
        if ( ! $item->get_order_item_id() ) {
1860
            $inserting = true;
1861
        } else {
1862
            $inserting = false;
1863
        }
1864
1865
        if ( isset( $args['code'] ) ) {
1866
            $item->set_coupon_code( $args['code'] );
1867
        }
1868
        if ( isset( $args['discount_amount'] ) ) {
1869
            $item->set_discount_amount( $args['discount_amount'] );
1870
        }
1871
        if ( isset( $args['discount_amount_tax'] ) ) {
1872
            $item->set_discount_amount_tax( $args['discount_amount_tax'] );
1873
        }
1874
1875
        $item->save();
1876
1877
        if ( ! $inserting ) {
1878
            do_action( 'woocommerce_order_update_coupon', $this->get_id(), $item->get_order_item_id(), $args );
1879
        }
1880
1881
        return $item->get_order_item_id();
1882
    }
1883
1884
    /**
1885
     * Add a shipping row to the order.
1886
     * Order must be saved prior to adding items.
1887
     *
1888
     * @param WC_Shipping_Rate shipping_rate
1889
     * @return int updated order item ID
1890
     */
1891
    public function add_shipping( $shipping_rate ) {
1892
        $args = wp_parse_args( $args, array(
1893
            'method_title' => $shipping_rate->label,
1894
            'method_id'    => $shipping_rate->id,
1895
            'cost'         => wc_format_decimal( $shipping_rate->cost ),
1896
            'taxes'        => $shipping_rate->taxes,
1897
            'meta'         => $shipping_rate->get_meta_data(),
1898
        ) );
1899
1900
        $item = new WC_Order_Item_Shipping();
1901
        $item_id = $this->update_shipping( $item, $args );
1902
1903
		do_action( 'woocommerce_order_add_shipping', $this->get_id(), $item->get_order_item_id(), $shipping_rate );
1904
1905
		return $item_id;
1906
    }
1907
1908
    /**
1909
     * Update shipping method for order.
1910
     *
1911
     * Note this does not update the order total.
1912
     *
1913
     * @since 2.2
1914
     * @param object|int $item
1915
     * @param array $args
1916
     * @return int updated order item ID
1917
     */
1918
    public function update_shipping( $item, $args ) {
1919
        if ( is_numeric( $item ) ) {
1920
            $item = $this->get_item( $item );
1921
        }
1922
1923
        if ( ! is_object( $product ) || ! $item->is_type( 'shipping' ) ) {
1924
            return false;
1925
        }
1926
1927
        if ( ! $this->get_id() ) {
1928
            $this->save();
1929
        }
1930
1931
		$item->set_order_id( $this->get_id() );
1932
1933
        if ( ! $item->get_order_item_id() ) {
1934
            $inserting = true;
1935
        } else {
1936
            $inserting = false;
1937
        }
1938
1939
        if ( isset( $args['method_title'] ) ) {
1940
            $item->set_method_title( $args['method_title'] );
1941
        }
1942
1943
        if ( isset( $args['method_id'] ) ) {
1944
            $item->set_method_id( $args['method_id'] );
1945
        }
1946
1947
        if ( isset( $args['cost'] ) ) {
1948
            // Get old cost before updating
1949
            $old_cost = $item->get_cost();
1950
1951
            // Update
1952
            $item->set_cost( $args['cost'] );
1953
1954
            // Update total
1955
            $this->set_total( $this->get_total_shipping() - wc_format_decimal( $old_cost ) + $item->get_cost(), 'shipping' );
1956
        }
1957
1958
        if ( isset( $args['taxes'] ) && is_array( $args['taxes'] ) ) {
1959
            $item->set_taxes( $args['taxes'] );
1960
        }
1961
1962
        if ( isset( $args['meta'] ) && is_array( $args['meta'] ) ) {
1963
			foreach ( $args['meta'] as $key => $value ) {
1964
				$item->update_meta_data( $key, $value );
1965
			}
1966
		}
1967
1968
        $item->save();
1969
1970
        if ( ! $inserting ) {
1971
            do_action( 'woocommerce_order_update_shipping', $this->get_id(), $item->get_order_item_id(), $args );
1972
        }
1973
1974
        return $item->get_order_item_id();
1975
    }
1976
1977
    /**
1978
     * Add a fee to the order.
1979
     * Order must be saved prior to adding items.
1980
     * @param object $fee
1981
     * @return int updated order item ID
1982
     */
1983
    public function add_fee( $fee ) {
1984
        $args = wp_parse_args( $args, array(
1985
            'name'      => $fee->name,
1986
            'tax_class' => $fee->taxable ? $fee->tax_class : 0,
1987
            'total'     => $fee->amount,
1988
            'total_tax' => $fee->tax,
1989
            'taxes'     => array(
1990
                'total' => $fee->tax_data
1991
            )
1992
        ) );
1993
        $item = new WC_Order_Item_Fee();
1994
        $item_id = $this->update_fee( $item, $args );
1995
1996
        do_action( 'woocommerce_order_add_fee', $this->get_id(), $item->get_order_item_id(), $fee );
1997
1998
        return $item_id;
1999
    }
2000
2001
    /**
2002
     * Update fee for order.
2003
     *
2004
     * Note this does not update order totals.
2005
     *
2006
     * @since 2.2
2007
     * @param object|int $item
2008
     * @param array $args
2009
     * @return int updated order item ID
2010
     */
2011
    public function update_fee( $item, $args ) {
2012
        if ( is_numeric( $item ) ) {
2013
            $item = $this->get_item( $item );
2014
        }
2015
2016
        if ( ! is_object( $product ) || ! $item->is_type( 'fee' ) ) {
2017
            return false;
2018
        }
2019
2020
        if ( ! $this->get_id() ) {
2021
            $this->save();
2022
        }
2023
2024
		$item->set_order_id( $this->get_id() );
2025
2026
        if ( ! $item->get_order_item_id() ) {
2027
            $inserting = true;
2028
        } else {
2029
            $inserting = false;
2030
        }
2031
2032
        if ( isset( $args['name'] ) ) {
2033
            $item->set_name( $args['name'] );
2034
        }
2035
2036
        if ( isset( $args['tax_class'] ) ) {
2037
            $item->set_tax_class( $args['tax_class'] );
2038
        }
2039
2040
        if ( isset( $args['total'] ) ) {
2041
            $item->set_total( $args['total'] );
2042
        }
2043
2044
        if ( isset( $args['total_tax'] ) ) {
2045
            $item->set_total_tax( $args['total_tax'] );
2046
        }
2047
2048
        if ( isset( $args['taxes'] ) ) {
2049
            $item->set_taxes( $args['taxes'] );
2050
        }
2051
2052
        $item->save();
2053
2054
        if ( ! $inserting ) {
2055
            do_action( 'woocommerce_order_update_fee', $this->get_id(), $item->get_order_item_id(), $args );
2056
        }
2057
2058
        return $item->get_order_item_id();
2059
    }
2060
2061
    /**
2062
     * Add a tax row to the order.
2063
     * Order must be saved prior to adding items.
2064
     * @since 2.2
2065
     * @param int tax_rate_id
2066
     * @return int updated order item ID
2067
     */
2068
    public function add_tax( $tax_rate_id, $tax_amount = 0, $shipping_tax_amount = 0 ) {
2069
        if ( ! $code = WC_Tax::get_rate_code( $tax_rate_id ) ) {
2070
            return false;
2071
        }
2072
2073
        $args = wp_parse_args( $args, array(
2074
            'rate_code'          => $code,
2075
            'rate_id'            => $tax_rate_id,
2076
            'label'              => WC_Tax::get_rate_label( $tax_rate_id ),
2077
            'compound'           => WC_Tax::is_compound( $tax_rate_id ),
2078
            'tax_total'          => $tax_amount,
2079
            'shipping_tax_total' => $shipping_tax_amount
2080
        ) );
2081
        $item = new WC_Order_Item_Tax();
2082
        $item_id = $this->update_tax( $item, $args );
2083
2084
        do_action( 'woocommerce_order_add_tax', $this->get_id(), $item->get_order_item_id(), $tax_rate_id, $tax_amount, $shipping_tax_amount );
2085
2086
        return $item_id;
2087
    }
2088
2089
    /**
2090
     * Update tax line on order.
2091
     * Note this does not update order totals.
2092
     *
2093
     * @since 2.6
2094
     * @param object|int $item
2095
     * @param array $args
2096
     * @return int updated order item ID
2097
     */
2098
    public function update_tax( $item, $args ) {
2099
        if ( is_numeric( $item ) ) {
2100
            $item = $this->get_item( $item );
2101
        }
2102
2103
        if ( ! is_object( $product ) || ! $item->is_type( 'tax' ) ) {
2104
            return false;
2105
        }
2106
2107
        if ( ! $this->get_id() ) {
2108
            $this->save();
2109
        }
2110
2111
		$item->set_order_id( $this->get_id() );
2112
2113
        if ( ! $item->get_order_item_id() ) {
2114
            $inserting = true;
2115
        } else {
2116
            $inserting = false;
2117
        }
2118
2119
        if ( isset( $args['rate_code'] ) ) {
2120
            $item->set_rate_code( $args['rate_code'] );
2121
        }
2122
2123
        if ( isset( $args['rate_id'] ) ) {
2124
            $item->set_rate_id( $args['rate_id'] );
2125
        }
2126
2127
        if ( isset( $args['label'] ) ) {
2128
            $item->set_label( $args['label'] );
2129
        }
2130
2131
        if ( isset( $args['compound'] ) ) {
2132
            $item->set_compound( $args['compound'] );
2133
        }
2134
2135
        if ( isset( $args['tax_total'] ) ) {
2136
            $item->set_tax_total( $args['tax_total'] );
2137
        }
2138
2139
        if ( isset( $args['shipping_tax_total'] ) ) {
2140
            $item->set_shipping_tax_total( $args['shipping_tax_total'] );
2141
        }
2142
2143
        $item->save();
2144
2145
        if ( ! $inserting ) {
2146
            do_action( 'woocommerce_order_update_tax', $this->get_id(), $item->get_order_item_id(), $args );
2147
        }
2148
2149
        return $item->get_order_item_id();
2150
    }
2151
2152
	/**
2153
     * Update tax lines at order level by looking at the line item taxes themselves.
2154
     * @return bool success or fail.
2155
     */
2156
    public function update_taxes() {
2157
        $cart_taxes     = array();
2158
        $shipping_taxes = array();
2159
2160 View Code Duplication
        foreach ( $this->get_items( array( 'line_item', 'fee' ) ) as $item_id => $item ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2161
            $taxes = $item->get_taxes();
2162
            if ( isset( $taxes['total'] ) ) {
2163
                foreach ( $taxes['total'] as $tax_rate_id => $tax ) {
2164
                    if ( ! isset( $cart_taxes[ $tax_rate_id ] ) ) {
2165
                        $cart_taxes[ $tax_rate_id ] = 0;
2166
                    }
2167
                    $cart_taxes[ $tax_rate_id ] += $tax;
2168
                }
2169
            }
2170
        }
2171
2172 View Code Duplication
        foreach ( $this->get_items( array( 'shipping' ) ) as $item_id => $item ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2173
			$taxes = $item->get_taxes();
2174
            if ( isset( $taxes['total'] ) ) {
2175
                foreach ( $taxes['total'] as $tax_rate_id => $tax ) {
2176
                    if ( ! isset( $shipping_taxes[ $tax_rate_id ] ) ) {
2177
                        $shipping_taxes[ $tax_rate_id ] = 0;
2178
                    }
2179
                    $shipping_taxes[ $tax_rate_id ] += $tax;
2180
                }
2181
            }
2182
        }
2183
2184
        // Remove old existing tax rows.
2185
        $this->remove_order_items( 'tax' );
2186
2187
        // Now merge to keep tax rows.
2188 View Code Duplication
        foreach ( array_keys( $cart_taxes + $shipping_taxes ) as $tax_rate_id ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2189
            $this->add_tax( $tax_rate_id, isset( $cart_taxes[ $tax_rate_id ] ) ? $cart_taxes[ $tax_rate_id ] : 0, isset( $shipping_taxes[ $tax_rate_id ] ) ? $shipping_taxes[ $tax_rate_id ] : 0 );
2190
        }
2191
2192
        // Save tax totals
2193
        $this->set_total( WC_Tax::round( array_sum( $shipping_taxes ) ), 'shipping_tax' );
2194
        $this->set_total( WC_Tax::round( array_sum( $cart_taxes ) ), 'tax' );
2195
2196
        return true;
2197
    }
2198
2199
    /*
2200
    |--------------------------------------------------------------------------
2201
    | Calculations.
2202
    |--------------------------------------------------------------------------
2203
    |
2204
    | These methods calculate order totals and taxes based on the current data.
2205
    |
2206
    */
2207
2208
    /**
2209
     * Calculate shipping total.
2210
     *
2211
     * @since 2.2
2212
     * @return float
2213
     */
2214
    public function calculate_shipping() {
2215
        $shipping_total = 0;
2216
2217
        foreach ( $this->get_shipping_methods() as $shipping ) {
2218
            $shipping_total += $shipping->get_total();
2219
        }
2220
2221
        $this->set_total( $shipping_total, 'shipping' );
2222
2223
        return $this->get_shipping_total();
2224
    }
2225
2226
    /**
2227
     * Calculate taxes for all line items and shipping, and store the totals and tax rows.
2228
     *
2229
     * Will use the base country unless customer addresses are set.
2230
     *
2231
     * @return bool success or fail.
2232
     */
2233
    public function calculate_taxes() {
2234
        $cart_tax     = 0;
2235
        $cart_taxes   = array();
2236
        $tax_based_on = get_option( 'woocommerce_tax_based_on' );
2237
2238
        if ( 'billing' === $tax_based_on ) {
2239
            $country  = $this->get_billing_country();
2240
            $state    = $this->get_billing_state();
2241
            $postcode = $this->get_billing_postcode();
2242
            $city     = $this->get_billing_city();
2243
        } elseif ( 'shipping' === $tax_based_on ) {
2244
            $country  = $this->get_shipping_country();
2245
            $state    = $this->get_shipping_state();
2246
            $postcode = $this->get_shipping_postcode();
2247
            $city     = $this->get_shipping_city();
2248
        }
2249
2250
        // Default to base
2251 View Code Duplication
        if ( 'base' === $tax_based_on || empty( $country ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2252
            $default  = wc_get_base_location();
2253
            $country  = $default['country'];
2254
            $state    = $default['state'];
2255
            $postcode = '';
2256
            $city     = '';
2257
        }
2258
2259
        // Get items
2260
        foreach ( $this->get_items( array( 'line_item', 'fee' ) ) as $item_id => $item ) {
2261
			$tax_class  = $item->get_tax_class();
2262
			$tax_status = $item->get_tax_status();
0 ignored issues
show
Unused Code introduced by
$tax_status is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
2263
2264
            if ( '0' !== $tax_class && 'taxable' === $item_tax_status ) {
2265
2266
                $tax_rates = WC_Tax::find_rates( array(
2267
                    'country'   => $country,
2268
                    'state'     => $state,
2269
                    'postcode'  => $postcode,
2270
                    'city'      => $city,
2271
                    'tax_class' => $tax_class
2272
                ) );
2273
2274
				$total     = $item->get_total();
2275
				$taxes     = WC_Tax::calc_tax( $total, $tax_rates, false );
2276
				$total_tax = max( 0, array_sum( $taxes ) );
2277
				$cart_tax += $total_tax;
2278
				$item->set_total_tax( $total_tax );
2279
2280
				if ( $item->is_type( 'line_item' ) ) {
2281
					$subtotal       = $item->get_subtotal();
2282
					$subtotal_taxes = WC_Tax::calc_tax( $subtotal, $tax_rates, false );
2283
					$subtotal_tax   = max( 0, array_sum( $subtotal_taxes ) );
2284
					$item->set_subtotal_tax( $subtotal_tax );
2285
					$item->set_taxes( array( 'total' => $taxes, 'subtotal' => $subtotal_taxes ) );
2286
				} else {
2287
					$item->set_taxes( array( 'total' => $taxes ) );
2288
				}
2289
2290
				$item->save(); //@todo store items to self, don't save right away
2291
2292
                // Sum the item taxes
2293
                foreach ( array_keys( $cart_taxes + $taxes ) as $key ) {
2294
                    $cart_taxes[ $key ] = ( isset( $taxes[ $key ] ) ? $taxes[ $key ] : 0 ) + ( isset( $taxes[ $key ] ) ? $cart_taxes[ $key ] : 0 );
2295
                }
2296
            }
2297
        }
2298
2299
        // Now calculate shipping tax
2300
        $shipping_methods = $this->get_shipping_methods();
2301
2302
        if ( ! empty( $shipping_methods ) ) {
2303
            $matched_tax_rates = array();
2304
            $tax_rates         = WC_Tax::find_rates( array(
2305
                'country'   => $country,
2306
                'state'     => $state,
2307
                'postcode'  => $postcode,
2308
                'city'      => $city,
2309
                'tax_class' => ''
2310
            ) );
2311
2312 View Code Duplication
            if ( ! empty( $tax_rates ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2313
                foreach ( $tax_rates as $key => $rate ) {
2314
                    if ( isset( $rate['shipping'] ) && 'yes' === $rate['shipping'] ) {
2315
                        $matched_tax_rates[ $key ] = $rate;
2316
                    }
2317
                }
2318
            }
2319
2320
            $shipping_taxes     = WC_Tax::calc_shipping_tax( $this->order_shipping, $matched_tax_rates );
2321
            $shipping_tax_total = WC_Tax::round( array_sum( $shipping_taxes ) );
2322
        } else {
2323
            $shipping_taxes     = array();
2324
            $shipping_tax_total = 0;
2325
        }
2326
2327
        // Save tax totals
2328
        $this->set_total( $shipping_tax_total, 'shipping_tax' );
2329
        $this->set_total( $tax_total, 'tax' );
2330
2331
        // Tax rows
2332
        $this->remove_order_items( 'tax' );
2333
2334
        // Now merge to keep tax rows
2335 View Code Duplication
        foreach ( array_keys( $taxes + $shipping_taxes ) as $tax_rate_id ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2336
            $this->add_tax( $tax_rate_id, isset( $taxes[ $tax_rate_id ] ) ? $taxes[ $tax_rate_id ] : 0, isset( $shipping_taxes[ $tax_rate_id ] ) ? $shipping_taxes[ $tax_rate_id ] : 0 );
2337
        }
2338
2339
        return true;
2340
    }
2341
2342
    /**
2343
     * Calculate totals by looking at the contents of the order. Stores the totals and returns the orders final total.
2344
     *
2345
     * @since 2.2
2346
     * @param  bool $and_taxes Calc taxes if true.
2347
     * @return float calculated grand total.
2348
     */
2349
    public function calculate_totals( $and_taxes = true ) {
2350
        $cart_subtotal     = 0;
2351
        $cart_total        = 0;
2352
        $fee_total         = 0;
2353
        $cart_subtotal_tax = 0;
2354
        $cart_total_tax    = 0;
2355
2356
        if ( $and_taxes && wc_tax_enabled() ) {
2357
            $this->calculate_taxes();
2358
        }
2359
2360
        // line items
2361
        foreach ( $this->get_items() as $item ) {
2362
            $cart_subtotal     += wc_format_decimal( isset( $item['line_subtotal'] ) ? $item['line_subtotal'] : 0 );
2363
            $cart_total        += wc_format_decimal( isset( $item['line_total'] ) ? $item['line_total'] : 0 );
2364
            $cart_subtotal_tax += wc_format_decimal( isset( $item['line_subtotal_tax'] ) ? $item['line_subtotal_tax'] : 0 );
2365
            $cart_total_tax    += wc_format_decimal( isset( $item['line_tax'] ) ? $item['line_tax'] : 0 );
2366
        }
2367
2368
        $this->calculate_shipping();
2369
2370
        foreach ( $this->get_fees() as $item ) {
2371
            $fee_total += $item['line_total'];
2372
        }
2373
2374
        $this->set_total( $cart_subtotal - $cart_total, 'cart_discount' );
2375
        $this->set_total( $cart_subtotal_tax - $cart_total_tax, 'cart_discount_tax' );
2376
2377
        $grand_total = round( $cart_total + $fee_total + $this->get_total_shipping() + $this->get_cart_tax() + $this->get_shipping_tax(), wc_get_price_decimals() );
2378
2379
        $this->set_total( $grand_total, 'total' );
2380
2381
        return $grand_total;
2382
    }
2383
2384
    /*
2385
    |--------------------------------------------------------------------------
2386
    | Total Getters
2387
    |--------------------------------------------------------------------------
2388
    |
2389
    | Methods for getting totals e.g. for display on the frontend.
2390
    |
2391
    */
2392
2393
    /**
2394
     * Get a product (either product or variation).
2395
     *
2396
     * @param object $item
2397
     * @return WC_Product|bool
2398
     */
2399
    public function get_product_from_item( $item ) {
2400
        if ( is_callable( array( $item, 'get_product' ) ) ) {
2401
            $product = $item->get_product();
2402
        } else {
2403
            $product = false;
2404
        }
2405
        return apply_filters( 'woocommerce_get_product_from_item', $product, $item, $this );
2406
    }
2407
2408
    /**
2409
     * Get item subtotal - this is the cost before discount.
2410
     *
2411
     * @param object $item
2412
     * @param bool $inc_tax (default: false).
2413
     * @param bool $round (default: true).
2414
     * @return float
2415
     */
2416 View Code Duplication
    public function get_item_subtotal( $item, $inc_tax = false, $round = true ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2417
        $subtotal = 0;
2418
2419
        if ( is_callable( array( $item, 'get_subtotal' ) ) ) {
2420
            if ( $inc_tax ) {
2421
                $subtotal = ( $item->get_subtotal() + $item->get_subtotal_tax() ) / max( 1, $item->get_qty() );
2422
            } else {
2423
                $subtotal = ( $item->get_subtotal() / max( 1, $item->get_qty() ) );
2424
            }
2425
2426
            $subtotal = $round ? number_format( (float) $subtotal, wc_get_price_decimals(), '.', '' ) : $subtotal;
2427
        }
2428
2429
        return apply_filters( 'woocommerce_order_amount_item_subtotal', $subtotal, $this, $item, $inc_tax, $round );
2430
    }
2431
2432
    /**
2433
     * Get line subtotal - this is the cost before discount.
2434
     *
2435
     * @param object $item
2436
     * @param bool $inc_tax (default: false).
2437
     * @param bool $round (default: true).
2438
     * @return float
2439
     */
2440 View Code Duplication
    public function get_line_subtotal( $item, $inc_tax = false, $round = true ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2441
        $subtotal = 0;
2442
2443
        if ( is_callable( array( $item, 'get_subtotal' ) ) ) {
2444
            if ( $inc_tax ) {
2445
                $subtotal = $item->get_subtotal() + $item->get_subtotal_tax();
2446
            } else {
2447
                $subtotal = $item->get_subtotal();
2448
            }
2449
2450
            $subtotal = $round ? round( $subtotal, wc_get_price_decimals() ) : $subtotal;
2451
        }
2452
2453
        return apply_filters( 'woocommerce_order_amount_line_subtotal', $subtotal, $this, $item, $inc_tax, $round );
2454
    }
2455
2456
    /**
2457
     * Calculate item cost - useful for gateways.
2458
     *
2459
     * @param object $item
2460
     * @param bool $inc_tax (default: false).
2461
     * @param bool $round (default: true).
2462
     * @return float
2463
     */
2464 View Code Duplication
    public function get_item_total( $item, $inc_tax = false, $round = true ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2465
        $total = 0;
2466
2467
        if ( is_callable( array( $item, 'get_total' ) ) ) {
2468
            if ( $inc_tax ) {
2469
                $total = ( $item->get_total() + $item->get_total_tax() ) / max( 1, $item->get_qty() );
2470
            } else {
2471
                $total = $item->get_total() / max( 1, $item->get_qty() );
2472
            }
2473
2474
            $total = $round ? round( $total, wc_get_price_decimals() ) : $total;
2475
        }
2476
2477
        return apply_filters( 'woocommerce_order_amount_item_total', $total, $this, $item, $inc_tax, $round );
2478
    }
2479
2480
    /**
2481
     * Calculate line total - useful for gateways.
2482
     *
2483
     * @param object $item
2484
     * @param bool $inc_tax (default: false).
2485
     * @param bool $round (default: true).
2486
     * @return float
2487
     */
2488 View Code Duplication
    public function get_line_total( $item, $inc_tax = false, $round = true ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2489
        $total = 0;
2490
2491
        if ( is_callable( array( $item, 'get_total' ) ) ) {
2492
            // Check if we need to add line tax to the line total.
2493
            $total = $inc_tax ? $item->get_total() + $item->get_total_tax() : $item->get_total();
2494
2495
            // Check if we need to round.
2496
            $total = $round ? round( $total, wc_get_price_decimals() ) : $total;
2497
        }
2498
2499
        return apply_filters( 'woocommerce_order_amount_line_total', $total, $this, $item, $inc_tax, $round );
2500
    }
2501
2502
    /**
2503
     * Get item tax - useful for gateways.
2504
     *
2505
     * @param mixed $item
2506
     * @param bool $round (default: true).
2507
     * @return float
2508
     */
2509
    public function get_item_tax( $item, $round = true ) {
2510
        $tax = 0;
2511
2512
        if ( is_callable( array( $item, 'get_total_tax' ) ) ) {
2513
            $tax = $item->get_total_tax() / max( 1, $item->get_qty() );
2514
            $tax = $round ? wc_round_tax_total( $tax ) : $tax;
2515
        }
2516
2517
        return apply_filters( 'woocommerce_order_amount_item_tax', $tax, $item, $round, $this );
2518
    }
2519
2520
    /**
2521
     * Get line tax - useful for gateways.
2522
     *
2523
     * @param mixed $item
2524
     * @return float
2525
     */
2526
    public function get_line_tax( $item ) {
2527
        return apply_filters( 'woocommerce_order_amount_line_tax', is_callable( array( $item, 'get_total_tax' ) ) ? wc_round_tax_total( $item->get_total_tax() ) : 0, $item, $this );
2528
    }
2529
2530
    /**
2531
     * Gets line subtotal - formatted for display.
2532
     *
2533
     * @param array  $item
2534
     * @param string $tax_display
2535
     * @return string
2536
     */
2537
    public function get_formatted_line_subtotal( $item, $tax_display = '' ) {
2538
        $tax_display = $tax_display ? $tax_display : get_option( 'woocommerce_tax_display_cart' );
2539
2540
        if ( ! isset( $item['line_subtotal'] ) || ! isset( $item['line_subtotal_tax'] ) ) {
2541
            return '';
2542
        }
2543
2544
        if ( 'excl' == $tax_display ) {
2545
            $ex_tax_label = $this->get_prices_include_tax() ? 1 : 0;
2546
2547
            $subtotal = wc_price( $this->get_line_subtotal( $item ), array( 'ex_tax_label' => $ex_tax_label, 'currency' => $this->get_order_currency() ) );
0 ignored issues
show
Documentation introduced by
$item is of type array<string,?,{"line_su...ine_subtotal_tax":"?"}>, but the function expects a object.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
2548
        } else {
2549
            $subtotal = wc_price( $this->get_line_subtotal( $item, true ), array('currency' => $this->get_order_currency()) );
0 ignored issues
show
Documentation introduced by
$item is of type array<string,?,{"line_su...ine_subtotal_tax":"?"}>, but the function expects a object.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
2550
        }
2551
2552
        return apply_filters( 'woocommerce_order_formatted_line_subtotal', $subtotal, $item, $this );
2553
    }
2554
2555
    /**
2556
     * Gets order total - formatted for display.
2557
     *
2558
     * @return string
2559
     */
2560
    public function get_formatted_order_total() {
2561
        $formatted_total = wc_price( $this->get_total(), array( 'currency' => $this->get_order_currency() ) );
2562
        return apply_filters( 'woocommerce_get_formatted_order_total', $formatted_total, $this );
2563
    }
2564
2565
    /**
2566
     * Gets subtotal - subtotal is shown before discounts, but with localised taxes.
2567
     *
2568
     * @param bool $compound (default: false).
2569
     * @param string $tax_display (default: the tax_display_cart value).
2570
     * @return string
2571
     */
2572
    public function get_subtotal_to_display( $compound = false, $tax_display = '' ) {
2573
        $tax_display = $tax_display ? $tax_display : get_option( 'woocommerce_tax_display_cart' );
2574
        $subtotal    = 0;
2575
2576
        if ( ! $compound ) {
2577
            foreach ( $this->get_items() as $item ) {
2578
                $subtotal += $item->get_subtotal();
2579
2580
                if ( 'incl' === $tax_display ) {
2581
                    $subtotal += $item->get_subtotal_tax();
2582
                }
2583
            }
2584
2585
            $subtotal = wc_price( $subtotal, array( 'currency' => $this->get_order_currency() ) );
2586
2587
            if ( 'excl' === $tax_display && $this->get_prices_include_tax() ) {
2588
                $subtotal .= ' <small class="tax_label">' . WC()->countries->ex_tax_or_vat() . '</small>';
2589
            }
2590
2591
        } else {
2592
            if ( 'incl' === $tax_display ) {
2593
                return '';
2594
            }
2595
2596
            foreach ( $this->get_items() as $item ) {
2597
                $subtotal += $item->get_subtotal();
2598
            }
2599
2600
            // Add Shipping Costs.
2601
            $subtotal += $this->get_total_shipping();
2602
2603
            // Remove non-compound taxes.
2604
            foreach ( $this->get_taxes() as $tax ) {
2605
                if ( ! empty( $tax['compound'] ) ) {
2606
                    continue;
2607
                }
2608
                $subtotal = $subtotal + $tax['tax_amount'] + $tax['shipping_tax_amount'];
2609
            }
2610
2611
            // Remove discounts.
2612
            $subtotal = $subtotal - $this->get_total_discount();
2613
            $subtotal = wc_price( $subtotal, array( 'currency' => $this->get_order_currency() ) );
2614
        }
2615
2616
        return apply_filters( 'woocommerce_order_subtotal_to_display', $subtotal, $compound, $this );
2617
    }
2618
2619
    /**
2620
     * Gets shipping (formatted).
2621
     *
2622
     * @return string
2623
     */
2624
    public function get_shipping_to_display( $tax_display = '' ) {
2625
        $tax_display = $tax_display ? $tax_display : get_option( 'woocommerce_tax_display_cart' );
2626
2627
        if ( $this->order_shipping != 0 ) {
2628
2629
            if ( $tax_display == 'excl' ) {
2630
2631
                // Show shipping excluding tax.
2632
                $shipping = wc_price( $this->order_shipping, array('currency' => $this->get_order_currency()) );
2633
2634 View Code Duplication
                if ( $this->order_shipping_tax != 0 && $this->prices_include_tax ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2635
                    $shipping .= apply_filters( 'woocommerce_order_shipping_to_display_tax_label', '&nbsp;<small class="tax_label">' . WC()->countries->ex_tax_or_vat() . '</small>', $this, $tax_display );
2636
                }
2637
2638
            } else {
2639
2640
                // Show shipping including tax.
2641
                $shipping = wc_price( $this->order_shipping + $this->order_shipping_tax, array('currency' => $this->get_order_currency()) );
2642
2643 View Code Duplication
                if ( $this->order_shipping_tax != 0 && ! $this->prices_include_tax ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2644
                    $shipping .= apply_filters( 'woocommerce_order_shipping_to_display_tax_label', '&nbsp;<small class="tax_label">' . WC()->countries->inc_tax_or_vat() . '</small>', $this, $tax_display );
2645
                }
2646
2647
            }
2648
2649
            $shipping .= apply_filters( 'woocommerce_order_shipping_to_display_shipped_via', '&nbsp;<small class="shipped_via">' . sprintf( __( 'via %s', 'woocommerce' ), $this->get_shipping_method() ) . '</small>', $this );
2650
2651
        } elseif ( $this->get_shipping_method() ) {
2652
            $shipping = $this->get_shipping_method();
2653
        } else {
2654
            $shipping = __( 'Free!', 'woocommerce' );
2655
        }
2656
2657
        return apply_filters( 'woocommerce_order_shipping_to_display', $shipping, $this );
2658
    }
2659
2660
    /**
2661
     * Get the discount amount (formatted).
2662
     * @since  2.3.0
2663
     * @return string
2664
     */
2665
    public function get_discount_to_display( $tax_display = '' ) {
2666
        $tax_display = $tax_display ? $tax_display : get_option( 'woocommerce_tax_display_cart' );
2667
        return apply_filters( 'woocommerce_order_discount_to_display', wc_price( $this->get_total_discount( 'excl' === $tax_display && 'excl' === get_option( 'woocommerce_tax_display_cart' ) ), array( 'currency' => $this->get_order_currency() ) ), $this );
2668
    }
2669
2670
    /**
2671
     * Get totals for display on pages and in emails.
2672
     *
2673
     * @param mixed $tax_display
2674
     * @return array
2675
     */
2676
    public function get_order_item_totals( $tax_display = '' ) {
2677
        $tax_display = $tax_display ? $tax_display : get_option( 'woocommerce_tax_display_cart' );
2678
        $total_rows  = array();
2679
2680
        if ( $subtotal = $this->get_subtotal_to_display( false, $tax_display ) ) {
2681
            $total_rows['cart_subtotal'] = array(
2682
                'label' => __( 'Subtotal:', 'woocommerce' ),
2683
                'value'    => $subtotal
2684
            );
2685
        }
2686
2687
        if ( $this->get_total_discount() > 0 ) {
2688
            $total_rows['discount'] = array(
2689
                'label' => __( 'Discount:', 'woocommerce' ),
2690
                'value'    => '-' . $this->get_discount_to_display( $tax_display )
2691
            );
2692
        }
2693
2694
        if ( $this->get_shipping_method() ) {
2695
            $total_rows['shipping'] = array(
2696
                'label' => __( 'Shipping:', 'woocommerce' ),
2697
                'value'    => $this->get_shipping_to_display( $tax_display )
2698
            );
2699
        }
2700
2701
        if ( $fees = $this->get_fees() ) {
2702
            foreach ( $fees as $id => $fee ) {
2703
                if ( apply_filters( 'woocommerce_get_order_item_totals_excl_free_fees', empty( $fee['line_total'] ) && empty( $fee['line_tax'] ), $id ) ) {
2704
                    continue;
2705
                }
2706
                $total_rows[ 'fee_' . $fee->get_order_item_id() ] = array(
2707
                    'label' => $fee->get_name() . ':',
2708
                    'value' => wc_price( 'excl' === $tax_display ? $fee->get_total() : $fee->get_total() + $fee->get_total_tax(), array('currency' => $this->get_order_currency()) )
2709
                );
2710
            }
2711
        }
2712
2713
        // Tax for tax exclusive prices.
2714
        if ( 'excl' === $tax_display ) {
2715
2716
            if ( get_option( 'woocommerce_tax_total_display' ) == 'itemized' ) {
2717
2718
                foreach ( $this->get_tax_totals() as $code => $tax ) {
2719
2720
                    $total_rows[ sanitize_title( $code ) ] = array(
2721
                        'label' => $tax->label . ':',
2722
                        'value'    => $tax->formatted_amount
2723
                    );
2724
                }
2725
2726
            } else {
2727
2728
                $total_rows['tax'] = array(
2729
                    'label' => WC()->countries->tax_or_vat() . ':',
2730
                    'value'    => wc_price( $this->get_total_tax(), array( 'currency' => $this->get_order_currency() ) )
2731
                );
2732
            }
2733
        }
2734
2735
        if ( $this->get_total() > 0 && $this->get_payment_method_title() ) {
2736
            $total_rows['payment_method'] = array(
2737
                'label' => __( 'Payment Method:', 'woocommerce' ),
2738
                'value' => $this->get_payment_method_title()
2739
            );
2740
        }
2741
2742
        if ( $refunds = $this->get_refunds() ) {
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class WC_Abstract_Order as the method get_refunds() does only exist in the following sub-classes of WC_Abstract_Order: WC_Order. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
2743
            foreach ( $refunds as $id => $refund ) {
2744
                $total_rows[ 'refund_' . $id ] = array(
2745
                    'label' => $refund->get_refund_reason() ? $refund->get_refund_reason() : __( 'Refund', 'woocommerce' ) . ':',
2746
                    'value'    => wc_price( '-' . $refund->get_refund_amount(), array( 'currency' => $this->get_order_currency() ) )
2747
                );
2748
            }
2749
        }
2750
2751
        $total_rows['order_total'] = array(
2752
            'label' => __( 'Total:', 'woocommerce' ),
2753
            'value'    => $this->get_formatted_order_total( $tax_display )
0 ignored issues
show
Unused Code introduced by
The call to WC_Abstract_Order::get_formatted_order_total() has too many arguments starting with $tax_display.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
2754
        );
2755
2756
        return apply_filters( 'woocommerce_get_order_item_totals', $total_rows, $this );
2757
    }
2758
2759
    /*
2760
    |--------------------------------------------------------------------------
2761
    | Conditionals
2762
    |--------------------------------------------------------------------------
2763
    |
2764
    | Checks if a condition is true or false.
2765
    |
2766
    */
2767
2768
    /**
2769
     * Checks the order status against a passed in status.
2770
     *
2771
     * @return bool
2772
     */
2773
    public function has_status( $status ) {
2774
        return apply_filters( 'woocommerce_order_has_status', ( is_array( $status ) && in_array( $this->get_status(), $status ) ) || $this->get_status() === $status ? true : false, $this, $status );
2775
    }
2776
2777
    /**
2778
     * has_meta function for order items.
2779
     *
2780
     * @param string $order_item_id
2781
     * @return array of meta data.
2782
     */
2783
    public function has_meta( $order_item_id ) {
2784
        global $wpdb;
2785
2786
        return $wpdb->get_results( $wpdb->prepare( "SELECT meta_key, meta_value, meta_id, order_item_id
2787
            FROM {$wpdb->prefix}woocommerce_order_itemmeta WHERE order_item_id = %d
2788
            ORDER BY meta_id", absint( $order_item_id ) ), ARRAY_A );
2789
    }
2790
2791
    /**
2792
     * Check whether this order has a specific shipping method or not.
2793
     *
2794
     * @param string $method_id
2795
     * @return bool
2796
     */
2797
    public function has_shipping_method( $method_id ) {
2798
        foreach ( $this->get_shipping_methods() as $shipping_method ) {
2799
            if ( $shipping_method->get_method_id() === $method_id ) {
2800
                return true;
2801
            }
2802
        }
2803
        return false;
2804
    }
2805
2806
    /**
2807
     * Check if an order key is valid.
2808
     *
2809
     * @param mixed $key
2810
     * @return bool
2811
     */
2812
    public function key_is_valid( $key ) {
2813
        return $key === $this->get_order_key();
2814
    }
2815
2816
    /**
2817
     * Returns true if the order contains a free product.
2818
     * @since 2.5.0
2819
     * @return bool
2820
     */
2821
    public function has_free_item() {
2822
        foreach ( $this->get_items() as $item ) {
2823
            if ( ! $item->get_total() ) {
2824
                return true;
2825
            }
2826
        }
2827
        return false;
2828
    }
2829
2830
    /*
2831
    |--------------------------------------------------------------------------
2832
    | Deprecated methods
2833
    |--------------------------------------------------------------------------
2834
    |
2835
    | Will be removed after 2 major releases, or 1 year.
2836
    |
2837
    */
2838
2839
    /**
2840
     * Magic __isset method for backwards compatibility.
2841
     * @param string $key
2842
     * @return bool
2843
     */
2844
    public function __isset( $key ) {
2845
        // Legacy properties which could be accessed directly in the past.
2846
        $legacy_props = array( 'completed_date', 'id', 'order_type', 'post', 'status', 'post_status', 'customer_note', 'customer_message', 'user_id', 'customer_user', 'prices_include_tax', 'tax_display_cart', 'display_totals_ex_tax', 'display_cart_ex_tax', 'order_date', 'modified_date', 'cart_discount', 'cart_discount_tax', 'order_shipping', 'order_shipping_tax', 'order_total', 'order_tax', 'billing_first_name', 'billing_last_name', 'billing_company', 'billing_address_1', 'billing_address_2', 'billing_city', 'billing_state', 'billing_postcode', 'billing_country', 'billing_phone', 'billing_email', 'shipping_first_name', 'shipping_last_name', 'shipping_company', 'shipping_address_1', 'shipping_address_2', 'shipping_city', 'shipping_state', 'shipping_postcode', 'shipping_country', 'customer_ip_address', 'customer_user_agent', 'payment_method_title', 'payment_method', 'order_currency' );
2847
        return $this->get_id() ? ( in_array( $key, $legacy_props ) || metadata_exists( 'post', $this->get_id(), '_' . $key ) ) : false;
2848
    }
2849
2850
    /**
2851
     * Magic __get method for backwards compatibility.
2852
     * @param string $key
2853
     * @return mixed
2854
     */
2855
    public function __get( $key ) {
2856
        /**
2857
         * Maps legacy vars to new getters.
2858
         */
2859
        if ( 'completed_date' === $key ) {
2860
            _doing_it_wrong( $key, 'Order properties should not be accessed directly.', '2.6' );
2861
            return $this->get_date_completed();
2862
		} elseif ( 'paid_date' === $key ) {
2863
            _doing_it_wrong( $key, 'Order properties should not be accessed directly.', '2.6' );
2864
            return $this->get_date_paid();
2865
        } elseif ( 'modified_date' === $key ) {
2866
            _doing_it_wrong( $key, 'Order properties should not be accessed directly.', '2.6' );
2867
            return $this->get_date_modified();
2868
        } elseif ( 'order_date' === $key ) {
2869
            _doing_it_wrong( $key, 'Order properties should not be accessed directly.', '2.6' );
2870
            return $this->get_date_created();
2871
        } elseif ( 'id' === $key ) {
2872
            _doing_it_wrong( $key, 'Order properties should not be accessed directly.', '2.6' );
2873
            return $this->get_id();
2874
		} elseif ( 'post' === $key ) {
2875
            _doing_it_wrong( $key, 'Order properties should not be accessed directly.', '2.6' );
2876
            return get_post( $this->get_id() );
2877
		} elseif ( 'status' === $key || 'post_status' === $key ) {
2878
            _doing_it_wrong( $key, 'Order properties should not be accessed directly.', '2.6' );
2879
            return $this->get_status();
2880
		} elseif ( 'customer_message' === $key || 'customer_note' === $key ) {
2881
            _doing_it_wrong( $key, 'Order properties should not be accessed directly.', '2.6' );
2882
            return $this->get_customer_note();
2883
		} elseif ( in_array( $key, array( 'user_id', 'customer_user' ) ) ) {
2884
            _doing_it_wrong( $key, 'Order properties should not be accessed directly.', '2.6' );
2885
            return $this->get_customer_id();
2886
		} elseif ( 'tax_display_cart' === $key ) {
2887
            _doing_it_wrong( $key, 'Order properties should not be accessed directly.', '2.6' );
2888
			return get_option( 'woocommerce_tax_display_cart' );
2889
		} elseif ( 'display_totals_ex_tax' === $key ) {
2890
            _doing_it_wrong( $key, 'Order properties should not be accessed directly.', '2.6' );
2891
			return 'excl' === get_option( 'woocommerce_tax_display_cart' );
2892
		} elseif ( 'display_cart_ex_tax' === $key ) {
2893
            _doing_it_wrong( $key, 'Order properties should not be accessed directly.', '2.6' );
2894
			return 'excl' === get_option( 'woocommerce_tax_display_cart' );
2895
        } elseif ( 'cart_discount' === $key ) {
2896
            _doing_it_wrong( $key, 'Order properties should not be accessed directly.', '2.6' );
2897
			return $this->get_discount();
0 ignored issues
show
Bug introduced by
The method get_discount() does not exist on WC_Abstract_Order. Did you maybe mean get_discount_total()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
2898
        } elseif ( 'cart_discount_tax' === $key ) {
2899
            _doing_it_wrong( $key, 'Order properties should not be accessed directly.', '2.6' );
2900
			return $this->get_discount_tax();
2901
        } elseif ( 'order_tax' === $key ) {
2902
            _doing_it_wrong( $key, 'Order properties should not be accessed directly.', '2.6' );
2903
			return $this->get_cart_tax();
2904
        } elseif ( 'order_shipping_tax' === $key ) {
2905
            _doing_it_wrong( $key, 'Order properties should not be accessed directly.', '2.6' );
2906
            return $this->get_shipping_tax();
2907
        } elseif ( 'order_shipping' === $key ) {
2908
            _doing_it_wrong( $key, 'Order properties should not be accessed directly.', '2.6' );
2909
            return $this->get_shipping();
0 ignored issues
show
Bug introduced by
The method get_shipping() does not exist on WC_Abstract_Order. Did you maybe mean get_shipping_first_name()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
2910
        /**
2911
         * Map vars to getters with warning.
2912
         */
2913
	 	} elseif ( is_callable( array( $this, "get_{$key}" ) ) ) {
2914
			_doing_it_wrong( $key, 'Order properties should not be accessed directly Use get_' . $key . '().', '2.6' );
2915
            return $this->{"get_{$key}"}();
2916
        /**
2917
         * Handle post meta
2918
         */
2919
        } else {
2920
            _doing_it_wrong( $key, 'Meta should not be accessed directly. Use WC_Order::get_order_meta( $key )', '2.6' );
2921
			$value = get_post_meta( $this->get_id(), '_' . $key, true );
2922
		}
2923
2924
        return $value;
2925
    }
2926
2927
    /**
2928
     * Get order item meta.
2929
     * @deprecated 2.6.0
2930
     * @param mixed $order_item_id
2931
     * @param string $key (default: '')
2932
     * @param bool $single (default: false)
2933
     * @return array|string
2934
     */
2935
    public function get_item_meta( $order_item_id, $key = '', $single = false ) {
2936
        _deprecated_function( 'get_item_meta', '2.6', 'wc_get_order_item_meta' );
2937
        return get_metadata( 'order_item', $order_item_id, $key, $single );
2938
    }
2939
2940
    /**
2941
     * Get all item meta data in array format in the order it was saved. Does not group meta by key like get_item_meta().
2942
     *
2943
     * @param mixed $order_item_id
2944
     * @return array of objects
2945
     */
2946
    public function get_item_meta_array( $order_item_id ) {
2947
        _deprecated_function( 'get_item_meta_array', '2.6', 'WC_Order_Item::get_meta_data()' );
2948
        $item = $this->get_item( $order_item_id );
2949
        return $item->get_meta_data();
2950
    }
2951
2952
    /**
2953
     * Expand item meta into the $item array.
2954
     * @deprecated 2.6.0 Item meta no longer expanded due to new order item
2955
     *        classes. This function now does nothing to avoid data breakage.
2956
     * @since 2.4.0
2957
     * @param array $item before expansion.
2958
     * @return array
2959
     */
2960
    public function expand_item_meta( $item ) {
2961
        _deprecated_function( 'expand_item_meta', '2.6', '' );
2962
        return $item;
2963
    }
2964
2965
	/**
2966
     * Load the order object. Called from the constructor.
2967
     * @deprecated 2.6.0 Logic moved to constructor
2968
     * @param int|object|WC_Order $order Order to init.
2969
     */
2970 View Code Duplication
    protected function init( $order ) {
1 ignored issue
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2971
		_deprecated_function( 'init', '2.6', 'Logic moved to constructor' );
2972
        if ( is_numeric( $order ) ) {
2973
            $this->read( $order );
2974
        } elseif ( $order instanceof WC_Order ) {
2975
            $this->read( absint( $order->get_id() ) );
2976
        } elseif ( isset( $order->ID ) ) {
2977
            $this->read( absint( $order->ID ) );
2978
        }
2979
    }
2980
2981
    /**
2982
     * Gets an order from the database.
2983
     * @deprecated 2.6
2984
     * @param int $id (default: 0).
2985
     * @return bool
2986
     */
2987 View Code Duplication
    public function get_order( $id = 0 ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2988
        _deprecated_function( 'get_order', '2.6', 'read' );
2989
        if ( ! $id ) {
2990
            return false;
2991
        }
2992
        if ( $result = get_post( $id ) ) {
2993
            $this->populate( $result );
0 ignored issues
show
Deprecated Code introduced by
The method WC_Abstract_Order::populate() has been deprecated with message: 2.6

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
2994
            return true;
2995
        }
2996
        return false;
2997
    }
2998
2999
    /**
3000
     * Populates an order from the loaded post data.
3001
     * @deprecated 2.6
3002
     * @param mixed $result
3003
     */
3004
    public function populate( $result ) {
3005
        _deprecated_function( 'populate', '2.6', 'read' );
3006
        $this->read( $result->ID );
3007
    }
3008
3009
	/**
3010
     * Cancel the order and restore the cart (before payment).
3011
     * @deprecated 2.6.0 Moved to event handler.
3012
     * @param string $note (default: '') Optional note to add.
3013
     */
3014
    public function cancel_order( $note = '' ) {
3015
		_deprecated_function( 'cancel_order', '2.6', 'update_status' );
3016
        WC()->session->set( 'order_awaiting_payment', false );
3017
        $this->update_status( 'cancelled', $note );
3018
    }
3019
3020
	/**
3021
     * Record sales.
3022
     * @deprecated 2.6.0
3023
     */
3024
    public function record_product_sales() {
3025
		_deprecated_function( 'record_product_sales', '2.6', 'wc_update_total_sales_counts' );
3026
		wc_update_total_sales_counts( $this->get_id() );
3027
    }
3028
3029
	/**
3030
     * Increase applied coupon counts.
3031
     * @deprecated 2.6.0
3032
     */
3033
    public function increase_coupon_usage_counts() {
3034
		_deprecated_function( 'increase_coupon_usage_counts', '2.6', 'wc_update_coupon_usage_counts' );
3035
		wc_update_coupon_usage_counts( $this->get_id() );
3036
    }
3037
3038
    /**
3039
     * Decrease applied coupon counts.
3040
     * @deprecated 2.6.0
3041
     */
3042
    public function decrease_coupon_usage_counts() {
3043
		_deprecated_function( 'decrease_coupon_usage_counts', '2.6', 'wc_update_coupon_usage_counts' );
3044
		wc_update_coupon_usage_counts( $this->get_id() );
3045
    }
3046
3047
	/**
3048
     * Reduce stock levels for all line items in the order.
3049
	 * @deprecated 2.6.0
3050
     */
3051
    public function reduce_order_stock() {
3052
        _deprecated_function( 'reduce_order_stock', '2.6', 'wc_reduce_stock_levels' );
3053
		wc_reduce_stock_levels( $this->get_id() );
3054
    }
3055
3056
	/**
3057
     * Send the stock notifications.
3058
	 * @deprecated 2.6.0 No longer needs to be called directly.
3059
     */
3060
    public function send_stock_notifications( $product, $new_stock, $qty_ordered ) {
0 ignored issues
show
Unused Code introduced by
The parameter $product is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $new_stock is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $qty_ordered is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
3061
        _deprecated_function( 'send_stock_notifications', '2.6' );
3062
    }
3063
3064
	/**
3065
	 * Output items for display in html emails.
3066
	 * @deprecated 2.6.0 Moved to template functions.
3067
	 * @param array $args Items args.
3068
	 * @return string
3069
	 */
3070
	public function email_order_items_table( $args = array() ) {
3071
		return wc_get_email_order_items( $this, $args );
0 ignored issues
show
Compatibility introduced by
$this of type object<WC_Abstract_Order> is not a sub-type of object<WC_Order>. It seems like you assume a child class of the class WC_Abstract_Order to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
3072
	}
3073
}
3074