Completed
Pull Request — master (#10259)
by Mike
12:26
created

WC_Abstract_Order::has_shipping_method()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 8
rs 9.4285
cc 3
eloc 5
nc 3
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
    /**
124
     * Get Meta Data by Key
125
     * @param  string $key
126
     * @return mixed
127
     */
128
    public function get_meta( $key = null ){
129
		if ( is_null( $key ) ) {
130
			return $this->_meta;
131
		} else {
132
        	return isset( $this->_meta[ $key ] ) ? $this->_meta[ $key ] : null;
133
		}
134
    }
135
136
    /*
137
    |--------------------------------------------------------------------------
138
    | Getters
139
    |--------------------------------------------------------------------------
140
    |
141
    | Methods for getting data from the order object.
142
    |
143
    */
144
145
    /**
146
     * Get all class data in array format.
147
     * @since 2.6.0
148
     * @return array
149
     */
150
    public function get_data() {
151
        return array_merge(
152
            $this->_data,
153
            $this->_meta,
154
            array(
155
                'line_items'     => $this->get_items( 'line_item' ),
156
                'tax_lines'      => $this->get_items( 'tax' ),
157
                'shipping_lines' => $this->get_items( 'shipping' ),
158
                'fee_lines'      => $this->get_items( 'fee' ),
159
                'coupon_lines'   => $this->get_items( 'coupon' ),
160
            )
161
        );
162
    }
163
164
    /**
165
     * Get order ID.
166
     * @since 2.6.0
167
     * @return integer
168
     */
169
    public function get_id() {
170
        return $this->get_order_id();
171
    }
172
173
    /**
174
     * Get order ID.
175
     * @since 2.6.0
176
     * @return integer
177
     */
178
    public function get_order_id() {
179
        return absint( $this->_data['order_id'] );
180
    }
181
182
    /**
183
     * Get parent order ID.
184
     * @since 2.6.0
185
     * @return integer
186
     */
187
    public function get_parent_id() {
188
        return absint( $this->_data['parent_id'] );
189
    }
190
191
    /**
192
     * get_order_number function.
193
     *
194
     * Gets the order number for display (by default, order ID).
195
     *
196
     * @return string
197
     */
198
    public function get_order_number() {
199
        return apply_filters( 'woocommerce_order_number', $this->get_id(), $this );
200
    }
201
202
    /**
203
     * Get order key.
204
     * @since 2.6.0
205
     * @return string
206
     */
207
    public function get_order_key() {
208
        return $this->_data['order_key'];
209
    }
210
211
    /**
212
     * Gets order currency.
213
     * @return string
214
     */
215
    public function get_order_currency() {
216
        return apply_filters( 'woocommerce_get_order_currency', $this->_data['order_currency'], $this );
217
    }
218
219
    /**
220
     * Get Order Type
221
     * @return string
222
     */
223
    public function get_order_type() {
224
        return $this->_data['order_type'];
225
    }
226
227
    /**
228
     * Get date_created
229
     * @return string
230
     */
231
    public function get_date_created() {
232
        return $this->_data['date_created'];
233
    }
234
235
    /**
236
     * Get date_modified
237
     * @return string
238
     */
239
    public function get_date_modified() {
240
        return $this->_data['date_modified'];
241
    }
242
243
    /**
244
     * Get customer_id
245
     * @return int
246
     */
247
    public function get_customer_id() {
248
        return absint( $this->_data['customer_id'] );
249
    }
250
251
    /**
252
     * Get billing_first_name
253
     * @return string
254
     */
255
    public function get_billing_first_name() {
256
        return $this->_data['billing_first_name'];
257
    }
258
259
    /**
260
     * Get billing_last_name
261
     * @return string
262
     */
263
    public function get_billing_last_name() {
264
        return $this->_data['billing_last_name'];
265
    }
266
267
    /**
268
     * Get billing_company
269
     * @return string
270
     */
271
    public function get_billing_company() {
272
        return $this->_data['billing_company'];
273
    }
274
275
    /**
276
     * Get billing_address_1
277
     * @return string
278
     */
279
    public function get_billing_address_1() {
280
        return $this->_data['billing_address_1'];
281
    }
282
283
    /**
284
     * Get billing_address_2
285
     * @return string $value
286
     */
287
    public function get_billing_address_2() {
288
        return $this->_data['billing_address_2'];
289
    }
290
291
    /**
292
     * Get billing_city
293
     * @return string $value
294
     */
295
    public function get_billing_city() {
296
        return $this->_data['billing_city'];
297
    }
298
299
    /**
300
     * Get billing_state
301
     * @return string
302
     */
303
    public function get_billing_state() {
304
        return $this->_data['billing_state'];
305
    }
306
307
    /**
308
     * Get billing_postcode
309
     * @return string
310
     */
311
    public function get_billing_postcode() {
312
        return $this->_data['billing_postcode'];
313
    }
314
315
    /**
316
     * Get billing_country
317
     * @return string
318
     */
319
    public function get_billing_country() {
320
        return $this->_data['billing_country'];
321
    }
322
323
    /**
324
     * Get billing_email
325
     * @return string
326
     */
327
    public function get_billing_email() {
328
        return sanitize_email( $this->_data['billing_email'] );
329
    }
330
331
    /**
332
     * Get billing_phone
333
     * @return string
334
     */
335
    public function get_billing_phone() {
336
        return $this->_data['billing_phone'];
337
    }
338
339
    /**
340
     * Get shipping_first_name
341
     * @return string
342
     */
343
    public function get_shipping_first_name() {
344
        return $this->_data['shipping_first_name'];
345
    }
346
347
    /**
348
     * Get shipping_last_name
349
     * @return string
350
     */
351
    public function get_shipping_last_name() {
352
         return $this->_data['shipping_last_name'];
353
    }
354
355
    /**
356
     * Get shipping_company
357
     * @return string
358
     */
359
    public function get_shipping_company() {
360
        return $this->_data['shipping_company'];
361
    }
362
363
    /**
364
     * Get shipping_address_1
365
     * @return string
366
     */
367
    public function get_shipping_address_1() {
368
        return $this->_data['shipping_address_1'];
369
    }
370
371
    /**
372
     * Get shipping_address_2
373
     * @return string
374
     */
375
    public function get_shipping_address_2() {
376
        return $this->_data['shipping_address_2'];
377
    }
378
379
    /**
380
     * Get shipping_city
381
     * @return string
382
     */
383
    public function get_shipping_city() {
384
        return $this->_data['shipping_city'];
385
    }
386
387
    /**
388
     * Get shipping_state
389
     * @return string
390
     */
391
    public function get_shipping_state() {
392
        return $this->_data['shipping_state'];
393
    }
394
395
    /**
396
     * Get shipping_postcode
397
     * @return string
398
     */
399
    public function get_shipping_postcode() {
400
        return $this->_data['shipping_postcode'];
401
    }
402
403
    /**
404
     * Get shipping_country
405
     * @return string
406
     */
407
    public function get_shipping_country() {
408
        return $this->_data['shipping_country'];
409
    }
410
411
    /**
412
     * Get the payment method.
413
     * @return string
414
     */
415
    public function get_payment_method() {
416
        return $this->_meta['payment_method'];
417
    }
418
419
    /**
420
     * Get payment_method_title
421
     * @return string
422
     */
423
    public function get_payment_method_title() {
424
        return $this->_meta['payment_method_title'];
425
    }
426
427
    /**
428
     * Get transaction_id
429
     * @return string
430
     */
431
    public function get_transaction_id() {
432
        return $this->_meta['transaction_id'];
433
    }
434
435
    /**
436
     * Get customer_ip_address
437
     * @return string
438
     */
439
    public function get_customer_ip_address() {
440
        return $this->_meta['customer_ip_address'];
441
    }
442
443
    /**
444
     * Get customer_user_agent
445
     * @return string
446
     */
447
    public function get_customer_user_agent() {
448
        return $this->_meta['customer_user_agent'];
449
    }
450
451
    /**
452
     * Get created_via
453
     * @return string
454
     */
455
    public function get_created_via() {
456
        return $this->_meta['created_via'];
457
    }
458
459
    /**
460
     * Get order_version
461
     * @return string
462
     */
463
    public function get_order_version() {
464
        return $this->_meta['order_version'];
465
    }
466
467
    /**
468
     * Get prices_include_tax
469
     * @return bool
470
     */
471
    public function get_prices_include_tax() {
472
        return (bool) $this->_meta['prices_include_tax'];
473
    }
474
475
    /**
476
     * Get customer_note
477
     * @return string
478
     */
479
    public function get_customer_note() {
480
        return $this->_meta['customer_note'];
481
    }
482
483
	/**
484
     * Get date_completed
485
     * @return string
486
     */
487
    public function get_date_completed() {
488
        return $this->_meta['date_completed'];
489
    }
490
491
	/**
492
     * Get date_paid
493
     * @return string
494
     */
495
    public function get_date_paid() {
496
        return $this->_meta['date_paid'];
497
    }
498
499
    /**
500
     * Returns the requested address in raw, non-formatted way.
501
     * @since  2.4.0
502
     * @param  string $type Billing or shipping. Anything else besides 'billing' will return shipping address.
503
     * @return array The stored address after filter.
504
     */
505
    public function get_address( $type = 'billing' ) {
506
        if ( 'billing' === $type ) {
507
            $address = array(
508
                'first_name' => $this->get_billing_first_name(),
509
                'last_name'  => $this->get_billing_last_name(),
510
                'company'    => $this->get_billing_company(),
511
                'address_1'  => $this->get_billing_address_1(),
512
                'address_2'  => $this->get_billing_address_2(),
513
                'city'       => $this->get_billing_city(),
514
                'state'      => $this->get_billing_state(),
515
                'postcode'   => $this->get_billing_postcode(),
516
                'country'    => $this->get_billing_country(),
517
                'email'      => $this->get_billing_email(),
518
                'phone'      => $this->get_billing_phone()
519
            );
520
        } else {
521
            $address = array(
522
                'first_name' => $this->get_shipping_first_name(),
523
                'last_name'  => $this->get_shipping_last_name(),
524
                'company'    => $this->get_shipping_company(),
525
                'address_1'  => $this->get_shipping_address_1(),
526
                'address_2'  => $this->get_shipping_address_2(),
527
                'city'       => $this->get_shipping_city(),
528
                'state'      => $this->get_shipping_state(),
529
                'postcode'   => $this->get_shipping_postcode(),
530
                'country'    => $this->get_shipping_country()
531
            );
532
        }
533
        return apply_filters( 'woocommerce_get_order_address', $address, $type, $this );
534
    }
535
536
    /**
537
     * Return the order statuses without wc- internal prefix.
538
     * @return string
539
     */
540
    public function get_status() {
541
        return apply_filters( 'woocommerce_order_get_status', 'wc-' === substr( $this->_data['status'], 0, 3 ) ? substr( $this->_data['status'], 3 ) : $this->_data['status'], $this );
542
    }
543
544
    /**
545
     * Alias for get_customer_id().
546
     * @since  2.2
547
     * @return int
548
     */
549
    public function get_user_id() {
550
        return $this->get_customer_id();
551
    }
552
553
    /**
554
     * Get the user associated with the order. False for guests.
555
     *
556
     * @since  2.2
557
     * @return WP_User|false
558
     */
559
    public function get_user() {
560
        return $this->get_user_id() ? get_user_by( 'id', $this->get_user_id() ) : false;
561
    }
562
563
    /**
564
     * Get a formatted billing address for the order.
565
     * @return string
566
     */
567
    public function get_formatted_billing_address() {
568
        return WC()->countries->get_formatted_address( apply_filters( 'woocommerce_order_formatted_billing_address', $this->get_address( 'billing' ), $this ) );
569
    }
570
571
    /**
572
     * Get a formatted shipping address for the order.
573
     * @return string
574
     */
575
    public function get_formatted_shipping_address() {
576
        if ( $this->get_shipping_address_1() || $this->get_shipping_address_2() ) {
577
            return WC()->countries->get_formatted_address( apply_filters( 'woocommerce_order_formatted_shipping_address', $this->get_address( 'shipping' ), $this ) );
578
        } else {
579
            return '';
580
        }
581
    }
582
583
    /**
584
     * Get a formatted shipping address for the order.
585
     *
586
     * @return string
587
     */
588
    public function get_shipping_address_map_url() {
589
        $address = apply_filters( 'woocommerce_shipping_address_map_url_parts', array(
590
            'address_1' => $this->get_shipping_address_1(),
591
            'address_2' => $this->get_shipping_address_2(),
592
            'city'      => $this->get_shipping_city(),
593
            'state'     => $this->get_shipping_state(),
594
            'postcode'  => $this->get_shipping_postcode(),
595
            'country'   => $this->get_shipping_country()
596
        ), $this );
597
        return apply_filters( 'woocommerce_shipping_address_map_url', 'http://maps.google.com/maps?&q=' . urlencode( implode( ', ', $address ) ) . '&z=16', $this );
598
    }
599
600
    /**
601
     * Get a formatted billing full name.
602
     *
603
     * @since 2.4.0
604
     *
605
     * @return string
606
     */
607
    public function get_formatted_billing_full_name() {
608
        return sprintf( _x( '%1$s %2$s', 'full name', 'woocommerce' ),  $this->get_billing_first_name(), $this->get_billing_last_name() );
609
    }
610
611
    /**
612
     * Get a formatted shipping full name.
613
     *
614
     * @since 2.4.0
615
     *
616
     * @return string
617
     */
618
    public function get_formatted_shipping_full_name() {
619
        return sprintf( _x( '%1$s %2$s', 'full name', 'woocommerce' ),  $this->get_shipping_first_name(), $this->get_shipping_last_name() );
620
    }
621
622
    /**
623
     * Get discount_total
624
     * @return string
625
     */
626
    public function get_discount_total() {
627
        $discount_total = wc_format_decimal( $this->_data['discount_total'] );
628
629
        // Backwards compatible total calculation - totals were not stored consistently in old versions.
630
        if ( ( ! $this->get_order_version() || version_compare( $this->get_order_version(), '2.3.7', '<' ) ) && $this->get_prices_include_tax() ) {
631
            $discount_total = $discount_total - $this->get_discount_tax();
632
        }
633
634
        return $discount_total;
635
    }
636
637
    /**
638
     * Get discount_tax
639
     * @return string
640
     */
641
    public function get_discount_tax() {
642
        return wc_format_decimal( $this->_data['discount_tax'] );
643
    }
644
645
    /**
646
     * Get shipping_total
647
     * woocommerce_order_amount_total_shipping filter has been removed to avoid
648
     * these values being modified and then saved back to the DB. There are
649
     * other, later hooks available to change totals on display. e.g.
650
     * woocommerce_get_order_item_totals.
651
     * @return string
652
     */
653
    public function get_shipping_total() {
654
        return wc_format_decimal( $this->_data['shipping_total'] );
655
    }
656
657
    /**
658
     * Gets cart tax amount.
659
     *
660
     * @since 2.6.0 woocommerce_order_amount_cart_tax filter has been removed to avoid
661
     * these values being modified and then saved back to the DB or used in
662
     * calculations. There are other, later hooks available to change totals on
663
     * display. e.g. woocommerce_get_order_item_totals.
664
     * @return float
665
     */
666
    public function get_cart_tax() {
667
        return wc_format_decimal( $this->_data['cart_tax'] );
668
    }
669
670
    /**
671
     * Get shipping_tax.
672
     *
673
     * @since 2.6.0 woocommerce_order_amount_shipping_tax filter has been removed to avoid
674
     * these values being modified and then saved back to the DB or used in
675
     * calculations. There are other, later hooks available to change totals on
676
     * display. e.g. woocommerce_get_order_item_totals.
677
     * @return string
678
     */
679
    public function get_shipping_tax() {
680
        return wc_format_decimal( $this->_data['shipping_tax'] );
681
    }
682
683
    /**
684
     * Order tax is the sum of all taxes.
685
     * @return string
686
     */
687
    public function get_order_tax() {
688
        return wc_round_tax_total( $this->_data['order_tax'] );
689
    }
690
691
    /**
692
     * Get the stored order total. Includes taxes and everything else.
693
     * @return string
694
     */
695
    public function get_order_total() {
696
        return wc_format_decimal( $this->_data['order_total'], wc_get_price_decimals() );
697
    }
698
699
    /**
700
     * Gets the total discount amount.
701
     * @param  bool $ex_tax Show discount excl any tax.
702
     * @return float
703
     */
704
    public function get_total_discount( $ex_tax = true ) {
705
        if ( $ex_tax ) {
706
            $total_discount = $this->get_discount_total();
707
        } else {
708
            $total_discount = $this->get_discount_total() + $this->get_discount_tax();
709
        }
710
        return apply_filters( 'woocommerce_order_amount_total_discount', round( $total_discount, WC_ROUNDING_PRECISION ), $this );
711
    }
712
713
    /**
714
     * Get total tax amount. Alias for get_order_tax().
715
     *
716
     * @since 2.6.0 woocommerce_order_amount_total_tax filter has been removed to avoid
717
     * these values being modified and then saved back to the DB. There are
718
     * other, later hooks available to change totals on display. e.g.
719
     * woocommerce_get_order_item_totals.
720
     * @return float
721
     */
722
    public function get_total_tax() {
723
        return $this->get_order_tax();
724
    }
725
726
    /**
727
     * Gets shipping total. Alias of WC_Order::get_shipping_total().
728
     *
729
     * @since 2.6.0 woocommerce_order_amount_total_shipping filter has been removed to avoid
730
     * these values being modified and then saved back to the DB or used in
731
     * calculations. There are other, later hooks available to change totals on
732
     * display. e.g. woocommerce_get_order_item_totals.
733
     * @return float
734
     */
735
    public function get_total_shipping() {
736
        return $this->get_shipping_total();
737
    }
738
739
    /**
740
     * Gets order grand total. incl. taxes. Used in gateways. Filtered.
741
     * @return float
742
     */
743
    public function get_total() {
744
        return apply_filters( 'woocommerce_order_amount_total', $this->get_order_total(), $this );
745
    }
746
747
    /**
748
     * Gets order subtotal.
749
     * @return float
750
     */
751
    public function get_subtotal() {
752
        $subtotal = 0;
753
754
        foreach ( $this->get_items() as $item ) {
755
            $subtotal += isset( $item['line_subtotal'] ) ? $item['line_subtotal'] : 0;
756
        }
757
758
        return apply_filters( 'woocommerce_order_amount_subtotal', (double) $subtotal, $this );
759
    }
760
761
    /**
762
     * Get taxes, merged by code, formatted ready for output.
763
     *
764
     * @return array
765
     */
766
    public function get_tax_totals() {
767
        $tax_totals = array();
768
769
        foreach ( $this->get_items( 'tax' ) as $key => $tax ) {
770
            $code = $tax[ 'name' ];
771
772 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...
773
                $tax_totals[ $code ] = new stdClass();
774
                $tax_totals[ $code ]->amount = 0;
775
            }
776
777
            $tax_totals[ $code ]->id                = $key;
778
            $tax_totals[ $code ]->rate_id           = $tax['rate_id'];
779
            $tax_totals[ $code ]->is_compound       = $tax[ 'compound' ];
780
            $tax_totals[ $code ]->label             = isset( $tax[ 'label' ] ) ? $tax[ 'label' ] : $tax[ 'name' ];
781
            $tax_totals[ $code ]->amount           += $tax[ 'tax_amount' ] + $tax[ 'shipping_tax_amount' ];
782
            $tax_totals[ $code ]->formatted_amount  = wc_price( wc_round_tax_total( $tax_totals[ $code ]->amount ), array('currency' => $this->get_order_currency()) );
783
        }
784
785
        return apply_filters( 'woocommerce_order_tax_totals', $tax_totals, $this );
786
    }
787
788
    /**
789
     * Gets formatted shipping method title.
790
     * @return string
791
     */
792
    public function get_shipping_method() {
793
        $names = array();
794
        foreach ( $this->get_shipping_methods() as $shipping_method ) {
795
            $names[] = $shipping_method->get_name();
796
        }
797
        return apply_filters( 'woocommerce_order_shipping_method', implode( ', ', $names ), $this );
798
    }
799
800
    /*
801
    |--------------------------------------------------------------------------
802
    | Setters
803
    |--------------------------------------------------------------------------
804
    |
805
    | Functions for setting order data. These should not update anything in the
806
    | database itself and should only change what is stored in the class
807
    | object. However, for backwards compatibility pre 2.6.0 some of these
808
    | setters may handle both.
809
    |
810
    */
811
812
    /**
813
     * Set order ID.
814
     * @since 2.6.0
815
     * @param int $value
816
     */
817
    public function set_order_id( $value ) {
818
        $this->_data['order_id'] = absint( $value );
819
    }
820
821
    /**
822
     * Set parent order ID.
823
     * @since 2.6.0
824
     * @param int $value
825
     */
826
    public function set_parent_id( $value ) {
827
        $this->_data['parent_id'] = absint( $value );
828
    }
829
830
    /**
831
     * Set order status.
832
     * @since 2.6.0
833
     * @param string $new_status Status to change the order to. No internal wc- prefix is required.
834
     * @param string $note (default: '') Optional note to add.
835
     * @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...
836
     */
837
    public function set_status( $new_status, $note = '', $manual_update = false ) {
838
        // Remove prefixes and standardize
839
        $current_status = $this->get_status();
840
        $new_status     = 'wc-' === substr( $new_status, 0, 3 ) ? substr( $new_status, 3 ) : $new_status;
841
842
        if ( in_array( 'wc-' . $new_status, array_keys( wc_get_order_statuses() ) ) && $new_status !== $current_status ) {
843
            if ( ! empty( $current_status ) ) {
844
                $this->_status_transition = array(
845
                    'original' => ! empty( $this->_status_transition['original'] ) ? $this->_status_transition['original'] : $current_status,
846
                    'note'     => $note ? $note : '',
847
                    'manual'   => (bool) $manual_update
848
                );
849
				if ( 'completed' === $new_status ) {
850
					$this->set_date_completed( current_time( 'timestamp' ) );
851
				}
852
            }
853
            $this->_data['status'] = 'wc-' . $new_status;
854
        }
855
    }
856
857
	/**
858
     * Updates status of order immediately.
859
     * @uses WC_Order::set_status()
860
     */
861
    public function update_status( $new_status, $note = '', $manual = false ) {
862
        if ( ! $this->get_id() ) {
863
            return false;
864
        }
865
		$this->set_status( $new_status, $note, $manual );
866
		$this->save();
867
        return true;
868
    }
869
870
    /**
871
     * Set Order Type
872
     * @param string $value
873
     */
874
    public function set_order_type( $value ) {
875
        $this->_data['order_type'] = $value;
876
    }
877
878
    /**
879
     * Set order_key
880
     * @param string $value
881
     */
882
    public function set_order_key( $value ) {
883
        $this->_data['order_key'] = $value;
884
    }
885
886
    /**
887
     * Set order_currency
888
     * @param string $value
889
     */
890
    public function set_order_currency( $value ) {
891
        $this->_data['order_currency'] = $value;
892
    }
893
894
    /**
895
     * Set date_created
896
     * @param string $timestamp Timestamp
897
     */
898
    public function set_date_created( $timestamp ) {
899
        $this->_data['date_created'] = is_numeric( $timestamp ) ? $timestamp : strtotime( $timestamp );
900
    }
901
902
    /**
903
     * Set date_modified
904
     * @param string $timestamp
905
     */
906
    public function set_date_modified( $timestamp ) {
907
        $this->_data['date_modified'] = is_numeric( $timestamp ) ? $timestamp : strtotime( $timestamp );
908
    }
909
910
    /**
911
     * Set customer_id
912
     * @param int $value
913
     */
914
    public function set_customer_id( $value ) {
915
        $this->_data['customer_id'] = absint( $value );
916
    }
917
918
    /**
919
     * Set billing_first_name
920
     * @param string $value
921
     */
922
    public function set_billing_first_name( $value ) {
923
        $this->_data['billing_first_name'] = $value;
924
    }
925
926
    /**
927
     * Set billing_last_name
928
     * @param string $value
929
     */
930
    public function set_billing_last_name( $value ) {
931
        $this->_data['billing_last_name'] = $value;
932
    }
933
934
    /**
935
     * Set billing_company
936
     * @param string $value
937
     */
938
    public function set_billing_company( $value ) {
939
        $this->_data['billing_company'] = $value;
940
    }
941
942
    /**
943
     * Set billing_address_1
944
     * @param string $value
945
     */
946
    public function set_billing_address_1( $value ) {
947
        $this->_data['billing_address_1'] = $value;
948
    }
949
950
    /**
951
     * Set billing_address_2
952
     * @param string $value
953
     */
954
    public function set_billing_address_2( $value ) {
955
        $this->_data['billing_address_2'] = $value;
956
    }
957
958
    /**
959
     * Set billing_city
960
     * @param string $value
961
     */
962
    public function set_billing_city( $value ) {
963
        $this->_data['billing_city'] = $value;
964
    }
965
966
    /**
967
     * Set billing_state
968
     * @param string $value
969
     */
970
    public function set_billing_state( $value ) {
971
        $this->_data['billing_state'] = $value;
972
    }
973
974
    /**
975
     * Set billing_postcode
976
     * @param string $value
977
     */
978
    public function set_billing_postcode( $value ) {
979
        $this->_data['billing_postcode'] = $value;
980
    }
981
982
    /**
983
     * Set billing_country
984
     * @param string $value
985
     */
986
    public function set_billing_country( $value ) {
987
        $this->_data['billing_country'] = $value;
988
    }
989
990
    /**
991
     * Set billing_email
992
     * @param string $value
993
     */
994
    public function set_billing_email( $value ) {
995
		$value = sanitize_email( $value );
996
        $this->_data['billing_email'] = is_email( $value ) ? $value : '';
997
    }
998
999
    /**
1000
     * Set billing_phone
1001
     * @param string $value
1002
     */
1003
    public function set_billing_phone( $value ) {
1004
        $this->_data['billing_phone'] = $value;
1005
    }
1006
1007
    /**
1008
     * Set shipping_first_name
1009
     * @param string $value
1010
     */
1011
    public function set_shipping_first_name( $value ) {
1012
        $this->_data['shipping_first_name'] = $value;
1013
    }
1014
1015
    /**
1016
     * Set shipping_last_name
1017
     * @param string $value
1018
     */
1019
    public function set_shipping_last_name( $value ) {
1020
        $this->_data['shipping_last_name'] = $value;
1021
    }
1022
1023
    /**
1024
     * Set shipping_company
1025
     * @param string $value
1026
     */
1027
    public function set_shipping_company( $value ) {
1028
        $this->_data['shipping_company'] = $value;
1029
    }
1030
1031
    /**
1032
     * Set shipping_address_1
1033
     * @param string $value
1034
     */
1035
    public function set_shipping_address_1( $value ) {
1036
        $this->_data['shipping_address_1'] = $value;
1037
    }
1038
1039
    /**
1040
     * Set shipping_address_2
1041
     * @param string $value
1042
     */
1043
    public function set_shipping_address_2( $value ) {
1044
        $this->_data['shipping_address_2'] = $value;
1045
    }
1046
1047
    /**
1048
     * Set shipping_city
1049
     * @param string $value
1050
     */
1051
    public function set_shipping_city( $value ) {
1052
        $this->_data['shipping_city'] = $value;
1053
    }
1054
1055
    /**
1056
     * Set shipping_state
1057
     * @param string $value
1058
     */
1059
    public function set_shipping_state( $value ) {
1060
        $this->_data['shipping_state'] = $value;
1061
    }
1062
1063
    /**
1064
     * Set shipping_postcode
1065
     * @param string $value
1066
     */
1067
    public function set_shipping_postcode( $value ) {
1068
        $this->_data['shipping_postcode'] = $value;
1069
    }
1070
1071
    /**
1072
     * Set shipping_country
1073
     * @param string $value
1074
     */
1075
    public function set_shipping_country( $value ) {
1076
        $this->_data['shipping_country'] = $value;
1077
    }
1078
1079
    /**
1080
     * Set discount_total
1081
     * @param string $value
1082
     */
1083
    public function set_discount_total( $value ) {
1084
        $this->_data['discount_total'] = wc_format_decimal( $value );
1085
    }
1086
1087
    /**
1088
     * Set discount_tax
1089
     * @param string $value
1090
     */
1091
    public function set_discount_tax( $value ) {
1092
        $this->_data['discount_tax'] = wc_format_decimal( $value );
1093
    }
1094
1095
    /**
1096
     * Set shipping_total
1097
     * @param string $value
1098
     */
1099
    public function set_shipping_total( $value ) {
1100
        $this->_data['shipping_total'] = wc_format_decimal( $value );
1101
    }
1102
1103
    /**
1104
     * Set shipping_tax
1105
     * @param string $value
1106
     */
1107
    public function set_shipping_tax( $value ) {
1108
        $this->_data['shipping_tax'] = wc_format_decimal( $value );
1109
        $this->set_order_tax( $this->get_cart_tax() + $this->get_shipping_tax() );
1110
    }
1111
1112
    /**
1113
     * Set cart tax
1114
     * @param string $value
1115
     */
1116
    public function set_cart_tax( $value ) {
1117
        $this->_data['cart_tax'] = wc_format_decimal( $value );
1118
        $this->set_order_tax( $this->get_cart_tax() + $this->get_shipping_tax() );
1119
    }
1120
1121
    /**
1122
     * Sets order tax (sum of cart and shipping tax). Used internaly only.
1123
     * @access protected
1124
     * @param string $value
1125
     */
1126
    protected function set_order_tax( $value ) {
1127
        $this->_data['order_tax'] = wc_format_decimal( $value );
1128
    }
1129
1130
    /**
1131
     * Set order_total
1132
     * @param string $value
1133
     */
1134
    public function set_order_total( $value ) {
1135
        $this->_data['order_total'] = wc_format_decimal( $value, wc_get_price_decimals() );
1136
    }
1137
1138
    /**
1139
     * Set the payment method ID.
1140
     * @since 2.2.0
1141
     * @param string $value Supports WC_Payment_Gateway for bw compatibility with < 2.6
1142
     */
1143
    public function set_payment_method( $value ) {
1144
        if ( is_object( $value ) ) {
1145
            update_post_meta( $this->get_id(), '_payment_method', $value->id );
1146
            update_post_meta( $this->get_id(), '_payment_method_title', $value->get_title() );
1147
            $this->set_payment_method( $value->id );
1148
            $this->set_payment_method_title( $value->get_title() );
1149
        } else {
1150
            $this->_meta['payment_method'] = $value;
1151
        }
1152
    }
1153
1154
    /**
1155
     * Set payment_method_title
1156
     * @param string $value
1157
     */
1158
    public function set_payment_method_title( $value ) {
1159
        $this->_meta['payment_method_title'] = $value;
1160
    }
1161
1162
    /**
1163
     * Set transaction_id
1164
     * @param string $value
1165
     */
1166
    public function set_transaction_id( $value ) {
1167
        $this->_meta['transaction_id'] = $value;
1168
    }
1169
1170
    /**
1171
     * Set customer_ip_address
1172
     * @param string $value
1173
     */
1174
    public function set_customer_ip_address( $value ) {
1175
        $this->_meta['customer_ip_address'] = $value;
1176
    }
1177
1178
    /**
1179
     * Set customer_user_agent
1180
     * @param string $value
1181
     */
1182
    public function set_customer_user_agent( $value ) {
1183
        $this->_meta['customer_user_agent'] = $value;
1184
    }
1185
1186
    /**
1187
     * Set created_via
1188
     * @param string $value
1189
     */
1190
    public function set_created_via( $value ) {
1191
        $this->_meta['created_via'] = $value;
1192
    }
1193
1194
    /**
1195
     * Set order_version
1196
     * @param string $value
1197
     */
1198
    public function set_order_version( $value ) {
1199
        $this->_meta['order_version'] = $value;
1200
    }
1201
1202
    /**
1203
     * Set prices_include_tax
1204
     * @param bool $value
1205
     */
1206
    public function set_prices_include_tax( $value ) {
1207
        $this->_meta['prices_include_tax'] = (bool) $value;
1208
    }
1209
1210
    /**
1211
     * Set customer_note
1212
     * @param string $value
1213
     */
1214
    public function set_customer_note( $value ) {
1215
        $this->_meta['customer_note'] = $value;
1216
    }
1217
1218
	/**
1219
     * Set date_completed
1220
     * @param string $timestamp
1221
     */
1222
    public function set_date_completed( $timestamp ) {
1223
        $this->_meta['date_completed'] = is_numeric( $timestamp ) ? $timestamp : strtotime( $timestamp );
1224
    }
1225
1226
	/**
1227
     * Set date_paid
1228
     * @param string $timestamp
1229
     */
1230
    public function set_date_paid( $timestamp ) {
1231
        $this->_meta['date_paid'] = is_numeric( $timestamp ) ? $timestamp : strtotime( $timestamp );
1232
    }
1233
1234
    /**
1235
     * Set the customer address.
1236
     * @since 2.2.0
1237
     * @param array $address Address data.
1238
     * @param string $type billing or shipping.
1239
     */
1240
    public function set_address( $address, $type = 'billing' ) {
1241
        foreach ( $address as $key => $value ) {
1242
            update_post_meta( $this->get_id(), "_{$type}_" . $key, $value );
1243
            if ( method_exists( $this, "set_{$type}_{$key}" ) ) {
1244
                $this->{"set_{$type}_{$key}"}( $value );
1245
            }
1246
        }
1247
    }
1248
1249
    /**
1250
     * Set an order total.
1251
     * @since 2.2.0
1252
     * @param float $amount
1253
     * @param string $total_type
1254
     * @return bool
1255
     */
1256
    public function set_total( $amount, $total_type = 'total' ) {
1257
        if ( ! in_array( $total_type, array( 'shipping', 'tax', 'shipping_tax', 'total', 'cart_discount', 'cart_discount_tax' ) ) ) {
1258
            return false;
1259
        }
1260
1261
        switch ( $total_type ) {
1262
            case 'total' :
1263
                $amount = wc_format_decimal( $amount, wc_get_price_decimals() );
1264
                $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 1263 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...
1265
                update_post_meta( $this->get_id(), '_order_total', $amount );
1266
                break;
1267
            case 'cart_discount' :
1268
                $amount = wc_format_decimal( $amount );
1269
                $this->set_discount_total( $amount );
1 ignored issue
show
Bug introduced by
It seems like $amount defined by wc_format_decimal($amount) on line 1268 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...
1270
                update_post_meta( $this->get_id(), '_cart_discount', $amount );
1271
                break;
1272
            case 'cart_discount_tax' :
1273
                $amount = wc_format_decimal( $amount );
1274
                $this->set_discount_tax( $amount );
1 ignored issue
show
Bug introduced by
It seems like $amount defined by wc_format_decimal($amount) on line 1273 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...
1275
                update_post_meta( $this->get_id(), '_cart_discount_tax', $amount );
1276
                break;
1277
            case 'shipping' :
1278
                $amount = wc_format_decimal( $amount );
1279
                $this->set_shipping_total( $amount );
1 ignored issue
show
Bug introduced by
It seems like $amount defined by wc_format_decimal($amount) on line 1278 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...
1280
                update_post_meta( $this->get_id(), '_order_shipping', $amount );
1281
                break;
1282
            case 'shipping_tax' :
1283
                $amount = wc_format_decimal( $amount );
1284
                $this->set_shipping_tax( $amount );
1 ignored issue
show
Bug introduced by
It seems like $amount defined by wc_format_decimal($amount) on line 1283 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...
1285
                update_post_meta( $this->get_id(), '_order_shipping_tax', $amount );
1286
                break;
1287
            case 'tax' :
1288
                $amount = wc_format_decimal( $amount );
1289
                $this->set_cart_tax( $amount );
1 ignored issue
show
Bug introduced by
It seems like $amount defined by wc_format_decimal($amount) on line 1288 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...
1290
                update_post_meta( $this->get_id(), '_order_tax', $amount );
1291
                break;
1292
        }
1293
1294
        return true;
1295
    }
1296
1297
    /*
1298
    |--------------------------------------------------------------------------
1299
    | CRUD methods
1300
    |--------------------------------------------------------------------------
1301
    |
1302
    | Methods which create, read, update and delete orders from the database.
1303
    | Written in abstract fashion so that the way orders are stored can be
1304
    | changed more easily in the future.
1305
    |
1306
    | A save method is included for convenience (chooses update or create based
1307
    | on if the order exists yet).
1308
    |
1309
    */
1310
1311
    /**
1312
     * Insert data into the database.
1313
     * @since 2.6.0
1314
     * @access protected
1315
     */
1316
    public function create() {
1317
        // Set random key
1318
        $this->set_order_key( uniqid( 'order_' ) );
1319
1320
        $order_id = wp_insert_post( apply_filters( 'woocommerce_new_order_data', array(
1321
            'post_type'     => $this->get_order_type(),
1322
            'post_status'   => 'wc-' . ( $this->get_status() ? $this->get_status() : apply_filters( 'woocommerce_default_order_status', 'pending' ) ),
1323
            'ping_status'   => 'closed',
1324
            'post_author'   => 1,
1325
            'post_title'    => sprintf( __( 'Order &ndash; %s', 'woocommerce' ), strftime( _x( '%b %d, %Y @ %I:%M %p', 'Order date parsed by strftime', 'woocommerce' ) ) ),
1326
            'post_password' => $this->get_order_key(),
1327
            'post_parent'   => $this->get_parent_id()
1328
        ) ), true );
1329
1330
        if ( $order_id ) {
1331
            $this->set_order_id( $order_id );
1332
1333
            // Set meta data
1334
            update_post_meta( $order_id, '_billing_first_name', $this->get_billing_first_name() );
1335
            update_post_meta( $order_id, '_billing_last_name', $this->get_billing_last_name() );
1336
            update_post_meta( $order_id, '_billing_company', $this->get_billing_company() );
1337
            update_post_meta( $order_id, '_billing_address_1', $this->get_billing_address_1() );
1338
            update_post_meta( $order_id, '_billing_address_2', $this->get_billing_address_2() );
1339
            update_post_meta( $order_id, '_billing_city', $this->get_billing_city() );
1340
            update_post_meta( $order_id, '_billing_state', $this->get_billing_state() );
1341
            update_post_meta( $order_id, '_billing_postcode', $this->get_billing_postcode() );
1342
            update_post_meta( $order_id, '_billing_country', $this->get_billing_country() );
1343
            update_post_meta( $order_id, '_billing_email', $this->get_billing_email() );
1344
            update_post_meta( $order_id, '_billing_phone', $this->get_billing_phone() );
1345
            update_post_meta( $order_id, '_shipping_first_name', $this->get_shipping_first_name() );
1346
            update_post_meta( $order_id, '_shipping_last_name', $this->get_shipping_last_name() );
1347
            update_post_meta( $order_id, '_shipping_company', $this->get_shipping_company() );
1348
            update_post_meta( $order_id, '_shipping_address_1', $this->get_shipping_address_1() );
1349
            update_post_meta( $order_id, '_shipping_address_2', $this->get_shipping_address_2() );
1350
            update_post_meta( $order_id, '_shipping_city', $this->get_shipping_city() );
1351
            update_post_meta( $order_id, '_shipping_state', $this->get_shipping_state() );
1352
            update_post_meta( $order_id, '_shipping_postcode', $this->get_shipping_postcode() );
1353
            update_post_meta( $order_id, '_shipping_country', $this->get_shipping_country() );
1354
            update_post_meta( $order_id, '_order_currency', $this->get_order_currency() );
1355
            update_post_meta( $order_id, '_order_key', $this->get_order_key() );
1356
            update_post_meta( $order_id, '_cart_discount', $this->get_discount_total() );
1357
            update_post_meta( $order_id, '_cart_discount_tax', $this->get_discount_tax() );
1358
            update_post_meta( $order_id, '_order_shipping', $this->get_shipping_total() );
1359
            update_post_meta( $order_id, '_order_shipping_tax', $this->get_shipping_tax() );
1360
            update_post_meta( $order_id, '_order_tax', $this->get_cart_tax() );
1361
            update_post_meta( $order_id, '_order_total', $this->get_order_total() );
1362
1363
			foreach ( $this->get_meta() as $key => $value ) {
1364
				update_post_meta( $order_id, '_' . $key, $value );
1365
			}
1366
        }
1367
    }
1368
1369
    /**
1370
     * Read from the database.
1371
     * @since 2.6.0
1372
     * @access protected
1373
     * @param int $id ID of object to read.
1374
     */
1375
    public function read( $id ) {
1376
        $post_object = get_post( $id );
1377
        $order_id    = absint( $post_object->ID );
1378
1379
        // Map standard post data
1380
        $this->set_order_id( $order_id );
1381
        $this->set_date_created( $post_object->post_date );
1382
        $this->set_date_modified( $post_object->post_modified );
1383
        $this->set_status( $post_object->post_status );
1384
        $this->set_customer_note( $post_object->post_excerpt );
1385
1386
        // Map meta data
1387
        $this->set_customer_id( get_post_meta( $order_id, '_customer_user', true ) );
1388
        $this->set_order_key( get_post_meta( $order_id, '_order_key', true ) );
1389
        $this->set_order_currency( get_post_meta( $order_id, '_order_currency', true ) );
1390
        $this->set_billing_first_name( get_post_meta( $order_id, '_billing_first_name', true ) );
1391
        $this->set_billing_last_name( get_post_meta( $order_id, '_billing_last_name', true ) );
1392
        $this->set_billing_company( get_post_meta( $order_id, '_billing_company', true ) );
1393
        $this->set_billing_address_1( get_post_meta( $order_id, '_billing_address_1', true ) );
1394
        $this->set_billing_address_2( get_post_meta( $order_id, '_billing_address_2', true ) );
1395
        $this->set_billing_city( get_post_meta( $order_id, '_billing_city', true ) );
1396
        $this->set_billing_state( get_post_meta( $order_id, '_billing_state', true ) );
1397
        $this->set_billing_postcode( get_post_meta( $order_id, '_billing_postcode', true ) );
1398
        $this->set_billing_country( get_post_meta( $order_id, '_billing_country', true ) );
1399
        $this->set_billing_email( get_post_meta( $order_id, '_billing_email', true ) );
1400
        $this->set_billing_phone( get_post_meta( $order_id, '_billing_phone', true ) );
1401
        $this->set_shipping_first_name( get_post_meta( $order_id, '_shipping_first_name', true ) );
1402
        $this->set_shipping_last_name( get_post_meta( $order_id, '_shipping_last_name', true ) );
1403
        $this->set_shipping_company( get_post_meta( $order_id, '_shipping_company', true ) );
1404
        $this->set_shipping_address_1( get_post_meta( $order_id, '_shipping_address_1', true ) );
1405
        $this->set_shipping_address_2( get_post_meta( $order_id, '_shipping_address_2', true ) );
1406
        $this->set_shipping_city( get_post_meta( $order_id, '_shipping_city', true ) );
1407
        $this->set_shipping_state( get_post_meta( $order_id, '_shipping_state', true ) );
1408
        $this->set_shipping_postcode( get_post_meta( $order_id, '_shipping_postcode', true ) );
1409
        $this->set_shipping_country( get_post_meta( $order_id, '_shipping_country', true ) );
1410
1411
		// Map totals
1412
        $this->set_discount_total( get_post_meta( $order_id, '_cart_discount', true ) );
1413
        $this->set_discount_tax( get_post_meta( $order_id, '_cart_discount_tax', true ) );
1414
        $this->set_shipping_total( get_post_meta( $order_id, '_order_shipping', true ) );
1415
        $this->set_shipping_tax( get_post_meta( $order_id, '_order_shipping_tax', true ) );
1416
        $this->set_cart_tax( get_post_meta( $order_id, '_order_tax', true ) );
1417
        $this->set_order_total( get_post_meta( $order_id, '_order_total', true ) );
1418
1419
        // Map user data
1420
        if ( empty( $this->get_billing_email() ) && ( $user = $this->get_user() ) ) {
1421
            $this->set_billing_email( $user->user_email );
1422
        }
1423
1424
        // This is meta data, but due to the keys not matching the props, load here
1425
		$this->set_date_completed( get_post_meta( $order_id, '_completed_date', true ) );
1426
		$this->set_date_paid( get_post_meta( $order_id, '_paid_date', true ) );
1427
1428
		// Load meta data, including anything custom set.
1429
		$ignore_keys = array( '_customer_user', '_order_key', '_order_currency', '_billing_first_name', '_billing_last_name', '_billing_company', '_billing_address_1', '_billing_address_2', '_billing_city', '_billing_state', '_billing_postcode', '_billing_country', '_billing_email', '_billing_phone', '_shipping_first_name', '_shipping_last_name', '_shipping_company', '_shipping_address_1', '_shipping_address_2', '_shipping_city', '_shipping_state', '_shipping_postcode', '_shipping_country', '_completed_date', '_paid_date', '_edit_lock', '_cart_discount', '_cart_discount_tax', '_order_shipping', '_order_shipping_tax', '_order_tax', '_order_total', '_order_total' );
1430
		$meta_data   = get_post_meta( $order_id );
1431
		$meta_data   = array_diff_key( $meta_data, array_fill_keys( $ignore_keys, '' ) );
1432
1433
		// Set meta data. Remove _ from key for hidden meta.
1434
		foreach ( $meta_data as $key => $value ) {
1435
			$this->_meta[ ltrim( $key, '_' ) ] = $value[0];
1436
		}
1437
1438
        // Orders store the state of prices including tax when created.
1439
        $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' );
1440
    }
1441
1442
    /**
1443
     * Update data in the database.
1444
     * @since 2.6.0
1445
     * @access protected
1446
     */
1447
    public function update() {
1448
        global $wpdb;
1449
1450
        $order_id = $this->get_id();
1451
1452
        $wpdb->update(
1453
            $wpdb->posts,
1454
            array(
1455
                'post_date'     => date( 'Y-m-d H:i:s', $this->get_date_created() ),
1456
                'post_date_gmt' => get_gmt_from_date( date( 'Y-m-d H:i:s', $this->get_date_created() ) ),
1457
                'post_status'   => 'wc-' . ( $this->get_status() ? $this->get_status() : apply_filters( 'woocommerce_default_order_status', 'pending' ) ),
1458
                'post_parent'   => $this->get_parent_id()
1459
            ),
1460
            array(
1461
                'ID' => $order_id
1462
            )
1463
        );
1464
1465
        // Update meta data
1466
        update_post_meta( $order_id, '_billing_first_name', $this->get_billing_first_name() );
1467
        update_post_meta( $order_id, '_billing_last_name', $this->get_billing_last_name() );
1468
        update_post_meta( $order_id, '_billing_company', $this->get_billing_company() );
1469
        update_post_meta( $order_id, '_billing_address_1', $this->get_billing_address_1() );
1470
        update_post_meta( $order_id, '_billing_address_2', $this->get_billing_address_2() );
1471
        update_post_meta( $order_id, '_billing_city', $this->get_billing_city() );
1472
        update_post_meta( $order_id, '_billing_state', $this->get_billing_state() );
1473
        update_post_meta( $order_id, '_billing_postcode', $this->get_billing_postcode() );
1474
        update_post_meta( $order_id, '_billing_country', $this->get_billing_country() );
1475
        update_post_meta( $order_id, '_billing_email', $this->get_billing_email() );
1476
        update_post_meta( $order_id, '_billing_phone', $this->get_billing_phone() );
1477
        update_post_meta( $order_id, '_shipping_first_name', $this->get_shipping_first_name() );
1478
        update_post_meta( $order_id, '_shipping_last_name', $this->get_shipping_last_name() );
1479
        update_post_meta( $order_id, '_shipping_company', $this->get_shipping_company() );
1480
        update_post_meta( $order_id, '_shipping_address_1', $this->get_shipping_address_1() );
1481
        update_post_meta( $order_id, '_shipping_address_2', $this->get_shipping_address_2() );
1482
        update_post_meta( $order_id, '_shipping_city', $this->get_shipping_city() );
1483
        update_post_meta( $order_id, '_shipping_state', $this->get_shipping_state() );
1484
        update_post_meta( $order_id, '_shipping_postcode', $this->get_shipping_postcode() );
1485
        update_post_meta( $order_id, '_shipping_country', $this->get_shipping_country() );
1486
1487
		update_post_meta( $order_id, '_payment_method', $this->get_payment_method() );
1488
        update_post_meta( $order_id, '_payment_method_title', $this->get_payment_method_title() );
1489
        update_post_meta( $order_id, '_transaction_id', $this->get_transaction_id() );
1490
        update_post_meta( $order_id, '_customer_user', $this->get_customer_id() );
1491
        update_post_meta( $order_id, '_customer_ip_address', $this->get_customer_ip_address() );
1492
        update_post_meta( $order_id, '_customer_user_agent', $this->get_customer_user_agent() );
1493
        update_post_meta( $order_id, '_created_via', $this->get_created_via() );
1494
        update_post_meta( $order_id, '_order_version', $this->get_order_version() );
1495
        update_post_meta( $order_id, '_prices_include_tax', $this->get_prices_include_tax() );
1496
        update_post_meta( $order_id, '_order_currency', $this->get_order_currency() );
1497
        update_post_meta( $order_id, '_order_key', $this->get_order_key() );
1498
        update_post_meta( $order_id, '_cart_discount', $this->get_discount_total() );
1499
        update_post_meta( $order_id, '_cart_discount_tax', $this->get_discount_tax() );
1500
        update_post_meta( $order_id, '_order_shipping', $this->get_shipping_total() );
1501
        update_post_meta( $order_id, '_order_shipping_tax', $this->get_shipping_tax() );
1502
        update_post_meta( $order_id, '_order_tax', $this->get_cart_tax() );
1503
        update_post_meta( $order_id, '_order_total', $this->get_order_total() );
1504
1505
        if ( $this->_status_transition ) {
1506
            if ( ! empty( $this->_status_transition['original'] ) ) {
1507
                $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() ) );
1508
1509
                do_action( 'woocommerce_order_status_' . $this->_status_transition['original'] . '_to_' . $this->get_status(), $this->get_id() );
1510
                do_action( 'woocommerce_order_status_changed', $this->get_id(), $this->_status_transition['original'], $this->get_status() );
1511
            } else {
1512
                $transition_note = sprintf( __( 'Order status set to %s.', 'woocommerce' ), wc_get_order_status_name( $this->get_status() ) );
1513
            }
1514
1515
            do_action( 'woocommerce_order_status_' . $this->get_status(), $this->get_id() );
1516
1517
            // Note the transition occured
1518
            $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...
1519
1520
            // This has ran, so reset status transition variable
1521
            $this->_status_transition = false;
1522
        }
1523
    }
1524
1525
    /**
1526
     * Delete data from the database.
1527
     * @since 2.6.0
1528
     * @access protected
1529
     */
1530
    public function delete() {
1531
        wp_delete_post( $this->get_id() );
1532
    }
1533
1534
    /**
1535
     * Save data to the database.
1536
     * @since 2.6.0
1537
     * @access protected
1538
     */
1539
    public function save() {
1540
        if ( ! $this->get_id() ) {
1541
            $this->create();
1542
        } else {
1543
            $this->update();
1544
        }
1545
        wc_delete_shop_order_transients( $this->get_id() );
1546
    }
1547
1548
    /*
1549
    |--------------------------------------------------------------------------
1550
    | Order Item Handling
1551
    |--------------------------------------------------------------------------
1552
    |
1553
    | Order items are used for products, taxes, shipping, and fees within
1554
    | each order.
1555
    |
1556
    */
1557
1558
    /**
1559
     * Return an array of items/products within this order.
1560
     *
1561
     * @param string|array $type Types of line items to get (array or string).
1562
     * @return Array of WC_Order_item
1563
     */
1564
    public function get_items( $type = 'line_item' ) {
1565
        global $wpdb;
1566
1567
        $type            = ! is_array( $type ) ? array( $type ) : $type;
1568
        $items           = array();
1569
        $get_items_sql   = $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}woocommerce_order_items WHERE order_id = %d ", $this->get_id() );
1570
        $get_items_sql  .= "AND order_item_type IN ( '" . implode( "','", array_map( 'esc_sql', $type ) ) . "' ) ORDER BY order_item_id;";
1571
        $raw_items       = $wpdb->get_results( $get_items_sql );
1572
1573
        foreach ( $raw_items as $item ) {
1574
            $item                                = $this->get_item( $item );
1575
            $items[ $item->get_order_item_id() ] = $item;
1576
        }
1577
1578
        return apply_filters( 'woocommerce_order_get_items', $items, $this );
1579
    }
1580
1581
    /**
1582
     * Get an order item object, based on it's type.
1583
     * @param  int $item_id
1584
     * @return WC_Order_Item
1585
     */
1586
    public function get_item( $item_id ) {
1587
        return WC_Order_Factory::get_order_item( $item_id );
1588
    }
1589
1590
    /**
1591
     * Display meta data belonging to an item. @todo
1592
     * @param  array $item
1593
     */
1594
    public function display_item_meta( $item ) {
1595
        $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...
1596
        $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 1595 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...
1597
        $item_meta->display();
1598
    }
1599
1600
    /**
1601
     * Return an array of fees within this order.
1602
     *
1603
     * @return array
1604
     */
1605
    public function get_fees() {
1606
        return $this->get_items( 'fee' );
1607
    }
1608
1609
    /**
1610
     * Return an array of taxes within this order.
1611
     *
1612
     * @return array
1613
     */
1614
    public function get_taxes() {
1615
        return $this->get_items( 'tax' );
1616
    }
1617
1618
    /**
1619
     * Return an array of shipping costs within this order.
1620
     *
1621
     * @return array
1622
     */
1623
    public function get_shipping_methods() {
1624
        return $this->get_items( 'shipping' );
1625
    }
1626
1627
    /**
1628
     * Get coupon codes only.
1629
     *
1630
     * @return array
1631
     */
1632
    public function get_used_coupons() {
1633
        return array_map( 'trim', wp_list_pluck( $this->get_items( 'coupon' ), 'name' ) );
1634
    }
1635
1636
    /**
1637
     * Gets the count of order items of a certain type.
1638
     *
1639
     * @param string $item_type
1640
     * @return string
1641
     */
1642
    public function get_item_count( $item_type = '' ) {
1643
        if ( empty( $item_type ) ) {
1644
            $item_type = array( 'line_item' );
1645
        }
1646
        if ( ! is_array( $item_type ) ) {
1647
            $item_type = array( $item_type );
1648
        }
1649
1650
        $items = $this->get_items( $item_type );
1651
        $count = 0;
1652
1653
        foreach ( $items as $item ) {
1654
            $count += $item->get_qty();
1655
        }
1656
1657
        return apply_filters( 'woocommerce_get_item_count', $count, $item_type, $this );
1658
    }
1659
1660
    /**
1661
     * Remove all line items (products, coupons, shipping, taxes) from the order.
1662
     *
1663
     * @param string $type Order item type. Default null.
1664
     */
1665
    public function remove_order_items( $type = null ) {
1666
        global $wpdb;
1667
1668
        if ( ! empty( $type ) ) {
1669
            $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 ) );
1670
            $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}woocommerce_order_items WHERE order_id = %d AND order_item_type = %s", $this->get_id(), $type ) );
1671
        } else {
1672
            $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() ) );
1673
            $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}woocommerce_order_items WHERE order_id = %d", $this->get_id() ) );
1674
        }
1675
    }
1676
1677
    /**
1678
     * Add a product line item to the order.
1679
     * Order must be saved prior to adding items.
1680
     *
1681
     * @since 2.2
1682
     * @param \WC_Product $product
1683
     * @param int $qty Line item quantity.
1684
     * @param array $args
1685
     * @return int updated order item ID
1686
     */
1687
    public function add_product( $product, $qty = 1, $args = array() ) {
1688
        $args = wp_parse_args( $args, array(
1689
            'name'         => $product->get_title(),
1690
            'qty'          => absint( $qty ),
1691
            'tax_class'    => $product->get_tax_class(),
1692
            'product_id'   => $product->id,
1693
            'variation_id' => isset( $product->variation_id ) ? $product->variation_id : 0,
1694
            'variation'    => array(),
1695
            'subtotal'     => $product->get_price_excluding_tax( $qty ),
1696
            'subtotal_tax' => 0,
1697
            'total'        => $product->get_price_excluding_tax( $qty ),
1698
            'total_tax'    => 0,
1699
            'taxes'        => array(
1700
                'subtotal' => array(),
1701
                'total'    => array()
1702
            )
1703
        ) );
1704
        $item = new WC_Order_Item_Product();
1705
        $item_id = $this->update_product( $item, $product, $args );
1706
1707
		do_action( 'woocommerce_order_add_product', $this->get_id(), $item->get_order_item_id(), $product, $qty, $args );
1708
1709
		return $item_id;
1710
    }
1711
1712
    /**
1713
     * Update a line item for the order.
1714
     *
1715
     * Note this does not update order totals.
1716
     *
1717
     * @since 2.2
1718
     * @param object|int $item order item ID or item object.
1719
     * @param WC_Product $product
1720
     * @param array $args data to update.
1721
     * @return int updated order item ID
1722
     */
1723
    public function update_product( $item, $product, $args ) {
1724
        if ( is_numeric( $item ) ) {
1725
            $item = $this->get_item( $item );
1726
        }
1727
1728
        if ( ! is_object( $product ) || ! $item->is_type( 'line_item' ) ) {
1729
            return false;
1730
        }
1731
1732
        if ( ! $this->get_id() ) {
1733
            $this->save();
1734
        }
1735
1736
		$item->set_order_id( $this->get_id() );
1737
1738
        if ( ! $item->get_order_item_id() ) {
1739
            $inserting = true;
1740
        } else {
1741
            $inserting = false;
1742
        }
1743
1744
        if ( isset( $args['name'] ) ) {
1745
            $item->set_name( $args['name'] );
1746
        }
1747
1748
        if ( isset( $args['qty'] ) ) {
1749
            $item->set_qty( $args['qty'] );
1750
1751
            if ( $product->backorders_require_notification() && $product->is_on_backorder( $args['qty'] ) ) {
1752
                $item->add_meta_data( apply_filters( 'woocommerce_backordered_item_meta_name', __( 'Backordered', 'woocommerce' ) ), $args['qty'] - max( 0, $product->get_total_stock() ), true );
1753
            }
1754
1755
            $item->set_line_subtotal( $product->get_price_excluding_tax( $args['qty'] ) );
1756
            $item->set_line_total( $product->get_price_excluding_tax( $args['qty'] ) );
1757
        }
1758
1759
        if ( isset( $args['tax_class'] ) ) {
1760
            $item->set_tax_class( $args['tax_class'] );
1761
        }
1762
1763
        if ( isset( $args['product_id'] ) ) {
1764
            $item->set_product_id( $args['product_id'] );
1765
        }
1766
1767
        if ( isset( $args['variation_id'] ) ) {
1768
            $item->set_variation_id( $args['variation_id'] );
1769
        }
1770
1771
        if ( isset( $args['variation'] ) && is_array( $args['variation'] ) ) {
1772
            foreach ( $args['variation'] as $key => $value ) {
1773
                $item->add_meta_data( str_replace( 'attribute_', '', $key ), $value, true );
1774
            }
1775
        }
1776
1777
        if ( isset( $args['totals'] ) ) {
1778
            // BW compatibility with old args
1779
            if ( isset( $args['totals']['subtotal'] ) ) {
1780
                $args['subtotal'] = $args['totals']['subtotal'];
1781
            }
1782
            if ( isset( $args['totals']['total'] ) ) {
1783
                $args['total'] = $args['totals']['total'];
1784
            }
1785
            if ( isset( $args['totals']['subtotal_tax'] ) ) {
1786
                $args['subtotal_tax'] = $args['totals']['subtotal_tax'];
1787
            }
1788
            if ( isset( $args['totals']['tax'] ) ) {
1789
                $args['total_tax'] = $args['totals']['tax'];
1790
            }
1791
            if ( isset( $args['totals']['tax_data'] ) ) {
1792
                $args['taxes'] = $args['totals']['tax_data'];
1793
            }
1794
        }
1795
1796
        if ( isset( $args['subtotal'] ) ) {
1797
            $item->set_subtotal( $args['subtotal'] );
1798
        }
1799
        if ( isset( $args['total'] ) ) {
1800
            $item->set_total( $args['total'] );
1801
        }
1802
        if ( isset( $args['subtotal_tax'] ) ) {
1803
            $item->set_line_subtotal_tax( $args['subtotal_tax'] );
1804
        }
1805
        if ( isset( $args['total_tax'] ) ) {
1806
            $item->set_total_tax( $args['total_tax'] );
1807
        }
1808
        if ( isset( $args['taxes'] ) ) {
1809
            $item->set_taxes( $args['taxes'] );
1810
        }
1811
1812
        $item->save();
1813
1814
        if ( ! $inserting ) {
1815
            do_action( 'woocommerce_order_edit_product', $this->get_id(), $item->get_order_item_id(), $args, $product );
1816
        }
1817
1818
        return $item->get_order_item_id();
1819
    }
1820
1821
    /**
1822
     * Add coupon code to the order.
1823
     * Order must be saved prior to adding items.
1824
     *
1825
     * @param string $code
1826
     * @param int $discount_amount
1827
     * @param int $discount_amount_tax "Discounted" tax - used for tax inclusive prices.
1828
     * @return int updated order item ID
1829
     */
1830
    public function add_coupon( $code, $discount_amount = 0, $discount_amount_tax = 0 ) {
1831
        $args = wp_parse_args( $args, array(
1832
            'code'                => $code,
1833
            'discount_amount'     => $discount_amount,
1834
            'discount_amount_tax' => $discount_amount_tax
1835
        ) );
1836
        $item = new WC_Order_Item_Coupon();
1837
        $item_id = $this->update_coupon( $item, $args );
1838
1839
		do_action( 'woocommerce_order_add_coupon', $this->get_id(), $item->get_order_item_id(), $code, $discount_amount, $discount_amount_tax );
1840
1841
		return $item_id;
1842
    }
1843
1844
    /**
1845
     * Update coupon for order. Note this does not update order totals.
1846
     * @since 2.2
1847
     * @param object|int $item
1848
     * @param array $args
1849
     * @return int updated order item ID
1850
     */
1851
    public function update_coupon( $item, $args ) {
1852
        if ( is_numeric( $item ) ) {
1853
            $item = $this->get_item( $item );
1854
        }
1855
1856
        if ( ! is_object( $product ) || ! $item->is_type( 'coupon' ) ) {
1857
            return false;
1858
        }
1859
1860
        if ( ! $this->get_id() ) {
1861
            $this->save();
1862
        }
1863
1864
		$item->set_order_id( $this->get_id() );
1865
1866
        if ( ! $item->get_order_item_id() ) {
1867
            $inserting = true;
1868
        } else {
1869
            $inserting = false;
1870
        }
1871
1872
        if ( isset( $args['code'] ) ) {
1873
            $item->set_coupon_code( $args['code'] );
1874
        }
1875
        if ( isset( $args['discount_amount'] ) ) {
1876
            $item->set_discount_amount( $args['discount_amount'] );
1877
        }
1878
        if ( isset( $args['discount_amount_tax'] ) ) {
1879
            $item->set_discount_amount_tax( $args['discount_amount_tax'] );
1880
        }
1881
1882
        $item->save();
1883
1884
        if ( ! $inserting ) {
1885
            do_action( 'woocommerce_order_update_coupon', $this->get_id(), $item->get_order_item_id(), $args );
1886
        }
1887
1888
        return $item->get_order_item_id();
1889
    }
1890
1891
    /**
1892
     * Add a shipping row to the order.
1893
     * Order must be saved prior to adding items.
1894
     *
1895
     * @param WC_Shipping_Rate shipping_rate
1896
     * @return int updated order item ID
1897
     */
1898
    public function add_shipping( $shipping_rate ) {
1899
        $args = wp_parse_args( $args, array(
1900
            'method_title' => $shipping_rate->label,
1901
            'method_id'    => $shipping_rate->id,
1902
            'cost'         => wc_format_decimal( $shipping_rate->cost ),
1903
            'taxes'        => $shipping_rate->taxes,
1904
            'meta'         => $shipping_rate->get_meta_data(),
1905
        ) );
1906
1907
        $item = new WC_Order_Item_Shipping();
1908
        $item_id = $this->update_shipping( $item, $args );
1909
1910
		do_action( 'woocommerce_order_add_shipping', $this->get_id(), $item->get_order_item_id(), $shipping_rate );
1911
1912
		return $item_id;
1913
    }
1914
1915
    /**
1916
     * Update shipping method for order.
1917
     *
1918
     * Note this does not update the order total.
1919
     *
1920
     * @since 2.2
1921
     * @param object|int $item
1922
     * @param array $args
1923
     * @return int updated order item ID
1924
     */
1925
    public function update_shipping( $item, $args ) {
1926
        if ( is_numeric( $item ) ) {
1927
            $item = $this->get_item( $item );
1928
        }
1929
1930
        if ( ! is_object( $product ) || ! $item->is_type( 'shipping' ) ) {
1931
            return false;
1932
        }
1933
1934
        if ( ! $this->get_id() ) {
1935
            $this->save();
1936
        }
1937
1938
		$item->set_order_id( $this->get_id() );
1939
1940
        if ( ! $item->get_order_item_id() ) {
1941
            $inserting = true;
1942
        } else {
1943
            $inserting = false;
1944
        }
1945
1946
        if ( isset( $args['method_title'] ) ) {
1947
            $item->set_method_title( $args['method_title'] );
1948
        }
1949
1950
        if ( isset( $args['method_id'] ) ) {
1951
            $item->set_method_id( $args['method_id'] );
1952
        }
1953
1954
        if ( isset( $args['cost'] ) ) {
1955
            // Get old cost before updating
1956
            $old_cost = $item->get_cost();
1957
1958
            // Update
1959
            $item->set_cost( $args['cost'] );
1960
1961
            // Update total
1962
            $this->set_total( $this->get_total_shipping() - wc_format_decimal( $old_cost ) + $item->get_cost(), 'shipping' );
1963
        }
1964
1965
        if ( isset( $args['taxes'] ) && is_array( $args['taxes'] ) ) {
1966
            $item->set_taxes( $args['taxes'] );
1967
        }
1968
1969
        if ( isset( $args['meta'] ) && is_array( $args['meta'] ) ) {
1970
			foreach ( $args['meta'] as $key => $value ) {
1971
				$item->update_meta_data( $key, $value );
1972
			}
1973
		}
1974
1975
        $item->save();
1976
1977
        if ( ! $inserting ) {
1978
            do_action( 'woocommerce_order_update_shipping', $this->get_id(), $item->get_order_item_id(), $args );
1979
        }
1980
1981
        return $item->get_order_item_id();
1982
    }
1983
1984
    /**
1985
     * Add a fee to the order.
1986
     * Order must be saved prior to adding items.
1987
     * @param object $fee
1988
     * @return int updated order item ID
1989
     */
1990
    public function add_fee( $fee ) {
1991
        $args = wp_parse_args( $args, array(
1992
            'name'      => $fee->name,
1993
            'tax_class' => $fee->taxable ? $fee->tax_class : 0,
1994
            'total'     => $fee->amount,
1995
            'total_tax' => $fee->tax,
1996
            'taxes'     => array(
1997
                'total' => $fee->tax_data
1998
            )
1999
        ) );
2000
        $item = new WC_Order_Item_Fee();
2001
        $item_id = $this->update_fee( $item, $args );
2002
2003
        do_action( 'woocommerce_order_add_fee', $this->get_id(), $item->get_order_item_id(), $fee );
2004
2005
        return $item_id;
2006
    }
2007
2008
    /**
2009
     * Update fee for order.
2010
     *
2011
     * Note this does not update order totals.
2012
     *
2013
     * @since 2.2
2014
     * @param object|int $item
2015
     * @param array $args
2016
     * @return int updated order item ID
2017
     */
2018
    public function update_fee( $item, $args ) {
2019
        if ( is_numeric( $item ) ) {
2020
            $item = $this->get_item( $item );
2021
        }
2022
2023
        if ( ! is_object( $product ) || ! $item->is_type( 'fee' ) ) {
2024
            return false;
2025
        }
2026
2027
        if ( ! $this->get_id() ) {
2028
            $this->save();
2029
        }
2030
2031
		$item->set_order_id( $this->get_id() );
2032
2033
        if ( ! $item->get_order_item_id() ) {
2034
            $inserting = true;
2035
        } else {
2036
            $inserting = false;
2037
        }
2038
2039
        if ( isset( $args['name'] ) ) {
2040
            $item->set_name( $args['name'] );
2041
        }
2042
2043
        if ( isset( $args['tax_class'] ) ) {
2044
            $item->set_tax_class( $args['tax_class'] );
2045
        }
2046
2047
        if ( isset( $args['total'] ) ) {
2048
            $item->set_total( $args['total'] );
2049
        }
2050
2051
        if ( isset( $args['total_tax'] ) ) {
2052
            $item->set_total_tax( $args['total_tax'] );
2053
        }
2054
2055
        if ( isset( $args['taxes'] ) ) {
2056
            $item->set_taxes( $args['taxes'] );
2057
        }
2058
2059
        $item->save();
2060
2061
        if ( ! $inserting ) {
2062
            do_action( 'woocommerce_order_update_fee', $this->get_id(), $item->get_order_item_id(), $args );
2063
        }
2064
2065
        return $item->get_order_item_id();
2066
    }
2067
2068
    /**
2069
     * Add a tax row to the order.
2070
     * Order must be saved prior to adding items.
2071
     * @since 2.2
2072
     * @param int tax_rate_id
2073
     * @return int updated order item ID
2074
     */
2075
    public function add_tax( $tax_rate_id, $tax_amount = 0, $shipping_tax_amount = 0 ) {
2076
        if ( ! $code = WC_Tax::get_rate_code( $tax_rate_id ) ) {
2077
            return false;
2078
        }
2079
2080
        $args = wp_parse_args( $args, array(
2081
            'rate_code'          => $code,
2082
            'rate_id'            => $tax_rate_id,
2083
            'label'              => WC_Tax::get_rate_label( $tax_rate_id ),
2084
            'compound'           => WC_Tax::is_compound( $tax_rate_id ),
2085
            'tax_total'          => $tax_amount,
2086
            'shipping_tax_total' => $shipping_tax_amount
2087
        ) );
2088
        $item = new WC_Order_Item_Tax();
2089
        $item_id = $this->update_tax( $item, $args );
2090
2091
        do_action( 'woocommerce_order_add_tax', $this->get_id(), $item->get_order_item_id(), $tax_rate_id, $tax_amount, $shipping_tax_amount );
2092
2093
        return $item_id;
2094
    }
2095
2096
    /**
2097
     * Update tax line on order.
2098
     * Note this does not update order totals.
2099
     *
2100
     * @since 2.6
2101
     * @param object|int $item
2102
     * @param array $args
2103
     * @return int updated order item ID
2104
     */
2105
    public function update_tax( $item, $args ) {
2106
        if ( is_numeric( $item ) ) {
2107
            $item = $this->get_item( $item );
2108
        }
2109
2110
        if ( ! is_object( $product ) || ! $item->is_type( 'tax' ) ) {
2111
            return false;
2112
        }
2113
2114
        if ( ! $this->get_id() ) {
2115
            $this->save();
2116
        }
2117
2118
		$item->set_order_id( $this->get_id() );
2119
2120
        if ( ! $item->get_order_item_id() ) {
2121
            $inserting = true;
2122
        } else {
2123
            $inserting = false;
2124
        }
2125
2126
        if ( isset( $args['rate_code'] ) ) {
2127
            $item->set_rate_code( $args['rate_code'] );
2128
        }
2129
2130
        if ( isset( $args['rate_id'] ) ) {
2131
            $item->set_rate_id( $args['rate_id'] );
2132
        }
2133
2134
        if ( isset( $args['label'] ) ) {
2135
            $item->set_label( $args['label'] );
2136
        }
2137
2138
        if ( isset( $args['compound'] ) ) {
2139
            $item->set_compound( $args['compound'] );
2140
        }
2141
2142
        if ( isset( $args['tax_total'] ) ) {
2143
            $item->set_tax_total( $args['tax_total'] );
2144
        }
2145
2146
        if ( isset( $args['shipping_tax_total'] ) ) {
2147
            $item->set_shipping_tax_total( $args['shipping_tax_total'] );
2148
        }
2149
2150
        $item->save();
2151
2152
        if ( ! $inserting ) {
2153
            do_action( 'woocommerce_order_update_tax', $this->get_id(), $item->get_order_item_id(), $args );
2154
        }
2155
2156
        return $item->get_order_item_id();
2157
    }
2158
2159
	/**
2160
     * Update tax lines at order level by looking at the line item taxes themselves.
2161
     * @return bool success or fail.
2162
     */
2163
    public function update_taxes() {
2164
        $cart_taxes     = array();
2165
        $shipping_taxes = array();
2166
2167 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...
2168
            $taxes = $item->get_taxes();
2169
            if ( isset( $taxes['total'] ) ) {
2170
                foreach ( $taxes['total'] as $tax_rate_id => $tax ) {
2171
                    if ( ! isset( $cart_taxes[ $tax_rate_id ] ) ) {
2172
                        $cart_taxes[ $tax_rate_id ] = 0;
2173
                    }
2174
                    $cart_taxes[ $tax_rate_id ] += $tax;
2175
                }
2176
            }
2177
        }
2178
2179 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...
2180
			$taxes = $item->get_taxes();
2181
            if ( isset( $taxes['total'] ) ) {
2182
                foreach ( $taxes['total'] as $tax_rate_id => $tax ) {
2183
                    if ( ! isset( $shipping_taxes[ $tax_rate_id ] ) ) {
2184
                        $shipping_taxes[ $tax_rate_id ] = 0;
2185
                    }
2186
                    $shipping_taxes[ $tax_rate_id ] += $tax;
2187
                }
2188
            }
2189
        }
2190
2191
        // Remove old existing tax rows.
2192
        $this->remove_order_items( 'tax' );
2193
2194
        // Now merge to keep tax rows.
2195 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...
2196
            $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 );
2197
        }
2198
2199
        // Save tax totals
2200
        $this->set_total( WC_Tax::round( array_sum( $shipping_taxes ) ), 'shipping_tax' );
2201
        $this->set_total( WC_Tax::round( array_sum( $cart_taxes ) ), 'tax' );
2202
2203
        return true;
2204
    }
2205
2206
    /*
2207
    |--------------------------------------------------------------------------
2208
    | Calculations.
2209
    |--------------------------------------------------------------------------
2210
    |
2211
    | These methods calculate order totals and taxes based on the current data.
2212
    |
2213
    */
2214
2215
    /**
2216
     * Calculate shipping total.
2217
     *
2218
     * @since 2.2
2219
     * @return float
2220
     */
2221
    public function calculate_shipping() {
2222
        $shipping_total = 0;
2223
2224
        foreach ( $this->get_shipping_methods() as $shipping ) {
2225
            $shipping_total += $shipping->get_total();
2226
        }
2227
2228
        $this->set_total( $shipping_total, 'shipping' );
2229
2230
        return $this->get_shipping_total();
2231
    }
2232
2233
    /**
2234
     * Calculate taxes for all line items and shipping, and store the totals and tax rows.
2235
     *
2236
     * Will use the base country unless customer addresses are set.
2237
     *
2238
     * @return bool success or fail.
2239
     */
2240
    public function calculate_taxes() {
2241
        $cart_tax     = 0;
2242
        $cart_taxes   = array();
2243
        $tax_based_on = get_option( 'woocommerce_tax_based_on' );
2244
2245
        if ( 'billing' === $tax_based_on ) {
2246
            $country  = $this->get_billing_country();
2247
            $state    = $this->get_billing_state();
2248
            $postcode = $this->get_billing_postcode();
2249
            $city     = $this->get_billing_city();
2250
        } elseif ( 'shipping' === $tax_based_on ) {
2251
            $country  = $this->get_shipping_country();
2252
            $state    = $this->get_shipping_state();
2253
            $postcode = $this->get_shipping_postcode();
2254
            $city     = $this->get_shipping_city();
2255
        }
2256
2257
        // Default to base
2258 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...
2259
            $default  = wc_get_base_location();
2260
            $country  = $default['country'];
2261
            $state    = $default['state'];
2262
            $postcode = '';
2263
            $city     = '';
2264
        }
2265
2266
        // Get items
2267
        foreach ( $this->get_items( array( 'line_item', 'fee' ) ) as $item_id => $item ) {
2268
			$tax_class  = $item->get_tax_class();
2269
			$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...
2270
2271
            if ( '0' !== $tax_class && 'taxable' === $item_tax_status ) {
2272
2273
                $tax_rates = WC_Tax::find_rates( array(
2274
                    'country'   => $country,
2275
                    'state'     => $state,
2276
                    'postcode'  => $postcode,
2277
                    'city'      => $city,
2278
                    'tax_class' => $tax_class
2279
                ) );
2280
2281
				$total     = $item->get_total();
2282
				$taxes     = WC_Tax::calc_tax( $total, $tax_rates, false );
2283
				$total_tax = max( 0, array_sum( $taxes ) );
2284
				$cart_tax += $total_tax;
2285
				$item->set_total_tax( $total_tax );
2286
2287
				if ( $item->is_type( 'line_item' ) ) {
2288
					$subtotal       = $item->get_subtotal();
2289
					$subtotal_taxes = WC_Tax::calc_tax( $subtotal, $tax_rates, false );
2290
					$subtotal_tax   = max( 0, array_sum( $subtotal_taxes ) );
2291
					$item->set_subtotal_tax( $subtotal_tax );
2292
					$item->set_taxes( array( 'total' => $taxes, 'subtotal' => $subtotal_taxes ) );
2293
				} else {
2294
					$item->set_taxes( array( 'total' => $taxes ) );
2295
				}
2296
2297
				$item->save(); //@todo store items to self, don't save right away
2298
2299
                // Sum the item taxes
2300 View Code Duplication
                foreach ( array_keys( $cart_taxes + $taxes ) as $key ) {
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...
2301
                    $cart_taxes[ $key ] = ( isset( $taxes[ $key ] ) ? $taxes[ $key ] : 0 ) + ( isset( $taxes[ $key ] ) ? $cart_taxes[ $key ] : 0 );
2302
                }
2303
            }
2304
        }
2305
2306
        // Now calculate shipping tax
2307
        $shipping_methods = $this->get_shipping_methods();
2308
2309
        if ( ! empty( $shipping_methods ) ) {
2310
            $matched_tax_rates = array();
2311
            $tax_rates         = WC_Tax::find_rates( array(
2312
                'country'   => $country,
2313
                'state'     => $state,
2314
                'postcode'  => $postcode,
2315
                'city'      => $city,
2316
                'tax_class' => ''
2317
            ) );
2318
2319 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...
2320
                foreach ( $tax_rates as $key => $rate ) {
2321
                    if ( isset( $rate['shipping'] ) && 'yes' === $rate['shipping'] ) {
2322
                        $matched_tax_rates[ $key ] = $rate;
2323
                    }
2324
                }
2325
            }
2326
2327
            $shipping_taxes     = WC_Tax::calc_shipping_tax( $this->order_shipping, $matched_tax_rates );
2328
            $shipping_tax_total = WC_Tax::round( array_sum( $shipping_taxes ) );
2329
        } else {
2330
            $shipping_taxes     = array();
2331
            $shipping_tax_total = 0;
2332
        }
2333
2334
        // Save tax totals
2335
        $this->set_total( $shipping_tax_total, 'shipping_tax' );
2336
        $this->set_total( $tax_total, 'tax' );
2337
2338
        // Tax rows
2339
        $this->remove_order_items( 'tax' );
2340
2341
        // Now merge to keep tax rows
2342 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...
2343
            $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 );
2344
        }
2345
2346
        return true;
2347
    }
2348
2349
    /**
2350
     * Calculate totals by looking at the contents of the order. Stores the totals and returns the orders final total.
2351
     *
2352
     * @since 2.2
2353
     * @param  bool $and_taxes Calc taxes if true.
2354
     * @return float calculated grand total.
2355
     */
2356
    public function calculate_totals( $and_taxes = true ) {
2357
        $cart_subtotal     = 0;
2358
        $cart_total        = 0;
2359
        $fee_total         = 0;
2360
        $cart_subtotal_tax = 0;
2361
        $cart_total_tax    = 0;
2362
2363
        if ( $and_taxes && wc_tax_enabled() ) {
2364
            $this->calculate_taxes();
2365
        }
2366
2367
        // line items
2368
        foreach ( $this->get_items() as $item ) {
2369
            $cart_subtotal     += wc_format_decimal( isset( $item['line_subtotal'] ) ? $item['line_subtotal'] : 0 );
2370
            $cart_total        += wc_format_decimal( isset( $item['line_total'] ) ? $item['line_total'] : 0 );
2371
            $cart_subtotal_tax += wc_format_decimal( isset( $item['line_subtotal_tax'] ) ? $item['line_subtotal_tax'] : 0 );
2372
            $cart_total_tax    += wc_format_decimal( isset( $item['line_tax'] ) ? $item['line_tax'] : 0 );
2373
        }
2374
2375
        $this->calculate_shipping();
2376
2377
        foreach ( $this->get_fees() as $item ) {
2378
            $fee_total += $item['line_total'];
2379
        }
2380
2381
        $this->set_total( $cart_subtotal - $cart_total, 'cart_discount' );
2382
        $this->set_total( $cart_subtotal_tax - $cart_total_tax, 'cart_discount_tax' );
2383
2384
        $grand_total = round( $cart_total + $fee_total + $this->get_total_shipping() + $this->get_cart_tax() + $this->get_shipping_tax(), wc_get_price_decimals() );
2385
2386
        $this->set_total( $grand_total, 'total' );
2387
2388
        return $grand_total;
2389
    }
2390
2391
    /*
2392
    |--------------------------------------------------------------------------
2393
    | Total Getters
2394
    |--------------------------------------------------------------------------
2395
    |
2396
    | Methods for getting totals e.g. for display on the frontend.
2397
    |
2398
    */
2399
2400
    /**
2401
     * Get a product (either product or variation).
2402
     *
2403
     * @param object $item
2404
     * @return WC_Product|bool
2405
     */
2406
    public function get_product_from_item( $item ) {
2407
        if ( is_callable( array( $item, 'get_product' ) ) ) {
2408
            $product = $item->get_product();
2409
        } else {
2410
            $product = false;
2411
        }
2412
        return apply_filters( 'woocommerce_get_product_from_item', $product, $item, $this );
2413
    }
2414
2415
    /**
2416
     * Get item subtotal - this is the cost before discount.
2417
     *
2418
     * @param object $item
2419
     * @param bool $inc_tax (default: false).
2420
     * @param bool $round (default: true).
2421
     * @return float
2422
     */
2423 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...
2424
        $subtotal = 0;
2425
2426
        if ( is_callable( array( $item, 'get_subtotal' ) ) ) {
2427
            if ( $inc_tax ) {
2428
                $subtotal = ( $item->get_subtotal() + $item->get_subtotal_tax() ) / max( 1, $item->get_qty() );
2429
            } else {
2430
                $subtotal = ( $item->get_subtotal() / max( 1, $item->get_qty() ) );
2431
            }
2432
2433
            $subtotal = $round ? number_format( (float) $subtotal, wc_get_price_decimals(), '.', '' ) : $subtotal;
2434
        }
2435
2436
        return apply_filters( 'woocommerce_order_amount_item_subtotal', $subtotal, $this, $item, $inc_tax, $round );
2437
    }
2438
2439
    /**
2440
     * Get line subtotal - this is the cost before discount.
2441
     *
2442
     * @param object $item
2443
     * @param bool $inc_tax (default: false).
2444
     * @param bool $round (default: true).
2445
     * @return float
2446
     */
2447 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...
2448
        $subtotal = 0;
2449
2450
        if ( is_callable( array( $item, 'get_subtotal' ) ) ) {
2451
            if ( $inc_tax ) {
2452
                $subtotal = $item->get_subtotal() + $item->get_subtotal_tax();
2453
            } else {
2454
                $subtotal = $item->get_subtotal();
2455
            }
2456
2457
            $subtotal = $round ? round( $subtotal, wc_get_price_decimals() ) : $subtotal;
2458
        }
2459
2460
        return apply_filters( 'woocommerce_order_amount_line_subtotal', $subtotal, $this, $item, $inc_tax, $round );
2461
    }
2462
2463
    /**
2464
     * Calculate item cost - useful for gateways.
2465
     *
2466
     * @param object $item
2467
     * @param bool $inc_tax (default: false).
2468
     * @param bool $round (default: true).
2469
     * @return float
2470
     */
2471 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...
2472
        $total = 0;
2473
2474
        if ( is_callable( array( $item, 'get_total' ) ) ) {
2475
            if ( $inc_tax ) {
2476
                $total = ( $item->get_total() + $item->get_total_tax() ) / max( 1, $item->get_qty() );
2477
            } else {
2478
                $total = $item->get_total() / max( 1, $item->get_qty() );
2479
            }
2480
2481
            $total = $round ? round( $total, wc_get_price_decimals() ) : $total;
2482
        }
2483
2484
        return apply_filters( 'woocommerce_order_amount_item_total', $total, $this, $item, $inc_tax, $round );
2485
    }
2486
2487
    /**
2488
     * Calculate line total - useful for gateways.
2489
     *
2490
     * @param object $item
2491
     * @param bool $inc_tax (default: false).
2492
     * @param bool $round (default: true).
2493
     * @return float
2494
     */
2495 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...
2496
        $total = 0;
2497
2498
        if ( is_callable( array( $item, 'get_total' ) ) ) {
2499
            // Check if we need to add line tax to the line total.
2500
            $total = $inc_tax ? $item->get_total() + $item->get_total_tax() : $item->get_total();
2501
2502
            // Check if we need to round.
2503
            $total = $round ? round( $total, wc_get_price_decimals() ) : $total;
2504
        }
2505
2506
        return apply_filters( 'woocommerce_order_amount_line_total', $total, $this, $item, $inc_tax, $round );
2507
    }
2508
2509
    /**
2510
     * Get item tax - useful for gateways.
2511
     *
2512
     * @param mixed $item
2513
     * @param bool $round (default: true).
2514
     * @return float
2515
     */
2516
    public function get_item_tax( $item, $round = true ) {
2517
        $tax = 0;
2518
2519
        if ( is_callable( array( $item, 'get_total_tax' ) ) ) {
2520
            $tax = $item->get_total_tax() / max( 1, $item->get_qty() );
2521
            $tax = $round ? wc_round_tax_total( $tax ) : $tax;
2522
        }
2523
2524
        return apply_filters( 'woocommerce_order_amount_item_tax', $tax, $item, $round, $this );
2525
    }
2526
2527
    /**
2528
     * Get line tax - useful for gateways.
2529
     *
2530
     * @param mixed $item
2531
     * @return float
2532
     */
2533
    public function get_line_tax( $item ) {
2534
        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 );
2535
    }
2536
2537
    /**
2538
     * Gets line subtotal - formatted for display.
2539
     *
2540
     * @param array  $item
2541
     * @param string $tax_display
2542
     * @return string
2543
     */
2544
    public function get_formatted_line_subtotal( $item, $tax_display = '' ) {
2545
        $tax_display = $tax_display ? $tax_display : get_option( 'woocommerce_tax_display_cart' );
2546
2547
        if ( ! isset( $item['line_subtotal'] ) || ! isset( $item['line_subtotal_tax'] ) ) {
2548
            return '';
2549
        }
2550
2551
        if ( 'excl' == $tax_display ) {
2552
            $ex_tax_label = $this->get_prices_include_tax() ? 1 : 0;
2553
2554
            $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...
2555
        } else {
2556
            $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...
2557
        }
2558
2559
        return apply_filters( 'woocommerce_order_formatted_line_subtotal', $subtotal, $item, $this );
2560
    }
2561
2562
    /**
2563
     * Gets order total - formatted for display.
2564
     *
2565
     * @return string
2566
     */
2567
    public function get_formatted_order_total() {
2568
        $formatted_total = wc_price( $this->get_total(), array( 'currency' => $this->get_order_currency() ) );
2569
        return apply_filters( 'woocommerce_get_formatted_order_total', $formatted_total, $this );
2570
    }
2571
2572
    /**
2573
     * Gets subtotal - subtotal is shown before discounts, but with localised taxes.
2574
     *
2575
     * @param bool $compound (default: false).
2576
     * @param string $tax_display (default: the tax_display_cart value).
2577
     * @return string
2578
     */
2579
    public function get_subtotal_to_display( $compound = false, $tax_display = '' ) {
2580
        $tax_display = $tax_display ? $tax_display : get_option( 'woocommerce_tax_display_cart' );
2581
        $subtotal    = 0;
2582
2583
        if ( ! $compound ) {
2584
            foreach ( $this->get_items() as $item ) {
2585
                $subtotal += $item->get_subtotal();
2586
2587
                if ( 'incl' === $tax_display ) {
2588
                    $subtotal += $item->get_subtotal_tax();
2589
                }
2590
            }
2591
2592
            $subtotal = wc_price( $subtotal, array( 'currency' => $this->get_order_currency() ) );
2593
2594
            if ( 'excl' === $tax_display && $this->get_prices_include_tax() ) {
2595
                $subtotal .= ' <small class="tax_label">' . WC()->countries->ex_tax_or_vat() . '</small>';
2596
            }
2597
2598
        } else {
2599
            if ( 'incl' === $tax_display ) {
2600
                return '';
2601
            }
2602
2603
            foreach ( $this->get_items() as $item ) {
2604
                $subtotal += $item->get_subtotal();
2605
            }
2606
2607
            // Add Shipping Costs.
2608
            $subtotal += $this->get_total_shipping();
2609
2610
            // Remove non-compound taxes.
2611
            foreach ( $this->get_taxes() as $tax ) {
2612
                if ( ! empty( $tax['compound'] ) ) {
2613
                    continue;
2614
                }
2615
                $subtotal = $subtotal + $tax['tax_amount'] + $tax['shipping_tax_amount'];
2616
            }
2617
2618
            // Remove discounts.
2619
            $subtotal = $subtotal - $this->get_total_discount();
2620
            $subtotal = wc_price( $subtotal, array( 'currency' => $this->get_order_currency() ) );
2621
        }
2622
2623
        return apply_filters( 'woocommerce_order_subtotal_to_display', $subtotal, $compound, $this );
2624
    }
2625
2626
    /**
2627
     * Gets shipping (formatted).
2628
     *
2629
     * @return string
2630
     */
2631
    public function get_shipping_to_display( $tax_display = '' ) {
2632
        $tax_display = $tax_display ? $tax_display : get_option( 'woocommerce_tax_display_cart' );
2633
2634
        if ( $this->order_shipping != 0 ) {
2635
2636
            if ( $tax_display == 'excl' ) {
2637
2638
                // Show shipping excluding tax.
2639
                $shipping = wc_price( $this->order_shipping, array('currency' => $this->get_order_currency()) );
2640
2641 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...
2642
                    $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 );
2643
                }
2644
2645
            } else {
2646
2647
                // Show shipping including tax.
2648
                $shipping = wc_price( $this->order_shipping + $this->order_shipping_tax, array('currency' => $this->get_order_currency()) );
2649
2650 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...
2651
                    $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 );
2652
                }
2653
2654
            }
2655
2656
            $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 );
2657
2658
        } elseif ( $this->get_shipping_method() ) {
2659
            $shipping = $this->get_shipping_method();
2660
        } else {
2661
            $shipping = __( 'Free!', 'woocommerce' );
2662
        }
2663
2664
        return apply_filters( 'woocommerce_order_shipping_to_display', $shipping, $this );
2665
    }
2666
2667
    /**
2668
     * Get the discount amount (formatted).
2669
     * @since  2.3.0
2670
     * @return string
2671
     */
2672
    public function get_discount_to_display( $tax_display = '' ) {
2673
        $tax_display = $tax_display ? $tax_display : get_option( 'woocommerce_tax_display_cart' );
2674
        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 );
2675
    }
2676
2677
    /**
2678
     * Get totals for display on pages and in emails.
2679
     *
2680
     * @param mixed $tax_display
2681
     * @return array
2682
     */
2683
    public function get_order_item_totals( $tax_display = '' ) {
2684
        $tax_display = $tax_display ? $tax_display : get_option( 'woocommerce_tax_display_cart' );
2685
        $total_rows  = array();
2686
2687
        if ( $subtotal = $this->get_subtotal_to_display( false, $tax_display ) ) {
2688
            $total_rows['cart_subtotal'] = array(
2689
                'label' => __( 'Subtotal:', 'woocommerce' ),
2690
                'value'    => $subtotal
2691
            );
2692
        }
2693
2694
        if ( $this->get_total_discount() > 0 ) {
2695
            $total_rows['discount'] = array(
2696
                'label' => __( 'Discount:', 'woocommerce' ),
2697
                'value'    => '-' . $this->get_discount_to_display( $tax_display )
2698
            );
2699
        }
2700
2701
        if ( $this->get_shipping_method() ) {
2702
            $total_rows['shipping'] = array(
2703
                'label' => __( 'Shipping:', 'woocommerce' ),
2704
                'value'    => $this->get_shipping_to_display( $tax_display )
2705
            );
2706
        }
2707
2708
        if ( $fees = $this->get_fees() ) {
2709
            foreach ( $fees as $id => $fee ) {
2710
                if ( apply_filters( 'woocommerce_get_order_item_totals_excl_free_fees', empty( $fee['line_total'] ) && empty( $fee['line_tax'] ), $id ) ) {
2711
                    continue;
2712
                }
2713
                $total_rows[ 'fee_' . $fee->get_order_item_id() ] = array(
2714
                    'label' => $fee->get_name() . ':',
2715
                    'value' => wc_price( 'excl' === $tax_display ? $fee->get_total() : $fee->get_total() + $fee->get_total_tax(), array('currency' => $this->get_order_currency()) )
2716
                );
2717
            }
2718
        }
2719
2720
        // Tax for tax exclusive prices.
2721
        if ( 'excl' === $tax_display ) {
2722
2723
            if ( get_option( 'woocommerce_tax_total_display' ) == 'itemized' ) {
2724
2725
                foreach ( $this->get_tax_totals() as $code => $tax ) {
2726
2727
                    $total_rows[ sanitize_title( $code ) ] = array(
2728
                        'label' => $tax->label . ':',
2729
                        'value'    => $tax->formatted_amount
2730
                    );
2731
                }
2732
2733
            } else {
2734
2735
                $total_rows['tax'] = array(
2736
                    'label' => WC()->countries->tax_or_vat() . ':',
2737
                    'value'    => wc_price( $this->get_total_tax(), array( 'currency' => $this->get_order_currency() ) )
2738
                );
2739
            }
2740
        }
2741
2742
        if ( $this->get_total() > 0 && $this->get_payment_method_title() ) {
2743
            $total_rows['payment_method'] = array(
2744
                'label' => __( 'Payment Method:', 'woocommerce' ),
2745
                'value' => $this->get_payment_method_title()
2746
            );
2747
        }
2748
2749
        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...
2750
            foreach ( $refunds as $id => $refund ) {
2751
                $total_rows[ 'refund_' . $id ] = array(
2752
                    'label' => $refund->get_refund_reason() ? $refund->get_refund_reason() : __( 'Refund', 'woocommerce' ) . ':',
2753
                    'value'    => wc_price( '-' . $refund->get_refund_amount(), array( 'currency' => $this->get_order_currency() ) )
2754
                );
2755
            }
2756
        }
2757
2758
        $total_rows['order_total'] = array(
2759
            'label' => __( 'Total:', 'woocommerce' ),
2760
            '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...
2761
        );
2762
2763
        return apply_filters( 'woocommerce_get_order_item_totals', $total_rows, $this );
2764
    }
2765
2766
    /*
2767
    |--------------------------------------------------------------------------
2768
    | Conditionals
2769
    |--------------------------------------------------------------------------
2770
    |
2771
    | Checks if a condition is true or false.
2772
    |
2773
    */
2774
2775
    /**
2776
     * Checks the order status against a passed in status.
2777
     *
2778
     * @return bool
2779
     */
2780
    public function has_status( $status ) {
2781
        return apply_filters( 'woocommerce_order_has_status', ( is_array( $status ) && in_array( $this->get_status(), $status ) ) || $this->get_status() === $status ? true : false, $this, $status );
2782
    }
2783
2784
    /**
2785
     * has_meta function for order items.
2786
     *
2787
     * @param string $order_item_id
2788
     * @return array of meta data.
2789
     */
2790
    public function has_meta( $order_item_id ) {
2791
        global $wpdb;
2792
2793
        return $wpdb->get_results( $wpdb->prepare( "SELECT meta_key, meta_value, meta_id, order_item_id
2794
            FROM {$wpdb->prefix}woocommerce_order_itemmeta WHERE order_item_id = %d
2795
            ORDER BY meta_id", absint( $order_item_id ) ), ARRAY_A );
2796
    }
2797
2798
    /**
2799
     * Check whether this order has a specific shipping method or not.
2800
     *
2801
     * @param string $method_id
2802
     * @return bool
2803
     */
2804
    public function has_shipping_method( $method_id ) {
2805
        foreach ( $this->get_shipping_methods() as $shipping_method ) {
2806
            if ( $shipping_method->get_method_id() === $method_id ) {
2807
                return true;
2808
            }
2809
        }
2810
        return false;
2811
    }
2812
2813
    /**
2814
     * Check if an order key is valid.
2815
     *
2816
     * @param mixed $key
2817
     * @return bool
2818
     */
2819
    public function key_is_valid( $key ) {
2820
        return $key === $this->get_order_key();
2821
    }
2822
2823
    /**
2824
     * Returns true if the order contains a free product.
2825
     * @since 2.5.0
2826
     * @return bool
2827
     */
2828
    public function has_free_item() {
2829
        foreach ( $this->get_items() as $item ) {
2830
            if ( ! $item->get_total() ) {
2831
                return true;
2832
            }
2833
        }
2834
        return false;
2835
    }
2836
2837
    /*
2838
    |--------------------------------------------------------------------------
2839
    | Deprecated methods
2840
    |--------------------------------------------------------------------------
2841
    |
2842
    | Will be removed after 2 major releases, or 1 year.
2843
    |
2844
    */
2845
2846
    /**
2847
     * Magic __isset method for backwards compatibility.
2848
     * @param string $key
2849
     * @return bool
2850
     */
2851
    public function __isset( $key ) {
2852
        // Legacy properties which could be accessed directly in the past.
2853
        $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' );
2854
        return $this->get_id() ? ( in_array( $key, $legacy_props ) || metadata_exists( 'post', $this->get_id(), '_' . $key ) ) : false;
2855
    }
2856
2857
    /**
2858
     * Magic __get method for backwards compatibility.
2859
     * @param string $key
2860
     * @return mixed
2861
     */
2862
    public function __get( $key ) {
2863
        /**
2864
         * Maps legacy vars to new getters.
2865
         */
2866
        if ( 'completed_date' === $key ) {
2867
            _doing_it_wrong( $key, 'Order properties should not be accessed directly.', '2.6' );
2868
            return $this->get_date_completed();
2869
		} elseif ( 'paid_date' === $key ) {
2870
            _doing_it_wrong( $key, 'Order properties should not be accessed directly.', '2.6' );
2871
            return $this->get_date_paid();
2872
        } elseif ( 'modified_date' === $key ) {
2873
            _doing_it_wrong( $key, 'Order properties should not be accessed directly.', '2.6' );
2874
            return $this->get_date_modified();
2875
        } elseif ( 'order_date' === $key ) {
2876
            _doing_it_wrong( $key, 'Order properties should not be accessed directly.', '2.6' );
2877
            return $this->get_date_created();
2878
        } elseif ( 'id' === $key ) {
2879
            _doing_it_wrong( $key, 'Order properties should not be accessed directly.', '2.6' );
2880
            return $this->get_id();
2881
		} elseif ( 'post' === $key ) {
2882
            _doing_it_wrong( $key, 'Order properties should not be accessed directly.', '2.6' );
2883
            return get_post( $this->get_id() );
2884
		} elseif ( 'status' === $key || 'post_status' === $key ) {
2885
            _doing_it_wrong( $key, 'Order properties should not be accessed directly.', '2.6' );
2886
            return $this->get_status();
2887
		} elseif ( 'customer_message' === $key || 'customer_note' === $key ) {
2888
            _doing_it_wrong( $key, 'Order properties should not be accessed directly.', '2.6' );
2889
            return $this->get_customer_note();
2890
		} elseif ( in_array( $key, array( 'user_id', 'customer_user' ) ) ) {
2891
            _doing_it_wrong( $key, 'Order properties should not be accessed directly.', '2.6' );
2892
            return $this->get_customer_id();
2893
		} elseif ( 'tax_display_cart' === $key ) {
2894
            _doing_it_wrong( $key, 'Order properties should not be accessed directly.', '2.6' );
2895
			return get_option( 'woocommerce_tax_display_cart' );
2896
		} elseif ( 'display_totals_ex_tax' === $key ) {
2897
            _doing_it_wrong( $key, 'Order properties should not be accessed directly.', '2.6' );
2898
			return 'excl' === get_option( 'woocommerce_tax_display_cart' );
2899
		} elseif ( 'display_cart_ex_tax' === $key ) {
2900
            _doing_it_wrong( $key, 'Order properties should not be accessed directly.', '2.6' );
2901
			return 'excl' === get_option( 'woocommerce_tax_display_cart' );
2902
        } elseif ( 'cart_discount' === $key ) {
2903
            _doing_it_wrong( $key, 'Order properties should not be accessed directly.', '2.6' );
2904
			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...
2905
        } elseif ( 'cart_discount_tax' === $key ) {
2906
            _doing_it_wrong( $key, 'Order properties should not be accessed directly.', '2.6' );
2907
			return $this->get_discount_tax();
2908
        } elseif ( 'order_tax' === $key ) {
2909
            _doing_it_wrong( $key, 'Order properties should not be accessed directly.', '2.6' );
2910
			return $this->get_cart_tax();
2911
        } elseif ( 'order_shipping_tax' === $key ) {
2912
            _doing_it_wrong( $key, 'Order properties should not be accessed directly.', '2.6' );
2913
            return $this->get_shipping_tax();
2914
        } elseif ( 'order_shipping' === $key ) {
2915
            _doing_it_wrong( $key, 'Order properties should not be accessed directly.', '2.6' );
2916
            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...
2917
        /**
2918
         * Map vars to getters with warning.
2919
         */
2920
	 	} elseif ( is_callable( array( $this, "get_{$key}" ) ) ) {
2921
			_doing_it_wrong( $key, 'Order properties should not be accessed directly Use get_' . $key . '().', '2.6' );
2922
            return $this->{"get_{$key}"}();
2923
        /**
2924
         * Handle post meta
2925
         */
2926
        } else {
2927
            _doing_it_wrong( $key, 'Meta should not be accessed directly. Use WC_Order::get_order_meta( $key )', '2.6' );
2928
			$value = get_post_meta( $this->get_id(), '_' . $key, true );
2929
		}
2930
2931
        return $value;
2932
    }
2933
2934
    /**
2935
     * Get order item meta.
2936
     * @deprecated 2.6.0
2937
     * @param mixed $order_item_id
2938
     * @param string $key (default: '')
2939
     * @param bool $single (default: false)
2940
     * @return array|string
2941
     */
2942
    public function get_item_meta( $order_item_id, $key = '', $single = false ) {
2943
        _deprecated_function( 'get_item_meta', '2.6', 'wc_get_order_item_meta' );
2944
        return get_metadata( 'order_item', $order_item_id, $key, $single );
2945
    }
2946
2947
    /**
2948
     * Get all item meta data in array format in the order it was saved. Does not group meta by key like get_item_meta().
2949
     *
2950
     * @param mixed $order_item_id
2951
     * @return array of objects
2952
     */
2953
    public function get_item_meta_array( $order_item_id ) {
2954
        _deprecated_function( 'get_item_meta_array', '2.6', 'WC_Order_Item::get_meta_data()' );
2955
        $item = $this->get_item( $order_item_id );
2956
        return $item->get_meta_data();
2957
    }
2958
2959
    /**
2960
     * Expand item meta into the $item array.
2961
     * @deprecated 2.6.0 Item meta no longer expanded due to new order item
2962
     *        classes. This function now does nothing to avoid data breakage.
2963
     * @since 2.4.0
2964
     * @param array $item before expansion.
2965
     * @return array
2966
     */
2967
    public function expand_item_meta( $item ) {
2968
        _deprecated_function( 'expand_item_meta', '2.6', '' );
2969
        return $item;
2970
    }
2971
2972
	/**
2973
     * Load the order object. Called from the constructor.
2974
     * @deprecated 2.6.0 Logic moved to constructor
2975
     * @param int|object|WC_Order $order Order to init.
2976
     */
2977 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...
2978
		_deprecated_function( 'init', '2.6', 'Logic moved to constructor' );
2979
        if ( is_numeric( $order ) ) {
2980
            $this->read( $order );
2981
        } elseif ( $order instanceof WC_Order ) {
2982
            $this->read( absint( $order->get_id() ) );
2983
        } elseif ( isset( $order->ID ) ) {
2984
            $this->read( absint( $order->ID ) );
2985
        }
2986
    }
2987
2988
    /**
2989
     * Gets an order from the database.
2990
     * @deprecated 2.6
2991
     * @param int $id (default: 0).
2992
     * @return bool
2993
     */
2994 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...
2995
        _deprecated_function( 'get_order', '2.6', 'read' );
2996
        if ( ! $id ) {
2997
            return false;
2998
        }
2999
        if ( $result = get_post( $id ) ) {
3000
            $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...
3001
            return true;
3002
        }
3003
        return false;
3004
    }
3005
3006
    /**
3007
     * Populates an order from the loaded post data.
3008
     * @deprecated 2.6
3009
     * @param mixed $result
3010
     */
3011
    public function populate( $result ) {
3012
        _deprecated_function( 'populate', '2.6', 'read' );
3013
        $this->read( $result->ID );
3014
    }
3015
3016
	/**
3017
     * Cancel the order and restore the cart (before payment).
3018
     * @deprecated 2.6.0 Moved to event handler.
3019
     * @param string $note (default: '') Optional note to add.
3020
     */
3021
    public function cancel_order( $note = '' ) {
3022
		_deprecated_function( 'cancel_order', '2.6', 'update_status' );
3023
        WC()->session->set( 'order_awaiting_payment', false );
3024
        $this->update_status( 'cancelled', $note );
3025
    }
3026
3027
	/**
3028
     * Record sales.
3029
     * @deprecated 2.6.0
3030
     */
3031
    public function record_product_sales() {
3032
		_deprecated_function( 'record_product_sales', '2.6', 'wc_update_total_sales_counts' );
3033
		wc_update_total_sales_counts( $this->get_id() );
3034
    }
3035
3036
	/**
3037
     * Increase applied coupon counts.
3038
     * @deprecated 2.6.0
3039
     */
3040
    public function increase_coupon_usage_counts() {
3041
		_deprecated_function( 'increase_coupon_usage_counts', '2.6', 'wc_update_coupon_usage_counts' );
3042
		wc_update_coupon_usage_counts( $this->get_id() );
3043
    }
3044
3045
    /**
3046
     * Decrease applied coupon counts.
3047
     * @deprecated 2.6.0
3048
     */
3049
    public function decrease_coupon_usage_counts() {
3050
		_deprecated_function( 'decrease_coupon_usage_counts', '2.6', 'wc_update_coupon_usage_counts' );
3051
		wc_update_coupon_usage_counts( $this->get_id() );
3052
    }
3053
3054
	/**
3055
     * Reduce stock levels for all line items in the order.
3056
	 * @deprecated 2.6.0
3057
     */
3058
    public function reduce_order_stock() {
3059
        _deprecated_function( 'reduce_order_stock', '2.6', 'wc_reduce_stock_levels' );
3060
		wc_reduce_stock_levels( $this->get_id() );
3061
    }
3062
3063
	/**
3064
     * Send the stock notifications.
3065
	 * @deprecated 2.6.0 No longer needs to be called directly.
3066
     */
3067
    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...
3068
        _deprecated_function( 'send_stock_notifications', '2.6' );
3069
    }
3070
3071
	/**
3072
	 * Output items for display in html emails.
3073
	 * @deprecated 2.6.0 Moved to template functions.
3074
	 * @param array $args Items args.
3075
	 * @return string
3076
	 */
3077
	public function email_order_items_table( $args = array() ) {
3078
		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...
3079
	}
3080
}
3081