Completed
Push — master ( 0782b8...fce8dc )
by Mike
10:30
created

wc-product-functions.php ➔ wc_get_product_attachment_props()   B

Complexity

Conditions 6
Paths 17

Size

Total Lines 21
Code Lines 16

Duplication

Lines 0
Ratio 0 %
Metric Value
cc 6
eloc 16
nc 17
nop 2
dl 0
loc 21
rs 8.7624
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 24 and the first side effect is on line 14.

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
 * WooCommerce Product Functions
4
 *
5
 * Functions for product specific things.
6
 *
7
 * @author   WooThemes
8
 * @category Core
9
 * @package  WooCommerce/Functions
10
 * @version  2.3.0
11
 */
12
13
if ( ! defined( 'ABSPATH' ) ) {
14
	exit; // Exit if accessed directly
15
}
16
17
/**
18
 * Main function for returning products, uses the WC_Product_Factory class.
19
 *
20
 * @param mixed $the_product Post object or post ID of the product.
21
 * @param array $args (default: array()) Contains all arguments to be used to get this product.
22
 * @return WC_Product
23
 */
24 View Code Duplication
function wc_get_product( $the_product = false, $args = array() ) {
0 ignored issues
show
Duplication introduced by
This function 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...
25
	if ( ! did_action( 'woocommerce_init' ) ) {
26
		_doing_it_wrong( __FUNCTION__, __( 'wc_get_product should not be called before the woocommerce_init action.', 'woocommerce' ), '2.5' );
27
		return false;
28
	}
29
	return WC()->product_factory->get_product( $the_product, $args );
30
}
31
32
/**
33
 * Update a product's stock amount.
34
 *
35
 * @param  int $product_id
36
 * @param  int $new_stock_level
37
 */
38
function wc_update_product_stock( $product_id, $new_stock_level ) {
39
	$product = wc_get_product( $product_id );
40
41
	if ( ! metadata_exists( 'post', $product_id, '_stock' ) || $product->get_stock_quantity() !== $new_stock_level ) {
42
		$product->set_stock( $new_stock_level );
43
	}
44
}
45
46
/**
47
 * Update a product's stock status.
48
 *
49
 * @param  int $product_id
50
 * @param  int $status
51
 */
52
function wc_update_product_stock_status( $product_id, $status ) {
53
	$product = wc_get_product( $product_id );
54
	if ( $product ) {
55
		$product->set_stock_status( $status );
56
	}
57
}
58
59
/**
60
 * Returns whether or not SKUS are enabled.
61
 * @return bool
62
 */
63
function wc_product_sku_enabled() {
64
	return apply_filters( 'wc_product_sku_enabled', true );
65
}
66
67
/**
68
 * Returns whether or not product weights are enabled.
69
 * @return bool
70
 */
71
function wc_product_weight_enabled() {
72
	return apply_filters( 'wc_product_weight_enabled', true );
73
}
74
75
/**
76
 * Returns whether or not product dimensions (HxWxD) are enabled.
77
 * @return bool
78
 */
79
function wc_product_dimensions_enabled() {
80
	return apply_filters( 'wc_product_dimensions_enabled', true );
81
}
82
83
/**
84
 * Clear all transients cache for product data.
85
 *
86
 * @param int $post_id (default: 0)
87
 */
88
function wc_delete_product_transients( $post_id = 0 ) {
89
	// Core transients
90
	$transients_to_clear = array(
91
		'wc_products_onsale',
92
		'wc_featured_products',
93
		'wc_outofstock_count',
94
		'wc_low_stock_count'
95
	);
96
97
	// Transient names that include an ID
98
	$post_transient_names = array(
99
		'wc_product_children_',
100
		'wc_product_total_stock_',
101
		'wc_var_prices_',
102
		'wc_related_'
103
	);
104
105
	if ( $post_id > 0 ) {
106
		foreach( $post_transient_names as $transient ) {
107
			$transients_to_clear[] = $transient . $post_id;
108
		}
109
	}
110
111
	// Delete transients
112
	foreach( $transients_to_clear as $transient ) {
113
		delete_transient( $transient );
114
	}
115
116
	// Increments the transient version to invalidate cache
117
	WC_Cache_Helper::get_transient_version( 'product', true );
118
119
	do_action( 'woocommerce_delete_product_transients', $post_id );
120
}
121
122
/**
123
 * Function that returns an array containing the IDs of the products that are on sale.
124
 *
125
 * @since 2.0
126
 * @access public
127
 * @return array
128
 */
129
function wc_get_product_ids_on_sale() {
130
	global $wpdb;
131
132
	// Load from cache
133
	$product_ids_on_sale = get_transient( 'wc_products_onsale' );
134
135
	// Valid cache found
136
	if ( false !== $product_ids_on_sale ) {
137
		return $product_ids_on_sale;
138
	}
139
140
	$on_sale_posts = $wpdb->get_results( "
141
		SELECT post.ID, post.post_parent FROM `$wpdb->posts` AS post
142
		LEFT JOIN `$wpdb->postmeta` AS meta ON post.ID = meta.post_id
143
		LEFT JOIN `$wpdb->postmeta` AS meta2 ON post.ID = meta2.post_id
144
		WHERE post.post_type IN ( 'product', 'product_variation' )
145
			AND post.post_status = 'publish'
146
			AND meta.meta_key = '_sale_price'
147
			AND meta2.meta_key = '_price'
148
			AND CAST( meta.meta_value AS DECIMAL ) >= 0
149
			AND CAST( meta.meta_value AS CHAR ) != ''
150
			AND CAST( meta.meta_value AS DECIMAL ) = CAST( meta2.meta_value AS DECIMAL )
151
		GROUP BY post.ID;
152
	" );
153
154
	$product_ids_on_sale = array_unique( array_map( 'absint', array_merge( wp_list_pluck( $on_sale_posts, 'ID' ), array_diff( wp_list_pluck( $on_sale_posts, 'post_parent' ), array( 0 ) ) ) ) );
155
156
	set_transient( 'wc_products_onsale', $product_ids_on_sale, DAY_IN_SECONDS * 30 );
157
158
	return $product_ids_on_sale;
159
}
160
161
/**
162
 * Function that returns an array containing the IDs of the featured products.
163
 *
164
 * @since 2.1
165
 * @access public
166
 * @return array
167
 */
168
function wc_get_featured_product_ids() {
169
170
	// Load from cache
171
	$featured_product_ids = get_transient( 'wc_featured_products' );
172
173
	// Valid cache found
174
	if ( false !== $featured_product_ids )
175
		return $featured_product_ids;
176
177
	$featured = get_posts( array(
178
		'post_type'      => array( 'product', 'product_variation' ),
179
		'posts_per_page' => -1,
180
		'post_status'    => 'publish',
181
		'meta_query'     => array(
182
			array(
183
				'key' 		=> '_visibility',
184
				'value' 	=> array('catalog', 'visible'),
185
				'compare' 	=> 'IN'
186
			),
187
			array(
188
				'key' 	=> '_featured',
189
				'value' => 'yes'
190
			)
191
		),
192
		'fields' => 'id=>parent'
193
	) );
194
195
	$product_ids          = array_keys( $featured );
196
	$parent_ids           = array_values( array_filter( $featured ) );
197
	$featured_product_ids = array_unique( array_merge( $product_ids, $parent_ids ) );
198
199
	set_transient( 'wc_featured_products', $featured_product_ids, DAY_IN_SECONDS * 30 );
200
201
	return $featured_product_ids;
202
}
203
204
/**
205
 * Filter to allow product_cat in the permalinks for products.
206
 *
207
 * @param  string  $permalink The existing permalink URL.
208
 * @param  WP_Post $post
209
 * @return string
210
 */
211
function wc_product_post_type_link( $permalink, $post ) {
212
	// Abort if post is not a product.
213
	if ( $post->post_type !== 'product' ) {
214
		return $permalink;
215
	}
216
217
	// Abort early if the placeholder rewrite tag isn't in the generated URL.
218
	if ( false === strpos( $permalink, '%' ) ) {
219
		return $permalink;
220
	}
221
222
	// Get the custom taxonomy terms in use by this post.
223
	$terms = get_the_terms( $post->ID, 'product_cat' );
224
225
	if ( ! empty( $terms ) ) {
226
		usort( $terms, '_usort_terms_by_ID' ); // order by ID
227
228
		$category_object = apply_filters( 'wc_product_post_type_link_product_cat', $terms[0], $terms, $post );
229
		$category_object = get_term( $category_object, 'product_cat' );
230
		$product_cat     = $category_object->slug;
231
232
		if ( $category_object->parent ) {
233
			$ancestors = get_ancestors( $category_object->term_id, 'product_cat' );
234
			foreach ( $ancestors as $ancestor ) {
235
				$ancestor_object = get_term( $ancestor, 'product_cat' );
236
				$product_cat     = $ancestor_object->slug . '/' . $product_cat;
237
			}
238
		}
239
	} else {
240
		// If no terms are assigned to this post, use a string instead (can't leave the placeholder there)
241
		$product_cat = _x( 'uncategorized', 'slug', 'woocommerce' );
242
	}
243
244
	$find = array(
245
		'%year%',
246
		'%monthnum%',
247
		'%day%',
248
		'%hour%',
249
		'%minute%',
250
		'%second%',
251
		'%post_id%',
252
		'%category%',
253
		'%product_cat%'
254
	);
255
256
	$replace = array(
257
		date_i18n( 'Y', strtotime( $post->post_date ) ),
258
		date_i18n( 'm', strtotime( $post->post_date ) ),
259
		date_i18n( 'd', strtotime( $post->post_date ) ),
260
		date_i18n( 'H', strtotime( $post->post_date ) ),
261
		date_i18n( 'i', strtotime( $post->post_date ) ),
262
		date_i18n( 's', strtotime( $post->post_date ) ),
263
		$post->ID,
264
		$product_cat,
265
		$product_cat
266
	);
267
268
	$permalink = str_replace( $find, $replace, $permalink );
269
270
	return $permalink;
271
}
272
add_filter( 'post_type_link', 'wc_product_post_type_link', 10, 2 );
273
274
275
/**
276
 * Get the placeholder image URL for products etc.
277
 *
278
 * @access public
279
 * @return string
280
 */
281
function wc_placeholder_img_src() {
282
	return apply_filters( 'woocommerce_placeholder_img_src', WC()->plugin_url() . '/assets/images/placeholder.png' );
283
}
284
285
/**
286
 * Get the placeholder image.
287
 *
288
 * @access public
289
 * @return string
290
 */
291
function wc_placeholder_img( $size = 'shop_thumbnail' ) {
292
	$dimensions = wc_get_image_size( $size );
293
294
	return apply_filters('woocommerce_placeholder_img', '<img src="' . wc_placeholder_img_src() . '" alt="' . esc_attr__( 'Placeholder', 'woocommerce' ) . '" width="' . esc_attr( $dimensions['width'] ) . '" class="woocommerce-placeholder wp-post-image" height="' . esc_attr( $dimensions['height'] ) . '" />', $size, $dimensions );
295
}
296
297
/**
298
 * Variation Formatting.
299
 *
300
 * Gets a formatted version of variation data or item meta.
301
 *
302
 * @access public
303
 * @param string $variation
304
 * @param bool $flat (default: false)
305
 * @return string
306
 */
307
function wc_get_formatted_variation( $variation, $flat = false ) {
308
	$return = '';
309
	if ( is_array( $variation ) ) {
310
311
		if ( ! $flat ) {
312
			$return = '<dl class="variation">';
313
		}
314
315
		$variation_list = array();
316
317
		foreach ( $variation as $name => $value ) {
318
			if ( ! $value ) {
319
				continue;
320
			}
321
322
			// If this is a term slug, get the term's nice name
323
			if ( taxonomy_exists( esc_attr( str_replace( 'attribute_', '', $name ) ) ) ) {
324
				$term = get_term_by( 'slug', $value, esc_attr( str_replace( 'attribute_', '', $name ) ) );
325
				if ( ! is_wp_error( $term ) && ! empty( $term->name ) ) {
326
					$value = $term->name;
327
				}
328
			} else {
329
				$value = ucwords( str_replace( '-', ' ', $value ) );
330
			}
331
332
			if ( $flat ) {
333
				$variation_list[] = wc_attribute_label( str_replace( 'attribute_', '', $name ) ) . ': ' . rawurldecode( $value );
334
			} else {
335
				$variation_list[] = '<dt>' . wc_attribute_label( str_replace( 'attribute_', '', $name ) ) . ':</dt><dd>' . rawurldecode( $value ) . '</dd>';
336
			}
337
		}
338
339
		if ( $flat ) {
340
			$return .= implode( ', ', $variation_list );
341
		} else {
342
			$return .= implode( '', $variation_list );
343
		}
344
345
		if ( ! $flat ) {
346
			$return .= '</dl>';
347
		}
348
	}
349
	return $return;
350
}
351
352
/**
353
 * Function which handles the start and end of scheduled sales via cron.
354
 *
355
 * @access public
356
 */
357
function wc_scheduled_sales() {
358
	global $wpdb;
359
360
	// Sales which are due to start
361
	$product_ids = $wpdb->get_col( $wpdb->prepare( "
362
		SELECT postmeta.post_id FROM {$wpdb->postmeta} as postmeta
363
		LEFT JOIN {$wpdb->postmeta} as postmeta_2 ON postmeta.post_id = postmeta_2.post_id
364
		LEFT JOIN {$wpdb->postmeta} as postmeta_3 ON postmeta.post_id = postmeta_3.post_id
365
		WHERE postmeta.meta_key = '_sale_price_dates_from'
366
		AND postmeta_2.meta_key = '_price'
367
		AND postmeta_3.meta_key = '_sale_price'
368
		AND postmeta.meta_value > 0
369
		AND postmeta.meta_value < %s
370
		AND postmeta_2.meta_value != postmeta_3.meta_value
371
	", current_time( 'timestamp' ) ) );
372
373
	if ( $product_ids ) {
374 View Code Duplication
		foreach ( $product_ids as $product_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...
375
			$sale_price = get_post_meta( $product_id, '_sale_price', true );
376
377
			if ( $sale_price ) {
378
				update_post_meta( $product_id, '_price', $sale_price );
379
			} else {
380
				// No sale price!
381
				update_post_meta( $product_id, '_sale_price_dates_from', '' );
382
				update_post_meta( $product_id, '_sale_price_dates_to', '' );
383
			}
384
385
			$parent = wp_get_post_parent_id( $product_id );
386
387
			// Sync parent
388
			if ( $parent ) {
389
				// Clear prices transient for variable products.
390
				delete_transient( 'wc_var_prices_' . $parent );
391
392
				// Grouped products need syncing via a function
393
				$this_product = wc_get_product( $product_id );
394
395
				if ( $this_product->is_type( 'simple' ) ) {
396
					$this_product->grouped_product_sync();
397
				}
398
			}
399
		}
400
401
		delete_transient( 'wc_products_onsale' );
402
	}
403
404
	// Sales which are due to end
405
	$product_ids = $wpdb->get_col( $wpdb->prepare( "
406
		SELECT postmeta.post_id FROM {$wpdb->postmeta} as postmeta
407
		LEFT JOIN {$wpdb->postmeta} as postmeta_2 ON postmeta.post_id = postmeta_2.post_id
408
		LEFT JOIN {$wpdb->postmeta} as postmeta_3 ON postmeta.post_id = postmeta_3.post_id
409
		WHERE postmeta.meta_key = '_sale_price_dates_to'
410
		AND postmeta_2.meta_key = '_price'
411
		AND postmeta_3.meta_key = '_regular_price'
412
		AND postmeta.meta_value > 0
413
		AND postmeta.meta_value < %s
414
		AND postmeta_2.meta_value != postmeta_3.meta_value
415
	", current_time( 'timestamp' ) ) );
416
417
	if ( $product_ids ) {
418 View Code Duplication
		foreach ( $product_ids as $product_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...
419
			$regular_price = get_post_meta( $product_id, '_regular_price', true );
420
421
			update_post_meta( $product_id, '_price', $regular_price );
422
			update_post_meta( $product_id, '_sale_price', '' );
423
			update_post_meta( $product_id, '_sale_price_dates_from', '' );
424
			update_post_meta( $product_id, '_sale_price_dates_to', '' );
425
426
			$parent = wp_get_post_parent_id( $product_id );
427
428
			// Sync parent
429
			if ( $parent ) {
430
				// Grouped products need syncing via a function
431
				$this_product = wc_get_product( $product_id );
432
				if ( $this_product->is_type( 'simple' ) ) {
433
					$this_product->grouped_product_sync();
434
				}
435
			}
436
		}
437
438
		WC_Cache_Helper::get_transient_version( 'product', true );
439
		delete_transient( 'wc_products_onsale' );
440
	}
441
}
442
add_action( 'woocommerce_scheduled_sales', 'wc_scheduled_sales' );
443
444
/**
445
 * Get attachment image attributes.
446
 *
447
 * @access public
448
 * @param array $attr
449
 * @return array
450
 */
451
function wc_get_attachment_image_attributes( $attr ) {
452
	if ( strstr( $attr['src'], 'woocommerce_uploads/' ) ) {
453
		$attr['src'] = wc_placeholder_img_src();
454
	}
455
456
	return $attr;
457
}
458
add_filter( 'wp_get_attachment_image_attributes', 'wc_get_attachment_image_attributes' );
459
460
461
/**
462
 * Prepare attachment for JavaScript.
463
 *
464
 * @access public
465
 * @param array $response
466
 * @return array
467
 */
468
function wc_prepare_attachment_for_js( $response ) {
469
470
	if ( isset( $response['url'] ) && strstr( $response['url'], 'woocommerce_uploads/' ) ) {
471
		$response['full']['url'] = wc_placeholder_img_src();
472
		if ( isset( $response['sizes'] ) ) {
473
			foreach( $response['sizes'] as $size => $value ) {
474
				$response['sizes'][ $size ]['url'] = wc_placeholder_img_src();
475
			}
476
		}
477
	}
478
479
	return $response;
480
}
481
add_filter( 'wp_prepare_attachment_for_js', 'wc_prepare_attachment_for_js' );
482
483
/**
484
 * Track product views.
485
 */
486
function wc_track_product_view() {
487
	if ( ! is_singular( 'product' ) || ! is_active_widget( false, false, 'woocommerce_recently_viewed_products', true ) ) {
488
		return;
489
	}
490
491
	global $post;
492
493
	if ( empty( $_COOKIE['woocommerce_recently_viewed'] ) )
494
		$viewed_products = array();
495
	else
496
		$viewed_products = (array) explode( '|', $_COOKIE['woocommerce_recently_viewed'] );
497
498
	if ( ! in_array( $post->ID, $viewed_products ) ) {
499
		$viewed_products[] = $post->ID;
500
	}
501
502
	if ( sizeof( $viewed_products ) > 15 ) {
503
		array_shift( $viewed_products );
504
	}
505
506
	// Store for session only
507
	wc_setcookie( 'woocommerce_recently_viewed', implode( '|', $viewed_products ) );
508
}
509
510
add_action( 'template_redirect', 'wc_track_product_view', 20 );
511
512
/**
513
 * Get product types.
514
 *
515
 * @since 2.2
516
 * @return array
517
 */
518
function wc_get_product_types() {
519
	return (array) apply_filters( 'product_type_selector', array(
520
		'simple'   => __( 'Simple product', 'woocommerce' ),
521
		'grouped'  => __( 'Grouped product', 'woocommerce' ),
522
		'external' => __( 'External/Affiliate product', 'woocommerce' ),
523
		'variable' => __( 'Variable product', 'woocommerce' )
524
	) );
525
}
526
527
/**
528
 * Check if product sku is unique.
529
 *
530
 * @since 2.2
531
 * @param int $product_id
532
 * @param string $sku Will be slashed to work around https://core.trac.wordpress.org/ticket/27421
533
 * @return bool
534
 */
535
function wc_product_has_unique_sku( $product_id, $sku ) {
536
	global $wpdb;
537
538
	$sku_found = $wpdb->get_var( $wpdb->prepare( "
539
		SELECT $wpdb->posts.ID
540
		FROM $wpdb->posts
541
		LEFT JOIN $wpdb->postmeta ON ( $wpdb->posts.ID = $wpdb->postmeta.post_id )
542
		WHERE $wpdb->posts.post_type IN ( 'product', 'product_variation' )
543
		AND $wpdb->posts.post_status = 'publish'
544
		AND $wpdb->postmeta.meta_key = '_sku' AND $wpdb->postmeta.meta_value = '%s'
545
		AND $wpdb->postmeta.post_id <> %d LIMIT 1
546
	 ", wp_slash( $sku ), $product_id ) );
547
548
	if ( apply_filters( 'wc_product_has_unique_sku', $sku_found, $product_id, $sku ) ) {
549
		return false;
550
	} else {
551
		return true;
552
	}
553
}
554
555
/**
556
 * Get product ID by SKU.
557
 *
558
 * @since  2.3.0
559
 * @param  string $sku
560
 * @return int
561
 */
562
function wc_get_product_id_by_sku( $sku ) {
563
	global $wpdb;
564
565
	$product_id = $wpdb->get_var( $wpdb->prepare( "
566
		SELECT posts.ID
567
		FROM $wpdb->posts AS posts
568
		LEFT JOIN $wpdb->postmeta AS postmeta ON ( posts.ID = postmeta.post_id )
569
		WHERE posts.post_type IN ( 'product', 'product_variation' )
570
		AND postmeta.meta_key = '_sku' AND postmeta.meta_value = '%s'
571
		LIMIT 1
572
	 ", $sku ) );
573
574
	return ( $product_id ) ? intval( $product_id ) : 0;
575
}
576
577
/**
578
 * Save product price.
579
 *
580
 * This is a private function (internal use ONLY) used until a data manipulation api is built.
581
 *
582
 * @since 2.4.0
583
 * @todo  look into Data manipulation API
584
 *
585
 * @param int $product_id
586
 * @param float $regular_price
587
 * @param float $sale_price
588
 * @param string $date_from
589
 * @param string $date_to
590
 */
591
function _wc_save_product_price( $product_id, $regular_price, $sale_price = '', $date_from = '', $date_to = '' ) {
592
	$product_id  = absint( $product_id );
593
	$regular_price = wc_format_decimal( $regular_price );
594
	$sale_price    = $sale_price === '' ? '' : wc_format_decimal( $sale_price );
595
	$date_from     = wc_clean( $date_from );
596
	$date_to       = wc_clean( $date_to );
597
598
	update_post_meta( $product_id, '_regular_price', $regular_price );
599
	update_post_meta( $product_id, '_sale_price', $sale_price );
600
601
	// Save Dates
602
	update_post_meta( $product_id, '_sale_price_dates_from', $date_from ? strtotime( $date_from ) : '' );
603
	update_post_meta( $product_id, '_sale_price_dates_to', $date_to ? strtotime( $date_to ) : '' );
604
605
	if ( $date_to && ! $date_from ) {
606
		update_post_meta( $product_id, '_sale_price_dates_from', strtotime( 'NOW', current_time( 'timestamp' ) ) );
607
	}
608
609
	// Update price if on sale
610 View Code Duplication
	if ( '' !== $sale_price && '' === $date_to && '' === $date_from ) {
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...
611
		update_post_meta( $product_id, '_price', $sale_price );
612
	} else {
613
		update_post_meta( $product_id, '_price', $regular_price );
614
	}
615
616
	if ( '' !== $sale_price && $date_from && strtotime( $date_from ) < strtotime( 'NOW', current_time( 'timestamp' ) ) ) {
617
		update_post_meta( $product_id, '_price', $sale_price );
618
	}
619
620 View Code Duplication
	if ( $date_to && strtotime( $date_to ) < strtotime( 'NOW', current_time( 'timestamp' ) ) ) {
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...
621
		update_post_meta( $product_id, '_price', $regular_price );
622
		update_post_meta( $product_id, '_sale_price_dates_from', '' );
623
		update_post_meta( $product_id, '_sale_price_dates_to', '' );
624
	}
625
}
626
627
/**
628
 * Get attibutes/data for an individual variation from the database and maintain it's integrity.
629
 * @since  2.4.0
630
 * @param  int $variation_id
631
 * @return array
632
 */
633
function wc_get_product_variation_attributes( $variation_id ) {
634
	// Build variation data from meta
635
	$all_meta                = get_post_meta( $variation_id );
636
	$parent_id               = wp_get_post_parent_id( $variation_id );
637
	$parent_attributes       = array_filter( (array) get_post_meta( $parent_id, '_product_attributes', true ) );
638
	$found_parent_attributes = array();
639
	$variation_attributes    = array();
640
641
	// Compare to parent variable product attributes and ensure they match
642
	foreach ( $parent_attributes as $attribute_name => $options ) {
643
		if ( ! empty( $options['is_variation'] ) ) {
644
			$attribute                 = 'attribute_' . sanitize_title( $attribute_name );
645
			$found_parent_attributes[] = $attribute;
646
			if ( ! array_key_exists( $attribute, $variation_attributes ) ) {
647
				$variation_attributes[ $attribute ] = ''; // Add it - 'any' will be asumed
648
			}
649
		}
650
	}
651
652
	// Get the variation attributes from meta
653
	foreach ( $all_meta as $name => $value ) {
654
		// Only look at valid attribute meta, and also compare variation level attributes and remove any which do not exist at parent level
655
		if ( 0 !== strpos( $name, 'attribute_' ) || ! in_array( $name, $found_parent_attributes ) ) {
656
			unset( $variation_attributes[ $name ] );
657
			continue;
658
		}
659
		/**
660
		 * Pre 2.4 handling where 'slugs' were saved instead of the full text attribute.
661
		 * Attempt to get full version of the text attribute from the parent.
662
		 */
663
		if ( sanitize_title( $value[0] ) === $value[0] && version_compare( get_post_meta( $parent_id, '_product_version', true ), '2.4.0', '<' ) ) {
664 View Code Duplication
			foreach ( $parent_attributes as $attribute ) {
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...
665
				if ( $name !== 'attribute_' . sanitize_title( $attribute['name'] ) ) {
666
					continue;
667
				}
668
				$text_attributes = wc_get_text_attributes( $attribute['value'] );
669
670
				foreach ( $text_attributes as $text_attribute ) {
671
					if ( sanitize_title( $text_attribute ) === $value[0] ) {
672
						$value[0] = $text_attribute;
673
						break;
674
					}
675
				}
676
			}
677
		}
678
679
		$variation_attributes[ $name ] = $value[0];
680
	}
681
682
	return $variation_attributes;
683
}
684
685
/**
686
 * Get all product cats for a product by ID, including hierarchy
687
 * @since  2.5.0
688
 * @param  int $product_id
689
 * @return array
690
 */
691
function wc_get_product_cat_ids( $product_id ) {
692
	$product_cats = wp_get_post_terms( $product_id, 'product_cat', array( "fields" => "ids" ) );
693
694
	foreach ( $product_cats as $product_cat ) {
695
		$product_cats = array_merge( $product_cats, get_ancestors( $product_cat, 'product_cat' ) );
696
	}
697
698
	return $product_cats;
699
}
700
701
/**
702
 * Gets data about an attachment, such as alt text and captions.
703
 * @since 2.6.0
704
 * @param int $thumbnail_id
0 ignored issues
show
Bug introduced by
There is no parameter named $thumbnail_id. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

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

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

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

Loading history...
705
 * @param object|bool $product
706
 * @return array
707
 */
708
function wc_get_product_attachment_props( $attachment_id, $product = false ) {
709
	$props = array(
710
		'title'   => '',
711
		'caption' => '',
712
		'url'     => '',
713
		'alt'     => '',
714
	);
715
	if ( $attachment_id ) {
716
		$attachment       = get_post( $attachment_id );
717
		$props['title']   = trim( strip_tags( $attachment->post_title ) );
718
		$props['caption'] = trim( strip_tags( $attachment->post_excerpt ) );
719
		$props['url']     = wp_get_attachment_url( $attachment_id );
720
		$props['alt']     = trim( strip_tags( get_post_meta( $attachment_id, '_wp_attachment_image_alt', true ) ) );
721
722
		// Alt text fallbacks
723
		$props['alt']     = empty( $props['alt'] ) ? $props['caption'] : $props['alt'];
724
		$props['alt']     = empty( $props['alt'] ) ? trim( strip_tags( $attachment->post_title ) ) : $props['alt'];
725
		$props['alt']     = empty( $props['alt'] ) && $product ? trim( strip_tags( get_the_title( $product->ID ) ) ) : $props['alt'];
726
	}
727
	return $props;
728
}
729