Completed
Pull Request — master (#10259)
by Mike
08:16
created

WC_Product_Variation::__get()   C

Complexity

Conditions 11
Paths 14

Size

Total Lines 34
Code Lines 18

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 34
rs 5.2653
cc 11
eloc 18
nc 14
nop 1

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
1 ignored issue
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 18 and the first side effect is on line 4.

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

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

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

Loading history...
2
3
if ( ! defined( 'ABSPATH' ) ) {
4
	exit; // Exit if accessed directly
5
}
6
7
/**
8
 * Product Variation Class.
9
 *
10
 * The WooCommerce product variation class handles product variation data.
11
 *
12
 * @class       WC_Product_Variation
13
 * @version     2.2.0
14
 * @package     WooCommerce/Classes
15
 * @category    Class
16
 * @author      WooThemes
17
 */
18
class WC_Product_Variation extends WC_Product {
19
20
	/** @public int ID of the variation itself. */
21
	public $variation_id;
22
23
	/** @public object Parent Variable product object. */
24
	public $parent;
25
26
	/** @public string Stores the shipping class of the variation. */
27
	public $variation_shipping_class         = false;
28
29
	/** @public int Stores the shipping class ID of the variation. */
30
	public $variation_shipping_class_id      = false;
31
32
	/** @public unused vars @deprecated in 2.2 */
33
	public $variation_has_sku                = true;
34
	public $variation_has_length             = true;
35
	public $variation_has_width              = true;
36
	public $variation_has_height             = true;
37
	public $variation_has_weight             = true;
38
	public $variation_has_tax_class          = true;
39
	public $variation_has_downloadable_files = true;
40
41
	/** @private array Data which is only at variation level - no inheritance plus their default values if left blank. */
42
	protected $variation_level_meta_data = array(
43
		'downloadable'          => 'no',
44
		'virtual'               => 'no',
45
		'manage_stock'          => 'no',
46
		'sale_price_dates_from' => '',
47
		'sale_price_dates_to'   => '',
48
		'price'                 => '',
49
		'regular_price'         => '',
50
		'sale_price'            => '',
51
		'stock'                 => 0,
52
		'stock_status'          => 'instock',
53
		'downloadable_files'    => array()
54
	);
55
56
	/** @private array Data which can be at variation level, otherwise fallback to parent if not set. */
57
	protected $variation_inherited_meta_data = array(
58
		'tax_class'  => '',
59
		'backorders' => 'no',
60
		'sku'        => '',
61
		'weight'     => '',
62
		'length'     => '',
63
		'width'      => '',
64
		'height'     => ''
65
	);
66
67
	/**
68
	 * Loads required variation data.
69
	 *
70
	 * @param int $variation ID of the variation to load
71
	 * @param array $args Array of the arguments containing parent product data
72
	 */
73
	public function __construct( $variation, $args = array() ) {
74
		if ( is_object( $variation ) ) {
75
			$this->variation_id = absint( $variation->ID );
76
		} else {
77
			$this->variation_id = absint( $variation );
78
		}
79
80
		/* Get main product data from parent (args) */
81
		$this->id = ! empty( $args['parent_id'] ) ? intval( $args['parent_id'] ) : wp_get_post_parent_id( $this->variation_id );
82
83
		// The post doesn't have a parent id, therefore its invalid.
84
		if ( empty( $this->id ) ) {
85
			return;
86
		}
87
88
		$this->product_type = 'variation';
89
		$this->parent       = ! empty( $args['parent'] ) ? $args['parent'] : wc_get_product( $this->id );
90
		$this->post         = ! empty( $this->parent->post ) ? $this->parent->post : array();
91
	}
92
93
	/**
94
	 * __isset function.
95
	 *
96
	 * @param mixed $key
97
	 * @return bool
98
	 */
99
	public function __isset( $key ) {
100
		if ( in_array( $key, array_keys( $this->variation_level_meta_data ) ) ) {
101
			return metadata_exists( 'post', $this->variation_id, '_' . $key );
102
		} elseif ( in_array( $key, array_keys( $this->variation_inherited_meta_data ) ) ) {
103
			return metadata_exists( 'post', $this->variation_id, '_' . $key ) || metadata_exists( 'post', $this->id, '_' . $key );
104
		} else {
105
			return metadata_exists( 'post', $this->id, '_' . $key );
106
		}
107
	}
108
109
	/**
110
	 * Get method returns variation meta data if set, otherwise in most cases the data from the parent.
111
	 *
112
	 * @param string $key
113
	 * @return mixed
114
	 */
115
	public function __get( $key ) {
116
		if ( in_array( $key, array_keys( $this->variation_level_meta_data ) ) ) {
117
118
			$value = get_post_meta( $this->variation_id, '_' . $key, true );
119
120
			if ( '' === $value ) {
121
				$value = $this->variation_level_meta_data[ $key ];
122
			}
123
124
		} elseif ( in_array( $key, array_keys( $this->variation_inherited_meta_data ) ) ) {
125
126
			$value = metadata_exists( 'post', $this->variation_id, '_' . $key ) ? get_post_meta( $this->variation_id, '_' . $key, true ) : get_post_meta( $this->id, '_' . $key, true );
127
128
			// Handle meta data keys which can be empty at variation level to cause inheritance
129
			if ( '' === $value && in_array( $key, array( 'sku', 'weight', 'length', 'width', 'height' ) ) ) {
130
				$value = get_post_meta( $this->id, '_' . $key, true );
131
			}
132
133
			if ( '' === $value ) {
134
				$value = $this->variation_inherited_meta_data[ $key ];
135
			}
136
137
		} elseif ( 'variation_data' === $key ) {
138
			return $this->variation_data = wc_get_product_variation_attributes( $this->variation_id );
139
140
		} elseif ( 'variation_has_stock' === $key ) {
141
			return $this->managing_stock();
142
143
		} else {
144
			$value = metadata_exists( 'post', $this->variation_id, '_' . $key ) ? get_post_meta( $this->variation_id, '_' . $key, true ) : parent::__get( $key );
145
		}
146
147
		return $value;
148
	}
149
150
	/**
151
	 * Return the variation ID
152
	 *
153
	 * @since 2.5.0
154
	 * @return int variation (post) ID
155
	 */
156
	public function get_id() {
157
158
		return $this->variation_id;
159
	}
160
161
	/**
162
	 * Returns whether or not the product post exists.
163
	 *
164
	 * @return bool
165
	 */
166
	public function exists() {
167
		return ! empty( $this->id );
168
	}
169
170
	/**
171
	 * Wrapper for get_permalink. Adds this variations attributes to the URL.
172
	 *
173
	 * @param  $cart item array If the cart item is passed, we can get a link containing the exact attributes selected for the variation, rather than the default attributes.
174
	 * @return string
175
	 */
176
	public function get_permalink( $cart_item = null ) {
177
		return add_query_arg( array_map( 'urlencode', array_filter( isset( $cart_item['variation'] ) ? $cart_item['variation'] : $this->variation_data ) ), get_permalink( $this->id ) );
178
	}
179
180
	/**
181
	 * Get the add to url used mainly in loops.
182
	 *
183
	 * @return string
184
	 */
185
	public function add_to_cart_url() {
186
		$variation_data = array_map( 'urlencode', $this->variation_data );
187
		$url            = $this->is_purchasable() && $this->is_in_stock() ? remove_query_arg( 'added-to-cart', add_query_arg( array_merge( array( 'variation_id' => $this->variation_id, 'add-to-cart' => $this->id ), $variation_data ) ) ) : get_permalink( $this->id );
188
189
		return apply_filters( 'woocommerce_product_add_to_cart_url', $url, $this );
190
	}
191
192
	/**
193
	 * Get the add to cart button text.
194
	 *
195
	 * @return string
196
	 */
197
	public function add_to_cart_text() {
198
		$text = $this->is_purchasable() && $this->is_in_stock() ? __( 'Add to cart', 'woocommerce' ) : __( 'Read More', 'woocommerce' );
199
200
		return apply_filters( 'woocommerce_product_add_to_cart_text', $text, $this );
201
	}
202
203
	/**
204
	 * Checks if this particular variation is visible. Invisible variations are enabled and can be selected, but no price / stock info is displayed.
205
	 * Instead, a suitable 'unavailable' message is displayed.
206
	 * Invisible by default: Disabled variations and variations with an empty price.
207
	 *
208
	 * @return bool
209
	 */
210
	public function variation_is_visible() {
211
		$visible = true;
212
213
		// Published == enabled checkbox
214
		if ( get_post_status( $this->variation_id ) != 'publish' ) {
215
			$visible = false;
216
		}
217
218
		// Price not set
219
		elseif ( $this->get_price() === "" ) {
220
			$visible = false;
221
		}
222
223
		return apply_filters( 'woocommerce_variation_is_visible', $visible, $this->variation_id, $this->id, $this );
224
	}
225
226
	/**
227
	 * Controls whether this particular variation will appear greyed-out (inactive) or not (active).
228
	 * Used by extensions to make incompatible variations appear greyed-out, etc.
229
	 * Other possible uses: prevent out-of-stock variations from being selected.
230
	 *
231
	 * @return bool
232
	 */
233
	public function variation_is_active() {
234
		return apply_filters( 'woocommerce_variation_is_active', true, $this );
235
	}
236
237
	/**
238
	 * Returns false if the product cannot be bought.
239
	 * Override abstract method so that: i) Disabled variations are not be purchasable by admins. ii) Enabled variations are not purchasable if the parent product is not purchasable.
240
	 *
241
	 * @return bool
242
	 */
243
	public function is_purchasable() {
244
		// Published == enabled checkbox
245
		if ( get_post_status( $this->variation_id ) != 'publish' ) {
246
			$purchasable = false;
247
		} else {
248
			$purchasable = parent::is_purchasable();
249
		}
250
		return apply_filters( 'woocommerce_variation_is_purchasable', $purchasable, $this );
251
	}
252
253
	/**
254
	 * Returns whether or not the variations parent is visible.
255
	 *
256
	 * @return bool
257
	 */
258
	public function parent_is_visible() {
259
		return $this->is_visible();
260
	}
261
262
	/**
263
	 * Get variation ID.
264
	 *
265
	 * @return int
266
	 */
267
	public function get_variation_id() {
268
		return absint( $this->variation_id );
269
	}
270
271
	/**
272
	 * Get variation attribute values.
273
	 *
274
	 * @return array of attributes and their values for this variation
275
	 */
276
	public function get_variation_attributes() {
277
		return $this->variation_data;
278
	}
279
280
	/**
281
	 * Check if all variation's attributes are set.
282
	 *
283
	 * @return boolean
284
	 */
285
	public function has_all_attributes_set() {
286
287
		$set = true;
288
289
		// undefined attributes have null strings as array values
290
		foreach( $this->get_variation_attributes() as $att ){
291
			if( ! $att ){
292
				$set = false;
293
				break;
294
			}
295
		}
296
297
		return $set;
298
299
	}
300
301
	/**
302
	 * Get variation price HTML. Prices are not inherited from parents.
303
	 *
304
	 * @return string containing the formatted price
305
	 */
306
	public function get_price_html( $price = '' ) {
307
308
		$display_price         = $this->get_display_price();
309
		$display_regular_price = $this->get_display_price( $this->get_regular_price() );
310
		$display_sale_price    = $this->get_display_price( $this->get_sale_price() );
311
312
		if ( $this->get_price() !== '' ) {
313
			if ( $this->is_on_sale() ) {
314
				$price = apply_filters( 'woocommerce_variation_sale_price_html', '<del>' . wc_price( $display_regular_price ) . '</del> <ins>' . wc_price( $display_sale_price ) . '</ins>' . $this->get_price_suffix(), $this );
315
			} elseif ( $this->get_price() > 0 ) {
316
				$price = apply_filters( 'woocommerce_variation_price_html', wc_price( $display_price ) . $this->get_price_suffix(), $this );
317
			} else {
318
				$price = apply_filters( 'woocommerce_variation_free_price_html', __( 'Free!', 'woocommerce' ), $this );
319
			}
320
		} else {
321
			$price = apply_filters( 'woocommerce_variation_empty_price_html', '', $this );
322
		}
323
324
		return apply_filters( 'woocommerce_get_variation_price_html', $price, $this );
325
	}
326
327
	/**
328
	 * Gets the main product image ID.
329
	 *
330
	 * @return int
331
	 */
332
	public function get_image_id() {
333
		if ( $this->variation_id && has_post_thumbnail( $this->variation_id ) ) {
334
			$image_id = get_post_thumbnail_id( $this->variation_id );
335
		} elseif ( has_post_thumbnail( $this->id ) ) {
336
			$image_id = get_post_thumbnail_id( $this->id );
337 View Code Duplication
		} elseif ( ( $parent_id = wp_get_post_parent_id( $this->id ) ) && has_post_thumbnail( $parent_id ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
338
			$image_id = get_post_thumbnail_id( $parent_id );
339
		} else {
340
			$image_id = 0;
341
		}
342
		return $image_id;
343
	}
344
345
	/**
346
	 * Gets the main product image.
347
	 *
348
	 * @param string $size (default: 'shop_thumbnail')
349
	 * @return string
350
	 */
351
	public function get_image( $size = 'shop_thumbnail', $attr = array() ) {
352
		if ( $this->variation_id && has_post_thumbnail( $this->variation_id ) ) {
353
			$image = get_the_post_thumbnail( $this->variation_id, $size, $attr );
354
		} elseif ( has_post_thumbnail( $this->id ) ) {
355
			$image = get_the_post_thumbnail( $this->id, $size, $attr );
356 View Code Duplication
		} elseif ( ( $parent_id = wp_get_post_parent_id( $this->id ) ) && has_post_thumbnail( $parent_id ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
357
			$image = get_the_post_thumbnail( $parent_id, $size , $attr);
358
		} else {
359
			$image = wc_placeholder_img( $size );
360
		}
361
		return $image;
362
	}
363
364
	/**
365
	 * Returns whether or not the product (or variation) is stock managed.
366
	 *
367
	 * @return bool|string Bool if managed at variation level, 'parent' if managed by the parent.
368
	 */
369
	public function managing_stock() {
370
		if ( 'yes' === get_option( 'woocommerce_manage_stock', 'yes' ) ) {
371
			if ( 'no' === $this->manage_stock ) {
372
				if ( $this->parent && $this->parent->managing_stock() ) {
373
					return 'parent';
374
				}
375
			} else {
376
				return true;
377
			}
378
		}
379
		return false;
380
	}
381
382
	/**
383
	 * Returns number of items available for sale from the variation, or parent.
384
	 *
385
	 * @return int
386
	 */
387
	public function get_stock_quantity() {
388
		return true === $this->managing_stock() ? wc_stock_amount( $this->stock ) : $this->parent->get_stock_quantity();
389
	}
390
391
	/**
392
	 * Returns the tax status. Always use parent data.
393
	 *
394
	 * @return string
395
	 */
396
	public function get_tax_status() {
397
		return $this->parent->get_tax_status();
398
	}
399
400
	/**
401
	 * Returns whether or not the product is in stock.
402
	 *
403
	 * @return bool
404
	 */
405
	public function is_in_stock() {
406
		// If we're managing stock at variation level, check stock levels
407
		if ( true === $this->managing_stock() ) {
408
			if ( $this->backorders_allowed() ) {
409
				return true;
410
			} elseif ( $this->get_stock_quantity() <= get_option( 'woocommerce_notify_no_stock_amount' ) ) {
411
				return false;
412
			} else {
413
				return $this->stock_status === 'instock';
414
			}
415
		} else {
416
			return $this->stock_status === 'instock';
417
		}
418
	}
419
420
	/**
421
	 * Set stock level of the product variation.
422
	 *
423
	 * Uses queries rather than update_post_meta so we can do this in one query (to avoid stock issues).
424
	 * We cannot rely on the original loaded value in case another order was made since then.
425
	 *
426
	 * @param int $amount
427
	 * @param string $mode can be set, add, or subtract
428
	 * @return int new stock level
429
	 */
430
	public function set_stock( $amount = null, $mode = 'set' ) {
431
		global $wpdb;
432
433
		if ( ! is_null( $amount ) && true === $this->managing_stock() ) {
434
435
			// Ensure key exists
436
			add_post_meta( $this->variation_id, '_stock', 0, true );
437
438
			// Update stock in DB directly
439 View Code Duplication
			switch ( $mode ) {
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...
440
				case 'add' :
441
					$wpdb->query( $wpdb->prepare( "UPDATE {$wpdb->postmeta} SET meta_value = meta_value + %f WHERE post_id = %d AND meta_key='_stock'", $amount, $this->variation_id ) );
442
				break;
443
				case 'subtract' :
444
					$wpdb->query( $wpdb->prepare( "UPDATE {$wpdb->postmeta} SET meta_value = meta_value - %f WHERE post_id = %d AND meta_key='_stock'", $amount, $this->variation_id ) );
445
				break;
446
				default :
447
					$wpdb->query( $wpdb->prepare( "UPDATE {$wpdb->postmeta} SET meta_value = %f WHERE post_id = %d AND meta_key='_stock'", $amount, $this->variation_id ) );
448
				break;
449
			}
450
451
			// Clear caches
452
			wp_cache_delete( $this->variation_id, 'post_meta' );
453
454
			// Clear total stock transient
455
			delete_transient( 'wc_product_total_stock_' . $this->id . WC_Cache_Helper::get_transient_version( 'product' ) );
456
457
			// Stock status
458
			$this->check_stock_status();
459
460
			// Sync the parent
461
			WC_Product_Variable::sync( $this->id );
462
463
			// Trigger action
464
			do_action( 'woocommerce_variation_set_stock', $this );
465
466
		} elseif ( ! is_null( $amount ) ) {
467
			return $this->parent->set_stock( $amount, $mode );
468
		}
469
470
		return $this->get_stock_quantity();
471
	}
472
473
	/**
474
	 * Set stock status.
475
	 *
476
	 * @param string $status
477
	 */
478
	public function set_stock_status( $status ) {
479
		$status = 'outofstock' === $status ? 'outofstock' : 'instock';
480
481
		// Sanity check
482
		if ( true === $this->managing_stock() ) {
483
			if ( ! $this->backorders_allowed() && $this->get_stock_quantity() <= get_option( 'woocommerce_notify_no_stock_amount' ) ) {
484
				$status = 'outofstock';
485
			}
486
		} elseif ( 'parent' === $this->managing_stock() ) {
487 View Code Duplication
			if ( ! $this->parent->backorders_allowed() && $this->parent->get_stock_quantity() <= get_option( 'woocommerce_notify_no_stock_amount' ) ) {
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...
488
				$status = 'outofstock';
489
			}
490
		}
491
492
		if ( update_post_meta( $this->variation_id, '_stock_status', $status ) ) {
493
			do_action( 'woocommerce_variation_set_stock_status', $this->variation_id, $status );
494
495
			WC_Product_Variable::sync_stock_status( $this->id );
496
		}
497
	}
498
499
	/**
500
	 * Reduce stock level of the product.
501
	 *
502
	 * @param int $amount (default: 1) Amount to reduce by
503
	 * @return int stock level
504
	 */
505
	public function reduce_stock( $amount = 1 ) {
506
		if ( true === $this->managing_stock() ) {
507
			$new_stock = $this->set_stock( $amount, 'subtract' );
508
509 View Code Duplication
			if ( 'yes' === get_option( 'woocommerce_notify_no_stock' ) && get_option( 'woocommerce_notify_no_stock_amount' ) >= $new_stock ) {
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...
510
	            do_action( 'woocommerce_no_stock', $this );
511
	        } elseif ( 'yes' === get_option( 'woocommerce_notify_low_stock' ) && get_option( 'woocommerce_notify_low_stock_amount' ) >= $new_stock ) {
512
				do_action( 'woocommerce_low_stock', $this );
513
			}
514
515
			return $new_stock;
516
		} else {
517
			return $this->parent->reduce_stock( $amount );
518
		}
519
	}
520
521
	/**
522
	 * Increase stock level of the product.
523
	 *
524
	 * @param int $amount (default: 1) Amount to increase by
525
	 * @return int stock level
526
	 */
527
	public function increase_stock( $amount = 1 ) {
528
		if ( true === $this->managing_stock() ) {
529
			return $this->set_stock( $amount, 'add' );
530
		} else {
531
			return $this->parent->increase_stock( $amount );
532
		}
533
	}
534
535
	/**
536
	 * Returns the availability of the product.
537
	 *
538
	 * @return string
539
	 */
540 View Code Duplication
	public function get_availability() {
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...
541
		$availability = $class = '';
542
543
		if ( $this->managing_stock() ) {
544
			if ( $this->is_in_stock() && $this->get_stock_quantity() > get_option( 'woocommerce_notify_no_stock_amount' ) ) {
545
				switch ( get_option( 'woocommerce_stock_format' ) ) {
546
					case 'no_amount' :
547
						$availability = __( 'In stock', 'woocommerce' );
548
					break;
549
					case 'low_amount' :
550
						if ( $this->get_stock_quantity() <= get_option( 'woocommerce_notify_low_stock_amount' ) ) {
551
							$availability = sprintf( __( 'Only %s left in stock', 'woocommerce' ), $this->get_stock_quantity() );
552
553
							if ( $this->backorders_allowed() && $this->backorders_require_notification() ) {
554
								$availability .= ' ' . __( '(can be backordered)', 'woocommerce' );
555
							}
556
						} else {
557
							$availability = __( 'In stock', 'woocommerce' );
558
						}
559
					break;
560
					default :
561
						$availability = sprintf( __( '%s in stock', 'woocommerce' ), $this->get_stock_quantity() );
562
563
						if ( $this->backorders_allowed() && $this->backorders_require_notification() ) {
564
							$availability .= ' ' . __( '(can be backordered)', 'woocommerce' );
565
						}
566
					break;
567
				}
568
				$class        = 'in-stock';
569
			} elseif ( $this->backorders_allowed() && $this->backorders_require_notification() ) {
570
				$availability = __( 'Available on backorder', 'woocommerce' );
571
				$class        = 'available-on-backorder';
572
			} elseif ( $this->backorders_allowed() ) {
573
				$availability = __( 'In stock', 'woocommerce' );
574
				$class        = 'in-stock';
575
			} else {
576
				$availability = __( 'Out of stock', 'woocommerce' );
577
				$class        = 'out-of-stock';
578
			}
579
		} elseif ( ! $this->is_in_stock() ) {
580
			$availability = __( 'Out of stock', 'woocommerce' );
581
			$class        = 'out-of-stock';
582
		}
583
584
		return apply_filters( 'woocommerce_get_availability', array( 'availability' => $availability, 'class' => $class ), $this );
585
	}
586
587
	/**
588
	 * Returns whether or not the product needs to notify the customer on backorder.
589
	 *
590
	 * @return bool
591
	 */
592
	public function backorders_require_notification() {
593
		if ( true === $this->managing_stock() ) {
594
			return parent::backorders_require_notification();
595
		} else {
596
			return $this->parent->backorders_require_notification();
597
		}
598
	}
599
600
	/**
601
	 * Is on backorder?
602
	 *
603
	 * @param int $qty_in_cart (default: 0)
604
	 * @return bool
605
	 */
606
	public function is_on_backorder( $qty_in_cart = 0 ) {
607
		if ( true === $this->managing_stock() ) {
608
			return parent::is_on_backorder( $qty_in_cart );
609
		} else {
610
			return $this->parent->is_on_backorder( $qty_in_cart );
611
		}
612
	}
613
614
	/**
615
	 * Returns whether or not the product has enough stock for the order.
616
	 *
617
	 * @param mixed $quantity
618
	 * @return bool
619
	 */
620
	public function has_enough_stock( $quantity ) {
621
		if ( true === $this->managing_stock() ) {
622
			return parent::has_enough_stock( $quantity );
623
		} else {
624
			return $this->parent->has_enough_stock( $quantity );
625
		}
626
	}
627
628
	/**
629
	 * Get the shipping class, and if not set, get the shipping class of the parent.
630
	 *
631
	 * @return string
632
	 */
633 View Code Duplication
	public function get_shipping_class() {
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...
634
		if ( ! $this->variation_shipping_class ) {
635
			$classes = get_the_terms( $this->variation_id, 'product_shipping_class' );
636
637
			if ( $classes && ! is_wp_error( $classes ) ) {
638
				$this->variation_shipping_class = current( $classes )->slug;
639
			} else {
640
				$this->variation_shipping_class = parent::get_shipping_class();
0 ignored issues
show
Documentation Bug introduced by
The property $variation_shipping_class was declared of type boolean, but parent::get_shipping_class() is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
641
			}
642
		}
643
		return $this->variation_shipping_class;
644
	}
645
646
	/**
647
	 * Returns the product shipping class ID.
648
	 *
649
	 * @return int
650
	 */
651 View Code Duplication
	public function get_shipping_class_id() {
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...
652
		if ( ! $this->variation_shipping_class_id ) {
653
			$classes = get_the_terms( $this->variation_id, 'product_shipping_class' );
654
655
			if ( $classes && ! is_wp_error( $classes ) ) {
656
				$this->variation_shipping_class_id = current( $classes )->term_id;
657
			} else {
658
				$this->variation_shipping_class_id = parent::get_shipping_class_id();
0 ignored issues
show
Documentation Bug introduced by
The property $variation_shipping_class_id was declared of type boolean, but parent::get_shipping_class_id() is of type integer. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
659
			}
660
		}
661
		return absint( $this->variation_shipping_class_id );
662
	}
663
664
	/**
665
	 * Get formatted variation data with WC < 2.4 back compat and proper formatting of text-based attribute names.
666
	 *
667
	 * @return string
668
	 */
669
	public function get_formatted_variation_attributes( $flat = false ) {
670
		$variation_data = $this->get_variation_attributes();
671
		$attributes     = $this->parent->get_attributes();
672
		$description    = array();
673
		$return         = '';
674
675
		if ( is_array( $variation_data ) ) {
676
677
			if ( ! $flat ) {
678
				$return = '<dl class="variation">';
679
			}
680
681
			foreach ( $attributes as $attribute ) {
682
683
				// Only deal with attributes that are variations
684
				if ( ! $attribute[ 'is_variation' ] ) {
685
					continue;
686
				}
687
688
				$variation_selected_value = isset( $variation_data[ 'attribute_' . sanitize_title( $attribute[ 'name' ] ) ] ) ? $variation_data[ 'attribute_' . sanitize_title( $attribute[ 'name' ] ) ] : '';
689
				$description_name         = esc_html( wc_attribute_label( $attribute[ 'name' ] ) );
690
				$description_value        = __( 'Any', 'woocommerce' );
691
692
				// Get terms for attribute taxonomy or value if its a custom attribute
693
				if ( $attribute[ 'is_taxonomy' ] ) {
694
695
					$post_terms = wp_get_post_terms( $this->id, $attribute[ 'name' ] );
696
697
					foreach ( $post_terms as $term ) {
698
						if ( $variation_selected_value === $term->slug ) {
699
							$description_value = esc_html( apply_filters( 'woocommerce_variation_option_name', $term->name ) );
700
						}
701
					}
702
703
				} else {
704
705
					$options = wc_get_text_attributes( $attribute[ 'value' ] );
706
707
					foreach ( $options as $option ) {
708
709
						if ( sanitize_title( $variation_selected_value ) === $variation_selected_value ) {
710
							if ( $variation_selected_value !== sanitize_title( $option ) ) {
711
								continue;
712
							}
713
						} else {
714
							if ( $variation_selected_value !== $option ) {
715
								continue;
716
							}
717
						}
718
719
						$description_value = esc_html( apply_filters( 'woocommerce_variation_option_name', $option ) );
720
					}
721
				}
722
723
				if ( $flat ) {
724
					$description[] = $description_name . ': ' . rawurldecode( $description_value );
725
				} else {
726
					$description[] = '<dt>' . $description_name . ':</dt><dd>' . rawurldecode( $description_value ) . '</dd>';
727
				}
728
			}
729
730
			if ( $flat ) {
731
				$return .= implode( ', ', $description );
732
			} else {
733
				$return .= implode( '', $description );
734
			}
735
736
			if ( ! $flat ) {
737
				$return .= '</dl>';
738
			}
739
		}
740
741
		return $return;
742
	}
743
744
	/**
745
	 * Get product name with extra details such as SKU, price and attributes. Used within admin.
746
	 *
747
	 * @return string Formatted product name, including attributes and price
748
	 */
749
	public function get_formatted_name() {
750
		if ( $this->get_sku() ) {
751
			$identifier = $this->get_sku();
752
		} else {
753
			$identifier = '#' . $this->variation_id;
754
		}
755
756
		$formatted_attributes = $this->get_formatted_variation_attributes( true );
757
		$extra_data           = ' &ndash; ' . $formatted_attributes . ' &ndash; ' . wc_price( $this->get_price() );
758
759
		return sprintf( __( '%s &ndash; %s%s', 'woocommerce' ), $identifier, $this->get_title(), $extra_data );
760
	}
761
762
	/**
763
	 * Get product variation description.
764
	 *
765
	 * @return string
766
	 */
767
	public function get_variation_description() {
768
		return wpautop( do_shortcode( wp_kses_post( get_post_meta( $this->variation_id, '_variation_description', true ) ) ) );
769
	}
770
}
771