Completed
Push — master ( df624b...09e43c )
by Mike
11:25
created

WC_Abstract_Order::__construct()   B

Complexity

Conditions 6
Paths 8

Size

Total Lines 13
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 9
c 1
b 0
f 0
nc 8
nop 1
dl 0
loc 13
rs 8.8571
1
<?php
2
if ( ! defined( 'ABSPATH' ) ) {
3
	exit;
4
}
5
6
include_once( 'abstract-wc-legacy-order.php' );
7
8
/**
9
 * Abstract Order
10
 *
11
 * Handles generic order data and database interaction which is extended by both
12
 * WC_Order (regular orders) and WC_Order_Refund (refunds are negative orders).
13
 *
14
 * @class       WC_Abstract_Order
15
 * @version     2.7.0
16
 * @package     WooCommerce/Classes
17
 * @category    Class
18
 * @author      WooThemes
19
 */
20
abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order {
21
22
	/**
23
	 * Order Data array, with defaults. This is the core order data exposed
24
	 * in APIs since 2.7.0.
25
	 *
26
	 * Notes:
27
	 * order_tax = Sum of all taxes.
28
	 * cart_tax = cart_tax is the new name for the legacy 'order_tax' which is the tax for items only, not shipping.
29
	 *
30
	 * @since 2.7.0
31
	 * @var array
32
	 */
33
	protected $_data = array(
34
		'id'                 => 0,
35
		'parent_id'          => 0,
36
		'status'             => '',
37
		'currency'           => '',
38
		'version'            => '',
39
		'prices_include_tax' => false,
40
		'date_created'       => '',
41
		'date_modified'      => '',
42
		'discount_total'     => 0,
43
		'discount_tax'       => 0,
44
		'shipping_total'     => 0,
45
		'shipping_tax'       => 0,
46
		'cart_tax'           => 0,
47
		'total'              => 0,
48
		'total_tax'          => 0,
49
	);
50
51
	/**
52
	 * Data stored in meta keys, but not considered "meta" for an order.
53
	 * @since 2.7.0
54
	 * @var array
55
	 */
56
	protected $_internal_meta_keys = array(
57
		'_order_currency', '_cart_discount',
58
		'_cart_discount_tax', '_order_shipping', '_order_shipping_tax',
59
		'_order_tax', '_order_total', '_order_version', '_prices_include_tax',
60
		'_payment_tokens',
61
	);
62
63
	/**
64
	 * Order items will be stored here, sometimes before they persist in the DB.
65
	 * @since 2.7.0
66
	 * @var array
67
	 */
68
	protected $_items = array(
69
		'line_items'     => null,
70
		'coupon_lines'   => null,
71
		'shipping_lines' => null,
72
		'fee_lines'      => null,
73
		'tax_lines'      => null,
74
	);
75
76
	/**
77
	 * Order items that need deleting are stored here.
78
	 * @since 2.7.0
79
	 * @var array
80
	 */
81
	protected $_items_to_delete = array();
82
83
	/**
84
	 *  Internal meta type used to store order data.
85
	 * @var string
86
	 */
87
	protected $_meta_type = 'post';
88
89
	/**
90
	 * Stores meta in cache for future reads.
91
	 * A group must be set to to enable caching.
92
	 * @var string
93
	 */
94
	protected $_cache_group = 'order';
95
96
	/**
97
	 * Get the order if ID is passed, otherwise the order is new and empty.
98
	 * This class should NOT be instantiated, but the get_order function or new WC_Order_Factory.
99
	 * should be used. It is possible, but the aforementioned are preferred and are the only.
100
	 * methods that will be maintained going forward.
101
	 *
102
	 * @param  int|object|WC_Order $order Order to init.
103
	 */
104
	public function __construct( $order = 0 ) {
105
		if ( is_numeric( $order ) && $order > 0 ) {
106
			$this->read( $order );
107
		} elseif ( $order instanceof self ) {
108
			$this->read( absint( $order->get_id() ) );
109
		} elseif ( ! empty( $order->ID ) ) {
110
			$this->read( absint( $order->ID ) );
111
		}
112
		// Set default status if none were read.
113
		if ( ! $this->get_status() ) {
114
			$this->set_status( apply_filters( 'woocommerce_default_order_status', 'pending' ) );
115
		}
116
	}
117
118
	/*
119
	|--------------------------------------------------------------------------
120
	| CRUD methods
121
	|--------------------------------------------------------------------------
122
	|
123
	| Methods which create, read, update and delete orders from the database.
124
	| Written in abstract fashion so that the way orders are stored can be
125
	| changed more easily in the future.
126
	|
127
	| A save method is included for convenience (chooses update or create based
128
	| on if the order exists yet).
129
	|
130
	*/
131
132
	/**
133
	 * Get internal type (post type.)
134
	 * @return string
135
	 */
136
	public function get_type() {
137
		return 'shop_order';
138
	}
139
140
	/**
141
	 * Get a title for the new post type.
142
	 */
143
	protected function get_post_title() {
144
		return sprintf( __( 'Order &ndash; %s', 'woocommerce' ), strftime( _x( '%b %d, %Y @ %I:%M %p', 'Order date parsed by strftime', 'woocommerce' ) ) );
145
	}
146
147
	/**
148
	 * Insert data into the database.
149
	 * @since 2.7.0
150
	 */
151
	public function create() {
152
		$this->set_date_created( current_time( 'timestamp' ) );
153
		$this->set_currency( $this->get_currency() ? $this->get_currency() : get_woocommerce_currency() );
154
155
		$order_id = wp_insert_post( apply_filters( 'woocommerce_new_order_data', array(
156
			'post_date'     => date( 'Y-m-d H:i:s', $this->get_date_created() ),
157
			'post_date_gmt' => get_gmt_from_date( date( 'Y-m-d H:i:s', $this->get_date_created() ) ),
158
			'post_type'     => $this->get_type(),
159
			'post_status'   => 'wc-' . ( $this->get_status() ? $this->get_status() : apply_filters( 'woocommerce_default_order_status', 'pending' ) ),
160
			'ping_status'   => 'closed',
161
			'post_author'   => 1,
162
			'post_title'    => $this->get_post_title(),
163
			'post_password' => uniqid( 'order_' ),
164
			'post_parent'   => $this->get_parent_id(),
165
		) ), true );
166
167
		if ( $order_id ) {
168
			$this->set_id( $order_id );
169
170
			// Set meta data
171
			$this->update_post_meta( '_order_currency', $this->get_currency() );
172
			$this->update_post_meta( '_cart_discount', $this->get_discount_total( true ) );
173
			$this->update_post_meta( '_cart_discount_tax', $this->get_discount_tax( true ) );
174
			$this->update_post_meta( '_order_shipping', $this->get_shipping_total( true ) );
175
			$this->update_post_meta( '_order_shipping_tax', $this->get_shipping_tax( true ) );
176
			$this->update_post_meta( '_order_tax', $this->get_cart_tax( true ) );
177
			$this->update_post_meta( '_order_total', $this->get_total( true ) );
178
			$this->update_post_meta( '_order_version', $this->get_version() );
179
			$this->update_post_meta( '_prices_include_tax', $this->get_prices_include_tax() );
180
			$this->save_meta_data();
181
		}
182
	}
183
184
	/**
185
	 * Read from the database.
186
	 * @since 2.7.0
187
	 * @param int $id ID of object to read.
188
	 */
189
	public function read( $id ) {
190
		if ( empty( $id ) || ! ( $post_object = get_post( $id ) ) ) {
191
			return;
192
		}
193
194
		// Map standard post data
195
		$this->set_id( $post_object->ID );
196
		$this->set_parent_id( $post_object->post_parent );
197
		$this->set_date_created( $post_object->post_date );
198
		$this->set_date_modified( $post_object->post_modified );
199
		$this->set_status( $post_object->post_status );
200
		$this->set_currency( get_post_meta( $this->get_id(), '_order_currency', true ) );
201
		$this->set_discount_total( get_post_meta( $this->get_id(), '_cart_discount', true ) );
202
		$this->set_discount_tax( get_post_meta( $this->get_id(), '_cart_discount_tax', true ) );
203
		$this->set_shipping_total( get_post_meta( $this->get_id(), '_order_shipping', true ) );
204
		$this->set_shipping_tax( get_post_meta( $this->get_id(), '_order_shipping_tax', true ) );
205
		$this->set_cart_tax( get_post_meta( $this->get_id(), '_order_tax', true ) );
206
		$this->set_total( get_post_meta( $this->get_id(), '_order_total', true ) );
207
		$this->set_version( get_post_meta( $this->get_id(), '_order_version', true ) );
208
209
		// Orders store the state of prices including tax when created.
210
		$this->set_prices_include_tax( metadata_exists( 'post', $this->get_id(), '_prices_include_tax' ) ? 'yes' === get_post_meta( $this->get_id(), '_prices_include_tax', true ) : 'yes' === get_option( 'woocommerce_prices_include_tax' ) );
211
212
		// Load meta data
213
		$this->read_meta_data();
214
	}
215
216
	/**
217
	 * Post meta update wrapper. Sets or deletes based on value.
218
	 * @since 2.7.0
219
	 * @return bool Was it changed?
220
	 */
221
	protected function update_post_meta( $key, $value ) {
222
		if ( '' !== $value ) {
223
			return update_post_meta( $this->get_id(), $key, $value );
224
		} else {
225
			return delete_post_meta( $this->get_id(), $key );
226
		}
227
	}
228
229
	/**
230
	 * Update data in the database.
231
	 * @since 2.7.0
232
	 */
233
	public function update() {
234
		global $wpdb;
235
236
		$order_id = $this->get_id();
237
238
		$wpdb->update(
239
			$wpdb->posts,
240
			array(
241
				'post_date'     => date( 'Y-m-d H:i:s', $this->get_date_created() ),
242
				'post_date_gmt' => get_gmt_from_date( date( 'Y-m-d H:i:s', $this->get_date_created() ) ),
243
				'post_status'   => 'wc-' . ( $this->get_status() ? $this->get_status() : apply_filters( 'woocommerce_default_order_status', 'pending' ) ),
244
				'post_parent'   => $this->get_parent_id(),
245
			),
246
			array(
247
				'ID' => $order_id
248
			)
249
		);
250
251
		// Update meta data
252
		$this->update_post_meta( '_order_currency', $this->get_currency() );
253
		$this->update_post_meta( '_cart_discount', $this->get_discount_total( true ) );
254
		$this->update_post_meta( '_cart_discount_tax', $this->get_discount_tax( true ) );
255
		$this->update_post_meta( '_order_shipping', $this->get_shipping_total( true ) );
256
		$this->update_post_meta( '_order_shipping_tax', $this->get_shipping_tax( true ) );
257
		$this->update_post_meta( '_order_tax', $this->get_cart_tax( true ) );
258
		$this->update_post_meta( '_order_total', $this->get_total( true ) );
259
		$this->update_post_meta( '_order_version', $this->get_version() );
260
		$this->update_post_meta( '_prices_include_tax', $this->get_prices_include_tax() );
261
		$this->save_meta_data();
262
	}
263
264
	/**
265
	 * Delete data from the database.
266
	 * @since 2.7.0
267
	 */
268
	public function delete() {
269
		wp_delete_post( $this->get_id() );
270
	}
271
272
	/**
273
	 * Save data to the database.
274
	 * @since 2.7.0
275
	 * @return int order ID
276
	 */
277
	public function save() {
278
		$this->set_version( WC_VERSION );
279
280
		if ( ! $this->get_id() ) {
281
			$this->create();
282
		} else {
283
			$this->update();
284
		}
285
286
		$this->save_items();
287
		clean_post_cache( $this->get_id() );
288
		wc_delete_shop_order_transients( $this->get_id() );
289
290
		return $this->get_id();
291
	}
292
293
	/**
294
	 * Save all order items which are part of this order.
295
	 */
296
	protected function save_items() {
297
		// remove items
298
		foreach ( $this->_items_to_delete as $item ) {
299
			$item->delete();
300
		}
301
302
		$this->_items_to_delete = array();
303
304
		// Add/save items
305
		foreach ( $this->_items as $item_group => $items ) {
306
			if ( is_array( $items ) ) {
307
				foreach ( $items as $item_key => $item ) {
308
					$item->set_order_id( $this->get_id() );
309
					$item_id = $item->save();
310
311
					// If ID changed (new item saved to DB)...
312
					if ( $item_id !== $item_key ) {
313
						$this->_items[ $item_group ][ $item_id ] = $item;
314
						unset( $this->_items[ $item_group ][ $item_key ] );
315
316
						// Legacy action handler
317
						switch ( $item_group ) {
318
							case 'fee_lines' :
319
								if ( isset( $item->legacy_fee, $item->legacy_fee_key ) ) {
320
									wc_do_deprecated_action( 'woocommerce_add_order_fee_meta', array( $this->get_id(), $item_id, $item->legacy_fee, $item->legacy_fee_key ), '2.7', 'Use woocommerce_new_order_item action instead.' );
321
								}
322
							break;
323
							case 'shipping_lines' :
324
								if ( isset( $item->legacy_package_key ) ) {
325
									wc_do_deprecated_action( 'woocommerce_add_shipping_order_item', array( $item_id, $item->legacy_package_key ), '2.7', 'Use woocommerce_new_order_item action instead.' );
326
								}
327
							break;
328
							case 'line_items' :
329
								if ( isset( $item->legacy_values, $item->legacy_cart_item_key ) ) {
330
									wc_do_deprecated_action( 'woocommerce_add_order_item_meta', array( $item_id, $item->legacy_values, $item->legacy_cart_item_key ), '2.7', 'Use woocommerce_new_order_item action instead.' );
331
								}
332
							break;
333
						}
334
					}
335
				}
336
			}
337
		}
338
	}
339
340
	/*
341
	|--------------------------------------------------------------------------
342
	| Getters
343
	|--------------------------------------------------------------------------
344
	|
345
	| Methods for getting data from the order object.
346
	|
347
	*/
348
349
	/**
350
	 * Get all class data in array format.
351
	 * @since 2.7.0
352
	 * @return array
353
	 */
354 View Code Duplication
	public function get_data() {
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...
355
		return array_merge(
356
			$this->_data,
357
			array(
358
				'meta_data'      => $this->get_meta_data(),
359
				'line_items'     => $this->get_items( 'line_item' ),
360
				'tax_lines'      => $this->get_items( 'tax' ),
361
				'shipping_lines' => $this->get_items( 'shipping' ),
362
				'fee_lines'      => $this->get_items( 'fee' ),
363
				'coupon_lines'   => $this->get_items( 'coupon' ),
364
			)
365
		);
366
	}
367
368
	/**
369
	 * Get order ID.
370
	 * @since 2.7.0
371
	 * @return integer
372
	 */
373
	public function get_id() {
374
		return $this->_data['id'];
375
	}
376
377
	/**
378
	 * Get parent order ID.
379
	 * @since 2.7.0
380
	 * @return integer
381
	 */
382
	public function get_parent_id() {
383
		return $this->_data['parent_id'];
384
	}
385
386
	/**
387
	 * Gets order currency.
388
	 * @return string
389
	 */
390
	public function get_currency() {
391
		return apply_filters( 'woocommerce_get_currency', $this->_data['currency'], $this );
392
	}
393
394
	/**
395
	 * Get order_version
396
	 * @return string
397
	 */
398
	public function get_version() {
399
		return $this->_data['version'];
400
	}
401
402
	/**
403
	 * Get prices_include_tax
404
	 * @return bool
405
	 */
406
	public function get_prices_include_tax() {
407
		return $this->_data['prices_include_tax'];
408
	}
409
410
	/**
411
	 * Get date_created
412
	 * @return int
413
	 */
414
	public function get_date_created() {
415
		return $this->_data['date_created'];
416
	}
417
418
	/**
419
	 * Get date_modified
420
	 * @return int
421
	 */
422
	public function get_date_modified() {
423
		return $this->_data['date_modified'];
424
	}
425
426
	/**
427
	 * Return the order statuses without wc- internal prefix.
428
	 * @return string
429
	 */
430
	public function get_status() {
431
		return apply_filters( 'woocommerce_order_get_status', 'wc-' === substr( $this->_data['status'], 0, 3 ) ? substr( $this->_data['status'], 3 ) : $this->_data['status'], $this );
432
	}
433
434
	/**
435
	 * Get discount_total
436
	 * @param bool $raw Gets raw unfiltered value.
437
	 * @return string
438
	 */
439
	public function get_discount_total( $raw = false ) {
440
		$value = wc_format_decimal( $this->_data['discount_total'] );
441
		return $raw ? $value : apply_filters( 'woocommerce_order_amount_discount_total', $value, $this );
442
	}
443
444
	/**
445
	 * Get discount_tax
446
	 * @param bool $raw Gets raw unfiltered value.
447
	 * @return string
448
	 */
449
	public function get_discount_tax( $raw = false ) {
450
		$value = wc_format_decimal( $this->_data['discount_tax'] );
451
		return $raw ? $value : apply_filters( 'woocommerce_order_amount_discount_tax', $value, $this );
452
	}
453
454
	/**
455
	 * Get shipping_total
456
	 * @param bool $raw Gets raw unfiltered value.
457
	 * @return string
458
	 */
459
	public function get_shipping_total( $raw = false ) {
460
		$value = wc_format_decimal( $this->_data['shipping_total'] );
461
		return $raw ? $value : apply_filters( 'woocommerce_order_amount_shipping_total', $value, $this );
462
	}
463
464
	/**
465
	 * Get shipping_tax.
466
	 * @param bool $raw Gets raw unfiltered value.
467
	 * @return string
468
	 */
469
	public function get_shipping_tax( $raw = false ) {
470
		$value = wc_format_decimal( $this->_data['shipping_tax'] );
471
		return $raw ? $value : apply_filters( 'woocommerce_order_amount_shipping_tax', $value, $this );
472
	}
473
474
	/**
475
	 * Gets cart tax amount.
476
	 * @param bool $raw Gets raw unfiltered value.
477
	 * @return float
478
	 */
479
	public function get_cart_tax( $raw = false ) {
480
		$value = wc_format_decimal( $this->_data['cart_tax'] );
481
		return $raw ? $value : apply_filters( 'woocommerce_order_amount_cart_tax', $value, $this );
482
	}
483
484
	/**
485
	 * Gets order grand total. incl. taxes. Used in gateways. Filtered.
486
	 * @param bool $raw Gets raw unfiltered value.
487
	 * @return float
488
	 */
489
	public function get_total( $raw = false ) {
490
		$value = wc_format_decimal( $this->_data['total'], wc_get_price_decimals() );
491
		return $raw ? $value : apply_filters( 'woocommerce_order_amount_total', $value, $this );
492
	}
493
494
	/**
495
	 * Get total tax amount. Alias for get_order_tax().
496
	 *
497
	 * @since 2.7.0 woocommerce_order_amount_total_tax filter has been removed to avoid
498
	 * these values being modified and then saved back to the DB. There are
499
	 * other, later hooks available to change totals on display. e.g.
500
	 * woocommerce_get_order_item_totals.
501
	 * @param bool $raw Gets raw unfiltered value.
502
	 * @return float
503
	 */
504
	public function get_total_tax( $raw = false ) {
505
		$value = wc_format_decimal( $this->_data['total_tax'] );
506
		return $raw ? $value : apply_filters( 'woocommerce_order_amount_total_tax', $value, $this );
507
	}
508
509
	/**
510
	 * Gets the total discount amount.
511
	 * @param  bool $ex_tax Show discount excl any tax.
512
	 * @return float
513
	 */
514
	public function get_total_discount( $ex_tax = true ) {
515
		if ( $ex_tax ) {
516
			$total_discount = $this->get_discount_total();
517
		} else {
518
			$total_discount = $this->get_discount_total() + $this->get_discount_tax();
519
		}
520
		return apply_filters( 'woocommerce_order_amount_total_discount', round( $total_discount, WC_ROUNDING_PRECISION ), $this );
521
	}
522
523
	/**
524
	 * Gets order subtotal.
525
	 * @return float
526
	 */
527
	public function get_subtotal() {
528
		$subtotal = 0;
529
530
		foreach ( $this->get_items() as $item ) {
531
			$subtotal += $item->get_subtotal();
532
		}
533
534
		return apply_filters( 'woocommerce_order_amount_subtotal', (double) $subtotal, $this );
535
	}
536
537
	/**
538
	 * Get taxes, merged by code, formatted ready for output.
539
	 *
540
	 * @return array
541
	 */
542
	public function get_tax_totals() {
543
		$tax_totals = array();
544
545
		foreach ( $this->get_items( 'tax' ) as $key => $tax ) {
546
			$code = $tax->get_rate_code();
547
548 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...
549
				$tax_totals[ $code ] = new stdClass();
550
				$tax_totals[ $code ]->amount = 0;
551
			}
552
553
			$tax_totals[ $code ]->id                = $key;
554
			$tax_totals[ $code ]->rate_id           = $tax->get_rate_id();
555
			$tax_totals[ $code ]->is_compound       = $tax->is_compound();
556
			$tax_totals[ $code ]->label             = $tax->get_label();
557
			$tax_totals[ $code ]->amount           += $tax->get_tax_total() + $tax->get_shipping_tax_total();
558
			$tax_totals[ $code ]->formatted_amount  = wc_price( wc_round_tax_total( $tax_totals[ $code ]->amount ), array( 'currency' => $this->get_currency() ) );
559
		}
560
561 View Code Duplication
		if ( apply_filters( 'woocommerce_order_hide_zero_taxes', true ) ) {
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...
562
			$amounts    = array_filter( wp_list_pluck( $tax_totals, 'amount' ) );
563
			$tax_totals = array_intersect_key( $tax_totals, $amounts );
564
		}
565
566
		return apply_filters( 'woocommerce_order_tax_totals', $tax_totals, $this );
567
	}
568
569
	/*
570
	|--------------------------------------------------------------------------
571
	| Setters
572
	|--------------------------------------------------------------------------
573
	|
574
	| Functions for setting order data. These should not update anything in the
575
	| database itself and should only change what is stored in the class
576
	| object. However, for backwards compatibility pre 2.7.0 some of these
577
	| setters may handle both.
578
	|
579
	*/
580
581
	/**
582
	 * Set order ID.
583
	 * @since 2.7.0
584
	 * @param int $value
585
	 */
586
	public function set_id( $value ) {
587
		$this->_data['id'] = absint( $value );
588
	}
589
590
	/**
591
	 * Set parent order ID.
592
	 * @since 2.7.0
593
	 * @param int $value
594
	 */
595
	public function set_parent_id( $value ) {
596
		$this->_data['parent_id'] = absint( $value );
597
	}
598
599
	/**
600
	 * Set order status.
601
	 * @since 2.7.0
602
	 * @param string $new_status Status to change the order to. No internal wc- prefix is required.
603
	 * @param array details of change
604
	 */
605
	 public function set_status( $new_status ) {
606
		$old_status = $this->get_status();
607
		$new_status = 'wc-' === substr( $new_status, 0, 3 ) ? substr( $new_status, 3 ) : $new_status;
608
609
		// Only allow valid new status
610
		if ( ! in_array( 'wc-' . $new_status, array_keys( wc_get_order_statuses() ) ) ) {
611
			$new_status = 'pending';
612
		}
613
614
		$this->_data['status'] = 'wc-' . $new_status;
615
616
		// If the old status is set but unknown (e.g. draft) assume its pending for action usage.
617
		if ( $old_status && ! in_array( 'wc-' . $old_status, array_keys( wc_get_order_statuses() ) ) ) {
618
			$old_status = 'pending';
619
		}
620
621
		return array(
622
			'from' => $old_status,
623
			'to'   => $new_status
624
		);
625
	 }
626
627
	/**
628
	 * Set order_version
629
	 * @param string $value
630
	 */
631
	public function set_version( $value ) {
632
		$this->_data['version'] = $value;
633
	}
634
635
	/**
636
	 * Set order_currency
637
	 * @param string $value
638
	 */
639
	public function set_currency( $value ) {
640
		if ( $value && ! in_array( $value, array_keys( get_woocommerce_currencies() ) ) ) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
641
			//$this->throw_exception( 'invalid_currency', 'Invalid currency code' );
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
642
		}
643
		$this->_data['currency'] = $value;
644
	}
645
646
	/**
647
	 * Set prices_include_tax
648
	 * @param bool $value
649
	 */
650
	public function set_prices_include_tax( $value ) {
651
		$this->_data['prices_include_tax'] = (bool) $value;
652
	}
653
654
	/**
655
	 * Set date_created
656
	 * @param string $timestamp Timestamp
657
	 */
658
	public function set_date_created( $timestamp ) {
659
		$this->_data['date_created'] = is_numeric( $timestamp ) ? $timestamp : strtotime( $timestamp );
660
	}
661
662
	/**
663
	 * Set date_modified
664
	 * @param string $timestamp
665
	 */
666
	public function set_date_modified( $timestamp ) {
667
		$this->_data['date_modified'] = is_numeric( $timestamp ) ? $timestamp : strtotime( $timestamp );
668
	}
669
670
	/**
671
	 * Set discount_total
672
	 * @param string $value
673
	 */
674
	public function set_discount_total( $value ) {
675
		$this->_data['discount_total'] = wc_format_decimal( $value );
676
	}
677
678
	/**
679
	 * Set discount_tax
680
	 * @param string $value
681
	 */
682
	public function set_discount_tax( $value ) {
683
		$this->_data['discount_tax'] = wc_format_decimal( $value );
684
	}
685
686
	/**
687
	 * Set shipping_total
688
	 * @param string $value
689
	 */
690
	public function set_shipping_total( $value ) {
691
		$this->_data['shipping_total'] = wc_format_decimal( $value );
692
	}
693
694
	/**
695
	 * Set shipping_tax
696
	 * @param string $value
697
	 */
698
	public function set_shipping_tax( $value ) {
699
		$this->_data['shipping_tax'] = wc_format_decimal( $value );
700
		$this->set_total_tax( $this->get_cart_tax() + $this->get_shipping_tax() );
701
	}
702
703
	/**
704
	 * Set cart tax
705
	 * @param string $value
706
	 */
707
	public function set_cart_tax( $value ) {
708
		$this->_data['cart_tax'] = wc_format_decimal( $value );
709
		$this->set_total_tax( $this->get_cart_tax() + $this->get_shipping_tax() );
710
	}
711
712
	/**
713
	 * Sets order tax (sum of cart and shipping tax). Used internaly only.
714
	 * @param string $value
715
	 */
716
	protected function set_total_tax( $value ) {
717
		$this->_data['total_tax'] = wc_format_decimal( $value );
718
	}
719
720
	/**
721
	 * Set total
722
	 * @param string $value
723
	 * @param string $deprecated Function used to set different totals based on this.
724
	 */
725
	public function set_total( $value, $deprecated = '' ) {
726
		if ( $deprecated ) {
727
			_deprecated_argument( 'total_type', '2.7', 'Use dedicated total setter methods instead.' );
728
			return $this->legacy_set_total( $value, $deprecated );
729
		}
730
		$this->_data['total'] = wc_format_decimal( $value, wc_get_price_decimals() );
731
	}
732
733
	/*
734
	|--------------------------------------------------------------------------
735
	| Order Item Handling
736
	|--------------------------------------------------------------------------
737
	|
738
	| Order items are used for products, taxes, shipping, and fees within
739
	| each order.
740
	|
741
	*/
742
743
	/**
744
	 * Remove all line items (products, coupons, shipping, taxes) from the order.
745
	 * @param string $type Order item type. Default null.
746
	 */
747
	public function remove_order_items( $type = null ) {
748
		global $wpdb;
749
		if ( ! empty( $type ) ) {
750
			$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 ) );
751
			$wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}woocommerce_order_items WHERE order_id = %d AND order_item_type = %s", $this->get_id(), $type ) );
752
			if ( $group = $this->type_to_group( $type ) ) {
753
				$this->_items[ $group ] = null;
754
			}
755
		} else {
756
			$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() ) );
757
			$wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}woocommerce_order_items WHERE order_id = %d", $this->get_id() ) );
758
			$this->_items = array(
759
				'line_items'     => null,
760
				'coupon_lines'   => null,
761
				'shipping_lines' => null,
762
				'fee_lines'      => null,
763
				'tax_lines'      => null,
764
			);
765
		}
766
	}
767
768
	/**
769
	 * Convert a type to a types group.
770
	 * @param string $type
771
	 * @return string group
772
	 */
773
	protected function type_to_group( $type ) {
774
		$type_to_group = array(
775
			'line_item' => 'line_items',
776
			'tax'       => 'tax_lines',
777
			'shipping'  => 'shipping_lines',
778
			'fee'       => 'fee_lines',
779
			'coupon'    => 'coupon_lines',
780
		);
781
		return isset( $type_to_group[ $type ] ) ? $type_to_group[ $type ] : '';
782
	}
783
784
	/**
785
	 * Return an array of items/products within this order.
786
	 * @param string|array $types Types of line items to get (array or string).
787
	 * @return Array of WC_Order_item
788
	 */
789
	public function get_items( $types = 'line_item' ) {
790
		$items = array();
791
		$types = array_filter( (array) $types );
792
793
		foreach ( $types as $type ) {
794
			if ( $group = $this->type_to_group( $type ) ) {
795
				if ( is_null( $this->_items[ $group ] ) ) {
796
					$this->_items[ $group ] = $this->get_items_from_db( $type );
797
				}
798
				// Don't use array_merge here because keys are numeric
799
				$items = $items + $this->_items[ $group ];
800
			}
801
		}
802
803
		return apply_filters( 'woocommerce_order_get_items', $items, $this );
804
	}
805
806
	/**
807
	 * Gets items from the database by type.
808
	 * @param  string $type
809
	 * @return array
810
	 */
811
	protected function get_items_from_db( $type ) {
812
		global $wpdb;
813
814
		$get_items_sql = $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}woocommerce_order_items WHERE order_id = %d AND order_item_type = %s ORDER BY order_item_id;", $this->get_id(), $type ) ;
815
		$items         = $wpdb->get_results( $get_items_sql );
816
817
		if ( ! empty( $items ) ) {
818
			$items = array_map( array( $this, 'get_item' ), array_combine( wp_list_pluck( $items, 'order_item_id' ), $items ) );
819
		} else {
820
			$items = array();
821
		}
822
823
		return $items;
824
	}
825
826
	/**
827
	 * Return an array of fees within this order.
828
	 * @return array
829
	 */
830
	public function get_fees() {
831
		return $this->get_items( 'fee' );
832
	}
833
834
	/**
835
	 * Return an array of taxes within this order.
836
	 * @return array
837
	 */
838
	public function get_taxes() {
839
		return $this->get_items( 'tax' );
840
	}
841
842
	/**
843
	 * Return an array of shipping costs within this order.
844
	 * @return array
845
	 */
846
	public function get_shipping_methods() {
847
		return $this->get_items( 'shipping' );
848
	}
849
850
	/**
851
	 * Gets formatted shipping method title.
852
	 * @return string
853
	 */
854
	public function get_shipping_method() {
855
		$names = array();
856
		foreach ( $this->get_shipping_methods() as $shipping_method ) {
857
			$names[] = $shipping_method->get_name();
858
		}
859
		return apply_filters( 'woocommerce_order_shipping_method', implode( ', ', $names ), $this );
860
	}
861
862
	/**
863
	 * Get coupon codes only.
864
	 * @return array
865
	 */
866
	public function get_used_coupons() {
867
		$coupon_codes = array();
868
		if ( $coupons = $this->get_items( 'coupon' ) ) {
869
			foreach ( $coupons as $coupon ) {
870
				$coupon_codes[] = $coupon->get_code();
871
			}
872
		}
873
		return $coupon_codes;
874
	}
875
876
	/**
877
	 * Gets the count of order items of a certain type.
878
	 *
879
	 * @param string $item_type
880
	 * @return string
881
	 */
882
	public function get_item_count( $item_type = '' ) {
883
		$items = $this->get_items( empty( $item_type ) ? 'line_item' : $item_type );
884
		$count = 0;
885
886
		foreach ( $items as $item ) {
887
			$count += $item->get_quantity();
888
		}
889
890
		return apply_filters( 'woocommerce_get_item_count', $count, $item_type, $this );
891
	}
892
893
	/**
894
	 * Get an order item object, based on it's type.
895
	 * @since  2.7.0
896
	 * @param  int $item_id
897
	 * @return WC_Order_Item
898
	 */
899
	public function get_item( $item_id ) {
900
		return WC_Order_Factory::get_order_item( $item_id );
901
	}
902
903
	/**
904
	 * Get key for where a certain item type is stored in _items.
905
	 * @since  2.7.0
906
	 * @param  $item object Order item (product, shipping, fee, coupon, tax)
907
	 * @return string
908
	 */
909
	protected function get_items_key( $item ) {
910
		if ( is_a( $item, 'WC_Order_Item_Product' ) ) {
911
			return 'line_items';
912
		} elseif ( is_a( $item, 'WC_Order_Item_Fee' ) ) {
913
			return 'fee_lines';
914
		} elseif ( is_a( $item, 'WC_Order_Item_Shipping' ) ) {
915
			return 'shipping_lines';
916
		} elseif ( is_a( $item, 'WC_Order_Item_Tax' ) ) {
917
			return 'tax_lines';
918
		} elseif ( is_a( $item, 'WC_Order_Item_Coupon' ) ) {
919
			return 'coupon_lines';
920
		} else {
921
			return '';
922
		}
923
	}
924
925
	/**
926
	 * Remove item from the order.
927
	 * @param int $item_id
928
	 */
929
	public function remove_item( $item_id ) {
930
		$item = $this->get_item( $item_id );
931
932
		if ( ! $item || ! ( $items_key = $this->get_items_key( $item ) ) ) {
933
			return false;
934
		}
935
936
		// Unset and remove later
937
		$this->_items_to_delete[] = $item;
938
		unset( $this->_items[ $items_key ][ $item->get_id() ] );
939
	}
940
941
	/**
942
	 * Adds an order item to this order. The order item will not persist until save.
943
	 * @param object Order item (product, shipping, fee, coupon, tax)
944
	 */
945
	public function add_item( $item ) {
946
		if ( ! $items_key = $this->get_items_key( $item ) ) {
947
			return false;
948
		}
949
950
		// Make sure existing items are loaded so we can append this new one.
951
		if ( is_null( $this->_items[ $items_key ] ) ) {
952
			$this->_items[ $items_key ] = $this->get_items( $item->get_type() );
953
		}
954
955
		// Append new row with generated temporary ID
956
		if ( $item->get_id() ) {
957
			$this->_items[ $items_key ][ $item->get_id() ] = $item;
958
		} else {
959
			$this->_items[ $items_key ][ 'new:' . md5( json_encode( $item ) ) ] = $item;
960
		}
961
	}
962
963
	/**
964
	 * Add a product line item to the order.
965
	 * @param  \WC_Product $product
966
	 * @param  int $qty
967
	 * @param  array $args
968
	 * @return int order item ID
969
	 */
970
	public function add_product( $product, $qty = 1, $args = array() ) {
971
		$args = wp_parse_args( $args, array(
972
			'quantity'     => $qty,
973
			'name'         => $product ? $product->get_title() : '',
974
			'tax_class'    => $product ? $product->get_tax_class() : '',
975
			'product_id'   => $product ? $product->get_id() : '',
976
			'variation_id' => $product && isset( $product->variation_id ) ? $product->variation_id : 0,
977
			'variation'    => $product && isset( $product->variation_id ) ? $product->get_variation_attributes() : array(),
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class WC_Product as the method get_variation_attributes() does only exist in the following sub-classes of WC_Product: WC_Product_Variable, WC_Product_Variation. 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...
978
			'subtotal'     => $product ? $product->get_price_excluding_tax( $qty ) : '',
979
			'total'        => $product ? $product->get_price_excluding_tax( $qty ) : '',
980
			'subtotal_tax' => 0,
981
			'total_tax'    => 0,
982
			'taxes'        => array(
983
				'subtotal' => array(),
984
				'total'    => array(),
985
			),
986
		) );
987
988
		// BW compatibility with old args
989 View Code Duplication
		if ( isset( $args['totals'] ) ) {
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...
990
			foreach ( $args['totals'] as $key => $value ) {
991
				if ( 'tax' === $key ) {
992
					$args['total_tax'] = $value;
993
				} elseif ( 'tax_data' === $key ) {
994
					$args['taxes'] = $value;
995
				} else {
996
					$args[ $key ] = $value;
997
				}
998
			}
999
		}
1000
1001
		$item = new WC_Order_Item_Product( $args );
1002
		$item->set_backorder_meta();
1003
		$item->set_order_id( $this->get_id() );
1004
		$item->save();
1005
		$this->add_item( $item );
1006
		wc_do_deprecated_action( 'woocommerce_order_add_product', array( $this->get_id(), $item->get_id(), $product, $qty, $args ), '2.7', 'Use woocommerce_new_order_item action instead.' );
1007
		return $item->get_id();
1008
	}
1009
1010
	/**
1011
	 * Add coupon code to the order.
1012
	 * @param string $code
1013
	 * @param int $discount tax amount.
1014
	 * @param int $discount_tax amount.
1015
	 * @return int order item ID
1016
	 */
1017
	public function add_coupon( $code = array(), $discount = 0, $discount_tax = 0 ) {
1018
		$item = new WC_Order_Item_Coupon( array(
0 ignored issues
show
Documentation introduced by
array('code' => $code, '..._tax' => $discount_tax) is of type array<string,string|arra...scount_tax":"integer"}>, but the function expects a integer.

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...
1019
			'code'         => $code,
1020
			'discount'     => $discount,
1021
			'discount_tax' => $discount_tax,
1022
		) );
1023
		$item->set_order_id( $this->get_id() );
1024
		$item->save();
1025
		$this->add_item( $item );
1026
		wc_do_deprecated_action( 'woocommerce_order_add_coupon', array( $this->get_id(), $item->get_id(), $code, $discount, $discount_tax ), '2.7', 'Use woocommerce_new_order_item action instead.' );
1027
		return $item->get_id();
1028
	}
1029
1030
	/**
1031
	 * Add a tax row to the order.
1032
	 * @param array $args
1033
	 * @param int $deprecated1 2.7.0 tax_rate_id, amount, shipping amount.
1034
	 * @param int $deprecated2 2.7.0 tax_rate_id, amount, shipping amount.
1035
	 * @return int order item ID
1036
	 */
1037
	public function add_tax( $args = array(), $deprecated1 = 0, $deprecated2 = 0 ) {
1038
		if ( ! is_array( $args ) ) {
1039
			_deprecated_argument( 'tax_rate_id', '2.7', 'Pass only an array of args' );
1040
			$args = array(
1041
				'rate_id'            => $args,
1042
				'tax_total'          => $deprecated1,
1043
				'shipping_tax_total' => $deprecated2,
1044
			);
1045
		}
1046
		$args = wp_parse_args( $args, array(
1047
			'rate_id'            => '',
1048
			'tax_total'          => 0,
1049
			'shipping_tax_total' => 0,
1050
			'rate_code'          => isset( $args['rate_id'] ) ? WC_Tax::get_rate_code( $args['rate_id'] ) : '',
1051
			'label'              => isset( $args['rate_id'] ) ? WC_Tax::get_rate_label( $args['rate_id'] ) : '',
1052
			'compound'           => isset( $args['rate_id'] ) ? WC_Tax::is_compound( $args['rate_id'] ) : '',
1053
		) );
1054
		$item = new WC_Order_Item_Tax( $args );
1055
		$item->set_order_id( $this->get_id() );
1056
		$item->save();
1057
		$this->add_item( $item );
1058
		wc_do_deprecated_action( 'woocommerce_order_add_tax', array( $this->get_id(), $item->get_id(), $args['rate_id'], $args['tax_total'], $args['shipping_tax_total'] ), '2.7', 'Use woocommerce_new_order_item action instead.' );
1059
		return $item->get_id();
1060
	}
1061
1062
	/**
1063
	 * Add a shipping row to the order.
1064
	 * @param WC_Shipping_Rate shipping_rate
1065
	 * @return int order item ID
1066
	 */
1067
	public function add_shipping( $shipping_rate ) {
1068
		$item = new WC_Order_Item_Shipping( array(
0 ignored issues
show
Documentation introduced by
array('method_title' => ..._rate->get_meta_data()) is of type array<string,?,{"method_...":"?","meta_data":"?"}>, but the function expects a integer.

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...
1069
			'method_title' => $shipping_rate->label,
1070
			'method_id'    => $shipping_rate->id,
1071
			'total'        => wc_format_decimal( $shipping_rate->cost ),
1072
			'taxes'        => $shipping_rate->taxes,
1073
			'meta_data'    => $shipping_rate->get_meta_data(),
1074
		) );
1075
		$item->set_order_id( $this->get_id() );
1076
		$item->save();
1077
		$this->add_item( $item );
1078
		wc_do_deprecated_action( 'woocommerce_order_add_shipping', array( $this->get_id(), $item->get_id(), $shipping_rate ), '2.7', 'Use woocommerce_new_order_item action instead.' );
1079
		return $item->get_id();
1080
	}
1081
1082
	/**
1083
	 * Add a fee to the order.
1084
	 * Order must be saved prior to adding items.
1085
	 * @param object $fee
1086
	 * @return int updated order item ID
1087
	 */
1088
	public function add_fee( $fee ) {
1089
		$item = new WC_Order_Item_Fee( array(
0 ignored issues
show
Documentation introduced by
array('name' => $fee->na...al' => $fee->tax_data)) is of type array<string,?,{"name":"...?,{\"total\":\"?\"}>"}>, but the function expects a integer.

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...
1090
			'name'      => $fee->name,
1091
			'tax_class' => $fee->taxable ? $fee->tax_class : 0,
1092
			'total'     => $fee->amount,
1093
			'total_tax' => $fee->tax,
1094
			'taxes'     => array(
1095
				'total' => $fee->tax_data,
1096
			),
1097
		) );
1098
		$item->set_order_id( $this->get_id() );
1099
		$item->save();
1100
		$this->add_item( $item );
1101
		wc_do_deprecated_action( 'woocommerce_order_add_fee', array( $this->get_id(), $item->get_id(), $fee ), '2.7', 'Use woocommerce_new_order_item action instead.' );
1102
		return $item->get_id();
1103
	}
1104
1105
	/*
1106
	|--------------------------------------------------------------------------
1107
	| Payment Token Handling
1108
	|--------------------------------------------------------------------------
1109
	|
1110
	| Payment tokens are hashes used to take payments by certain gateways.
1111
	|
1112
	*/
1113
1114
	/**
1115
	 * Add a payment token to an order
1116
	 *
1117
	 * @since 2.6
1118
	 * @param  WC_Payment_Token   $token     Payment token object
1119
	 * @return boolean|int The new token ID or false if it failed.
1120
	 */
1121
	public function add_payment_token( $token ) {
1122
		if ( empty( $token ) || ! ( $token instanceof WC_Payment_Token ) ) {
1123
			return false;
1124
		}
1125
1126
		$token_ids = get_post_meta( $this->get_id(), '_payment_tokens', true );
1127
1128
		if ( empty ( $token_ids ) ) {
1129
			$token_ids = array();
1130
		}
1131
1132
		$token_ids[] = $token->get_id();
1133
1134
		update_post_meta( $this->get_id(), '_payment_tokens', $token_ids );
1135
		do_action( 'woocommerce_payment_token_added_to_order', $this->get_id(), $token->get_id(), $token, $token_ids );
1136
		return $token->get_id();
1137
	}
1138
1139
	/**
1140
	 * Returns a list of all payment tokens associated with the current order
1141
	 *
1142
	 * @since 2.6
1143
	 * @return array An array of payment token objects
1144
	 */
1145
	public function get_payment_tokens() {
1146
		return WC_Payment_Tokens::get_order_tokens( $this->get_id() );
1147
	}
1148
1149
	/*
1150
	|--------------------------------------------------------------------------
1151
	| Calculations.
1152
	|--------------------------------------------------------------------------
1153
	|
1154
	| These methods calculate order totals and taxes based on the current data.
1155
	|
1156
	*/
1157
1158
	/**
1159
	 * Calculate shipping total.
1160
	 *
1161
	 * @since 2.2
1162
	 * @return float
1163
	 */
1164
	public function calculate_shipping() {
1165
		$shipping_total = 0;
1166
1167
		foreach ( $this->get_shipping_methods() as $shipping ) {
1168
			$shipping_total += $shipping->get_total();
1169
		}
1170
1171
		$this->set_shipping_total( $shipping_total );
1172
		$this->save();
1173
1174
		return $this->get_shipping_total();
1175
	}
1176
1177
	/**
1178
	 * Get all tax classes for items in the order.
1179
	 *
1180
	 * @since 2.6.3
1181
	 * @return array
1182
	 */
1183
	public function get_items_tax_classes() {
1184
		$found_tax_classes = array();
1185
1186
		foreach ( $this->get_items() as $item ) {
1187
			if ( $_product = $this->get_product_from_item( $item ) ) {
0 ignored issues
show
Deprecated Code introduced by
The method WC_Abstract_Legacy_Order::get_product_from_item() has been deprecated with message: Add deprecation notices in future release. Replaced with $item->get_product()

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...
1188
				$found_tax_classes[] = $_product->get_tax_class();
0 ignored issues
show
Bug introduced by
The method get_tax_class cannot be called on $_product (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
1189
			}
1190
		}
1191
1192
		return array_unique( $found_tax_classes );
1193
	}
1194
1195
	/**
1196
	 * Calculate taxes for all line items and shipping, and store the totals and tax rows.
1197
	 *
1198
	 * Will use the base country unless customer addresses are set.
1199
	 * @param $args array Added in 2.7.0 to pass things like location.
1200
	 */
1201
	public function calculate_taxes( $args = array() ) {
1202
		$tax_based_on      = get_option( 'woocommerce_tax_based_on' );
1203
		$args              = wp_parse_args( $args, array(
1204
			'country'  => 'billing' === $tax_based_on ? $this->get_billing_country()  : $this->get_shipping_country(),
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_billing_country() 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...
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_shipping_country() 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...
1205
			'state'    => 'billing' === $tax_based_on ? $this->get_billing_state()    : $this->get_shipping_state(),
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_billing_state() 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...
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_shipping_state() 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...
1206
			'postcode' => 'billing' === $tax_based_on ? $this->get_billing_postcode() : $this->get_shipping_postcode(),
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_billing_postcode() 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...
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_shipping_postcode() 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...
1207
			'city'     => 'billing' === $tax_based_on ? $this->get_billing_city()     : $this->get_shipping_city(),
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_billing_city() 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...
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_shipping_city() 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...
1208
		) );
1209
1210
		// Default to base
1211
		if ( 'base' === $tax_based_on || empty( $args['country'] ) ) {
1212
			$default          = wc_get_base_location();
1213
			$args['country']  = $default['country'];
1214
			$args['state']    = $default['state'];
1215
			$args['postcode'] = '';
1216
			$args['city']     = '';
1217
		}
1218
1219
		// Calc taxes for line items
1220
		foreach ( $this->get_items( array( 'line_item', 'fee' ) ) as $item_id => $item ) {
1221
			$tax_class           = $item->get_tax_class();
1222
			$tax_status          = $item->get_tax_status();
1223
1224
			if ( '0' !== $tax_class && 'taxable' === $tax_status ) {
1225
				$tax_rates = WC_Tax::find_rates( array(
1226
					'country'   => $args['country'],
1227
					'state'     => $args['state'],
1228
					'postcode'  => $args['postcode'],
1229
					'city'      => $args['city'],
1230
					'tax_class' => $tax_class,
1231
				) );
1232
1233
				$total = $item->get_total();
1234
				$taxes = WC_Tax::calc_tax( $total, $tax_rates, false );
1235
1236
				if ( $item->is_type( 'line_item' ) ) {
1237
					$subtotal       = $item->get_subtotal();
1238
					$subtotal_taxes = WC_Tax::calc_tax( $subtotal, $tax_rates, false );
1239
					$subtotal_tax   = max( 0, array_sum( $subtotal_taxes ) );
1240
					$item->set_subtotal_tax( $subtotal_tax );
1241
					$item->set_taxes( array( 'total' => $taxes, 'subtotal' => $subtotal_taxes ) );
1242
				} else {
1243
					$item->set_taxes( array( 'total' => $taxes ) );
1244
				}
1245
				$item->save();
1246
			}
1247
		}
1248
1249
		// Calc taxes for shipping
1250
		foreach ( $this->get_shipping_methods() as $item_id => $item ) {
1251
			$shipping_tax_class = get_option( 'woocommerce_shipping_tax_class' );
1252
1253
			// Inherit tax class from items
1254
			if ( '' === $shipping_tax_class ) {
1255
				$tax_rates         = array();
1256
				$tax_classes       = array_merge( array( '' ), WC_Tax::get_tax_classes() );
1257
				$found_tax_classes = $this->get_items_tax_classes();
1258
1259
				foreach ( $tax_classes as $tax_class ) {
1260
					$tax_class = sanitize_title( $tax_class );
1261
					if ( in_array( $tax_class, $found_tax_classes ) ) {
1262
						$tax_rates = WC_Tax::find_shipping_rates( array(
1263
							'country'   => $args['country'],
1264
							'state'     => $args['state'],
1265
							'postcode'  => $args['postcode'],
1266
							'city'      => $args['city'],
1267
							'tax_class' => $tax_class,
1268
						) );
1269
						break;
1270
					}
1271
				}
1272
			} else {
1273
				$tax_rates = WC_Tax::find_shipping_rates( array(
1274
					'country'   => $args['country'],
1275
					'state'     => $args['state'],
1276
					'postcode'  => $args['postcode'],
1277
					'city'      => $args['city'],
1278
					'tax_class' => 'standard' === $shipping_tax_class ? '' : $shipping_tax_class,
1279
				) );
1280
			}
1281
			$item->set_taxes( array( 'total' => WC_Tax::calc_tax( $item->get_total(), $tax_rates, false ) ) );
1282
			$item->save();
1283
		}
1284
		$this->update_taxes();
1285
	}
1286
1287
	/**
1288
	 * Update tax lines for the order based on the line item taxes themselves.
1289
	 */
1290
	public function update_taxes() {
1291
		$cart_taxes     = array();
1292
		$shipping_taxes = array();
1293
1294
		foreach ( $this->get_items( array( 'line_item', 'fee' ) ) as $item_id => $item ) {
1295
			$taxes = $item->get_taxes();
1296 View Code Duplication
			foreach ( $taxes['total'] as $tax_rate_id => $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...
1297
				$cart_taxes[ $tax_rate_id ] = isset( $cart_taxes[ $tax_rate_id ] ) ? $cart_taxes[ $tax_rate_id ] + $tax : $tax;
1298
			}
1299
		}
1300
1301
		foreach ( $this->get_shipping_methods() as $item_id => $item ) {
1302
			$taxes = $item->get_taxes();
1303 View Code Duplication
			foreach ( $taxes['total'] as $tax_rate_id => $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...
1304
				$shipping_taxes[ $tax_rate_id ] = isset( $shipping_taxes[ $tax_rate_id ] ) ? $shipping_taxes[ $tax_rate_id ] + $tax : $tax;
1305
			}
1306
		}
1307
1308
		// Remove old existing tax rows.
1309
		$this->remove_order_items( 'tax' );
1310
1311
		// Now merge to keep tax rows.
1312
		foreach ( array_keys( $cart_taxes + $shipping_taxes ) as $tax_rate_id ) {
1313
			$this->add_tax( array(
1314
				'rate_id'            => $tax_rate_id,
1315
				'tax_total'          => isset( $cart_taxes[ $tax_rate_id ] ) ? $cart_taxes[ $tax_rate_id ] : 0,
1316
				'shipping_tax_total' => isset( $shipping_taxes[ $tax_rate_id ] ) ? $shipping_taxes[ $tax_rate_id ] : 0,
1317
			) );
1318
		}
1319
1320
		// Save tax totals
1321
		$this->set_shipping_tax( WC_Tax::round( array_sum( $shipping_taxes ) ) );
1322
		$this->set_cart_tax( WC_Tax::round( array_sum( $cart_taxes ) ) );
1323
		$this->save();
1324
	}
1325
1326
	/**
1327
	 * Calculate totals by looking at the contents of the order. Stores the totals and returns the orders final total.
1328
	 *
1329
	 * @since 2.2
1330
	 * @param  bool $and_taxes Calc taxes if true.
1331
	 * @return float calculated grand total.
1332
	 */
1333
	public function calculate_totals( $and_taxes = true ) {
1334
		$cart_subtotal     = 0;
1335
		$cart_total        = 0;
1336
		$fee_total         = 0;
1337
		$cart_subtotal_tax = 0;
1338
		$cart_total_tax    = 0;
1339
1340
		if ( $and_taxes && wc_tax_enabled() ) {
1341
			$this->calculate_taxes();
1342
		}
1343
1344
		// line items
1345
		foreach ( $this->get_items() as $item ) {
1346
			$cart_subtotal     += $item->get_subtotal();
1347
			$cart_total        += $item->get_total();
1348
			$cart_subtotal_tax += $item->get_subtotal_tax();
1349
			$cart_total_tax    += $item->get_total_tax();
1350
		}
1351
1352
		$this->calculate_shipping();
1353
1354
		foreach ( $this->get_fees() as $item ) {
1355
			$fee_total += $item->get_total();
1356
		}
1357
1358
		$grand_total = round( $cart_total + $fee_total + $this->get_shipping_total() + $this->get_cart_tax() + $this->get_shipping_tax(), wc_get_price_decimals() );
1359
1360
		$this->set_discount_total( $cart_subtotal - $cart_total );
1361
		$this->set_discount_tax( $cart_subtotal_tax - $cart_total_tax );
1362
		$this->set_total( $grand_total );
1363
		$this->save();
1364
1365
		return $grand_total;
1366
	}
1367
1368
	/**
1369
	 * Get item subtotal - this is the cost before discount.
1370
	 *
1371
	 * @param object $item
1372
	 * @param bool $inc_tax (default: false).
1373
	 * @param bool $round (default: true).
1374
	 * @return float
1375
	 */
1376 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...
1377
		$subtotal = 0;
1378
1379
		if ( is_callable( array( $item, 'get_subtotal' ) ) ) {
1380
			if ( $inc_tax ) {
1381
				$subtotal = ( $item->get_subtotal() + $item->get_subtotal_tax() ) / max( 1, $item->get_quantity() );
1382
			} else {
1383
				$subtotal = ( $item->get_subtotal() / max( 1, $item->get_quantity() ) );
1384
			}
1385
1386
			$subtotal = $round ? number_format( (float) $subtotal, wc_get_price_decimals(), '.', '' ) : $subtotal;
1387
		}
1388
1389
		return apply_filters( 'woocommerce_order_amount_item_subtotal', $subtotal, $this, $item, $inc_tax, $round );
1390
	}
1391
1392
	/**
1393
	 * Get line subtotal - this is the cost before discount.
1394
	 *
1395
	 * @param object $item
1396
	 * @param bool $inc_tax (default: false).
1397
	 * @param bool $round (default: true).
1398
	 * @return float
1399
	 */
1400 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...
1401
		$subtotal = 0;
1402
1403
		if ( is_callable( array( $item, 'get_subtotal' ) ) ) {
1404
			if ( $inc_tax ) {
1405
				$subtotal = $item->get_subtotal() + $item->get_subtotal_tax();
1406
			} else {
1407
				$subtotal = $item->get_subtotal();
1408
			}
1409
1410
			$subtotal = $round ? round( $subtotal, wc_get_price_decimals() ) : $subtotal;
1411
		}
1412
1413
		return apply_filters( 'woocommerce_order_amount_line_subtotal', $subtotal, $this, $item, $inc_tax, $round );
1414
	}
1415
1416
	/**
1417
	 * Calculate item cost - useful for gateways.
1418
	 *
1419
	 * @param object $item
1420
	 * @param bool $inc_tax (default: false).
1421
	 * @param bool $round (default: true).
1422
	 * @return float
1423
	 */
1424 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...
1425
		$total = 0;
1426
1427
		if ( is_callable( array( $item, 'get_total' ) ) ) {
1428
			if ( $inc_tax ) {
1429
				$total = ( $item->get_total() + $item->get_total_tax() ) / max( 1, $item->get_quantity() );
1430
			} else {
1431
				$total = $item->get_total() / max( 1, $item->get_quantity() );
1432
			}
1433
1434
			$total = $round ? round( $total, wc_get_price_decimals() ) : $total;
1435
		}
1436
1437
		return apply_filters( 'woocommerce_order_amount_item_total', $total, $this, $item, $inc_tax, $round );
1438
	}
1439
1440
	/**
1441
	 * Calculate line total - useful for gateways.
1442
	 *
1443
	 * @param object $item
1444
	 * @param bool $inc_tax (default: false).
1445
	 * @param bool $round (default: true).
1446
	 * @return float
1447
	 */
1448 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...
1449
		$total = 0;
1450
1451
		if ( is_callable( array( $item, 'get_total' ) ) ) {
1452
			// Check if we need to add line tax to the line total.
1453
			$total = $inc_tax ? $item->get_total() + $item->get_total_tax() : $item->get_total();
1454
1455
			// Check if we need to round.
1456
			$total = $round ? round( $total, wc_get_price_decimals() ) : $total;
1457
		}
1458
1459
		return apply_filters( 'woocommerce_order_amount_line_total', $total, $this, $item, $inc_tax, $round );
1460
	}
1461
1462
	/**
1463
	 * Get item tax - useful for gateways.
1464
	 *
1465
	 * @param mixed $item
1466
	 * @param bool $round (default: true).
1467
	 * @return float
1468
	 */
1469
	public function get_item_tax( $item, $round = true ) {
1470
		$tax = 0;
1471
1472
		if ( is_callable( array( $item, 'get_total_tax' ) ) ) {
1473
			$tax = $item->get_total_tax() / max( 1, $item->get_quantity() );
1474
			$tax = $round ? wc_round_tax_total( $tax ) : $tax;
1475
		}
1476
1477
		return apply_filters( 'woocommerce_order_amount_item_tax', $tax, $item, $round, $this );
1478
	}
1479
1480
	/**
1481
	 * Get line tax - useful for gateways.
1482
	 *
1483
	 * @param mixed $item
1484
	 * @return float
1485
	 */
1486
	public function get_line_tax( $item ) {
1487
		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 );
1488
	}
1489
1490
	/**
1491
	 * Gets line subtotal - formatted for display.
1492
	 *
1493
	 * @param array  $item
1494
	 * @param string $tax_display
1495
	 * @return string
1496
	 */
1497
	public function get_formatted_line_subtotal( $item, $tax_display = '' ) {
1498
		$tax_display = $tax_display ? $tax_display : get_option( 'woocommerce_tax_display_cart' );
1499
1500
		if ( 'excl' == $tax_display ) {
1501
			$ex_tax_label = $this->get_prices_include_tax() ? 1 : 0;
1502
1503
			$subtotal = wc_price( $this->get_line_subtotal( $item ), array( 'ex_tax_label' => $ex_tax_label, 'currency' => $this->get_currency() ) );
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...
1504
		} else {
1505
			$subtotal = wc_price( $this->get_line_subtotal( $item, true ), array('currency' => $this->get_currency()) );
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...
1506
		}
1507
1508
		return apply_filters( 'woocommerce_order_formatted_line_subtotal', $subtotal, $item, $this );
1509
	}
1510
1511
	/**
1512
	 * Gets order total - formatted for display.
1513
	 * @return string
1514
	 */
1515
	public function get_formatted_order_total() {
1516
		$formatted_total = wc_price( $this->get_total(), array( 'currency' => $this->get_currency() ) );
1517
		return apply_filters( 'woocommerce_get_formatted_order_total', $formatted_total, $this );
1518
	}
1519
1520
	/**
1521
	 * Gets subtotal - subtotal is shown before discounts, but with localised taxes.
1522
	 *
1523
	 * @param bool $compound (default: false).
1524
	 * @param string $tax_display (default: the tax_display_cart value).
1525
	 * @return string
1526
	 */
1527
	public function get_subtotal_to_display( $compound = false, $tax_display = '' ) {
1528
		$tax_display = $tax_display ? $tax_display : get_option( 'woocommerce_tax_display_cart' );
1529
		$subtotal    = 0;
1530
1531
		if ( ! $compound ) {
1532
			foreach ( $this->get_items() as $item ) {
1533
				$subtotal += $item->get_subtotal();
1534
1535
				if ( 'incl' === $tax_display ) {
1536
					$subtotal += $item->get_subtotal_tax();
1537
				}
1538
			}
1539
1540
			$subtotal = wc_price( $subtotal, array( 'currency' => $this->get_currency() ) );
1541
1542
			if ( 'excl' === $tax_display && $this->get_prices_include_tax() ) {
1543
				$subtotal .= ' <small class="tax_label">' . WC()->countries->ex_tax_or_vat() . '</small>';
1544
			}
1545
1546
		} else {
1547
			if ( 'incl' === $tax_display ) {
1548
				return '';
1549
			}
1550
1551
			foreach ( $this->get_items() as $item ) {
1552
				$subtotal += $item->get_subtotal();
1553
			}
1554
1555
			// Add Shipping Costs.
1556
			$subtotal += $this->get_shipping_total();
1557
1558
			// Remove non-compound taxes.
1559
			foreach ( $this->get_taxes() as $tax ) {
1560
				if ( $this->is_compound() ) {
0 ignored issues
show
Bug introduced by
The method is_compound() does not seem to exist on object<WC_Abstract_Order>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
1561
					continue;
1562
				}
1563
				$subtotal = $subtotal + $tax->get_tax_total() + $tax->get_shipping_tax_total();
1564
			}
1565
1566
			// Remove discounts.
1567
			$subtotal = $subtotal - $this->get_total_discount();
1568
			$subtotal = wc_price( $subtotal, array( 'currency' => $this->get_currency() ) );
1569
		}
1570
1571
		return apply_filters( 'woocommerce_order_subtotal_to_display', $subtotal, $compound, $this );
1572
	}
1573
1574
	/**
1575
	 * Gets shipping (formatted).
1576
	 *
1577
	 * @return string
1578
	 */
1579
	public function get_shipping_to_display( $tax_display = '' ) {
1580
		$tax_display = $tax_display ? $tax_display : get_option( 'woocommerce_tax_display_cart' );
1581
1582
		if ( $this->get_shipping_total() != 0 ) {
1583
1584
			if ( $tax_display == 'excl' ) {
1585
1586
				// Show shipping excluding tax.
1587
				$shipping = wc_price( $this->get_shipping_total(), array('currency' => $this->get_currency()) );
1588
1589 View Code Duplication
				if ( $this->get_shipping_tax() != 0 && $this->get_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...
1590
					$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 );
1591
				}
1592
1593
			} else {
1594
1595
				// Show shipping including tax.
1596
				$shipping = wc_price( $this->get_shipping_total() + $this->get_shipping_tax(), array('currency' => $this->get_currency()) );
1597
1598 View Code Duplication
				if ( $this->get_shipping_tax() != 0 && ! $this->get_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...
1599
					$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 );
1600
				}
1601
1602
			}
1603
1604
			$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 );
1605
1606
		} elseif ( $this->get_shipping_method() ) {
1607
			$shipping = $this->get_shipping_method();
1608
		} else {
1609
			$shipping = __( 'Free!', 'woocommerce' );
1610
		}
1611
1612
		return apply_filters( 'woocommerce_order_shipping_to_display', $shipping, $this );
1613
	}
1614
1615
	/**
1616
	 * Get the discount amount (formatted).
1617
	 * @since  2.3.0
1618
	 * @return string
1619
	 */
1620
	public function get_discount_to_display( $tax_display = '' ) {
1621
		$tax_display = $tax_display ? $tax_display : get_option( 'woocommerce_tax_display_cart' );
1622
		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_currency() ) ), $this );
1623
	}
1624
1625
	/**
1626
	 * Get totals for display on pages and in emails.
1627
	 *
1628
	 * @param mixed $tax_display
1629
	 * @return array
1630
	 */
1631
	public function get_order_item_totals( $tax_display = '' ) {
1632
		$tax_display = $tax_display ? $tax_display : get_option( 'woocommerce_tax_display_cart' );
1633
		$total_rows  = array();
1634
1635
		if ( $subtotal = $this->get_subtotal_to_display( false, $tax_display ) ) {
1636
			$total_rows['cart_subtotal'] = array(
1637
				'label' => __( 'Subtotal:', 'woocommerce' ),
1638
				'value'    => $subtotal,
1639
			);
1640
		}
1641
1642
		if ( $this->get_total_discount() > 0 ) {
1643
			$total_rows['discount'] = array(
1644
				'label' => __( 'Discount:', 'woocommerce' ),
1645
				'value'    => '-' . $this->get_discount_to_display( $tax_display ),
1646
			);
1647
		}
1648
1649
		if ( $this->get_shipping_method() ) {
1650
			$total_rows['shipping'] = array(
1651
				'label' => __( 'Shipping:', 'woocommerce' ),
1652
				'value'    => $this->get_shipping_to_display( $tax_display ),
1653
			);
1654
		}
1655
1656
		if ( $fees = $this->get_fees() ) {
1657
			foreach ( $fees as $id => $fee ) {
1658
				if ( apply_filters( 'woocommerce_get_order_item_totals_excl_free_fees', empty( $fee['line_total'] ) && empty( $fee['line_tax'] ), $id ) ) {
1659
					continue;
1660
				}
1661
				$total_rows[ 'fee_' . $fee->get_id() ] = array(
1662
					'label' => $fee->get_name() . ':',
1663
					'value' => wc_price( 'excl' === $tax_display ? $fee->get_total() : $fee->get_total() + $fee->get_total_tax(), array('currency' => $this->get_currency()) )
1664
				);
1665
			}
1666
		}
1667
1668
		// Tax for tax exclusive prices.
1669
		if ( 'excl' === $tax_display ) {
1670
1671
			if ( get_option( 'woocommerce_tax_total_display' ) == 'itemized' ) {
1672
1673
				foreach ( $this->get_tax_totals() as $code => $tax ) {
1674
1675
					$total_rows[ sanitize_title( $code ) ] = array(
1676
						'label' => $tax->label . ':',
1677
						'value'    => $tax->formatted_amount,
1678
					);
1679
				}
1680
1681
			} else {
1682
1683
				$total_rows['tax'] = array(
1684
					'label' => WC()->countries->tax_or_vat() . ':',
1685
					'value'    => wc_price( $this->get_total_tax(), array( 'currency' => $this->get_currency() ) ),
1686
				);
1687
			}
1688
		}
1689
1690
		if ( $this->get_total() > 0 && $this->get_payment_method_title() ) {
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_payment_method_title() 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...
1691
			$total_rows['payment_method'] = array(
1692
				'label' => __( 'Payment Method:', 'woocommerce' ),
1693
				'value' => $this->get_payment_method_title(),
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_payment_method_title() 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...
1694
			);
1695
		}
1696
1697
		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...
1698
			foreach ( $refunds as $id => $refund ) {
1699
				$total_rows[ 'refund_' . $id ] = array(
1700
					'label' => $refund->get_reason() ? $refund->get_reason() : __( 'Refund', 'woocommerce' ) . ':',
1701
					'value'    => wc_price( '-' . $refund->get_amount(), array( 'currency' => $this->get_currency() ) ),
1702
				);
1703
			}
1704
		}
1705
1706
		$total_rows['order_total'] = array(
1707
			'label' => __( 'Total:', 'woocommerce' ),
1708
			'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...
1709
		);
1710
1711
		return apply_filters( 'woocommerce_get_order_item_totals', $total_rows, $this );
1712
	}
1713
1714
	/*
1715
	|--------------------------------------------------------------------------
1716
	| Conditionals
1717
	|--------------------------------------------------------------------------
1718
	|
1719
	| Checks if a condition is true or false.
1720
	|
1721
	*/
1722
1723
	/**
1724
	 * Checks the order status against a passed in status.
1725
	 *
1726
	 * @return bool
1727
	 */
1728
	public function has_status( $status ) {
1729
		return apply_filters( 'woocommerce_order_has_status', ( is_array( $status ) && in_array( $this->get_status(), $status ) ) || $this->get_status() === $status ? true : false, $this, $status );
1730
	}
1731
1732
	/**
1733
	 * Check whether this order has a specific shipping method or not.
1734
	 *
1735
	 * @param string $method_id
1736
	 * @return bool
1737
	 */
1738
	public function has_shipping_method( $method_id ) {
1739
		foreach ( $this->get_shipping_methods() as $shipping_method ) {
1740
			if ( $shipping_method->get_method_id() === $method_id ) {
1741
				return true;
1742
			}
1743
		}
1744
		return false;
1745
	}
1746
1747
	/**
1748
	 * Returns true if the order contains a free product.
1749
	 * @since 2.5.0
1750
	 * @return bool
1751
	 */
1752
	public function has_free_item() {
1753
		foreach ( $this->get_items() as $item ) {
1754
			if ( ! $item->get_total() ) {
1755
				return true;
1756
			}
1757
		}
1758
		return false;
1759
	}
1760
}
1761